From b0cd68af353969bb6f8cb5326a41449c94306f1a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 11 May 2020 15:11:54 +0000 Subject: [PATCH 001/175] modified: mmnode-blocks-info --- mmnode-blocks-info | 400 +++++++++++++++++++++++++++++++-------------- 1 file changed, 280 insertions(+), 120 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 8d0b2ca..9a43006 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2017 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -21,9 +21,12 @@ mmgen-blocks-info: Display information about a block or range of blocks """ import time,re +from collections import namedtuple from mmgen.common import * +from decimal import Decimal opts_data = { + 'sets': [('raw_miner_info',True,'miner_info',True)], 'text': { 'desc': 'Display information about or find a transaction within a range of blocks', 'usage': '[opts] +||', @@ -32,9 +35,10 @@ opts_data = { --, --longhelp Print help message for long options (common options) -H, --hashes Display only block numbers and hashes -m, --miner-info Display miner info in coinbase transaction --n, --nya Display NYA (New York Agreement) support -M, --raw-miner-info Display miner info in uninterpreted form +-o, --fields= Display the specified fields -s, --summary Print the summary only +-S, --no-summary Don't print the summary -t, --transaction=t Search for transaction 't' in specified block range If no block number is specified, the current block is assumed @@ -42,128 +46,284 @@ If no block number is specified, the current block is assumed } } +class local_vars: pass + +class BlocksInfo: + + first = None + last = None + nblocks = None + sum_fs = '{:<15} {}\n' + + def __init__(self): + self.get_block_range() + self.post_init() + + def get_block_range(self): + + if not cmd_args: + first = last = c.blockcount + else: + arg = cmd_args[0] + if arg.startswith('+') and is_int(arg[1:]): + last = c.blockcount + first = last - int(arg[1:]) + 1 + elif is_int(arg): + first = last = int(arg) + else: + try: + first,last = [int(ep) for ep in arg.split('-')] + except: + opts.usage() + + if first > last: + die(2,f'{first}-{last}: invalid block range') + + if last > c.blockcount: + die(2,f'Requested block number ({last}) greater than current block height') + + self.first = first + self.last = last + self.nblocks = last - first + 1 + + def post_init(self): pass + + async def run(self): + + for height in range(self.first,self.last+1): + await self.process_block(height,await c.call('getblockhash',height)) + + return + + # WIP + heights = range(self.first,self.last+1) + hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) + print(hashes) + stats = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + pdie(stats) + + def print_header(self): pass + + async def print_summary(self): + if not opt.summary: + Msg('') + Msg_r( + self.sum_fs.format('Current height:', c.blockcount) + + self.sum_fs.format('Range:', f'{self.first}-{self.last} ({self.nblocks} blocks)') + ) + +class BlocksInfoOverview(BlocksInfo): + + total_bytes = 0 + t_start = None + t_prev = None + t_cur = None + + bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) + # bs=getblockstats(), bh=getblockheader() + # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' + fields = { + 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), + 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), + 'date': bf('', 'Date', '{:<19}', None, 'df', ['bs'],None), + 'interval': bf('Solve','Time ', '{:>5}', None, 'if', ['bs'],None), + 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), + 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), + 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), + 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],0), + 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],1), + 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],2), + 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],3), + 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],4), + 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), + 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), + 'totalfee': bf('', 'Total Fee',' {:>10}','totalfee', 'tf', ['bs'],None), + 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), + 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), + 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), + 'nTx': bf('', 'nTx ', '{:>5}', None, 'bh', [], 'nTx'), + 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), + } + dfl_fields = ['block','date','interval','subsidy','size','weight','fee50','fee25','fee10','version'] + funcs = { + 'df': lambda self,loc: time.strftime('%Y-%m-%d %X',time.gmtime(self.t_cur)), + 'if': lambda self,loc: '{:02}:{:02}'.format(loc.t_diff//60,loc.t_diff%60), + 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), + 'bh': lambda self,loc: c.call('getblockheader',loc.H), + 'fp': lambda self,loc: loc.bs['feerate_percentiles'], + 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0'), + } + + def __init__(self): + + super().__init__() + + if opt.fields: + fnames = opt.fields.split(',') + for n in fnames: + if n not in self.fields: + die(1,f'{n!r}: unrecognized field') + else: + fnames = self.dfl_fields + + self.fvals = list(self.fields[k] for k in fnames if k in self.fields) + self.fs = ' '.join( v.fs for v in self.fvals ) + hdr1 = [v.hdr1 for v in self.fvals] + hdr2 = [v.hdr2 for v in self.fvals] + self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) + + self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] + self.bs_keys.extend(['total_size','time']) + + self.ufuncs = {v.varname:self.funcs[v.varname] for v in self.fvals if v.varname in self.funcs} + + if opt.miner_info: + self.fs += ' {}' + hdr1.append(' ') + hdr2.append('Miner') + self.miner_pats = [re.compile(pat) for pat in ( + r'([a-zA-Z0-9. ]+/Mined by [a-zA-Z0-9. ]+)', + r'Mined by ([a-zA-Z0-9. ]+)', + r'/([a-zA-Z0-9&. ]+)/', + )] + else: + self.miner_pats = None + + if not opt.summary: + Msg(self.fs.format(*hdr1)) + Msg(self.fs.format(*hdr2)) + + async def get_miner_string(self,H): + tx0 = (await c.call('getblock',H))['tx'][0] + bd = await c.call('getrawtransaction',tx0,1) + if type(bd) == tuple: + ret = '---' + else: + cb = bytes.fromhex(bd['vin'][0]['coinbase']) + ret = ''.join(chr(b) for b in cb if 31 < b < 128) + if not opt.raw_miner_info: + for pat in self.miner_pats: + m = pat.search(ret) + if m: + ret = m[1] + break + return ret + + async def process_block(self,height,H): + loc = local_vars() + loc.H = H + loc.height = height + if 'bs' in self.deps: + loc.bs = await c.call('getblockstats',H,self.bs_keys) + #pdie(loc.bs) + self.total_bytes += loc.bs['total_size'] + self.t_cur = loc.bs['time'] + if self.t_prev == None: + if height == 0: + b_prev = loc.bs + else: + bh = await c.call('getblockhash',height-1) + b_prev = await c.call('getblockstats',bh) + self.t_start = self.t_prev = b_prev['time'] + loc.t_diff = self.t_cur - self.t_prev + self.t_prev = self.t_cur + + if opt.summary: + return + + for varname,func in self.ufuncs.items(): + ret = func(self,loc) + if type(ret).__name__ == 'coroutine': + ret = await ret + setattr(loc,varname,ret) + + if opt.miner_info: + miner_info = await self.get_miner_string(H) + + def gen(): + for v in self.fvals: + if v.key is None: + yield getattr(loc,v.varname) + else: + yield getattr(loc,v.varname)[v.key] + if opt.miner_info: + yield miner_info + + Msg(self.fs.format(*gen())) + + async def print_summary(self): + if 'bs' in self.deps: + await super().print_summary() + if self.nblocks > 1: + elapsed = self.t_cur - self.t_start + ac = int(elapsed / self.nblocks) + rate = (self.total_bytes / 10000) / (elapsed / 36) + Msg_r ( + self.sum_fs.format('Avg size:', f'{self.total_bytes//self.nblocks} bytes') + + self.sum_fs.format('MB/hr:', f'{rate:0.4f}') + + self.sum_fs.format('Avg conf time:', f'{ac//60}:{ac%60:02}') + ) + +class BlocksInfoTxFind(BlocksInfo): + + found_tx = False + + def post_init(self): + if len(opt.transaction) != 64 or not is_hex_str(opt.transaction): + die(2,f'{opt.transaction}: invalid transaction id') + + async def process_block(self,height,H): + if opt.transaction in (await c.call('getblock',H))['tx']: + Msg('\rRequested transaction is in block {} ({} confirmations)'.format(height,c.blockcount-height+1)) + return True + msg_r('\rChecking block {} '.format(height)) + + async def print_summary(self): + if self.found_tx: + try: + await c.call('getmempoolentry',opt.transaction) # ,on_fail='silent')): + except: + Msg('\rTransaction not found in block range {}-{} or in mempool'.format(self.first,self.last)) + else: + Msg('\rTransaction is in mempool') + + async def run(self): + for height in range(self.first,self.last+1): + H = await c.call('getblockhash',height) + if await self.process_block(height,H): # returns True when finished + break + else: + self.found_tx = True + +class BlocksInfoHashes(BlocksInfo): + + def print_header(self): + Msg('{:<7} {}'.format('BLOCK','HASH')) + + async def run(self): + heights = range(self.first,self.last+1) + hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) + Msg('\n'.join('{:<7} {}'.format(height,H) for height,H in zip(heights,hashes))) + cmd_args = opts.init(opts_data) -if len(cmd_args) not in (0,1): opts.usage() -if opt.raw_miner_info: opt.miner_info = True +if len(cmd_args) not in (0,1): + opts.usage() -c = rpc_init() -cur_blk = c.getblockcount() +async def main(): + from mmgen.rpc import rpc_init + global c + c = await rpc_init() -arg = cmd_args[0] if cmd_args else None - -if not arg: - first = last = cur_blk -elif arg[0] == '+' and is_int(arg[1:]): - last = cur_blk - first = last - int(arg[1:]) + 1 -elif is_int(arg): - first = last = int(arg) -else: - try: - a,b = arg.split('-') - assert is_int(a) and is_int(b) - first,last = int(a),int(b) - except: - opts.usage() - -if first > last: - die(2,'{}: invalid block range'.format(arg)) -if last > cur_blk: - die(2,'Requested block number ({}) greater than current block height'.format(last)) - -if opt.summary: - pass -elif opt.transaction: - if len(opt.transaction) != 64 or not is_hex_str(opt.transaction): - die(2,'{}: invalid transaction id'.format(opt.transaction)) -elif opt.hashes: - Msg('{:<7} {}'.format('BLOCK','HASH')) -else: - if opt.miner_info: - fs='{:<6} {:<19}{:>9} {:>6} {:>7} {:8} {}' - Msg(fs.format('BLOCK','DATE','INTERVAL','CONFS','SIZE','VERSION','MINER')) - else: - fs='{:<6} {:<19}{:>9} {:>6} {:>7} {}' - Msg(fs.format('BLOCK','DATE','INTERVAL','CONFS','SIZE','VERSION')) - -miner_strings = { - 'Bixin':'Bixin', - 'AntPool':'AntPool', - 'Bitfury':'Bitfury', - 'BTCC':'BTCC', - 'BTC.COM':'BTC.COM', - 'BTPOOL':'BTPOOL', - 'ViaBTC':'ViaBTC', - 'slush':'Slush', - 'BitMinter':'BitMinter', - 'BW.COM':'BW.COM', - 'gbminers':'GBMiners', - 'BitClub Network':'BitClub Network', - 'bitcoin.com':'bitcoin.com', - 'KanoPool':'KanoPool', - 'BTC.TOP':'BTC.TOP', -} - -total,prev_time = 0,None -for i in range(first,last+1): - b = c.getblock(c.getblockhash(i)) - total += b['size'] if opt.transaction: - if opt.transaction in b['tx']: - Msg('Requested transaction is in block {}'.format(i)) - sys.exit(0) - msg('Checking block {}'.format(i)) + m = BlocksInfoTxFind() + elif opt.hashes: + m = BlocksInfoHashes() else: - gmt = time.gmtime(b['mediantime']) - date = '{}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*gmt[:6]) - if prev_time == None: - b_prev = b if i == 0 else c.getblock(c.getblockhash(i-1)) - first_time = prev_time = b_prev['mediantime'] - et = b['mediantime'] - prev_time - prev_time = b['mediantime'] - if opt.summary: - continue - if opt.hashes: - Msg('{:<7} {}'.format(i,b['hash'])) - else: - d = (i,date,'{}:{:02}'.format(et//60,et%60),b['confirmations'],b['size'],b['versionHex']) - if opt.miner_info: - bd = c.getrawtransaction(b['tx'][0],1,on_fail='silent') - if type(bd) == tuple: - Msg(fs.format(*(d+('---',)))) - else: - cb = unhexlify(bd['vin'][0]['coinbase']) - scb = re.sub(b'[^\w /-:,;.]',b'',cb)[1:] - if opt.raw_miner_info: - Msg(fs.format(*(d+(scb.decode(),)))) - else: - for ms in miner_strings: - if ms.encode() in scb: - s = miner_strings[ms] - break - else: - try: s = scb.split('ined by')[1].strip() - except: s = '??' - nya_str = (b' ',b'NYA ')[b'NYA' in scb] if opt.nya else b'' - Msg(fs.format(*(d+(nya_str.decode()+s,)))) - else: - Msg(fs.format(*d)) + m = BlocksInfoOverview() -if opt.transaction: - from mmgen.rpc import rpc_error - if rpc_error(c.getmempoolentry(opt.transaction,on_fail='silent')): - Msg('\rTransaction not found in block range {}-{} or in mempool'.format(first,last)) - else: - Msg('\rTransaction is in mempool') -else: - blocks = last - first + 1 - if blocks > 1: - fs2 = '{:<15} {}\n' - et = b['mediantime'] - first_time - ac = int(et / blocks) - if not opt.summary: Msg('') - Msg_r( fs2.format('Range:','{}-{} ({} blocks)'.format(first,last,blocks)) + - fs2.format('Avg size:','{} bytes'.format(total//blocks)) + - fs2.format('MB/hr:','{:0.5f}'.format(float(total)*36/10000/et)) + - fs2.format('Avg conf time:','{}:{:02}'.format(ac//60,ac%60))) + m.print_header() + await m.run() + await m.print_summary() + +run_session(main()) From 9f801a9cb3ad1c30cc6cfded6282f6a161867918 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 12 May 2020 16:40:08 +0000 Subject: [PATCH 002/175] modified: mmgen/node_tools/Sound.py modified: mmgen/node_tools/Util.py modified: mmnode-netrate modified: mmnode-peerblocks modified: mmnode-play-sound --- mmgen/node_tools/Sound.py | 14 +-- mmgen/node_tools/Util.py | 1 + mmnode-netrate | 35 +++++--- mmnode-peerblocks | 176 +++++++++++++++++++++++--------------- mmnode-play-sound | 9 +- 5 files changed, 139 insertions(+), 96 deletions(-) diff --git a/mmgen/node_tools/Sound.py b/mmgen/node_tools/Sound.py index cd200e0..ba59a24 100644 --- a/mmgen/node_tools/Sound.py +++ b/mmgen/node_tools/Sound.py @@ -42,22 +42,22 @@ def parse_repeat_spec(rs): def init_sound(): def _restore_sound(): # msg('Restoring sound volume') - do_system('alsactl restore -f ' + _alsa_config_file) + do_system('sudo alsactl restore -f ' + _alsa_config_file) os.unlink(_alsa_config_file) import atexit atexit.register(_restore_sound) - do_system('alsactl store -f ' + _alsa_config_file) + do_system('sudo alsactl store -f ' + _alsa_config_file) def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False): if not remote_host: - do_system('alsactl store -f ' + _alsa_config_file) + do_system('sudo alsactl store -f ' + _alsa_config_file) for k in 'Master','Speaker','Headphone': - do_system(('amixer -q set %s on' % k),testing) + do_system(('sudo amixer -q set %s on' % k),testing) # do_system('amixer -q set Headphone off') vols = dict([(k,int(_dvols[k] * float(vol) / 100)) for k in _dvols]) for k in vols: - do_system('amixer -q set %s %s' % (k,vols[k]),testing) + do_system('sudo amixer -q set %s %s' % (k,vols[k]),testing) fn = os.path.expanduser(fn) cmd = ( @@ -72,9 +72,9 @@ def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False) do_system(cmd,testing) if kill_flg.wait(interval): if not remote_host: - do_system('alsactl restore -f ' + _alsa_config_file) + do_system('sudo alsactl restore -f ' + _alsa_config_file) return else: # Play once do_system(cmd,testing) if not remote_host: - do_system('alsactl restore -f ' + _alsa_config_file) + do_system('sudo alsactl restore -f ' + _alsa_config_file) diff --git a/mmgen/node_tools/Util.py b/mmgen/node_tools/Util.py index 1fcf34b..fb7e318 100644 --- a/mmgen/node_tools/Util.py +++ b/mmgen/node_tools/Util.py @@ -20,6 +20,7 @@ mmgen.node_tools.Util: utility functions for MMGen node tools """ import time,subprocess +from mmgen.util import msg def get_hms(t=None,utc=False,no_secs=False): secs = t or time.time() diff --git a/mmnode-netrate b/mmnode-netrate index 1266951..a21d7a7 100755 --- a/mmnode-netrate +++ b/mmnode-netrate @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2017 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -37,24 +37,33 @@ opts_data = { cmd_args = opts.init(opts_data) ERASE_LINE,CUR_UP = '\033[K','\033[1A' -c = rpc_init() -def do_loop(): - def get_data(): - d = c.getnettotals() +async def main(): + from mmgen.rpc import rpc_init + c = await rpc_init() + + async def get_data(): + d = await c.call('getnettotals') return [float(e) for e in (d['totalbytesrecv'],d['totalbytessent'],d['timemillis'])] - r,s,t = get_data() - time.sleep(0.2) + rs = None while True: - rs,ss,ts = r,s,t - r,s,t = get_data() - td = t-ts - sys.stderr.write('\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format((r-rs)/td,(s-ss)/td)) + r,s,t = await get_data() + + if rs is not None: + sys.stderr.write( + '\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format( + (r-rs)/(t-ts), + (s-ss)/(t-ts) )) + time.sleep(2) - sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE)) + + if rs is not None: + sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE)) + + rs,ss,ts = r,s,t try: - do_loop() + run_session(main(),do_rpc_init=False) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmnode-peerblocks b/mmnode-peerblocks index a3a2f30..71c2fae 100755 --- a/mmnode-peerblocks +++ b/mmnode-peerblocks @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2017 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -20,7 +20,7 @@ mmgen-peerblocks: List blocks in flight, disconnect stalling nodes """ -import time,threading +import asyncio from mmgen.common import * opts_data = { @@ -34,85 +34,119 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +opts.init(opts_data) -colors = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)] -_red,_reset = '\033[31m','\033[0m' +async def inflight_display(rpc): -ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = \ - '\033[J','\033[K','\033[H','\033[?25l','\033[?25h' + def gen_peers(peerinfo): + global min_height + min_height = None + for d in peerinfo: + if 'inflight' in d and d['inflight']: + blks = d['inflight'] + if not min_height or min_height > blks[0]: + min_height = blks[0] + blks_trunc = ' '.join(map(str,blks))[:term_width-6].split() + trim = blks_trunc[-1] != str(blks[len(blks_trunc)-1]) + blks = blks[:len(blks_trunc)-trim] + else: + blks = [] + yield { 'id': d['id'], 'data': blks } -import atexit -def at_exit(): - import os - os.system('stty sane') - sys.stderr.write('\n') -atexit.register(at_exit) + def gen_line(peer): + for blk in peer['data']: + if blk == min_height: + yield RED + str(blk) + RESET + else: + yield COLORS[blk % 10] + str(blk) + RESET -bc = rpc_init() - -msg_r(CUR_HOME+ERASE_ALL) - -def do_display(): from mmgen.term import get_terminal_size - global data + term_width = get_terminal_size()[0] + count = 1 while True: - twid = get_terminal_size()[0] - data = bc.getpeerinfo() - min_t = None - lines = [] - with lock: - msg('{}{}{}ACTIVE PEERS ({}) - poll {}'.format( - CUR_HOME,ERASE_ALL,CUR_HOME,len(data),count)) - for d in data: - line = { 'id': d['id'], 'data': [] } - if 'inflight' in d and d['inflight']: - blks = [str(e) for e in d['inflight']] - min_p = min(e for e in d['inflight']) - if not min_t or min_t > min_p: min_t = min_p - line_d = ' '.join(blks)[:twid-6] - blks = blks[:len(line_d) - len(line_d.replace(' ','')) + 1] - blks[-1] = blks[-1][:len(line_d.split(' ')[-1])] - line['data'] = [[colors[int(i)%10],i,_reset] for i in blks if i] - else: - line['data'] = [] - lines.append(line) - for line in lines: - d = ' '.join([(a,_red)[int(b)==min_t]+b+c for a,b,c in line['data']]) - sys.stderr.write('\r{} {:>3}: {}\n'.format(ERASE_LINE,line['id'],d)) - msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ') - time.sleep(2) + info = await rpc.call('getpeerinfo') + + msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) + msg(f'ACTIVE PEERS ({len(info)}) - poll {count}') + + for peer in gen_peers(info): + sys.stderr.write('\r{} {:>3}: {}\n'.format( + ERASE_LINE, + peer['id'], + ' '.join(gen_line(peer)) )) + + msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ') + await asyncio.sleep(2) count += 1 -lock = threading.Lock() -data = {} +async def do_inflight(rpc): + task = asyncio.ensure_future(inflight_display(rpc)) # Python 3.7+: create_task() + from select import select -t = threading.Thread(target=do_display,name='display') -t.daemon = True -t.start() - -def do_loop(): - global data while True: - input() - with lock: - ids = [str(d['id']) for d in data] - msg('{}{}{}ACTIVE PEERS ({})'.format(CUR_HOME,ERASE_ALL,CUR_HOME,len(data))) - msg(' '+'\n '.join(['{:>3}: {:30} {}'.format(*[d[k] for k in ('id','addr','subver')]) for d in data])) - reply = input('Enter a peer number to disconnect> ') - if reply == '': - pass - elif reply in ids: - idx = ids.index(reply) - msg("Disconnecting peer {} ('{}')".format(reply,data[idx]['addr'])) - bc.disconnectnode(data[idx]['addr']) - time.sleep(1.5) - else: - msg("'{}': invalid peer number".format(reply)) - time.sleep(0.5) + key = select([sys.stdin], [], [], 0.1)[0] + if key: + sys.stdin.read(1) + task.cancel() + break + await asyncio.sleep(0.1) + + try: + await task + except asyncio.CancelledError: + pass + +async def do_disconnect_menu(rpc): + + while True: + peerinfo = await rpc.call('getpeerinfo') + ids = [str(d['id']) for d in peerinfo] + + msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) + msg(f'ACTIVE PEERS ({len(peerinfo)})') + + if peerinfo: + msg('\n'.join([f"{d['id']:>3}: {d['addr']:30} {d['subver']}" for d in peerinfo])) + + reply = input("Peer number to disconnect, ENTER to quit menu, 'u' to update peer list> ") + + if reply == '': + return + elif reply == 'u': + msg(f'Updating peer list') + await asyncio.sleep(0.5) + elif reply in ids: + addr = peerinfo[ids.index(reply)]['addr'] + msg(f'Disconnecting peer {reply} ({addr})') + try: + await rpc.call('disconnectnode',addr) + except RPCFailure: + msg(f'Unable to disconnect peer {addr}') + await asyncio.sleep(1.5) + else: + msg(f'{reply!r}: invalid peer number') + await asyncio.sleep(0.5) + +async def main(): + + msg_r(CUR_HOME+ERASE_ALL) + + from mmgen.rpc import rpc_init + rpc = await rpc_init() + + while True: + await do_inflight(rpc) + await do_disconnect_menu(rpc) + +RED,RESET = ('\033[31m','\033[0m') +COLORS = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)] +ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = ( + '\033[J','\033[K','\033[H','\033[?25l','\033[?25h') try: - do_loop() -except KeyboardInterrupt: - pass + run_session(main(),do_rpc_init=False) +except: + from subprocess import run + run(['stty','sane']) + msg('') diff --git a/mmnode-play-sound b/mmnode-play-sound index 25b4fbc..133bed2 100755 --- a/mmnode-play-sound +++ b/mmnode-play-sound @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2016 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -32,6 +32,7 @@ opts_data = { 'usage': '[opts]', 'options': """ -h, --help Print this help message +-t, --testing Test, don't execute shell commands -v, --volume= n Adjust sound volume by percentage 'n' (default: {}) """.format(volume) } @@ -40,10 +41,8 @@ opts_data = { args = opts.init(opts_data) if opt.volume: - volume = opt.volume try: - volume = int(volume) - assert 1 <= volume <= 120 + assert 1 <= int(opt.volume) <= 120 except: die(1,'Sound volume must be an integer between 1 and 120') @@ -53,4 +52,4 @@ if len(args) != 1: try: os.stat(args[0]) except: die(1,"Couldn't stat file '{}'".format(args[0])) -play_sound(args[0],volume) +play_sound(args[0],int(opt.volume),testing=opt.testing) From 679f2d53b375193829ca4265840dc0b119ff4c18 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 13 May 2020 13:36:29 +0000 Subject: [PATCH 003/175] modified: mmnode-blocks-info modified: mmnode-peerblocks --- mmnode-blocks-info | 2 +- mmnode-peerblocks | 78 ++++++++++++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 9a43006..3b019e6 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -159,7 +159,7 @@ class BlocksInfoOverview(BlocksInfo): super().__init__() if opt.fields: - fnames = opt.fields.split(',') + fnames = opt.fields.split(',') for n in fnames: if n not in self.fields: die(1,f'{n!r}: unrecognized field') diff --git a/mmnode-peerblocks b/mmnode-peerblocks index 71c2fae..b699cba 100755 --- a/mmnode-peerblocks +++ b/mmnode-peerblocks @@ -21,6 +21,7 @@ mmgen-peerblocks: List blocks in flight, disconnect stalling nodes """ import asyncio +from collections import namedtuple from mmgen.common import * opts_data = { @@ -34,49 +35,53 @@ opts_data = { } } -opts.init(opts_data) +def format_peer_info(peerinfo): -async def inflight_display(rpc): + pd = namedtuple('peer_data',['id','blocks_data','screen_width']) def gen_peers(peerinfo): global min_height min_height = None for d in peerinfo: if 'inflight' in d and d['inflight']: - blks = d['inflight'] - if not min_height or min_height > blks[0]: - min_height = blks[0] - blks_trunc = ' '.join(map(str,blks))[:term_width-6].split() - trim = blks_trunc[-1] != str(blks[len(blks_trunc)-1]) - blks = blks[:len(blks_trunc)-trim] + blocks = d['inflight'] + if not min_height or min_height > blocks[0]: + min_height = blocks[0] + line = ' '.join(map(str,blocks))[:term_width - 2 - id_width] + blocks_disp = line.split() + yield pd( + d['id'], + [(blocks[i],blocks_disp[i]) for i in range(len(blocks_disp))], + len(line) ) else: - blks = [] - yield { 'id': d['id'], 'data': blks } + yield pd( d['id'], [], 0 ) def gen_line(peer): - for blk in peer['data']: + for blk,blk_disp in peer.blocks_data: if blk == min_height: - yield RED + str(blk) + RESET + yield RED + blk_disp + RESET else: - yield COLORS[blk % 10] + str(blk) + RESET + yield COLORS[blk % 10] + blk_disp + RESET - from mmgen.term import get_terminal_size - term_width = get_terminal_size()[0] + id_width = max(2,max(len(str(i['id'])) for i in peerinfo)) + + for peer in gen_peers(peerinfo): + line = '{:>{iw}}: {}'.format( + peer.id, + ' '.join(gen_line(peer)), + iw = id_width ) + yield line + ' ' * (term_width - 2 - id_width - peer.screen_width) + +async def inflight_display(rpc): count = 1 while True: info = await rpc.call('getpeerinfo') - - msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) - msg(f'ACTIVE PEERS ({len(info)}) - poll {count}') - - for peer in gen_peers(info): - sys.stderr.write('\r{} {:>3}: {}\n'.format( - ERASE_LINE, - peer['id'], - ' '.join(gen_line(peer)) )) - - msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ') + msg_r( + CUR_HOME + + f'ACTIVE PEERS ({len(info)}) Blocks in Flight - poll {count} \n' + + ('\n'.join(format_peer_info(info)) + '\n' if info else '') + + ERASE_ALL + 'Hit ENTER for disconnect menu: ' ) await asyncio.sleep(2) count += 1 @@ -103,13 +108,19 @@ async def do_disconnect_menu(rpc): peerinfo = await rpc.call('getpeerinfo') ids = [str(d['id']) for d in peerinfo] - msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) - msg(f'ACTIVE PEERS ({len(peerinfo)})') + msg(f'{CUR_HOME}ACTIVE PEERS ({len(peerinfo)}) Disconnect Menu' + ' '*16) + + def gen_peerinfo(): + for d in peerinfo: + line = f"{d['id']:>{id_width}}: {d['addr']:30} {d['subver']}" + yield line + ' ' * (term_width - len(line)) if peerinfo: - msg('\n'.join([f"{d['id']:>3}: {d['addr']:30} {d['subver']}" for d in peerinfo])) + id_width = max(2,max(len(str(i['id'])) for i in peerinfo)) + msg('\n'.join(gen_peerinfo())) - reply = input("Peer number to disconnect, ENTER to quit menu, 'u' to update peer list> ") + msg_r(ERASE_ALL) + reply = input("Type peer number to disconnect, ENTER to quit menu, 'u' to update peer list: ") if reply == '': return @@ -139,8 +150,13 @@ async def main(): await do_inflight(rpc) await do_disconnect_menu(rpc) +opts.init(opts_data) + +from mmgen.term import get_terminal_size +term_width = get_terminal_size()[0] + RED,RESET = ('\033[31m','\033[0m') -COLORS = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)] +COLORS = ['\033[38;5;%s;1m' % c for c in (247,248,249,250,251,252,253,254,255,231)] ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = ( '\033[J','\033[K','\033[H','\033[?25l','\033[?25h') From 3568682aea64f976847ac1a055571ef1a7eb4e8a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 16 May 2020 19:17:06 +0000 Subject: [PATCH 004/175] modified: mmnode-blocks-info modified: mmnode-peerblocks --- mmnode-blocks-info | 8 ++++---- mmnode-peerblocks | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 3b019e6..b7833a5 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -99,8 +99,8 @@ class BlocksInfo: heights = range(self.first,self.last+1) hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) print(hashes) - stats = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - pdie(stats) + header = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + pdie(header) def print_header(self): pass @@ -137,14 +137,14 @@ class BlocksInfoOverview(BlocksInfo): 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],4), 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), - 'totalfee': bf('', 'Total Fee',' {:>10}','totalfee', 'tf', ['bs'],None), + 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'],None), 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), 'nTx': bf('', 'nTx ', '{:>5}', None, 'bh', [], 'nTx'), 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), } - dfl_fields = ['block','date','interval','subsidy','size','weight','fee50','fee25','fee10','version'] + dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] funcs = { 'df': lambda self,loc: time.strftime('%Y-%m-%d %X',time.gmtime(self.t_cur)), 'if': lambda self,loc: '{:02}:{:02}'.format(loc.t_diff//60,loc.t_diff%60), diff --git a/mmnode-peerblocks b/mmnode-peerblocks index b699cba..0c43000 100755 --- a/mmnode-peerblocks +++ b/mmnode-peerblocks @@ -57,21 +57,28 @@ def format_peer_info(peerinfo): yield pd( d['id'], [], 0 ) def gen_line(peer): - for blk,blk_disp in peer.blocks_data: - if blk == min_height: - yield RED + blk_disp + RESET - else: + if peer.blocks_data: + if peer.blocks_data[0][0] == min_height: + yield RED + peer.blocks_data[0][1] + RESET + peer.blocks_data.pop(0) + for blk,blk_disp in peer.blocks_data: yield COLORS[blk % 10] + blk_disp + RESET id_width = max(2,max(len(str(i['id'])) for i in peerinfo)) - for peer in gen_peers(peerinfo): + for peer in tuple(gen_peers(peerinfo)): line = '{:>{iw}}: {}'.format( peer.id, ' '.join(gen_line(peer)), iw = id_width ) yield line + ' ' * (term_width - 2 - id_width - peer.screen_width) +def test_format(): + import json + info = json.loads(open('test_data/peerinfo.json').read()) + print('\n'.join(format_peer_info(info)) + '\n') + sys.exit(0) + async def inflight_display(rpc): count = 1 From d96011742bcee0f88678f2158195eaa479f295e7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 1 Jun 2020 20:29:58 +0000 Subject: [PATCH 005/175] modified: mmnode-blocks-info modified: mmnode-netrate modified: mmnode-peerblocks --- mmnode-blocks-info | 6 +++++- mmnode-netrate | 8 ++++++-- mmnode-peerblocks | 7 +++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index b7833a5..aeb925a 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -311,9 +311,13 @@ if len(cmd_args) not in (0,1): opts.usage() async def main(): + + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts() + from mmgen.rpc import rpc_init global c - c = await rpc_init() + c = await rpc_init(proto) if opt.transaction: m = BlocksInfoTxFind() diff --git a/mmnode-netrate b/mmnode-netrate index a21d7a7..8e436da 100755 --- a/mmnode-netrate +++ b/mmnode-netrate @@ -39,8 +39,12 @@ cmd_args = opts.init(opts_data) ERASE_LINE,CUR_UP = '\033[K','\033[1A' async def main(): + + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts() + from mmgen.rpc import rpc_init - c = await rpc_init() + c = await rpc_init(proto) async def get_data(): d = await c.call('getnettotals') @@ -64,6 +68,6 @@ async def main(): rs,ss,ts = r,s,t try: - run_session(main(),do_rpc_init=False) + run_session(main()) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmnode-peerblocks b/mmnode-peerblocks index 0c43000..54496ca 100755 --- a/mmnode-peerblocks +++ b/mmnode-peerblocks @@ -150,8 +150,11 @@ async def main(): msg_r(CUR_HOME+ERASE_ALL) + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts() + from mmgen.rpc import rpc_init - rpc = await rpc_init() + rpc = await rpc_init(proto) while True: await do_inflight(rpc) @@ -168,7 +171,7 @@ ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = ( '\033[J','\033[K','\033[H','\033[?25l','\033[?25h') try: - run_session(main(),do_rpc_init=False) + run_session(main()) except: from subprocess import run run(['stty','sane']) From 33c209b8501295b1c136f0cfefe21ee2d36d8865 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 5 Jun 2020 20:10:15 +0000 Subject: [PATCH 006/175] mmnode-blocks-info: support negative timestamp differences --- mmnode-blocks-info | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index aeb925a..6d38359 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -126,7 +126,7 @@ class BlocksInfoOverview(BlocksInfo): 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), 'date': bf('', 'Date', '{:<19}', None, 'df', ['bs'],None), - 'interval': bf('Solve','Time ', '{:>5}', None, 'if', ['bs'],None), + 'interval': bf('Solve','Time ', '{:>6}', None, 'if', ['bs'],None), 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), @@ -145,9 +145,12 @@ class BlocksInfoOverview(BlocksInfo): 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), } dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] + funcs = { 'df': lambda self,loc: time.strftime('%Y-%m-%d %X',time.gmtime(self.t_cur)), - 'if': lambda self,loc: '{:02}:{:02}'.format(loc.t_diff//60,loc.t_diff%60), + 'if': lambda self,loc: ( + '-{:02}:{:02}'.format(abs(loc.t_diff)//60,abs(loc.t_diff)%60) if loc.t_diff < 0 else + ' {:02}:{:02}'.format(loc.t_diff//60,loc.t_diff%60) ), 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), 'bh': lambda self,loc: c.call('getblockheader',loc.H), 'fp': lambda self,loc: loc.bs['feerate_percentiles'], From 641d3e742ea88331bd468bca24289f56de008f9c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 17 Jun 2020 09:10:56 +0000 Subject: [PATCH 007/175] mmnode-blocks-info: improve miner string parsing, add total weight to summary --- mmnode-blocks-info | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 6d38359..a5626c2 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -115,6 +115,7 @@ class BlocksInfo: class BlocksInfoOverview(BlocksInfo): total_bytes = 0 + total_weight = 0 t_start = None t_prev = None t_cur = None @@ -185,9 +186,12 @@ class BlocksInfoOverview(BlocksInfo): hdr1.append(' ') hdr2.append('Miner') self.miner_pats = [re.compile(pat) for pat in ( - r'([a-zA-Z0-9. ]+/Mined by [a-zA-Z0-9. ]+)', - r'Mined by ([a-zA-Z0-9. ]+)', - r'/([a-zA-Z0-9&. ]+)/', + rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', + rb'([a-zA-Z0-9&. -]+/Mined by [a-zA-Z0-9. ]+)', + rb'\x08/(.*Mined by [a-zA-Z0-9. ]+)', + rb'Mined by ([a-zA-Z0-9. ]+)', + rb'[/^]([a-zA-Z0-9&. /-]{5,})', + rb'[/^]([a-zA-Z0-9&. /-]+)/', )] else: self.miner_pats = None @@ -200,17 +204,16 @@ class BlocksInfoOverview(BlocksInfo): tx0 = (await c.call('getblock',H))['tx'][0] bd = await c.call('getrawtransaction',tx0,1) if type(bd) == tuple: - ret = '---' + return '---' else: cb = bytes.fromhex(bd['vin'][0]['coinbase']) - ret = ''.join(chr(b) for b in cb if 31 < b < 128) - if not opt.raw_miner_info: + if opt.raw_miner_info: + return repr(cb) + else: for pat in self.miner_pats: - m = pat.search(ret) + m = pat.search(cb) if m: - ret = m[1] - break - return ret + return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') async def process_block(self,height,H): loc = local_vars() @@ -220,6 +223,7 @@ class BlocksInfoOverview(BlocksInfo): loc.bs = await c.call('getblockstats',H,self.bs_keys) #pdie(loc.bs) self.total_bytes += loc.bs['total_size'] + self.total_weight += loc.bs['total_weight'] self.t_cur = loc.bs['time'] if self.t_prev == None: if height == 0: @@ -262,9 +266,10 @@ class BlocksInfoOverview(BlocksInfo): ac = int(elapsed / self.nblocks) rate = (self.total_bytes / 10000) / (elapsed / 36) Msg_r ( - self.sum_fs.format('Avg size:', f'{self.total_bytes//self.nblocks} bytes') + - self.sum_fs.format('MB/hr:', f'{rate:0.4f}') + - self.sum_fs.format('Avg conf time:', f'{ac//60}:{ac%60:02}') + self.sum_fs.format('Avg size: ', f'{self.total_bytes//self.nblocks} bytes') + + self.sum_fs.format('Avg weight: ', f'{self.total_weight//self.nblocks} bytes') + + self.sum_fs.format('MB/hr:', f'{rate:0.4f}') + + self.sum_fs.format('Avg conf time:', f'{ac//60}:{ac%60:02}') ) class BlocksInfoTxFind(BlocksInfo): From a320a084fbe2254da2c07022d36156246e0dbfea Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 19 Oct 2020 18:28:01 +0000 Subject: [PATCH 008/175] new file: mmnode-halving-calculator.py This commit requires an updated mmgen installation --- mmnode-halving-calculator.py | 182 +++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100755 mmnode-halving-calculator.py diff --git a/mmnode-halving-calculator.py b/mmnode-halving-calculator.py new file mode 100755 index 0000000..35edb97 --- /dev/null +++ b/mmnode-halving-calculator.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2020 The MMGen Project +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . + +""" +mmnode-halving-calculator: Estimate date(s) of future block subsidy halving(s) +""" + +import time +from decimal import Decimal +from mmgen.common import * + +bdr_proj = 9.95 + +opts.init({ + 'sets': [('mined',True,'list',True)], + 'text': { + 'desc': 'Estimate date(s) of future block subsidy halving(s)', + 'usage':'[opts]', + 'options': f""" +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-l, --list List historical and projected halvings +-m, --mined Same as above, plus list coins mined +-r, --bdr-proj=I Block discovery interval for projected halvings (default: + {bdr_proj:.5f} min) +-s, --sample-size=N Block range to calculate block discovery interval for next + halving estimate (default: dynamically calculated) +""" } +}) + +if opt.bdr_proj: + bdr_proj = float(opt.bdr_proj) + +def date(t): + return '{}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*time.gmtime(t)[:6]) + +def dhms(t): + t,neg = (-t,'-') if t < 0 else (t,' ') + return f'{neg}{t//60//60//24} days, {t//60//60%24:02}:{t//60%60:02}:{t%60:02} h/m/s' + +def time_diff_warning(t_diff): + if abs(t_diff) > 60*60: + print('Warning: block tip time is {} {} clock time!'.format( + dhms(abs(t_diff)), + ('behind','ahead of')[t_diff<0])) + +async def main(): + + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts() + + from mmgen.rpc import rpc_init + c = await rpc_init(proto) + + tip = await c.call('getblockcount') + assert tip > 1, 'block tip must be > 1' + remaining = proto.halving_interval - tip % proto.halving_interval + sample_size = int(opt.sample_size) if opt.sample_size else min(tip-1,max(remaining,144)) + + cur,old = await c.gathered_call('getblockstats',((tip,),(tip - sample_size,))) + + clock_time = int(time.time()) + time_diff_warning(clock_time - cur['time']) + bdr = (cur['time'] - old['time']) / sample_size + t_rem = remaining * int(bdr) + t_next = cur['time'] + t_rem + + if proto.name == 'BitcoinCash': + sub = proto.coin_amt(str(cur['subsidy'])) + else: + sub = cur['subsidy'] * proto.coin_amt.min_coin_unit + + def print_current_stats(): + print( + f'Current block: {tip:>7}\n' + f'Next halving block: {tip + remaining:>7}\n' + f'Halving interval: {proto.halving_interval:>7}\n' + f'Blocks since last halving: {proto.halving_interval - remaining:>7}\n' + f'Blocks until next halving: {remaining:>7}\n\n' + f'Current block subsidy: {str(sub).rstrip("0")} {proto.coin}\n' + f'Current block discovery interval (over last {sample_size} blocks): {bdr/60:0.2f} min\n\n' + f'Current clock time (UTC): {date(clock_time)}\n' + f'Est. halving date (UTC): {date(t_next)}\n' + f'Est. time until halving: {dhms(cur["time"] + t_rem - clock_time)}' + ) + + async def print_halvings(): + halving_blocknums = [i*proto.halving_interval for i in range(proto.max_halvings+1)][1:] + hist_halvings = await c.gathered_call('getblockstats',([(n,) for n in halving_blocknums if n <= tip])) + halving_secs = bdr_proj * 60 * proto.halving_interval + nhist = len(hist_halvings) + nSubsidy = int(proto.start_subsidy / proto.coin_amt.min_coin_unit) + + block0_hash = await c.call('getblockhash',0) + block0_date = (await c.call('getblock',block0_hash))['time'] + + def gen_data(): + total_mined = 0 + date = block0_date + for n,blk in enumerate(halving_blocknums): + mined = (nSubsidy >> n) * proto.halving_interval + if n == 0: + mined -= nSubsidy # subtract unspendable genesis block subsidy + total_mined += mined + sub = nSubsidy >> n+1 if n+1 < proto.max_halvings else 0 + bdi = ( + (hist_halvings[n]['time'] - date) / (proto.halving_interval * 60) if n < nhist + else bdr/60 if n == nhist + else bdr_proj + ) + date = ( + hist_halvings[n]['time'] if n < nhist + else t_next + int((n - nhist) * halving_secs) + ) + yield ( n, sub, blk, mined, total_mined, bdi, date ) + if sub == 0: + break + + fs = ( + ' {a:<7} {b:>8} {c:19}{d:2} {e:10} {f}', + ' {a:<7} {b:>8} {c:19}{d:2} {e:10} {f:17} {g:17} {h}' + )[bool(opt.mined)] + + print( + f'Historical/Estimated/Projected Halvings ({proto.coin}):\n\n' + + f' Sample size for next halving estimate (E): {sample_size} blocks\n' + + f' Block discovery interval for projected halvings (P): {bdr_proj:.5f} minutes\n\n' + + fs.format( + a = 'HALVING', + b = 'BLOCK', + c = 'DATE', + d = '', + e = f'BDI (min)', + f = f'SUBSIDY ({proto.coin})', + g = f'MINED ({proto.coin})', + h = f'TOTAL MINED ({proto.coin})' + ) + + '\n' + + fs.format( + a = '-' * 7, + b = '-' * 8, + c = '-' * 19, + d = '-' * 2, + e = '-' * 10, + f = '-' * 13, + g = '-' * 17, + h = '-' * 17 + ) + + '\n' + + '\n'.join(fs.format( + a = n + 1, + b = blk, + c = date(t), + d = ' P' if n > nhist else '' if n < nhist else ' E', + e = f'{bdr:8.5f}', + f = proto.coin_amt(sub,from_unit='satoshi').fmt(fs='2.8'), + g = proto.coin_amt(mined,from_unit='satoshi').fmt(fs='8.8'), + h = proto.coin_amt(total_mined,from_unit='satoshi').fmt(fs='8.8') + ) for n,sub,blk,mined,total_mined,bdr,t in gen_data()) + ) + + if opt.list: + await print_halvings() + else: + print_current_stats() + +run_session(main()) From 5c9437cd7063e396d8afa14018c500cd0a9ec97c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 23 Oct 2020 17:13:21 +0000 Subject: [PATCH 009/175] new file: mmnode-feeview modified: README.md renamed: mmnode-halving-calculator.py -> mmnode-halving-calculator This commit requires an updated mmgen installation --- README.md | 18 +- mmnode-feeview | 215 ++++++++++++++++++ ...calculator.py => mmnode-halving-calculator | 0 setup.py | 6 +- 4 files changed, 232 insertions(+), 7 deletions(-) create mode 100755 mmnode-feeview rename mmnode-halving-calculator.py => mmnode-halving-calculator (100%) diff --git a/README.md b/README.md index 78066b5..2fab8f7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,20 @@ # MMGen node tools -Optional helper programs for the [MMGen online/offline Bitcoin wallet][6] +Helper utilities for Bitcoin and forkcoin full nodes. -Included in: https://github.com/mmgen/MMGenLive +Requires modules from the [MMGen online/offline cryptocurrency wallet][6]. -Requires: https://github.com/mmgen/mmgen +Currently tested on Linux only. Some scripts may not work under Windows/MSYS2. -The node tools are Linux-only for now +## Install: + +First, install [MMGen][6]. + +Then, + + $ git clone https://github.com/mmgen/mmgen-node-tools + $ cd mmgen-node-tools + $ sudo ./setup.py install - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -15,5 +23,5 @@ The node tools are Linux-only for now Donate: 15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w [4]: https://bitcointalk.org/index.php?topic=567069.0 -[5]: https://github.com/mmgen/mmgen/wiki/MMGen-Signing-Key +[5]: https://github.com/mmgen/mmgen/wiki/MMGen-Signing-Keys [6]: https://github.com/mmgen/mmgen/ diff --git a/mmnode-feeview b/mmnode-feeview new file mode 100755 index 0000000..3e600d1 --- /dev/null +++ b/mmnode-feeview @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2020 The MMGen Project +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . + +""" +mmnode-feeview: Visualize the fee structure of a node’s mempool +""" + +from mmgen.common import * + +min_prec,max_prec,dfl_prec = (0,6,2) +fee_brackets = [ + 1, 2, 3, 4, 5, 6, + 8, 10, 12, 14, 16, 18, + 20, 25, 30, 35, 40, 45, + 50, 60, 70, 80, 90, + 100, 120, 140, 160, 180, + 200, 250, 300, 350, 400, 450, + 500, 600, 700, 800, 900, + 1000, 1200, 1400, 1600, 1800, + 2000, 2500, 3000, 3500, 4000, 4500, + 5000, 6000, 7000, 8000, 9000, + 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, + 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 2100000000000000, +] + +opts.init({ + 'sets': [ + ('detail',True,'ranges',True), + ('detail',True,'show_mb_col',True), + ('detail',True,'precision',6), + ], + 'text': { + 'desc': 'Visualize the fee structure of a node’s mempool', + 'usage':'[opts]', + 'options': f""" +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-c, --include-current Include current bracket’s TXs in cumulative MB value +-d, --detail Same as --ranges --show-mb-col --precision=6 +-e, --show-empty Show all fee brackets, including empty ones +-i, --ignore-below=B Ignore fee brackets with less than 'B' bytes of TXs +-l, --log Log JSON-RPC mempool data to 'mempool.json' +-p, --precision=P Use 'P' decimal points of precision for megabyte amts + (min: {min_prec}, max: {max_prec}, default: {dfl_prec}) +-r, --ranges Display fee brackets as ranges +-s, --show-mb-col Display column with each fee bracket’s megabyte count +-w, --width=W Force output width of 'W' columns (default: term width) +""", + 'notes': """ ++ By default, fee bracket row labels include only the top of the range. ++ By default, empty fee brackets are not displayed. ++ Mempool amounts are shown in decimal megabytes. ++ Values in the Total MB column are cumulative and represent megabytes of + transactions in the mempool with fees higher than the TOP of the current + fee bracket. To change this behavior, use the --include-current option. + +Note that there is no global mempool in Bitcoin, and your node’s mempool may +differ significantly from those of mining nodes depending on uptime and other +factors. +""" +} +}) + +if opt.ignore_below: + if opt.show_empty: + die(1,'Conflicting options: --ignore-below, --show-empty') + ignore_below = parse_bytespec(opt.ignore_below) + +if opt.precision: + precision = check_int_between(opt.precision,min_prec,max_prec,'--precision arg') +else: + precision = dfl_prec + +if opt.width: + width = check_int_between(opt.width,40,1024,'--width arg') +else: + from mmgen.term import get_terminal_size + width = get_terminal_size()[0] + +class fee_bracket: + def __init__(self,top,bottom): + self.top = top + self.bottom = bottom + self.tx_bytes = 0 + self.tx_bytes_cum = 0 + self.skip = False + +def get_fake_data(fn): # for debugging + import json + from mmgen.rpc import json_encoder + from decimal import Decimal + return json.loads(open(os.path.join(fn)).read(),parse_float=Decimal) + +def log(data,fn): + import json + from mmgen.rpc import json_encoder + open(fn,'w').write(json.dumps(data,cls=json_encoder,sort_keys=True,indent=4)) + +def create_data(coin_amt,mempool): + out = [fee_bracket(fee_brackets[i],fee_brackets[i-1] if i else 0) for i in range(len(fee_brackets))] + + # populate fee brackets: + for tx in mempool.values(): + fee = coin_amt(tx['fee']).toSatoshi() + vsize = tx['vsize'] + for bracket in out: + if fee / vsize < bracket.top: + bracket.tx_bytes += vsize + break + + # remove empty top brackets: + while out[-1].tx_bytes == 0: + out.pop() + + out.reverse() # cumulative totals and display are top-down + + # calculate cumulative byte totals, filter rows: + tBytes = 0 + for i in out: + if not (i.tx_bytes or opt.show_empty): + i.skip = True + if opt.ignore_below and i.tx_bytes < ignore_below: + i.skip = True + i.tx_bytes_cum = tBytes + tBytes += i.tx_bytes + + return out + +def print_header(host,blockcount): + print('MEMPOOL FEE STRUCTURE ({})\n{} UTC\nBlock {}'.format( + host, + make_timestr(), + blockcount, + )) + if opt.show_empty: + print('Displaying all fee brackets') + elif opt.ignore_below: + print('Ignoring fee brackets with less than {} bytes ({})'.format( + ignore_below, + int2bytespec(ignore_below,'MB','0.6'), + )) + if opt.include_current: + print('Including transactions in current fee bracket in Total MB amounts') + +def fmt_mb(n): + return int2bytespec(n,'MB',f'0.{precision}',False) + +def print_body(data): + tx_bytes_max = max(i.tx_bytes for i in data) + top_max = max(i.top for i in data if not i.skip) + bot_max = max(i.bottom for i in data if not i.skip) + col1_w = max(len(f'{bot_max}-{top_max}') if opt.ranges else len(f'{top_max}'),6) + col2_w = len(fmt_mb(tx_bytes_max)) if opt.show_mb_col else 0 + col3_w = len(fmt_mb(data[-1].tx_bytes_cum)) + col4_w = width - col1_w - col2_w - col3_w - (4 if col2_w else 3) + if opt.show_mb_col: + fs = '{a:<%i} {b:>%i} {c:>%i} {d}' % (col1_w,col2_w,col3_w) + else: + fs = '{a:<%i} {c:>%i} {d}' % (col1_w,col3_w) + + print( + '\n' + fs.format(a='', b='', c=f'{"Total":<{col3_w}}', d='') + + '\n' + fs.format(a='sat/B', b=f'{"MB":<{col2_w}}', c=f'{"MB":<{col3_w}}', d='') + ) + + for i in data: + if not i.skip: + cum_bytes = i.tx_bytes_cum + i.tx_bytes if opt.include_current else i.tx_bytes_cum + print(fs.format( + a = '{}-{}'.format(i.bottom,i.top) if opt.ranges else i.top, + b = fmt_mb(i.tx_bytes), + c = fmt_mb(cum_bytes), + d = '-' * int(col4_w * ( i.tx_bytes / tx_bytes_max )) )) + + print(fs.format( + a = 'TOTAL', + b = '', + c = fmt_mb(data[-1].tx_bytes_cum + data[-1].tx_bytes), + d = '' )) + +async def main(): + + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts() + + from mmgen.rpc import rpc_init + c = await rpc_init(proto) + +# pmsg(await c.call('getmempoolinfo')) + mempool = await c.call('getrawmempool',True) +# mempool = get_fake_data('test_data/mempool-sample.json') + + if opt.log: + log(mempool,'mempool.json') + + data = create_data(proto.coin_amt,mempool) + print_header(c.host,await c.call('getblockcount')) + print_body(data) + +run_session(main()) diff --git a/mmnode-halving-calculator.py b/mmnode-halving-calculator similarity index 100% rename from mmnode-halving-calculator.py rename to mmnode-halving-calculator diff --git a/setup.py b/setup.py index d293ab9..995605a 100755 --- a/setup.py +++ b/setup.py @@ -40,10 +40,12 @@ setup( keywords = g.keywords, packages = ['mmgen.node_tools'], scripts = [ - 'mmnode-play-sound', + 'mmnode-blocks-info', + 'mmnode-feeview', + 'mmnode-halving-calculator', 'mmnode-netrate', 'mmnode-peerblocks', - 'mmnode-blocks-info', + 'mmnode-play-sound', ], data_files = [('share/mmgen/node_tools/audio', [ 'data_files/audio/ringtone.wav', # source files must have 0644 mode From 48ec09f6faaa70db261c4438b1c2511d03634a94 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 23 Oct 2020 19:13:08 +0000 Subject: [PATCH 010/175] mmnode-feeview: add --pager option --- mmnode-feeview | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/mmnode-feeview b/mmnode-feeview index 3e600d1..85dbcb2 100755 --- a/mmnode-feeview +++ b/mmnode-feeview @@ -57,6 +57,7 @@ opts.init({ -l, --log Log JSON-RPC mempool data to 'mempool.json' -p, --precision=P Use 'P' decimal points of precision for megabyte amts (min: {min_prec}, max: {max_prec}, default: {dfl_prec}) +-P, --pager Pipe the output to a pager -r, --ranges Display fee brackets as ranges -s, --show-mb-col Display column with each fee bracket’s megabyte count -w, --width=W Force output width of 'W' columns (default: term width) @@ -141,26 +142,26 @@ def create_data(coin_amt,mempool): return out -def print_header(host,blockcount): - print('MEMPOOL FEE STRUCTURE ({})\n{} UTC\nBlock {}'.format( +def gen_header(host,blockcount): + yield('MEMPOOL FEE STRUCTURE ({})\n{} UTC\nBlock {}'.format( host, make_timestr(), blockcount, )) if opt.show_empty: - print('Displaying all fee brackets') + yield('Displaying all fee brackets') elif opt.ignore_below: - print('Ignoring fee brackets with less than {} bytes ({})'.format( + yield('Ignoring fee brackets with less than {} bytes ({})'.format( ignore_below, int2bytespec(ignore_below,'MB','0.6'), )) if opt.include_current: - print('Including transactions in current fee bracket in Total MB amounts') + yield('Including transactions in current fee bracket in Total MB amounts') def fmt_mb(n): return int2bytespec(n,'MB',f'0.{precision}',False) -def print_body(data): +def gen_body(data): tx_bytes_max = max(i.tx_bytes for i in data) top_max = max(i.top for i in data if not i.skip) bot_max = max(i.bottom for i in data if not i.skip) @@ -173,7 +174,7 @@ def print_body(data): else: fs = '{a:<%i} {c:>%i} {d}' % (col1_w,col3_w) - print( + yield( '\n' + fs.format(a='', b='', c=f'{"Total":<{col3_w}}', d='') + '\n' + fs.format(a='sat/B', b=f'{"MB":<{col2_w}}', c=f'{"MB":<{col3_w}}', d='') ) @@ -181,13 +182,13 @@ def print_body(data): for i in data: if not i.skip: cum_bytes = i.tx_bytes_cum + i.tx_bytes if opt.include_current else i.tx_bytes_cum - print(fs.format( + yield(fs.format( a = '{}-{}'.format(i.bottom,i.top) if opt.ranges else i.top, b = fmt_mb(i.tx_bytes), c = fmt_mb(cum_bytes), d = '-' * int(col4_w * ( i.tx_bytes / tx_bytes_max )) )) - print(fs.format( + yield(fs.format( a = 'TOTAL', b = '', c = fmt_mb(data[-1].tx_bytes_cum + data[-1].tx_bytes), @@ -209,7 +210,9 @@ async def main(): log(mempool,'mempool.json') data = create_data(proto.coin_amt,mempool) - print_header(c.host,await c.call('getblockcount')) - print_body(data) + (do_pager if opt.pager else print)( + '\n'.join(gen_header(c.host,await c.call('getblockcount'))) + '\n' + + '\n'.join(gen_body(data)) + ) run_session(main()) From 7af544b6aab419d2a04fd83a8dd23e9d49189bcb Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 28 Nov 2020 19:19:10 +0300 Subject: [PATCH 011/175] deleted: mmnode-play-sound --- mmnode-play-sound | 55 ----------------------------------------------- setup.py | 1 - 2 files changed, 56 deletions(-) delete mode 100755 mmnode-play-sound diff --git a/mmnode-play-sound b/mmnode-play-sound deleted file mode 100755 index 133bed2..0000000 --- a/mmnode-play-sound +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2020 The MMGen Project -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -""" -mmnode-play-sound: play a sound with controlled volume -""" - -import sys -from mmgen.common import * -from mmgen.util import die -from mmgen.node_tools.Sound import * -volume = 100 - -opts_data = { - 'text': { - 'desc': 'Play a sound file at controlled volume', - 'usage': '[opts]', - 'options': """ --h, --help Print this help message --t, --testing Test, don't execute shell commands --v, --volume= n Adjust sound volume by percentage 'n' (default: {}) -""".format(volume) - } -} - -args = opts.init(opts_data) - -if opt.volume: - try: - assert 1 <= int(opt.volume) <= 120 - except: - die(1,'Sound volume must be an integer between 1 and 120') - -if len(args) != 1: - die(1,'You must supply a sound file') - -try: os.stat(args[0]) -except: die(1,"Couldn't stat file '{}'".format(args[0])) - -play_sound(args[0],int(opt.volume),testing=opt.testing) diff --git a/setup.py b/setup.py index 995605a..6f6f9a4 100755 --- a/setup.py +++ b/setup.py @@ -45,7 +45,6 @@ setup( 'mmnode-halving-calculator', 'mmnode-netrate', 'mmnode-peerblocks', - 'mmnode-play-sound', ], data_files = [('share/mmgen/node_tools/audio', [ 'data_files/audio/ringtone.wav', # source files must have 0644 mode From 4da65900fdde33729009545d0fc7c83424d856ca Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 12 Mar 2021 09:32:25 +0000 Subject: [PATCH 012/175] new utility: mmnode-txfind --- mmnode-blocks-info | 40 ++------------------ mmnode-txfind | 94 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 37 deletions(-) create mode 100755 mmnode-txfind diff --git a/mmnode-blocks-info b/mmnode-blocks-info index a5626c2..774de0d 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2020 The MMGen Project +# Copyright (C)2013-2021 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -28,7 +28,7 @@ from decimal import Decimal opts_data = { 'sets': [('raw_miner_info',True,'miner_info',True)], 'text': { - 'desc': 'Display information about or find a transaction within a range of blocks', + 'desc': 'Display information about a range of blocks', 'usage': '[opts] +||', 'options': """ -h, --help Print this help message @@ -39,7 +39,6 @@ opts_data = { -o, --fields= Display the specified fields -s, --summary Print the summary only -S, --no-summary Don't print the summary --t, --transaction=t Search for transaction 't' in specified block range If no block number is specified, the current block is assumed """ @@ -272,37 +271,6 @@ class BlocksInfoOverview(BlocksInfo): self.sum_fs.format('Avg conf time:', f'{ac//60}:{ac%60:02}') ) -class BlocksInfoTxFind(BlocksInfo): - - found_tx = False - - def post_init(self): - if len(opt.transaction) != 64 or not is_hex_str(opt.transaction): - die(2,f'{opt.transaction}: invalid transaction id') - - async def process_block(self,height,H): - if opt.transaction in (await c.call('getblock',H))['tx']: - Msg('\rRequested transaction is in block {} ({} confirmations)'.format(height,c.blockcount-height+1)) - return True - msg_r('\rChecking block {} '.format(height)) - - async def print_summary(self): - if self.found_tx: - try: - await c.call('getmempoolentry',opt.transaction) # ,on_fail='silent')): - except: - Msg('\rTransaction not found in block range {}-{} or in mempool'.format(self.first,self.last)) - else: - Msg('\rTransaction is in mempool') - - async def run(self): - for height in range(self.first,self.last+1): - H = await c.call('getblockhash',height) - if await self.process_block(height,H): # returns True when finished - break - else: - self.found_tx = True - class BlocksInfoHashes(BlocksInfo): def print_header(self): @@ -327,9 +295,7 @@ async def main(): global c c = await rpc_init(proto) - if opt.transaction: - m = BlocksInfoTxFind() - elif opt.hashes: + if opt.hashes: m = BlocksInfoHashes() else: m = BlocksInfoOverview() diff --git a/mmnode-txfind b/mmnode-txfind new file mode 100755 index 0000000..b794b2e --- /dev/null +++ b/mmnode-txfind @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2021 The MMGen Project +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . + +""" +mmgen-txfind: Find a transaction in the blockchain or mempool +""" + +from mmgen.common import * + +opts_data = { + 'text': { + 'desc': 'Find a transaction in the blockchain or mempool', + 'usage': '[opts] ', + 'options': """ +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-q, --quiet Be quieter +-v, --verbose Be more verbose +""", + 'notes': """ +If transaction is in blockchain, the block number and number of confirmations +are displayed. + +Requires --txindex for correct operation. +""" + } +} + +msg_data = { + 'normal': { + 'none': 'Transaction not found in blockchain or mempool', + 'block': 'Transaction is in block {b} ({c} confirmations)', + 'mem': 'Transaction is in mempool', + }, + 'quiet': { + 'none': 'None', + 'block': '{b} {c}', + 'mem': 'mempool', + } +} + +async def main(txid): + if len(txid) != 64 or not is_hex_str(txid): + die(2,f'{txid}: invalid transaction ID') + + if opt.verbose: + msg(f'TxID: {txid}') + + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts() + + from mmgen.rpc import rpc_init + c = await rpc_init(proto) + + exitval = 0 + try: + tip1 = await c.call('getblockcount') + ret = await c.call('getrawtransaction',txid,True) + tip2 = await c.call('getblockcount') + except: + Msg('\r' + msgs['none']) + exitval = 1 + else: + assert tip1 == tip2, 'Blockchain is updating. Try again later' + if 'confirmations' in ret: + confs = ret['confirmations'] + Msg('\r' + msgs['block'].format(b = tip1 - confs + 1, c = confs)) + else: + Msg('\r' + msgs['mem']) + + return exitval + +cmd_args = opts.init(opts_data) +msgs = msg_data['quiet' if opt.quiet else 'normal'] + +if len(cmd_args) != 1: + die(1,'One transaction ID must be specified') + +sys.exit(run_session(main(cmd_args[0]))) From 1614a269941c6bfc114c9cdfb2373b9322783919 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 12 Mar 2021 10:32:25 +0000 Subject: [PATCH 013/175] mmnode-blocks-info: add difficulty adjustment estimate, --no-header option --- mmnode-blocks-info | 67 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 774de0d..884ec0c 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -36,11 +36,20 @@ opts_data = { -H, --hashes Display only block numbers and hashes -m, --miner-info Display miner info in coinbase transaction -M, --raw-miner-info Display miner info in uninterpreted form +-n, --no-header Don't print the column header -o, --fields= Display the specified fields -s, --summary Print the summary only -S, --no-summary Don't print the summary +""", + 'notes': """ +If no block number is specified, the current block is assumed. -If no block number is specified, the current block is assumed +In addition to information about the requested range of blocks, an estimate +of the next difficulty adjustment is also displayed based on the average +Block Discovery Interval from the beginning of the current 2016-block period +to the chain tip. + +Requires --txindex for correct operation. """ } } @@ -52,7 +61,6 @@ class BlocksInfo: first = None last = None nblocks = None - sum_fs = '{:<15} {}\n' def __init__(self): self.get_block_range() @@ -104,12 +112,37 @@ class BlocksInfo: def print_header(self): pass async def print_summary(self): + from mmgen.util import secs_to_hms if not opt.summary: Msg('') - Msg_r( - self.sum_fs.format('Current height:', c.blockcount) + - self.sum_fs.format('Range:', f'{self.first}-{self.last} ({self.nblocks} blocks)') - ) + + tip = c.blockcount + rel = tip % 2016 + + if rel: + H1,H2,HA,HB = await c.gathered_call('getblockhash',[[self.first],[self.last],[tip-rel],[tip]]) + h1,h2,hA,hB = await c.gathered_call('getblockheader',[[H1],[H2],[HA],[HB]]) + bdi = (hB['time']-hA['time']) / rel + adj_pct = ((600 / bdi) - 1) * 100 + Msg_r(fmt(f""" + Current height: {tip} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) + BDI (cur period): {bdi/60:.2f} min + Est. diff adjust: {adj_pct:+.2f}% + """)) + else: + H1,H2 = await c.gathered_call('getblockhash',[[self.first],[self.last]]) + h1,h2 = await c.gathered_call('getblockheader',[[H1],[H2]]) + Msg_r(fmt(f""" + Current height: {tip} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) + """)) + + Msg('\nRange: {}-{} ({} blocks [{}])'.format( + self.first, + self.last, + self.nblocks, + secs_to_hms(h2['time'] - h1['time']) )) class BlocksInfoOverview(BlocksInfo): @@ -195,7 +228,7 @@ class BlocksInfoOverview(BlocksInfo): else: self.miner_pats = None - if not opt.summary: + if not (opt.summary or opt.no_header): Msg(self.fs.format(*hdr1)) Msg(self.fs.format(*hdr2)) @@ -264,12 +297,12 @@ class BlocksInfoOverview(BlocksInfo): elapsed = self.t_cur - self.t_start ac = int(elapsed / self.nblocks) rate = (self.total_bytes / 10000) / (elapsed / 36) - Msg_r ( - self.sum_fs.format('Avg size: ', f'{self.total_bytes//self.nblocks} bytes') + - self.sum_fs.format('Avg weight: ', f'{self.total_weight//self.nblocks} bytes') + - self.sum_fs.format('MB/hr:', f'{rate:0.4f}') + - self.sum_fs.format('Avg conf time:', f'{ac//60}:{ac%60:02}') - ) + Msg_r (fmt(f""" + Avg size: {self.total_bytes//self.nblocks} bytes + Avg weight: {self.total_weight//self.nblocks} bytes + MB/hr: {rate:0.4f} + Avg BDI: {ac/60:.2f} min + """)) class BlocksInfoHashes(BlocksInfo): @@ -300,8 +333,12 @@ async def main(): else: m = BlocksInfoOverview() - m.print_header() + if not opt.no_header: + m.print_header() + await m.run() - await m.print_summary() + + if not opt.no_summary: + await m.print_summary() run_session(main()) From 089a844fb6f005cbd5608161771c4c055e4df95d Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 12 Mar 2021 11:31:35 +0000 Subject: [PATCH 014/175] mmnode-blocks-info: code fixes, cleanups and speedups --- mmnode-blocks-info | 55 ++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 884ec0c..d2108d4 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -26,7 +26,11 @@ from mmgen.common import * from decimal import Decimal opts_data = { - 'sets': [('raw_miner_info',True,'miner_info',True)], + 'sets': [ + ('raw_miner_info',True,'miner_info',True), + ('summary',True,'raw_miner_info',False), + ('summary',True,'miner_info',False) + ], 'text': { 'desc': 'Display information about a range of blocks', 'usage': '[opts] +||', @@ -97,25 +101,24 @@ class BlocksInfo: async def run(self): - for height in range(self.first,self.last+1): - await self.process_block(height,await c.call('getblockhash',height)) - - return - - # WIP heights = range(self.first,self.last+1) hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) - print(hashes) - header = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - pdie(header) + hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - def print_header(self): pass + for height in heights: + await self.process_block(height,hashes.pop(0),hdrs.pop(0)) + + def print_header(self): + hdr1 = [v.hdr1 for v in self.fvals] + hdr2 = [v.hdr2 for v in self.fvals] + if opt.miner_info: + hdr1.append(' ') + hdr2.append('Miner') + Msg(self.fs.format(*hdr1)) + Msg(self.fs.format(*hdr2)) async def print_summary(self): from mmgen.util import secs_to_hms - if not opt.summary: - Msg('') - tip = c.blockcount rel = tip % 2016 @@ -185,7 +188,7 @@ class BlocksInfoOverview(BlocksInfo): '-{:02}:{:02}'.format(abs(loc.t_diff)//60,abs(loc.t_diff)%60) if loc.t_diff < 0 else ' {:02}:{:02}'.format(loc.t_diff//60,loc.t_diff%60) ), 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), - 'bh': lambda self,loc: c.call('getblockheader',loc.H), + 'bh': lambda self,loc: loc.hdr, 'fp': lambda self,loc: loc.bs['feerate_percentiles'], 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0'), } @@ -204,8 +207,6 @@ class BlocksInfoOverview(BlocksInfo): self.fvals = list(self.fields[k] for k in fnames if k in self.fields) self.fs = ' '.join( v.fs for v in self.fvals ) - hdr1 = [v.hdr1 for v in self.fvals] - hdr2 = [v.hdr2 for v in self.fvals] self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] @@ -215,8 +216,6 @@ class BlocksInfoOverview(BlocksInfo): if opt.miner_info: self.fs += ' {}' - hdr1.append(' ') - hdr2.append('Miner') self.miner_pats = [re.compile(pat) for pat in ( rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', rb'([a-zA-Z0-9&. -]+/Mined by [a-zA-Z0-9. ]+)', @@ -228,10 +227,6 @@ class BlocksInfoOverview(BlocksInfo): else: self.miner_pats = None - if not (opt.summary or opt.no_header): - Msg(self.fs.format(*hdr1)) - Msg(self.fs.format(*hdr2)) - async def get_miner_string(self,H): tx0 = (await c.call('getblock',H))['tx'][0] bd = await c.call('getrawtransaction',tx0,1) @@ -247,13 +242,13 @@ class BlocksInfoOverview(BlocksInfo): if m: return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') - async def process_block(self,height,H): + async def process_block(self,height,H,hdr): loc = local_vars() - loc.H = H loc.height = height + loc.H = H + loc.hdr = hdr if 'bs' in self.deps: loc.bs = await c.call('getblockstats',H,self.bs_keys) - #pdie(loc.bs) self.total_bytes += loc.bs['total_size'] self.total_weight += loc.bs['total_weight'] self.t_cur = loc.bs['time'] @@ -261,8 +256,8 @@ class BlocksInfoOverview(BlocksInfo): if height == 0: b_prev = loc.bs else: - bh = await c.call('getblockhash',height-1) - b_prev = await c.call('getblockstats',bh) + bH = await c.call('getblockhash',height-1) + b_prev = await c.call('getblockstats',bH) self.t_start = self.t_prev = b_prev['time'] loc.t_diff = self.t_cur - self.t_prev self.t_prev = self.t_cur @@ -333,12 +328,14 @@ async def main(): else: m = BlocksInfoOverview() - if not opt.no_header: + if not (opt.summary or opt.no_header): m.print_header() await m.run() if not opt.no_summary: + if not opt.summary: + Msg('') await m.print_summary() run_session(main()) From ef4696e297656c4ef305d2fafd20823ef2c12394 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 12 Mar 2021 18:20:44 +0000 Subject: [PATCH 015/175] docstrings, copyright dates --- mmnode-blocks-info | 2 +- mmnode-feeview | 2 +- mmnode-halving-calculator | 2 +- mmnode-netrate | 4 ++-- mmnode-peerblocks | 4 ++-- mmnode-txfind | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index d2108d4..21a9e9b 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -17,7 +17,7 @@ # this program. If not, see . """ -mmgen-blocks-info: Display information about a block or range of blocks +mmnode-blocks-info: Display information about a block or range of blocks """ import time,re diff --git a/mmnode-feeview b/mmnode-feeview index 85dbcb2..8e867fb 100755 --- a/mmnode-feeview +++ b/mmnode-feeview @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2020 The MMGen Project +# Copyright (C)2013-2021 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software diff --git a/mmnode-halving-calculator b/mmnode-halving-calculator index 35edb97..b191411 100755 --- a/mmnode-halving-calculator +++ b/mmnode-halving-calculator @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2020 The MMGen Project +# Copyright (C)2013-2021 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software diff --git a/mmnode-netrate b/mmnode-netrate index 8e436da..b1fb919 100755 --- a/mmnode-netrate +++ b/mmnode-netrate @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2020 The MMGen Project +# Copyright (C)2013-2021 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -17,7 +17,7 @@ # this program. If not, see . """ -mmgen-netrate: Bitcoin daemon network rate monitor +mmnode-netrate: Bitcoin daemon network rate monitor """ import time diff --git a/mmnode-peerblocks b/mmnode-peerblocks index 54496ca..87df582 100755 --- a/mmnode-peerblocks +++ b/mmnode-peerblocks @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2020 The MMGen Project +# Copyright (C)2013-2021 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -17,7 +17,7 @@ # this program. If not, see . """ -mmgen-peerblocks: List blocks in flight, disconnect stalling nodes +mmnode-peerblocks: List blocks in flight, disconnect stalling nodes """ import asyncio diff --git a/mmnode-txfind b/mmnode-txfind index b794b2e..3c7935d 100755 --- a/mmnode-txfind +++ b/mmnode-txfind @@ -17,7 +17,7 @@ # this program. If not, see . """ -mmgen-txfind: Find a transaction in the blockchain or mempool +mmnode-txfind: Find a transaction in the blockchain or mempool """ from mmgen.common import * From 019dbe52711b275d79ca1dd5088d3e7d6c61c162 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 12 Mar 2021 18:27:37 +0000 Subject: [PATCH 016/175] mmgen-blocks-info: cleanups, fix off-by-one error in t_start var --- mmnode-blocks-info | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 21a9e9b..b2dd4b6 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -123,8 +123,8 @@ class BlocksInfo: rel = tip % 2016 if rel: - H1,H2,HA,HB = await c.gathered_call('getblockhash',[[self.first],[self.last],[tip-rel],[tip]]) - h1,h2,hA,hB = await c.gathered_call('getblockheader',[[H1],[H2],[HA],[HB]]) + HA,HB = await c.gathered_call('getblockhash',([tip-rel],[tip])) + hA,hB = await c.gathered_call('getblockheader',([HA],[HB])) bdi = (hB['time']-hA['time']) / rel adj_pct = ((600 / bdi) - 1) * 100 Msg_r(fmt(f""" @@ -134,8 +134,6 @@ class BlocksInfo: Est. diff adjust: {adj_pct:+.2f}% """)) else: - H1,H2 = await c.gathered_call('getblockhash',[[self.first],[self.last]]) - h1,h2 = await c.gathered_call('getblockheader',[[H1],[H2]]) Msg_r(fmt(f""" Current height: {tip} Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) @@ -145,7 +143,7 @@ class BlocksInfo: self.first, self.last, self.nblocks, - secs_to_hms(h2['time'] - h1['time']) )) + secs_to_hms(self.t_cur - self.t_start) )) class BlocksInfoOverview(BlocksInfo): @@ -252,13 +250,14 @@ class BlocksInfoOverview(BlocksInfo): self.total_bytes += loc.bs['total_size'] self.total_weight += loc.bs['total_weight'] self.t_cur = loc.bs['time'] - if self.t_prev == None: + if self.t_start == None: if height == 0: b_prev = loc.bs else: bH = await c.call('getblockhash',height-1) b_prev = await c.call('getblockstats',bH) - self.t_start = self.t_prev = b_prev['time'] + self.t_start = loc.bs['time'] + self.t_prev = b_prev['time'] loc.t_diff = self.t_cur - self.t_prev self.t_prev = self.t_cur From 97ae5576819b0afad1916dc3c61317cf0469ce2c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 13 Mar 2021 16:47:47 +0000 Subject: [PATCH 017/175] mmgen-blocks-info: whitespace --- mmnode-blocks-info | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index b2dd4b6..a34d6fa 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -157,26 +157,26 @@ class BlocksInfoOverview(BlocksInfo): # bs=getblockstats(), bh=getblockheader() # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' fields = { - 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), - 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), - 'date': bf('', 'Date', '{:<19}', None, 'df', ['bs'],None), - 'interval': bf('Solve','Time ', '{:>6}', None, 'if', ['bs'],None), - 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), - 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), - 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), - 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],0), - 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],1), - 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],2), - 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],3), - 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'],4), - 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), - 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), - 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'],None), - 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), - 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), - 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), - 'nTx': bf('', 'nTx ', '{:>5}', None, 'bh', [], 'nTx'), - 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), + 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), + 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), + 'date': bf('', 'Date', '{:<19}', None, 'df', ['bs'], None), + 'interval': bf('Solve','Time ', '{:>6}', None, 'if', ['bs'], None), + 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), + 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), + 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), + 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 0), + 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 1), + 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 2), + 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 3), + 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 4), + 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), + 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), + 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), + 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), + 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), + 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), + 'nTx': bf('', 'nTx ', '{:>5}', None, 'bh', [], 'nTx'), + 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), } dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] From 28e72a8f05dfebbaf94faed84420719b13aed5bb Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 13 Mar 2021 16:49:05 +0000 Subject: [PATCH 018/175] mmgen-blocks-info: move opts_data --- mmnode-blocks-info | 66 +++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index a34d6fa..dbb7ed8 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -25,39 +25,6 @@ from collections import namedtuple from mmgen.common import * from decimal import Decimal -opts_data = { - 'sets': [ - ('raw_miner_info',True,'miner_info',True), - ('summary',True,'raw_miner_info',False), - ('summary',True,'miner_info',False) - ], - 'text': { - 'desc': 'Display information about a range of blocks', - 'usage': '[opts] +||', - 'options': """ --h, --help Print this help message ---, --longhelp Print help message for long options (common options) --H, --hashes Display only block numbers and hashes --m, --miner-info Display miner info in coinbase transaction --M, --raw-miner-info Display miner info in uninterpreted form --n, --no-header Don't print the column header --o, --fields= Display the specified fields --s, --summary Print the summary only --S, --no-summary Don't print the summary -""", - 'notes': """ -If no block number is specified, the current block is assumed. - -In addition to information about the requested range of blocks, an estimate -of the next difficulty adjustment is also displayed based on the average -Block Discovery Interval from the beginning of the current 2016-block period -to the chain tip. - -Requires --txindex for correct operation. -""" - } -} - class local_vars: pass class BlocksInfo: @@ -308,6 +275,39 @@ class BlocksInfoHashes(BlocksInfo): hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) Msg('\n'.join('{:<7} {}'.format(height,H) for height,H in zip(heights,hashes))) +opts_data = { + 'sets': [ + ('raw_miner_info',True,'miner_info',True), + ('summary',True,'raw_miner_info',False), + ('summary',True,'miner_info',False) + ], + 'text': { + 'desc': 'Display information about a range of blocks', + 'usage': '[opts] +||', + 'options': """ +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-H, --hashes Display only block numbers and hashes +-m, --miner-info Display miner info in coinbase transaction +-M, --raw-miner-info Display miner info in uninterpreted form +-n, --no-header Don't print the column header +-o, --fields= Display the specified fields +-s, --summary Print the summary only +-S, --no-summary Don't print the summary +""", + 'notes': """ +If no block number is specified, the current block is assumed. + +In addition to information about the requested range of blocks, an estimate +of the next difficulty adjustment is also displayed based on the average +Block Discovery Interval from the beginning of the current 2016-block period +to the chain tip. + +Requires --txindex for correct operation. +""" + } +} + cmd_args = opts.init(opts_data) if len(cmd_args) not in (0,1): From 3d898a895c3207ad3580100d5a7a8f5f804983d2 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 13 Mar 2021 17:01:45 +0000 Subject: [PATCH 019/175] mmgen-blocks-info: reduce to one class, reorder methods --- mmnode-blocks-info | 197 ++++++++++++++++++++------------------------- 1 file changed, 87 insertions(+), 110 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index dbb7ed8..b0e6718 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -32,88 +32,6 @@ class BlocksInfo: first = None last = None nblocks = None - - def __init__(self): - self.get_block_range() - self.post_init() - - def get_block_range(self): - - if not cmd_args: - first = last = c.blockcount - else: - arg = cmd_args[0] - if arg.startswith('+') and is_int(arg[1:]): - last = c.blockcount - first = last - int(arg[1:]) + 1 - elif is_int(arg): - first = last = int(arg) - else: - try: - first,last = [int(ep) for ep in arg.split('-')] - except: - opts.usage() - - if first > last: - die(2,f'{first}-{last}: invalid block range') - - if last > c.blockcount: - die(2,f'Requested block number ({last}) greater than current block height') - - self.first = first - self.last = last - self.nblocks = last - first + 1 - - def post_init(self): pass - - async def run(self): - - heights = range(self.first,self.last+1) - hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) - hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - - for height in heights: - await self.process_block(height,hashes.pop(0),hdrs.pop(0)) - - def print_header(self): - hdr1 = [v.hdr1 for v in self.fvals] - hdr2 = [v.hdr2 for v in self.fvals] - if opt.miner_info: - hdr1.append(' ') - hdr2.append('Miner') - Msg(self.fs.format(*hdr1)) - Msg(self.fs.format(*hdr2)) - - async def print_summary(self): - from mmgen.util import secs_to_hms - tip = c.blockcount - rel = tip % 2016 - - if rel: - HA,HB = await c.gathered_call('getblockhash',([tip-rel],[tip])) - hA,hB = await c.gathered_call('getblockheader',([HA],[HB])) - bdi = (hB['time']-hA['time']) / rel - adj_pct = ((600 / bdi) - 1) * 100 - Msg_r(fmt(f""" - Current height: {tip} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) - BDI (cur period): {bdi/60:.2f} min - Est. diff adjust: {adj_pct:+.2f}% - """)) - else: - Msg_r(fmt(f""" - Current height: {tip} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) - """)) - - Msg('\nRange: {}-{} ({} blocks [{}])'.format( - self.first, - self.last, - self.nblocks, - secs_to_hms(self.t_cur - self.t_start) )) - -class BlocksInfoOverview(BlocksInfo): - total_bytes = 0 total_weight = 0 t_start = None @@ -158,9 +76,10 @@ class BlocksInfoOverview(BlocksInfo): 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0'), } + def __init__(self): - super().__init__() + self.get_block_range() if opt.fields: fnames = opt.fields.split(',') @@ -192,20 +111,41 @@ class BlocksInfoOverview(BlocksInfo): else: self.miner_pats = None - async def get_miner_string(self,H): - tx0 = (await c.call('getblock',H))['tx'][0] - bd = await c.call('getrawtransaction',tx0,1) - if type(bd) == tuple: - return '---' + def get_block_range(self): + + if not cmd_args: + first = last = c.blockcount else: - cb = bytes.fromhex(bd['vin'][0]['coinbase']) - if opt.raw_miner_info: - return repr(cb) + arg = cmd_args[0] + if arg.startswith('+') and is_int(arg[1:]): + last = c.blockcount + first = last - int(arg[1:]) + 1 + elif is_int(arg): + first = last = int(arg) else: - for pat in self.miner_pats: - m = pat.search(cb) - if m: - return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') + try: + first,last = [int(ep) for ep in arg.split('-')] + except: + opts.usage() + + if first > last: + die(2,f'{first}-{last}: invalid block range') + + if last > c.blockcount: + die(2,f'Requested block number ({last}) greater than current block height') + + self.first = first + self.last = last + self.nblocks = last - first + 1 + + async def run(self): + + heights = range(self.first,self.last+1) + hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) + hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + + for height in heights: + await self.process_block(height,hashes.pop(0),hdrs.pop(0)) async def process_block(self,height,H,hdr): loc = local_vars() @@ -251,9 +191,59 @@ class BlocksInfoOverview(BlocksInfo): Msg(self.fs.format(*gen())) + async def get_miner_string(self,H): + tx0 = (await c.call('getblock',H))['tx'][0] + bd = await c.call('getrawtransaction',tx0,1) + if type(bd) == tuple: + return '---' + else: + cb = bytes.fromhex(bd['vin'][0]['coinbase']) + if opt.raw_miner_info: + return repr(cb) + else: + for pat in self.miner_pats: + m = pat.search(cb) + if m: + return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') + + def print_header(self): + hdr1 = [v.hdr1 for v in self.fvals] + hdr2 = [v.hdr2 for v in self.fvals] + if opt.miner_info: + hdr1.append(' ') + hdr2.append('Miner') + Msg(self.fs.format(*hdr1)) + Msg(self.fs.format(*hdr2)) + async def print_summary(self): + from mmgen.util import secs_to_hms + tip = c.blockcount + rel = tip % 2016 + + if rel: + HA,HB = await c.gathered_call('getblockhash',([tip-rel],[tip])) + hA,hB = await c.gathered_call('getblockheader',([HA],[HB])) + bdi = (hB['time']-hA['time']) / rel + adj_pct = ((600 / bdi) - 1) * 100 + Msg_r(fmt(f""" + Current height: {tip} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) + BDI (cur period): {bdi/60:.2f} min + Est. diff adjust: {adj_pct:+.2f}% + """)) + else: + Msg_r(fmt(f""" + Current height: {tip} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) + """)) + + Msg('\nRange: {}-{} ({} blocks [{}])'.format( + self.first, + self.last, + self.nblocks, + secs_to_hms(self.t_cur - self.t_start) )) + if 'bs' in self.deps: - await super().print_summary() if self.nblocks > 1: elapsed = self.t_cur - self.t_start ac = int(elapsed / self.nblocks) @@ -265,16 +255,6 @@ class BlocksInfoOverview(BlocksInfo): Avg BDI: {ac/60:.2f} min """)) -class BlocksInfoHashes(BlocksInfo): - - def print_header(self): - Msg('{:<7} {}'.format('BLOCK','HASH')) - - async def run(self): - heights = range(self.first,self.last+1) - hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) - Msg('\n'.join('{:<7} {}'.format(height,H) for height,H in zip(heights,hashes))) - opts_data = { 'sets': [ ('raw_miner_info',True,'miner_info',True), @@ -322,10 +302,7 @@ async def main(): global c c = await rpc_init(proto) - if opt.hashes: - m = BlocksInfoHashes() - else: - m = BlocksInfoOverview() + m = BlocksInfo() if not (opt.summary or opt.no_header): m.print_header() From 078ba6dad68da9fa05e48baea44fa32f471da822 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 13 Mar 2021 17:03:06 +0000 Subject: [PATCH 020/175] mmgen-blocks-info: fixes, cleanups --- mmnode-blocks-info | 157 ++++++++++++++++++++++----------------------- 1 file changed, 77 insertions(+), 80 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index b0e6718..0f01177 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -20,32 +20,30 @@ mmnode-blocks-info: Display information about a block or range of blocks """ -import time,re +import re from collections import namedtuple +from time import strftime,gmtime + from mmgen.common import * +from mmgen.util import secs_to_hms from decimal import Decimal class local_vars: pass class BlocksInfo: - first = None - last = None - nblocks = None total_bytes = 0 total_weight = 0 - t_start = None - t_prev = None - t_cur = None bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) # bs=getblockstats(), bh=getblockheader() + # 'getblockstats' raises exception on Genesis Block! # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' fields = { 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), - 'date': bf('', 'Date', '{:<19}', None, 'df', ['bs'], None), - 'interval': bf('Solve','Time ', '{:>6}', None, 'if', ['bs'], None), + 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), + 'interval': bf('Solve','Time ', '{:>6}', None, 'td', [], None), 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), @@ -66,17 +64,15 @@ class BlocksInfo: dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] funcs = { - 'df': lambda self,loc: time.strftime('%Y-%m-%d %X',time.gmtime(self.t_cur)), - 'if': lambda self,loc: ( - '-{:02}:{:02}'.format(abs(loc.t_diff)//60,abs(loc.t_diff)%60) if loc.t_diff < 0 else - ' {:02}:{:02}'.format(loc.t_diff//60,loc.t_diff%60) ), + 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), + 'td': lambda self,loc: ( + '-{:02}:{:02}'.format(abs(self.t_diff)//60,abs(self.t_diff)%60) if self.t_diff < 0 else + ' {:02}:{:02}'.format(self.t_diff//60,self.t_diff%60) ), 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), - 'bh': lambda self,loc: loc.hdr, 'fp': lambda self,loc: loc.bs['feerate_percentiles'], - 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0'), + 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0').rstrip('.'), } - def __init__(self): self.get_block_range() @@ -94,7 +90,7 @@ class BlocksInfo: self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] - self.bs_keys.extend(['total_size','time']) + self.bs_keys.extend(['total_size','total_weight']) self.ufuncs = {v.varname:self.funcs[v.varname] for v in self.fvals if v.varname in self.funcs} @@ -136,7 +132,6 @@ class BlocksInfo: self.first = first self.last = last - self.nblocks = last - first + 1 async def run(self): @@ -144,6 +139,12 @@ class BlocksInfo: hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + self.t_start = hdrs[0]['time'] + self.t_cur = ( + self.t_start if heights[0] == 0 else + (await c.call('getblockheader',await c.call('getblockhash',heights[0]-1)))['time'] + ) + for height in heights: await self.process_block(height,hashes.pop(0),hdrs.pop(0)) @@ -151,31 +152,21 @@ class BlocksInfo: loc = local_vars() loc.height = height loc.H = H - loc.hdr = hdr + loc.bh = hdr + + self.t_diff = hdr['time'] - self.t_cur + self.t_cur = hdr['time'] + if 'bs' in self.deps: loc.bs = await c.call('getblockstats',H,self.bs_keys) self.total_bytes += loc.bs['total_size'] self.total_weight += loc.bs['total_weight'] - self.t_cur = loc.bs['time'] - if self.t_start == None: - if height == 0: - b_prev = loc.bs - else: - bH = await c.call('getblockhash',height-1) - b_prev = await c.call('getblockstats',bH) - self.t_start = loc.bs['time'] - self.t_prev = b_prev['time'] - loc.t_diff = self.t_cur - self.t_prev - self.t_prev = self.t_cur if opt.summary: return for varname,func in self.ufuncs.items(): - ret = func(self,loc) - if type(ret).__name__ == 'coroutine': - ret = await ret - setattr(loc,varname,ret) + setattr(loc,varname,func(self,loc)) if opt.miner_info: miner_info = await self.get_miner_string(H) @@ -212,79 +203,85 @@ class BlocksInfo: if opt.miner_info: hdr1.append(' ') hdr2.append('Miner') - Msg(self.fs.format(*hdr1)) + if ''.join(hdr1).replace(' ',''): + Msg(self.fs.format(*hdr1)) Msg(self.fs.format(*hdr2)) async def print_summary(self): - from mmgen.util import secs_to_hms tip = c.blockcount - rel = tip % 2016 - if rel: - HA,HB = await c.gathered_call('getblockhash',([tip-rel],[tip])) - hA,hB = await c.gathered_call('getblockheader',([HA],[HB])) - bdi = (hB['time']-hA['time']) / rel - adj_pct = ((600 / bdi) - 1) * 100 - Msg_r(fmt(f""" - Current height: {tip} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) - BDI (cur period): {bdi/60:.2f} min - Est. diff adjust: {adj_pct:+.2f}% - """)) - else: - Msg_r(fmt(f""" - Current height: {tip} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) - """)) + if self.last == tip: + rel = tip % 2016 + if rel: + HA,HB = await c.gathered_call('getblockhash',([tip-rel],[tip])) + hA,hB = await c.gathered_call('getblockheader',([HA],[HB])) + bdi = (hB['time']-hA['time']) / rel + adj_pct = ((600 / bdi) - 1) * 100 + Msg(fmt(f""" + Current height: {tip} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) + BDI (cur period): {bdi/60:.2f} min + Est. diff adjust: {adj_pct:+.2f}% + """)) + else: + Msg(fmt(f""" + Current height: {tip} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) + """)) - Msg('\nRange: {}-{} ({} blocks [{}])'.format( + nblocks = self.last - self.first + 1 + + Msg('Range: {}-{} ({} blocks [{}])'.format( self.first, self.last, - self.nblocks, + nblocks, secs_to_hms(self.t_cur - self.t_start) )) - if 'bs' in self.deps: - if self.nblocks > 1: - elapsed = self.t_cur - self.t_start - ac = int(elapsed / self.nblocks) - rate = (self.total_bytes / 10000) / (elapsed / 36) - Msg_r (fmt(f""" - Avg size: {self.total_bytes//self.nblocks} bytes - Avg weight: {self.total_weight//self.nblocks} bytes - MB/hr: {rate:0.4f} - Avg BDI: {ac/60:.2f} min - """)) + if 'bs' in self.deps and nblocks > 1: + elapsed = self.t_cur - self.t_start + ac = int(elapsed / nblocks) + rate = (self.total_bytes / 10000) / (elapsed / 36) + Msg_r(fmt(f""" + Avg size: {self.total_bytes//nblocks} bytes + Avg weight: {self.total_weight//nblocks} bytes + MB/hr: {rate:0.4f} + Avg BDI: {ac/60:.2f} min + """)) opts_data = { 'sets': [ - ('raw_miner_info',True,'miner_info',True), - ('summary',True,'raw_miner_info',False), - ('summary',True,'miner_info',False) + ('raw_miner_info', True, 'miner_info', True), + ('summary', True, 'raw_miner_info', False), + ('summary', True, 'miner_info', False), + ('hashes', True, 'fields', 'block,hash'), + ('hashes', True, 'no_summary', True), ], 'text': { - 'desc': 'Display information about a range of blocks', - 'usage': '[opts] +||', + 'desc': 'Display information about a block or range of blocks', + 'usage': '[opts] +|[-]', 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -H, --hashes Display only block numbers and hashes -m, --miner-info Display miner info in coinbase transaction -M, --raw-miner-info Display miner info in uninterpreted form --n, --no-header Don't print the column header --o, --fields= Display the specified fields +-n, --no-header Don’t print the column header +-o, --fields= Display the specified fields (comma-separated list) + See AVAILABLE FIELDS below. -s, --summary Print the summary only --S, --no-summary Don't print the summary +-S, --no-summary Don’t print the summary """, 'notes': """ If no block number is specified, the current block is assumed. -In addition to information about the requested range of blocks, an estimate -of the next difficulty adjustment is also displayed based on the average -Block Discovery Interval from the beginning of the current 2016-block period -to the chain tip. +If the requested range ends at the current chain tip, an estimate of the next +difficulty adjustment is also displayed. The estimate is based on the average +Block Discovery Interval from the beginning of the current 2016-block period. -Requires --txindex for correct operation. -""" +AVAILABLE FIELDS: {} + +This program requires a txindex-enabled daemon for correct operation. +""".format(fmt_list(BlocksInfo.fields,fmt='bare')) } } From 082e225df4df033b2849d05f2d5a99214f5ef10d Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 13 Mar 2021 20:27:10 +0000 Subject: [PATCH 021/175] mmgen-blocks-info: add difficulty display; append user fields with '+' --- mmnode-blocks-info | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 0f01177..01efe33 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -60,6 +60,7 @@ class BlocksInfo: 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), 'nTx': bf('', 'nTx ', '{:>5}', None, 'bh', [], 'nTx'), 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), + 'difficulty':bf('Diffi-','culty', '{:8}', None, 'di', [], None), } dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] @@ -71,21 +72,24 @@ class BlocksInfo: 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), 'fp': lambda self,loc: loc.bs['feerate_percentiles'], 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0').rstrip('.'), + 'di': lambda self,loc: '{:.2e}'.format(loc.bh['difficulty']), } def __init__(self): + def get_fields(): + if opt.fields: + ufields = opt.fields.lstrip('+').split(',') + for field in ufields: + if field not in self.fields: + die(1,f'{field!r}: unrecognized field') + return self.dfl_fields + ufields if opt.fields[0] == '+' else ufields + else: + return self.dfl_fields + self.get_block_range() - if opt.fields: - fnames = opt.fields.split(',') - for n in fnames: - if n not in self.fields: - die(1,f'{n!r}: unrecognized field') - else: - fnames = self.dfl_fields - - self.fvals = list(self.fields[k] for k in fnames if k in self.fields) + self.fvals = list(self.fields[k] for k in get_fields()) self.fs = ' '.join( v.fs for v in self.fvals ) self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) @@ -138,6 +142,7 @@ class BlocksInfo: heights = range(self.first,self.last+1) hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + self.last_hdr = hdrs[-1] self.t_start = hdrs[0]['time'] self.t_cur = ( @@ -153,7 +158,7 @@ class BlocksInfo: loc.height = height loc.H = H loc.bh = hdr - + self.t_diff = hdr['time'] - self.t_cur self.t_cur = hdr['time'] @@ -211,21 +216,23 @@ class BlocksInfo: tip = c.blockcount if self.last == tip: + cur_diff_disp = f'Cur difficulty: {self.last_hdr["difficulty"]:.2e}' rel = tip % 2016 if rel: - HA,HB = await c.gathered_call('getblockhash',([tip-rel],[tip])) - hA,hB = await c.gathered_call('getblockheader',([HA],[HB])) - bdi = (hB['time']-hA['time']) / rel + rel_hdr = await c.call('getblockheader',await c.call('getblockhash',tip-rel)) + bdi = (self.last_hdr['time']-rel_hdr['time']) / rel adj_pct = ((600 / bdi) - 1) * 100 Msg(fmt(f""" Current height: {tip} Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) BDI (cur period): {bdi/60:.2f} min + {cur_diff_disp} Est. diff adjust: {adj_pct:+.2f}% """)) else: Msg(fmt(f""" Current height: {tip} + {cur_diff_disp} Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) """)) @@ -267,7 +274,8 @@ opts_data = { -M, --raw-miner-info Display miner info in uninterpreted form -n, --no-header Don’t print the column header -o, --fields= Display the specified fields (comma-separated list) - See AVAILABLE FIELDS below. + See AVAILABLE FIELDS below. If the first character + is '+', fields are appended to the defaults. -s, --summary Print the summary only -S, --no-summary Don’t print the summary """, From 8ed44f819c9fd2c8ec7c543f1a80f3aba32f74ca Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 14 Mar 2021 10:39:50 +0000 Subject: [PATCH 022/175] mmgen-blocks-info: fine-tune column widths --- mmnode-blocks-info | 39 ++++++++++++++++++++++++++++++++++----- mmnode-halving-calculator | 2 +- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 01efe33..301ec92 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -43,7 +43,7 @@ class BlocksInfo: 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), - 'interval': bf('Solve','Time ', '{:>6}', None, 'td', [], None), + 'interval': bf('Solve','Time ', '{:>7}', None, 'td', [], None), 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), @@ -58,11 +58,27 @@ class BlocksInfo: 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), - 'nTx': bf('', 'nTx ', '{:>5}', None, 'bh', [], 'nTx'), - 'subsidy': bf('', 'Subsidy', '{:7}', 'subsidy', 'su', ['bs'], None), + 'nTx': bf('', ' nTx ', '{:>5}', None, 'bh', [], 'nTx'), + 'subsidy': bf('Sub-', 'sidy', '{:5}', 'subsidy', 'su', ['bs'], None), 'difficulty':bf('Diffi-','culty', '{:8}', None, 'di', [], None), } dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] + fixed_fields = [ + 'block', # until ≈ 09/01/2028 (block 1000000) + 'hash', + 'date', + 'size', # until ≈ 6x block size increase + 'weight', # until ≈ 2.5x block size increase + 'version', + 'subsidy', # until ≈ 01/04/2028 (increases by 1 digit per halving until 9th halving [max 10 digits]) + 'difficulty', # until 1.00e+100 (i.e. never) + ] + # column width adjustment data: + fs_lsqueeze = ['interval','totalfee','inputs','outputs','nTx'] + fs_rsqueeze = [] + fs_groups = [ + ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min'), + ] funcs = { 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), @@ -87,10 +103,23 @@ class BlocksInfo: else: return self.dfl_fields + def gen_fs(fnames): + for i in range(len(fnames)): + name = fnames[i] + ls = (' ','')[name in self.fs_lsqueeze] + rs = (' ','')[name in self.fs_rsqueeze] + if i: + for group in self.fs_groups: + if name in group and fnames[i-1] in group: + ls = '' + break + yield ls + self.fields[name].fs + rs + self.get_block_range() - self.fvals = list(self.fields[k] for k in get_fields()) - self.fs = ' '.join( v.fs for v in self.fvals ) + fnames = get_fields() + self.fvals = list(self.fields[name] for name in fnames) + self.fs = ''.join(gen_fs(fnames)).strip() self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] diff --git a/mmnode-halving-calculator b/mmnode-halving-calculator index b191411..3409c5a 100755 --- a/mmnode-halving-calculator +++ b/mmnode-halving-calculator @@ -145,7 +145,7 @@ async def main(): b = 'BLOCK', c = 'DATE', d = '', - e = f'BDI (min)', + e = f'BDI (mins)', f = f'SUBSIDY ({proto.coin})', g = f'MINED ({proto.coin})', h = f'TOTAL MINED ({proto.coin})' From cc02c489eafe8560e9be7f833ba92fae671b6f69 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 14 Mar 2021 11:47:19 +0000 Subject: [PATCH 023/175] mmgen-blocks-info: allow range spec of type + --- mmnode-blocks-info | 58 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 301ec92..d7266dc 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -115,7 +115,7 @@ class BlocksInfo: break yield ls + self.fields[name].fs + rs - self.get_block_range() + self.get_block_range(cmd_args) fnames = get_fields() self.fvals = list(self.fields[name] for name in fnames) @@ -140,15 +140,22 @@ class BlocksInfo: else: self.miner_pats = None - def get_block_range(self): + def get_block_range(self,args): - if not cmd_args: + if not args: first = last = c.blockcount else: - arg = cmd_args[0] - if arg.startswith('+') and is_int(arg[1:]): - last = c.blockcount - first = last - int(arg[1:]) + 1 + arg = args[0] + ps = arg.split('+') + if len(ps) == 2 and is_int(ps[1]): + if not ps[0]: + last = c.blockcount + first = last - int(arg[1:]) + 1 + elif is_int(ps[0]): + first = int(ps[0]) + last = first + int(ps[1]) - 1 + else: + opts.usage() elif is_int(arg): first = last = int(arg) else: @@ -157,11 +164,11 @@ class BlocksInfo: except: opts.usage() - if first > last: - die(2,f'{first}-{last}: invalid block range') + if first > last: + die(2,f'{first}-{last}: invalid block range') - if last > c.blockcount: - die(2,f'Requested block number ({last}) greater than current block height') + if last > c.blockcount: + die(2,f'Requested block number ({last}) greater than current block height') self.first = first self.last = last @@ -294,7 +301,7 @@ opts_data = { ], 'text': { 'desc': 'Display information about a block or range of blocks', - 'usage': '[opts] +|[-]', + 'usage': '[opts] []+|[-]', 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) @@ -315,10 +322,33 @@ If the requested range ends at the current chain tip, an estimate of the next difficulty adjustment is also displayed. The estimate is based on the average Block Discovery Interval from the beginning of the current 2016-block period. -AVAILABLE FIELDS: {} +AVAILABLE FIELDS: {f} + +EXAMPLES: + + # Display default info for current block: + {p} + + # Display default info for blocks 1-200 + {p} 1-200 + + # Display default info for 20 blocks beginning from block 600000 + {p} 600000+20 + + # Display info for block 152817, adding miner field: + {p} --miner-info 152817 + + # Display info for last 10 blocks, adding 'inputs' and 'nTx' fields: + {p} -o +inputs,nTx +10 + + # Display 'block', 'date', 'version' and 'hash' fields for blocks 0-10: + # Note: these are the only supported fields for the Genesis Block + {p} -o block,date,version,hash 0-10 This program requires a txindex-enabled daemon for correct operation. -""".format(fmt_list(BlocksInfo.fields,fmt='bare')) +""".format( + f = fmt_list(BlocksInfo.fields,fmt='bare'), + p = g.prog_name ) } } From b9f0eb10d5cee100a6699a0579df7a10302e1e58 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 14 Mar 2021 13:28:03 +0000 Subject: [PATCH 024/175] mmgen-blocks-info: allow range spec of type -+ --- mmnode-blocks-info | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index d7266dc..cc32790 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -146,20 +146,24 @@ class BlocksInfo: first = last = c.blockcount else: arg = args[0] + from_current = arg[0] == '-' + if arg[0] == '-': + arg = arg[1:] ps = arg.split('+') if len(ps) == 2 and is_int(ps[1]): - if not ps[0]: + if not ps[0] and not from_current: last = c.blockcount first = last - int(arg[1:]) + 1 elif is_int(ps[0]): - first = int(ps[0]) + first = (c.blockcount - int(ps[0])) if from_current else int(ps[0]) last = first + int(ps[1]) - 1 else: opts.usage() elif is_int(arg): - first = last = int(arg) + first = last = (c.blockcount - int(arg)) if from_current else int(arg) else: try: + assert not from_current first,last = [int(ep) for ep in arg.split('-')] except: opts.usage() @@ -301,7 +305,7 @@ opts_data = { ], 'text': { 'desc': 'Display information about a block or range of blocks', - 'usage': '[opts] []+|[-]', + 'usage': '[opts] [|-]+|[-]', 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) @@ -335,6 +339,9 @@ EXAMPLES: # Display default info for 20 blocks beginning from block 600000 {p} 600000+20 + # Display default info for 12 blocks beginning 100 blocks from chain tip + {p} -- -100+12 + # Display info for block 152817, adding miner field: {p} --miner-info 152817 From 7ce9c0088353b0afe71ca35cda9dfc9ecd4e3c8b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 14 Mar 2021 13:29:26 +0000 Subject: [PATCH 025/175] mmgen-blocks-info: improve miner string parsing --- mmnode-blocks-info | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index cc32790..00adf47 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -134,8 +134,9 @@ class BlocksInfo: rb'([a-zA-Z0-9&. -]+/Mined by [a-zA-Z0-9. ]+)', rb'\x08/(.*Mined by [a-zA-Z0-9. ]+)', rb'Mined by ([a-zA-Z0-9. ]+)', - rb'[/^]([a-zA-Z0-9&. /-]{5,})', - rb'[/^]([a-zA-Z0-9&. /-]+)/', + rb'[`]([_a-zA-Z0-9&. #/-]+)[/\xfa]', + rb'[/^]([a-zA-Z0-9&. #/-]{5,})', + rb'[/^]([_a-zA-Z0-9&. #/-]+)/', )] else: self.miner_pats = None From c1f7539d792750c28380560627825151e16c2582 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 16 Mar 2021 16:04:02 +0000 Subject: [PATCH 026/175] mmgen-blocks-info: add Genesis Block stats --- mmnode-blocks-info | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 00adf47..2f3bd1c 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -37,7 +37,6 @@ class BlocksInfo: bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) # bs=getblockstats(), bh=getblockheader() - # 'getblockstats' raises exception on Genesis Block! # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' fields = { 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), @@ -204,7 +203,7 @@ class BlocksInfo: self.t_cur = hdr['time'] if 'bs' in self.deps: - loc.bs = await c.call('getblockstats',H,self.bs_keys) + loc.bs = genesis_stats if height == 0 else await c.call('getblockstats',H,self.bs_keys) self.total_bytes += loc.bs['total_size'] self.total_weight += loc.bs['total_weight'] @@ -215,7 +214,7 @@ class BlocksInfo: setattr(loc,varname,func(self,loc)) if opt.miner_info: - miner_info = await self.get_miner_string(H) + miner_info = '-' if height == 0 else await self.get_miner_string(H) def gen(): for v in self.fvals: @@ -350,7 +349,6 @@ EXAMPLES: {p} -o +inputs,nTx +10 # Display 'block', 'date', 'version' and 'hash' fields for blocks 0-10: - # Note: these are the only supported fields for the Genesis Block {p} -o block,date,version,hash 0-10 This program requires a txindex-enabled daemon for correct operation. @@ -365,6 +363,36 @@ cmd_args = opts.init(opts_data) if len(cmd_args) not in (0,1): opts.usage() +# 'getblockstats' RPC raises exception on Genesis Block, so provide our own stats: +genesis_stats = { + 'avgfee': 0, + 'avgfeerate': 0, + 'avgtxsize': 0, + 'feerate_percentiles': [ 0, 0, 0, 0, 0 ], + 'height': 0, + 'ins': 0, + 'maxfee': 0, + 'maxfeerate': 0, + 'maxtxsize': 0, + 'medianfee': 0, + 'mediantxsize': 0, + 'minfee': 0, + 'minfeerate': 0, + 'mintxsize': 0, + 'outs': 1, + 'subsidy': 5000000000, + 'swtotal_size': 0, + 'swtotal_weight': 0, + 'swtxs': 0, + 'total_out': 0, + 'total_size': 0, + 'total_weight': 0, + 'totalfee': 0, + 'txs': 1, + 'utxo_increase': 1, + 'utxo_size_inc': 117 +} + async def main(): from mmgen.protocol import init_proto_from_opts From 11d9ddf680c766316b6b12f86b9664dbd3e4ff7f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 16 Mar 2021 16:08:00 +0000 Subject: [PATCH 027/175] mmgen-blocks-info: column tweaks, add 'fee_max' field --- mmnode-blocks-info | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 2f3bd1c..fbbb7bb 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -42,7 +42,7 @@ class BlocksInfo: 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), - 'interval': bf('Solve','Time ', '{:>7}', None, 'td', [], None), + 'interval': bf('Solve','Time ', '{:>8}', None, 'td', [], None), 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), @@ -53,6 +53,7 @@ class BlocksInfo: 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 4), 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), + 'fee_max': bf('Max', 'Fee', '{:>5}', None, 'bs', [], 'maxfeerate'), 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), @@ -61,7 +62,21 @@ class BlocksInfo: 'subsidy': bf('Sub-', 'sidy', '{:5}', 'subsidy', 'su', ['bs'], None), 'difficulty':bf('Diffi-','culty', '{:8}', None, 'di', [], None), } - dfl_fields = ['block','date','interval','subsidy','totalfee','size','weight','fee50','fee25','fee10','version'] + dfl_fields = [ + 'block', + 'date', + 'interval', + 'subsidy', + 'totalfee', + 'size', + 'weight', + 'fee50', + 'fee25', + 'fee10', + 'fee_avg', + 'fee_min', + 'version', + ] fixed_fields = [ 'block', # until ≈ 09/01/2028 (block 1000000) 'hash', @@ -73,11 +88,12 @@ class BlocksInfo: 'difficulty', # until 1.00e+100 (i.e. never) ] # column width adjustment data: - fs_lsqueeze = ['interval','totalfee','inputs','outputs','nTx'] + fs_lsqueeze = ['totalfee','inputs','outputs','nTx'] fs_rsqueeze = [] fs_groups = [ - ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min'), + ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min','fee_max'), ] + fs_lsqueeze2 = ['interval'] funcs = { 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), @@ -105,8 +121,10 @@ class BlocksInfo: def gen_fs(fnames): for i in range(len(fnames)): name = fnames[i] - ls = (' ','')[name in self.fs_lsqueeze] + ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] rs = (' ','')[name in self.fs_rsqueeze] + if i < len(fnames) - 1 and fnames[i+1] in self.fs_lsqueeze2: + rs = '' if i: for group in self.fs_groups: if name in group and fnames[i-1] in group: @@ -326,6 +344,8 @@ If the requested range ends at the current chain tip, an estimate of the next difficulty adjustment is also displayed. The estimate is based on the average Block Discovery Interval from the beginning of the current 2016-block period. +All fee fields except for 'totalfee' are in satoshis per virtual byte. + AVAILABLE FIELDS: {f} EXAMPLES: From 780ff3647edaaedcb460e7aaa49aff044dcce848 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 16 Mar 2021 16:25:48 +0000 Subject: [PATCH 028/175] mmnode-blocks-info: support new rangespecs, rewrite range parser - support arbitrary block lists - support ranges with step - support multiplication symbol in nBlocks specifier - support 'cur' to designate current block --- mmnode-blocks-info | 300 +++++++++++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 90 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index fbbb7bb..4f1f3f4 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -34,6 +34,8 @@ class BlocksInfo: total_bytes = 0 total_weight = 0 + total_solve_time = 0 + step = None bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) # bs=getblockstats(), bh=getblockheader() @@ -132,7 +134,7 @@ class BlocksInfo: break yield ls + self.fields[name].fs + rs - self.get_block_range(cmd_args) + self.block_list,self.first,self.last = self.parse_block_range(cmd_args) fnames = get_fields() self.fvals = list(self.fields[name] for name in fnames) @@ -158,58 +160,141 @@ class BlocksInfo: else: self.miner_pats = None - def get_block_range(self,args): + def parse_block_range(self,args): - if not args: - first = last = c.blockcount - else: - arg = args[0] - from_current = arg[0] == '-' - if arg[0] == '-': - arg = arg[1:] - ps = arg.split('+') - if len(ps) == 2 and is_int(ps[1]): - if not ps[0] and not from_current: - last = c.blockcount - first = last - int(arg[1:]) + 1 - elif is_int(ps[0]): - first = (c.blockcount - int(ps[0])) if from_current else int(ps[0]) - last = first + int(ps[1]) - 1 - else: - opts.usage() - elif is_int(arg): - first = last = (c.blockcount - int(arg)) if from_current else int(arg) + def conv_blkspec(arg): + if arg == 'cur': + return c.blockcount + elif is_int(arg) and int(arg) >= 0: + return int(arg) else: - try: - assert not from_current - first,last = [int(ep) for ep in arg.split('-')] - except: + die(1,f'{arg}: invalid block specifier') + + def parse_rangespec(arg): + + class RangeParser: + + def __init__(self,arg): + self.arg = arg + + def parse(self,target): + ret = getattr(self,'parse_'+target)() + if debug: print(f'after parse({target}): {self.arg}') + return ret + + def parse_from_tip(self): + m = re.match(r'-(\d+)(.*)',self.arg) + if m: + self.arg = m[2] + assert int(m[1]) > 0, 'block count cannot be zero' + return int(m[1]) + + def parse_range(self): + if self.arg and self.arg[0] == '-': + opts.usage() + else: + m = re.match(r'([^+-]+)(-([^+-]+))*(.*)',self.arg) + if m: + if debug: print(m.groups()) + self.arg = m[4] + return ( + conv_blkspec(m[1]), + conv_blkspec(m[3]) if m[3] else None + ) + return (None,None) + + def parse_add(self): + m = re.match(r'\+([0-9*]+)(.*)',self.arg) + if m: + self.arg = m[2] + assert m[1].strip('*') == m[1], f"'+{m[1]}': malformed nBlocks specifier" + assert len(m[1]) <= 30, f"'+{m[1]}': overly long nBlocks specifier" + res = eval(m[1]) # m[1] is only digits plus '*', so safe + assert res > 0, "'+0' not allowed" + assert res <= c.blockcount, f"'+{m[1]}': nBlocks must be less than current chain height" + return res + + debug = False + range_spec = namedtuple('parsed_range_spec',['first','last','from_tip','nblocks','step']) + + p = RangeParser(arg) + # parsing order must be preserved! + from_tip = p.parse('from_tip') + first,last = p.parse('range') + add1 = p.parse('add') + add2 = p.parse('add') + + if p.arg or (from_tip and first): + opts.usage() + + if last: + nblocks,step = (None,add1) + if add2: opts.usage() + else: + nblocks,step = (add1,add2) + + if debug: print(range_spec(first,last,from_tip,nblocks,step)) + + if from_tip: + first = c.blockcount - from_tip + if nblocks: + if not first: + first = c.blockcount - nblocks + 1 + last = first + nblocks - 1 + if not last: + last = first + + if debug: print(range_spec(first,last,from_tip,nblocks,step)) if first > last: - die(2,f'{first}-{last}: invalid block range') + die(1,f'{first}-{last}: invalid block range') if last > c.blockcount: - die(2,f'Requested block number ({last}) greater than current block height') + die(1,f'Requested block number {last} greater than current chain height') - self.first = first - self.last = last + block_list = list(range(first,last+1,step)) if step else None + return (block_list, first, last) + + def parse_blocklist(args): + for arg in args: + if arg != 'cur': + if not is_int(arg): + die(1,f'{arg!r}: invalid block number (not an integer)') + if int(arg) > c.blockcount: + die(1,f'Requested block number {arg} greater than current chain height') + + return [conv_blkspec(a) for a in args] + + # return (block_list,first,last) + if not args: + return (None,c.blockcount,c.blockcount) + elif len(args) == 1: + return parse_rangespec(args[0]) + else: + return (parse_blocklist(args),None,None) async def run(self): - - heights = range(self.first,self.last+1) + heights = self.block_list or range(self.first,self.last+1) hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) - hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - self.last_hdr = hdrs[-1] + self.hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - self.t_start = hdrs[0]['time'] - self.t_cur = ( - self.t_start if heights[0] == 0 else - (await c.call('getblockheader',await c.call('getblockhash',heights[0]-1)))['time'] - ) + async def init(count): + h0 = ( + self.hdrs[count] if heights[count] == 0 else + await c.call('getblockheader',await c.call('getblockhash',heights[count]-1)) + ) + self.t_cur = h0['time'] + if count == 0: + self.first_prev_hdr = h0 - for height in heights: - await self.process_block(height,hashes.pop(0),hdrs.pop(0)) + if not self.block_list: + await init(0) + + for n in range(len(heights)): + if self.block_list: + await init(n) + await self.process_block(heights[n],hashes[n],self.hdrs[n]) async def process_block(self,height,H,hdr): loc = local_vars() @@ -219,6 +304,7 @@ class BlocksInfo: self.t_diff = hdr['time'] - self.t_cur self.t_cur = hdr['time'] + self.total_solve_time += self.t_diff if 'bs' in self.deps: loc.bs = genesis_stats if height == 0 else await c.call('getblockstats',H,self.bs_keys) @@ -270,47 +356,63 @@ class BlocksInfo: Msg(self.fs.format(*hdr1)) Msg(self.fs.format(*hdr2)) - async def print_summary(self): + async def print_range_stats(self): + + # These figures don’t include the Genesis Block: + elapsed = self.hdrs[-1]['time'] - self.first_prev_hdr['time'] + nblocks = self.hdrs[-1]['height'] - self.first_prev_hdr['height'] + + Msg('Range: {}-{} ({} blocks [{}])'.format( + self.hdrs[0]['height'], + self.hdrs[-1]['height'], + self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1, # includes Genesis Block + secs_to_hms(elapsed) )) + + if elapsed: + avg_bdi = int(elapsed / nblocks) + if 'bs' in self.deps: + total_blocks = len(self.hdrs) + rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) + Msg_r(fmt(f""" + Avg size: {self.total_bytes//total_blocks} bytes + Avg weight: {self.total_weight//total_blocks} bytes + MB/hr: {rate:0.4f} + """)) + Msg(f'Avg BDI: {avg_bdi/60:.2f} min') + + async def print_diff_stats(self): + tip = c.blockcount - if self.last == tip: - cur_diff_disp = f'Cur difficulty: {self.last_hdr["difficulty"]:.2e}' - rel = tip % 2016 - if rel: - rel_hdr = await c.call('getblockheader',await c.call('getblockhash',tip-rel)) - bdi = (self.last_hdr['time']-rel_hdr['time']) / rel + # Only display stats if user-requested range ends with chain tip + if self.last != tip: + return + + cur_diff_disp = 'Cur difficulty: {:.2e}'.format(self.hdrs[-1]['difficulty']) + rel = tip % 2016 + + if rel: + rel_hdr = await c.call('getblockheader',await c.call('getblockhash',tip-rel)) + tip_time = ( + self.hdrs[-1]['time'] if self.hdrs[-1]['height'] == tip else + (await c.call('getblockheader',await c.call('getblockhash',tip)))['time'] + ) + tdiff = tip_time - rel_hdr['time'] + if tdiff: # if the 2 timestamps are equal (very unlikely), skip display to avoid div-by-zero error + bdi = tdiff / rel adj_pct = ((600 / bdi) - 1) * 100 - Msg(fmt(f""" + Msg_r(fmt(f""" Current height: {tip} Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) BDI (cur period): {bdi/60:.2f} min {cur_diff_disp} Est. diff adjust: {adj_pct:+.2f}% """)) - else: - Msg(fmt(f""" - Current height: {tip} - {cur_diff_disp} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) - """)) - - nblocks = self.last - self.first + 1 - - Msg('Range: {}-{} ({} blocks [{}])'.format( - self.first, - self.last, - nblocks, - secs_to_hms(self.t_cur - self.t_start) )) - - if 'bs' in self.deps and nblocks > 1: - elapsed = self.t_cur - self.t_start - ac = int(elapsed / nblocks) - rate = (self.total_bytes / 10000) / (elapsed / 36) + else: Msg_r(fmt(f""" - Avg size: {self.total_bytes//nblocks} bytes - Avg weight: {self.total_weight//nblocks} bytes - MB/hr: {rate:0.4f} - Avg BDI: {ac/60:.2f} min + Current height: {tip} + {cur_diff_disp} + Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) """)) opts_data = { @@ -323,10 +425,16 @@ opts_data = { ], 'text': { 'desc': 'Display information about a block or range of blocks', - 'usage': '[opts] [|-]+|[-]', + 'usage': '[opts] blocknum [blocknum ...] | blocknum-blocknum[+step] | [blocknum|-nBlocks]+nBlocks[+step]', + 'usage2': [ + '[opts] blocknum [blocknum ...]', + '[opts] blocknum-blocknum[+step]', + '[opts] [blocknum|-nBlocks]+nBlocks[+step]', + ], 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) +-D, --no-diff-stats Omit difficulty adjustment stats from summary -H, --hashes Display only block numbers and hashes -m, --miner-info Display miner info in coinbase transaction -M, --raw-miner-info Display miner info in uninterpreted form @@ -338,7 +446,8 @@ opts_data = { -S, --no-summary Don’t print the summary """, 'notes': """ -If no block number is specified, the current block is assumed. +If no block number is specified, the current block is assumed. The string +'cur' can be used in place of the current block number. If the requested range ends at the current chain tip, an estimate of the next difficulty adjustment is also displayed. The estimate is based on the average @@ -350,26 +459,37 @@ AVAILABLE FIELDS: {f} EXAMPLES: - # Display default info for current block: + # Display info for current block: {p} - # Display default info for blocks 1-200 - {p} 1-200 + # Display info for the Genesis Block: + {p} 0 - # Display default info for 20 blocks beginning from block 600000 - {p} 600000+20 + # Display info for the last 20 blocks: + {p} +20 - # Display default info for 12 blocks beginning 100 blocks from chain tip - {p} -- -100+12 + # Display specified fields for blocks 165-190 + {p} -o block,date,size,inputs,nTx 165-190 + + # Display info for 10 blocks beginning at block 600000: + {p} 600000+10 + + # Display info for every 5th block of 50-block range beginning at 1000 + # blocks from chain tip: + {p} -- -1000+50+5 # Display info for block 152817, adding miner field: {p} --miner-info 152817 - # Display info for last 10 blocks, adding 'inputs' and 'nTx' fields: - {p} -o +inputs,nTx +10 + # Display specified fields for listed blocks: + {p} -o block,date,hash 245798 170 624044 - # Display 'block', 'date', 'version' and 'hash' fields for blocks 0-10: - {p} -o block,date,version,hash 0-10 + # Display every difficulty adjustment from Genesis Block to chain tip: + {p} -o +difficulty 0-cur+2016 + + # Display roughly a block a day over the last two weeks. Note that + # multiplication is allowed in the nBlocks spec: + {p} +144*14+144 This program requires a txindex-enabled daemon for correct operation. """.format( @@ -380,9 +500,6 @@ This program requires a txindex-enabled daemon for correct operation. cmd_args = opts.init(opts_data) -if len(cmd_args) not in (0,1): - opts.usage() - # 'getblockstats' RPC raises exception on Genesis Block, so provide our own stats: genesis_stats = { 'avgfee': 0, @@ -429,9 +546,12 @@ async def main(): await m.run() - if not opt.no_summary: - if not opt.summary: + if m.last and not opt.no_summary: + Msg('') + await m.print_range_stats() + + if not opt.no_diff_stats: Msg('') - await m.print_summary() + await m.print_diff_stats() run_session(main()) From 95f01db6b8cb2f7102ec536f4eea72634c14c237 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 16 Mar 2021 20:20:26 +0000 Subject: [PATCH 029/175] mmnode-blocks-info: minor fixes, cleanups --- mmnode-blocks-info | 56 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 4f1f3f4..51fa3a8 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -165,8 +165,13 @@ class BlocksInfo: def conv_blkspec(arg): if arg == 'cur': return c.blockcount - elif is_int(arg) and int(arg) >= 0: - return int(arg) + elif is_int(arg): + if int(arg) < 0: + die(1,f'{arg}: block number must be non-negative') + elif int(arg) > c.blockcount: + die(1,f'{arg}: requested block greater than current chain tip!') + else: + return int(arg) else: die(1,f'{arg}: invalid block specifier') @@ -183,24 +188,23 @@ class BlocksInfo: return ret def parse_from_tip(self): - m = re.match(r'-(\d+)(.*)',self.arg) + m = re.match(r'-([0-9]+)(.*)',self.arg) if m: self.arg = m[2] - assert int(m[1]) > 0, 'block count cannot be zero' - return int(m[1]) + res = int(m[1]) + assert res > 0, 'block count cannot be zero' + assert res <= c.blockcount, f"'+{m[1]}': block count must be less than current chain height" + return res def parse_range(self): - if self.arg and self.arg[0] == '-': - opts.usage() - else: - m = re.match(r'([^+-]+)(-([^+-]+))*(.*)',self.arg) - if m: - if debug: print(m.groups()) - self.arg = m[4] - return ( - conv_blkspec(m[1]), - conv_blkspec(m[3]) if m[3] else None - ) + m = re.match(r'([^+-]+)(-([^+-]+))*(.*)',self.arg) + if m: + if debug: print(m.groups()) + self.arg = m[4] + return ( + conv_blkspec(m[1]), + conv_blkspec(m[3]) if m[3] else None + ) return (None,None) def parse_add(self): @@ -220,6 +224,8 @@ class BlocksInfo: p = RangeParser(arg) # parsing order must be preserved! from_tip = p.parse('from_tip') + if p.arg.startswith('-'): + opts.usage() first,last = p.parse('range') add1 = p.parse('add') add2 = p.parse('add') @@ -247,32 +253,22 @@ class BlocksInfo: if debug: print(range_spec(first,last,from_tip,nblocks,step)) + first = conv_blkspec(first) + last = conv_blkspec(last) + if first > last: die(1,f'{first}-{last}: invalid block range') - if last > c.blockcount: - die(1,f'Requested block number {last} greater than current chain height') - block_list = list(range(first,last+1,step)) if step else None return (block_list, first, last) - def parse_blocklist(args): - for arg in args: - if arg != 'cur': - if not is_int(arg): - die(1,f'{arg!r}: invalid block number (not an integer)') - if int(arg) > c.blockcount: - die(1,f'Requested block number {arg} greater than current chain height') - - return [conv_blkspec(a) for a in args] - # return (block_list,first,last) if not args: return (None,c.blockcount,c.blockcount) elif len(args) == 1: return parse_rangespec(args[0]) else: - return (parse_blocklist(args),None,None) + return ([conv_blkspec(a) for a in args],None,None) async def run(self): heights = self.block_list or range(self.first,self.last+1) From 48d9e309521217d7f16dddfc0a5f5fc2408528f6 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 17 Mar 2021 11:48:09 +0000 Subject: [PATCH 030/175] mmnode-blocks-info: cleanups --- mmnode-blocks-info | 88 +++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 51fa3a8..98a0586 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -169,37 +169,46 @@ class BlocksInfo: if int(arg) < 0: die(1,f'{arg}: block number must be non-negative') elif int(arg) > c.blockcount: - die(1,f'{arg}: requested block greater than current chain tip!') + die(1,f'{arg}: requested block height greater than current chain tip!') else: return int(arg) else: die(1,f'{arg}: invalid block specifier') + def check_nblocks(arg): + if arg <= 0: + die(1,'nBlocks must be a positive integer') + if arg > c.blockcount: + die(1, f"'{arg}': nBlocks must be less than current chain height") + return arg + def parse_rangespec(arg): class RangeParser: + debug = True def __init__(self,arg): - self.arg = arg + self.arg = self.orig_arg = arg def parse(self,target): ret = getattr(self,'parse_'+target)() - if debug: print(f'after parse({target}): {self.arg}') + if self.debug: print(f'arg after parse({target}): {self.arg}') return ret + def finalize(self): + if self.arg: + die(1,f'{self.orig_arg!r}: invalid range specifier') + def parse_from_tip(self): m = re.match(r'-([0-9]+)(.*)',self.arg) if m: - self.arg = m[2] - res = int(m[1]) - assert res > 0, 'block count cannot be zero' - assert res <= c.blockcount, f"'+{m[1]}': block count must be less than current chain height" - return res + res,self.arg = (m[1],m[2]) + return check_nblocks(int(res)) - def parse_range(self): - m = re.match(r'([^+-]+)(-([^+-]+))*(.*)',self.arg) + def parse_abs_range(self): + m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',self.arg) if m: - if debug: print(m.groups()) + if self.debug: print(f'abs_range parse: first={m[1]}, last={m[3]}') self.arg = m[4] return ( conv_blkspec(m[1]), @@ -210,63 +219,54 @@ class BlocksInfo: def parse_add(self): m = re.match(r'\+([0-9*]+)(.*)',self.arg) if m: - self.arg = m[2] - assert m[1].strip('*') == m[1], f"'+{m[1]}': malformed nBlocks specifier" - assert len(m[1]) <= 30, f"'+{m[1]}': overly long nBlocks specifier" - res = eval(m[1]) # m[1] is only digits plus '*', so safe - assert res > 0, "'+0' not allowed" - assert res <= c.blockcount, f"'+{m[1]}': nBlocks must be less than current chain height" - return res + res,self.arg = (m[1],m[2]) + if res.strip('*') != res: + die(1,f"'+{res}': malformed nBlocks specifier") + if len(res) > 30: + die(1,f"'+{res}': overly long nBlocks specifier") + return check_nblocks(eval(res)) # res is only digits plus '*', so eval safe - debug = False - range_spec = namedtuple('parsed_range_spec',['first','last','from_tip','nblocks','step']) + range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) p = RangeParser(arg) - # parsing order must be preserved! from_tip = p.parse('from_tip') - if p.arg.startswith('-'): - opts.usage() - first,last = p.parse('range') + first,last = (c.blockcount-from_tip,None) if from_tip else p.parse('abs_range') add1 = p.parse('add') add2 = p.parse('add') + p.finalize() - if p.arg or (from_tip and first): - opts.usage() + if add2 and last is not None: + die(1,f'{arg!r}: invalid range specifier') - if last: - nblocks,step = (None,add1) - if add2: - opts.usage() - else: - nblocks,step = (add1,add2) + nblocks,step = (add1,add2) if last is None else (None,add1) - if debug: print(range_spec(first,last,from_tip,nblocks,step)) + if p.debug: print(range_data(first,last,from_tip,nblocks,step)) - if from_tip: - first = c.blockcount - from_tip if nblocks: - if not first: + if first == None: first = c.blockcount - nblocks + 1 last = first + nblocks - 1 - if not last: - last = first - - if debug: print(range_spec(first,last,from_tip,nblocks,step)) first = conv_blkspec(first) - last = conv_blkspec(last) + last = conv_blkspec(last or first) + + if p.debug: print(range_data(first,last,from_tip,nblocks,step)) if first > last: die(1,f'{first}-{last}: invalid block range') - block_list = list(range(first,last+1,step)) if step else None - return (block_list, first, last) + return range_data(first,last,from_tip,nblocks,step) # return (block_list,first,last) if not args: return (None,c.blockcount,c.blockcount) elif len(args) == 1: - return parse_rangespec(args[0]) + r = parse_rangespec(args[0]) + return ( + list(range(r.first,r.last+1,r.step)) if r.step else None, + r.first, + r.last + ) else: return ([conv_blkspec(a) for a in args],None,None) From 6b6f426be342f42bd89612049adf85fac49a5f95 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 18 Mar 2021 12:07:29 +0000 Subject: [PATCH 031/175] mmnode-blocks-info: move BlocksInfo class to separate module, add test --- MANIFEST.in | 3 + mmgen/node_tools/BlocksInfo.py | 446 +++++++++++++++++++++++++++++ mmnode-blocks-info | 425 +-------------------------- test/test-release.sh | 77 +++++ test/unit_tests_d/nt_BlocksInfo.py | 83 ++++++ 5 files changed, 611 insertions(+), 423 deletions(-) create mode 100644 MANIFEST.in create mode 100644 mmgen/node_tools/BlocksInfo.py create mode 100755 test/test-release.sh create mode 100755 test/unit_tests_d/nt_BlocksInfo.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..22b60bd --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README.md LICENSE +include test/test-release.sh +include test/unit_tests_d/*.py diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py new file mode 100644 index 0000000..4ec3b5e --- /dev/null +++ b/mmgen/node_tools/BlocksInfo.py @@ -0,0 +1,446 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2021 The MMGen Project +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . + +""" +mmgen.node_tools.BlocksInfo: Display information about a block or range of blocks +""" + +import re +from collections import namedtuple +from time import strftime,gmtime +from decimal import Decimal + +from mmgen.common import * + +class BlocksInfo: + + total_bytes = 0 + total_weight = 0 + total_solve_time = 0 + + bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) + # bs=getblockstats(), bh=getblockheader() + # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' + fields = { + 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), + 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), + 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), + 'interval': bf('Solve','Time ', '{:>8}', None, 'td', [], None), + 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), + 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), + 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), + 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 0), + 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 1), + 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 2), + 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 3), + 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 4), + 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), + 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), + 'fee_max': bf('Max', 'Fee', '{:>5}', None, 'bs', [], 'maxfeerate'), + 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), + 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), + 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), + 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), + 'nTx': bf('', ' nTx ', '{:>5}', None, 'bh', [], 'nTx'), + 'subsidy': bf('Sub-', 'sidy', '{:5}', 'subsidy', 'su', ['bs'], None), + 'difficulty':bf('Diffi-','culty', '{:8}', None, 'di', [], None), + } + dfl_fields = [ + 'block', + 'date', + 'interval', + 'subsidy', + 'totalfee', + 'size', + 'weight', + 'fee50', + 'fee25', + 'fee10', + 'fee_avg', + 'fee_min', + 'version', + ] + fixed_fields = [ + 'block', # until ≈ 09/01/2028 (block 1000000) + 'hash', + 'date', + 'size', # until ≈ 6x block size increase + 'weight', # until ≈ 2.5x block size increase + 'version', + 'subsidy', # until ≈ 01/04/2028 (increases by 1 digit per halving until 9th halving [max 10 digits]) + 'difficulty', # until 1.00e+100 (i.e. never) + ] + # column width adjustment data: + fs_lsqueeze = ['totalfee','inputs','outputs','nTx'] + fs_rsqueeze = [] + fs_groups = [ + ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min','fee_max'), + ] + fs_lsqueeze2 = ['interval'] + + funcs = { + 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), + 'td': lambda self,loc: ( + '-{:02}:{:02}'.format(abs(self.t_diff)//60,abs(self.t_diff)%60) if self.t_diff < 0 else + ' {:02}:{:02}'.format(self.t_diff//60,self.t_diff%60) ), + 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), + 'fp': lambda self,loc: loc.bs['feerate_percentiles'], + 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0').rstrip('.'), + 'di': lambda self,loc: '{:.2e}'.format(loc.bh['difficulty']), + } + + range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) + + t_fmt = lambda self,t: f'{t/86400:.2f} days' if t > 172800 else secs_to_hms(int(t)) + + def __init__(self,cmd_args,opt,rpc): + + def get_fields(): + if opt.fields: + ufields = opt.fields.lstrip('+').split(',') + for field in ufields: + if field not in self.fields: + die(1,f'{field!r}: unrecognized field') + return self.dfl_fields + ufields if opt.fields[0] == '+' else ufields + else: + return self.dfl_fields + + def gen_fs(fnames): + for i in range(len(fnames)): + name = fnames[i] + ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] + rs = (' ','')[name in self.fs_rsqueeze] + if i < len(fnames) - 1 and fnames[i+1] in self.fs_lsqueeze2: + rs = '' + if i: + for group in self.fs_groups: + if name in group and fnames[i-1] in group: + ls = '' + break + yield ls + self.fields[name].fs + rs + + def parse_cmd_args(): + if not cmd_args: + return (None,self.tip,self.tip) + elif len(cmd_args) == 1: + r = self.parse_rangespec(cmd_args[0]) + return ( + list(range(r.first,r.last+1,r.step)) if r.step else None, + r.first, + r.last + ) + else: + return ([self.conv_blkspec(a) for a in cmd_args],None,None) + + self.rpc = rpc + self.opt = opt + self.tip = rpc.blockcount + + self.block_list,self.first,self.last = parse_cmd_args() + + fnames = get_fields() + self.fvals = list(self.fields[name] for name in fnames) + self.fs = ''.join(gen_fs(fnames)).strip() + self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) + + self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] + self.bs_keys.extend(['total_size','total_weight']) + + self.ufuncs = {v.varname:self.funcs[v.varname] for v in self.fvals if v.varname in self.funcs} + + if opt.miner_info: + self.fs += ' {}' + self.miner_pats = [re.compile(pat) for pat in ( + rb'`/([_a-zA-Z0-9&. #/-]+)/', + rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', + rb'([a-zA-Z0-9&. -]+/Mined by [a-zA-Z0-9. ]+)', + rb'\x08/(.*Mined by [a-zA-Z0-9. ]+)', + rb'Mined by ([a-zA-Z0-9. ]+)', + rb'[`]([_a-zA-Z0-9&. #/-]+)[/\xfa]', + rb'[/^]([a-zA-Z0-9&. #/-]{5,})', + rb'[/^]([_a-zA-Z0-9&. #/-]+)/', + )] + else: + self.miner_pats = None + + def conv_blkspec(self,arg): + if arg == 'cur': + return self.tip + elif is_int(arg): + if int(arg) < 0: + die(1,f'{arg}: block number must be non-negative') + elif int(arg) > self.tip: + die(1,f'{arg}: requested block height greater than current chain tip!') + else: + return int(arg) + else: + die(1,f'{arg}: invalid block specifier') + + def check_nblocks(self,arg): + if arg <= 0: + die(1,'nBlocks must be a positive integer') + elif arg > self.tip: + die(1, f"'{arg}': nBlocks must be less than current chain height") + return arg + + def parse_rangespec(self,arg): + + class RangeParser: + debug = False + + def __init__(rp,arg): + rp.arg = rp.orig_arg = arg + + def parse(rp,target): + ret = getattr(rp,'parse_'+target)() + if rp.debug: print(f'arg after parse({target}): {rp.arg}') + return ret + + def finalize(rp): + if rp.arg: + die(1,f'{rp.orig_arg!r}: invalid range specifier') + + def parse_from_tip(rp): + m = re.match(r'-([0-9]+)(.*)',rp.arg) + if m: + res,rp.arg = (m[1],m[2]) + return self.check_nblocks(int(res)) + + def parse_abs_range(rp): + m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',rp.arg) + if m: + if rp.debug: print(f'abs_range parse: first={m[1]}, last={m[3]}') + rp.arg = m[4] + return ( + self.conv_blkspec(m[1]), + self.conv_blkspec(m[3]) if m[3] else None + ) + return (None,None) + + def parse_add(rp): + m = re.match(r'\+([0-9*]+)(.*)',rp.arg) + if m: + res,rp.arg = (m[1],m[2]) + if res.strip('*') != res: + die(1,f"'+{res}': malformed nBlocks specifier") + if len(res) > 30: + die(1,f"'+{res}': overly long nBlocks specifier") + return self.check_nblocks(eval(res)) # res is only digits plus '*', so eval safe + + p = RangeParser(arg) + from_tip = p.parse('from_tip') + first,last = (self.tip-from_tip,None) if from_tip else p.parse('abs_range') + add1 = p.parse('add') + add2 = p.parse('add') + p.finalize() + + if add2 and last is not None: + die(1,f'{arg!r}: invalid range specifier') + + nblocks,step = (add1,add2) if last is None else (None,add1) + + if p.debug: print(self.range_data(first,last,from_tip,nblocks,step)) + + if nblocks: + if first == None: + first = self.tip - nblocks + 1 + last = first + nblocks - 1 + + first = self.conv_blkspec(first) + last = self.conv_blkspec(last or first) + + if p.debug: print(self.range_data(first,last,from_tip,nblocks,step)) + + if first > last: + die(1,f'{first}-{last}: invalid block range') + + return self.range_data(first,last,from_tip,nblocks,step) + + async def run(self): + + c = self.rpc + heights = self.block_list or range(self.first,self.last+1) + hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) + self.hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + + async def init(count): + h0 = ( + self.hdrs[count] if heights[count] == 0 else + await c.call('getblockheader',await c.call('getblockhash',heights[count]-1)) + ) + self.t_cur = h0['time'] + if count == 0: + self.first_prev_hdr = h0 + + if not self.block_list: + await init(0) + + for n in range(len(heights)): + if self.block_list: + await init(n) + await self.process_block(heights[n],hashes[n],self.hdrs[n]) + + async def process_block(self,height,H,hdr): + class local_vars: pass + loc = local_vars() + loc.height = height + loc.H = H + loc.bh = hdr + + self.t_diff = hdr['time'] - self.t_cur + self.t_cur = hdr['time'] + self.total_solve_time += self.t_diff + + if 'bs' in self.deps: + loc.bs = self.genesis_stats if height == 0 else await self.rpc.call('getblockstats',H,self.bs_keys) + self.total_bytes += loc.bs['total_size'] + self.total_weight += loc.bs['total_weight'] + + if self.opt.summary: + return + + for varname,func in self.ufuncs.items(): + setattr(loc,varname,func(self,loc)) + + if self.opt.miner_info: + miner_info = '-' if height == 0 else await self.get_miner_string(H) + + def gen(): + for v in self.fvals: + if v.key is None: + yield getattr(loc,v.varname) + else: + yield getattr(loc,v.varname)[v.key] + if self.opt.miner_info: + yield miner_info + + Msg(self.fs.format(*gen())) + + async def get_miner_string(self,H): + tx0 = (await self.rpc.call('getblock',H))['tx'][0] + bd = await self.rpc.call('getrawtransaction',tx0,1) + if type(bd) == tuple: + return '---' + else: + cb = bytes.fromhex(bd['vin'][0]['coinbase']) + if self.opt.raw_miner_info: + return repr(cb) + else: + for pat in self.miner_pats: + m = pat.search(cb) + if m: + return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') + + def print_header(self): + hdr1 = [v.hdr1 for v in self.fvals] + hdr2 = [v.hdr2 for v in self.fvals] + if self.opt.miner_info: + hdr1.append(' ') + hdr2.append('Miner') + if ''.join(hdr1).replace(' ',''): + Msg(self.fs.format(*hdr1)) + Msg(self.fs.format(*hdr2)) + + async def print_range_stats(self): + + # These figures don’t include the Genesis Block: + elapsed = self.hdrs[-1]['time'] - self.first_prev_hdr['time'] + nblocks = self.hdrs[-1]['height'] - self.first_prev_hdr['height'] + + Msg('Range: {}-{} ({} blocks [{}])'.format( + self.hdrs[0]['height'], + self.hdrs[-1]['height'], + self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1, # includes Genesis Block + self.t_fmt(elapsed) )) + + if elapsed: + avg_bdi = int(elapsed / nblocks) + if 'bs' in self.deps: + total_blocks = len(self.hdrs) + rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) + Msg_r(fmt(f""" + Avg size: {self.total_bytes//total_blocks} bytes + Avg weight: {self.total_weight//total_blocks} bytes + MB/hr: {rate:0.4f} + """)) + Msg(f'Avg BDI: {avg_bdi/60:.2f} min') + + async def print_diff_stats(self): + + c = self.rpc + + # Only display stats if user-requested range ends with chain tip + if self.last != self.tip: + return + + cur_diff_disp = 'Cur difficulty: {:.2e}'.format(self.hdrs[-1]['difficulty']) + rel = self.tip % 2016 + + if rel: + rel_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-rel)) + tip_time = ( + self.hdrs[-1]['time'] if self.hdrs[-1]['height'] == self.tip else + (await c.call('getblockheader',await c.call('getblockhash',self.tip)))['time'] + ) + tdiff = tip_time - rel_hdr['time'] + if tdiff: # if the 2 timestamps are equal (very unlikely), skip display to avoid div-by-zero error + bdi = tdiff / rel + adj_pct = ((600 / bdi) - 1) * 100 + Msg_r(fmt(f""" + Current height: {self.tip} + Next diff adjust: {self.tip-rel+2016} (in {2016-rel} blocks [{self.t_fmt((2016-rel)*bdi)}]) + BDI (cur period): {bdi/60:.2f} min + {cur_diff_disp} + Est. diff adjust: {adj_pct:+.2f}% + """)) + else: + Msg_r(fmt(f""" + Current height: {self.tip} + {cur_diff_disp} + Next diff adjust: {self.tip-rel+2016} (in {2016-rel} blocks) + """)) + + # 'getblockstats' RPC raises exception on Genesis Block, so provide our own stats: + genesis_stats = { + 'avgfee': 0, + 'avgfeerate': 0, + 'avgtxsize': 0, + 'feerate_percentiles': [ 0, 0, 0, 0, 0 ], + 'height': 0, + 'ins': 0, + 'maxfee': 0, + 'maxfeerate': 0, + 'maxtxsize': 0, + 'medianfee': 0, + 'mediantxsize': 0, + 'minfee': 0, + 'minfeerate': 0, + 'mintxsize': 0, + 'outs': 1, + 'subsidy': 5000000000, + 'swtotal_size': 0, + 'swtotal_weight': 0, + 'swtxs': 0, + 'total_out': 0, + 'total_size': 0, + 'total_weight': 0, + 'totalfee': 0, + 'txs': 1, + 'utxo_increase': 1, + 'utxo_size_inc': 117 + } diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 98a0586..7b216a3 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -20,396 +20,8 @@ mmnode-blocks-info: Display information about a block or range of blocks """ -import re -from collections import namedtuple -from time import strftime,gmtime - from mmgen.common import * -from mmgen.util import secs_to_hms -from decimal import Decimal - -class local_vars: pass - -class BlocksInfo: - - total_bytes = 0 - total_weight = 0 - total_solve_time = 0 - step = None - - bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) - # bs=getblockstats(), bh=getblockheader() - # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' - fields = { - 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), - 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), - 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), - 'interval': bf('Solve','Time ', '{:>8}', None, 'td', [], None), - 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), - 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), - 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), - 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 0), - 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 1), - 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 2), - 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 3), - 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 4), - 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), - 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), - 'fee_max': bf('Max', 'Fee', '{:>5}', None, 'bs', [], 'maxfeerate'), - 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), - 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), - 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), - 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), - 'nTx': bf('', ' nTx ', '{:>5}', None, 'bh', [], 'nTx'), - 'subsidy': bf('Sub-', 'sidy', '{:5}', 'subsidy', 'su', ['bs'], None), - 'difficulty':bf('Diffi-','culty', '{:8}', None, 'di', [], None), - } - dfl_fields = [ - 'block', - 'date', - 'interval', - 'subsidy', - 'totalfee', - 'size', - 'weight', - 'fee50', - 'fee25', - 'fee10', - 'fee_avg', - 'fee_min', - 'version', - ] - fixed_fields = [ - 'block', # until ≈ 09/01/2028 (block 1000000) - 'hash', - 'date', - 'size', # until ≈ 6x block size increase - 'weight', # until ≈ 2.5x block size increase - 'version', - 'subsidy', # until ≈ 01/04/2028 (increases by 1 digit per halving until 9th halving [max 10 digits]) - 'difficulty', # until 1.00e+100 (i.e. never) - ] - # column width adjustment data: - fs_lsqueeze = ['totalfee','inputs','outputs','nTx'] - fs_rsqueeze = [] - fs_groups = [ - ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min','fee_max'), - ] - fs_lsqueeze2 = ['interval'] - - funcs = { - 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), - 'td': lambda self,loc: ( - '-{:02}:{:02}'.format(abs(self.t_diff)//60,abs(self.t_diff)%60) if self.t_diff < 0 else - ' {:02}:{:02}'.format(self.t_diff//60,self.t_diff%60) ), - 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), - 'fp': lambda self,loc: loc.bs['feerate_percentiles'], - 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0').rstrip('.'), - 'di': lambda self,loc: '{:.2e}'.format(loc.bh['difficulty']), - } - - def __init__(self): - - def get_fields(): - if opt.fields: - ufields = opt.fields.lstrip('+').split(',') - for field in ufields: - if field not in self.fields: - die(1,f'{field!r}: unrecognized field') - return self.dfl_fields + ufields if opt.fields[0] == '+' else ufields - else: - return self.dfl_fields - - def gen_fs(fnames): - for i in range(len(fnames)): - name = fnames[i] - ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] - rs = (' ','')[name in self.fs_rsqueeze] - if i < len(fnames) - 1 and fnames[i+1] in self.fs_lsqueeze2: - rs = '' - if i: - for group in self.fs_groups: - if name in group and fnames[i-1] in group: - ls = '' - break - yield ls + self.fields[name].fs + rs - - self.block_list,self.first,self.last = self.parse_block_range(cmd_args) - - fnames = get_fields() - self.fvals = list(self.fields[name] for name in fnames) - self.fs = ''.join(gen_fs(fnames)).strip() - self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) - - self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] - self.bs_keys.extend(['total_size','total_weight']) - - self.ufuncs = {v.varname:self.funcs[v.varname] for v in self.fvals if v.varname in self.funcs} - - if opt.miner_info: - self.fs += ' {}' - self.miner_pats = [re.compile(pat) for pat in ( - rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', - rb'([a-zA-Z0-9&. -]+/Mined by [a-zA-Z0-9. ]+)', - rb'\x08/(.*Mined by [a-zA-Z0-9. ]+)', - rb'Mined by ([a-zA-Z0-9. ]+)', - rb'[`]([_a-zA-Z0-9&. #/-]+)[/\xfa]', - rb'[/^]([a-zA-Z0-9&. #/-]{5,})', - rb'[/^]([_a-zA-Z0-9&. #/-]+)/', - )] - else: - self.miner_pats = None - - def parse_block_range(self,args): - - def conv_blkspec(arg): - if arg == 'cur': - return c.blockcount - elif is_int(arg): - if int(arg) < 0: - die(1,f'{arg}: block number must be non-negative') - elif int(arg) > c.blockcount: - die(1,f'{arg}: requested block height greater than current chain tip!') - else: - return int(arg) - else: - die(1,f'{arg}: invalid block specifier') - - def check_nblocks(arg): - if arg <= 0: - die(1,'nBlocks must be a positive integer') - if arg > c.blockcount: - die(1, f"'{arg}': nBlocks must be less than current chain height") - return arg - - def parse_rangespec(arg): - - class RangeParser: - debug = True - - def __init__(self,arg): - self.arg = self.orig_arg = arg - - def parse(self,target): - ret = getattr(self,'parse_'+target)() - if self.debug: print(f'arg after parse({target}): {self.arg}') - return ret - - def finalize(self): - if self.arg: - die(1,f'{self.orig_arg!r}: invalid range specifier') - - def parse_from_tip(self): - m = re.match(r'-([0-9]+)(.*)',self.arg) - if m: - res,self.arg = (m[1],m[2]) - return check_nblocks(int(res)) - - def parse_abs_range(self): - m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',self.arg) - if m: - if self.debug: print(f'abs_range parse: first={m[1]}, last={m[3]}') - self.arg = m[4] - return ( - conv_blkspec(m[1]), - conv_blkspec(m[3]) if m[3] else None - ) - return (None,None) - - def parse_add(self): - m = re.match(r'\+([0-9*]+)(.*)',self.arg) - if m: - res,self.arg = (m[1],m[2]) - if res.strip('*') != res: - die(1,f"'+{res}': malformed nBlocks specifier") - if len(res) > 30: - die(1,f"'+{res}': overly long nBlocks specifier") - return check_nblocks(eval(res)) # res is only digits plus '*', so eval safe - - range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) - - p = RangeParser(arg) - from_tip = p.parse('from_tip') - first,last = (c.blockcount-from_tip,None) if from_tip else p.parse('abs_range') - add1 = p.parse('add') - add2 = p.parse('add') - p.finalize() - - if add2 and last is not None: - die(1,f'{arg!r}: invalid range specifier') - - nblocks,step = (add1,add2) if last is None else (None,add1) - - if p.debug: print(range_data(first,last,from_tip,nblocks,step)) - - if nblocks: - if first == None: - first = c.blockcount - nblocks + 1 - last = first + nblocks - 1 - - first = conv_blkspec(first) - last = conv_blkspec(last or first) - - if p.debug: print(range_data(first,last,from_tip,nblocks,step)) - - if first > last: - die(1,f'{first}-{last}: invalid block range') - - return range_data(first,last,from_tip,nblocks,step) - - # return (block_list,first,last) - if not args: - return (None,c.blockcount,c.blockcount) - elif len(args) == 1: - r = parse_rangespec(args[0]) - return ( - list(range(r.first,r.last+1,r.step)) if r.step else None, - r.first, - r.last - ) - else: - return ([conv_blkspec(a) for a in args],None,None) - - async def run(self): - heights = self.block_list or range(self.first,self.last+1) - hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) - self.hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) - - async def init(count): - h0 = ( - self.hdrs[count] if heights[count] == 0 else - await c.call('getblockheader',await c.call('getblockhash',heights[count]-1)) - ) - self.t_cur = h0['time'] - if count == 0: - self.first_prev_hdr = h0 - - if not self.block_list: - await init(0) - - for n in range(len(heights)): - if self.block_list: - await init(n) - await self.process_block(heights[n],hashes[n],self.hdrs[n]) - - async def process_block(self,height,H,hdr): - loc = local_vars() - loc.height = height - loc.H = H - loc.bh = hdr - - self.t_diff = hdr['time'] - self.t_cur - self.t_cur = hdr['time'] - self.total_solve_time += self.t_diff - - if 'bs' in self.deps: - loc.bs = genesis_stats if height == 0 else await c.call('getblockstats',H,self.bs_keys) - self.total_bytes += loc.bs['total_size'] - self.total_weight += loc.bs['total_weight'] - - if opt.summary: - return - - for varname,func in self.ufuncs.items(): - setattr(loc,varname,func(self,loc)) - - if opt.miner_info: - miner_info = '-' if height == 0 else await self.get_miner_string(H) - - def gen(): - for v in self.fvals: - if v.key is None: - yield getattr(loc,v.varname) - else: - yield getattr(loc,v.varname)[v.key] - if opt.miner_info: - yield miner_info - - Msg(self.fs.format(*gen())) - - async def get_miner_string(self,H): - tx0 = (await c.call('getblock',H))['tx'][0] - bd = await c.call('getrawtransaction',tx0,1) - if type(bd) == tuple: - return '---' - else: - cb = bytes.fromhex(bd['vin'][0]['coinbase']) - if opt.raw_miner_info: - return repr(cb) - else: - for pat in self.miner_pats: - m = pat.search(cb) - if m: - return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') - - def print_header(self): - hdr1 = [v.hdr1 for v in self.fvals] - hdr2 = [v.hdr2 for v in self.fvals] - if opt.miner_info: - hdr1.append(' ') - hdr2.append('Miner') - if ''.join(hdr1).replace(' ',''): - Msg(self.fs.format(*hdr1)) - Msg(self.fs.format(*hdr2)) - - async def print_range_stats(self): - - # These figures don’t include the Genesis Block: - elapsed = self.hdrs[-1]['time'] - self.first_prev_hdr['time'] - nblocks = self.hdrs[-1]['height'] - self.first_prev_hdr['height'] - - Msg('Range: {}-{} ({} blocks [{}])'.format( - self.hdrs[0]['height'], - self.hdrs[-1]['height'], - self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1, # includes Genesis Block - secs_to_hms(elapsed) )) - - if elapsed: - avg_bdi = int(elapsed / nblocks) - if 'bs' in self.deps: - total_blocks = len(self.hdrs) - rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) - Msg_r(fmt(f""" - Avg size: {self.total_bytes//total_blocks} bytes - Avg weight: {self.total_weight//total_blocks} bytes - MB/hr: {rate:0.4f} - """)) - Msg(f'Avg BDI: {avg_bdi/60:.2f} min') - - async def print_diff_stats(self): - - tip = c.blockcount - - # Only display stats if user-requested range ends with chain tip - if self.last != tip: - return - - cur_diff_disp = 'Cur difficulty: {:.2e}'.format(self.hdrs[-1]['difficulty']) - rel = tip % 2016 - - if rel: - rel_hdr = await c.call('getblockheader',await c.call('getblockhash',tip-rel)) - tip_time = ( - self.hdrs[-1]['time'] if self.hdrs[-1]['height'] == tip else - (await c.call('getblockheader',await c.call('getblockhash',tip)))['time'] - ) - tdiff = tip_time - rel_hdr['time'] - if tdiff: # if the 2 timestamps are equal (very unlikely), skip display to avoid div-by-zero error - bdi = tdiff / rel - adj_pct = ((600 / bdi) - 1) * 100 - Msg_r(fmt(f""" - Current height: {tip} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks [{((2016-rel)*bdi)/86400:.2f} days]) - BDI (cur period): {bdi/60:.2f} min - {cur_diff_disp} - Est. diff adjust: {adj_pct:+.2f}% - """)) - else: - Msg_r(fmt(f""" - Current height: {tip} - {cur_diff_disp} - Next diff adjust: {tip-rel+2016} (in {2016-rel} blocks) - """)) +from mmgen.node_tools.BlocksInfo import BlocksInfo opts_data = { 'sets': [ @@ -496,46 +108,13 @@ This program requires a txindex-enabled daemon for correct operation. cmd_args = opts.init(opts_data) -# 'getblockstats' RPC raises exception on Genesis Block, so provide our own stats: -genesis_stats = { - 'avgfee': 0, - 'avgfeerate': 0, - 'avgtxsize': 0, - 'feerate_percentiles': [ 0, 0, 0, 0, 0 ], - 'height': 0, - 'ins': 0, - 'maxfee': 0, - 'maxfeerate': 0, - 'maxtxsize': 0, - 'medianfee': 0, - 'mediantxsize': 0, - 'minfee': 0, - 'minfeerate': 0, - 'mintxsize': 0, - 'outs': 1, - 'subsidy': 5000000000, - 'swtotal_size': 0, - 'swtotal_weight': 0, - 'swtxs': 0, - 'total_out': 0, - 'total_size': 0, - 'total_weight': 0, - 'totalfee': 0, - 'txs': 1, - 'utxo_increase': 1, - 'utxo_size_inc': 117 -} - async def main(): from mmgen.protocol import init_proto_from_opts proto = init_proto_from_opts() from mmgen.rpc import rpc_init - global c - c = await rpc_init(proto) - - m = BlocksInfo() + m = BlocksInfo( cmd_args, opt, await rpc_init(proto) ) if not (opt.summary or opt.no_header): m.print_header() diff --git a/test/test-release.sh b/test/test-release.sh new file mode 100755 index 0000000..4b2df03 --- /dev/null +++ b/test/test-release.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +export MMGEN_TEST_SUITE=1 +export PYTHONPATH=. + +RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" MAGENTA="\e[35;1m" CYAN="\e[36;1m" +RESET="\e[0m" + +set -o errtrace +set -o functrace + +trap 'echo -e "${GREEN}Exiting at user request$RESET"; exit' INT +trap 'echo -e "${RED}Node tools test suite exited with error$RESET"' ERR +umask 0022 + +unit_tests_py='test/unit_tests.py --names --quiet' + +PROGNAME=$(basename $0) +while getopts hv OPT +do + case "$OPT" in + h) printf " %-16s The MMGen node tools test suite\n" "${PROGNAME}:" + echo " USAGE: $PROGNAME [options] [tests or test group]" + echo " OPTIONS: '-h' Print this help message" + echo " '-v' Run commands with '--verbose' switch" + exit ;; + v) VERBOSE=1 + unit_tests_py="${unit_tests_py/--quiet/--verbose}" ;; + *) exit ;; + esac +done + +shift $((OPTIND-1)) + +nt_repo='../mmgen-node-tools' +mm_repo='../mmgen' + +die() { echo -e $YELLOW$1$RESET; false; } +gecho() { echo -e $GREEN$1$RESET; } +pecho() { echo -e $MAGENTA$1$RESET; } +becho() { echo -e $BLUE$1$RESET; } + +check_mmgen_repo() { + ( cd $mm_repo; python3 ./setup.py --url | grep -iq 'mmgen' ) +} + +create_links() { + ( cd 'mmgen'; [ -L 'node_tools' ] || ln -s "../$nt_repo/mmgen/node_tools" ) + ( + cd 'test/unit_tests_d' + for fn in ../../$nt_repo/test/unit_tests_d/nt_*.py; do + [ -L "$(basename $fn)" ] || ln -s "$fn" + done + ) +} + +run_unit_tests() { + pecho 'Running unit tests:' + $unit_tests_py --node-tools + pecho 'Completed unit tests' +} + +# start execution + +set -e + +becho 'Starting node tools test suite (WIP)' + +check_mmgen_repo || die "No MMGen repository found at $mm_repo!" + +cd $mm_repo + +create_links + +run_unit_tests + +becho 'Node tools test suite completed successfully' diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py new file mode 100755 index 0000000..63b6137 --- /dev/null +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +""" +test.unit_tests_d.nt_BlocksInfo: + BlocksInfo unit test for the MMGen Node Tools suite +""" + +from mmgen.common import * +from mmgen.exception import * +from mmgen.node_tools.BlocksInfo import BlocksInfo + +tip = 50000 +vecs = ( + # First Last FromTip nBlocks Step First Last BlockList + ( (), (), (tip, tip, None) ), + ( (199,2,37), (), (None, None, [199,2,37]) ), + ( '0', (0, 0, None, None, None), (0, 0, None) ), + ( '0-0', (0, 0, None, None, None), (0, 0, None) ), + (f'-{tip}', (0, 0, tip, None, None), (0, 0, None) ), + ( '0-10', (0, 10, None, None, None), (0, 10, None) ), + ( '0+10', (0, 9, None, 10, None), (0, 9, None) ), + ( '0+10+2', (0, 9, None, 10, 2 ), (0, 9, [0,2,4,6,8]) ), + + ( '1', (1, 1, None, None, None), (1, 1, None) ), + ( '1-1', (1, 1, None, None, None), (1, 1, None) ), + ( '1-10', (1, 10, None, None, None), (1, 10, None) ), + ( '1+10', (1, 10, None, 10, None), (1, 10, None) ), + ( '1+10+2', (1, 10, None, 10, 2 ), (1, 10, [1,3,5,7,9]) ), + + ( '+1', (tip, tip, None, 1, None), (tip, tip, None) ), + ( '+10', (tip-9, tip, None, 10, None), (tip-9, tip, None) ), + + ( '-1', (tip-1, tip-1, 1, None, None), (tip-1, tip-1, None) ), + ( '-1+1', (tip-1, tip-1, 1, 1, None), (tip-1, tip-1, None) ), + ( '-1+2', (tip-1, tip, 1, 2, None), (tip-1, tip, None) ), + ( '-10', (tip-10, tip-10, 10, None, None), (tip-10, tip-10, None) ), + ( '-10+11', (tip-10, tip, 10, 11, None), (tip-10, tip, None) ), + ( '-10+11+2', (tip-10, tip, 10, 11, 2 ), (tip-10, tip, list(range(tip-10,tip+1,2))) ), + + ( 'cur', (tip, tip, None, None, None), (tip, tip, None) ), + ( 'cur-cur', (tip, tip, None, None, None), (tip, tip, None) ), + ( '0-cur', (0, tip, None, None, None), (0, tip, None) ), + (f'{tip-1}-cur', (tip-1, tip, None, None, None), (tip-1, tip, None) ), + ( '0-cur+3000', (0, tip, None, None, 3000 ), (0, tip, list(range(0,tip+1,3000))) ), + ( '+1440+144', (tip-1439, tip, None, 1440, 144 ), (tip-1439, tip, list(range(tip-1439,tip+1,144))) ), + ( '+144*10+12*12', (tip-1439, tip, None, 1440, 144 ), (tip-1439, tip, list(range(tip-1439,tip+1,144))) ), +) + +class dummyRPC: + blockcount = tip + +class dummyOpt: + fields = None + miner_info = None + +class unit_tests: + + def rangespec(self,name,ut): + + b = BlocksInfo(0,dummyOpt(),dummyRPC()) + + def test(spec,chk,foo): + ret = b.parse_rangespec(spec) + vmsg(f'{spec:13} => {BlocksInfo.range_data(*chk)}') + assert ret == chk, f'{ret} != {chk}' + + for vec in vecs: + if vec[1]: + test(*vec) + + return True + + def parse_cmd_args(self,name,ut): + + def test(spec,foo,chk): + b = BlocksInfo(spec if type(spec) == tuple else [spec],dummyOpt(),dummyRPC()) + ret = (b.first,b.last,b.block_list) + vmsg('{:13} => {}'.format(repr(spec) if type(spec) == tuple else spec,chk)) + assert ret == chk, f'{ret} != {chk}' + + for vec in vecs: + test(*vec) + + return True From 199afd759569db313eb03a129ad535355613cfc5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 18 Mar 2021 17:15:09 +0000 Subject: [PATCH 032/175] mmnode-blocks-info: improve diff adj. estimate for beginning of period --- mmgen/node_tools/BlocksInfo.py | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 4ec3b5e..a140ff0 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -388,32 +388,31 @@ class BlocksInfo: if self.last != self.tip: return - cur_diff_disp = 'Cur difficulty: {:.2e}'.format(self.hdrs[-1]['difficulty']) rel = self.tip % 2016 - if rel: + tip_hdr = ( + self.hdrs[-1] if self.hdrs[-1]['height'] == self.tip else + await c.call('getblockheader',await c.call('getblockhash',self.tip)) + ) + + bdi_avg_blks = 144 + bdi_avg_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-bdi_avg_blks)) + bdi_avg = ( tip_hdr['time'] - bdi_avg_hdr['time'] ) / bdi_avg_blks + + if rel > bdi_avg_blks: rel_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-rel)) - tip_time = ( - self.hdrs[-1]['time'] if self.hdrs[-1]['height'] == self.tip else - (await c.call('getblockheader',await c.call('getblockhash',self.tip)))['time'] - ) - tdiff = tip_time - rel_hdr['time'] - if tdiff: # if the 2 timestamps are equal (very unlikely), skip display to avoid div-by-zero error - bdi = tdiff / rel - adj_pct = ((600 / bdi) - 1) * 100 - Msg_r(fmt(f""" - Current height: {self.tip} - Next diff adjust: {self.tip-rel+2016} (in {2016-rel} blocks [{self.t_fmt((2016-rel)*bdi)}]) - BDI (cur period): {bdi/60:.2f} min - {cur_diff_disp} - Est. diff adjust: {adj_pct:+.2f}% - """)) + bdi = ( tip_hdr['time'] - rel_hdr['time'] ) / rel else: - Msg_r(fmt(f""" - Current height: {self.tip} - {cur_diff_disp} - Next diff adjust: {self.tip-rel+2016} (in {2016-rel} blocks) - """)) + bdi_adj = float(tip_hdr['difficulty'] / bdi_avg_hdr['difficulty']) + bdi = bdi_avg * ( (bdi_adj * (bdi_avg_blks-rel)) + rel ) / bdi_avg_blks + + Msg_r(fmt(f""" + Current height: {self.tip} + Next diff adjust: {self.tip-rel+2016} (in {2016-rel} blocks [{self.t_fmt((2016-rel)*bdi_avg)}]) + BDI (cur period): {bdi/60:.2f} min + Cur difficulty: {tip_hdr['difficulty']:.2e} + Est. diff adjust: {((600 / bdi) - 1) * 100:+.2f}% + """)) # 'getblockstats' RPC raises exception on Genesis Block, so provide our own stats: genesis_stats = { From 2f364cc4dc6f97a8c415a97280718db26c822517 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 19 Mar 2021 13:44:33 +0000 Subject: [PATCH 033/175] mmnode-blocks-info: minor fixes, cleanups --- mmgen/node_tools/BlocksInfo.py | 38 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index a140ff0..3c437fe 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -55,10 +55,10 @@ class BlocksInfo: 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), - 'version': bf('', 'Version', '{:8}', None, 'bh', [], 'versionHex'), + 'version': bf('', 'Version', '{:<8}', None, 'bh', [], 'versionHex'), 'nTx': bf('', ' nTx ', '{:>5}', None, 'bh', [], 'nTx'), - 'subsidy': bf('Sub-', 'sidy', '{:5}', 'subsidy', 'su', ['bs'], None), - 'difficulty':bf('Diffi-','culty', '{:8}', None, 'di', [], None), + 'subsidy': bf('Sub-', 'sidy', '{:<5}', 'subsidy', 'su', ['bs'], None), + 'difficulty':bf('Diffi-','culty', '{:<8}', None, 'di', [], None), } dfl_fields = [ 'block', @@ -106,7 +106,7 @@ class BlocksInfo: range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) - t_fmt = lambda self,t: f'{t/86400:.2f} days' if t > 172800 else secs_to_hms(int(t)) + t_fmt = lambda self,t: f'{t/86400:.2f} days' if t > 172800 else f'{t/3600:.2f} hrs' def __init__(self,cmd_args,opt,rpc): @@ -164,7 +164,8 @@ class BlocksInfo: self.ufuncs = {v.varname:self.funcs[v.varname] for v in self.fvals if v.varname in self.funcs} if opt.miner_info: - self.fs += ' {}' + fnames.append('miner') + self.fs += ' {:<5}' self.miner_pats = [re.compile(pat) for pat in ( rb'`/([_a-zA-Z0-9&. #/-]+)/', rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', @@ -178,6 +179,8 @@ class BlocksInfo: else: self.miner_pats = None + self.block_data = namedtuple('block_data',fnames) + def conv_blkspec(self,arg): if arg == 'cur': return self.tip @@ -208,7 +211,7 @@ class BlocksInfo: def parse(rp,target): ret = getattr(rp,'parse_'+target)() - if rp.debug: print(f'arg after parse({target}): {rp.arg}') + if rp.debug: msg(f'arg after parse({target}): {rp.arg}') return ret def finalize(rp): @@ -224,7 +227,7 @@ class BlocksInfo: def parse_abs_range(rp): m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',rp.arg) if m: - if rp.debug: print(f'abs_range parse: first={m[1]}, last={m[3]}') + if rp.debug: msg(f'abs_range parse: first={m[1]}, last={m[3]}') rp.arg = m[4] return ( self.conv_blkspec(m[1]), @@ -254,7 +257,7 @@ class BlocksInfo: nblocks,step = (add1,add2) if last is None else (None,add1) - if p.debug: print(self.range_data(first,last,from_tip,nblocks,step)) + if p.debug: msg(repr(self.range_data(first,last,from_tip,nblocks,step))) if nblocks: if first == None: @@ -264,7 +267,7 @@ class BlocksInfo: first = self.conv_blkspec(first) last = self.conv_blkspec(last or first) - if p.debug: print(self.range_data(first,last,from_tip,nblocks,step)) + if p.debug: msg(repr(self.range_data(first,last,from_tip,nblocks,step))) if first > last: die(1,f'{first}-{last}: invalid block range') @@ -293,7 +296,11 @@ class BlocksInfo: for n in range(len(heights)): if self.block_list: await init(n) - await self.process_block(heights[n],hashes[n],self.hdrs[n]) + ret = await self.process_block(heights[n],hashes[n],self.hdrs[n]) + if self.opt.summary: + continue + else: + Msg(self.fs.format(*ret)) async def process_block(self,height,H,hdr): class local_vars: pass @@ -311,9 +318,6 @@ class BlocksInfo: self.total_bytes += loc.bs['total_size'] self.total_weight += loc.bs['total_weight'] - if self.opt.summary: - return - for varname,func in self.ufuncs.items(): setattr(loc,varname,func(self,loc)) @@ -329,7 +333,7 @@ class BlocksInfo: if self.opt.miner_info: yield miner_info - Msg(self.fs.format(*gen())) + return self.block_data(*gen()) async def get_miner_string(self,H): tx0 = (await self.rpc.call('getblock',H))['tx'][0] @@ -345,6 +349,7 @@ class BlocksInfo: m = pat.search(cb) if m: return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') + return '' def print_header(self): hdr1 = [v.hdr1 for v in self.fvals] @@ -395,7 +400,7 @@ class BlocksInfo: await c.call('getblockheader',await c.call('getblockhash',self.tip)) ) - bdi_avg_blks = 144 + bdi_avg_blks = 432 # ≈3 days bdi_avg_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-bdi_avg_blks)) bdi_avg = ( tip_hdr['time'] - bdi_avg_hdr['time'] ) / bdi_avg_blks @@ -406,9 +411,10 @@ class BlocksInfo: bdi_adj = float(tip_hdr['difficulty'] / bdi_avg_hdr['difficulty']) bdi = bdi_avg * ( (bdi_adj * (bdi_avg_blks-rel)) + rel ) / bdi_avg_blks + rem = 2016 - rel Msg_r(fmt(f""" Current height: {self.tip} - Next diff adjust: {self.tip-rel+2016} (in {2016-rel} blocks [{self.t_fmt((2016-rel)*bdi_avg)}]) + Next diff adjust: {self.tip+rem} (in {rem} block{suf(rem)} [{self.t_fmt((rem)*bdi_avg)}]) BDI (cur period): {bdi/60:.2f} min Cur difficulty: {tip_hdr['difficulty']:.2e} Est. diff adjust: {((600 / bdi) - 1) * 100:+.2f}% From 0952e178375d1a27c0678f709724172303c89b15 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 19 Mar 2021 14:09:01 +0000 Subject: [PATCH 034/175] mmnode-blocks-info: remove --summary* options, add --stats option --- mmgen/node_tools/BlocksInfo.py | 42 ++++++++++++++++++++------------ mmnode-blocks-info | 44 ++++++++++++++++++++-------------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 3c437fe..ec9f938 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -93,6 +93,9 @@ class BlocksInfo: ] fs_lsqueeze2 = ['interval'] + all_stats = ['range','diff'] + dfl_stats = ['range','diff'] + funcs = { 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), 'td': lambda self,loc: ( @@ -110,15 +113,24 @@ class BlocksInfo: def __init__(self,cmd_args,opt,rpc): + def parse_cslist(uarg,full_set,dfl_set,desc): + usr_set = uarg.lstrip('+').split(',') + for e in usr_set: + if e not in full_set: + die(1,f'{e!r}: unrecognized {desc}') + res = dfl_set + usr_set if uarg[0] == '+' else usr_set + # display elements in order: + return [e for e in full_set if e in res] + def get_fields(): - if opt.fields: - ufields = opt.fields.lstrip('+').split(',') - for field in ufields: - if field not in self.fields: - die(1,f'{field!r}: unrecognized field') - return self.dfl_fields + ufields if opt.fields[0] == '+' else ufields - else: - return self.dfl_fields + return parse_cslist(opt.fields,self.fields,self.dfl_fields,'field') + + def get_stats(): + arg = opt.stats.lower() + return ( + self.all_stats if arg == 'all' else [] if arg == 'none' else + parse_cslist(arg,self.all_stats,self.dfl_stats,'stat') + ) def gen_fs(fnames): for i in range(len(fnames)): @@ -153,7 +165,8 @@ class BlocksInfo: self.block_list,self.first,self.last = parse_cmd_args() - fnames = get_fields() + fnames = get_fields() if opt.fields else self.dfl_fields + self.fvals = list(self.fields[name] for name in fnames) self.fs = ''.join(gen_fs(fnames)).strip() self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) @@ -180,6 +193,7 @@ class BlocksInfo: self.miner_pats = None self.block_data = namedtuple('block_data',fnames) + self.stats = get_stats() if opt.stats else self.dfl_stats def conv_blkspec(self,arg): if arg == 'cur': @@ -297,7 +311,7 @@ class BlocksInfo: if self.block_list: await init(n) ret = await self.process_block(heights[n],hashes[n],self.hdrs[n]) - if self.opt.summary: + if opt.stats_only: continue else: Msg(self.fs.format(*ret)) @@ -361,6 +375,9 @@ class BlocksInfo: Msg(self.fs.format(*hdr1)) Msg(self.fs.format(*hdr2)) + def print_stats(self,name): + return getattr(self,f'print_{name}_stats')() + async def print_range_stats(self): # These figures don’t include the Genesis Block: @@ -388,11 +405,6 @@ class BlocksInfo: async def print_diff_stats(self): c = self.rpc - - # Only display stats if user-requested range ends with chain tip - if self.last != self.tip: - return - rel = self.tip % 2016 tip_hdr = ( diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 7b216a3..93274d5 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -25,11 +25,10 @@ from mmgen.node_tools.BlocksInfo import BlocksInfo opts_data = { 'sets': [ - ('raw_miner_info', True, 'miner_info', True), - ('summary', True, 'raw_miner_info', False), - ('summary', True, 'miner_info', False), - ('hashes', True, 'fields', 'block,hash'), - ('hashes', True, 'no_summary', True), + ('hashes', True, 'fields', 'block,hash'), + ('hashes', True, 'stats', 'none'), + ('stats', 'none', 'stats_only', False), + ('stats_only', True, 'no_header', True), ], 'text': { 'desc': 'Display information about a block or range of blocks', @@ -42,16 +41,18 @@ opts_data = { 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) --D, --no-diff-stats Omit difficulty adjustment stats from summary -H, --hashes Display only block numbers and hashes -m, --miner-info Display miner info in coinbase transaction -M, --raw-miner-info Display miner info in uninterpreted form -n, --no-header Don’t print the column header --o, --fields= Display the specified fields (comma-separated list) +-o, --fields= Display the specified fields (comma-separated list). See AVAILABLE FIELDS below. If the first character - is '+', fields are appended to the defaults. --s, --summary Print the summary only --S, --no-summary Don’t print the summary + is '+', specified fields are added to the defaults. +-s, --stats= Display the specified stats (comma-separated list). + See AVAILABLE STATS below. If the first character is + '+', specified stats are added to the defaults. Use + 'none' to disable, or 'all' for all available stats. +-S, --stats-only Display stats only. Skip display of per-block data. """, 'notes': """ If no block number is specified, the current block is assumed. The string @@ -65,6 +66,8 @@ All fee fields except for 'totalfee' are in satoshis per virtual byte. AVAILABLE FIELDS: {f} +AVAILABLE STATS: {s} + EXAMPLES: # Display info for current block: @@ -99,9 +102,13 @@ EXAMPLES: # multiplication is allowed in the nBlocks spec: {p} +144*14+144 + # Display only range stats for the last ten blocks: + {p} -s range -S +10 + This program requires a txindex-enabled daemon for correct operation. """.format( f = fmt_list(BlocksInfo.fields,fmt='bare'), + s = fmt_list(BlocksInfo.all_stats,fmt='bare'), p = g.prog_name ) } } @@ -116,17 +123,18 @@ async def main(): from mmgen.rpc import rpc_init m = BlocksInfo( cmd_args, opt, await rpc_init(proto) ) - if not (opt.summary or opt.no_header): + if not opt.no_header: m.print_header() await m.run() - if m.last and not opt.no_summary: - Msg('') - await m.print_range_stats() - - if not opt.no_diff_stats: - Msg('') - await m.print_diff_stats() + if m.last and m.stats: + for i,stat in enumerate(m.stats): + if stat == 'diff': # Display diff stats by default only if user-requested range ends with chain tip + if not opt.stats and m.last != m.tip: + continue + if not (opt.stats_only and i == 0): + Msg('') + await m.print_stats(stat) run_session(main()) From 374bb21bcd7677b80e8d3919ffcef79d43a429b3 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 19 Mar 2021 20:41:49 +0000 Subject: [PATCH 035/175] mmnode-blocks-info: restore field order --- mmgen/node_tools/BlocksInfo.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index ec9f938..82ed205 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -41,23 +41,23 @@ class BlocksInfo: 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), 'interval': bf('Solve','Time ', '{:>8}', None, 'td', [], None), + 'subsidy': bf('Sub-', 'sidy', '{:<5}', 'subsidy', 'su', ['bs'], None), + 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), - 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), - 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 0), - 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 1), - 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 2), - 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 3), 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 4), + 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 3), + 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 2), + 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 1), + 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 0), + 'fee_max': bf('Max', 'Fee', '{:>5}', None, 'bs', [], 'maxfeerate'), 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), - 'fee_max': bf('Max', 'Fee', '{:>5}', None, 'bs', [], 'maxfeerate'), - 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), - 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), - 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), - 'version': bf('', 'Version', '{:<8}', None, 'bh', [], 'versionHex'), 'nTx': bf('', ' nTx ', '{:>5}', None, 'bh', [], 'nTx'), - 'subsidy': bf('Sub-', 'sidy', '{:<5}', 'subsidy', 'su', ['bs'], None), + 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), + 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), + 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), + 'version': bf('', 'Version', '{:<8}', None, 'bh', [], 'versionHex'), 'difficulty':bf('Diffi-','culty', '{:<8}', None, 'di', [], None), } dfl_fields = [ From 5d1c392131cde87c77ad4dd4c4cd386c56259d00 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 12:06:14 +0000 Subject: [PATCH 036/175] mmnode-blocks-info: minor fixes, cleanups --- mmgen/node_tools/BlocksInfo.py | 34 +++++++++++++++++++----------- mmnode-blocks-info | 12 ++++++----- setup.py | 2 ++ test/unit_tests_d/nt_BlocksInfo.py | 1 + 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 82ed205..5bf2b3b 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -146,24 +146,25 @@ class BlocksInfo: break yield ls + self.fields[name].fs + rs - def parse_cmd_args(): + def parse_cmd_args(): # => (block_list, first, last, step) if not cmd_args: - return (None,self.tip,self.tip) + return (None,self.tip,self.tip,None) elif len(cmd_args) == 1: r = self.parse_rangespec(cmd_args[0]) return ( list(range(r.first,r.last+1,r.step)) if r.step else None, r.first, - r.last + r.last, + r.step ) else: - return ([self.conv_blkspec(a) for a in cmd_args],None,None) + return ([self.conv_blkspec(a) for a in cmd_args],None,None,None) self.rpc = rpc self.opt = opt self.tip = rpc.blockcount - self.block_list,self.first,self.last = parse_cmd_args() + self.block_list,self.first,self.last,self.step = parse_cmd_args() fnames = get_fields() if opt.fields else self.dfl_fields @@ -288,7 +289,7 @@ class BlocksInfo: return self.range_data(first,last,from_tip,nblocks,step) - async def run(self): + async def process_blocks(self): c = self.rpc heights = self.block_list or range(self.first,self.last+1) @@ -311,10 +312,11 @@ class BlocksInfo: if self.block_list: await init(n) ret = await self.process_block(heights[n],hashes[n],self.hdrs[n]) - if opt.stats_only: - continue - else: - Msg(self.fs.format(*ret)) + if not self.opt.stats_only: + self.output_block(ret,n) + + def output_block(self,data,n): + Msg(self.fs.format(*data)) async def process_block(self,height,H,hdr): class local_vars: pass @@ -375,7 +377,7 @@ class BlocksInfo: Msg(self.fs.format(*hdr1)) Msg(self.fs.format(*hdr2)) - def print_stats(self,name): + def process_stats(self,name): return getattr(self,f'print_{name}_stats')() async def print_range_stats(self): @@ -419,19 +421,27 @@ class BlocksInfo: if rel > bdi_avg_blks: rel_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-rel)) bdi = ( tip_hdr['time'] - rel_hdr['time'] ) / rel + bdi_disp = bdi else: bdi_adj = float(tip_hdr['difficulty'] / bdi_avg_hdr['difficulty']) bdi = bdi_avg * ( (bdi_adj * (bdi_avg_blks-rel)) + rel ) / bdi_avg_blks + bdi_disp = bdi_avg rem = 2016 - rel Msg_r(fmt(f""" Current height: {self.tip} Next diff adjust: {self.tip+rem} (in {rem} block{suf(rem)} [{self.t_fmt((rem)*bdi_avg)}]) - BDI (cur period): {bdi/60:.2f} min + BDI (cur period): {bdi_disp/60:.2f} min Cur difficulty: {tip_hdr['difficulty']:.2e} Est. diff adjust: {((600 / bdi) - 1) * 100:+.2f}% """)) + def process_stats_pre(self,i): + if not (self.opt.stats_only and i == 0): + Msg('') + + def finalize_output(self): pass + # 'getblockstats' RPC raises exception on Genesis Block, so provide our own stats: genesis_stats = { 'avgfee': 0, diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 93274d5..4b5e9c5 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -118,23 +118,25 @@ cmd_args = opts.init(opts_data) async def main(): from mmgen.protocol import init_proto_from_opts + from mmgen.rpc import rpc_init + proto = init_proto_from_opts() - from mmgen.rpc import rpc_init m = BlocksInfo( cmd_args, opt, await rpc_init(proto) ) if not opt.no_header: m.print_header() - await m.run() + await m.process_blocks() if m.last and m.stats: for i,stat in enumerate(m.stats): if stat == 'diff': # Display diff stats by default only if user-requested range ends with chain tip if not opt.stats and m.last != m.tip: continue - if not (opt.stats_only and i == 0): - Msg('') - await m.print_stats(stat) + m.process_stats_pre(i) + await m.process_stats(stat) + + m.finalize_output() run_session(main()) diff --git a/setup.py b/setup.py index 6f6f9a4..c5f9c17 100755 --- a/setup.py +++ b/setup.py @@ -28,6 +28,8 @@ class my_install_data(install_data): os.chmod(os.path.join(sdir,f),0o644) install_data.run(self) +os.umask(0o0022) + setup( name = 'mmgen-node-tools', description = 'Optional tools for the MMGen wallet system', diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index 63b6137..156620f 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -50,6 +50,7 @@ class dummyRPC: class dummyOpt: fields = None + stats = None miner_info = None class unit_tests: From 8a89d966a9ca24396a4edca7110da9c81431967f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 12:11:15 +0000 Subject: [PATCH 037/175] mmnode-blocks-info: print_stats() -> output_stats(create_stats()) --- mmgen/node_tools/BlocksInfo.py | 77 ++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 5bf2b3b..f5ce4e8 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -378,33 +378,44 @@ class BlocksInfo: Msg(self.fs.format(*hdr2)) def process_stats(self,name): - return getattr(self,f'print_{name}_stats')() + return self.output_stats(getattr(self,f'create_{name}_stats')()) - async def print_range_stats(self): + async def output_stats(self,res): + name,data = await res + Msg( + capfirst(name) + ' Statistics:\n' + + '\n'.join([(' '+d[0]).format(**d[1]) if len(d) == 2 else (' '+d[0]).format(d[2]) for d in data]) + ) + async def create_range_stats(self): # These figures don’t include the Genesis Block: elapsed = self.hdrs[-1]['time'] - self.first_prev_hdr['time'] nblocks = self.hdrs[-1]['height'] - self.first_prev_hdr['height'] + total_blks = len(self.hdrs) + step_disp = f', nBlocks={total_blks}, step={self.step}' if self.step else '' + def gen(): + yield ( + 'Range: {start}-{end} ({range} blocks [{elapsed}]%s)' % step_disp, { + 'start': self.hdrs[0]['height'], + 'end': self.hdrs[-1]['height'], + 'range': self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1, # includes Genesis Block + 'elapsed': self.t_fmt(elapsed), + 'nBlocks': total_blks, + 'step': self.step, + } + ) + if elapsed: + avg_bdi = int(elapsed / nblocks) + if 'bs' in self.deps: + rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) + yield ( 'Avg size: {} bytes', 'avg_size', self.total_bytes//total_blks ) + yield ( 'Avg weight: {} bytes', 'avg_weight', self.total_weight//total_blks ) + yield ( 'MB/hr: {}', 'mb_per_hour', f'{rate:0.4f}' ) + yield ('Avg BDI: {} min', 'avg_bdi', f'{avg_bdi/60:.2f}') - Msg('Range: {}-{} ({} blocks [{}])'.format( - self.hdrs[0]['height'], - self.hdrs[-1]['height'], - self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1, # includes Genesis Block - self.t_fmt(elapsed) )) + return ( 'range', gen() ) - if elapsed: - avg_bdi = int(elapsed / nblocks) - if 'bs' in self.deps: - total_blocks = len(self.hdrs) - rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) - Msg_r(fmt(f""" - Avg size: {self.total_bytes//total_blocks} bytes - Avg weight: {self.total_weight//total_blocks} bytes - MB/hr: {rate:0.4f} - """)) - Msg(f'Avg BDI: {avg_bdi/60:.2f} min') - - async def print_diff_stats(self): + async def create_diff_stats(self): c = self.rpc rel = self.tip % 2016 @@ -428,13 +439,25 @@ class BlocksInfo: bdi_disp = bdi_avg rem = 2016 - rel - Msg_r(fmt(f""" - Current height: {self.tip} - Next diff adjust: {self.tip+rem} (in {rem} block{suf(rem)} [{self.t_fmt((rem)*bdi_avg)}]) - BDI (cur period): {bdi_disp/60:.2f} min - Cur difficulty: {tip_hdr['difficulty']:.2e} - Est. diff adjust: {((600 / bdi) - 1) * 100:+.2f}% - """)) + + return ( 'difficulty', ( + ('Current height: {}', 'chain_tip', self.tip), + ('Next diff adjust: {next_diff_adjust} (in {blks_remaining} block%s [{time_remaining}])' % suf(rem), + { + 'next_diff_adjust': self.tip + rem, + 'blks_remaining': rem, + 'time_remaining': self.t_fmt(rem * bdi_avg) + } + ), + ('Avg BDI: {avg_bdi} min (over {avg_bdi_blks}-block period)', + { + 'avg_bdi': f'{bdi_disp/60:.2f}', + 'avg_bdi_blks': max(rel,bdi_avg_blks) + } + ), + ('Cur difficulty: {}', 'cur_diff', f'{tip_hdr["difficulty"]:.2e}'), + ('Est. diff adjust: {}%', 'est_diff_adjust_pct', f'{((600 / bdi) - 1) * 100:+.2f}'), + )) def process_stats_pre(self,i): if not (self.opt.stats_only and i == 0): From 39280443391f1cfa76d221dc2cd0362f390df20f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 12:16:29 +0000 Subject: [PATCH 038/175] mmnode-blocks-info: support JSON output --- mmgen/node_tools/BlocksInfo.py | 32 +++++++++++++++++++++++++++++++- mmnode-blocks-info | 8 ++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index f5ce4e8..61e7731 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -20,7 +20,7 @@ mmgen.node_tools.BlocksInfo: Display information about a block or range of blocks """ -import re +import re,json from collections import namedtuple from time import strftime,gmtime from decimal import Decimal @@ -494,3 +494,33 @@ class BlocksInfo: 'utxo_increase': 1, 'utxo_size_inc': 117 } + +class JSONBlocksInfo(BlocksInfo): + + def __init__(self,cmd_args,opt,rpc): + super().__init__(cmd_args,opt,rpc) + Msg_r('{') + + async def process_blocks(self): + Msg_r('"block_data": [') + await super().process_blocks() + Msg_r(']') + + def output_block(self,data,n): + Msg_r( (', ','')[n==0] + json.dumps(data._asdict()) ) + + async def output_stats(self,res): + name,data = await res + def gen(data): + for d in data: + if len(d) == 2: + for k,v in d[1].items(): + yield (k,v) + else: + yield (d[1],d[2]) + Msg_r(', "{}_data": {}'.format(name,json.dumps(dict(gen(data))))) + + def process_stats_pre(self,i): pass + + def finalize_output(self): + Msg('}') diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 4b5e9c5..7fb9966 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -21,7 +21,7 @@ mmnode-blocks-info: Display information about a block or range of blocks """ from mmgen.common import * -from mmgen.node_tools.BlocksInfo import BlocksInfo +from mmgen.node_tools.BlocksInfo import BlocksInfo,JSONBlocksInfo opts_data = { 'sets': [ @@ -29,6 +29,7 @@ opts_data = { ('hashes', True, 'stats', 'none'), ('stats', 'none', 'stats_only', False), ('stats_only', True, 'no_header', True), + ('json', True, 'no_header', True), ], 'text': { 'desc': 'Display information about a block or range of blocks', @@ -42,6 +43,7 @@ opts_data = { -h, --help Print this help message --, --longhelp Print help message for long options (common options) -H, --hashes Display only block numbers and hashes +-j, --json Produce JSON output -m, --miner-info Display miner info in coinbase transaction -M, --raw-miner-info Display miner info in uninterpreted form -n, --no-header Don’t print the column header @@ -122,7 +124,9 @@ async def main(): proto = init_proto_from_opts() - m = BlocksInfo( cmd_args, opt, await rpc_init(proto) ) + cls = JSONBlocksInfo if opt.json else BlocksInfo + + m = cls( cmd_args, opt, await rpc_init(proto) ) if not opt.no_header: m.print_header() From 1b96e79c61b8302616880d48c71ff52c9b61d645 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 21:25:08 +0000 Subject: [PATCH 039/175] mmnode-blocks-info: minor changes and fixes --- mmgen/node_tools/BlocksInfo.py | 65 +++++++++++++++++++++------------- mmnode-feeview | 2 +- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 61e7731..e45b703 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -41,7 +41,7 @@ class BlocksInfo: 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), 'interval': bf('Solve','Time ', '{:>8}', None, 'td', [], None), - 'subsidy': bf('Sub-', 'sidy', '{:<5}', 'subsidy', 'su', ['bs'], None), + 'subsidy': bf('Sub-', 'sidy', '{:<5}', 'subsidy', 'su', ['bs'], None), 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), @@ -59,6 +59,7 @@ class BlocksInfo: 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), 'version': bf('', 'Version', '{:<8}', None, 'bh', [], 'versionHex'), 'difficulty':bf('Diffi-','culty', '{:<8}', None, 'di', [], None), + 'miner': bf('', 'Miner', '{:<5}', None, 'mi', [], None), } dfl_fields = [ 'block', @@ -132,20 +133,6 @@ class BlocksInfo: parse_cslist(arg,self.all_stats,self.dfl_stats,'stat') ) - def gen_fs(fnames): - for i in range(len(fnames)): - name = fnames[i] - ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] - rs = (' ','')[name in self.fs_rsqueeze] - if i < len(fnames) - 1 and fnames[i+1] in self.fs_lsqueeze2: - rs = '' - if i: - for group in self.fs_groups: - if name in group and fnames[i-1] in group: - ls = '' - break - yield ls + self.fields[name].fs + rs - def parse_cmd_args(): # => (block_list, first, last, step) if not cmd_args: return (None,self.tip,self.tip,None) @@ -169,7 +156,7 @@ class BlocksInfo: fnames = get_fields() if opt.fields else self.dfl_fields self.fvals = list(self.fields[name] for name in fnames) - self.fs = ''.join(gen_fs(fnames)).strip() + self.fs = ''.join(self.gen_fs(fnames)).strip() self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] @@ -179,7 +166,7 @@ class BlocksInfo: if opt.miner_info: fnames.append('miner') - self.fs += ' {:<5}' + self.fs += ' ' + self.fields['miner'].fs self.miner_pats = [re.compile(pat) for pat in ( rb'`/([_a-zA-Z0-9&. #/-]+)/', rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', @@ -196,6 +183,24 @@ class BlocksInfo: self.block_data = namedtuple('block_data',fnames) self.stats = get_stats() if opt.stats else self.dfl_stats + def gen_fs(self,fnames,fill=[],fill_char='-'): + for i in range(len(fnames)): + name = fnames[i] + ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] + rs = (' ','')[name in self.fs_rsqueeze] + if i < len(fnames) - 1 and fnames[i+1] in self.fs_lsqueeze2: + rs = '' + if i: + for group in self.fs_groups: + if name in group and fnames[i-1] in group: + ls = '' + break + yield ( + ls + + (self.fields[name].fs.replace(':',':'+fill_char) if name in fill else self.fields[name].fs) + + rs + ) + def conv_blkspec(self,arg): if arg == 'cur': return self.tip @@ -368,24 +373,32 @@ class BlocksInfo: return '' def print_header(self): + Msg('\n'.join(self.gen_header())) + + def gen_header(self): hdr1 = [v.hdr1 for v in self.fvals] hdr2 = [v.hdr2 for v in self.fvals] if self.opt.miner_info: hdr1.append(' ') hdr2.append('Miner') if ''.join(hdr1).replace(' ',''): - Msg(self.fs.format(*hdr1)) - Msg(self.fs.format(*hdr2)) + yield self.fs.format(*hdr1) + yield self.fs.format(*hdr2) def process_stats(self,name): return self.output_stats(getattr(self,f'create_{name}_stats')()) async def output_stats(self,res): + def gen(data): + for d in data: + if len(d) == 2: + yield (' '+d[0]).format(**d[1]) + elif len(d) == 3: + yield (' '+d[0]).format(d[2]) + else: + yield d name,data = await res - Msg( - capfirst(name) + ' Statistics:\n' + - '\n'.join([(' '+d[0]).format(**d[1]) if len(d) == 2 else (' '+d[0]).format(d[2]) for d in data]) - ) + Msg('\n'.join(gen(data))) async def create_range_stats(self): # These figures don’t include the Genesis Block: @@ -394,6 +407,7 @@ class BlocksInfo: total_blks = len(self.hdrs) step_disp = f', nBlocks={total_blks}, step={self.step}' if self.step else '' def gen(): + yield 'Range Statistics:' yield ( 'Range: {start}-{end} ({range} blocks [{elapsed}]%s)' % step_disp, { 'start': self.hdrs[0]['height'], @@ -441,6 +455,7 @@ class BlocksInfo: rem = 2016 - rel return ( 'difficulty', ( + 'Difficulty Statistics:', ('Current height: {}', 'chain_tip', self.tip), ('Next diff adjust: {next_diff_adjust} (in {blks_remaining} block%s [{time_remaining}])' % suf(rem), { @@ -510,14 +525,14 @@ class JSONBlocksInfo(BlocksInfo): Msg_r( (', ','')[n==0] + json.dumps(data._asdict()) ) async def output_stats(self,res): - name,data = await res def gen(data): for d in data: if len(d) == 2: for k,v in d[1].items(): yield (k,v) - else: + elif len(d) == 3: yield (d[1],d[2]) + name,data = await res Msg_r(', "{}_data": {}'.format(name,json.dumps(dict(gen(data))))) def process_stats_pre(self,i): pass diff --git a/mmnode-feeview b/mmnode-feeview index 8e867fb..305094a 100755 --- a/mmnode-feeview +++ b/mmnode-feeview @@ -22,7 +22,7 @@ mmnode-feeview: Visualize the fee structure of a node’s mempool from mmgen.common import * -min_prec,max_prec,dfl_prec = (0,6,2) +min_prec,max_prec,dfl_prec = (0,6,4) fee_brackets = [ 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, From 0dfdcf73726a41f7ee1d1b3e5c40391b166f8ffc Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 21:29:22 +0000 Subject: [PATCH 040/175] mmnode-blocks-info: store internal data in unformatted form --- mmgen/node_tools/BlocksInfo.py | 67 ++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index e45b703..4fbe371 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -97,15 +97,14 @@ class BlocksInfo: all_stats = ['range','diff'] dfl_stats = ['range','diff'] - funcs = { - 'df': lambda self,loc: strftime('%Y-%m-%d %X',gmtime(self.t_cur)), - 'td': lambda self,loc: ( - '-{:02}:{:02}'.format(abs(self.t_diff)//60,abs(self.t_diff)%60) if self.t_diff < 0 else - ' {:02}:{:02}'.format(self.t_diff//60,self.t_diff%60) ), - 'tf': lambda self,loc: '{:.8f}'.format(loc.bs["totalfee"] * Decimal('0.00000001')), - 'fp': lambda self,loc: loc.bs['feerate_percentiles'], - 'su': lambda self,loc: str(loc.bs['subsidy'] * Decimal('0.00000001')).rstrip('0').rstrip('.'), - 'di': lambda self,loc: '{:.2e}'.format(loc.bh['difficulty']), + fmt_funcs = { + 'df': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), + 'td': lambda arg: ( + '-{:02}:{:02}'.format(abs(arg)//60,abs(self.t_diff)%60) if arg < 0 else + ' {:02}:{:02}'.format(arg//60,arg%60) ), + 'tf': lambda arg: '{:.8f}'.format(arg * Decimal('0.00000001')), + 'su': lambda arg: str(arg * Decimal('0.00000001')).rstrip('0').rstrip('.'), + 'di': lambda arg: '{:.2e}'.format(arg), } range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) @@ -159,10 +158,10 @@ class BlocksInfo: self.fs = ''.join(self.gen_fs(fnames)).strip() self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) - self.bs_keys = [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] - self.bs_keys.extend(['total_size','total_weight']) - - self.ufuncs = {v.varname:self.funcs[v.varname] for v in self.fvals if v.varname in self.funcs} + self.bs_keys = set( + [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] + + ['total_size','total_weight'] ) + self.blk_data_bs_add = set([(v.varname,v.bs_key) for v in self.fvals if v.bs_key in self.bs_keys]) if opt.miner_info: fnames.append('miner') @@ -321,26 +320,34 @@ class BlocksInfo: self.output_block(ret,n) def output_block(self,data,n): - Msg(self.fs.format(*data)) + def gen(): + for k,v in data._asdict().items(): + vn = self.fields[k].varname + yield self.fmt_funcs[vn](v) if vn in self.fmt_funcs else v + Msg(self.fs.format(*gen())) async def process_block(self,height,H,hdr): - class local_vars: pass - loc = local_vars() - loc.height = height - loc.H = H - loc.bh = hdr self.t_diff = hdr['time'] - self.t_cur self.t_cur = hdr['time'] self.total_solve_time += self.t_diff - if 'bs' in self.deps: - loc.bs = self.genesis_stats if height == 0 else await self.rpc.call('getblockstats',H,self.bs_keys) - self.total_bytes += loc.bs['total_size'] - self.total_weight += loc.bs['total_weight'] + blk_data = { + 'height': height, + 'H': H, + 'bh': hdr, + 'df': self.t_cur, + 'td': self.t_diff, + 'di': hdr['difficulty'] + } - for varname,func in self.ufuncs.items(): - setattr(loc,varname,func(self,loc)) + if 'bs' in self.deps: + bs = self.genesis_stats if height == 0 else await self.rpc.call('getblockstats',H,list(self.bs_keys)) + self.total_bytes += bs['total_size'] + self.total_weight += bs['total_weight'] + blk_data['bs'] = bs + for k1,k2 in self.blk_data_bs_add: + blk_data[k1] = bs[k2] if self.opt.miner_info: miner_info = '-' if height == 0 else await self.get_miner_string(H) @@ -348,9 +355,9 @@ class BlocksInfo: def gen(): for v in self.fvals: if v.key is None: - yield getattr(loc,v.varname) + yield blk_data[v.varname] else: - yield getattr(loc,v.varname)[v.key] + yield blk_data[v.varname][v.key] if self.opt.miner_info: yield miner_info @@ -522,7 +529,11 @@ class JSONBlocksInfo(BlocksInfo): Msg_r(']') def output_block(self,data,n): - Msg_r( (', ','')[n==0] + json.dumps(data._asdict()) ) + def gen(): + for k,v in data._asdict().items(): + vn = self.fields[k].varname + yield ( k, (self.fmt_funcs[vn](v) if vn in self.fmt_funcs else v) ) + Msg_r(json.dumps(dict(gen()))) async def output_stats(self,res): def gen(data): From 46bc925a5f39aae9f40623d95db4d85cb78cf75f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 21:39:40 +0000 Subject: [PATCH 041/175] mmnode-blocks-info: add --json-raw option --- mmgen/node_tools/BlocksInfo.py | 9 ++++++++- mmnode-blocks-info | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 4fbe371..46226c5 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -521,6 +521,8 @@ class JSONBlocksInfo(BlocksInfo): def __init__(self,cmd_args,opt,rpc): super().__init__(cmd_args,opt,rpc) + if opt.json_raw: + self.output_block = self.output_block_raw Msg_r('{') async def process_blocks(self): @@ -528,12 +530,17 @@ class JSONBlocksInfo(BlocksInfo): await super().process_blocks() Msg_r(']') + def output_block_raw(self,data,n): + Msg_r( (', ','')[n==0] + json.dumps(data._asdict()) ) + def output_block(self,data,n): def gen(): for k,v in data._asdict().items(): vn = self.fields[k].varname yield ( k, (self.fmt_funcs[vn](v) if vn in self.fmt_funcs else v) ) - Msg_r(json.dumps(dict(gen()))) + Msg_r( (', ','')[n==0] + json.dumps(dict(gen()))) + + def print_header(self): pass async def output_stats(self,res): def gen(data): diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 7fb9966..6a1b74d 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -29,7 +29,7 @@ opts_data = { ('hashes', True, 'stats', 'none'), ('stats', 'none', 'stats_only', False), ('stats_only', True, 'no_header', True), - ('json', True, 'no_header', True), + ('json_raw', True, 'json', True), ], 'text': { 'desc': 'Display information about a block or range of blocks', @@ -44,6 +44,7 @@ opts_data = { --, --longhelp Print help message for long options (common options) -H, --hashes Display only block numbers and hashes -j, --json Produce JSON output +-J, --json-raw Produce JSON output with unformatted values -m, --miner-info Display miner info in coinbase transaction -M, --raw-miner-info Display miner info in uninterpreted form -n, --no-header Don’t print the column header From 6a761f8ac92a803cb886dcb05b9b8eb8757936fc Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Mar 2021 21:43:11 +0000 Subject: [PATCH 042/175] mmnode-blocks-info: add average stats --- mmgen/node_tools/BlocksInfo.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 46226c5..fa6136f 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -94,7 +94,7 @@ class BlocksInfo: ] fs_lsqueeze2 = ['interval'] - all_stats = ['range','diff'] + all_stats = ['avg','range','diff'] dfl_stats = ['range','diff'] fmt_funcs = { @@ -312,10 +312,12 @@ class BlocksInfo: if not self.block_list: await init(0) + self.res = [] for n in range(len(heights)): if self.block_list: await init(n) ret = await self.process_block(heights[n],hashes[n],self.hdrs[n]) + self.res.append(ret) if not self.opt.stats_only: self.output_block(ret,n) @@ -481,6 +483,21 @@ class BlocksInfo: ('Est. diff adjust: {}%', 'est_diff_adjust_pct', f'{((600 / bdi) - 1) * 100:+.2f}'), )) + async def create_avg_stats(self): + skip = ('block', 'hash', 'date', 'version','miner') + fields = self.block_data._fields + nblocks = len(self.res) + def gen(): + for field in fields: + if field in skip: + yield '' + else: + ret = sum(getattr(block,field) for block in self.res) // nblocks + vn = self.fields[field].varname + yield self.fmt_funcs[vn](ret) if vn in self.fmt_funcs else ret + fs = ''.join(self.gen_fs(fields,fill=skip)).strip() + return ( 'averages', ( 'Averages:', fs.format(*gen()) ) ) + def process_stats_pre(self,i): if not (self.opt.stats_only and i == 0): Msg('') From bf15039e45e01e31782dfb01c6231c9f5600fabc Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 10:41:50 +0000 Subject: [PATCH 043/175] mmnode-blocks-info: allow --miner-info or -o +miner, or both --- mmgen/node_tools/BlocksInfo.py | 35 +++++++++++++--------------------- mmnode-blocks-info | 3 ++- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index fa6136f..80e428e 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -152,10 +152,12 @@ class BlocksInfo: self.block_list,self.first,self.last,self.step = parse_cmd_args() - fnames = get_fields() if opt.fields else self.dfl_fields + self.fnames = get_fields() if opt.fields else self.dfl_fields + if opt.miner_info and 'miner' not in self.fnames: + self.fnames.append('miner') - self.fvals = list(self.fields[name] for name in fnames) - self.fs = ''.join(self.gen_fs(fnames)).strip() + self.fvals = list(self.fields[name] for name in self.fnames) + self.fs = ''.join(self.gen_fs(self.fnames)).strip() self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) self.bs_keys = set( @@ -163,9 +165,7 @@ class BlocksInfo: + ['total_size','total_weight'] ) self.blk_data_bs_add = set([(v.varname,v.bs_key) for v in self.fvals if v.bs_key in self.bs_keys]) - if opt.miner_info: - fnames.append('miner') - self.fs += ' ' + self.fields['miner'].fs + if 'miner' in self.fnames: self.miner_pats = [re.compile(pat) for pat in ( rb'`/([_a-zA-Z0-9&. #/-]+)/', rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', @@ -176,10 +176,8 @@ class BlocksInfo: rb'[/^]([a-zA-Z0-9&. #/-]{5,})', rb'[/^]([_a-zA-Z0-9&. #/-]+)/', )] - else: - self.miner_pats = None - self.block_data = namedtuple('block_data',fnames) + self.block_data = namedtuple('block_data',self.fnames) self.stats = get_stats() if opt.stats else self.dfl_stats def gen_fs(self,fnames,fill=[],fill_char='-'): @@ -351,8 +349,8 @@ class BlocksInfo: for k1,k2 in self.blk_data_bs_add: blk_data[k1] = bs[k2] - if self.opt.miner_info: - miner_info = '-' if height == 0 else await self.get_miner_string(H) + if 'miner' in self.fnames: + blk_data['mi'] = '-' if height == 0 else await self.get_miner_string(H) def gen(): for v in self.fvals: @@ -360,8 +358,6 @@ class BlocksInfo: yield blk_data[v.varname] else: yield blk_data[v.varname][v.key] - if self.opt.miner_info: - yield miner_info return self.block_data(*gen()) @@ -387,10 +383,7 @@ class BlocksInfo: def gen_header(self): hdr1 = [v.hdr1 for v in self.fvals] hdr2 = [v.hdr2 for v in self.fvals] - if self.opt.miner_info: - hdr1.append(' ') - hdr2.append('Miner') - if ''.join(hdr1).replace(' ',''): + if ''.join(hdr1): yield self.fs.format(*hdr1) yield self.fs.format(*hdr2) @@ -485,17 +478,15 @@ class BlocksInfo: async def create_avg_stats(self): skip = ('block', 'hash', 'date', 'version','miner') - fields = self.block_data._fields - nblocks = len(self.res) def gen(): - for field in fields: + for field in self.fnames: if field in skip: yield '' else: - ret = sum(getattr(block,field) for block in self.res) // nblocks + ret = sum(getattr(block,field) for block in self.res) // len(self.res) vn = self.fields[field].varname yield self.fmt_funcs[vn](ret) if vn in self.fmt_funcs else ret - fs = ''.join(self.gen_fs(fields,fill=skip)).strip() + fs = ''.join(self.gen_fs(self.fnames,fill=skip)).strip() return ( 'averages', ( 'Averages:', fs.format(*gen()) ) ) def process_stats_pre(self,i): diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 6a1b74d..148e38b 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -30,6 +30,7 @@ opts_data = { ('stats', 'none', 'stats_only', False), ('stats_only', True, 'no_header', True), ('json_raw', True, 'json', True), + ('raw_miner_info',True,'miner_info', True), ], 'text': { 'desc': 'Display information about a block or range of blocks', @@ -93,7 +94,7 @@ EXAMPLES: {p} -- -1000+50+5 # Display info for block 152817, adding miner field: - {p} --miner-info 152817 + {p} -o +miner 152817 # Display specified fields for listed blocks: {p} -o block,date,hash 245798 170 624044 From 1f40e9069ba69268d35cac304285a4292ea293a7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 10:43:03 +0000 Subject: [PATCH 044/175] mmnode-blocks-info: fix regression in negative solve time display --- mmgen/node_tools/BlocksInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 80e428e..5190aab 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -100,7 +100,7 @@ class BlocksInfo: fmt_funcs = { 'df': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), 'td': lambda arg: ( - '-{:02}:{:02}'.format(abs(arg)//60,abs(self.t_diff)%60) if arg < 0 else + '-{:02}:{:02}'.format(abs(arg)//60,abs(arg)%60) if arg < 0 else ' {:02}:{:02}'.format(arg//60,arg%60) ), 'tf': lambda arg: '{:.8f}'.format(arg * Decimal('0.00000001')), 'su': lambda arg: str(arg * Decimal('0.00000001')).rstrip('0').rstrip('.'), From d4b940720b3cd721b9fc9f2a3d0b233815f1e00a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 12:34:41 +0000 Subject: [PATCH 045/175] mmnode-blocks-info: allow JSON encoding of Decimal values --- mmgen/node_tools/BlocksInfo.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 5190aab..6c70e04 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -26,6 +26,7 @@ from time import strftime,gmtime from decimal import Decimal from mmgen.common import * +from mmgen.rpc import json_encoder class BlocksInfo: @@ -539,14 +540,14 @@ class JSONBlocksInfo(BlocksInfo): Msg_r(']') def output_block_raw(self,data,n): - Msg_r( (', ','')[n==0] + json.dumps(data._asdict()) ) + Msg_r( (', ','')[n==0] + json.dumps(data._asdict(),cls=json_encoder) ) def output_block(self,data,n): def gen(): for k,v in data._asdict().items(): vn = self.fields[k].varname yield ( k, (self.fmt_funcs[vn](v) if vn in self.fmt_funcs else v) ) - Msg_r( (', ','')[n==0] + json.dumps(dict(gen()))) + Msg_r( (', ','')[n==0] + json.dumps(dict(gen()),cls=json_encoder) ) def print_header(self): pass @@ -558,8 +559,8 @@ class JSONBlocksInfo(BlocksInfo): yield (k,v) elif len(d) == 3: yield (d[1],d[2]) - name,data = await res - Msg_r(', "{}_data": {}'.format(name,json.dumps(dict(gen(data))))) + varname,data = await res + Msg_r(', "{}_data": {}'.format( varname, json.dumps(dict(gen(data)),cls=json_encoder) )) def process_stats_pre(self,i): pass From b220d8822659d25dbad1e7fdd6e774453436024f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 12:43:32 +0000 Subject: [PATCH 046/175] mmnode-blocks-info: enable JSON output of averages stats --- mmgen/node_tools/BlocksInfo.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 6c70e04..5dec41f 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -97,6 +97,7 @@ class BlocksInfo: all_stats = ['avg','range','diff'] dfl_stats = ['range','diff'] + noindent_stats = ['avg'] fmt_funcs = { 'df': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), @@ -181,7 +182,7 @@ class BlocksInfo: self.block_data = namedtuple('block_data',self.fnames) self.stats = get_stats() if opt.stats else self.dfl_stats - def gen_fs(self,fnames,fill=[],fill_char='-'): + def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): for i in range(len(fnames)): name = fnames[i] ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] @@ -193,11 +194,8 @@ class BlocksInfo: if name in group and fnames[i-1] in group: ls = '' break - yield ( - ls - + (self.fields[name].fs.replace(':',':'+fill_char) if name in fill else self.fields[name].fs) - + rs - ) + repl = (name if add_name else '') + ':' + (fill_char if name in fill else '') + yield (ls + self.fields[name].fs.replace(':',repl) + rs) def conv_blkspec(self,arg): if arg == 'cur': @@ -388,19 +386,20 @@ class BlocksInfo: yield self.fs.format(*hdr1) yield self.fs.format(*hdr2) - def process_stats(self,name): - return self.output_stats(getattr(self,f'create_{name}_stats')()) + def process_stats(self,sname): + return self.output_stats(getattr(self,f'create_{sname}_stats')(),sname) - async def output_stats(self,res): + async def output_stats(self,res,sname): def gen(data): for d in data: if len(d) == 2: - yield (' '+d[0]).format(**d[1]) + yield (indent+d[0]).format(**d[1]) elif len(d) == 3: - yield (' '+d[0]).format(d[2]) + yield (indent+d[0]).format(d[2]) else: yield d - name,data = await res + foo,data = await res + indent = '' if sname in self.noindent_stats else ' ' Msg('\n'.join(gen(data))) async def create_range_stats(self): @@ -482,13 +481,13 @@ class BlocksInfo: def gen(): for field in self.fnames: if field in skip: - yield '' + yield ( field, '' ) else: ret = sum(getattr(block,field) for block in self.res) // len(self.res) vn = self.fields[field].varname - yield self.fmt_funcs[vn](ret) if vn in self.fmt_funcs else ret - fs = ''.join(self.gen_fs(self.fnames,fill=skip)).strip() - return ( 'averages', ( 'Averages:', fs.format(*gen()) ) ) + yield ( field, (self.fmt_funcs[vn](ret) if vn in self.fmt_funcs else ret) ) + fs = ''.join(self.gen_fs(self.fnames,fill=skip,add_name=True)).strip() + return ('averages', ('Averages:', (fs, dict(gen())) )) def process_stats_pre(self,i): if not (self.opt.stats_only and i == 0): @@ -551,7 +550,7 @@ class JSONBlocksInfo(BlocksInfo): def print_header(self): pass - async def output_stats(self,res): + async def output_stats(self,res,sname): def gen(data): for d in data: if len(d) == 2: From db98ef9ea17aac87983d59d2028e164bcb5a961e Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 12:45:41 +0000 Subject: [PATCH 047/175] mmnode-blocks-info: support -o all, -o none, -o - - The --stats-only option has been removed, since -o none produces the equivalent output. - -s - is also supported. --- mmgen/node_tools/BlocksInfo.py | 36 +++++++++++---- mmnode-blocks-info | 84 ++++++++++++++++++---------------- 2 files changed, 70 insertions(+), 50 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 5dec41f..b0fb1a2 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -116,23 +116,32 @@ class BlocksInfo: def __init__(self,cmd_args,opt,rpc): def parse_cslist(uarg,full_set,dfl_set,desc): - usr_set = uarg.lstrip('+').split(',') + m = re.match('([+-]){1}',uarg) + pfx = m[1] if m else None + usr_set = set((uarg[1:] if m else uarg).split(',')) + dfl_set = set(dfl_set) for e in usr_set: if e not in full_set: die(1,f'{e!r}: unrecognized {desc}') - res = dfl_set + usr_set if uarg[0] == '+' else usr_set + res = ( + dfl_set | usr_set if pfx == '+' else + dfl_set - usr_set if pfx == '-' else + usr_set + ) # display elements in order: return [e for e in full_set if e in res] + def parse_cs_uarg(uarg,full_set,dfl_set,desc): + return ( + full_set if uarg == 'all' else [] if uarg == 'none' else + parse_cslist(uarg,full_set,dfl_set,desc) + ) + def get_fields(): - return parse_cslist(opt.fields,self.fields,self.dfl_fields,'field') + return parse_cs_uarg(opt.fields,list(self.fields),self.dfl_fields,'field') def get_stats(): - arg = opt.stats.lower() - return ( - self.all_stats if arg == 'all' else [] if arg == 'none' else - parse_cslist(arg,self.all_stats,self.dfl_stats,'stat') - ) + return parse_cs_uarg(opt.stats.lower(),self.all_stats,self.dfl_stats,'stat') def parse_cmd_args(): # => (block_list, first, last, step) if not cmd_args: @@ -182,6 +191,13 @@ class BlocksInfo: self.block_data = namedtuple('block_data',self.fnames) self.stats = get_stats() if opt.stats else self.dfl_stats + # Display diff stats by default only if user-requested range ends with chain tip + if 'diff' in self.stats and not opt.stats and self.last != self.tip: + self.stats.remove('diff') + + if 'avg' in self.stats and not self.fnames: + self.stats.remove('avg') + def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): for i in range(len(fnames)): name = fnames[i] @@ -315,7 +331,7 @@ class BlocksInfo: await init(n) ret = await self.process_block(heights[n],hashes[n],self.hdrs[n]) self.res.append(ret) - if not self.opt.stats_only: + if self.fnames: self.output_block(ret,n) def output_block(self,data,n): @@ -490,7 +506,7 @@ class BlocksInfo: return ('averages', ('Averages:', (fs, dict(gen())) )) def process_stats_pre(self,i): - if not (self.opt.stats_only and i == 0): + if self.fnames or i != 0: Msg('') def finalize_output(self): pass diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 148e38b..2e9e24c 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -27,8 +27,6 @@ opts_data = { 'sets': [ ('hashes', True, 'fields', 'block,hash'), ('hashes', True, 'stats', 'none'), - ('stats', 'none', 'stats_only', False), - ('stats_only', True, 'no_header', True), ('json_raw', True, 'json', True), ('raw_miner_info',True,'miner_info', True), ], @@ -50,17 +48,19 @@ opts_data = { -M, --raw-miner-info Display miner info in uninterpreted form -n, --no-header Don’t print the column header -o, --fields= Display the specified fields (comma-separated list). - See AVAILABLE FIELDS below. If the first character - is '+', specified fields are added to the defaults. + See AVAILABLE FIELDS below. Prefix the list with '+' + to add the fields to the defaults, or '-' to remove + them. The special values 'all' and 'none' select all + available fields or none, respectively. -s, --stats= Display the specified stats (comma-separated list). - See AVAILABLE STATS below. If the first character is - '+', specified stats are added to the defaults. Use - 'none' to disable, or 'all' for all available stats. --S, --stats-only Display stats only. Skip display of per-block data. + See AVAILABLE STATS below. The prefixes and special + values available to the --fields option are recognized. """, 'notes': """ -If no block number is specified, the current block is assumed. The string -'cur' can be used in place of the current block number. +If no block number is specified, the current chain tip is assumed. + +The special value 'cur' can be used to designate the chain tip wherever a +block number is expected. If the requested range ends at the current chain tip, an estimate of the next difficulty adjustment is also displayed. The estimate is based on the average @@ -74,40 +74,47 @@ AVAILABLE STATS: {s} EXAMPLES: - # Display info for current block: - {p} + Display info for current block: + $ {p} - # Display info for the Genesis Block: - {p} 0 + Display info for the Genesis Block: + $ {p} 0 - # Display info for the last 20 blocks: - {p} +20 + Display info for the last 20 blocks: + $ {p} +20 - # Display specified fields for blocks 165-190 - {p} -o block,date,size,inputs,nTx 165-190 + Display specified fields for blocks 165-190 + $ {p} -o block,date,size,inputs,nTx 165-190 - # Display info for 10 blocks beginning at block 600000: - {p} 600000+10 + Display info for 10 blocks beginning at block 600000: + $ {p} 600000+10 - # Display info for every 5th block of 50-block range beginning at 1000 - # blocks from chain tip: - {p} -- -1000+50+5 + Display info for every 5th block of 50-block range beginning at 1000 + blocks from chain tip: + $ {p} -- -1000+50+5 - # Display info for block 152817, adding miner field: - {p} -o +miner 152817 + Display info for block 152817, adding miner field: + $ {p} -o +miner 152817 - # Display specified fields for listed blocks: - {p} -o block,date,hash 245798 170 624044 + Display specified fields for listed blocks: + $ {p} -o block,date,hash 245798 170 624044 - # Display every difficulty adjustment from Genesis Block to chain tip: - {p} -o +difficulty 0-cur+2016 + Display every difficulty adjustment from Genesis Block to chain tip: + $ {p} -o +difficulty 0-cur+2016 - # Display roughly a block a day over the last two weeks. Note that - # multiplication is allowed in the nBlocks spec: - {p} +144*14+144 + Display roughly a block a day over the last two weeks. Note that + multiplication is allowed in the nBlocks spec: + $ {p} +144*14+144 - # Display only range stats for the last ten blocks: - {p} -s range -S +10 + Display only range stats for the last ten blocks: + $ {p} -o none -s range +10 + + Display data for the last ten blocks, skipping 'size' and 'subsidy' + fields and stats: + $ {p} -o -size,subsidy -s none +10 + + Display all fields and stats for the last ten blocks: + $ {p} -o all -s all +10 This program requires a txindex-enabled daemon for correct operation. """.format( @@ -130,18 +137,15 @@ async def main(): m = cls( cmd_args, opt, await rpc_init(proto) ) - if not opt.no_header: + if m.fnames and not opt.no_header: m.print_header() await m.process_blocks() if m.last and m.stats: - for i,stat in enumerate(m.stats): - if stat == 'diff': # Display diff stats by default only if user-requested range ends with chain tip - if not opt.stats and m.last != m.tip: - continue + for i,sname in enumerate(m.stats): m.process_stats_pre(i) - await m.process_stats(stat) + await m.process_stats(sname) m.finalize_output() From 2e7c50bf80d36edff4fa50c99e311f1679ce415b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 15:29:11 +0000 Subject: [PATCH 048/175] mmnode-blocks-info: produce unformatted stats values with --json-raw --- mmgen/node_tools/BlocksInfo.py | 69 +++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index b0fb1a2..6778e04 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -405,15 +405,22 @@ class BlocksInfo: def process_stats(self,sname): return self.output_stats(getattr(self,f'create_{sname}_stats')(),sname) + def fmt_stat_item(self,fs,s): + return fs.format(s) if type(fs) == str else fs(s) + async def output_stats(self,res,sname): + def gen(data): for d in data: if len(d) == 2: - yield (indent+d[0]).format(**d[1]) - elif len(d) == 3: - yield (indent+d[0]).format(d[2]) - else: + yield (indent+d[0]).format(**{k:self.fmt_stat_item(*v) for k,v in d[1].items()}) + elif len(d) == 4: + yield (indent+d[0]).format(self.fmt_stat_item(d[2],d[3])) + elif type(d) == str: yield d + else: + assert False, f'{d}: invalid stats data' + foo,data = await res indent = '' if sname in self.noindent_stats else ' ' Msg('\n'.join(gen(data))) @@ -428,22 +435,22 @@ class BlocksInfo: yield 'Range Statistics:' yield ( 'Range: {start}-{end} ({range} blocks [{elapsed}]%s)' % step_disp, { - 'start': self.hdrs[0]['height'], - 'end': self.hdrs[-1]['height'], - 'range': self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1, # includes Genesis Block - 'elapsed': self.t_fmt(elapsed), - 'nBlocks': total_blks, - 'step': self.step, + 'start': ('{}', self.hdrs[0]['height']), + 'end': ('{}', self.hdrs[-1]['height']), + 'range': ('{}', self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1), + 'elapsed': (self.t_fmt, elapsed), + 'nBlocks': ('{}', total_blks), + 'step': ('{}', self.step), } ) if elapsed: avg_bdi = int(elapsed / nblocks) if 'bs' in self.deps: rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) - yield ( 'Avg size: {} bytes', 'avg_size', self.total_bytes//total_blks ) - yield ( 'Avg weight: {} bytes', 'avg_weight', self.total_weight//total_blks ) - yield ( 'MB/hr: {}', 'mb_per_hour', f'{rate:0.4f}' ) - yield ('Avg BDI: {} min', 'avg_bdi', f'{avg_bdi/60:.2f}') + yield ( 'Avg size: {} bytes', 'avg_size', '{}', self.total_bytes//total_blks ) + yield ( 'Avg weight: {} bytes', 'avg_weight', '{}', self.total_weight//total_blks ) + yield ( 'MB/hr: {}', 'mb_per_hour', '{:0.4f}', rate ) + yield ('Avg BDI: {} min', 'avg_bdi', '{:.2f}', avg_bdi/60) return ( 'range', gen() ) @@ -474,22 +481,22 @@ class BlocksInfo: return ( 'difficulty', ( 'Difficulty Statistics:', - ('Current height: {}', 'chain_tip', self.tip), + ('Current height: {}', 'chain_tip', '{}', self.tip), ('Next diff adjust: {next_diff_adjust} (in {blks_remaining} block%s [{time_remaining}])' % suf(rem), { - 'next_diff_adjust': self.tip + rem, - 'blks_remaining': rem, - 'time_remaining': self.t_fmt(rem * bdi_avg) + 'next_diff_adjust': ('{}', self.tip + rem), + 'blks_remaining': ('{}', rem), + 'time_remaining': (self.t_fmt, rem * bdi_avg) } ), ('Avg BDI: {avg_bdi} min (over {avg_bdi_blks}-block period)', { - 'avg_bdi': f'{bdi_disp/60:.2f}', - 'avg_bdi_blks': max(rel,bdi_avg_blks) + 'avg_bdi': ('{:.2f}', bdi_disp/60), + 'avg_bdi_blks': ('{}', max(rel,bdi_avg_blks)) } ), - ('Cur difficulty: {}', 'cur_diff', f'{tip_hdr["difficulty"]:.2e}'), - ('Est. diff adjust: {}%', 'est_diff_adjust_pct', f'{((600 / bdi) - 1) * 100:+.2f}'), + ('Cur difficulty: {}', 'cur_diff', '{:.2e}', tip_hdr['difficulty']), + ('Est. diff adjust: {}%', 'est_diff_adjust_pct', '{:+.2f}', ((600 / bdi) - 1) * 100), )) async def create_avg_stats(self): @@ -497,11 +504,11 @@ class BlocksInfo: def gen(): for field in self.fnames: if field in skip: - yield ( field, '' ) + yield ( field, ('{}','') ) else: ret = sum(getattr(block,field) for block in self.res) // len(self.res) vn = self.fields[field].varname - yield ( field, (self.fmt_funcs[vn](ret) if vn in self.fmt_funcs else ret) ) + yield ( field, ( (self.fmt_funcs[vn] if vn in self.fmt_funcs else '{}'), ret )) fs = ''.join(self.gen_fs(self.fnames,fill=skip,add_name=True)).strip() return ('averages', ('Averages:', (fs, dict(gen())) )) @@ -547,6 +554,7 @@ class JSONBlocksInfo(BlocksInfo): super().__init__(cmd_args,opt,rpc) if opt.json_raw: self.output_block = self.output_block_raw + self.fmt_stat_item = self.fmt_stat_item_raw Msg_r('{') async def process_blocks(self): @@ -566,14 +574,21 @@ class JSONBlocksInfo(BlocksInfo): def print_header(self): pass + def fmt_stat_item_raw(self,fs,s): + return s + async def output_stats(self,res,sname): + def gen(data): for d in data: if len(d) == 2: for k,v in d[1].items(): - yield (k,v) - elif len(d) == 3: - yield (d[1],d[2]) + yield (k,self.fmt_stat_item(*v)) + elif len(d) == 4: + yield (d[1],self.fmt_stat_item(d[2],d[3])) + elif type(d) != str: + assert False, f'{d}: invalid stats data' + varname,data = await res Msg_r(', "{}_data": {}'.format( varname, json.dumps(dict(gen(data)),cls=json_encoder) )) From 030869ec7d395383fbad6dd48f6ab8196fd53fd0 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 15:55:19 +0000 Subject: [PATCH 049/175] mmnode-blocks-info: display full range stats with -o none --- mmgen/node_tools/BlocksInfo.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 6778e04..d7b1ad3 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -169,7 +169,6 @@ class BlocksInfo: self.fvals = list(self.fields[name] for name in self.fnames) self.fs = ''.join(self.gen_fs(self.fnames)).strip() - self.deps = set(' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split()) self.bs_keys = set( [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] @@ -198,6 +197,11 @@ class BlocksInfo: if 'avg' in self.stats and not self.fnames: self.stats.remove('avg') + self.deps = set( + ' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split() + + ( ['bs'] if 'range' in self.stats else [] ) + ) + def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): for i in range(len(fnames)): name = fnames[i] @@ -445,12 +449,11 @@ class BlocksInfo: ) if elapsed: avg_bdi = int(elapsed / nblocks) - if 'bs' in self.deps: - rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) - yield ( 'Avg size: {} bytes', 'avg_size', '{}', self.total_bytes//total_blks ) - yield ( 'Avg weight: {} bytes', 'avg_weight', '{}', self.total_weight//total_blks ) - yield ( 'MB/hr: {}', 'mb_per_hour', '{:0.4f}', rate ) - yield ('Avg BDI: {} min', 'avg_bdi', '{:.2f}', avg_bdi/60) + rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) + yield ( 'Avg size: {} bytes', 'avg_size', '{}', self.total_bytes//total_blks ) + yield ( 'Avg weight: {} bytes', 'avg_weight', '{}', self.total_weight//total_blks ) + yield ( 'MB/hr: {}', 'mb_per_hour', '{:0.4f}', rate ) + yield ( 'Avg BDI: {} min', 'avg_bdi', '{:.2f}', avg_bdi/60 ) return ( 'range', gen() ) From 6d6c640619d6869b1dbb2a47a65fa18b3ee49501 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 21 Mar 2021 19:31:04 +0000 Subject: [PATCH 050/175] mmnode-blocks-info: cleanups --- mmgen/node_tools/BlocksInfo.py | 105 +++++++++++++++------------------ 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index d7b1ad3..e582cc7 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -34,33 +34,32 @@ class BlocksInfo: total_weight = 0 total_solve_time = 0 - bf = namedtuple('block_info_fields',['hdr1','hdr2','fs','bs_key','varname','deps','key']) - # bs=getblockstats(), bh=getblockheader() - # If 'bs_key' is set, it's included in self.bs_keys instead of 'key' + bf = namedtuple('block_info_fields',['fmt_func','src','fs','hdr1','hdr2','key1','key2']) + # bh=getblockheader, bs=getblockstats, lo=local fields = { - 'block': bf('', 'Block', '{:<6}', None, 'height',[], None), - 'hash': bf('', 'Hash', '{:<64}', None, 'H', [], None), - 'date': bf('', 'Date', '{:<19}', None, 'df', [], None), - 'interval': bf('Solve','Time ', '{:>8}', None, 'td', [], None), - 'subsidy': bf('Sub-', 'sidy', '{:<5}', 'subsidy', 'su', ['bs'], None), - 'totalfee': bf('', 'Total Fee','{:>10}', 'totalfee', 'tf', ['bs'], None), - 'size': bf('', 'Size', '{:>7}', None, 'bs', [], 'total_size'), - 'weight': bf('', 'Weight', '{:>7}', None, 'bs', [], 'total_weight'), - 'fee90': bf('90%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 4), - 'fee75': bf('75%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 3), - 'fee50': bf('50%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 2), - 'fee25': bf('25%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 1), - 'fee10': bf('10%', 'Fee', '{:>3}', 'feerate_percentiles','fp', ['bs'], 0), - 'fee_max': bf('Max', 'Fee', '{:>5}', None, 'bs', [], 'maxfeerate'), - 'fee_avg': bf('Avg', 'Fee', '{:>3}', None, 'bs', [], 'avgfeerate'), - 'fee_min': bf('Min', 'Fee', '{:>3}', None, 'bs', [], 'minfeerate'), - 'nTx': bf('', ' nTx ', '{:>5}', None, 'bh', [], 'nTx'), - 'inputs': bf('In- ', 'puts', '{:>5}', None, 'bs', [], 'ins'), - 'outputs': bf('Out-', 'puts', '{:>5}', None, 'bs', [], 'outs'), - 'utxo_inc': bf(' UTXO',' Incr', '{:>5}', None, 'bs', [], 'utxo_increase'), - 'version': bf('', 'Version', '{:<8}', None, 'bh', [], 'versionHex'), - 'difficulty':bf('Diffi-','culty', '{:<8}', None, 'di', [], None), - 'miner': bf('', 'Miner', '{:<5}', None, 'mi', [], None), + 'block': bf( None, 'bh', '{:<6}', '', 'Block', 'height', None ), + 'hash': bf( None, 'bh', '{:<64}', '', 'Hash', 'hash', None ), + 'date': bf( 'da', 'bh', '{:<19}', '', 'Date', 'time', None ), + 'interval': bf( 'td', 'lo', '{:>8}', 'Solve', 'Time ', 'interval', None ), + 'subsidy': bf( 'su', 'bs', '{:<5}', 'Sub-', 'sidy', 'subsidy', None ), + 'totalfee': bf( 'tf', 'bs', '{:>10}', '', 'Total Fee', 'totalfee', None ), + 'size': bf( None, 'bs', '{:>7}', '', 'Size', 'total_size', None ), + 'weight': bf( None, 'bs', '{:>7}', '', 'Weight', 'total_weight', None ), + 'fee90': bf( None, 'bs', '{:>3}', '90%', 'Fee', 'feerate_percentiles', 4 ), + 'fee75': bf( None, 'bs', '{:>3}', '75%', 'Fee', 'feerate_percentiles', 3 ), + 'fee50': bf( None, 'bs', '{:>3}', '50%', 'Fee', 'feerate_percentiles', 2 ), + 'fee25': bf( None, 'bs', '{:>3}', '25%', 'Fee', 'feerate_percentiles', 1 ), + 'fee10': bf( None, 'bs', '{:>3}', '10%', 'Fee', 'feerate_percentiles', 0 ), + 'fee_max': bf( None, 'bs', '{:>5}', 'Max', 'Fee', 'maxfeerate', None ), + 'fee_avg': bf( None, 'bs', '{:>3}', 'Avg', 'Fee', 'avgfeerate', None ), + 'fee_min': bf( None, 'bs', '{:>3}', 'Min', 'Fee', 'minfeerate', None ), + 'nTx': bf( None, 'bh', '{:>5}', '', ' nTx ', 'nTx', None ), + 'inputs': bf( None, 'bs', '{:>5}', 'In- ', 'puts', 'ins', None ), + 'outputs': bf( None, 'bs', '{:>5}', 'Out-', 'puts', 'outs', None ), + 'utxo_inc': bf( None, 'bs', '{:>5}', ' UTXO', ' Incr', 'utxo_increase', None ), + 'version': bf( None, 'bh', '{:<8}', '', 'Version', 'versionHex', None ), + 'difficulty': bf( 'di', 'bh', '{:<8}', 'Diffi-','culty', 'difficulty', None ), + 'miner': bf( None, 'lo', '{:<5}', '', 'Miner', 'miner', None ), } dfl_fields = [ 'block', @@ -100,7 +99,7 @@ class BlocksInfo: noindent_stats = ['avg'] fmt_funcs = { - 'df': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), + 'da': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), 'td': lambda arg: ( '-{:02}:{:02}'.format(abs(arg)//60,abs(arg)%60) if arg < 0 else ' {:02}:{:02}'.format(arg//60,arg%60) ), @@ -167,13 +166,13 @@ class BlocksInfo: if opt.miner_info and 'miner' not in self.fnames: self.fnames.append('miner') - self.fvals = list(self.fields[name] for name in self.fnames) + self.fvals = [self.fields[name] for name in self.fnames] self.fs = ''.join(self.gen_fs(self.fnames)).strip() self.bs_keys = set( - [(v.bs_key or v.key) for v in self.fvals if v.bs_key or v.varname == 'bs'] - + ['total_size','total_weight'] ) - self.blk_data_bs_add = set([(v.varname,v.bs_key) for v in self.fvals if v.bs_key in self.bs_keys]) + [v.key1 for v in self.fvals if v.src == 'bs'] + + ['total_size','total_weight'] + ) if 'miner' in self.fnames: self.miner_pats = [re.compile(pat) for pat in ( @@ -197,10 +196,7 @@ class BlocksInfo: if 'avg' in self.stats and not self.fnames: self.stats.remove('avg') - self.deps = set( - ' '.join(v.varname + ' ' + ' '.join(v.deps) for v in self.fvals).split() - + ( ['bs'] if 'range' in self.stats else [] ) - ) + self.deps = set([v.src for v in self.fvals] + (['bs'] if 'range' in self.stats else [])) def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): for i in range(len(fnames)): @@ -333,7 +329,7 @@ class BlocksInfo: for n in range(len(heights)): if self.block_list: await init(n) - ret = await self.process_block(heights[n],hashes[n],self.hdrs[n]) + ret = await self.process_block(self.hdrs[n]) self.res.append(ret) if self.fnames: self.output_block(ret,n) @@ -341,42 +337,39 @@ class BlocksInfo: def output_block(self,data,n): def gen(): for k,v in data._asdict().items(): - vn = self.fields[k].varname - yield self.fmt_funcs[vn](v) if vn in self.fmt_funcs else v + func = self.fields[k].fmt_func + yield self.fmt_funcs[func](v) if func else v Msg(self.fs.format(*gen())) - async def process_block(self,height,H,hdr): + async def process_block(self,hdr): self.t_diff = hdr['time'] - self.t_cur self.t_cur = hdr['time'] self.total_solve_time += self.t_diff blk_data = { - 'height': height, - 'H': H, 'bh': hdr, - 'df': self.t_cur, - 'td': self.t_diff, - 'di': hdr['difficulty'] + 'lo': { 'interval': self.t_diff } } if 'bs' in self.deps: - bs = self.genesis_stats if height == 0 else await self.rpc.call('getblockstats',H,list(self.bs_keys)) + bs = ( + self.genesis_stats if hdr['height'] == 0 else + await self.rpc.call('getblockstats',hdr['hash'],list(self.bs_keys)) + ) self.total_bytes += bs['total_size'] self.total_weight += bs['total_weight'] blk_data['bs'] = bs - for k1,k2 in self.blk_data_bs_add: - blk_data[k1] = bs[k2] if 'miner' in self.fnames: - blk_data['mi'] = '-' if height == 0 else await self.get_miner_string(H) + blk_data['lo']['miner'] = '-' if hdr['height'] == 0 else await self.get_miner_string(hdr['hash']) def gen(): for v in self.fvals: - if v.key is None: - yield blk_data[v.varname] - else: - yield blk_data[v.varname][v.key] + yield ( + blk_data[v.src][v.key1] if v.key2 is None else + blk_data[v.src][v.key1][v.key2] + ) return self.block_data(*gen()) @@ -510,8 +503,8 @@ class BlocksInfo: yield ( field, ('{}','') ) else: ret = sum(getattr(block,field) for block in self.res) // len(self.res) - vn = self.fields[field].varname - yield ( field, ( (self.fmt_funcs[vn] if vn in self.fmt_funcs else '{}'), ret )) + func = self.fields[field].fmt_func + yield ( field, ( (self.fmt_funcs[func] if func else '{}'), ret )) fs = ''.join(self.gen_fs(self.fnames,fill=skip,add_name=True)).strip() return ('averages', ('Averages:', (fs, dict(gen())) )) @@ -571,8 +564,8 @@ class JSONBlocksInfo(BlocksInfo): def output_block(self,data,n): def gen(): for k,v in data._asdict().items(): - vn = self.fields[k].varname - yield ( k, (self.fmt_funcs[vn](v) if vn in self.fmt_funcs else v) ) + func = self.fields[k].fmt_func + yield ( k, (self.fmt_funcs[func](v) if func else v) ) Msg_r( (', ','')[n==0] + json.dumps(dict(gen()),cls=json_encoder) ) def print_header(self): pass From 707a1d01b9854fe582ea3843027f628e1831ed24 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 22 Mar 2021 14:32:58 +0000 Subject: [PATCH 051/175] mmnode-blocks-info: minor cleanups --- mmgen/node_tools/BlocksInfo.py | 34 +++++++++++++++++++++------------- mmnode-blocks-info | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index e582cc7..1f895da 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -56,12 +56,12 @@ class BlocksInfo: 'nTx': bf( None, 'bh', '{:>5}', '', ' nTx ', 'nTx', None ), 'inputs': bf( None, 'bs', '{:>5}', 'In- ', 'puts', 'ins', None ), 'outputs': bf( None, 'bs', '{:>5}', 'Out-', 'puts', 'outs', None ), - 'utxo_inc': bf( None, 'bs', '{:>5}', ' UTXO', ' Incr', 'utxo_increase', None ), + 'utxo_inc': bf( None, 'bs', '{:>6}', ' UTXO', ' Incr', 'utxo_increase', None ), 'version': bf( None, 'bh', '{:<8}', '', 'Version', 'versionHex', None ), 'difficulty': bf( 'di', 'bh', '{:<8}', 'Diffi-','culty', 'difficulty', None ), 'miner': bf( None, 'lo', '{:<5}', '', 'Miner', 'miner', None ), } - dfl_fields = [ + dfl_fields = ( 'block', 'date', 'interval', @@ -75,8 +75,8 @@ class BlocksInfo: 'fee_avg', 'fee_min', 'version', - ] - fixed_fields = [ + ) + fixed_fields = ( 'block', # until ≈ 09/01/2028 (block 1000000) 'hash', 'date', @@ -85,14 +85,15 @@ class BlocksInfo: 'version', 'subsidy', # until ≈ 01/04/2028 (increases by 1 digit per halving until 9th halving [max 10 digits]) 'difficulty', # until 1.00e+100 (i.e. never) - ] + ) + # column width adjustment data: - fs_lsqueeze = ['totalfee','inputs','outputs','nTx'] - fs_rsqueeze = [] - fs_groups = [ + fs_lsqueeze = ('totalfee','inputs','outputs','nTx') + fs_rsqueeze = () + fs_groups = ( ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min','fee_max'), - ] - fs_lsqueeze2 = ['interval'] + ) + fs_lsqueeze2 = ('interval',) all_stats = ['avg','range','diff'] dfl_stats = ['range','diff'] @@ -162,9 +163,12 @@ class BlocksInfo: self.block_list,self.first,self.last,self.step = parse_cmd_args() - self.fnames = get_fields() if opt.fields else self.dfl_fields + self.fnames = tuple( + get_fields() if opt.fields else + self.dfl_fields + ) if opt.miner_info and 'miner' not in self.fnames: - self.fnames.append('miner') + self.fnames += ('miner',) self.fvals = [self.fields[name] for name in self.fnames] self.fs = ''.join(self.gen_fs(self.fnames)).strip() @@ -196,7 +200,11 @@ class BlocksInfo: if 'avg' in self.stats and not self.fnames: self.stats.remove('avg') - self.deps = set([v.src for v in self.fvals] + (['bs'] if 'range' in self.stats else [])) + self.deps = set( + [v.src for v in self.fvals] + + # display full range stats if no fields selected + (['bs'] if 'range' in self.stats else []) + ) def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): for i in range(len(fnames)): diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 2e9e24c..45994c6 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -142,7 +142,7 @@ async def main(): await m.process_blocks() - if m.last and m.stats: + if m.last: for i,sname in enumerate(m.stats): m.process_stats_pre(i) await m.process_stats(sname) From 52fb93ba73c1976344a3495d0e6fbfccc7f70cf0 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 22 Mar 2021 14:32:58 +0000 Subject: [PATCH 052/175] mmnode-blocks-info: --hashes -> --header-info; block list optimizations --- mmgen/node_tools/BlocksInfo.py | 46 +++++++++++++++++++--------------- mmnode-blocks-info | 15 +++++++---- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 1f895da..f0e32dd 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -164,6 +164,7 @@ class BlocksInfo: self.block_list,self.first,self.last,self.step = parse_cmd_args() self.fnames = tuple( + [f for f in self.fields if self.fields[f].src == 'bh' or f == 'interval'] if opt.header_info else get_fields() if opt.fields else self.dfl_fields ) @@ -203,7 +204,7 @@ class BlocksInfo: self.deps = set( [v.src for v in self.fvals] + # display full range stats if no fields selected - (['bs'] if 'range' in self.stats else []) + (['bs'] if 'range' in self.stats and not self.fvals else []) ) def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): @@ -316,27 +317,30 @@ class BlocksInfo: async def process_blocks(self): + async def get_hdrs(heights): + hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) + return await c.gathered_call('getblockheader',[(H,) for H in hashes]) + c = self.rpc + heights = self.block_list or range(self.first,self.last+1) - hashes = await c.gathered_call('getblockhash',[(height,) for height in heights]) - self.hdrs = await c.gathered_call('getblockheader',[(H,) for H in hashes]) + self.hdrs = await get_hdrs(heights) - async def init(count): - h0 = ( - self.hdrs[count] if heights[count] == 0 else - await c.call('getblockheader',await c.call('getblockhash',heights[count]-1)) + if self.block_list: + self.prev_hdrs = await get_hdrs([(n-1 if n else 0) for n in self.block_list]) + self.first_prev_hdr = self.prev_hdrs[0] + else: + self.first_prev_hdr = ( + self.hdrs[0] if heights[0] == 0 else + await c.call('getblockheader',await c.call('getblockhash',heights[0]-1)) ) - self.t_cur = h0['time'] - if count == 0: - self.first_prev_hdr = h0 - - if not self.block_list: - await init(0) + self.t_cur = self.first_prev_hdr['time'] self.res = [] + for n in range(len(heights)): if self.block_list: - await init(n) + self.t_cur = self.prev_hdrs[n]['time'] ret = await self.process_block(self.hdrs[n]) self.res.append(ret) if self.fnames: @@ -449,12 +453,14 @@ class BlocksInfo: } ) if elapsed: - avg_bdi = int(elapsed / nblocks) - rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) - yield ( 'Avg size: {} bytes', 'avg_size', '{}', self.total_bytes//total_blks ) - yield ( 'Avg weight: {} bytes', 'avg_weight', '{}', self.total_weight//total_blks ) - yield ( 'MB/hr: {}', 'mb_per_hour', '{:0.4f}', rate ) - yield ( 'Avg BDI: {} min', 'avg_bdi', '{:.2f}', avg_bdi/60 ) + if 'bs' in self.deps: + rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) + yield ( 'Avg size: {} bytes', 'avg_size', '{}', self.total_bytes//total_blks ) + yield ( 'Avg weight: {} bytes', 'avg_weight', '{}', self.total_weight//total_blks ) + yield ( 'MB/hr: {}', 'mb_per_hour', '{:0.4f}', rate ) + yield ( 'Start: {}', 'start_date', self.fmt_funcs['da'], self.hdrs[0]['time'] ) + yield ( 'End: {}', 'end_date', self.fmt_funcs['da'], self.hdrs[-1]['time'] ) + yield ( 'Avg BDI: {} min', 'avg_bdi', '{:.2f}', elapsed / nblocks / 60 ) return ( 'range', gen() ) diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 45994c6..8d257d7 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -25,10 +25,11 @@ from mmgen.node_tools.BlocksInfo import BlocksInfo,JSONBlocksInfo opts_data = { 'sets': [ - ('hashes', True, 'fields', 'block,hash'), - ('hashes', True, 'stats', 'none'), - ('json_raw', True, 'json', True), - ('raw_miner_info',True,'miner_info', True), + ('header_info', True, 'fields', None), + ('header_info', True, 'miner_info', None), + ('header_info', True, 'stats', 'range'), + ('json_raw', True, 'json', True), + ('raw_miner_info', True, 'miner_info', True), ], 'text': { 'desc': 'Display information about a block or range of blocks', @@ -41,7 +42,7 @@ opts_data = { 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) --H, --hashes Display only block numbers and hashes +-H, --header-info Display information from block headers only -j, --json Produce JSON output -J, --json-raw Produce JSON output with unformatted values -m, --miner-info Display miner info in coinbase transaction @@ -116,6 +117,10 @@ EXAMPLES: Display all fields and stats for the last ten blocks: $ {p} -o all -s all +10 + Display headers-only info for the last 1000 blocks. Speed up execution + using the async RPC backend: + $ {p} --rpc-backend=aio -H +1000 + This program requires a txindex-enabled daemon for correct operation. """.format( f = fmt_list(BlocksInfo.fields,fmt='bare'), From db87c8e62e08ddf4954aa53bc4e3661aedd4dfbe Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 22 Mar 2021 14:32:58 +0000 Subject: [PATCH 053/175] mmnode-blocks-info: add totals stats --- mmgen/node_tools/BlocksInfo.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index f0e32dd..dc2967c 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -95,7 +95,7 @@ class BlocksInfo: ) fs_lsqueeze2 = ('interval',) - all_stats = ['avg','range','diff'] + all_stats = ['avg','totals','range','diff'] dfl_stats = ['range','diff'] noindent_stats = ['avg'] @@ -522,6 +522,26 @@ class BlocksInfo: fs = ''.join(self.gen_fs(self.fnames,fill=skip,add_name=True)).strip() return ('averages', ('Averages:', (fs, dict(gen())) )) + async def create_totals_stats(self): + coin = self.rpc.proto.coin + fields = { + 'interval': ('Solve Time: {} h/m/s', secs_to_dhms), + 'subsidy': ('Subsidy: {} %s' % coin, self.fmt_funcs['su']), + 'totalfee': ('Fees: {} %s' % coin, self.fmt_funcs['tf']), + 'earnings': ('Subsidy+Fees: {} %s' % coin, self.fmt_funcs['tf']), + 'nTx': ('nTx: {}', int), + 'inputs': ('Inputs: {}', int), + 'outputs': ('Outputs: {}', int), + 'utxo_inc': ('UTXO change: {:<+}', int), + } + fnames = list( set(fields) & set(self.fnames) ) + vals = dict( ( name, sum(getattr(blk,name) for blk in self.res) ) for name in fnames ) + if 'subsidy' in vals and 'totalfee' in vals: + vals['earnings'] = vals['subsidy'] + vals['totalfee'] + fnames.append('earnings') + res = [( v[0], k, v[1], vals[k] ) for k,v in fields.items() if k in fnames] + return ( 'totals', ['Totals:'] + res ) + def process_stats_pre(self,i): if self.fnames or i != 0: Msg('') From 998894331864e1e5fe516a7af29174e0f6aeb2d6 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 23 Mar 2021 20:46:34 +0000 Subject: [PATCH 054/175] mmnode-blocks-info: restore --stats-only option --- mmgen/node_tools/BlocksInfo.py | 8 ++++++-- mmnode-blocks-info | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index dc2967c..4baffc5 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -33,6 +33,7 @@ class BlocksInfo: total_bytes = 0 total_weight = 0 total_solve_time = 0 + header_printed = False bf = namedtuple('block_info_fields',['fmt_func','src','fs','hdr1','hdr2','key1','key2']) # bh=getblockheader, bs=getblockstats, lo=local @@ -343,7 +344,7 @@ class BlocksInfo: self.t_cur = self.prev_hdrs[n]['time'] ret = await self.process_block(self.hdrs[n]) self.res.append(ret) - if self.fnames: + if self.fnames and not self.opt.stats_only: self.output_block(ret,n) def output_block(self,data,n): @@ -403,6 +404,7 @@ class BlocksInfo: def print_header(self): Msg('\n'.join(self.gen_header())) + self.header_printed = True def gen_header(self): hdr1 = [v.hdr1 for v in self.fvals] @@ -519,6 +521,8 @@ class BlocksInfo: ret = sum(getattr(block,field) for block in self.res) // len(self.res) func = self.fields[field].fmt_func yield ( field, ( (self.fmt_funcs[func] if func else '{}'), ret )) + if not self.header_printed: + self.print_header() fs = ''.join(self.gen_fs(self.fnames,fill=skip,add_name=True)).strip() return ('averages', ('Averages:', (fs, dict(gen())) )) @@ -543,7 +547,7 @@ class BlocksInfo: return ( 'totals', ['Totals:'] + res ) def process_stats_pre(self,i): - if self.fnames or i != 0: + if (self.fnames and not self.opt.stats_only) or i != 0: Msg('') def finalize_output(self): pass diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 8d257d7..d213a8e 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -30,6 +30,7 @@ opts_data = { ('header_info', True, 'stats', 'range'), ('json_raw', True, 'json', True), ('raw_miner_info', True, 'miner_info', True), + ('stats_only', True, 'no_header', True), ], 'text': { 'desc': 'Display information about a block or range of blocks', @@ -56,6 +57,7 @@ opts_data = { -s, --stats= Display the specified stats (comma-separated list). See AVAILABLE STATS below. The prefixes and special values available to the --fields option are recognized. +-S, --stats-only Display stats only. Suppress display of per-block data. """, 'notes': """ If no block number is specified, the current chain tip is assumed. @@ -117,6 +119,9 @@ EXAMPLES: Display all fields and stats for the last ten blocks: $ {p} -o all -s all +10 + Same as above, but display stats only: + $ {p} -o all -s all -S +10 + Display headers-only info for the last 1000 blocks. Speed up execution using the async RPC backend: $ {p} --rpc-backend=aio -H +1000 From c13fd84b88592ddaedd9ae80b3cb80c8dc112d89 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 23 Mar 2021 20:46:34 +0000 Subject: [PATCH 055/175] mmnode-blocks-info: add --full-stats option --- mmgen/node_tools/BlocksInfo.py | 43 ++++++++++++++++++++-------------- mmnode-blocks-info | 9 ++++++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 4baffc5..5279626 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -100,6 +100,14 @@ class BlocksInfo: dfl_stats = ['range','diff'] noindent_stats = ['avg'] + avg_stats_skip = {'block', 'hash', 'date', 'version','miner'} + stats_deps = { + 'avg': set(fields) - avg_stats_skip, + 'totals': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, + 'range': {}, + 'diff': {}, + } + fmt_funcs = { 'da': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), 'td': lambda arg: ( @@ -172,6 +180,21 @@ class BlocksInfo: if opt.miner_info and 'miner' not in self.fnames: self.fnames += ('miner',) + self.stats = get_stats() if opt.stats else self.dfl_stats + + # Display diff stats by default only if user-requested range ends with chain tip + if 'diff' in self.stats and not opt.stats and self.last != self.tip: + self.stats.remove('diff') + + if opt.full_stats: + add_fnames = {fname for sname in self.stats for fname in self.stats_deps[sname]} + self.fnames = tuple(f for f in self.fields if f in {'block'} | set(self.fnames) | add_fnames ) + else: + if 'avg' in self.stats and not self.fnames: + self.stats.remove('avg') + + # self.fnames is now finalized + self.fvals = [self.fields[name] for name in self.fnames] self.fs = ''.join(self.gen_fs(self.fnames)).strip() @@ -193,20 +216,7 @@ class BlocksInfo: )] self.block_data = namedtuple('block_data',self.fnames) - self.stats = get_stats() if opt.stats else self.dfl_stats - - # Display diff stats by default only if user-requested range ends with chain tip - if 'diff' in self.stats and not opt.stats and self.last != self.tip: - self.stats.remove('diff') - - if 'avg' in self.stats and not self.fnames: - self.stats.remove('avg') - - self.deps = set( - [v.src for v in self.fvals] + - # display full range stats if no fields selected - (['bs'] if 'range' in self.stats and not self.fvals else []) - ) + self.deps = { v.src for v in self.fvals } def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): for i in range(len(fnames)): @@ -512,10 +522,9 @@ class BlocksInfo: )) async def create_avg_stats(self): - skip = ('block', 'hash', 'date', 'version','miner') def gen(): for field in self.fnames: - if field in skip: + if field in self.avg_stats_skip: yield ( field, ('{}','') ) else: ret = sum(getattr(block,field) for block in self.res) // len(self.res) @@ -523,7 +532,7 @@ class BlocksInfo: yield ( field, ( (self.fmt_funcs[func] if func else '{}'), ret )) if not self.header_printed: self.print_header() - fs = ''.join(self.gen_fs(self.fnames,fill=skip,add_name=True)).strip() + fs = ''.join(self.gen_fs(self.fnames,fill=self.avg_stats_skip,add_name=True)).strip() return ('averages', ('Averages:', (fs, dict(gen())) )) async def create_totals_stats(self): diff --git a/mmnode-blocks-info b/mmnode-blocks-info index d213a8e..e7a79fa 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -43,6 +43,10 @@ opts_data = { 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) +-f, --full-stats Stats that relate to a specific field are shown only + if that field is configured, whether by default or via + the --fields option. This option adds the fields req- + uired to produce a full display of configured stats. -H, --header-info Display information from block headers only -j, --json Produce JSON output -J, --json-raw Produce JSON output with unformatted values @@ -119,8 +123,11 @@ EXAMPLES: Display all fields and stats for the last ten blocks: $ {p} -o all -s all +10 + Same as above, but display only relevant fields: + $ {p} -o none -s all -f +10 + Same as above, but display stats only: - $ {p} -o all -s all -S +10 + $ {p} -o none -s all -fS +10 Display headers-only info for the last 1000 blocks. Speed up execution using the async RPC backend: From cb42dd9207651f8ec8bc775e003aa9bca09f8454 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 24 Mar 2021 16:25:25 +0000 Subject: [PATCH 056/175] mmnode-blocks-info: -s totals -> -s total; rewrite total stats code --- mmgen/node_tools/BlocksInfo.py | 86 ++++++++++++++++++++++-------- test/unit_tests_d/nt_BlocksInfo.py | 2 + 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 5279626..a668e9a 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -96,14 +96,14 @@ class BlocksInfo: ) fs_lsqueeze2 = ('interval',) - all_stats = ['avg','totals','range','diff'] + all_stats = ['avg','total','range','diff'] dfl_stats = ['range','diff'] noindent_stats = ['avg'] avg_stats_skip = {'block', 'hash', 'date', 'version','miner'} stats_deps = { 'avg': set(fields) - avg_stats_skip, - 'totals': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, + 'total': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, 'range': {}, 'diff': {}, } @@ -424,7 +424,8 @@ class BlocksInfo: yield self.fs.format(*hdr2) def process_stats(self,sname): - return self.output_stats(getattr(self,f'create_{sname}_stats')(),sname) + method = getattr(self,f'create_{sname}_stats',None) + return self.output_stats(method() if method else self.create_stats(sname),sname) def fmt_stat_item(self,fs,s): return fs.format(s) if type(fs) == str else fs(s) @@ -535,25 +536,68 @@ class BlocksInfo: fs = ''.join(self.gen_fs(self.fnames,fill=self.avg_stats_skip,add_name=True)).strip() return ('averages', ('Averages:', (fs, dict(gen())) )) - async def create_totals_stats(self): + def total_stats_data(self,data,spec_conv,spec_val): coin = self.rpc.proto.coin - fields = { - 'interval': ('Solve Time: {} h/m/s', secs_to_dhms), - 'subsidy': ('Subsidy: {} %s' % coin, self.fmt_funcs['su']), - 'totalfee': ('Fees: {} %s' % coin, self.fmt_funcs['tf']), - 'earnings': ('Subsidy+Fees: {} %s' % coin, self.fmt_funcs['tf']), - 'nTx': ('nTx: {}', int), - 'inputs': ('Inputs: {}', int), - 'outputs': ('Outputs: {}', int), - 'utxo_inc': ('UTXO change: {:<+}', int), - } - fnames = list( set(fields) & set(self.fnames) ) - vals = dict( ( name, sum(getattr(blk,name) for blk in self.res) ) for name in fnames ) - if 'subsidy' in vals and 'totalfee' in vals: - vals['earnings'] = vals['subsidy'] + vals['totalfee'] - fnames.append('earnings') - res = [( v[0], k, v[1], vals[k] ) for k,v in fields.items() if k in fnames] - return ( 'totals', ['Totals:'] + res ) + return data( + hdr = 'Totals for processed blocks:', + func = lambda field: sum(getattr(block,field) for block in self.res), + spec_sufs = { 'subsidy': f' {coin}', 'totalfee': f' {coin}', 'reward': f' {coin}' }, + spec_convs = { + 'interval': spec_conv(0, lambda arg: secs_to_dhms(arg)), + 'utxo_inc': spec_conv(-1, '{:<+}'), + 'reward': spec_conv(0, self.fmt_funcs['tf']), + }, + spec_vals = ( + spec_val( + 'reward', 'Reward', 'totalfee', + lambda values: {'subsidy','totalfee'} <= set(values), + lambda values: values['subsidy'] + values['totalfee'] + ), + ) + ) + + async def create_stats(self,sname): + + def convert_stats_hdr(field): + v = self.fields[field] + return '{} {}'.format(v.hdr1.strip(), v.hdr2.strip()).replace('- ','') if v.hdr1 else v.hdr2.strip() + + d = getattr(self,f'{sname}_stats_data')( + namedtuple('stats_data',['hdr','func','spec_sufs','spec_convs','spec_vals']), + namedtuple('spec_conv',['width_adj','conv']), + namedtuple('spec_val',['name','lbl','insert_after','condition','code']) + ) + + fnames = [n for n in self.fnames if n in self.stats_deps[sname]] + lbls = {n:convert_stats_hdr(n) for n in fnames} + values = {n:d.func(n) for n in fnames} + col1_w = max((len(l) for l in lbls.values()),default=0) + 2 + + for v in d.spec_vals: + if v.condition(values): + try: idx = fnames.index(v.insert_after) + 1 + except: idx = 0 + fnames.insert(idx,v.name) + lbls[v.name] = v.lbl + values[v.name] = v.code(values) + + def gen(): + for n,fname in enumerate(fnames): + spec_conv = d.spec_convs.get(fname) + yield ( + '{lbl:{wid}} {{}}{suf}'.format( + lbl = lbls[fname] + ':', + wid = col1_w + (spec_conv.width_adj if spec_conv else 0), + suf = d.spec_sufs.get(fname) or '' + ), + fname, + spec_conv.conv if spec_conv else ( + (lambda x: self.fmt_funcs[x] if x else '{}')(self.fields[fname].fmt_func) + ), + values[fname] + ) + + return ( sname, (d.hdr,) + tuple(gen()) ) def process_stats_pre(self,i): if (self.fnames and not self.opt.stats_only) or i != 0: diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index 156620f..ea10655 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -52,6 +52,8 @@ class dummyOpt: fields = None stats = None miner_info = None + header_info = None + full_stats = None class unit_tests: From 3b09e5d4bc7ae7c7a551e1700a85221c533d8976 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 24 Mar 2021 16:25:25 +0000 Subject: [PATCH 057/175] mmnode-blocks-info: -s avg -> -s col_avg; add non-tabular avg stats --- mmgen/node_tools/BlocksInfo.py | 43 ++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index a668e9a..5c291f7 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -96,13 +96,14 @@ class BlocksInfo: ) fs_lsqueeze2 = ('interval',) - all_stats = ['avg','total','range','diff'] - dfl_stats = ['range','diff'] - noindent_stats = ['avg'] + all_stats = ['col_avg','range','avg','total','diff'] + dfl_stats = ['range','avg','diff'] + noindent_stats = ['col_avg'] avg_stats_skip = {'block', 'hash', 'date', 'version','miner'} stats_deps = { 'avg': set(fields) - avg_stats_skip, + 'col_avg':set(fields) - avg_stats_skip, 'total': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, 'range': {}, 'diff': {}, @@ -186,12 +187,15 @@ class BlocksInfo: if 'diff' in self.stats and not opt.stats and self.last != self.tip: self.stats.remove('diff') + if {'avg','col_avg'} <= set(self.stats) and opt.stats_only: + self.stats.remove('col_avg') + if opt.full_stats: add_fnames = {fname for sname in self.stats for fname in self.stats_deps[sname]} self.fnames = tuple(f for f in self.fields if f in {'block'} | set(self.fnames) | add_fnames ) else: - if 'avg' in self.stats and not self.fnames: - self.stats.remove('avg') + if 'col_avg' in self.stats and not self.fnames: + self.stats.remove('col_avg') # self.fnames is now finalized @@ -466,11 +470,6 @@ class BlocksInfo: } ) if elapsed: - if 'bs' in self.deps: - rate = (self.total_bytes / 10000) / (self.total_solve_time / 36) - yield ( 'Avg size: {} bytes', 'avg_size', '{}', self.total_bytes//total_blks ) - yield ( 'Avg weight: {} bytes', 'avg_weight', '{}', self.total_weight//total_blks ) - yield ( 'MB/hr: {}', 'mb_per_hour', '{:0.4f}', rate ) yield ( 'Start: {}', 'start_date', self.fmt_funcs['da'], self.hdrs[0]['time'] ) yield ( 'End: {}', 'end_date', self.fmt_funcs['da'], self.hdrs[-1]['time'] ) yield ( 'Avg BDI: {} min', 'avg_bdi', '{:.2f}', elapsed / nblocks / 60 ) @@ -522,7 +521,7 @@ class BlocksInfo: ('Est. diff adjust: {}%', 'est_diff_adjust_pct', '{:+.2f}', ((600 / bdi) - 1) * 100), )) - async def create_avg_stats(self): + async def create_col_avg_stats(self): def gen(): for field in self.fnames: if field in self.avg_stats_skip: @@ -534,7 +533,27 @@ class BlocksInfo: if not self.header_printed: self.print_header() fs = ''.join(self.gen_fs(self.fnames,fill=self.avg_stats_skip,add_name=True)).strip() - return ('averages', ('Averages:', (fs, dict(gen())) )) + return ('column_averages', ('Column averages:', (fs, dict(gen())) )) + + def avg_stats_data(self,data,spec_conv,spec_val): + coin = self.rpc.proto.coin + return data( + hdr = 'Averages for processed blocks:', + func = lambda field: sum(getattr(block,field) for block in self.res) // len(self.res), + spec_sufs = { 'subsidy': f' {coin}', 'totalfee': f' {coin}' }, + spec_convs = { + 'interval': spec_conv(0, lambda arg: secs_to_ms(arg)), + 'utxo_inc': spec_conv(-1, '{:<+}'), + 'mb_per_hour': spec_conv(0, '{:.4f}'), + }, + spec_vals = ( + spec_val( + 'mb_per_hour', 'MB/hr', 'interval', + lambda values: 'bs' in self.deps, + lambda values: (self.total_bytes / 10000) / (self.total_solve_time / 36) + ), + ) + ) def total_stats_data(self,data,spec_conv,spec_val): coin = self.rpc.proto.coin From 5473fe20f74076b5624ad277935568ba1fa84def Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 24 Mar 2021 16:25:26 +0000 Subject: [PATCH 058/175] mmnode-blocks-info: add mini_avg stats --- mmgen/node_tools/BlocksInfo.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 5c291f7..55e2b7a 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -96,14 +96,15 @@ class BlocksInfo: ) fs_lsqueeze2 = ('interval',) - all_stats = ['col_avg','range','avg','total','diff'] - dfl_stats = ['range','avg','diff'] + all_stats = ['col_avg','range','avg','mini_avg','total','diff'] + dfl_stats = ['range','mini_avg','diff'] noindent_stats = ['col_avg'] avg_stats_skip = {'block', 'hash', 'date', 'version','miner'} stats_deps = { 'avg': set(fields) - avg_stats_skip, 'col_avg':set(fields) - avg_stats_skip, + 'mini_avg':{'interval','size','weight'}, 'total': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, 'range': {}, 'diff': {}, @@ -190,6 +191,9 @@ class BlocksInfo: if {'avg','col_avg'} <= set(self.stats) and opt.stats_only: self.stats.remove('col_avg') + if {'avg','mini_avg'} <= set(self.stats): + self.stats.remove('mini_avg') + if opt.full_stats: add_fnames = {fname for sname in self.stats for fname in self.stats_deps[sname]} self.fnames = tuple(f for f in self.fields if f in {'block'} | set(self.fnames) | add_fnames ) @@ -555,6 +559,8 @@ class BlocksInfo: ) ) + mini_avg_stats_data = avg_stats_data + def total_stats_data(self,data,spec_conv,spec_val): coin = self.rpc.proto.coin return data( From b9f9072f1f169c51500063bc74b350904ef5f8fc Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 25 Mar 2021 11:59:13 +0000 Subject: [PATCH 059/175] setup.py: disable installation of audio files --- setup.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/setup.py b/setup.py index c5f9c17..4d12521 100755 --- a/setup.py +++ b/setup.py @@ -18,16 +18,8 @@ import os from distutils.core import setup -from distutils.command.install_data import install_data from mmgen.globalvars import g -class my_install_data(install_data): - def run(self): - sdir = os.path.join('data_files','audio') - for f in [e for e in os.listdir(sdir) if e[-4:] == '.wav']: - os.chmod(os.path.join(sdir,f),0o644) - install_data.run(self) - os.umask(0o0022) setup( @@ -38,7 +30,7 @@ setup( author_email = g.email, url = g.proj_url, license = 'GNU GPL v3', - platforms = 'Linux, MS Windows, Raspberry Pi', + platforms = ('Linux, Armbian, Raspbian, MS Windows'), keywords = g.keywords, packages = ['mmgen.node_tools'], scripts = [ @@ -47,13 +39,12 @@ setup( 'mmnode-halving-calculator', 'mmnode-netrate', 'mmnode-peerblocks', - ], - data_files = [('share/mmgen/node_tools/audio', [ - 'data_files/audio/ringtone.wav', # source files must have 0644 mode - 'data_files/audio/Positive.wav', - 'data_files/audio/Rhodes.wav', - 'data_files/audio/Counterpoint.wav' - ]) ], - cmdclass = { 'install_data': my_install_data }, +# data_files = [('share/mmgen/node_tools/audio', [ +# 'data_files/audio/ringtone.wav', # source files must have 0644 mode +# 'data_files/audio/Positive.wav', +# 'data_files/audio/Rhodes.wav', +# 'data_files/audio/Counterpoint.wav' +# ]) +# ], ) From d4effe14aef7483c5092b8230720db4aec481bf7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 25 Mar 2021 11:59:14 +0000 Subject: [PATCH 060/175] remove audio files --- data_files/audio/Counterpoint.wav | Bin 1687348 -> 0 bytes data_files/audio/Positive.wav | Bin 362968 -> 0 bytes data_files/audio/Rhodes.wav | Bin 208092 -> 0 bytes data_files/audio/ringtone.wav | Bin 44669 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data_files/audio/Counterpoint.wav delete mode 100644 data_files/audio/Positive.wav delete mode 100644 data_files/audio/Rhodes.wav delete mode 100644 data_files/audio/ringtone.wav diff --git a/data_files/audio/Counterpoint.wav b/data_files/audio/Counterpoint.wav deleted file mode 100644 index f5ad6811fc4fec45cf524f893cf5d2ab6df762c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1687348 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

#GM<%I@BrbM1cSHwi|5BLRqGISK!&2PaM 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 zͅ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 z

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%0Dw{CE+jH!+3XOy1#MP>2U(@(Tsna^8AeAzv3> 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≥03Cw4?ba+(L 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

  • cNT* z@cYi!!s{J^Rd&$=o7~y=WO$YPTuB?R#^A4GX}8d@PPs5@VMMqRD_NUPXu$$^ArS*; zy3v@VDdy}eR_Yt`_8sfAT$bia7;Ke!TglR_pd-G+dVPcc`kFrc9Ht(xy5cbTbv?;z zJMp5X&J=k+@?uaqk~=7E2VxQWF)yr^6%I{rXJTrfNhY7-S^Dp3xp{GHV#Kv#vKT`K zH3rSb++iHtlfb<^gD4glfv0YAC|il1=Nt zruC3d*q`q(OuT5cIQ_UtN{mP{zTb03HVI}Si5W_!|0!Y4v}FB7GM~qFiqT=^&FK5s z)Hbk8KkIsmwZ4@8+U?)DkY8X2pX0x(>Oe00;-)&Wu&vEg6L(PuE~%;2@Vpv}T$L5B z>MpCf+v>d88u;_@>J9Yh1J`LwYlLUR5A-YJ%;`*Xx{!p2^(XeQhIU@jJN@XI_HyqU zu+vrD*K59=m-JL+lyNeLj*>nr^D;yX{!Fk?h<&8A`ic_fJ;j z86)}*Na-r^NhHrS8+}?g5~!9t>zQS_z5=K5BLLLX)pipP;6Ro zLN59Rc?wsZ(so;(=wmwRX?8HB^JOxd^Zeq)D(Avb>I||i4cL=bY)J=5wi_+g*W3hgbtI|px}MXe==7r2RZ$*pQ6pEB?th6tP?(>i zLWA{pBbr+rI^;>S;7Bv7N1ayfM*9|VP8FliMao2T(>wC+NfY!&;w8UMdW9N-Aw;Q~8;U2PnApDaE@UXxn8X{?wW=IK={ z;w@IXwN)~LK3eRTez6W8S-Gkvo$$fq8+d;$7qMj)z24!iMvoAUdr1Zq?9@6+(_ADN}|e5N1xe&6ux zN0Gjda9LI8YqJZ(F0wwB=*dc`bDiF95m(y{2OnbRPr)+3!!URFy;9Nfh4;+oO+5HN z{M&z`6+C~>YmL3x#=7i^w;E_Ij>0>AMl;N?;^!LaMKIG+2xJ9DWHo)fnzdU27ktOw zEs#$(iw&I2pBP2kTAi%sa+-Jz3kQatb5FJ5p~#Bs~c78tByl=C40l8UR5JVei7ToG@avPG0_CMl+lou8(8= z9z++bt+rCW%?9;Ac4}g#(O>OJ{7vriMYTV!@FV|$@$T>$AMiJ$#w-EtC@+W%%%bg9 z(d9pBXGzm7$SWaxkPh!ufVO`_{O)~mjt|A+x`~nv^fAHN#)IcK%fG+Usa?{TM#jM+>mb}UO5R+&;2 zfY+4+GR|%#XZ7lfKADj}p5C0KamBFqaT34yG#@siJ%l~(lJM(R;h+AU*V~icIjl$a zud}v!V9HnVU10_5>e#8;S`*Jdv?e=v{}b}p(>%#m;r*Y+3S4BBu3~I&vHcIQc5yfl zV}%V^=YBTxLAJQNjXeMFX|ZLn(p<=BGGAf{1la*+P@BhCiYD?S3?H7+CzDc!#VzEUa#YIemA#g&Hr(g zf_}jf?5BVB!sfUvmZTzIst&KCEql@*3YpB;`c4b8V1HvPZ$To7;h```xQJFktEH)^ zZ?&&A78~TkY>W3KdKkY?jAM8@R&#!SZKGd>RxN2Jav7Ht@WDd}(D@N$=sD7z3?5B^ z3rtSeCTF*kS!apa`-C{(h<-dIqyLIE{o^#ms~&$>PvV^TzzO~Nh0k$-p4tPy|G; zf3OgDti`xUgnU1N79O!q|LNCVzR+#ba1*a`gLK@$$lV|gmr8w&Gn^g;xr> z?^IUHgXjx5?9}QOI}wWPt&FOU7JXJeQ`npW5=~^kT_Sl+i6SS(ZT|6ELVe1jy{># zhB>u;&2JBK*k1cUtD{xYipeX@X+)o=1p<%ZiI%^Sp}ihAkf+78!c5-5c&~=OV;{J! z8(YzdBz5rV4pu{W?oSU=Jiz^pG9Odj^E|q7jrrPR?pP>ynl@f5=AeV#Fsk*9byq8U zJpHtQp4tkt9Dzmt(*Dz)B3sF|G+O!~7aS9w9nRJU+110!imx!+;>$PrRF_@TPCj{0 zX?SR<=#u9l`V?wyKQH4iIh&K5kCwvxK2HLlXK7NxH)+I%RReP`VFiJ}y#L2i-ZZ*@ zLk544N2h#|%R=-{K}=hIOj~}dB0rhK`}%C0`+U~-!fI6+^dq~LS1aVo5CJ@x&F|zS zNqM}U*ZT#1u8_|cV&Mu~`Gt9bg=qKuekYGv%3-{+u%GrC@@3W3!dgE~*F_+f2gdj= zzwHK=;3}TzPdlG~hfglBM`1;d-~4@o&v+DbK;M|ZPF8$3x`0PUkNtps*v1lWVl6jd zB-XKUfbe3yS%>7_YP~vJ^#&X zr@eQ^C(gR=Y5L{_+;q&Re}Ve&7X8^yZg!KredOf`DLiY< z|79)RHoj5g9-egYf;E_p*Ihs?pg4Z9j8@6ts{6_XD1pB$PM(Ux7q3DSMOl!Sv5YTi z1MHSp{d4E-)AHyt$^0ZJnclibmAv~+1WIS^))F(%=;p5!tcy~CFoK7I4VeN`> zR_#doaxm1_mj&ty>9r#_%`ujBdFa(WmX2p9=NZ%R{FTM@$WrTdne`iTLNfE9Li^mz z*?e*^+wS|Bu+enVFf~3FJ%s(}qkTd~oN-9Hrpu?A#cP|3pG^qvIXOoVk1`=)m$!sOF=oO8+45^=coGQ9S{Zbz*V8Qbv8 zIa>6zE1Za5X<>}}ZJPB1xTQNAIGSY%Yp*S16Sr%>`ulfLjl15D3l7DS2gk*f&dL3~ z!soaPcSoGRn^a4$293oHI{Q~qOs+$@XTqvpN2RB!)FX zyO6HVq^YA{`WS|6LuZD$=yFzBuF)iD1S>X7zQ#y?;AsBec-nd@AM8u|Da?~zZjFRo z*B%=D7>oXU{JP0Pmu05yGFda3tb|N{Gc*4*GnU=i8eZ*Z9gManCR^bByr&7&3p*)29>bhUSRh^V8y4Ts6o1H|yG`2qawUfFhIeC0IJktj*>nKLt z3P;+AG`-KhyhREt!e3>{*6XCUB>YlRPfPNG!&+2t`1LnQel@J#dsamQtX>NqNqZQ( zr#3`X^;2HTbkX8@Y~eC?a-;RS+ua_96!BuLYf5(XAsrjWbADudLmW1Vwe3mnn&2R+ z8sB32ligUqpe4mYC#I{Pg;SD}sx+>U)pv@p-4#e$UDxSIrbm&>xvuPtQHc6KEAN3- z_>k6rWTaxEo(b@UPmAd$VYiZtS*PNYrD7+3 z(#LxL5zGH!{5;MV!wh#(4S_7WVR_opVjsBECbU=+TC5qg+tR4DBa_{=q2~HCk8?59 z-_vkEdH*-D@-UQyrIYs*-f+1{T_!wjbZdcpJ^ zJ+{)CXbt>b-+K*wrV)+xf$xPlKir+uhYlRZHcrxJX^S)!RD3eDW7~K|JFt*H&@#K+ z$1a}UPBXuQ6l|qaHn3Jk|GwQ*Xw-@iZ1-_r{20`0bE`9EUy+gQDA z^z|csZO8J3=ec($NquR!p`>Yo-+iMXX1Di`7!RB{jGLb9q&IHqjciyi`vqQh z8grl8oTs*GQ)8gh#%FzgO_G)v*Y(DD2jsDr<@p5;I>tvl0hyn)wvV%;M@Y>9dTKXa zvDwOBY2AP0YM*-?+gNO89I}Eu~0y9J_m)^jxCFj#*!O<9)+WJA|1$ z?X0_YIEl7e8_&Z!Ce8J!p|Pt)gVlgTL&+&Y8pFJxtZ;5BS|-d5x^M0MZN$zR)eyDr z!uqczql^5?m*jUmYdn~B?uwIWgKKMqm#AsASEUUqu$6CUrFf$yY55Y~FTt|E#v3o~ zj>_?3E7BrW$?rRKYb`U;kfgP=PCuq?dO$Hl%z0SJ?kh~=GFEUKDfyLT{f@o-PmXd@ zznImEf5op>k)hH+Yl{=@DOxnt6~k)uldYU-xbUgw{xfqth9w(Bvv;NKKa_7&*9xj= zY>JX0aR%4iO>6uN<^AFgj+5$B(AHUZaDffJM7IAF?{jiHiTr@QtHbV9cQ;|orZl`* zf(|OqK9;19%D7s1mQ^hh)Y7PS*G7}F*;ds?@169Sd$ejAe$=bRy{-}OLB6J#hZWZ0 z9*<{Wn18(X$a_!YpTf$nDT9P^*^}TCQ?Pq!Ve(A&3uU#pFsIsA;rZIRaXYzS@thD$ zcG@RPJnkTFVbW&psCF&n z0$@;1Eo4hyCh_lRop_Mnnyvc9Co|aVa0cgUna?de%0FqtPOm$>Zt~emZJui;xb_Ef z-`Ray@X%gHtx_?6K0o@Yxq`mS*^ZE%`W8xb;Hx*nry7u>6#nHPHBaDaVmK6yu9JIO zEf=xEp8MtgUMANh!i6_k;ZPCSQ<)c2S8J(t*G6dbe7;>f?YgMzGti%+at+n^F7;`~ zdvvLTc)lxr`UY0?=kEkh@q1M64>v+Bj;?UEBaLbWrQg)*h-#{9W%#POwa0xH<)4S$ z&IjngI!~L+iyBAnek6Zi8BZsz4fJj1?>DuEEN4U4sQ&4*e|&d_hS(M1E~EHjGHj6d z8VSB>=GPf&IG>O82^oM;3%3nj?1*pYhAu`R-l6 za+(ndd!OxWET(yx9zBI7;e>>AXeJYcsu%F*1pXXE-<|Mji@ff7vxF-k(-QrjXBWgQ z-qTc=FkTLOlywlpwIT9cKa#IOYF7RzH#)>xf?+)NU+^u|ZJP%3=JBalu!GzDb;R8K zd6x1%=}jT}$x62hlcWlK*k@Vmmr?&!J!`}Uy~)}&+ z)cvA|zxD8pdDE+6m%Ca5Iq%f4G&4Jsm+dLeHkYF#Re5&Li+5zBev#kveLo` z+M;O(auoK`b~cJ0diIr(^d^ISwQx4;0Dj6qa`!zB3?!%h$X;JQOK(=HI~o4cSUTd& z2O`(jaP%GbeM@Vo)g$kq-SHE(tUjk?V%RsImNR`kMWuk@VOK7 zShxf_JbwaV^WB47R!3oK2GA)gkovarTQC9NZ z%VgtiGV+PL_xC#|47kg3+P=nMi8ugWF>rc}lBkKDeH_HJL2)IB6p-UDrnLs-4(RwilwUVn_Rq&)R6?nCP?F zzEd5__14GpNbVU`1~#B98Lz_zv}FPN8rwLY{W5l79|<`R3-23SGVyEr$Yc8bgt_fp z=CVs#&r^*JuWSG7+i>(l9$`25+n@J0jGX@}dYH}{&Z5D;)7&{w_IDHub3Ie}V5(Hu zwf{CN^9+wCFYh6tnBrX2X>-wfa-B7tvGk-h_h#Ipd8`Aq8Zw$D?!#!ZxDHYEN22%Q zdq?>ja@y0xh-;#15l5YA8a<4cVeRlvbwIZ9x;8?xb?o0N^a|%?EhUvpMJG$)`%<~j zW&A2DxcKA~`A;*&3csVle70}_8qB5lzrp@Vbbl1-{eiFAQ)cfYuVLL?EuWWTC!E;^ z@%hBAY_{|wH)#a>h=J%w<~IeqN5I-Ak+@&81xS|j_PJ(2g-?Rw05=A`Cz zQ<@_s^?aEu^s_v@3~Pcvhn*wE@T=+7zn-X`8&BdX(Y7c-8>_9P^+$bvS8Sd>Qbw$2 zH%p|bwVwr?)0->q3-b6)#NL!hvba%^hhRPY~{%NeCjVC-!~9&h|Jgo9GVAd z*J``9lm1q#5fWJc&emn+1y~u+Mm&!a)#Sk{dRIbhQoyIV*uN}VSkL%4?=7qsP3GF9 zbSUh+OX#lzv^Bzed8qgINy{A)a+Btpe}Xjq`O3j#e;?o8WLav_wJLI?C0LYPY-U<` zkc=M1^T%%T$1d~7&U!uR@8kA}9gD8^eyzP^>&;h-LFF)#utXW?Yd)=<`#jIXd4qhm zrHeg0J)EIC6W*+2J%a_kt|j4}hW!C0#YNAsOZCa@yHK?Q->o}&?MGjSLf0`dIXy{+ z&sy(f?F(uxgtUw0C6^lM682>=JFy6+&V#ML8UJJmJX#x!CVjQ8@Hb>?nzI>=P`MUl zt_q1uK;&HPSsHdYA&YV=7GLGV6Mj4UaKP(cZ8zTT_T64`djQt|CF1-C-!HH`H_1TQ z&z{`#GI@4>xL#I&o?!{ow_%;k$%+OY9yS39kyXCutkq7G0s2#(d$rnjz zZH@Jsk>p-e`#T+c%4&{0x12^{YYa=r^kEZ=7*<~mGn&p;@>p@r8*e~LUN_Q4`qqdf zzaba;I=Oq54A!x3??rLoGj=UhHa}C&oM3V5F7ngSoa%jKvQH?rJ?Kg76^e@+8NF*? z%~k8%&&Q9Ap0=Oa>X_(3`<(Y$PxU9+*vyAtXTE#|eON3%HHXhN6CO{Bd0^M%IG>Pf zDrWZP1+iTdF??7J-jfv`s12777^#i+*+}0FlWiHuO7{>0c3`oaT2K0d)fS~$@Jy`k zLlW#Hc*r;3Qx@^b{xGk-l5e_}E^aiRy$ufSB9HsXrjwSVVNJz(xwfltlwFNRj4`Py zl22G;k`W$dxBnuKHAMN$H5C-=7G&=Wvhn#LW{9(Mk&Ep7*3A6Q4E&%c%*&><0xr4g z+ll$w39X4rzz#lU9xxJD+xtd%*9`AkMiM)BH;FUjlGy*1B(7;BsXA9l?Qc$EjxdQe zREhmI5#K$L^$&@xe^11cII9QezVM93o?MMIagt319#@zLV^xvauA89%W)jcLVpHRF@MMP|au z26gyqc3(o20p#juqn=2LXYpL;ljtS%V+A^_ggYyEHp}#N2^pUklV`pPY7L3SRN^7_ zHJoZymoHJ74(HR7YhnL&uav?@QZFq4dL3i2BDq-#8tuxj@ZeA^E9!kV&W-p%K`&DN)>`Zqyee}%{4{Iihf z9|J>vWfLcpy;=CYnB7>%L*B&_oxrE-cot@3vWkXE<4#R6(_6H@gY}kua4P(p>F;IY zsja?0XjREcUid{Z*LB{?Eg7oYVvt+BmQX$Z0^~a>18{&`ZNk?@Jk-f}`V-vhDVAyv zIU4f`LTpeOf)u9>xzHyquQv(WMCsuzS?H^L&Wm2p8Ch7de9n0PHM@1*STCaVWz@LF z4*lns_vq83)v47vbQPGj=WSvc0=Rw+#30@SlZ{c);CuK!Sk+*_8p6uwFMg)3SN}&p;Zo#^ylU(d|xjygLu#TQczjUn5k~7{jX=FHW84 zb+W&w_p*b68stPBbiw(PlM0&P5BgxIki^#;Q%S4|tp?F4~UIrX6H?A#q?E-DC>wow6 zZ(e6;)3qr+54(lNYh&GaG)wl2NO}ZKx0_x|z;BI1|4=XSKlbPveqE&N=UK$l5auM* zISvPol9MAoIqVyID}8cJyWz9zUaxAGef}@3`bRrP4i1s7um(BQXbkcGRywuOUDuL| zm7Wl4;D__(=6T}pbniF+W@uCOVxm}Zyido9lSZ3S9O>FeJ2^Ix(yj6$dwhSGT%B~) zzrMXfo~@;d`HBf>eloh63gyEpsr2kaW_BnW@(O`9lZGun4U$F8FCfAciAG3@htixNxms-LSFCw|~&HMc> zmYuGR*M`eR^)my|nGJ0X%Nn}2I`6y?f9!GidP@%W2u)upM=*i5_ob`tjIJ*IEz9f8 zqDA)z%6{w9kKOw`^14q zpv5sgJ!X#n2(Ndq49aGbKB>cOHH+$<^;1@gC_GquM+BAQu>;P?q-8;MaW(i{HlwGE$K^l zWBQpiE+F%}#ST|VXlgiGf@Q1?>DqW&Uz#})Radjke`){G($tX&&ilO@ev5m2>pT22dDrL-r^{Uv z|6Q~%-gA8`&{dVuLJdP z7$hDAmB+K26Cw3vQZ`xdCqnZH?B!VU`wQ=AD3t$ReApKsyUS60LXKOL%Es`c4r@@2 zJeMTTImmNrGxQI6Ef@9RDC`dF=2k+Qxp)~WoQ{rpa)m@7Magmr(Mn04T?xJ!TZfPH z#DA;scn29c%C25;kNdDc8JVHQuK0%!c@$+2@)dW>cW&opZ{~5Y=aH|`&sB0UEBMJP z>=#)fj$J7avr;XGP#0l^9NS7dx!Ri6N9!&2khpz*zuy!0@ul||+YZmzU~XbL8J)`} zPJq!r!maMGxDEcl27jL98D@t~5mx;)s%)Y8vt@Te<$%U}e_tSD5Wz7>75HDqsCrv4@5n&^5 zu}SA>?r~OSKg+lkW!FH!#eC{Hcrp_`!fMrV=3d6&-Dq+B7(5+|9^K+_=bh7XDn6?MeXxTL>E(;aZGQnFPnV=xvfVpXpL(z zCy|waucgI&rPT{BrIvVctq4rbN2YVY)lBvnJRYm|otkXqXE&;n<3?;uCkXTtT(vWt z)m#=EVFrDBk~B0nrmkK``+K=D9^qBoG2XPWr3h?!I--tqM|_o_O(fabV3za%*Q&s!#fanpo+qSIEFG&%)L`TF2amfAkXlFXCTV zNqvs?Y$cm>w4b%F$?PZi(Ta^}OxoYTmxlb72BiLf{G+$|O07wMCs?aOJ%9ExU*SBB zJj;5V_Wv0pKT8|_MYW3%=^C2flC`{Nj4DIH!ilhO0eih(hHft(@;LAPqJG{GqyEQ7 z2&Y=!(%&m=`&sP}e_<1UVJ^J-8IE-&qfPmjDx$i&Dr@|V`Rg#t`x5zi1y0tJGkcYM zHBh6mfjWM#sy*Wz;b>#1!@Tye+w_Vj-9zVa z?)4Kg9a+WW`OSQn#G@zek9fwIUxdGP*XEmfm*p)(rhvX5%aMP|iUi~Qy+huOaSolUjs*uJ2|kw4_A$ zUiMU0Ck=S%)o`~6JC%|3Nyx6^>|mJ7 zSO6&(`~MI2a9OM^G%Y+i4270zt^PV47=KQ{PVvhj7y0{zRj zJ0BXJw2tLMBfRCrWF>`_1Zj9D>G>*|wQS~wbCKP=-sj`F6!cwTua8_VKrH5BpQ6)F36g0YUT5kz_D~U2C@vb;Z7Db(~5?S>T zoJvBzl0*H+S%CEPJ&Tr$+!gS5SjAS#_n|6(1-yBZ+&x;eTpIqD)hhbC5?{9xo`u=) zD*WB2{O&3It>_+Q{i>u_J50%Ne)lTmzJ*ClVHT(;q$#eI2Rde4b$ze(T%13T9>2Gqqj*5RS$L0#Mve%h;2^nBgI{1|? zwsyBYoNA)ILhh=Gi^}n(io>e{WGp|KD!_IWrSGM2>nT`M6XzP>+k0fC6JCBzHinUj zDP&?1{%?d3X7-@Z?`EoI!LQ#)J_qeh*YhGaWlnj!^GshVc>_J-o&@B z=s|T@Re@fVGM?h_sf1sap+}YU>RGW>UHaC<_}in2o!w{?Dk5j+{pDmYa`NJ{i$JrX zXBJj16YixK-K3GPejGni$`FSAadN0I?suBI64iH*)1$EFJp8)pHL4|wM9uZaSws4m z8Z)#yl9aENlF#zEGbYli{SeN>$tsr0Vb5J&dDW2X2s0^#@i$0eVNWXr4GZd1L0)-S zZ=4@h^RqSi*{ra(AP+AgxAoXEnEKvB-+O6&jp=**AC7VpXqvOZQ7@J8C4(P{$VdcM zJw%DSe&zg32=g@Edl@n{;_o+i#Yfr~S`VLx^XL2NNq^((Z_ERXzpvi)q77YXL&&DK zrFqTBVA%WiBAI+j--{SS7BZU5yW6DT3@zWIt)Sf@A{(lOouF-f_c|-^ENf80xU!Pt zWX5rik9C!g72?7Z(FE#o-4_F1GdFnFyzgJm2j3$XA8J6Xw!S9R>X>cM;Ux2aW96ZS zlTLN~pw+)nD`J&mCKM_`6I2D@`Ss)#e8sNxv74SH=aXQUGCv4ZZmJWkuRpkrr73!eDFPYNEUI*ik6q3 zRU+n(E$5G|j@56f7TK!y!)`v>!C2kf9Ab%75ivIzKY{NXpy3qA8+Jvs!sBP5P)_3w zyQ22d?s>F(F#ozEjc&xhs%lL6jqM4Ul=x_SaefCe(kJ5lPt_dzln)(RCw0g=#+{0O zBtO;8KD+nDfbUwv9%^bgM4MNvFt>Kl8q_aH>rdz~i;QoBG8f2mQZz72$-6)2SI78= z`^EHs+E1{_-p+O6*%k6GOJgsm@Hdk%Ax#%-jJj_#qH50ssc<>_{ z*O2B^5KCutpIdC&e)Bnt;Lq?_F6@Xr{t4?o&zcLmY;BBnRPq#;g5FH@g0H`{ZU0%;FpCaG!FfSl_1tYF! zTy4bXVSm{u+B}af-D2E-)79(2QxNk#5xHw#S8NG`ifF|m@m?Rs7w}m&Emh>6(~$0} z&vn~=!~gi}H%06>jrWGg;090S2DH4XNB`-=U46NyPxsAzJ~T7&fSx^&bK$G`t~9(Y zY22lGw&l^FGAyo%9`*dX3EH%S(VewWiC~O8&F_2wr~L9njzR1bkogpgb(Vd&K;kds z-!*agO__n)_UKw!!#6wxb1w7l6GW2QgOyD0?)qu# z^;LYZHs~D0-a;#BrO+xEG%i zJ&K6NUSvbxH^%;W`a6GrKQwxPm-*ycYKv_?CRfAA*AjAeNc&Gq9dV*`B%GtQ(%CVq z=;d1as9twuBYh+Xk?mrmUCyNlIhw=P*Bpge$HjyHkd3qO?Gj|T2J?cn-s9(~hrtGR zz|ltR{WI)-Q9eLs7?MnTDEE6y{_To2L6_tuu0&I*{g>S5N#tN2a~t(b&R3n?O=E!`yG|!oJ^Vz`#@i|~kIXF`@;*4Q9)5Lh1 z8&6B)X&L!bEr?xeSOr zLKQxdOqT0g!`jbaOkcJs>@l4NS^ltQZ<}^ZyTJ}8hI?7qwi23^4f0^2?oM$lNQ;>< zEhc|a%-Uv^s=S8U4pY5O@;Z(k9N~Sa7tsT5e#B2~3Oirq6P9K%v$Fh;x#Kx~+sO-B zOiF%*ef@YvpV7?b-ZvDhzM%hASg|tlyTxU^it1AlTquU0CD{0IGW?_3=~wXWEIB-G z{_&7?>U-#<-6lM@kN9z4@Y{Ry)_-6r)!^Z|&Gbn?o=I?L0__g9w??DDC{J^eo%Vrf zx2M;^J{vEeHk&uHh`n6NZ(GZ%ujOs5grSRLImZpKbUVIhDr89>+L$8+Cu*L6#N^;8uk%Qb~1bQo^@6A z&E-@#>r~vla~88UiLFn(&Rek3nK#i9l{(2ze#Te(T)zHGw!RCS?;J&~qE^UfS?rJt z=ba`nOCHXdxCM`{M;mChysyqjD$iDmPxykF`Fw@Z<|(>fn+!Cg9i8AnKRL#+Y|H|v z5Oy^jmovBpe?!gLT*g&~zfeO?`!$&I9v|l;6z*coeeiAo$seo@)rR={C-!HMYx?17 zh{L~tJs7lM#elb}=t5?*#^IAm9=CdcraHv#&m#khUtEc(iNA=F>mZ&OL zFvhSVWV>Gf2~YPJ+hP4b$tGT48*h=@ICM>gFDm8o?G8YsgY++)({xZ4?yy|{U*wgq zKps2EFnnf=pP_a*BfOn{zazeAC_=5NZ{=uLUJ>f!{(3;t&(Y#={!G}dFh|aBoJ`ta z{%0Spv%fpAz-{=d&7o{lxbqet_J1(4k!Y+DO#B}?dy||s#o>32sU?f{0e*GBr_YU{ zC%f<+d>I|E1>3uj)FRX`I>YDYg-RC#|_-&ZUO@gyo(6$)4 zu10R^qj_`G{0u66r!T+YjZ=!*@;&mnyX0~A@X7Yb;qGAv_w&6EsG+992kk%UH;34> zo#Zr}6)>NCPV|%?$z(V8ZbNz+@O`RjMftXwa4w;C9j#B2q1`ZVt+v2;r_r5p^lXGH zexg%9vV}k3O*s2=1PS^T%1skf&7~bHA^A2Q!V#1|Z~XU-Ii*}gSaDWdtIQ6)L}Ef7 zqZu33md)x&hQ1)zJ>YV0vi&ub4zE2)RcEiCXkosfsa993?(b4!gPi`Qbmx2Me+D0R z(Sv0;HpyCsLHvqvB1j9~L%rDi$7Mdozt&X!V-L=8wbuT&rtUB6tqzLw_N%+LPo0?E z_Ui18)!9fbZeHy^knJ-bPd_L>M*eD^7IwaeN{m;0AL=WFeT+HrsR#;` z!-Fb%Rs$t!;ia9MG~*sQi>Dn4&1oiK(^Ek0)Y=o?r-9<>;Cg!V_!;D6GNDrzYg@9* z59EaYxzI4TcX|9GZ!Bwd3Elr@zjkVCy%%w@Hu+e}JgjDJ5}t>ghIKW${GAJJa+1GL zgCG~o3~N8_z0t0E|IqJ}<3oCU$m#xtVsy@pYq`i{IJ+UczGRW-%*5ACD{qwAtXxX# z4U*trLhB$x-2Q;SdB=R_ExvWwUly>@YHqRYs+cVP%ineajQ7j_&b`(R$UT$SFJWIBc>WgjeuW*YMjy+vaYaa69(tG! zUS^^P8PF%AUS;Kf=ccDc=xhboKj(K1^t`pFbY%maG{6_zMtZ~9scUHb3RGPNrIw3F zSHQ*9?C%D1)LZdtmpR*mYNQ|MFQ1Fg9JwOib1rNo&OBezxK|>N$JLKy&~kcTz?rAT z)R8GIhAOAVM@8%OoVJ3K%W3~8-dJaOi#OPdaxt4xnPfc4SE+zvWz8preJVxSfI@s` z`BHqyrDy4kF9rWQ31m-#^2vUAY`zCmiup2bKu z>+$ob(_B#f=Azc>mv#nYIqPN1Tf6WS-@B@niBDSt_6$j_LGzzy`(9udUa*GsB|g(j zauYS>BWjrYa+t!M%D)N;mEl#i+qwPPTfIIe2+Vf+hGJI+M>IZm@r1rJ6>ej_f@9xn}u)I{W8*agzJ8tXx-GF~jQte@7yd z;|oQm#lH}lX^gX-%{R{&7u!FePPF*%+X%Wu)Q^cAPz&#vc<{9I(X6(on}=Z5eA+jV zciECoR;P~{W4f3XC3C`*JaU5hLmQ8Hbdjp<0n!%D;rbnpg#hke8Kw5}}66t6pB zY1kW>Es{i>p2Aw?H0o$)i0c!{8aGtzJ^{~WM)Jk|PA=!*-7Iag_al8az;|Dg+2(lr zvg=EU_tQi&T2GqM{?Lqa=$WjC$|AbYf}`1CVz4ziS=n4PSxr3S?xaVJyszr>{QCU_ zIgO8|7o(@K20ev5R1!HS{$A9M%jkV}<~F|lrw-c}J=X>{bsamp+BK`#+Ew;+t+Eeel{(z3<)7Ay3)k`A z){6i)`D-)JbSwN(ql-?4x*e~>n~(Vaq2l-`e$7nS^9McM$Orlp2JIEA?-%DCfE;_- zmz{i?ZN{;lj4n66g>339W1d74N2A3MUUPr;EBF;*_3g)OTTAcXWY=nYts1kdImO1= zd0*K{d^R>F3)x0XC_PThI|zbwXA3@pJuUd8Z?pDISb?VK_8tm;ATA6T{T&*O=A+IN z3vYq3|H$J-VNkeRS@;uH_O{TzB+=otx3NB(4U<;D*)7Ju*VvD;%BM-$1wOjQ1E@xsRRP#6~WO@!CAH7~xBC|J$VHIe1i%=aATpW60dB%%9j#iK9L)#tTm)w8PXoWAnb@`d##Jez1f zYZ9`G3Db)QQ^Dwj;;e^i7TuDEyTZ1d6Xl=4;UgqvzYO1Qa`q>L*oME`__g8ek*y-q zu##u1csSIx|C6tFrthn{?kSR15x!JlJ1fDLU`uQ2e;vGRgqO|9 zeMe*HP3FU1{HbJqmGOkVxYxW7tLO5Qkm_1k&ECP^-+3LYzYDx>_WGB0Ub~HVacoPX z$eH-0k#pL4`(iGM1+T!Z8}@14#>czzPY={~j9LQ~XMb4mqvGgz9Dgv1USS{gJ-xrh zs$VBhm&9@E2I|WOdN^N%H;Mj?;5qcCV_(taQ0wATS>#W&@cIQi(NkWcA3Hyk)=i*$ z^T_1}zdhm}*NrnVpDerkm-Vbr)3y=HwuT>{(w&~XurSv+h%Fz2^22G)Fj=XeaAFWo zurHMC!Ap1QDxbc+Pd;ShKXg@lSks=BY{Q=n)yZ49>${}4DQSL#guhDqU*TiDAf|d2 z-ztmNLKTe?V!r~`^5r6@S*&|W3tv<4YZCDV;^E3&?G`+`?kup&&IWL{fEN6KTBy^+ zYX_hAhN&aq=`2#X)^84o3Bnob_t}#aa4?+gmY>##y=Ij(RawO+iOu6D;(I4By09`| zPFj!O|ksTeLf6X=E{xuvKz0D!{=m3Yl!HoF=+kvx z?hO?CkGFA0Zsb1SEuKa{#(z#Mjz}7#b}ROxwbsUn+w$-~fEgbeTL<#>iQjzc9-Z8? zld*ha9HDAad%D<$UWPg!O+~7&Lyy|l7S-SvR?Qrdnh3D~=k$jDFeQ3rvM&1V}zNTfpP`svr`(pIwX5pIh|NkfN z&;TCPF&FrfDjqf9R#iSr1?!c{u;InD{8}zK-7LOK=bDtRP2x8Zl|Am$quXZsuEDsA zG7e|?n8ABKF3<9pfA-*#u>XubUP}hY(`s2@S|0Q)LM}>b6}+$F^;xZ^R?9VYTvLy& z3nzuwL;pH*FtxNAG^DClUMmvIr5)9)zeVBp&qCL*)+b2Ek(ivEB28yVml#X`R{3@n zdmiL|ldD55(7muu)*HQ&$)+WTswqf7SY49}A5!_38jVxoL#i0EW+pEoCy|{#gk4+t zT^ZI)l!jShCDaS#=G9m}%sh<#eqlU)aA7dn7$d$3dHMz7qUGk>*RnnvM5G&J4c5~1 zmD(a%gx|c5!>J+iz~As$zjSRl-=wiPDVzvV!~LE_pOR!VtZvB$2h-_C3jY#_lkefz zHU8#VRQgL?_b1-2rime=GfBQ_sC?)*r27kAV_O-;W>)sRF1o2B^H_@&e$lM#i!9O0 zWT>8*%|p}}yrI>>*r?qa--d8f+hAy@;t`=-q z3mDo0zP``Sb)cDF8dD#ZaX629x@_+1*zDk=GdnUETX_i4z?eE3(+|e<8|2sw%T5^6 zfB2X*a?Bj|+4x-I-Fy-COd=|;i80cNFA_#nu#%BIB8pwB=hO6gfXv88vQ)3hij;@0 zD%{{*4;=5~&izDU1FWp1gYldUQFO}=`8{PZ$oU(4^@E~mIpe(|V1y=Um_ zB|N`HPao)c0(L%`kv@Tg8R0>8Jj+dT3zG0+##+i9%fa1>=E+&HNcsbT(z`At$Rl(C5 z;nNrTIUJ_W6+7&p{b#fXTH1)(r;%Oe1^32RkI=)&LF<><=!iPKkrU2(KJD*w_ElUm zZ+KPAYmOP8^Tg!W+Cvy~f~8r{mQIC^-x_l(nDcyWw4owxIz7lhJ~PtCtny6Rj5U|J zl|0rx<>gx!kc%lKPfwcrEJh!m-bDqgM&7s_W`$&9yJJAGK*dU+cGjX*XRj zlfyz5p*h7^#$w`^qNJ;sJ~>aFXEg~j|1P#!EY|sh%k*`zzRxp`FwZ^KoqpuI z^yGPcAO{!fqdW_{3d5n)wCxsqV%H2DI7VNNlcN*-$dmkG7)8e_@QHJ?a4GQNZuC<8 zIqgW)`GV1_@f)p~SslIMtmvEZ%fz>OfrQP!5hsgRd`Np&ISy#>3g!;e<%$%i?%g8Jm%!^7T zJn>0*Q(fMsmU-Fw;-okD3r)?3zRO2wX^jXU)>@hzS{Cm!@hvj56S;g}%ynU{b6p-- zOWM}MJw~!zi&%nvbm0b!PJ@FbdBC-0Gv8-Zd%^f$jCU@Zwo(6%xbmVg-h=Wr1+6U%)1w zA$&R)ZDH>IP3zI?nln`C)YwX~%GK$@YxvlbP5eTRF|02c&l3rI`nIzxr_9aVwc<0> zy~|I^pXA}c%o;ZqO}CRd>TI^Buc&%34f;hju(4*zf3>^pR~eR3sOd~Rs5u;3PU79o zL;1C=^G^Q9aU;4$ZXS}V6u6riBIJcZ#pqCZ^AzD+g=g#|c;3467x1x`ylHJYs+Z+V zYsL0|q+x5)(1dj8k{LGTgAHcJ*YK=QGd#o5XV zYJyaAmiY5*OKq|JtM*DYlE(ukz@a`*SiTpS!q1Z$GZzl1r>wDwPi98@+jZEpJ| z^NAtVBY;_LU{^!48P&DIniV7Dvk*#!Q>#X&`)9>+g2tW5{ZA`4~C8Kin-v6k5r*+dl z^4YsMQ;$SeB_Bn|Kt}Wn)!;Ad*HQEjbF?e)Vy>8IDrp+;+tIM`XFhpY^FN9_|4RO* zkm=cwdNCYXOE$NW(F3sMl%8I~>rizn>^ewS0*agGc)VlK~3F4eo1YT^O6|w zInmTJ@TZD(rcYW&QpOtLVseNDt zAK%cXZ{#(;VFUW&@wa^5{$zF_*$w;FhVU1L)8io9<56@PYz`5^VhFcJe77AwAEbRM zz>vt+P%q>enzMvqhwoc(w}Fw?MvLdK{e) zLyXyJO0HPR8{H7ovsrX5#QCAZ(^_&6c4&lo*CTZF6#icjS>K|0QT}{Vd`Tw)%Sn%l zY8Ab!p@q4@NBiD0!-foe>2&gcX{;e|rXdRS_RCPetQS7^pr4)HEyVooNX)zRHmq~1 z4L?KdQJ!B=Sj+CoX}pFrm_w9vi#}cEb^Xf+J55$)BcaAE98>E>JF8tFzgN6oGluXl zod5Q~HSyYGG}{SbBF&o+l#Y_6%vuh1I4{2>oNFHDyvyKPMX#Y|V5p7uNTTD6F%IU1 zb2g$-F3M-WuRVZ6_vz?;vS&WbZ!18b>=#-*j(wQQri4{J3rNgzvbk38 zoi0IQ^3e8BM<+84Nk@_b4-=zqs2B0Tzpz^6hRFL0`d+}r>!y2>Udhif;!%3G{V}GTDQR(SH4j5Lzm1@eeChKV6YF2YO z`v1Y>SxVEE!nvieafvLi~JuP#v

    Tk`+I{(s6Ji6)VQziU#GiDK=WS2|L39WKLo}5r52mWOvo0;Ht8u^%%d@1J~;ZbYUZ>(3ZL6N%n z^RiyPESpvrUtVQ7!ugl)!QXcJ|2Yf}rvN$64?m|t<51Op3f!C~`VZ9-X3??V`Iz(Z zaS3@1XW4|^d7EW_{$!W;;oA}8Iw5y)*1YCLIr?k-?Hl~<+txJSutBi8vQ zFdt~Ily;kqIE~jkS=hy}brg@KCmY!u&eSw+5uI-`pip{GN$1IF%(|u)1E=DDCg*=9 zu|G1AJW~R3LYTXM%$iCkOu&Y`v6jetH6WowY4oYY0@ZM@x^%V)`n7{CU1?&d12YaP zET9uxa4=M~y9U$jKH=k6X4RhNQ$2$RHDuXq%16L8IhF6A=wLE90+Np6d5w{sA59iV zXoIzHX3wkF2W z0FJx@{a?b@n*6cn=wD5i;RQSndHK4y)R5LSW;sF~){p3A7nY?r2mSM2CC8(vQe*IGOfkvRa7_7hpxw{hu|mOUbXQx=VGq`Yic% zCLEhFh~|9bwWrp_XJ7ihv+KIz#a9sI8@Tm7WFJA(9_^1wp;y7nyl2E$QRghGg|m?N zc!Ew9x#!>!#O zYi;i&vN+9L%S>_6OgW1ga#U0K+~c)T-Vd=Bw4ZbCdcvtrWVSVVZKB3@eKPQz$gU#) ztuPy#jb%uM^6@0#y78XI#}GqokIg5vGu|Go(~m6bude>hI2WMLE+?_+U!Mb=GJmFkCza++cnT_3x&PzRQ9R2y0oPLbTO<2(v$Zy!^ zpO?;LV82sBhy+?R`XrnUb>qs2Ym1A63&_#rGX6~BeQPmddiyZ8_9pBDbCLo42^IhL zup(Q1vfi^-;r252aWQ0HBv-o7xE7$?LgQF~FAJ;-T_{dpWG-+CtXT?cmdjbJmS42K zJ+e*iVz-$8ka#}S@%$GysiwoXSr21>lWQBsE_UP1hBHq@v9hU^AWl_MTphQcp$XMd zvs%ng>C0dK%s5*aPh&b8=Fwg$|EdmkN<3)&G`+JMT%5E= z4rnLeG*lSdLPIyB>_+Ico{t~S58Xrqg73eL-0sAWy<(%o#!Qd#c^qC$f~!->-xPk? zMA$hRUJhm-zTtO&A!pUvINmUd8lq!CY~r)OF9KCjuCV#ZbxraW(KuNm9B z?0-l6d$i{;)bv=-zh6s!)7(t}m}o>gpRsNb-X)UP7DtIb!em6zBk zZ?T0>vs0ep0PXl&KKV>c|GwtIcjK*vd{cWgXc4pinV@Vo@rS)7@^)n)P66}?>-|%E z4ZE7dp1ez9pOd8O5bXa`+sMyZr7gkB`M5C0>ul|JpUu@4XiL##wYJgkcKQ8bum5P* z{QiM!le5iP{VR;xPm-P&@#GCp4eJm)lCx0f_&fgC5Lb_d7E^exv+(gzeM;VgUM*xd z=kr%X-O6eH9ocBH*7FK@ZWhlE8L6UanjwFN}=YHC}orPYHT49&qVv;ovg3p19v&9&*$n-23mDzm1x%j<+ zue(@oXE~%_1FJWZzU?TvmxVvVlAnNV=SkQ#vD+Pz6Y;AgFh8{ndwTw97QS6B^A!cm z)fAQSDn%#D$$C|Uxld_T{9V=ORefK@b(M@mEnU&rC^miuANLRZ+XhRI>2*NSM6@JC zHpS4r8t&HDk9S~x2X?UsdF-dJL*UG4uZ6tRMJ$ zeMnjt_X{;WTks|tv4FKiQeh7>ZDSdqBf0fG`7NAo?&=Rbu@fZdqJOuaqS3)}-rbY_u1BQYOYN!W^~*+ehu$r(C< z5N0hw$#uB$CzJ?l9!~R|ug6yCtRXLR$lQ2(@)OGTfi+*E?nk)Znq<7ACrwF0W0vbJ zxrfFg(Wb2SJNn;T+}N6L&>lZO<&$+|Yretr;2({~uc@SDKG_IWjkkJcsQzWg5xcRG zAFy2JdmiMP3TwuY;34F)zdQ8Oi_RpV6U6&a-#;KVp?&22C!W&T_q|;=P>+A{yYTyk zF?^1Y%{%Zs)G9pf?tk+q_tBqiu3S$Bm%GCPeGRr^I(t1?|0f#91eBU!jFXIUsxi)_ z1#?ko4Z_~L)G7@i>q3p?VNOYc;d!3yL=iVoDS22aBggAXFJR1+cNBMP_;~i=o zLrB5k|7)Rc$S}S5*>_`%FI*dbGn)k1aZ6j$dRj`lnZ(#5&>`$zxWyV?_U&?MK*1CIm*ZqHteXv* ze@Z*8o$>d1+8a)=xJF+8bM1Yy8_tzXN)Eyq#_7mJ7O0U+v%8yY1Q{$&LQ2S_7ZU{) z7S9wC{p1tjnCob@3$ojut9#ULfJ8Y4}LOWk@lT> zvx|4KThI3y!G81!E6)z0VVDiHp4e*w?Jjv^Lb|{mEW$ z@lQAL(C1{M6K}aA8SO}7ID3r9W55?%_jPLkS?Cr)BAKd z6`anYl@RkiO$%$wJTyXy_fX^mwE6@+KIemVrC;5|v0d<}3mSZm1|QR)4`_CC{CpGj z>*7RM&0G#W@{yago)k}VFR^xq=*T(}9I6+O;x~QAU;138p`{4!4Xc-4f^Aj&U5ZsK z?3%o;&jtN*L%+PVt*|kcL8+%n%qujkF{x`0?Y^Q_!{F&GR;^U5#zUq(HJXhIlT`x8_)9fx8ct`{T>A=`s#ZpG2DCfsv$1F zfFe~%dj(QoO7@{7UX|p-g!<_f*@3Eb^aWbd00rOGKBfb`@Nzhf3H3SG(#9jS@}{Sy zhN$6`%4%frbr!N6+3Nv^exRS@c{6k1%QC3E5p{O>^&TF=er+GL-tDvPzFP;~m$-JO zJCBC?1KgvtC$x0WdNLGGc}5G=rTVv%tOeVzBnBG0pHg53()s>t(TVFBc206~k{8D~8$wNBEup z@Ut(QbGse$IaPHdA?Znac2+1q=`LoprAV?9v2b+-&7A>jelgAg@TQx-e(dj7@TIBm z{^y$4V{t|~`I-_jnQV?{!Poo}TK2`?VX$=yo-e`w?PTEuD{_lYB&Ufv(7m*AJj1Hi z7tg+J_M(kneZnv4!e;jtmGxuO2YDUlj=%5@NAniOvE$?PYMkGV$wSpQzHs7NN%9_Q$Rvk#cOdf_XcXp}*TaT|C_7CXs|_`tfj;}zci+&j zul2C6vGs@EA)hpiY>qa*N#t=h*A#QBZJqf1(enaMZ zpyubs_7VHpnorkMd(HcrK6{F74H%kBjv$TRCZI{TWf=d3SbxRV#O@I92DJVMmoLj2 z-<4aG?M0bsyu?tQc)D>1pK^||EHJL6R-LVaR2w~I8#}U_Jvk(P_**;W{l7lD!gIP# z2k+3ZDB48$Gl_UHNl_qF|9KoW(u)i;@_xd~yKG{lY$Q87`-}&e7-+0LS*A~nq^0ZMfOIdiwc%vw0$T7H68QVNe_>u}7izE8o4LG|iN5*S z7zfb1Ua;~@coky4HZbcw_M(ZNzDf2&UZ5cv54zikBsON9LwwcNC_cfV?j*CH_A?!y zN&=T?+eqm#?HcYSWKS|_g~?$h{;UdiGNP5u9+op#UCN$;B5Y=EHq$w!xYHeXx*FdX z#{P*rw8P)`&?fM^HZDEQFDOk)!@AEj+GE~_9i%7O`(1Qt4Zlbs2%X;Pxj?cvEiS1Y44fqr|S1adOOxw zM&SPs?2#($u;`q;BKt><8=}Dr?8uX-SBM4&KP+hWJqTzewqH1Fja^v7mMoTYoE^(M zzDe&I(7!tT+?sS%U2YitXpTFqrwRyj51lT-o>0Rh%!+P|VNM=VWEogf!>{Ypiq-(2SI@Te%IUYyVBcF`MB-Kc-ZUQfIPoI zexHIoq1InkIqwwwjQje15rR7z8)f?H*AU|v3ok-8ayE>a@2(5oeKCAp>`C+C>ugV* zE;<{pSHI}pAXxjAo_-2{TEU*z;c`vAP^XJ+vC>CR;%MAOBRv9vHuEwTX)sXPr^5H%%G{1%9Sali=7~*{+qee>=VU zOHS`RAM~D2!zuR}HS_+EYyq8E44Ib2`1Ljd1Te6qP=u#T{||BlKx2 z{%X#nZh~f^%I^!Juqt$?l)v&p@=US3QaFY5b6C-x9qUbwz9A9c!I=K6@pqosR}a2s z>$}6%FG)cMt(De9dzG~g`zOn@HHBboHW-_loqY^9oW{fYh8>yaa!@POLEJ)*Ho%N^ zyzr2{TZ2mL_*n6X4r#l*-vk5J(uox;%^y6bW$=C(&vhj&Sj*~eVqtfn^j>`Y z3r|nRvQY=|dy|NBG5wupG$Va85cc)bKF6PryuPnB_r96ere4E2Jmv9&Sl{Q%X%Ik-1T!4>~saHml9FYIgi3&I?P zIQz-fUe;z0eE6}xzp_@5cT(2hrF~w)^NNy*BA!*ybMm=w9;44~)H&pQ zv%<$L=#z=oq(`q%#XmLLCWEO7$wD-iPj}W3OK^#9hI1zVVU>^5&afgR)DYS4vvBrp zIAi@!__v)65A)VreY3??+sIL<^N<4W)m*0Zh~^dxwuo9RKw z0a+DGY75|OAzUqls|9eiAX`|FkC7kk^XYjmv=>X^^m+IB*FDeb^`ojD7xeFhz8%oF ztua*DNQ&2(dtE`2mssz!z#5oPu|KSnna(mzfn}3@GTFD2M2!>0f)m|k0^OZ}3KQIW z5(%7&S~K-^j=n6$`;{eA49Y<4}os!w0*oA(SS zS-)x>jH(Y};DN3iO1DS3VjSI`gqu^KRhX-vfNP=d#&G-}#Ao@2EO*7bjz-&xj=#m8 z*W+=9eHdX6Mxat=c0M_(JY>yA-T_zG;0t^*wfzCA?;0gC~fuaGaA&x)!+Kn zAWs{DLO;XCQLu9~+ct{kk7SL8vpheEZU%@mdhsZ}U^CkDDpdA|sqdp^JDS!3mp>;d zU-2Ra@)&=?m8m$i2tPJjyL2#CWB7e>!0W6}O-NA5_=@63PTp>MRx=g9IvKAnvAM(~ z5HKarBArMsyKxor#0uiZ7mTeDG;innp7=Jz(`Vr88ki8WCfCs}86C||g3#DzUr-fm*V9{kQ;Vy8aren0kPFzSX?6_a8d zs0{sbvt~(2d^m@1AIw_@YbVjfA82M*^7a8ecpDDA$_Boq2cbS*Rs5)eVo#HVXYr^O zPose`HZ!&ljkB8`4l?eEzF)+P*iKqb^8Wv0LsQ{ls1f=Uf2cmZY6(BWxs%_+m|u-{ z4t|EZj$4g+Ka@NMB~NQ-A>BDG)Q~x;9f2jg-Eo6!mT1$-z%SZ&aOQKrZNcw;l{Qp! zucGiLJ#358)qlxkh)7rR7iU80u$HhNUG749KZFM@Skkw|6%Dm|-q-PYUAp%gU4O&x z-iGGyvkae*@vm5lK{S6fd60DwDXgda%OTGKSIk1eX%J)*uX{Yojc5BNu!ECH`cyIR zZ*2Zt^17J4U#V^IUY$?!+YRb;qDyV)(%Yo}RpWim7{eLGVQ+F~XrJ789)>aI7Rxx>f2-u7PG_$<^BZvoH3`N%EFa%&c^ z9Xr^O|J?-!_2EZ^bCp7zG?@o659Wq*Nw!&Ga)?Hpg~zvyIRWhssG95l@}WeIMt6{_FLI7Ow@0t3u8qgp)u1 zruioz@M%vBHIOdS?yzt08eZPy*WEG~dJFFS2Y>$Kd*6z+VdVKIZ4gcW)+oNxzC@!B z>3R$IXoUYSYhmX1(RnEsaQGsx<|6;(qMlx2DK4@!m)MtZ*6>C6bjiM+aPHp~{?b)( z&^5fg?!2~}_5|Iuu1KtjzoB-}`)uL|bo~>0(angybN5hFdotQACOccn&N1?Fotz|O z%`>nl1z~R`?L`*;b*%**{ur;i@i_WGs{U|!5bpd4n+L~iM-sIHBEGvH%SC+}iwQ45 zk8@<>AAVpsx8XQ#J4Q1@eeM%z9Zv3&e`G->(Xk(ms|SQ?Z!Aq+QI}8n9AvJ{u9s&Y z!k7MpmCrff6PCHfd_x>YOiCp)Zlc9Q6zupVJ&@cqCX$fj&SDX zK$puQRQ!;Iq(_`E0~gvBfVms=x7lY5bwiX#+A@jT{&D zoU|BCMzH7| z{cffI9~j?<#`lpseQ0cLA!BoYH#WYo6Y?dTcpCneMZJP}m<^2{Cmkw7T{sisx;lxY~QUnkB4*En%UP5S;OeB>WpMp=A8AY=CD<_oAg@<`~>S{N8%@M=1To5@2WL7W6 zwZ-6Z*kx4$r%U*C3DhXzZpGcT2&#lS@1bf|4l)wzouy%)L+qLee#DW6hkE8jDt!wp zp~Fh{M=h*szZ>i4HP>BpeOSYNlNWIVFK9~WlgOz_pmdVcU@ zhqEO^;8HlLa;Sce(BIL1E%(Q0zO4K5wzu{c2!6`!JEPoi0Ql2eL~6{S`A zAwdq_RYv>qQppJ<;+H+(qugXW&huH+_%yEJC@{=;1{=#jROzF2gQZ~&SQ}AHGd^uY z-@oL#@WiqxRmfAb;cXf+kXUXctk(~_A1<=jr}h6hDLIV3`(W!X(y<+wYy0sg>@qtSlb+&w8L|$cmT6XUofb+YlevnrCf9j~XV`%wv5aMJbngef ze_*KwqtIX+|3Sub0PEA2x6nf@71qnNjajcy1$Gc*A5LOIRlgZ5>;l@k3P-ll-h*^J zpug8$8@vu7Vb^ndz&4X8=a_cG{|3=9DCpPXq?#9Cd?7-i680KPv#}j-;707V7l>U{6Rh3{=Ww`t_%n7(%E%vWg zY(Ak1h+F=V&Uw`S$;VOp3TMCxkfu%yf{s!<5zbdO7Fk* zq%U#5ElhY5uGE4Nm1%wvvX%{Yq|{Uxg=9DA)uTN+M`H7Mq5qGO&_rY^394Dk?&_V! zvx{8r*7j(7{k_-c!3Q`%=MMPIA!9#8V-LGuIO*|l%pX68ey36Cl)TjmF~bSiew?-* zC+h(Zj=RGNcRT?fPm=ghgZm#8I}M%AkoG;_`ny@{iJrl zIJThwD$ie_AJg>dSN#ur2nN7{Z*aD!J9MQ_o!z}N)a?TM!dm(6c+}$kxbC{X>#P@9FwqPVi_y87AkG^ey`CV_1!twgq2nI zjXD9pGn|)}7S3me(K+FBIGd(0ewARo%EIRgY+0Be2`i+Y#@}kL2>V40rdn>_ET znq;S@tDbY!v*ah_pTob3S{a{(^|3`=SIAw%saaueJ0F{vPaK(-{^#?oe4d$~cT<3T zJhD4CpyL&?af!Y-OBjudp>bF@Sls6&^t+^fmthafldVeRtO|sz=AO^NmT)%BEBg1U ze!VWc`j(zG)5{hz?-+Ca~CvKwvrC2h1;+Pki9?DN-rU)y!hL8Zz@Th{-DjWM_W zXVSyeXc10b47rN1H|h!={2P;{a9-$EvSlqh4%kHtd46RvhLeFGMC1K@+J|q_iw1vX zPP3Q1#MfpzzBLE-y)4!c>rj3%C+6%umZK*>q^Do@a?d{a7-}j0NHcz>ArolIOx#-t zrPq*;?PTR3T>S@yf(3pE&%zobm7?^!y#AL#jj;YKoa<5uMe^cj4m8VzLXR6`GT(%= ziSEJ98)V{w(VZr>VeUL+`}Z2*PLj2SE^d&w4X2;3mThx(o;FzmZ+pvvIzL zKs{XF4K8)l|L)|k2RZ#Z7W>{dx0?WNrsDl)W|8vI;}W#%Ns?F{b;F6Yub_T?S%e1S zz1LyR>(-GsP|x5s`&jBaYr(1}5_kmHPm+K$Msdz4&f(Enqy7h<{$?K!lHvc4s{4T3 zv3%nO{ya47goH?xk*t!D$liNoCM2^W4Mim((Ll08h>R#nnQ5BY*=fi~Wo2YP-tTey ze?IT~etb^X{ap8TU)Oo8-*Fu0b)Lt$1&yot$z^nH9xR?hUq)#C{r!~lZAi72YgB!t znK$_+Gi=O+pRqpeCNbwt&^Z$%zKQ+W*F$?+dt7Tv1}$)`DI|zJ?;d8Wt;HQ3(eQ*JZUCL&l26t zl%Y)56FW>BlbA1;T_C6UoZno+W3A*duBML<$QAB`bVXrNVe%-*5)09(yF~;=wNgG;bgrh>l)iO<7*El?SJ3qi zImSDi&uC2S1QAa@PG19sbBbnSrD0XkTys}+mJ>ZEVtY;G^ER%GKOBb6S48_y^OGILD~)-9N_bS9bi!|>gBce@Do4<<8(!M+&y}Tdrjn>3 zY8f<><32)eo#n1iz}4Px_62^wuQ{*%60MI>=6*)L`ulFM{No*&={SCVhWu(l;%!(} z`dQ8si$a~ev?ePHij_;To9Dm!b=a@>iA!P)+D3F3hZAFcr6;S={iBR!BROvJckC+} z^=DS%%NIP>T=C9iwlU{G;5v? zofe9azBOCSj-Iq<1TXd$IS!$@gAyL;zQmnh6YUK0wAbn0FkBt3-{Wv|s<><}I=(b^ z2m|QxU{S|Fc=9U0ANvD6FFxoAgSx;aGq0d-Lsr}hQblg4Hid6v#_f4`jH+Hc$m9nj z0}Dm0(~MD!6pIZIEk1+u5wEooA2$)h)a5g4@Tb+-V>NWvpo{hBViUU91|?ls!gDZe zuvlpfoSF?^m!WqP^g6(U{s&ppiZ5d~yAnKfRGMf^YdYd)Pv>5un?oVMyL5LvJDftM z)5&qB=wr5+VGjAtri-(o#|*LU2cp{vaQ9sj90FZlO1#@?WxO=%Aao+pE)cmZDPOCi ze2LEeMMrmf;t%4>&qYZw*Wg{2JrFuPht_T)#7EH^<4lcMKs`}n9dy@bv-Ma*6Smlj zO?P4yPm|iKqQ()TlBvA@=lHM&7VN`|Q@m6}gn8gZDH>Fp)jWchUZU5Shd+YsCTX+i z>O#kuyY`JtuUOv~bopcQo+9EO3Cjksm|pUUwy>=_ z-yOMQcK+q6xF~kHjJjS{715gXIDHeWG=mDRJPdzoLbCE`EGZf;Opo)U`wj?~UwnO+ zY$e|1+%JNx$Y<2SkCyyiSNwPpK1Bufn2~O`M3iOHqnM+Z8uDLu_jB$T`{6_tzavH( z*nx4wK`>~n@+e~8|x=zbslQ5$TS_OjN~xsEWpvFj@NO))&kf&S~! ze3HkE9kW;A|7SQoiB7&jKF{mh<|pM68TfnN>9~ ziu8!luqScwRpUJG86}#H?%1_HX6OXwT_xviXu6yBJOqQ9=u0PkeIDKnfeNE##?#~! z^Wp7cc>4{kT}ziYYTMlLC&#U9cN5)SD^vXzihL=5`WW{nds3_sdfC&WrhapF9QEA` ziq~%9TQAVPgEVNf>}7=vY(6cTOpiy9`s*=;f}W9uzCbf##l!2o z*9bI@XVXy|VLmUhlq^@_(^j(k74Kqw{&^8rRNA?jT=LM#Lj1vf`dEQ2R)_En_5WdX zw1xDKp{c9SPr7SQobSbodgDPaym%5%dXQbr4rx!`E%m+;U8pT$ssx)#L!Wy@LHWcI zw~8y$!Q2#Aop#qh`GtKl&TYKWS~RRgOT6KmPito3(sZ9EB@p5@PkvDkp2F!4iTrS! z@vDh+c(R^PrQ>6XHn`umppYCUaF;aMnJTF1bRD4PX^W@d^fx+=6 zsxFDvAkRwZd;rz=qqU^y{vMbdHQVyLB9E5CyQ{3?&P={bc`p_>ssaUZ zv(dz;kC)xOV{9dMeasClZ^z514H%=W`HTVOXW=n2cZZgj9HIh69-m|6&Yr(C;jDX} za{m)-=BVc$fjx(4OrZTQV(#5+Xgk#1l6ZIXyRm`TYif^3eK*~U*_82S^e3J%TKw%j zitoSi>+jqp=KXy~6IYSsY7$)Q{;>vR6JN6pO}i8JniHBv|MTigKG96f*3L&Fcd)0Z z{-0Y!lQWT-RG?wiebWGzG#3ZAbG02@*k|ld`ZxSK3?cr2J-@KHs9~@dn(Xp9-oI|= zkGHY1t)hgeaTE1iwzK>2o_pv=)GGX4gb=GS{-mkL*u=k(H+I^y*B2Ye%TMPNXQ>37 zi2BpB`!srYJ!?osU!tPN6+Y(@l)1>?oYT(we8#ymFe*kwqb}81ayUnh=kOs$W6y~s z{=@IHqKY%Tz-iWeN)C4d=VHFhQTFhcQRu@g{~)it-|<%?1V2N8JtPnkQ8kPNwvfOc z?GF~!v2-2L@)uGQ7_|OvcH@}t@%3epG&&p5_i(JHDd zZDo_(eWCB?LAjZ%ezM<;l35Oc3;o=uCk^OG1Dd&a4ftP{Uc`9&ZKR%oRb3^$QzZAN zF_xdjPO(3CtSJ5-1uNt?OXbIl96$5-Lh@dSH=nz1G3vfzC*RY6^^k5mTlt;rj-vJ= z`@GS!ZsA?-bi7v!-_sC{ZCFben)i&WUZP2Z=c#qdeAB6%H9JXZ*@$qok-;E+vH{2MZ4z1lE6T=|CBzrL0N4YSx#Szz>3>Z zoK62Sz_uIdbZWYtDy#(t-bl~Vvw^IkKt&c4I`C$hE=q1ha?&VyR>Y4lvQeTb$>&}Ec64WYSx zX>JcX@`${ufmXriqT-xb(|wa`Vt)Tg5>jCur9Z={KUmojZ-9=|%w+&J~k0#X6q*d87M9L#0Fw_j$s-Y)-95{yQ^|5j*Nu=Pe%9 z|JXld1S)6xb{Xv2NN;};SNx5Rvwn4%H6|xpZ<}cQd{+OK#hxM0W8}KuZ+4LNT4=Kr z`pk9D_vz#?Ua1dE>O^N7(cg0Z-N|!jq^W1kI*8d_YuNh&yq-t~gSFmTdwg#wB6^67 z?q^9wc+fl9@$F)@+Pgcdb?1ieR0&R%LU$oF z=B1N4$meEydXwlOqg>)9vc84o#-2HM(TC!&ssf9wC*o=+4td(B>0qO26VUi6Y+A!2 zem6RG&WKxPd2M0QdS$Uy8`|7k{5ZsT%ll}FJu|gGW->pk>MqtBnfqop+Ndp`0n@nP6?$#ZzC-_)V&>UK7svW;5b4HV&krXozaZbApQXfZma(2Ui>GE3 z>0NIu=Bhct7u3$NGdP;^!PuMOYHGc`kh0z044ds4y51fXtFHc&^6k|VDJ$*Mu)j973;x&l}12C(*$UR=-2cyp3IM&{k9X;$8QEt#_>vwLWg?lZ_A>G@}BRK-dq~|1Ks{jyl}xNmEAGieS+9-tUO>0y&WU-8fOm7 z1ioXEIhG%&{WK#nS7s}O+%9+6!Dsx$nl|&3YxH)7F}DSx<*9tnaC-id7@#w18psOn z=aoqaTGfKmk>R)I2ReFMced9H;yo)jf8LmB9~kt4daBQnoyrluKbP{DeSBkIiu(5J zwU#7Bb-olkAf#khrzH(PdXbM+I}NJdYXqdK=%~2}qN{PY{-UY(*yvoIDt1HI4UJF0 z-|Kj+Tv`d)v zbBrWT<%dVhb%u(1`to2syg_b5V`}qcWsHd51(mYW=v1zVS#PQviZBMb>nlbwpBFDa zDR=ECMr@ALHR*L(c3ep2mV?i{QHvS5N3=aC*?^v}Wqe!AHlBGg}agClx$)|kdsV-x9&v)#&H=J)Qg znD>ok9lb@B%f&l_8FJYv=o>4-d&irfL8RN4H|?oC2BTF&Ky!0hUVXV>b(T|}wHJp1 z`Pl6(P~rx1KMzr&I@ix)+f9)3TaoQza+*gf(|N`B;qH6p>W#3b_HFAM-qPNtyKnpZ zU1N@8)heHC+;N7ztLDq%m%t?T<=iXoxl;VKNsRa_L_Uu4E27}^Jfe|!T3C@5)*`LO ziSf|}`rMq4Y(rul5>!0xucwV3K8?DbsC<$i>B_%$5-+s#%jPt=Axx-FrVog=VyB2Z zX?x6zj};SFNatU2ILxy4pl_qTea{=p3`DX|)55Og(Sa{%gU*)x$;0H;!m%a!wLw=0 zlyoK2r=jf2Jn0+cJlgMLR!!_N_XFy7@V|Co_U7TRk+|6P?LU!ZM1@huID?+sYhJ`X z0)9LTd zznCc>d1e-^rNACLZHUNn)8*_mD%LG!gr7Ikv{)S(^E57?_B1}6)Q)L?`TI})^Pm{= z4?OyvveS!E1RI;R@{ksL8EG_tOZ1G!U)T(_&n?22`oy zd_~`v6-VA{^gAlA-w9=Nix9HGzVz-Ld&ylO(Gzmgcz?bRceaTO*Ye!oh@F;-N$>0~~WA0+?GaBn7_sBi=imLaK^{?6= z+9B;PQQ9#QIq90H1%6%(X)ka+sK%4jpzpO{aZS&x#V6NsLAU zP==&SvDkaH;`&=uyIU)$72tdG`97+u<`R=+7oWtw7a82|2KSD+XczG;M%jB)jNOf67m27!dO+{?Cn!2e{|>RiJuGn}{rQ$HMqRVH zj+0@|yR>wuZ(oJ8eQ=;B-Rh1To#1&#HXeIEwMYA7c+>^{_N0+7(AI%4VWc*hRe$2| z@9=Vm@BhY|Wb(|ZpC#!^%wKtoto!QYyJY_n9au&eHn8KL>Be93zB6>@3SXQm+1jb( zt?H+3@!1+G9Qx0!sefR=?^?tqtNi^1{Fng~-eb|P!G&Ia+ZHElxo0W&j*76cht6sL ze#eC$$#|(g&XLoNC#$#EMSrsD%^te5fsW+W4j0=Rt7=OY?HxP&`zbPcfzKYy#^1x; zX>4jC+lsY^yWB5mO>zI5>2ZEm7yHjwXI)M4zdf7i!6u$X^m8T+81Bh0 zc$eFeMl{qO)b7#p$jxtpfj6M(N=lKW3n>Nd!@#qo*yk=KyErWNS~Sm)+-@i1qWs*0 z#?Ts&e+Qg2IcWWy ztYcr$@6or0KCEYR>(RB2O|It4zJ)i-SnOwf?ktu(0m{6I+E@4|Z+7_bDn_2GI$MoR zS7FgnHTWTSQ!AD2{Dihuc(4>d=9AGhSf_#_n~S+asb#13d8EUG(KDES4>9f$d%KQ= z!{bo&fp~HbOZb9U`v#Sp`RU(S#R)N1lDlOQ8^=x!Q4P3(II9CJem3z|W1IK!d%gEN z;2qF0@Adw(|3J#sz2tU)1s@@^e?%w8*z!>*`nTR6MCUK0xLp*rR$H#k^Y2ZI)r3-9oqL&rfXc2xlm$)H48%|===To-W zuTRzS6ssw;6Df9z_V)$nli7a;*X1DFLZXebfn7xZ!z&}qN@?kzwi5xWIOZ4%b&>(7DDX>(E4N2`w)ta zOVF9m8v7iszFG8{&e>!#y+BH*QE(JC#X1RfEy=34zP2H+8g#BCoy$vZH;a{Sa7;;A zhaOQUnl7aLB*)$D{4ZBC8>`DhL!&l)HNLS8+}~!w8PCPLwfOcY zFLtAm>{we}+ta#;En+S9RC>Ks%&`T69B@1>_DD*eqc-I{b#xYLOVrF=34_HgG+{q| z-vqOk!1Bp(Y5*VF0VT0la~`o))NI-%5?CmH8g4}BNl{%rInX^UCX=Y`Ov-Ea6&#$j zI^_-ffeuZYoiZfpgOu0pf%96@*p!!&#-~`(m-1xNjFiXiO{gAOO2ecrDboeIvKWe0!Dm16()?L1r5c>b2PEQN#%L~$Q`*D(v;e#qZU^)B}V`?gOt zjyRQ{nQre8D}$2f+W+)Zx%(H6E7e`pxw(NhtcHu2@Cy!Om~Yo zkNSLBFVZHfMV4%z&1CzLC9CI@{I@+stW{3F>U_GSw8`1*)07t;6*ewa()dg{dBj6- zw>mqn$)al;nXAQOt3#nGXsjT@D9wA75P=pV+uL!{cr?B35083^3EI=WW@J&5-jx?i z+zUku8Y#>xa>^x-%Pw-tCNs#!vTudBIpsvR%l8V(fJ(@u%F7FDc^}e3e%lT1_H*5E z*H4GaUqa>Br{XvF{TDy3V>Q|3v<2AD{rpFaiZv)&%&$EvABI5n+~FbAmZZP={JR+%C80VhsqK^7{D`K-d|#|*e$OYX zP{l{ntBM?LMeq8AT0c+ihjyUF3XE%gtYatFIVe93;O}Ey%dT zSvxW&C%>MQA$d?zmgN4%tX{O&NN=s1&+XNc_F~66 zJZC96WG;4-ENc5xpEuIYMPx96o(>QPcZHRW_>S`8-U6=3LYGsa<8;ai`yc&lZ==&! z_avINx{mu(PF+3f{6#GTADqv%<+LVR5AAj5rfSQzUH<-83$K$OuOHCQM%rWAvtq^9 z97j0+zVByhA48M{EN)SvopwCy?{m&y67OC$ZfDPGxH}lf*&J^4f}8)U>?ce4vuf-vf&Zil6Qf z*V}WL%vSI{Kfh=eAjEquDC4zjTz2oM5EVc$wz#AZPxi;wE0h1^(PPb7c~8wuRKN~|FXT<;o=fI zOyY%O1Ss6(*@#%*91v0v;;_8ewv9^%0(?B?N8^;p)J+s$%^I;PZX3 zI`%xvXN)@!W9E=`@xHdZSi_j7C%5+G7JAeY2U|nP_Ila{2YbPV zmtesgbT8IiO{e7xWY8-`<{RXLd+EbrBU=9&qrOCst`je2NMu?u>M;~YUSn}D!?+g{ z?M_im0e385Hr`zjr+{p%fIG)X*_~+1FAmL%KTsVu?`2nq+1+Vg;wo>Efo6k3 zEpV~8`{nnv9JDI-roE0dFX_uEnNL)fjUAWv;m9sI-!^YgV-D3OvWhzB8^~~@c?X*l zGu-;I-u`4hfJP1SyCIO~4f+|??B3A(Auw$)M2;P8U-gu}^!izr@DxOQoIh^MA{xW9 z8nCP!{g0}qclewof#Xs0FM4zr+HZ#$x6-Q2WOAeTWGNz%v*MC}L`<-igvN_gMzi1d zj7^Vb!SAu)G3}&7eV^>Bgy{6SM7rb2i@@)L24!F%NmCd}fn(T&u-TE4}YpBA)tO zjrRrOta;uGeXPdlJpOmSntH^Zqv;n%K2oR!_aj5o{gGkUw74qu`UZEAN&7W1Ugyi1Vv0+fwv z#AjL786(0m^G}_1Z6NG@fh?ZjQDVRHcDz?hG_-)^5BpV1tsQwi4((&i?j`L_ZIbqx z7W+#7rbV6T7)7{Os}C=`YlHaWm`lG%TgMuH(PECpWjJ1*(_iiRwc3POPS{oVbpOM!`;>?NYc$>cWqYFLgK`T05P0mn)#paSdTrbBs1z&!J4(nlQ>o+Pqg*47M|Ql>{v$}>D>}KyYZEe(}9lsNC)vxCp30t zdr#8EXT>Nli^7MPdoW6rJPnNt*zropv=Ku6B6>cmUDVQvI%21QyTn|v!nrKOuk7hH z$hQs+tH(1ppttoC7(3NNCNN3yfOD)85O=(#ZJ#5S)H)h98 z(bs}~wn1YjR6GGupNGi*JrTiGhX8p_&gV~%!Fan#8K~~f2`vO zIy?j?`op*9=yVUVZg2cmWheZ)0k5vI%Gj6nKbRCdnH{B#f6=|eknfH}GUpD=i}0;@X4gixI|V z;+l5o>_(QoNvJP+V{g;J;T4C; zFV;fl&{DhSaX7jQo_-BqqvGO7@_La@cJgg~{ja*F0V=jXC+bMUN zn_!=gl)_1;VAaKx624b!f-c-CIx8dct0(qqPe1yg=53L~hirYh_LFu*i#Ik|;YR`1 zRE|E@=1E(SO($n!e&37a@hVxqPNqZohM~OvP&5q@6Ah%_FVltR+^sv^h<&b_id5q* zX3YN2FA~b^T-2WU2V(4zi?7D@FY)^$5}d$iyp!-HXN{{|GB&8HBPl#ZZ+nV|d&8hU z;)+*9VQZ3M8}rW zy|r{~Cz}4?PmiJN4Bv1;zJ7)OyDHMUEPuTyt~dj0{}#vYr*qqRi`8WPr95&5x<_l* z_AfrlGXG(Lf8%)UqxUzoj1|w3VI5~fCyhd!;_G6ar#eLB`ZkLiM7A$!&uhZoQPGojDZj>|H;B;2|Vm(q#=Gx9K5j5Ocs zY;IS@O6jNxQ$)mBoPTg$-M2V|VFC!sMqcO0z^_xVjaHN?|i_te3z9jq5a_}O7J z;@V1wY{nwf%hyst&x?sw1&h(U5>>0w7_+=~plKg}x!*X*VeQ&ZrJHed3vO-0wH{9mm>;`CqH#y*+oUVeYK94g-U|BQpbu$AB8>j50PR{1awUAq+xS?OQYV^1d@ zyOD0D<|UGi|6PR#5usgST`4HN?8?iK%WO%P+ly=$Gl z(S6hEV>-{uKyNeAji?I~_4T7(aKtFyg83X(Oqcs^v1>ke?Po0XQ~dsfT`!=~3(4dQ z8oQiDe@lAnM8;dma4%UN;7^Vjnf#9(B=KQsWtmyXJ(nyq9~~%6XJT)OQrZJr1%FrO zJLfot3m@B-(CC7`}c|HD*rNy3%XXPGe>Dw8%|Y{o*W*3$GN_HqYvd-P@sld94QP3SVyim{{Z=d2Uo?``f76-!ij*c#vbf z=LyGCW`F(X++|+E+iNJ)jiz>Fd2MKE6ZTk3tWcSDmPzPqO=s)TwI<~GuwTT!5p6uF zE&Faums*QETFd`h^4ZO0mQ6(vF)y@{*d*Q&)))2G6%E#Mt|lw4=KF|Q9&-HyFub(i z$F6>Nv!^@tGPmAE&5Njra|8Vj9g9jcC-MC#OgNIbvgrRjX30b(|A#vqq|twp(O+=- zs5_l-rx+bSi(ltx(0QDXxgqD^-G6L9>XBWm?C=v?-z1Az!@Ga$>{mSVV%%RuiVJAf z$3BO5|A?;5@ZEIiIE`ec(Z3I2#VmK5qs>o@DS4akyIA?M&h=aUHt;j5y&Uz|8F(5R zaRW@tgj=`Jid^(7FO9zohYI3a5!|}Rv8cbJmQA$Sh2`!HPaf4 z$6{}Sx;%a@QD-%pR#|&cj9u2f?$wI;e5d1Wj#=D0otEs{^N`>q+l?{H$k_IwZ5Me) z-oHuP;Pa2p2Wo6WYt;1EiOy?#313GCVi(>NwicCWF1XuSTsn=zQGe)o!Y|cM#99%> z|6wHPH*v+j#CoA&?ljVqVt=zqa-?av_#qpfiL*25?sOUYWD*%mpWh~(0i^XDsdaX2 z@jpGk-}kZ0L}tHBA@QTIU=J-=Lz5PZ0zVQ{PN0kL(8d9-=tDL=$*LQAI+0~NvTZ}w zElHy#f7Y5r9)a8)`M55|ZF;hv7qx-@euouJglV(cKVb}^4-h*}H}*N|&6Hugghtc{Qx$I;?>(H~p@e?if2a6iBQ7679i)l;a zYSD?(o*euAq^CQvN^3u@irtcy^Ja75%XmB{;Mqe4Z1i& z-^b|p1ji2?r#bhb?`EKJI$Edb|3n!59&~)${RTqBKKyVG7WN36uFuZOv(v)zpxCJ{ zmDu%|=xsMiuOh33P4k$NP7qp_;d}6^1-Y^iTJ!hBIYVD(y9c1 zViieLZLcj(ySA3|HlMRNmtGc_8d6P+H3v}_X&`!E(c9Hj-_v>bcSVW=wP*Aq<_JCF*i5VIb2Z0GT1EOl}2 zBD8%?0&B@{i@xuK!Mn+Ik2rWY%ir#9o3u6BN`HSwN+0r1W9au_TKKH%+oQD}+RJ(B zoisEv&R(H^@fKva7Q1?X&X3H{#?Z~^bF5o?2A8_S?J;`m)6&W*jSZ=+!`12d}(#C#mIbR?q}(DDwsd?32|lpI#F!Syg^oBrKGj>~r9v7zr5$B{y*kK+|xf71W z`j=wt@7{zDe;fY3roG_rr=5>Emp#P2Pw4-XWYL@KUSyL4^nVzckEX95(D9F9>|)XA z_t0}IpB!^UV$ZV6s7t5+IrxRBkbA$#F=pso!<-dp`$|^u71~znd(fp;y=y<#v(Mn{ zmr!ph|F@jXmXpjfGGC02&*{P^wBjRvV{&5LqpY!-hpa-YZiYr3Sk!P4MvZ8CEBg2-N@Ew>Zcwy`X!Z$K{R9bi(>iPI{oT@c4N0~- z^nCz&6=#`uk$MjIPS5)$;o=!``J0dUP0x17VmIpBDmWGWU4pKK#zp5Fvzcoa%`D^J zABhb=LfZ-1r%2O-!oC~!gKoJ!1>K`s{@ z&qo?XV5wa-(btb0hVpfzo&7*$Kiiw0PtmYQ`NkPr2uLR(y#y z#Hd3m2oioK4f@le7Wj%tShwa3FG!YwZEivA0&J%Y;{%L_ishuvqKrPPw)0 zT4W{hFSUC`rWB(jC$zud>3(>!oBnJg>-FsOJNmqgtw!DEIby+S^k9PaF0Q<(4fOdX z=li&(cOqWQDJHxHzGdp8Y_*}s@?e!Z4PZ!dWnyhN(ZZV(mej_5+i8(wc65iwwa)ao%m3H1^RxVz>mK#H zw(ilwy&Ah`ZO^#2&sb`>a~1kW{hkZBe3tZ1;%toA{KfkpWG%lZa+u#~+1`Yn#j1d{ z`1^eVF*b@(H$ac5@%tmoi%P*8*y#r7w9#?1zc-6tw}@c3(&yNBXbb(_0%f8S!xs17 zLPDE8ZL_CtVhtO~d1E4n&C6pHqBkYPlBKl=d66nwO_me$E*fZ!wWhvn>bho$w!!BO zMp!n81~&15n?*&NWwcR0H0HtYf@M*20F;^G-DVY;-bqyg0zS{>4Kcm)phMn5g8DETc}W7w_8ki!Nfnuyb;$ zR4B}Z(wwa9PIgzEZI**Fm5g@P#I5?I+}Kl^>)*p>20ZMoN^_`kt;*Lh-w$$Kf7kal z8t{TlJ9dM3J`vev_V*1C=aM(WC%k1mXddP+vmn+RzxtZ)+G&!PS?8VcZa(x+IoWx0 z8*g-n>@ca2@rcsiRackKHW7cdGhes6oVK@PKkxnqh$RObj~L?GA>xuZ^klF&a)7!Y zFN>9**W;dg-kC(&CNkOMVzA9idiYpE~dttk_Kh%C#S^zEyNH-lE2ChF`hsa(!LlX(6+(%nkZli&c5+{2gzdV#G6cP&=fj zM?G2gK!!1sy*L@h-u6+?{1%yJM)A+}qKFh@Dc35L$7pPSy?8^v-gcK!yvG=K9IK5N zZ&~s0ozFJ0*f#I^w#oCh%kyGh%}%+0>`C~GnC&++dj8OxLwfQT9z+%QQUiNZ|f;o*lDS;?3VLuwXOK@SWqA+8pQNoya)fkKkv98iyK0%lpHrmtn?Be)%%* z+Mkb&_e8HlieZrA+Pk7dH0X~+wjPyeRziz45Hv=)u2rAe<;tDzu*=fLlQ z&}k>m?-b?kpsznAkf91XYxrDCtL<|g-^VV$HI02#(ev1cqYQeBq9i|BBVtPfF)qWL zWAruZ+r(Jl3g67*hu`O4-{x!kL;q*!ad&Za2dy>kJghZ!zOnO7_~fRpYpz$V+&QXL zbY}HWY0u+gfA@;9ow4wGntOlh8DH_$f#I>w_z9Fn4fkwv*~0X=B6&AvzmGfH2b#UX z-%jL%W|PAgj^AqQoZsS_?XKD7`kj8e)jc*qf$!LVth=7W=S>vFyhWG#^1$6h&n;kI zH8EHzwv(SH%>obYkO4bO<4jqcjU6l>B;N;(EL4C+6X?rqJJU#+^nb z_9^JZXYn4%6Eg$vF0%YEFt(=xk9j z$*p36R4gf0@u|Y$spY(niFY)0j6*ad&(`wS_T<)CKG@a!>L=uPPpMDxWKu4lZ!w;j zmAz(#9JfG`-0UVXlfJ(yA|1s;&V+$W__vLG-F{x+jhw z`g^K)aU#qe?dflc6$k5OKRWu9r*`zz=CG|M#E6|riWn`(orucv(8J=iwo<|a{REd{ z%xO7UE+os@#to+$3w>XnH^$n+_l!x5GFCClIK^m96(Ab3oyKhRlvQ+LDd~JnHzw1O zx5@D(QhNf8QIn{)D6lj=E-2E>&N^XPX#ay`vGVcP1bvnGjtcO$EbiXt$@j1h`|7}# zo^pncP`U;DstbFo@KF!I*!$^X3G@_0MNyGvadFpuymdL$RT62{5uddXi*`cs)7oo3 zzeis`6ss)~tF1-ZxB9IN}`Mc;d3g>l*>@|nT<=A$>>$NzxlO`@=$wIg)lG}@E- z!VFL_hyFxe8@Sd-!fxD&#&c%_KtGCu4}4l<$W%t#d`ky_>znDX7lU}{P6XVD#`zs^za-`oYvoe z_5UP#Li3_p|8Y8e96wIr&q>+Rzha=Mm>BQvPbHdl+>U3QJL|jHC%_CJ(e*(bIGkv) z4^iw1a|s_WyGuOfa)L9#ov0Fj;eRbgH!iz2#qX18eQN!>K?IbR?~f7i%n;ogTX^yp zjEvd_2S{^2-yUz)f7bT$V7p0qhxU`U4GwLAtD9W&BXn5DC$8oreh}w;2V-N`*_Cu} zg*b6JZC|D@%TN=0lzgSnv4iKgX!!wMYtguYWUtM*ik0;-dwQ2|cDrH^YJWlLe*8Lw zH-9HMc?o?nPLYP4WhD1o$Ran{26Egj<|)Zm%gTlx5=~dXM z2X@?%9d}{_kC9*py3n2!A0flm^r{6NYr<;k<5o>vs)9qYTDpvw@?NoJ5!@(%3wPi^ ztc$uuwsI5d)A2|*_?+zAW!irpmd1YYC&}^{I{xkMT=>r z{2etGALKMg#>$Of*Xr&r+hY5k78 z=zz9Y`-!*w(Vf0`m#A3&1)uf_I%cC|h8|2K!6`6*5*nh4=oI~*PI9woZR~jT1>X?s zTh@`?4jyGc?{Si?USYQx#2xU`-TI14dWpch%7;3L655C>S{NyAZl-NB6-klK}?jV6*xCbq~3e z7H3wVmDOBb7fp@O6P1EmiXmHzyV{7e+KP7Ccw5m*&mPvAX!Rjy4LNQlZz#)ot9q}f zporKjpExrYImTY#foG}dz!f?j^-xce%~7-+cBkLPihK2QrdXj6}`Kd~tYU~23|+{sg(#pVJuS=s6m%w=H9Rn#--ujqgu9L- zugT)3sbn%8UDL&qQ}ulc?!7OI9wVC`fzxluuwTXP7qnhJcadqg^PL?h^tvZL_2G*K zif4w?*NOCW4ozGNk=C)hJ^ayM^05n|gqSg$Q-ofKU%4NLD&t-~?-81lSzB^dRa^}H zi0G%C*3px?ifntyabLjcfw=sRh<~EEdlt+20*9~d*A}=Eo-lTT$^a{4-^%k)+jjD~r}LXf1=AWzbsM@9r19ml9*fE;{##2=7I22^v(?bBfTB!t_NwF1Q*ar#G|O z{Cw+uG@vSNZVJOX`Q|x(bujH1D-WMR(>}+gZ)nYW<44=+LDcxz4|fjGr~|CxclxkHzwZ}n`*YWaMmeavKR0cyuRH-=$@z3JLIW~QnJtvFx1EBGO2{Qj(!7#%qJ%N$qTcluL4P5?&u=8SzN6i)q34}WJpyIIc`db$?P-o@xa?9DqB9?hhavuNLJG4>2N^Z_jz z2Nm9-jRUpkL^++bCeBy(ebEGN%|gjM*!sChemRs{E&kggrclEd(mugIM$A-$-z!V= z3!^I!TC$OA1{!svJnx2x)ah?pch9JI*+lbsVNem4RL-%s9I=f&t0%o5gsT&v%R-hF zGw2WLNs@7#oUFMN|5J~AJCW^+&c21;lhHk&JeHB&59nS`S2yF*R$91;F0Z38-=lx2 zEM}h1ljTQm(f1d5(#Pp`gT!dlR`KdyQQm<>6rn-|I%5UdP4b$Y z7u1xmHt_DUxj7YW)aPt(Z{Eky(OC}L6*6=s)2Q~0(t=7w%>6rYznt#i$mQ4QgYKi29 zSBoZpa`mp{`B!sl>3qIGLk^*JldSbi^i2>wyvkA@7h%+-w?%L>6J0&S&;Klb_>SGq zqqR|satOISPtUrt?zU{M345%=-&95KLlELYt$dfbBg-@XKEU!s#QLH7Q}h6j1i zIn=oENPka&$kDglJPN#dJ=~4FDfV`qkQ&fkFJ%^L%#W6a#SFE)LHhc*$K61qv z{T_nnFR<2bw7V@iH0H%>l1X_zDWw(D3bUSqzAIp4w~%X#Lc{x@-UH-bgEcf`gB{@8 z)AV8x+ZzkdKjx>u6;17g^0Bwh^$7%A&3a?>GFEQHysfW{0(@<3ViM z_(^!NJadtlFRI;rDie<}^LgIIe(e41d}E}az@<;kE&0s;wTtBN=Jl}X;v`iVe%)m} z;!YG5@arNX;*z4q@*?4ysCyWKKTe8$;Om=kb~3DveMCc__R-#x_?8+TWtZ8;?n`Cx zzJ`dnspBL5?o1wbeGq|^f>Fi2lPx47$*0D zEZPW{zowa=kl`fOsmh9{En*{I>fvJEa5*Yg(ze*`Y#r3FcZW|SpjAIHaW}NLCH007 zEan7NA*;$bRN1epkym~6w;;2QG^D49`xUr0oV~_e^M&NNhSvVWNAugh zi+2f6y0#6O)}r64hI;a;M0R+Bj>TN8@AQA33^MHObuDTD#Tv9`jzZQxKB?Rjkw4!-O7fi|C>EZ5fgywmZ3TsCG*UUZ)u z*l1?9c^g_|zqsPKQV#A^;+<;C#~X>GTHwzkbhm@Nt`m*uY)q;%*+1@$aqJ`bXkwhH zBL2miiAs1`IpNicvWa}Y%jU1NY#{| NTR+0yUivWJ|aw$Ntr(T|B(N$o8(t=G>@ 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

    ^CU`K@OnCMHC?{+@;%~-1yEHKae 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

    }iEJVG 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π1501mQl9dY#zwh*u8apYCp&+ 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 o?!O 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

    MkwDLyM@qr;W>WPjC%z*LO{Dof1onkA(I7 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≀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

    E9Zc|5&kZ@V1z`c!C^dfcuP}={u0>JV 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 OhFYYnkV-6{?|%bR){`ax diff --git a/data_files/audio/Positive.wav b/data_files/audio/Positive.wav deleted file mode 100644 index 0ebee8ffe0b9bff0f40b37ac6d9dc7e513ce5e08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362968 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�USoraKTEBUf=#-El{Lu6iUR6ojyePgzp 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%0AscJSDU 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ⓈPa&+y%bcecM~rPW$=w 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

    {WJ1vi5IQ#Bx8-#2Wy~}yj>l4s9nsg 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μByZ_4iEFZ5L!K0+@?!?v7punZJ=J91Qz(eY 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%0A62qRM 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 diff --git a/data_files/audio/Rhodes.wav b/data_files/audio/Rhodes.wav deleted file mode 100644 index 5da03fe63c04bcc29f6740d86f148317f30ece87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208092 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 b%s|uV5D}w&9kI=pG+xxsq96^I$LwCw&dG z@)g$h7}cKi8mG83<4|8~%WO|wh?kam?>h3jRa>$qvN9UiQJS$29hJ+cgLU9_s@_`)~P$`r#rRib~@@dSp3G5Z&H4u4OeUNC1fJ;^#O%! zY~tZ$)#65P->j|W^1EJLU+GGpe+4O-od z^cvz*HN~fDvJiCH#4Tb$kC36BME3?S*Qpe0r$c&webWvjHEzrLU`e)_j0Q5hXF1iB$ z+yBJO?GiK3D_H%r-(Hh@>k9J++w>{j`(wj4{DI>$tzzfwsvpnM0eC(I3s(MA;6Nh~gl^#{!2T|uCW$44+2g8m

    )FC)=(F(7W$1rBPPz%d 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 diff --git a/data_files/audio/ringtone.wav b/data_files/audio/ringtone.wav deleted file mode 100644 index 6f5eca99573d561b55aab0cef2a4e3b280dda23b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44669 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 Date: Thu, 25 Mar 2021 11:59:14 +0000 Subject: [PATCH 061/175] mmnode-blocks-info: minor fixes and cleanups --- mmgen/node_tools/BlocksInfo.py | 2 +- mmnode-blocks-info | 10 +++++----- test/unit_tests_d/nt_BlocksInfo.py | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 55e2b7a..c25d1a2 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -242,7 +242,7 @@ class BlocksInfo: yield (ls + self.fields[name].fs.replace(':',repl) + rs) def conv_blkspec(self,arg): - if arg == 'cur': + if str(arg).lower() == 'cur': return self.tip elif is_int(arg): if int(arg) < 0: diff --git a/mmnode-blocks-info b/mmnode-blocks-info index e7a79fa..2a8c9c6 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -34,9 +34,9 @@ opts_data = { ], 'text': { 'desc': 'Display information about a block or range of blocks', - 'usage': '[opts] blocknum [blocknum ...] | blocknum-blocknum[+step] | [blocknum|-nBlocks]+nBlocks[+step]', + 'usage': '[opts] blocknum ... | blocknum-blocknum[+step] | [blocknum|-nBlocks]+nBlocks[+step]', 'usage2': [ - '[opts] blocknum [blocknum ...]', + '[opts] blocknum ...', '[opts] blocknum-blocknum[+step]', '[opts] [blocknum|-nBlocks]+nBlocks[+step]', ], @@ -116,14 +116,14 @@ EXAMPLES: Display only range stats for the last ten blocks: $ {p} -o none -s range +10 - Display data for the last ten blocks, skipping 'size' and 'subsidy' - fields and stats: + Display data for the last ten blocks, omitting the 'size' and 'subsidy' + fields from the defaults and skipping stats: $ {p} -o -size,subsidy -s none +10 Display all fields and stats for the last ten blocks: $ {p} -o all -s all +10 - Same as above, but display only relevant fields: + Same as above, but display only fields relating to stats: $ {p} -o none -s all -f +10 Same as above, but display stats only: diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index ea10655..96b7299 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -9,7 +9,7 @@ from mmgen.exception import * from mmgen.node_tools.BlocksInfo import BlocksInfo tip = 50000 -vecs = ( +range_vecs = ( # First Last FromTip nBlocks Step First Last BlockList ( (), (), (tip, tip, None) ), ( (199,2,37), (), (None, None, [199,2,37]) ), @@ -36,8 +36,8 @@ vecs = ( ( '-10+11', (tip-10, tip, 10, 11, None), (tip-10, tip, None) ), ( '-10+11+2', (tip-10, tip, 10, 11, 2 ), (tip-10, tip, list(range(tip-10,tip+1,2))) ), - ( 'cur', (tip, tip, None, None, None), (tip, tip, None) ), - ( 'cur-cur', (tip, tip, None, None, None), (tip, tip, None) ), + ( 'cUr', (tip, tip, None, None, None), (tip, tip, None) ), + ( 'cur-cUR', (tip, tip, None, None, None), (tip, tip, None) ), ( '0-cur', (0, tip, None, None, None), (0, tip, None) ), (f'{tip-1}-cur', (tip-1, tip, None, None, None), (tip-1, tip, None) ), ( '0-cur+3000', (0, tip, None, None, 3000 ), (0, tip, list(range(0,tip+1,3000))) ), @@ -57,7 +57,7 @@ class dummyOpt: class unit_tests: - def rangespec(self,name,ut): + def parse_rangespec(self,name,ut): b = BlocksInfo(0,dummyOpt(),dummyRPC()) @@ -66,7 +66,7 @@ class unit_tests: vmsg(f'{spec:13} => {BlocksInfo.range_data(*chk)}') assert ret == chk, f'{ret} != {chk}' - for vec in vecs: + for vec in range_vecs: if vec[1]: test(*vec) @@ -80,7 +80,7 @@ class unit_tests: vmsg('{:13} => {}'.format(repr(spec) if type(spec) == tuple else spec,chk)) assert ret == chk, f'{ret} != {chk}' - for vec in vecs: + for vec in range_vecs: test(*vec) return True From ae03007bbad308ec01b127fbfe2a50de894de1a7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 25 Mar 2021 11:59:14 +0000 Subject: [PATCH 062/175] mmnode-blocks-info: --fields: support +-, 'all'- --- mmgen/node_tools/BlocksInfo.py | 46 +++++++++++++++++++----------- mmnode-blocks-info | 12 +++++++- test/unit_tests_d/nt_BlocksInfo.py | 28 ++++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index c25d1a2..4521741 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -124,28 +124,42 @@ class BlocksInfo: t_fmt = lambda self,t: f'{t/86400:.2f} days' if t > 172800 else f'{t/3600:.2f} hrs' - def __init__(self,cmd_args,opt,rpc): + @classmethod + def parse_cslist(cls,uarg,full_set,dfl_set,desc): - def parse_cslist(uarg,full_set,dfl_set,desc): - m = re.match('([+-]){1}',uarg) - pfx = m[1] if m else None - usr_set = set((uarg[1:] if m else uarg).split(',')) - dfl_set = set(dfl_set) - for e in usr_set: - if e not in full_set: - die(1,f'{e!r}: unrecognized {desc}') - res = ( - dfl_set | usr_set if pfx == '+' else - dfl_set - usr_set if pfx == '-' else - usr_set - ) + def make_list(m,func): + groups_lc = [set(e.lower() for e in g.split(',')) for g in m.groups()] + for group in groups_lc: + for e in group: + if e not in full_set_lc: + die(1,f'{e!r}: unrecognized {desc}') # display elements in order: - return [e for e in full_set if e in res] + return [e for e in full_set if e.lower() in func(groups_lc)] + + full_set_lc = set(e.lower() for e in full_set) + dfl_set_lc = set(e.lower() for e in dfl_set) + cspat = r'(\w+(?:,\w+)*)' + + for pat,func in ( + ( rf'{cspat}$', lambda g: g[0] ), + ( rf'\+{cspat}$', lambda g: dfl_set_lc | g[0] ), + ( rf'\-{cspat}$', lambda g: dfl_set_lc - g[0] ), + ( rf'\+{cspat}\-{cspat}$', lambda g: ( dfl_set_lc | g[0] ) - g[1] ), + ( rf'\-{cspat}\+{cspat}$', lambda g: ( dfl_set_lc - g[0] ) | g[1] ), + ( rf'all\-{cspat}$', lambda g: full_set_lc - g[0] ) + ): + m = re.match(pat,uarg,re.ASCII|re.IGNORECASE) + if m: + return make_list(m,func) + else: + die(1,f'{uarg}: invalid parameter') + + def __init__(self,cmd_args,opt,rpc): def parse_cs_uarg(uarg,full_set,dfl_set,desc): return ( full_set if uarg == 'all' else [] if uarg == 'none' else - parse_cslist(uarg,full_set,dfl_set,desc) + self.parse_cslist(uarg,full_set,dfl_set,desc) ) def get_fields(): diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 2a8c9c6..62e1a59 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -57,7 +57,10 @@ opts_data = { See AVAILABLE FIELDS below. Prefix the list with '+' to add the fields to the defaults, or '-' to remove them. The special values 'all' and 'none' select all - available fields or none, respectively. + available fields or none, respectively. The '+' and + '-'-prefixed lists may be concatenated to specify both + addition and removal of fields. A single '-'-prefixed + list may be additionally prefixed by 'all'. -s, --stats= Display the specified stats (comma-separated list). See AVAILABLE STATS below. The prefixes and special values available to the --fields option are recognized. @@ -120,9 +123,16 @@ EXAMPLES: fields from the defaults and skipping stats: $ {p} -o -size,subsidy -s none +10 + Display data for the last ten blocks, omitting the 'size' and 'version' + fields from the defaults and adding the 'inputs' and 'utxo_inc' fields: + $ {p} -o -version,size+utxo_inc,inputs +10 + Display all fields and stats for the last ten blocks: $ {p} -o all -s all +10 + Same as above, but omit the 'miner' and 'hash' fields: + $ {p} -o all-miner,hash -s all +10 + Same as above, but display only fields relating to stats: $ {p} -o none -s all -f +10 diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index 96b7299..307f6e8 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -45,6 +45,24 @@ range_vecs = ( ( '+144*10+12*12', (tip-1439, tip, None, 1440, 144 ), (tip-1439, tip, list(range(tip-1439,tip+1,144))) ), ) +full_set = ['aa','bbb','ccc_P2','ddddd','eeeeee','ffffffff','gg'] +dfl_set = ['aa','ddddd','ffffffff'] +fields_vecs = ( + ( 'Ccc_P2', ['ccc_P2'] ), + ( '+CCC_P2', ['aa','ccc_P2','ddddd','ffffffff'] ), + ( '+Aa', dfl_set ), + ( '+ddDDD,FffffffF', dfl_set ), + ( '+bBb', ['aa','bbb','ddddd','ffffffff'] ), + ( '-ddddd', ['aa','ffffffff'] ), + ( '-DDDDD,fFffffff', ['aa'] ), + ( '-ffffffff,AA,DdDdD', [] ), + ( '+aa,gG,ccC_P2', ['aa','ccc_P2','ddddd','ffffffff','gg'] ), + ( '+BBB,gg-dDddD,fFffffff', ['aa','bbb','gg'] ), + ( '-dDddD,fFffffff+BBB,gg', ['aa','bbb','gg'] ), + ( 'aLL-Ccc_P2', [e for e in full_set if e != 'ccc_P2'] ), + ( 'All-dDddd,aa', [e for e in full_set if e not in ('aa','ddddd')] ), +) + class dummyRPC: blockcount = tip @@ -57,6 +75,16 @@ class dummyOpt: class unit_tests: + def parse_field(self,name,ut): + vmsg('{:28} => {}'.format('FULL SET:',full_set)) + vmsg('{:28} => {}'.format('DFL SET: ',dfl_set)) + b = BlocksInfo + for opt,chk in fields_vecs: + ret = b.parse_cslist(opt,full_set,dfl_set,'field') + vmsg(f'{opt:28} => {ret}') + assert ret == chk, f'{ret} != {chk}' + return True + def parse_rangespec(self,name,ut): b = BlocksInfo(0,dummyOpt(),dummyRPC()) From 463431e586a4e3f900a4abec850ccf6e1cfda063 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 21 Jun 2021 16:37:07 +0000 Subject: [PATCH 063/175] mmnode-blocks-info: fix difficulty adj at start of period --- mmgen/node_tools/BlocksInfo.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index 4521741..8e12861 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -504,18 +504,19 @@ class BlocksInfo: await c.call('getblockheader',await c.call('getblockhash',self.tip)) ) - bdi_avg_blks = 432 # ≈3 days - bdi_avg_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-bdi_avg_blks)) - bdi_avg = ( tip_hdr['time'] - bdi_avg_hdr['time'] ) / bdi_avg_blks + min_sample_blks = 432 # ≈3 days + rel_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-rel)) - if rel > bdi_avg_blks: - rel_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-rel)) + if rel >= min_sample_blks: + sample_blks = rel bdi = ( tip_hdr['time'] - rel_hdr['time'] ) / rel - bdi_disp = bdi else: - bdi_adj = float(tip_hdr['difficulty'] / bdi_avg_hdr['difficulty']) - bdi = bdi_avg * ( (bdi_adj * (bdi_avg_blks-rel)) + rel ) / bdi_avg_blks - bdi_disp = bdi_avg + sample_blks = min_sample_blks + start_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-sample_blks)) + diff_adj = float(tip_hdr['difficulty'] / start_hdr['difficulty']) + time1 = rel_hdr['time'] - start_hdr['time'] + time2 = tip_hdr['time'] - rel_hdr['time'] + bdi = ((time1 * diff_adj) + time2) / sample_blks rem = 2016 - rel @@ -526,13 +527,13 @@ class BlocksInfo: { 'next_diff_adjust': ('{}', self.tip + rem), 'blks_remaining': ('{}', rem), - 'time_remaining': (self.t_fmt, rem * bdi_avg) + 'time_remaining': (self.t_fmt, rem * bdi) } ), - ('Avg BDI: {avg_bdi} min (over {avg_bdi_blks}-block period)', + ('Avg BDI: {avg_bdi} min (over {sample_blks}-block period)', { - 'avg_bdi': ('{:.2f}', bdi_disp/60), - 'avg_bdi_blks': ('{}', max(rel,bdi_avg_blks)) + 'avg_bdi': ('{:.2f}', bdi/60), + 'sample_blks': ('{}', sample_blks) } ), ('Cur difficulty: {}', 'cur_diff', '{:.2e}', tip_hdr['difficulty']), From 9090df312f6348d45a3fcd1807f8a7611ba95248 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 13 Oct 2021 19:50:58 +0000 Subject: [PATCH 064/175] minor fixes --- mmgen/node_tools/Global.py | 26 -------------------------- mmnode-halving-calculator | 4 ++-- test/test-release.sh | 3 +++ test/unit_tests_d/nt_BlocksInfo.py | 4 +++- 4 files changed, 8 insertions(+), 29 deletions(-) delete mode 100644 mmgen/node_tools/Global.py diff --git a/mmgen/node_tools/Global.py b/mmgen/node_tools/Global.py deleted file mode 100644 index 4384096..0000000 --- a/mmgen/node_tools/Global.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2016 Philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -mmgen.node_tools.Global: global variables for MMGen node tools -""" - -import os - -class nt(object): - system_data_dir = '/usr/local/share/mmgen/node_tools' - data_dir = os.getenv('HOME') + '/.mmgen/node_tools' diff --git a/mmnode-halving-calculator b/mmnode-halving-calculator index 3409c5a..7ddf0a1 100755 --- a/mmnode-halving-calculator +++ b/mmnode-halving-calculator @@ -83,7 +83,7 @@ async def main(): if proto.name == 'BitcoinCash': sub = proto.coin_amt(str(cur['subsidy'])) else: - sub = cur['subsidy'] * proto.coin_amt.min_coin_unit + sub = cur['subsidy'] * proto.coin_amt.satoshi def print_current_stats(): print( @@ -104,7 +104,7 @@ async def main(): hist_halvings = await c.gathered_call('getblockstats',([(n,) for n in halving_blocknums if n <= tip])) halving_secs = bdr_proj * 60 * proto.halving_interval nhist = len(hist_halvings) - nSubsidy = int(proto.start_subsidy / proto.coin_amt.min_coin_unit) + nSubsidy = int(proto.start_subsidy / proto.coin_amt.satoshi) block0_hash = await c.call('getblockhash',0) block0_date = (await c.call('getblock',block0_hash))['time'] diff --git a/test/test-release.sh b/test/test-release.sh index 4b2df03..45c3ee7 100755 --- a/test/test-release.sh +++ b/test/test-release.sh @@ -3,6 +3,8 @@ export MMGEN_TEST_SUITE=1 export PYTHONPATH=. +orig_pwd=$(pwd) + RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" MAGENTA="\e[35;1m" CYAN="\e[36;1m" RESET="\e[0m" @@ -46,6 +48,7 @@ check_mmgen_repo() { create_links() { ( cd 'mmgen'; [ -L 'node_tools' ] || ln -s "../$nt_repo/mmgen/node_tools" ) + ( cd $mm_repo && [ -L 'mmgen_node_tools' ] || ln -s "$orig_pwd/mmgen_node_tools" ) ( cd 'test/unit_tests_d' for fn in ../../$nt_repo/test/unit_tests_d/nt_*.py; do diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index 307f6e8..da411f4 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -105,7 +105,9 @@ class unit_tests: def test(spec,foo,chk): b = BlocksInfo(spec if type(spec) == tuple else [spec],dummyOpt(),dummyRPC()) ret = (b.first,b.last,b.block_list) - vmsg('{:13} => {}'.format(repr(spec) if type(spec) == tuple else spec,chk)) + vmsg('{:13} => {}'.format( + (repr(spec) if type(spec) == tuple else spec), + chk )) assert ret == chk, f'{ret} != {chk}' for vec in range_vecs: From 8faeb17a68891dd862aca568835e788c985441d6 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 13 Oct 2021 19:52:38 +0000 Subject: [PATCH 065/175] migrate to setuptools --- MANIFEST.in | 1 + README.md | 3 +- .../BlocksInfo.py | 2 +- .../node_tools => mmgen_node_tools}/Sound.py | 4 +- .../node_tools => mmgen_node_tools}/Term.py | 2 +- .../node_tools => mmgen_node_tools}/Util.py | 2 +- .../__init__.py | 0 mmgen_node_tools/data/keywords | 1 + mmnode-blocks-info | 2 +- pyproject.toml | 6 +++ setup.cfg | 36 +++++++++++++ setup.py | 50 ------------------- test/unit_tests_d/nt_BlocksInfo.py | 2 +- 13 files changed, 53 insertions(+), 58 deletions(-) rename {mmgen/node_tools => mmgen_node_tools}/BlocksInfo.py (99%) rename {mmgen/node_tools => mmgen_node_tools}/Sound.py (96%) rename {mmgen/node_tools => mmgen_node_tools}/Term.py (95%) rename {mmgen/node_tools => mmgen_node_tools}/Util.py (98%) rename {mmgen/node_tools => mmgen_node_tools}/__init__.py (100%) create mode 100644 mmgen_node_tools/data/keywords create mode 100644 pyproject.toml create mode 100644 setup.cfg delete mode 100755 setup.py diff --git a/MANIFEST.in b/MANIFEST.in index 22b60bd..7bdab90 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include README.md LICENSE include test/test-release.sh include test/unit_tests_d/*.py +include mmgen_node_tools/data/* diff --git a/README.md b/README.md index 2fab8f7..904c4d4 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Then, $ git clone https://github.com/mmgen/mmgen-node-tools $ cd mmgen-node-tools - $ sudo ./setup.py install + $ python3 -m build --no-isolation + $ python3 -m pip install --user dist/*.whl - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py similarity index 99% rename from mmgen/node_tools/BlocksInfo.py rename to mmgen_node_tools/BlocksInfo.py index 8e12861..3dde787 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -17,7 +17,7 @@ # this program. If not, see . """ -mmgen.node_tools.BlocksInfo: Display information about a block or range of blocks +mmgen_node_tools.BlocksInfo: Display information about a block or range of blocks """ import re,json diff --git a/mmgen/node_tools/Sound.py b/mmgen_node_tools/Sound.py similarity index 96% rename from mmgen/node_tools/Sound.py rename to mmgen_node_tools/Sound.py index ba59a24..271253c 100644 --- a/mmgen/node_tools/Sound.py +++ b/mmgen_node_tools/Sound.py @@ -16,11 +16,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -mmgen.node_tools.Sound: audio-related functions for MMGen node tools +mmgen_node_tools.Sound: audio-related functions for MMGen node tools """ import sys,os,time -from mmgen.node_tools.Util import * +from mmgen_node_tools.Util import * _alsa_config_file = '/tmp/alsa-config-' + os.path.basename(sys.argv[0]) _dvols = { 'Master': 78, 'Speaker': 78, 'Headphone': 15, 'PCM': 190 } diff --git a/mmgen/node_tools/Term.py b/mmgen_node_tools/Term.py similarity index 95% rename from mmgen/node_tools/Term.py rename to mmgen_node_tools/Term.py index 95111a0..7b5b7e0 100644 --- a/mmgen/node_tools/Term.py +++ b/mmgen_node_tools/Term.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -mmgen.node_tools.Term: terminal routines for MMGen node tools +mmgen_node_tools.Term: terminal routines for MMGen node tools """ import sys,os,termios diff --git a/mmgen/node_tools/Util.py b/mmgen_node_tools/Util.py similarity index 98% rename from mmgen/node_tools/Util.py rename to mmgen_node_tools/Util.py index fb7e318..2a67fe2 100644 --- a/mmgen/node_tools/Util.py +++ b/mmgen_node_tools/Util.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -mmgen.node_tools.Util: utility functions for MMGen node tools +mmgen_node_tools.Util: utility functions for MMGen node tools """ import time,subprocess diff --git a/mmgen/node_tools/__init__.py b/mmgen_node_tools/__init__.py similarity index 100% rename from mmgen/node_tools/__init__.py rename to mmgen_node_tools/__init__.py diff --git a/mmgen_node_tools/data/keywords b/mmgen_node_tools/data/keywords new file mode 100644 index 0000000..3c3c563 --- /dev/null +++ b/mmgen_node_tools/data/keywords @@ -0,0 +1 @@ +Bitcoin, BTC, Ethereum, ETH, Monero, XMR, ERC20, cryptocurrency, wallet, BIP32, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MSYS2, MinGW, MinGW64, MSWin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, DASH, Dashpay, SHA256Compress, monerod, EMC, Emercoin, token, deploy, contract, gas, fee, smart contract, solidity, Parity, OpenEthereum, testnet, devmode, Kovan diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 62e1a59..fc43706 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -21,7 +21,7 @@ mmnode-blocks-info: Display information about a block or range of blocks """ from mmgen.common import * -from mmgen.node_tools.BlocksInfo import BlocksInfo,JSONBlocksInfo +from mmgen_node_tools.BlocksInfo import BlocksInfo,JSONBlocksInfo opts_data = { 'sets': [ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..374b58c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d11eae6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,36 @@ +[metadata] +name = MMGen Node Tools +version = 0.0.3 +description = Optional online tools for the MMGen wallet suite +long_description = file: README.md +long_description_content_type = text/markdown +author = The MMGen Project +author_email = mmgen@tuta.io +url = https://github.com/mmgen/mmgen-node-tools +license = GNU GPL v3 +platforms = Linux, Armbian, Raspbian, MS Windows +keywords = file: mmgen_node_tools/data/keywords +project_urls = + Bug Tracker = https://github.com/mmgen/mmgen-node-tools/issues +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: GNU General Public License v3 (GPLv3) + Operating System :: POSIX :: Linux + Operating System :: Microsoft :: Windows + +[options] +python_requires = >=3.7 +include_package_data = True + +install_requires = + mmgen>=0.12.2 + +packages = + mmgen_node_tools + +scripts = + mmnode-blocks-info + mmnode-feeview + mmnode-halving-calculator + mmnode-netrate + mmnode-peerblocks diff --git a/setup.py b/setup.py deleted file mode 100755 index 4d12521..0000000 --- a/setup.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2016 Philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -from distutils.core import setup -from mmgen.globalvars import g - -os.umask(0o0022) - -setup( - name = 'mmgen-node-tools', - description = 'Optional tools for the MMGen wallet system', - version = g.version, - author = g.author, - author_email = g.email, - url = g.proj_url, - license = 'GNU GPL v3', - platforms = ('Linux, Armbian, Raspbian, MS Windows'), - keywords = g.keywords, - packages = ['mmgen.node_tools'], - scripts = [ - 'mmnode-blocks-info', - 'mmnode-feeview', - 'mmnode-halving-calculator', - 'mmnode-netrate', - 'mmnode-peerblocks', - ], -# data_files = [('share/mmgen/node_tools/audio', [ -# 'data_files/audio/ringtone.wav', # source files must have 0644 mode -# 'data_files/audio/Positive.wav', -# 'data_files/audio/Rhodes.wav', -# 'data_files/audio/Counterpoint.wav' -# ]) -# ], - ) diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index da411f4..1448e97 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -6,7 +6,7 @@ test.unit_tests_d.nt_BlocksInfo: from mmgen.common import * from mmgen.exception import * -from mmgen.node_tools.BlocksInfo import BlocksInfo +from mmgen_node_tools.BlocksInfo import BlocksInfo tip = 50000 range_vecs = ( From 7a9ccf5e2da3829c9b7f67a7f09f115efa68f351 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 14 Oct 2021 09:47:31 +0000 Subject: [PATCH 066/175] mmnode-feeview: fix --- mmnode-feeview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmnode-feeview b/mmnode-feeview index 305094a..c91fc34 100755 --- a/mmnode-feeview +++ b/mmnode-feeview @@ -117,7 +117,7 @@ def create_data(coin_amt,mempool): # populate fee brackets: for tx in mempool.values(): - fee = coin_amt(tx['fee']).toSatoshi() + fee = coin_amt(tx['fee']).to_unit('satoshi') vsize = tx['vsize'] for bracket in out: if fee / vsize < bracket.top: From bc9e9a07d3225cbbed440423a833efb6554375a7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 14 Oct 2021 13:29:29 +0000 Subject: [PATCH 067/175] Version 3.0.0 --- mmgen_node_tools/data/version | 1 + setup.cfg | 6 +++--- test/test-release.sh | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 mmgen_node_tools/data/version diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version new file mode 100644 index 0000000..4a36342 --- /dev/null +++ b/mmgen_node_tools/data/version @@ -0,0 +1 @@ +3.0.0 diff --git a/setup.cfg b/setup.cfg index d11eae6..eeafdef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = MMGen Node Tools -version = 0.0.3 +version = file: mmgen_node_tools/data/version description = Optional online tools for the MMGen wallet suite long_description = file: README.md long_description_content_type = text/markdown @@ -12,7 +12,7 @@ platforms = Linux, Armbian, Raspbian, MS Windows keywords = file: mmgen_node_tools/data/keywords project_urls = Bug Tracker = https://github.com/mmgen/mmgen-node-tools/issues -classifiers = +classifiers = Programming Language :: Python :: 3 License :: OSI Approved :: GNU General Public License v3 (GPLv3) Operating System :: POSIX :: Linux @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=0.12.2 + mmgen>=13.0.0 packages = mmgen_node_tools diff --git a/test/test-release.sh b/test/test-release.sh index 45c3ee7..1a4531a 100755 --- a/test/test-release.sh +++ b/test/test-release.sh @@ -47,7 +47,6 @@ check_mmgen_repo() { } create_links() { - ( cd 'mmgen'; [ -L 'node_tools' ] || ln -s "../$nt_repo/mmgen/node_tools" ) ( cd $mm_repo && [ -L 'mmgen_node_tools' ] || ln -s "$orig_pwd/mmgen_node_tools" ) ( cd 'test/unit_tests_d' From 3d62ff8211215127a57e7cfd1842dca225115b6c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 11 Feb 2022 09:48:08 +0000 Subject: [PATCH 068/175] use mmgen launcher; init fixes; blocks-info: improve miner string parsing --- README.md | 2 ++ cmds/mmnode-blocks-info | 17 +++++++++++++++++ cmds/mmnode-feeview | 17 +++++++++++++++++ cmds/mmnode-halving-calculator | 17 +++++++++++++++++ cmds/mmnode-netrate | 17 +++++++++++++++++ cmds/mmnode-peerblocks | 17 +++++++++++++++++ cmds/mmnode-txfind | 17 +++++++++++++++++ mmgen_node_tools/BlocksInfo.py | 13 +++++++++++-- mmgen_node_tools/Sound.py | 0 mmgen_node_tools/Term.py | 0 mmgen_node_tools/Util.py | 0 mmgen_node_tools/__init__.py | 0 mmgen_node_tools/data/version | 2 +- .../main_blocks_info.py | 2 +- .../main_feeview.py | 8 ++++++-- .../main_halving_calculator.py | 2 +- .../main_netrate.py | 0 .../main_peerblocks.py | 0 .../main_txfind.py | 0 setup.cfg | 12 ++++++------ 20 files changed, 130 insertions(+), 13 deletions(-) create mode 100755 cmds/mmnode-blocks-info create mode 100755 cmds/mmnode-feeview create mode 100755 cmds/mmnode-halving-calculator create mode 100755 cmds/mmnode-netrate create mode 100755 cmds/mmnode-peerblocks create mode 100755 cmds/mmnode-txfind mode change 100644 => 100755 mmgen_node_tools/BlocksInfo.py mode change 100644 => 100755 mmgen_node_tools/Sound.py mode change 100644 => 100755 mmgen_node_tools/Term.py mode change 100644 => 100755 mmgen_node_tools/Util.py mode change 100644 => 100755 mmgen_node_tools/__init__.py rename mmnode-blocks-info => mmgen_node_tools/main_blocks_info.py (98%) rename mmnode-feeview => mmgen_node_tools/main_feeview.py (98%) rename mmnode-halving-calculator => mmgen_node_tools/main_halving_calculator.py (99%) rename mmnode-netrate => mmgen_node_tools/main_netrate.py (100%) rename mmnode-peerblocks => mmgen_node_tools/main_peerblocks.py (100%) rename mmnode-txfind => mmgen_node_tools/main_txfind.py (100%) diff --git a/README.md b/README.md index 904c4d4..e8ac441 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Then, $ python3 -m build --no-isolation $ python3 -m pip install --user dist/*.whl +Also make sure that `~/.local/bin` is in `PATH`. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [**Forum**][4] | diff --git a/cmds/mmnode-blocks-info b/cmds/mmnode-blocks-info new file mode 100755 index 0000000..a1c855e --- /dev/null +++ b/cmds/mmnode-blocks-info @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-blocks-info: Display information about a block or range of blocks +""" + +from mmgen.main import launch + +launch('blocks_info',package='mmgen_node_tools') diff --git a/cmds/mmnode-feeview b/cmds/mmnode-feeview new file mode 100755 index 0000000..5a4625e --- /dev/null +++ b/cmds/mmnode-feeview @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-feeview: Visualize the fee structure of a node’s mempool +""" + +from mmgen.main import launch + +launch('feeview',package='mmgen_node_tools') diff --git a/cmds/mmnode-halving-calculator b/cmds/mmnode-halving-calculator new file mode 100755 index 0000000..ebf1f6d --- /dev/null +++ b/cmds/mmnode-halving-calculator @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-halving-calculator: Estimate date(s) of future block subsidy halving(s) +""" + +from mmgen.main import launch + +launch('halving_calculator',package='mmgen_node_tools') diff --git a/cmds/mmnode-netrate b/cmds/mmnode-netrate new file mode 100755 index 0000000..cfefab2 --- /dev/null +++ b/cmds/mmnode-netrate @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-netrate: Bitcoin daemon network rate monitor +""" + +from mmgen.main import launch + +launch('netrate',package='mmgen_node_tools') diff --git a/cmds/mmnode-peerblocks b/cmds/mmnode-peerblocks new file mode 100755 index 0000000..be4bb41 --- /dev/null +++ b/cmds/mmnode-peerblocks @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-peerblocks: List blocks in flight, disconnect stalling nodes +""" + +from mmgen.main import launch + +launch('peerblocks',package='mmgen_node_tools') diff --git a/cmds/mmnode-txfind b/cmds/mmnode-txfind new file mode 100755 index 0000000..05213a8 --- /dev/null +++ b/cmds/mmnode-txfind @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-txfind: Find a transaction in the blockchain or mempool +""" + +from mmgen.main import launch + +launch('txfind',package='mmgen_node_tools') diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py old mode 100644 new mode 100755 index 3dde787..28395b4 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -226,15 +226,18 @@ class BlocksInfo: ) if 'miner' in self.fnames: + # capturing parens must contain only ASCII chars! self.miner_pats = [re.compile(pat) for pat in ( rb'`/([_a-zA-Z0-9&. #/-]+)/', - rb'[\xe3\xe4\xe5][\^/](.*?)\xfa', + rb'[\xe3\xe4\xe5][\^/]([\x20-\x7e]+?)\xfa', rb'([a-zA-Z0-9&. -]+/Mined by [a-zA-Z0-9. ]+)', rb'\x08/(.*Mined by [a-zA-Z0-9. ]+)', rb'Mined by ([a-zA-Z0-9. ]+)', rb'[`]([_a-zA-Z0-9&. #/-]+)[/\xfa]', + rb'([\x20-\x7e]{9,})', rb'[/^]([a-zA-Z0-9&. #/-]{5,})', rb'[/^]([_a-zA-Z0-9&. #/-]+)/', + rb'^\x03...\W{0,5}([\\_a-zA-Z0-9&. #/-]+)[/\\]', )] self.block_data = namedtuple('block_data',self.fnames) @@ -428,10 +431,16 @@ class BlocksInfo: if self.opt.raw_miner_info: return repr(cb) else: + trmap_in = { + '\\': ' ', + '/': ' ', + ',': ' ', + } + trmap = { ord(a):b for a,b in trmap_in.items() } for pat in self.miner_pats: m = pat.search(cb) if m: - return ''.join(chr(b) for b in m[1] if 31 < b < 127).strip('^').strip('/').replace('/',' ') + return re.sub( r'\s+', ' ', m[1].decode().strip('^').translate(trmap).strip() ) return '' def print_header(self): diff --git a/mmgen_node_tools/Sound.py b/mmgen_node_tools/Sound.py old mode 100644 new mode 100755 diff --git a/mmgen_node_tools/Term.py b/mmgen_node_tools/Term.py old mode 100644 new mode 100755 diff --git a/mmgen_node_tools/Util.py b/mmgen_node_tools/Util.py old mode 100644 new mode 100755 diff --git a/mmgen_node_tools/__init__.py b/mmgen_node_tools/__init__.py old mode 100644 new mode 100755 diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 4a36342..88a447a 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.0.0 +3.1.dev1 diff --git a/mmnode-blocks-info b/mmgen_node_tools/main_blocks_info.py similarity index 98% rename from mmnode-blocks-info rename to mmgen_node_tools/main_blocks_info.py index fc43706..a7dff8d 100755 --- a/mmnode-blocks-info +++ b/mmgen_node_tools/main_blocks_info.py @@ -21,7 +21,7 @@ mmnode-blocks-info: Display information about a block or range of blocks """ from mmgen.common import * -from mmgen_node_tools.BlocksInfo import BlocksInfo,JSONBlocksInfo +from .BlocksInfo import BlocksInfo,JSONBlocksInfo opts_data = { 'sets': [ diff --git a/mmnode-feeview b/mmgen_node_tools/main_feeview.py similarity index 98% rename from mmnode-feeview rename to mmgen_node_tools/main_feeview.py index c91fc34..8979e44 100755 --- a/mmnode-feeview +++ b/mmgen_node_tools/main_feeview.py @@ -125,9 +125,12 @@ def create_data(coin_amt,mempool): break # remove empty top brackets: - while out[-1].tx_bytes == 0: + while out and out[-1].tx_bytes == 0: out.pop() + if not out: + die(1,'No data!') + out.reverse() # cumulative totals and display are top-down # calculate cumulative byte totals, filter rows: @@ -197,7 +200,7 @@ def gen_body(data): async def main(): from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts() + proto = init_proto_from_opts(need_amt=True) from mmgen.rpc import rpc_init c = await rpc_init(proto) @@ -210,6 +213,7 @@ async def main(): log(mempool,'mempool.json') data = create_data(proto.coin_amt,mempool) + (do_pager if opt.pager else print)( '\n'.join(gen_header(c.host,await c.call('getblockcount'))) + '\n' + '\n'.join(gen_body(data)) diff --git a/mmnode-halving-calculator b/mmgen_node_tools/main_halving_calculator.py similarity index 99% rename from mmnode-halving-calculator rename to mmgen_node_tools/main_halving_calculator.py index 7ddf0a1..423df0e 100755 --- a/mmnode-halving-calculator +++ b/mmgen_node_tools/main_halving_calculator.py @@ -62,7 +62,7 @@ def time_diff_warning(t_diff): async def main(): from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts() + proto = init_proto_from_opts(need_amt=True) from mmgen.rpc import rpc_init c = await rpc_init(proto) diff --git a/mmnode-netrate b/mmgen_node_tools/main_netrate.py similarity index 100% rename from mmnode-netrate rename to mmgen_node_tools/main_netrate.py diff --git a/mmnode-peerblocks b/mmgen_node_tools/main_peerblocks.py similarity index 100% rename from mmnode-peerblocks rename to mmgen_node_tools/main_peerblocks.py diff --git a/mmnode-txfind b/mmgen_node_tools/main_txfind.py similarity index 100% rename from mmnode-txfind rename to mmgen_node_tools/main_txfind.py diff --git a/setup.cfg b/setup.cfg index eeafdef..49c64c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,14 +23,14 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.0.0 + mmgen>=13.1.dev19 packages = mmgen_node_tools scripts = - mmnode-blocks-info - mmnode-feeview - mmnode-halving-calculator - mmnode-netrate - mmnode-peerblocks + cmds/mmnode-blocks-info + cmds/mmnode-feeview + cmds/mmnode-halving-calculator + cmds/mmnode-netrate + cmds/mmnode-peerblocks From 251660e3fe196d7376005a4c13fb142c4b61df02 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 31 May 2022 10:35:34 +0000 Subject: [PATCH 069/175] mmnode-feeview: support Bitcoin Core 23.0 --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_feeview.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 88a447a..544b443 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev1 +3.1.dev2 diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 8979e44..276c870 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -117,7 +117,7 @@ def create_data(coin_amt,mempool): # populate fee brackets: for tx in mempool.values(): - fee = coin_amt(tx['fee']).to_unit('satoshi') + fee = coin_amt(tx['fees']['base']).to_unit('satoshi') vsize = tx['vsize'] for bracket in out: if fee / vsize < bracket.top: From a01c9856b1976616db9ff29898abca7b36461e75 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 27 Jul 2022 16:16:41 +0000 Subject: [PATCH 070/175] support testing via MMGen test suite framework --- README.md | 11 ++++- setup.cfg | 3 +- test/init.sh | 92 ++++++++++++++++++++++++++++++++++++ test/test-release.d/cfg.sh | 62 ++++++++++++++++++++++++ test/test-release.sh | 79 ------------------------------- test/test_py_d/cfg.py | 28 +++++++++++ test/test_py_d/ts_misc.py | 50 ++++++++++++++++++++ test/test_py_d/ts_regtest.py | 64 +++++++++++++++++++++++++ 8 files changed, 308 insertions(+), 81 deletions(-) create mode 100755 test/init.sh create mode 100755 test/test-release.d/cfg.sh delete mode 100755 test/test-release.sh create mode 100755 test/test_py_d/cfg.py create mode 100755 test/test_py_d/ts_misc.py create mode 100755 test/test_py_d/ts_regtest.py diff --git a/README.md b/README.md index e8ac441..088a344 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MMGen node tools +# MMGen Node Tools Helper utilities for Bitcoin and forkcoin full nodes. @@ -19,6 +19,15 @@ Then, Also make sure that `~/.local/bin` is in `PATH`. +## Test: + +*NOTE: the tests require that the MMGen and MMGen Node Tools repositories be +located in the same directory.* + + $ test/init.sh + $ test/test-release.sh -A # BTC-only testing + $ test/test-release.sh # Full testing + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [**Forum**][4] | diff --git a/setup.cfg b/setup.cfg index 49c64c6..638546a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,10 +23,11 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.1.dev19 + mmgen>=13.2.dev10 packages = mmgen_node_tools + mmgen_node_tools.data scripts = cmds/mmnode-blocks-info diff --git a/test/init.sh b/test/init.sh new file mode 100755 index 0000000..0979c76 --- /dev/null +++ b/test/init.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" RESET="\e[0m" + +set -o errtrace +set -o functrace + +trap 'echo -e "${GREEN}Exiting at user request$RESET"; exit' INT +trap 'echo -e "${RED}Node Tools test suite initialization exited with error (line $BASH_LINENO) $RESET"' ERR +umask 0022 + +PROGNAME=$(basename $0) +while getopts h OPT +do + case "$OPT" in + h) printf " %-16s Initialize the MMGen Node Tools test suite\n" "${PROGNAME}:" + echo " USAGE: $PROGNAME" + echo " OPTIONS: '-h' Print this help message" + exit ;; + *) exit ;; + esac +done + +shift $((OPTIND-1)) + +mm_repo='../mmgen' + +die() { echo -e ${YELLOW}ERROR: $1$RESET; false; } +becho() { echo -e $BLUE$1$RESET; } + +check_mmgen_repo() { + ( cd $mm_repo; python3 ./setup.py --url | grep -iq 'mmgen' ) +} + +build_mmgen_extmod() { + ( cd $mm_repo; python3 ./setup.py build_ext --inplace ) +} + +create_dir_links() { + for target in 'mmgen' 'scripts'; do + src="$mm_repo/$target" + if [ -e $target ]; then + [ $(realpath --relative-to=. $target) == $src ] || die "'$target' does not point to '$src'" + else + echo "Creating symlink: $target" + ln -s $src + fi + done +} + +create_test_links() { + sources=' + test/include + test/overlay + test/__init__.py + test/test.py + test/unit_tests.py + test/test-release.sh + test/test_py_d/common.py + test/test_py_d/ts_base.py + cmds/mmgen-regtest + ' + for src in $sources; do + pfx=$(echo $src | sed -r 's/[^/]//g' | sed 's/\//..\//g') + if [ ! -e $src ]; then + echo "Creating symlink: $src" + ( cd "$(dirname $src)" && ln -s "$pfx$mm_repo/$src" ) + fi + done +} + +set -e + +becho 'Initializing MMGen Node Tools Test Suite' + +check_mmgen_repo || die "MMGen repository not found at $mm_repo!" + +build_mmgen_extmod + +create_dir_links + +create_test_links + +becho 'OK' diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh new file mode 100755 index 0000000..9044673 --- /dev/null +++ b/test/test-release.d/cfg.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +list_avail_tests() { + echo "AVAILABLE TESTS:" + echo " unit - unit tests" + echo " btc_rt - Bitcoin regtest" + echo " bch_rt - Bitcoin Cash Node (BCH) regtest" + echo " ltc_rt - Litecoin regtest" + echo " misc - miscellaneous tests that don't fit in the above categories" + echo + echo "AVAILABLE TEST GROUPS:" + echo " default - All tests minus the extra tests" + echo " extra - All tests minus the default tests" + echo " noalt - BTC-only tests" + echo " quick - Default tests minus btc_tn, bch, bch_rt, ltc and ltc_rt" + echo " qskip - The tests skipped in the 'quick' test group" + echo + echo "By default, all tests are run" +} + +init_groups() { + dfl_tests='unit misc btc_rt bch_rt ltc_rt' + extra_tests='' + noalt_tests='unit misc btc_rt' + quick_tests='unit misc btc_rt' + qskip_tests='bch_rt ltc_rt' +} + +init_tests() { + i_unit='Unit' + s_unit="The following tests will test various low-level subsystems" + t_unit="- $unit_tests_py --node-tools" + f_unit='Unit tests completed' + + i_misc='Misc' + s_misc="The following tests will test miscellaneous script features" + t_misc="- $test_py helpscreens" + f_misc='Misc tests completed' + + i_btc_rt='Bitcoin regtest' + s_btc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode" + t_btc_rt="- $test_py regtest" + f_btc_rt='Regtest (Bob and Alice) mode tests for BTC completed' + + i_bch_rt='BitcoinCashNode (BCH) regtest' + s_bch_rt="The following tests will test MMGen's regtest (Bob and Alice) mode" + t_bch_rt="- $test_py --coin=bch regtest" + f_bch_rt='Regtest (Bob and Alice) mode tests for BCH completed' + + i_ltc_rt='Litecoin regtest' + s_ltc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode" + t_ltc_rt="- $test_py --coin=ltc regtest" + f_ltc_rt='Regtest (Bob and Alice) mode tests for LTC completed' +} diff --git a/test/test-release.sh b/test/test-release.sh deleted file mode 100755 index 1a4531a..0000000 --- a/test/test-release.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -export MMGEN_TEST_SUITE=1 -export PYTHONPATH=. - -orig_pwd=$(pwd) - -RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" MAGENTA="\e[35;1m" CYAN="\e[36;1m" -RESET="\e[0m" - -set -o errtrace -set -o functrace - -trap 'echo -e "${GREEN}Exiting at user request$RESET"; exit' INT -trap 'echo -e "${RED}Node tools test suite exited with error$RESET"' ERR -umask 0022 - -unit_tests_py='test/unit_tests.py --names --quiet' - -PROGNAME=$(basename $0) -while getopts hv OPT -do - case "$OPT" in - h) printf " %-16s The MMGen node tools test suite\n" "${PROGNAME}:" - echo " USAGE: $PROGNAME [options] [tests or test group]" - echo " OPTIONS: '-h' Print this help message" - echo " '-v' Run commands with '--verbose' switch" - exit ;; - v) VERBOSE=1 - unit_tests_py="${unit_tests_py/--quiet/--verbose}" ;; - *) exit ;; - esac -done - -shift $((OPTIND-1)) - -nt_repo='../mmgen-node-tools' -mm_repo='../mmgen' - -die() { echo -e $YELLOW$1$RESET; false; } -gecho() { echo -e $GREEN$1$RESET; } -pecho() { echo -e $MAGENTA$1$RESET; } -becho() { echo -e $BLUE$1$RESET; } - -check_mmgen_repo() { - ( cd $mm_repo; python3 ./setup.py --url | grep -iq 'mmgen' ) -} - -create_links() { - ( cd $mm_repo && [ -L 'mmgen_node_tools' ] || ln -s "$orig_pwd/mmgen_node_tools" ) - ( - cd 'test/unit_tests_d' - for fn in ../../$nt_repo/test/unit_tests_d/nt_*.py; do - [ -L "$(basename $fn)" ] || ln -s "$fn" - done - ) -} - -run_unit_tests() { - pecho 'Running unit tests:' - $unit_tests_py --node-tools - pecho 'Completed unit tests' -} - -# start execution - -set -e - -becho 'Starting node tools test suite (WIP)' - -check_mmgen_repo || die "No MMGen repository found at $mm_repo!" - -cd $mm_repo - -create_links - -run_unit_tests - -becho 'Node tools test suite completed successfully' diff --git a/test/test_py_d/cfg.py b/test/test_py_d/cfg.py new file mode 100755 index 0000000..dbb792f --- /dev/null +++ b/test/test_py_d/cfg.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +""" +test.test_py_d.cfg: configuration data for test.py +""" + +import os + +cmd_groups_dfl = { + 'helpscreens': ('TestSuiteHelp',{'modname':'misc','full_data':True}), + 'regtest': ('TestSuiteRegtest',{}), +} + +cmd_groups_extra = {} + +cfgs = { + '1': {}, # regtest +} + +def fixup_cfgs(): pass diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py new file mode 100755 index 0000000..07d52cf --- /dev/null +++ b/test/test_py_d/ts_misc.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +""" +ts_misc.py: Miscellaneous test groups for the test.py test suite +""" + +from mmgen.globalvars import g +from ..include.common import * +from .common import * +from .ts_base import * + +class TestSuiteHelp(TestSuiteBase): + 'help, info and usage screens' + networks = ('btc','ltc','bch') + tmpdir_nums = [] + passthru_opts = ('daemon_data_dir','rpc_port','coin','testnet') + cmd_group = ( + ('version', (1,'version message',[])), + ('helpscreens', (1,'help screens', [])), + ('longhelpscreens', (1,'help screens (--longhelp)',[])), + ) + + def version(self): + t = self.spawn(f'mmnode-netrate',['--version']) + t.expect('MMNODE-NETRATE version') + return t + + def helpscreens(self,arg='--help',scripts=(),expect='USAGE:.*OPTIONS:'): + + scripts = list(scripts) or [s for s in os.listdir('cmds') if s.startswith('mmnode-')] + + for s in sorted(scripts): + t = self.spawn(s,[arg],extra_desc=f'({s})') + t.expect(expect,regex=True) + t.read() + t.ok() + t.skip_ok = True + + return t + + def longhelpscreens(self): + return self.helpscreens(arg='--longhelp',expect='USAGE:.*LONG OPTIONS:') diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py new file mode 100755 index 0000000..5f3ffaf --- /dev/null +++ b/test/test_py_d/ts_regtest.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +""" +ts_regtest.py: Regtest tests for the test.py test suite +""" + +import os +from mmgen.opts import opt +from mmgen.util import die,gmsg +from ..include.common import * +from .common import * + +from .ts_base import * + +class TestSuiteRegtest(TestSuiteBase): + 'various operations via regtest mode' + networks = ('btc','ltc','bch') + passthru_opts = ('coin',) + extra_spawn_args = ['--regtest=1'] + tmpdir_nums = [1] + color = True + deterministic = False + cmd_group = ( + ('setup', 'regtest (Bob and Alice) mode setup'), + ('stop', 'stopping regtest daemon'), + ) + + def __init__(self,trunner,cfgs,spawn): + TestSuiteBase.__init__(self,trunner,cfgs,spawn) + if trunner == None: + return + if self.proto.testnet: + die(2,'--testnet and --regtest options incompatible with regtest test suite') + + os.environ['MMGEN_TEST_SUITE_REGTEST'] = '1' + + def __del__(self): + os.environ['MMGEN_TEST_SUITE_REGTEST'] = '' + + def setup(self): + stop_test_daemons(self.proto.network_id,force=True,remove_datadir=True) + from shutil import rmtree + try: rmtree(joinpath(self.tr.data_dir,'regtest')) + except: pass + t = self.spawn('mmgen-regtest',['-n','setup']) + for s in ('Starting','Creating','Creating','Creating','Mined','Setup complete'): + t.expect(s) + return t + + def stop(self): + if opt.no_daemon_stop: + self.spawn('',msg_only=True) + msg_r('(leaving daemon running by user request)') + return 'ok' + else: + return self.spawn('mmgen-regtest',['stop']) From 6c370dec4bae8f067b969f61e68a14087bab01a7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 27 Jul 2022 16:16:42 +0000 Subject: [PATCH 071/175] new command: `mmnode-addrbal` Get balances for arbitrary addresses in the blockchain Testing: $ test/test.py -e regtest Example: # Top 10 Bitcoin addresses by balance (see https://blockchair.com/bitcoin/addresses) $ top10=' 34xp4vRoCGJym3xR7yCVPFHoCNxv4Twseo bc1qgdjqv0av3q56jvd82tkdjpy7gdp9ut8tlqmgrpmv24sq90ecnvqqjwvw97 1LQoWist8KkaUXSPKZHNvEyfrEkPHzSsCd 3LYJfcfHPXYJreMsASk2jkn69LWEYKzexb 3M219KR5vEneNb47ewrPfWyb5jQ2DjxRP6 bc1qazcm763858nkj2dj986etajv6wquslv8uxwczt 37XuVSEpWW4trkfmvWzegTHQt7BdktSKUs 1FeexV6bAHb8ybZjqQMjJrcCrHGW9sb6uF bc1qa5wkgaew2dkv56kfvj49j0av5nml45x9ek9hz6 3Kzh9qAqVWQhEsfQz7zEQL1EuSx5tyNLNS' # Compact summary: $ mmnode-addrbal --tabular $top10 # Full output: $ mmnode-addrbal $top10 --- cmds/mmnode-addrbal | 17 ++++ mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 170 +++++++++++++++++++++++++++++++ setup.cfg | 1 + test/test_py_d/ts_regtest.py | 60 +++++++++++ 5 files changed, 249 insertions(+), 1 deletion(-) create mode 100755 cmds/mmnode-addrbal create mode 100755 mmgen_node_tools/main_addrbal.py diff --git a/cmds/mmnode-addrbal b/cmds/mmnode-addrbal new file mode 100755 index 0000000..3a5bc68 --- /dev/null +++ b/cmds/mmnode-addrbal @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-addrbal: Get balances for arbitrary addresses in the blockchain +""" + +from mmgen.main import launch + +launch('addrbal',package='mmgen_node_tools') diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 544b443..59afffe 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev2 +3.1.dev3 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py new file mode 100755 index 0000000..c638ed6 --- /dev/null +++ b/mmgen_node_tools/main_addrbal.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-addrbal: Get balances for arbitrary addresses in the blockchain +""" + +from mmgen.common import * + +opts_data = { + 'text': { + 'desc': 'Get balances for arbitrary addresses in the blockchain', + 'usage': '[opts] address [address..]', + 'options': """ +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-f, --first-block With tabular output, additionally display first block info +-t, --tabular Produce compact tabular output +""" + } +} + +def do_output(proto,addr_data,blk_hdrs): + + col1w = len(str(len(addr_data))) + indent = ' ' * (col1w + 2) + + for n,(addr,unspents) in enumerate(addr_data.items(),1): + Msg(f'\n{n:{col1w}}) Address: {addr.hl()}') + + if unspents: + heights = { u['height'] for u in unspents } + Msg('{}Balance: {}'.format( + indent, + proto.coin_amt(sum(u['amount'] for u in unspents)).hl2(unit=True,fs='{:,}') )), + Msg('{}{} unspent output{} in {} block{}'.format( + indent, + red(str(len(unspents))), + suf(unspents), + red(str(len(heights))), + suf(heights) )) + blk_w = len(str(unspents[-1]['height'])) + fs = '%s{:%s} {:19} {:64} {:4} {}' % (indent,max(5,blk_w)) + Msg(fs.format('Block','Date','TxID','Vout',' Amount')) + for u in unspents: + Msg(fs.format( + u['height'], + make_timestr( blk_hdrs[u['height']]['time'] ), + CoinTxID(u['txid']).hl(), + red(str(u['vout']).rjust(4)), + proto.coin_amt(u['amount']).fmt(color=True,fs='6.8') + )) + else: + Msg(f'{indent}No balance') + +def do_output_tabular(proto,addr_data,blk_hdrs): + + col1w = len(str(len(addr_data))) + 1 + max_addrw = max(len(addr) for addr in addr_data) + fb_heights = [str(unspents[0]['height']) if unspents else '' for unspents in addr_data.values()] + lb_heights = [str(unspents[-1]['height']) if unspents else '' for unspents in addr_data.values()] + fb_w = max(len(h) for h in fb_heights) + lb_w = max(len(h) for h in lb_heights) + + fs = ( + ' {n:>%s} {a} {u} {b:>%s} {t:19} {B:>%s} {T:19} {A}' % (col1w,max(5,fb_w),max(4,lb_w)) + if opt.first_block else + ' {n:>%s} {a} {u} {B:>%s} {T:19} {A}' % (col1w,max(4,lb_w)) ) + + Msg('\n' + fs.format( + n = '', + a = 'Address'.ljust(max_addrw), + u = 'UTXOs', + b = 'First', + t = 'Block', + B = 'Last', + T = 'Block', + A = ' Amount' )) + + for n,(addr,unspents) in enumerate(addr_data.items(),1): + if unspents: + Msg(fs.format( + n = str(n) + ')', + a = addr.fmt(width=max_addrw,color=True), + u = red(str(len(unspents)).rjust(5)), + b = unspents[0]['height'], + t = make_timestr( blk_hdrs[unspents[0]['height']]['time'] ), + B = unspents[-1]['height'], + T = make_timestr( blk_hdrs[unspents[-1]['height']]['time'] ), + A = proto.coin_amt(sum(u['amount'] for u in unspents)).fmt(color=True,fs='7.8') + )) + else: + Msg(fs.format( + n = str(n) + ')', + a = addr.fmt(width=max_addrw,color=True), + u = ' -', + b = '-', + t = '', + B = '-', + T = '', + A = ' -' )) + +async def main(req_addrs): + + from mmgen.protocol import init_proto_from_opts + proto = init_proto_from_opts(need_amt=True) + + from mmgen.addr import CoinAddr + addrs = [CoinAddr(proto,addr) for addr in req_addrs] + + from mmgen.rpc import rpc_init + rpc = await rpc_init(proto) + + height = await rpc.call('getblockcount') + Msg(f'{proto.coin} {proto.network.upper()} [height {height}]') + + from mmgen.base_proto.bitcoin.misc import scantxoutset + res = await scantxoutset( rpc, [f'addr({addr})' for addr in addrs] ) + + if not res['success']: + die(1,'UTXO scanning failed or was interrupted') + elif not res['unspents']: + msg('Address has no balance' if len(addrs) == 1 else + 'Addresses have no balances' ) + else: + addr_data = {k:[] for k in addrs} + + if 'desc' in res['unspents'][0]: + import re + for unspent in sorted(res['unspents'],key=lambda x: x['height']): + addr = re.match('addr\((.*?)\)',unspent['desc'])[1] + addr_data[addr].append(unspent) + else: + from mmgen.base_proto.bitcoin.tx.base import scriptPubKey2addr + for unspent in sorted(res['unspents'],key=lambda x: x['height']): + addr = scriptPubKey2addr( proto, unspent['scriptPubKey'] )[0] + addr_data[addr].append(unspent) + + good_addrs = len([v for v in addr_data.values() if v]) + + Msg('Total: {} in {} address{}'.format( + proto.coin_amt(res['total_amount']).hl2(unit=True,fs='{:,}'), + red(str(good_addrs)), + suf(good_addrs,'es') + )) + + blk_heights = {i['height'] for i in res['unspents']} + blk_hashes = await rpc.batch_call('getblockhash', [(h,) for h in blk_heights]) + blk_hdrs = await rpc.batch_call('getblockheader', [(H,) for H in blk_hashes]) + + (do_output_tabular if opt.tabular else do_output)( proto, addr_data, dict(zip(blk_heights,blk_hdrs)) ) + +cmd_args = opts.init(opts_data,init_opts={'rpc_backend':'aiohttp'}) + +if len(cmd_args) < 1: + die(1,'This command requires at least one coin address argument') + +from mmgen.obj import CoinTxID,Int + +try: + run_session(main(cmd_args)) +except KeyboardInterrupt: + sys.stderr.write('\n') diff --git a/setup.cfg b/setup.cfg index 638546a..3f804e3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ packages = mmgen_node_tools.data scripts = + cmds/mmnode-addrbal cmds/mmnode-blocks-info cmds/mmnode-feeview cmds/mmnode-halving-calculator diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 5f3ffaf..0cbc718 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -13,13 +13,25 @@ ts_regtest.py: Regtest tests for the test.py test suite """ import os +from mmgen.globalvars import g from mmgen.opts import opt from mmgen.util import die,gmsg +from mmgen.protocol import init_proto from ..include.common import * from .common import * from .ts_base import * +args1 = ['--bob'] +args2 = ['--bob','--rpc-backend=http'] + +def gen_addrs(proto,network,keys): + from mmgen.tool.api import tool_api + tool = tool_api() + tool.init_coin(proto.coin,'regtest') + tool.addrtype = proto.mmtypes[-1] + return [tool.privhex2addr('{:064x}'.format(key)) for key in keys] + class TestSuiteRegtest(TestSuiteBase): 'various operations via regtest mode' networks = ('btc','ltc','bch') @@ -30,6 +42,18 @@ class TestSuiteRegtest(TestSuiteBase): deterministic = False cmd_group = ( ('setup', 'regtest (Bob and Alice) mode setup'), + ('sendto1', 'sending funds to address #1 (1)'), + ('sendto2', 'sending funds to address #1 (2)'), + ('sendto3', 'sending funds to address #2'), + ('addrbal_single', 'getting address balance (single address)'), + ('addrbal_multiple', 'getting address balances (multiple addresses)'), + ('addrbal_multiple_tabular1', 'getting address balances (multiple addresses, tabular output)'), + ('addrbal_multiple_tabular2', 'getting address balances (multiple addresses, tabular, show first block)'), + ('addrbal_nobal1', 'getting address balances (no balance)'), + ('addrbal_nobal2', 'getting address balances (no balances)'), + ('addrbal_nobal3', 'getting address balances (one null balance)'), + ('addrbal_nobal3_tabular1', 'getting address balances (one null balance, tabular output)'), + ('addrbal_nobal3_tabular2', 'getting address balances (one null balance, tabular, show first block)'), ('stop', 'stopping regtest daemon'), ) @@ -39,6 +63,8 @@ class TestSuiteRegtest(TestSuiteBase): return if self.proto.testnet: die(2,'--testnet and --regtest options incompatible with regtest test suite') + self.proto = init_proto(self.proto.coin,network='regtest',need_amt=True) + self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) os.environ['MMGEN_TEST_SUITE_REGTEST'] = '1' @@ -55,6 +81,40 @@ class TestSuiteRegtest(TestSuiteBase): t.expect(s) return t + def sendto(self,addr,amt): + return self.spawn('mmgen-regtest',['send',addr,amt]) + + def sendto1(self): return self.sendto(self.addrs[0],'0.123') + def sendto2(self): return self.sendto(self.addrs[0],'0.234') + def sendto3(self): return self.sendto(self.addrs[1],'0.345') + + def addrbal_single(self): + return self.spawn('mmnode-addrbal',args2+[self.addrs[0]]) + + def addrbal_multiple(self): + return self.spawn('mmnode-addrbal',args2+[self.addrs[1],self.addrs[0]]) + + def addrbal_multiple_tabular1(self): + return self.spawn('mmnode-addrbal',args2+['--tabular',self.addrs[1],self.addrs[0]]) + + def addrbal_multiple_tabular2(self): + return self.spawn('mmnode-addrbal',args1+['--tabular','--first-block',self.addrs[1],self.addrs[0]]) + + def addrbal_nobal1(self): + return self.spawn('mmnode-addrbal',args2+[self.addrs[2]]) + + def addrbal_nobal2(self): + return self.spawn('mmnode-addrbal',args2+[self.addrs[2],self.addrs[3]]) + + def addrbal_nobal3(self): + return self.spawn('mmnode-addrbal',args2+[self.addrs[4],self.addrs[0],self.addrs[3]]) + + def addrbal_nobal3_tabular1(self): + return self.spawn('mmnode-addrbal',args2+['--tabular',self.addrs[4],self.addrs[0],self.addrs[3]]) + + def addrbal_nobal3_tabular2(self): + return self.spawn('mmnode-addrbal',args1+['--tabular','--first-block',self.addrs[4],self.addrs[0],self.addrs[3]]) + def stop(self): if opt.no_daemon_stop: self.spawn('',msg_only=True) From 440bf142418a906bb2cd1f637fdccb84370b6219 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 4 Aug 2022 14:16:26 +0000 Subject: [PATCH 072/175] test suite: minor fixes and cleanups --- test/test-release.d/cfg.sh | 14 +++++++------- test/test_py_d/cfg.py | 4 ++-- test/test_py_d/ts_misc.py | 2 +- .../{nt_BlocksInfo.py => ut_BlocksInfo.py} | 0 4 files changed, 10 insertions(+), 10 deletions(-) rename test/unit_tests_d/{nt_BlocksInfo.py => ut_BlocksInfo.py} (100%) diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 9044673..fdc4800 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -37,7 +37,7 @@ init_groups() { init_tests() { i_unit='Unit' s_unit="The following tests will test various low-level subsystems" - t_unit="- $unit_tests_py --node-tools" + t_unit="- $unit_tests_py" f_unit='Unit tests completed' i_misc='Misc' @@ -46,17 +46,17 @@ init_tests() { f_misc='Misc tests completed' i_btc_rt='Bitcoin regtest' - s_btc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode" + s_btc_rt="The following tests will test various scripts using regtest mode" t_btc_rt="- $test_py regtest" - f_btc_rt='Regtest (Bob and Alice) mode tests for BTC completed' + f_btc_rt='Regtest mode tests for BTC completed' i_bch_rt='BitcoinCashNode (BCH) regtest' - s_bch_rt="The following tests will test MMGen's regtest (Bob and Alice) mode" + s_bch_rt="The following tests will test various scripts using regtest mode" t_bch_rt="- $test_py --coin=bch regtest" - f_bch_rt='Regtest (Bob and Alice) mode tests for BCH completed' + f_bch_rt='Regtest mode tests for BCH completed' i_ltc_rt='Litecoin regtest' - s_ltc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode" + s_ltc_rt="The following tests will test various scripts using regtest mode" t_ltc_rt="- $test_py --coin=ltc regtest" - f_ltc_rt='Regtest (Bob and Alice) mode tests for LTC completed' + f_ltc_rt='Regtest mode tests for LTC completed' } diff --git a/test/test_py_d/cfg.py b/test/test_py_d/cfg.py index dbb792f..c56f74c 100755 --- a/test/test_py_d/cfg.py +++ b/test/test_py_d/cfg.py @@ -15,8 +15,8 @@ test.test_py_d.cfg: configuration data for test.py import os cmd_groups_dfl = { - 'helpscreens': ('TestSuiteHelp',{'modname':'misc','full_data':True}), - 'regtest': ('TestSuiteRegtest',{}), + 'helpscreens': ('TestSuiteHelp',{'modname':'misc','full_data':True}), + 'regtest': ('TestSuiteRegtest',{}), } cmd_groups_extra = {} diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 07d52cf..707f32f 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -12,7 +12,6 @@ ts_misc.py: Miscellaneous test groups for the test.py test suite """ -from mmgen.globalvars import g from ..include.common import * from .common import * from .ts_base import * @@ -27,6 +26,7 @@ class TestSuiteHelp(TestSuiteBase): ('helpscreens', (1,'help screens', [])), ('longhelpscreens', (1,'help screens (--longhelp)',[])), ) + color = True def version(self): t = self.spawn(f'mmnode-netrate',['--version']) diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/ut_BlocksInfo.py similarity index 100% rename from test/unit_tests_d/nt_BlocksInfo.py rename to test/unit_tests_d/ut_BlocksInfo.py From ba28bffc5c538870a020773049eee8ce70929411 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 4 Aug 2022 14:16:27 +0000 Subject: [PATCH 073/175] `test.py`: addr `addrbal` subgroup, improve tests --- test/test_py_d/ts_regtest.py | 89 ++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 0cbc718..a6836be 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -40,11 +40,21 @@ class TestSuiteRegtest(TestSuiteBase): tmpdir_nums = [1] color = True deterministic = False - cmd_group = ( - ('setup', 'regtest (Bob and Alice) mode setup'), - ('sendto1', 'sending funds to address #1 (1)'), - ('sendto2', 'sending funds to address #1 (2)'), - ('sendto3', 'sending funds to address #2'), + cmd_group_in = ( + ('setup', 'regtest mode setup'), + ('subgroup.fund_addrbal', []), + ('subgroup.addrbal', ['fund_addrbal']), + ('stop', 'stopping regtest daemon'), + ) + cmd_subgroups = { + 'fund_addrbal': ( + "funding addresses for 'addrbal' subgroup", + ('sendto1', 'sending funds to address #1 (1)'), + ('sendto2', 'sending funds to address #1 (2)'), + ('sendto3', 'sending funds to address #2'), + ), + 'addrbal': ( + "'mmnode-addrbal' script", ('addrbal_single', 'getting address balance (single address)'), ('addrbal_multiple', 'getting address balances (multiple addresses)'), ('addrbal_multiple_tabular1', 'getting address balances (multiple addresses, tabular output)'), @@ -54,8 +64,8 @@ class TestSuiteRegtest(TestSuiteBase): ('addrbal_nobal3', 'getting address balances (one null balance)'), ('addrbal_nobal3_tabular1', 'getting address balances (one null balance, tabular output)'), ('addrbal_nobal3_tabular2', 'getting address balances (one null balance, tabular, show first block)'), - ('stop', 'stopping regtest daemon'), ) + } def __init__(self,trunner,cfgs,spawn): TestSuiteBase.__init__(self,trunner,cfgs,spawn) @@ -88,32 +98,81 @@ class TestSuiteRegtest(TestSuiteBase): def sendto2(self): return self.sendto(self.addrs[0],'0.234') def sendto3(self): return self.sendto(self.addrs[1],'0.345') + def addrbal(self,args,expect_list): + t = self.spawn('mmnode-addrbal',args) + t.match_expect_list(expect_list) + return t + def addrbal_single(self): - return self.spawn('mmnode-addrbal',args2+[self.addrs[0]]) + return self.addrbal( + args2 + [self.addrs[0]], + [ + f'Balance: 0.357 {g.coin}', + '2 unspent outputs in 2 blocks', + '394','0.123', + '395','0.234' + ]) def addrbal_multiple(self): - return self.spawn('mmnode-addrbal',args2+[self.addrs[1],self.addrs[0]]) + return self.addrbal( + args2 + [self.addrs[1],self.addrs[0]], + [ + '396','0.345', + '394','0.123', + '395','0.234' + ]) def addrbal_multiple_tabular1(self): - return self.spawn('mmnode-addrbal',args2+['--tabular',self.addrs[1],self.addrs[0]]) + return self.addrbal( + args2 + ['--tabular',self.addrs[1],self.addrs[0]], + [ + self.addrs[1] + ' 1 396','0.345', + self.addrs[0] + ' 2 395','0.357' + ]) def addrbal_multiple_tabular2(self): - return self.spawn('mmnode-addrbal',args1+['--tabular','--first-block',self.addrs[1],self.addrs[0]]) + return self.addrbal( + args2 + ['--tabular','--first-block',self.addrs[1],self.addrs[0]], + [ + self.addrs[1] + ' 1 396','396','0.345', + self.addrs[0] + ' 2 394','395','0.357' + ]) def addrbal_nobal1(self): - return self.spawn('mmnode-addrbal',args2+[self.addrs[2]]) + return self.addrbal( + args2 + [self.addrs[2]], ['Address has no balance'] ) def addrbal_nobal2(self): - return self.spawn('mmnode-addrbal',args2+[self.addrs[2],self.addrs[3]]) + return self.addrbal( + args2 + [self.addrs[2],self.addrs[3]], ['Addresses have no balances'] ) def addrbal_nobal3(self): - return self.spawn('mmnode-addrbal',args2+[self.addrs[4],self.addrs[0],self.addrs[3]]) + return self.addrbal( + args2 + [self.addrs[4],self.addrs[0],self.addrs[3]], + [ + 'No balance', + '2 unspent outputs in 2 blocks', + '394','0.123','395','0.234', + 'No balance' + ]) def addrbal_nobal3_tabular1(self): - return self.spawn('mmnode-addrbal',args2+['--tabular',self.addrs[4],self.addrs[0],self.addrs[3]]) + return self.addrbal( + args2 + ['--tabular',self.addrs[4],self.addrs[0],self.addrs[3]], + [ + self.addrs[4] + ' - - -', + self.addrs[0] + ' 2 395','0.357', + self.addrs[3] + ' - - -', + ]) def addrbal_nobal3_tabular2(self): - return self.spawn('mmnode-addrbal',args1+['--tabular','--first-block',self.addrs[4],self.addrs[0],self.addrs[3]]) + return self.addrbal( + args2 + ['--tabular','--first-block',self.addrs[4],self.addrs[0],self.addrs[3]], + [ + self.addrs[4] + ' - - - -', + self.addrs[0] + ' 2 394','395','0.357', + self.addrs[3] + ' - - - -', + ]) def stop(self): if opt.no_daemon_stop: From f9a084dba96d090752af663c5f7d0ee155369f50 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 4 Aug 2022 14:16:27 +0000 Subject: [PATCH 074/175] `test.py`: add halving calculator test --- test/test_py_d/ts_regtest.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index a6836be..4fb3eec 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -42,11 +42,21 @@ class TestSuiteRegtest(TestSuiteBase): deterministic = False cmd_group_in = ( ('setup', 'regtest mode setup'), + ('subgroup.halving_calculator', []), ('subgroup.fund_addrbal', []), ('subgroup.addrbal', ['fund_addrbal']), ('stop', 'stopping regtest daemon'), ) cmd_subgroups = { + 'halving_calculator': ( + "'mmnode-halving-calculator' script", + ('halving_calculator1', "halving calculator (--help)"), + ('halving_calculator2', "halving calculator"), + ('halving_calculator3', "halving calculator (--list)"), + ('halving_calculator4', "halving calculator (--mined)"), + ('halving_calculator5', "halving calculator (--mined --bdr-proj=5)"), + ('halving_calculator6', "halving calculator (--mined --sample-size=20)"), + ), 'fund_addrbal': ( "funding addresses for 'addrbal' subgroup", ('sendto1', 'sending funds to address #1 (1)'), @@ -91,6 +101,29 @@ class TestSuiteRegtest(TestSuiteBase): t.expect(s) return t + def halving_calculator(self,add_args,expect_list): + t = self.spawn('mmnode-halving-calculator',args1+add_args) + t.match_expect_list(expect_list) + return t + + def halving_calculator1(self): + return self.halving_calculator(['--help'],['USAGE:']) + + def halving_calculator2(self): + return self.halving_calculator([],['Current block: 393',f'Current block subsidy: 12.5 {g.coin}']) + + def halving_calculator3(self): + return self.halving_calculator(['--list'],['33 4950','0']) + + def halving_calculator4(self): + return self.halving_calculator(['--mined'],['0 0.0000015 14949.9999835']) + + def halving_calculator5(self): + return self.halving_calculator(['--mined','--bdr-proj=5'],['5.00000 0 0.0000015 14949.9999835']) + + def halving_calculator6(self): + return self.halving_calculator(['--mined','--sample-size=20'],['33 4950','0 0.0000015 14949.9999835']) + def sendto(self,addr,amt): return self.spawn('mmgen-regtest',['send',addr,amt]) From e7fcc00b95a9631b26d2e2c9d6cda23103958fe7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 4 Aug 2022 14:16:28 +0000 Subject: [PATCH 075/175] new script: `mmnode-ticker` Display cryptocurrency and other asset prices in convenient tabular format, with optional display of your portfolio. Output is highly configurable. Usage information: $ mmnode-ticker --help --- cmds/mmnode-ticker | 17 + mmgen_node_tools/Misc.py | 102 +++ mmgen_node_tools/Ticker.py | 777 ++++++++++++++++++++ mmgen_node_tools/Util.py | 19 +- mmgen_node_tools/data/ticker-cfg.yaml | 30 + mmgen_node_tools/data/ticker-portfolio.yaml | 6 + mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 193 +++++ setup.cfg | 3 +- test/ref/ticker/ticker-btc.json | 1 + test/ref/ticker/ticker-cfg.yaml | 24 + test/ref/ticker/ticker-portfolio-bad.yaml | 6 + test/ref/ticker/ticker-portfolio.yaml | 5 + test/ref/ticker/ticker.json | 1 + test/test-release.d/cfg.sh | 12 +- test/test_py_d/cfg.py | 2 + test/test_py_d/ts_misc.py | 223 ++++++ 17 files changed, 1416 insertions(+), 7 deletions(-) create mode 100755 cmds/mmnode-ticker create mode 100755 mmgen_node_tools/Misc.py create mode 100755 mmgen_node_tools/Ticker.py create mode 100644 mmgen_node_tools/data/ticker-cfg.yaml create mode 100644 mmgen_node_tools/data/ticker-portfolio.yaml create mode 100755 mmgen_node_tools/main_ticker.py create mode 100644 test/ref/ticker/ticker-btc.json create mode 100644 test/ref/ticker/ticker-cfg.yaml create mode 100644 test/ref/ticker/ticker-portfolio-bad.yaml create mode 100644 test/ref/ticker/ticker-portfolio.yaml create mode 100644 test/ref/ticker/ticker.json diff --git a/cmds/mmnode-ticker b/cmds/mmnode-ticker new file mode 100755 index 0000000..d6e91fb --- /dev/null +++ b/cmds/mmnode-ticker @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-ticker: Display price information for cryptocurrency and other assets +""" + +from mmgen.main import launch + +launch('ticker',package='mmgen_node_tools') diff --git a/mmgen_node_tools/Misc.py b/mmgen_node_tools/Misc.py new file mode 100755 index 0000000..999b68b --- /dev/null +++ b/mmgen_node_tools/Misc.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmgen_node_tools.Misc: miscellaneous data and functions for the MMGen Node Tools suite +""" + +curl_exit_codes = { + 1: 'Unsupported protocol. This build of curl has no support for this protocol', + 2: 'Failed to initialize', + 3: 'URL malformed. The syntax was not correct', + 4: 'A feature or option that was needed to perform the desired request was not enabled or was explicitly disabled at build-time. To make curl able to do this, you probably need another build of libcurl!', + 5: 'Couldn’t resolve proxy. The given proxy host could not be resolved', + 6: 'Couldn’t resolve host. The given remote host was not resolved', + 7: 'Failed to connect to host', + 8: 'Weird server reply. The server sent data curl couldn’t parse', + 9: 'FTP access denied. The server denied login or denied access to the particular resource or directory you wanted to reach. Most often you tried to change to a directory that doesn’t exist on the server', + 10: 'FTP accept failed. While waiting for the server to connect back when an active FTP session is used, an error code was sent over the control connection or similar', + 11: 'FTP weird PASS reply. Curl couldn’t parse the reply sent to the PASS request', + 12: 'During an active FTP session while waiting for the server to connect back to curl, the timeout expired', + 13: 'FTP weird PASV reply, Curl couldn’t parse the reply sent to the PASV request', + 14: 'FTP weird 227 format. Curl couldn’t parse the 227-line the server sent', + 15: 'FTP can’t get host. Couldn’t resolve the host IP we got in the 227-line', + 16: 'HTTP/2 error. A problem was detected in the HTTP2 framing layer. This is somewhat generic and can be one out of several problems, see the error message for details', + 17: 'FTP couldn’t set binary. Couldn’t change transfer method to binary', + 18: 'Partial file. Only a part of the file was transferred', + 19: 'FTP couldn’t download/access the given file, the RETR (or similar) command failed', + 21: 'FTP quote error. A quote command returned error from the server', + 22: 'HTTP page not retrieved. The requested url was not found or returned another error with the HTTP error code being 400 or above. This return code only appears if -f, --fail is used', + 23: 'Write error. Curl couldn’t write data to a local filesystem or similar', + 25: 'FTP couldn’t STOR file. The server denied the STOR operation, used for FTP uploading', + 26: 'Read error. Various reading problems', + 27: 'Out of memory. A memory allocation request failed', + 28: 'Operation timeout. The specified time-out period was reached according to the conditions', + 30: 'FTP PORT failed. The PORT command failed. Not all FTP servers support the PORT command, try doing a transfer using PASV instead!', + 31: 'FTP couldn’t use REST. The REST command failed. This command is used for resumed FTP transfers', + 33: 'HTTP range error. The range "command" didn’t work', + 34: 'HTTP post error. Internal post-request generation error', + 35: 'SSL connect error. The SSL handshaking failed', + 36: 'Bad download resume. Couldn’t continue an earlier aborted download', + 37: 'FILE couldn’t read file. Failed to open the file. Permissions?', + 38: 'LDAP cannot bind. LDAP bind operation failed', + 39: 'LDAP search failed', + 41: 'Function not found. A required LDAP function was not found', + 42: 'Aborted by callback. An application told curl to abort the operation', + 43: 'Internal error. A function was called with a bad parameter', + 45: 'Interface error. A specified outgoing interface could not be used', + 47: 'Too many redirects. When following redirects, curl hit the maximum amount', + 48: 'Unknown option specified to libcurl. This indicates that you passed a weird option to curl that was passed on to libcurl and rejected. Read up in the manual!', + 49: 'Malformed telnet option', + 51: 'The peer’s SSL certificate or SSH MD5 fingerprint was not OK', + 52: 'The server didn’t reply anything, which here is considered an error', + 53: 'SSL crypto engine not found', + 54: 'Cannot set SSL crypto engine as default', + 55: 'Failed sending network data', + 56: 'Failure in receiving network data', + 58: 'Problem with the local certificate', + 59: 'Couldn’t use specified SSL cipher', + 60: 'Peer certificate cannot be authenticated with known CA certificates', + 61: 'Unrecognized transfer encoding', + 62: 'Invalid LDAP URL', + 63: 'Maximum file size exceeded', + 64: 'Requested FTP SSL level failed', + 65: 'Sending the data requires a rewind that failed', + 66: 'Failed to initialise SSL Engine', + 67: 'The user name, password, or similar was not accepted and curl failed to log in', + 68: 'File not found on TFTP server', + 69: 'Permission problem on TFTP server', + 70: 'Out of disk space on TFTP server', + 71: 'Illegal TFTP operation', + 72: 'Unknown TFTP transfer ID', + 73: 'File already exists (TFTP)', + 74: 'No such user (TFTP)', + 75: 'Character conversion failed', + 76: 'Character conversion functions required', + 77: 'Problem with reading the SSL CA cert (path? access rights?)', + 78: 'The resource referenced in the URL does not exist', + 79: 'An unspecified error occurred during the SSH session', + 80: 'Failed to shut down the SSL connection', + 82: 'Could not load CRL file, missing or wrong format (added in 7.19.0)', + 83: 'Issuer check failed (added in 7.19.0)', + 84: 'The FTP PRET command failed', + 85: 'RTSP: mismatch of CSeq numbers', + 86: 'RTSP: mismatch of Session Identifiers', + 87: 'unable to parse FTP file list', + 88: 'FTP chunk callback reported error', + 89: 'No connection available, the session will be queued', + 90: 'SSL public key does not matched pinned public key', + 91: 'Invalid SSL certificate status', + 92: 'Stream error in HTTP/2 framing layer', + 93: 'An API function was called from inside a callback', + 94: 'An authentication function returned an error', + 95: 'A problem was detected in the HTTP/3 layer. This is somewhat generic and can be one out of several problems, see the error message for details', + 96: 'QUIC connection error. This error may be caused by an SSL library error. QUIC is the protocol used for HTTP/3 transfers', +} diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py new file mode 100755 index 0000000..cafe815 --- /dev/null +++ b/mmgen_node_tools/Ticker.py @@ -0,0 +1,777 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmgen_node_tools.Ticker: Display price information for cryptocurrency and other assets +""" + +api_host = 'api.coinpaprika.com' +api_url = f'https://{api_host}/v1/ticker' +ratelimit = 240 +btc_ratelimit = 10 + +# We use deprecated coinpaprika ‘ticker’ API for now because it returns ~45% less data. +# Old ‘ticker’ API (/v1/ticker): data['BTC']['price_usd'] +# New ‘tickers’ API (/v1/tickers): data['BTC']['quotes']['USD']['price'] + +# Possible alternatives: +# - https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,LTC&tsyms=USD,EUR + +import sys,os,time,json,yaml +from subprocess import run,PIPE,CalledProcessError +from decimal import Decimal +from collections import namedtuple +from mmgen.opts import opt +from mmgen.globalvars import g +from mmgen.color import * +from mmgen.util import die,fmt_list,msg,msg_r,Msg,do_pager,suf,fmt + +homedir = os.getenv('HOME') +cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') +cfg_fn = 'ticker-cfg.yaml' +portfolio_fn = 'ticker-portfolio.yaml' + +def assets_list_gen(cfg_in): + for k,v in cfg_in.cfg['assets'].items(): + yield('') + yield(k.upper()) + for e in v: + yield(' {:4s} {}'.format(*e.split('-',1))) + +def gen_data(data): + """ + Filter the raw data and return it as a dict keyed by the IDs of the assets + we want to display. + + Add dummy entry for USD and entry for user-specified asset, if any. + + Since symbols in source data are not guaranteed to be unique (e.g. XAG), we + must search the data twice: first for unique IDs, then for symbols while + checking for duplicates. + """ + + def dup_sym_errmsg(dup_sym): + return ( + f'The symbol {dup_sym!r} is shared by the following assets:\n' + + '\n ' + '\n '.join(d['id'] for d in data if d['symbol'] == dup_sym) + + '\n\nPlease specify the asset by one of the full IDs listed above\n' + + f'instead of {dup_sym!r}' + ) + + def check_assets_found(wants,found,keys=['symbol','id']): + error = False + for k in keys: + missing = wants[k] - found[k] + if missing: + msg( + ('The following IDs were not found in source data:\n{}' if k == 'id' else + 'The following symbols could not be resolved:\n{}').format( + fmt_list(missing,fmt='col',indent=' ') + )) + error = True + if error: + die(1,'Missing data, exiting') + + rows_want = { + 'id': {r.id for r in cfg.rows if getattr(r,'id',None)} - {'usd-us-dollar'}, + 'symbol': {r.symbol for r in cfg.rows if isinstance(r,tuple) and r.id is None} - {'USD'}, + } + usr_assets = cfg.usr_rows + cfg.usr_columns + tuple(c for c in (cfg.query or ()) if c) + usr_wants = { + 'id': ( + {a.id for a in usr_assets if a.id} - + {a.id for a in usr_assets if a.amount and a.id} - {'usd-us-dollar'} ) + , + 'symbol': ( + {a.symbol for a in usr_assets if not a.id} - + {a.symbol for a in usr_assets if a.amount} - {'USD'} ), + } + + found = { 'id': set(), 'symbol': set() } + + for k in ['id','symbol']: + wants = rows_want[k] | usr_wants[k] + if wants: + for d in data: + if d[k] in wants: + if d[k] in found[k]: + die(1,dup_sym_errmsg(d[k])) + yield (d['id'],d) + found[k].add(d[k]) + if k == 'id' and len(found[k]) == len(wants): + break + + for d in data: + if d['id'] == 'btc-bitcoin': + btcusd = Decimal(d['price_usd']) + break + + for asset in (cfg.usr_rows + cfg.usr_columns): + if asset.amount: + """ + User-supplied rate overrides rate from source data. + """ + _id = asset.id or f'{asset.symbol}-user-asset-{asset.symbol}'.lower() + yield ( _id, { + 'symbol': asset.symbol, + 'id': _id, + 'price_usd': str(Decimal(1/asset.amount)), + 'price_btc': str(Decimal(1/asset.amount/btcusd)), + 'last_updated': int(now), + }) + + yield ('usd-us-dollar', { + 'symbol': 'USD', + 'id': 'usd-us-dollar', + 'price_usd': '1.0', + 'price_btc': str(Decimal(1/btcusd)), + 'last_updated': int(now), + }) + + check_assets_found(usr_wants,found) + +def get_src_data(curl_cmd): + + tor_captcha_msg = f""" + If you’re using Tor, the API request may have failed due to Captcha protection. + A workaround for this issue is to retrieve the JSON data with a browser from + the following URL: + + {api_url} + + and save it to: + + ‘{cfg.cachedir}/ticker.json’ + + Then invoke the program with --cached-data and without --btc + """ + + def rate_limit_errmsg(timeout,elapsed): + return ( + f'Rate limit exceeded! Retry in {timeout-elapsed} seconds' + + ('' if cfg.btc_only else ', or use --cached-data or --btc') + ) + + if not os.path.exists(cachedir): + os.makedirs(cachedir) + + if cfg.btc_only: + fn = os.path.join(cfg.cachedir,'ticker-btc.json') + timeout = 5 if g.test_suite else btc_ratelimit + else: + fn = os.path.join(cfg.cachedir,'ticker.json') + timeout = 5 if g.test_suite else ratelimit + + fn_rel = os.path.relpath(fn,start=homedir) + + if not os.path.exists(fn): + open(fn,'w').write('{}') + + if opt.cached_data: + json_text = open(fn).read() + else: + elapsed = int(time.time() - os.stat(fn).st_mtime) + if elapsed >= timeout: + msg_r(f'Fetching data from {api_host}...') + try: + cp = run(curl_cmd,check=True,stdout=PIPE) + except CalledProcessError as e: + msg('') + from .Misc import curl_exit_codes + msg(red(curl_exit_codes[e.returncode])) + msg(red('Command line:\n {}'.format( ' '.join((repr(i) if ' ' in i else i) for i in e.cmd) ))) + from mmgen.exception import MMGenCalledProcessError + raise MMGenCalledProcessError(f'Subprocess returned non-zero exit status {e.returncode}') + json_text = cp.stdout.decode() + msg('done') + else: + die(1,rate_limit_errmsg(timeout,elapsed)) + + try: + data = json.loads(json_text) + except: + msg(json_text[:1024] + '...') + msg(orange(fmt(tor_captcha_msg,strip_char='\t'))) + die(2,'Retrieved data is not valid JSON, exiting') + + if not data: + if opt.cached_data: + die(1,'No cached data! Run command without --cached-data option to retrieve data from remote host') + else: + die(2,'Remote host returned no data!') + elif 'error' in data: + die(1,data['error']) + + if opt.cached_data: + msg(f'Using cached data from ~/{fn_rel}') + else: + open(fn,'w').write(json_text) + msg(f'JSON data cached to ~/{fn_rel}') + + return data + +def main(cfg_parm,cfg_in_parm): + + def update_sample_file(usr_cfg_file): + src_data = files('mmgen_node_tools').joinpath('data',os.path.basename(usr_cfg_file)).read_text() + sample_file = usr_cfg_file + '.sample' + sample_data = open(sample_file).read() if os.path.exists(sample_file) else None + if src_data != sample_data: + os.makedirs(os.path.dirname(sample_file),exist_ok=True) + msg('{} {}'.format( + ('Updating','Creating')[sample_data is None], + sample_file )) + open(sample_file,'w').write(src_data) + + def get_curl_cmd(): + return ([ + 'curl', + '--tr-encoding', + '--compressed', # adds 'Accept-Encoding: gzip' + '--silent', + '--header', 'Accept: application/json', + ] + + (['--proxy', cfg.proxy] if cfg.proxy else []) + + [api_url + ('/btc-bitcoin' if cfg.btc_only else '')] + ) + + global cfg,cfg_in + cfg = cfg_parm + cfg_in = cfg_in_parm + + try: + from importlib.resources import files # Python 3.9 + except ImportError: + from importlib_resources import files + + update_sample_file(cfg_in.cfg_file) + update_sample_file(cfg_in.portfolio_file) + + if opt.portfolio and not cfg_in.portfolio: + die(1,'No portfolio configured!\nTo configure a portfolio, edit the file ~/{}'.format( + os.path.relpath(cfg_in.portfolio_file,start=homedir))) + + curl_cmd = get_curl_cmd() + + if opt.print_curl: + Msg(curl_cmd + '\n' + ' '.join(curl_cmd)) + return + + parsed_json = [get_src_data(curl_cmd)] if cfg.btc_only else get_src_data(curl_cmd) + + if opt.list_ids: + do_pager('\n'.join(e['id'] for e in parsed_json)) + return + + global now + now = 1659465400 if g.test_suite else time.time() # 1659524400 1659445900 + + (do_pager if opt.pager else Msg)( + '\n'.join(getattr(Ticker,cfg.clsname)(dict(gen_data(parsed_json))).gen_output()) + ) + +def make_cfg(cmd_args,cfg_in): + + def get_rows_from_cfg(add_data=None): + def create_row(e): + return asset_tuple(e.split('-')[0].upper(),e) + def gen(): + for n,(k,v) in enumerate(cfg_in.cfg['assets'].items()): + yield(k) + if add_data and k in add_data: + v += tuple(add_data[k]) + for e in v: + yield(create_row(e)) + return tuple(gen()) + + def parse_asset_tuple(s): + sym,id = (s.split('-')[0],s) if '-' in s else (s,None) + return asset_tuple( sym.upper(), id.lower() if id else None ) + + def parse_asset_triplet(s): + ss = s.split(':') + sym,amt = ( ss[0], Decimal(ss[1]) ) if len(ss) == 2 else ( s, None ) + return asset_triplet( *parse_asset_tuple(sym), amt ) + + def parse_usr_asset_arg(s): + return tuple(parse_asset_triplet(ss) for ss in s.split(',')) if s else () + + def parse_query_arg(s): + ss = s.split(':') + if len(ss) == 2: + return query_tuple( + asset = parse_asset_triplet(s), + to_asset = None ) + elif len(ss) in (3,4): + return query_tuple( + asset = parse_asset_triplet(':'.join(ss[:2])), + to_asset = parse_asset_triplet(':'.join(ss[2:])), + ) + else: + die(1,f'{s}: malformed argument') + + def gen_uniq(obj_list,key,preload=None): + found = set([getattr(obj,key) for obj in preload if hasattr(obj,key)] if preload else ()) + for obj in obj_list: + id = getattr(obj,key) + if id not in found: + yield obj + found.add(id) + + def get_usr_assets(): + return ( + 'user_added', + usr_rows + + (tuple(asset for asset in query if asset) if query else ()) + + usr_columns ) + + def get_portfolio_assets(ret=()): + if cfg_in.portfolio and opt.portfolio: + ret = tuple( asset_tuple(e.split('-')[0].upper(),e) for e in cfg_in.portfolio ) + return ( 'portfolio', tuple(e for e in ret if (not opt.btc) or e.symbol == 'BTC') ) + + def get_portfolio(): + return {k:Decimal(v) for k,v in cfg_in.portfolio.items() if (not opt.btc) or k == 'btc-bitcoin'} + + def parse_add_precision(s): + if not s: + return 0 + if not (s.isdigit() and s.isascii()): + die(1,f'{s}: invalid parameter for --add-precision (not an integer)') + if int(s) > 30: + die(1,f'{s}: invalid parameter for --add-precision (value >30)') + return int(s) + + def create_rows(): + rows = ( + ('trade_pair',) + query if (query and query.to_asset) else + ('bitcoin',parse_asset_tuple('btc-bitcoin')) if opt.btc else + get_rows_from_cfg( add_data={'fiat':['usd-us-dollar']} if opt.add_columns else None ) + ) + + for hdr,data in ( + (get_usr_assets(),) if query else + (get_usr_assets(), get_portfolio_assets()) + ): + if data: + uniq_data = tuple(gen_uniq(data,'symbol',preload=rows)) + if uniq_data: + rows += (hdr,) + uniq_data + return rows + + cfg_tuple = namedtuple('global_cfg',[ + 'rows', + 'usr_rows', + 'usr_columns', + 'query', + 'adjust', + 'clsname', + 'btc_only', + 'add_prec', + 'cachedir', + 'proxy', + 'portfolio' ]) + + query_tuple = namedtuple('query',['asset','to_asset']) + asset_triplet = namedtuple('asset_triplet',['symbol','id','amount']) + asset_tuple = namedtuple('asset_tuple',['symbol','id']) + + usr_rows = parse_usr_asset_arg(opt.add_rows) + usr_columns = parse_usr_asset_arg(opt.add_columns) + query = parse_query_arg(cmd_args[0]) if cmd_args else None + + return cfg_tuple( + rows = create_rows(), + usr_rows = usr_rows, + usr_columns = usr_columns, + query = query, + adjust = ( lambda x: (100 + x) / 100 if x else 1 )( Decimal(opt.adjust or 0) ), + clsname = 'trading' if query else 'overview', + btc_only = opt.btc, + add_prec = parse_add_precision(opt.add_precision), + cachedir = opt.cachedir or cfg_in.cfg.get('cachedir') or cachedir, + proxy = None if opt.proxy == '' else (opt.proxy or cfg_in.cfg.get('proxy')), + portfolio = get_portfolio() if cfg_in.portfolio and opt.portfolio and not query else None + ) + +def get_cfg_in(): + ret = namedtuple('cfg_in_data',['cfg','portfolio','cfg_file','portfolio_file']) + cfg_file,portfolio_file = ( + [os.path.join(g.data_dir_root,'node_tools',fn) for fn in (cfg_fn,portfolio_fn)] + ) + cfg_data,portfolio_data = ( + [yaml.safe_load(open(fn).read()) if os.path.exists(fn) else None for fn in (cfg_file,portfolio_file)] + ) + return ret( + cfg = cfg_data or { + 'assets': { + 'coin': [ 'btc-bitcoin', 'eth-ethereum', 'xmr-monero' ], + 'commodity': [ 'xau-gold-spot-token', 'xag-silver-spot-token', 'xbr-brent-crude-oil-spot' ], + 'fiat': [ 'gbp-pound-sterling-token', 'eur-euro-token' ], + 'index': [ 'dj30-dow-jones-30-token', 'spx-sp-500', 'ndx-nasdaq-100-token' ], + }, + 'proxy': 'http://vpn-gw:8118' + }, + portfolio = portfolio_data, + cfg_file = cfg_file, + portfolio_file = portfolio_file, + ) + +class Ticker: + + class base: + + offer = None + to_asset = None + + def __init__(self,data): + + self.comma = ',' if opt.thousands_comma else '' + + self.col1_wid = max(len('TOTAL'),( + max(len(self.create_label(d['id'])) for d in data.values()) if opt.name_labels else + max(len(d['symbol']) for d in data.values()) + )) + 1 + + self.rows = [row._replace(id=self.get_id(row)) if isinstance(row,tuple) else row for row in cfg.rows] + self.col_usd_prices = {k:Decimal(self.data[k]['price_usd']) for k in self.col_ids} + + self.prices = {row.id:self.get_row_prices(row.id) + for row in self.rows if isinstance(row,tuple) and row.id in data} + self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') + + def format_last_update_col(self,cross_assets=()): + + if opt.elapsed: + from .Util import format_elapsed_hr + fmt_func = format_elapsed_hr + else: + fmt_func = lambda t,now: time.strftime('%F %X',time.gmtime(t)) # ticker API + # t.replace('T',' ').replace('Z','') # tickers API + + d = self.data + max_w = 0 + min_t = min( (int(d[a.id]['last_updated']) for a in cross_assets), default=None ) + + for row in self.rows: + if isinstance(row,tuple): + try: + t = int(d[row.id]['last_updated']) + except KeyError: + pass + else: + t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( (min(t,min_t) if min_t else t), now ) + max_w = max(len(t_fmt),max_w) + + self.upd_w = max_w + + def init_prec(self): + exp = [(a.id,Decimal.adjusted(self.prices[a.id]['usd-us-dollar'])) for a in self.usr_col_assets] + self.uprec = { k: max(0,v+4) + cfg.add_prec for k,v in exp } + self.uwid = { k: 12 + max(0, abs(v)-6) + cfg.add_prec for k,v in exp } + + def get_id(self,asset): + if asset.id: + return asset.id + else: + for d in self.data.values(): + if d['symbol'] == asset.symbol: + return d['id'] + + def create_label(self,id): + return ' '.join(id.split('-')[1:]).upper() + + def gen_output(self): + yield 'Current time: {} UTC'.format(time.strftime('%F %X',time.gmtime(now))) + + for asset in self.usr_col_assets: + if asset.symbol != 'USD': + usdprice = Decimal(self.data[asset.id]['price_usd']) + yield '{} ({}) = {:{}.{}f} USD'.format( + asset.symbol, + self.create_label(asset.id), + usdprice, + self.comma, + max(2,int(-usdprice.adjusted())+4) ) + + if hasattr(self,'subhdr'): + yield self.subhdr + + if self.show_adj: + yield ( + ('Offered price differs from spot' if self.offer else 'Adjusting prices') + + ' by ' + + yellow('{:+.2f}%'.format( (self.adjust-1) * 100 )) + ) + + yield '' + + if cfg.portfolio: + yield blue('PRICES') + + if self.table_hdr: + yield self.table_hdr + + for row in self.rows: + if isinstance(row,str): + yield ('-' * self.hl_wid) + else: + try: + yield self.fmt_row(self.data[row.id]) + except KeyError: + yield gray(f'(no data for {row.id})') + + yield '-' * self.hl_wid + + if cfg.portfolio: + self.fs_num = self.fs_num2 + self.fs_str = self.fs_str2 + yield '' + yield blue('PORTFOLIO') + yield self.table_hdr + yield '-' * self.hl_wid + for sym,amt in cfg.portfolio.items(): + try: + yield self.fmt_row(self.data[sym],amt=amt) + except KeyError: + yield gray(f'(no data for {sym})') + yield '-' * self.hl_wid + if not cfg.btc_only: + yield self.fs_num.format( + lbl = 'TOTAL', pc1='', pc2='', upd='', amt='', + **{ k.replace('-','_'): v for k,v in self.prices['total'].items() } + ) + + class overview(base): + + def __init__(self,data): + self.data = data + self.adjust = cfg.adjust + self.show_adj = self.adjust != 1 + self.usr_col_assets = [asset._replace(id=self.get_id(asset)) for asset in cfg.usr_columns] + self.col_ids = ('usd-us-dollar',) + tuple(a.id for a in self.usr_col_assets) + ('btc-bitcoin',) + + super().__init__(data) + + self.format_last_update_col() + + if cfg.portfolio: + self.prices['total'] = { col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] + for row in self.rows if isinstance(row,tuple) and row.id in cfg.portfolio and row.id in data) + for col_id in self.col_ids } + + self.init_prec() + self.init_fs() + + def get_row_prices(self,id): + if id in self.data: + d = self.data[id] + return { k: ( + Decimal(d['price_btc']) if k == 'btc-bitcoin' else + Decimal(d['price_usd']) / self.col_usd_prices[k] + ) * self.adjust for k in self.col_ids } + + def fmt_row(self,d,amt=None,amt_fmt=None): + + def fmt_pct(d): + if d in ('',None): + return gray(' --') + n = Decimal(d) + return (red,green)[n>=0](f'{n:+7.2f}') + + p = self.prices[d['id']] + + if amt is not None: + amt_fmt = f'{amt:{19+cfg.add_prec}{self.comma}.{8+cfg.add_prec}f}' + if '.' in amt_fmt: + amt_fmt = amt_fmt.rstrip('0').rstrip('.') + + return self.fs_num.format( + lbl = (self.create_label(d['id']) if opt.name_labels else d['symbol']), + pc1 = fmt_pct(d.get('percent_change_7d')), + pc2 = fmt_pct(d.get('percent_change_24h')), + upd = d.get('last_updated_fmt'), + amt = amt_fmt, + **{ k.replace('-','_'): v * (1 if amt is None else amt) for k,v in p.items() } + ) + + def init_fs(self): + + col_prec = {'usd-us-dollar':2+cfg.add_prec,'btc-bitcoin':8+cfg.add_prec } # | self.uprec # Python 3.9 + col_prec.update(self.uprec) + col_wid = {'usd-us-dollar':8+cfg.add_prec,'btc-bitcoin':12+cfg.add_prec } # """ + col_wid.update(self.uwid) + max_row = max( + ( (k,v['btc-bitcoin']) for k,v in self.prices.items() ), + key = lambda a: a[1] + ) + widths = { k: len('{:{}.{}f}'.format( self.prices[max_row[0]][k], self.comma, col_prec[k] )) + for k in self.col_ids } + + fd = namedtuple('format_str_data',['fs_str','fs_num','wid']) + + col_fs_data = { + 'label': fd(f'{{lbl:{self.col1_wid}}}',f'{{lbl:{self.col1_wid}}}',self.col1_wid), + 'pct7d': fd(' {pc1:7}', ' {pc1:7}', 8), + 'pct24h': fd(' {pc2:7}', ' {pc2:7}', 8), + 'update_time': fd(' {upd}', ' {upd}', max((19 if cfg.portfolio else 0),self.upd_w) + 2), + 'amt': fd(' {amt}', ' {amt}', 21), + } +# } | { k: fd( # Python 3.9 + col_fs_data.update({ k: fd( + ' {{{}:>{}}}'.format( k.replace('-','_'), widths[k] ), + ' {{{}:{}{}.{}f}}'.format( k.replace('-','_'), widths[k], self.comma, col_prec[k] ), + widths[k]+2 + ) for k in self.col_ids + }) + + cols = ( + ['label','usd-us-dollar'] + + [asset.id for asset in self.usr_col_assets] + + [a for a,b in ( + ( 'btc-bitcoin', not cfg.btc_only ), + ( 'pct7d', opt.percent_change ), + ( 'pct24h', opt.percent_change ), + ( 'update_time', opt.update_time ), + ) if b] + ) + cols2 = list(cols) + if opt.update_time: + cols2.pop() + cols2.append('amt') + + self.fs_str = ''.join(col_fs_data[c].fs_str for c in cols) + self.fs_num = ''.join(col_fs_data[c].fs_num for c in cols) + self.hl_wid = sum(col_fs_data[c].wid for c in cols) + + self.fs_str2 = ''.join(col_fs_data[c].fs_str for c in cols2) + self.fs_num2 = ''.join(col_fs_data[c].fs_num for c in cols2) + self.hl_wid2 = sum(col_fs_data[c].wid for c in cols2) + + @property + def table_hdr(self): + return self.fs_str.format( + lbl = '', + pc1 = ' CHG_7d', + pc2 = 'CHG_24h', + upd = 'UPDATED', + amt = ' AMOUNT', + usd_us_dollar = 'USD', + btc_bitcoin = ' BTC', + **{ a.id.replace('-','_'): a.symbol for a in self.usr_col_assets } + ) + + class trading(base): + + def __init__(self,data): + self.data = data + self.asset = cfg.query.asset._replace(id=self.get_id(cfg.query.asset)) + self.to_asset = ( + cfg.query.to_asset._replace(id=self.get_id(cfg.query.to_asset)) + if cfg.query.to_asset else None ) + self.col_ids = [self.asset.id] + self.adjust = cfg.adjust + if self.to_asset: + self.offer = self.to_asset.amount + if self.offer: + real_price = ( + self.asset.amount + * Decimal(data[self.asset.id]['price_usd']) + / Decimal(data[self.to_asset.id]['price_usd']) + ) + if self.adjust != 1: + die(1,'the --adjust option may not be combined with TO_AMOUNT in the trade specifier') + self.adjust = self.offer / real_price + self.hl_ids = [self.asset.id,self.to_asset.id] + else: + self.hl_ids = [self.asset.id] + + self.show_adj = self.adjust != 1 or self.offer + + super().__init__(data) + + self.usr_col_assets = [self.asset] + ([self.to_asset] if self.to_asset else []) + for a in self.usr_col_assets: + self.prices[a.id]['usd-us-dollar'] = Decimal(data[a.id]['price_usd']) + + self.format_last_update_col(cross_assets=self.usr_col_assets) + + self.init_prec() + self.init_fs() + + def get_row_prices(self,id): + if id in self.data: + d = self.data[id] + return { k: self.col_usd_prices[self.asset.id] / Decimal(d['price_usd']) for k in self.col_ids } + + def init_fs(self): + self.max_wid = max( + len('{:{}{}.{}f}'.format( + v[self.asset.id] * self.asset.amount, + 16 + cfg.add_prec, + self.comma, + 8 + cfg.add_prec + )) + for v in self.prices.values() + ) + self.fs_str = '{lbl:%s} {p_spot}' % self.col1_wid + self.hl_wid = self.col1_wid + self.max_wid + 1 + if self.show_adj: + self.fs_str += ' {p_adj}' + self.hl_wid += self.max_wid + 1 + if opt.update_time: + self.fs_str += ' {upd}' + self.hl_wid += self.upd_w + 2 + + def fmt_row(self,d): + id = d['id'] + p = self.prices[id][self.asset.id] * self.asset.amount + p_spot = '{:{}{}.{}f}'.format( p, self.max_wid, self.comma, 8+cfg.add_prec ) + p_adj = ( + '{:{}{}.{}f}'.format( p*self.adjust, self.max_wid, self.comma, 8+cfg.add_prec ) + if self.show_adj else '' ) + + return self.fs_str.format( + lbl = (self.create_label(id) if opt.name_labels else d['symbol']), + p_spot = green(p_spot) if id in self.hl_ids else p_spot, + p_adj = yellow(p_adj) if id in self.hl_ids else p_adj, + upd = d.get('last_updated_fmt'), + ) + + @property + def table_hdr(self): + return self.fs_str.format( + lbl = '', + p_spot = '{t:>{w}}'.format( + t = 'SPOT PRICE', + w = self.max_wid ), + p_adj = '{t:>{w}}'.format( + t = ('OFFERED' if self.offer else 'ADJUSTED') + ' PRICE', + w = self.max_wid ), + upd = 'UPDATED' + ) + + @property + def subhdr(self): + return ( + '{a}: {b:{c}} {d}'.format( + a = 'Offer' if self.offer else 'Amount', + b = self.asset.amount, + c = self.comma, + d = self.asset.symbol + ) + ( + ( + ' =>' + + (' {:{}}'.format(self.offer,self.comma) if self.offer else '') + + ' {} ({})'.format( + self.to_asset.symbol, + self.create_label(self.to_asset.id) ) + ) if self.to_asset else '' ) + ) diff --git a/mmgen_node_tools/Util.py b/mmgen_node_tools/Util.py index 2a67fe2..7fdc409 100755 --- a/mmgen_node_tools/Util.py +++ b/mmgen_node_tools/Util.py @@ -19,8 +19,8 @@ mmgen_node_tools.Util: utility functions for MMGen node tools """ -import time,subprocess -from mmgen.util import msg +import time +from mmgen.util import suf def get_hms(t=None,utc=False,no_secs=False): secs = t or time.time() @@ -33,11 +33,26 @@ def get_day_hms(t=None,utc=False): ret = (time.localtime,time.gmtime)[utc](secs) return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*ret[0:6]) +def format_elapsed_hr(t,now=None,cached={}): + now = now or time.time() + e = int(now - t) + if not e in cached: + h = abs(e) // 3600 + m = abs(e) // 60 % 60 + cached[e] = '{a}{b} minute{c} {d}'.format( + a = '{} hour{}, '.format(h,suf(h)) if h else '', + b = m, + c = suf(m), + d = 'ago' if e > 0 else 'in the future' ) if (h or m) else 'just now' + return cached[e] + def do_system(cmd,testing=False,shell=False): if testing: + from mmgen.util import msg msg("Would execute: '%s'" % cmd) return True else: + import subprocess return subprocess.call((cmd if shell else cmd.split()),shell,stderr=subprocess.PIPE) def get_url(url,gzip_ok=False,proxy=None,timeout=60,verbose=False,debug=False): diff --git a/mmgen_node_tools/data/ticker-cfg.yaml b/mmgen_node_tools/data/ticker-cfg.yaml new file mode 100644 index 0000000..2566da4 --- /dev/null +++ b/mmgen_node_tools/data/ticker-cfg.yaml @@ -0,0 +1,30 @@ +# Indentation must be consistent! Do not mix leading tabs and spaces. + +# See the curl manpage for supported --proxy parameters +# For a direct connection, leave the right-hand side blank +proxy: http://vpn-gw:8118 + +# Override the default cache directory (~/.cache/mmgen-node-tools) +cachedir: + +# Asset labels are arbitrary strings. Use as many or few as you wish. +# Invoke ‘mmnode-ticker --list-ids’ for a full list of supported asset IDs. +assets: + coin1: + - btc-bitcoin + - eth-ethereum + - xmr-monero + coin2: + - ada-cardano + - bnb-binance-coin + commodity: + - xau-gold-spot-token + - xag-silver-spot-token + - xbr-brent-crude-oil-spot + fiat: + - gbp-pound-sterling-token + - eur-euro-token + index: + - dj30-dow-jones-30-token + - spx-sp-500 + - ndx-nasdaq-100-token diff --git a/mmgen_node_tools/data/ticker-portfolio.yaml b/mmgen_node_tools/data/ticker-portfolio.yaml new file mode 100644 index 0000000..fcadd30 --- /dev/null +++ b/mmgen_node_tools/data/ticker-portfolio.yaml @@ -0,0 +1,6 @@ +# List each asset and amount in your portfolio here. +# Amounts should be strings rather than floating-point values. +# Invoke `mmnode-ticker --list-ids` for a full list of supported asset IDs. +btc-bitcoin: '1.23456789' +eth-ethereum: '2.3456789012' +xmr-monero: '4.5678901234' diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 59afffe..7fc0f94 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev3 +3.1.dev4 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py new file mode 100755 index 0000000..1949a66 --- /dev/null +++ b/mmgen_node_tools/main_ticker.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools + +""" +mmnode-ticker: Display price information for cryptocurrency and other assets +""" + +import sys,os +from mmgen.common import * +from .Ticker import * + +opts_data = { + 'sets': [ + ('wide', True, 'percent_change', True), + ('wide', True, 'name_labels', True), + ('wide', True, 'thousands_comma', True), + ('wide', True, 'update_time', True), + ], + 'text': { + 'desc': 'Display prices for cryptocurrency and other assets', + 'usage': '[opts] [TRADE_SPECIFIER]', + 'options': f""" +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-A, --adjust=P Adjust prices by percentage ‘P’. In ‘trading’ mode, + spot and adjusted prices are shown in separate columns. +-b, --btc Fetch and display data for Bitcoin only +-c, --add-columns=LIST Add columns for asset specifiers in LIST (comma- + separated, see ASSET SPECIFIERS below). Can also be + used to supply a USD exchange rate for missing assets. +-C, --cached-data Use cached data from previous network query instead of + live data from server +-d, --cachedir=D Read and write cached JSON data to directory ‘D’ + instead of ‘~/{os.path.relpath(cachedir,start=homedir)}’ +-e, --add-precision=N Add ‘N’ digits of precision to columns +-E, --elapsed Show elapsed time in UPDATED column (see --update-time) +-F, --portfolio Display portfolio data +-l, --list-ids List IDs of all available assets +-n, --name-labels Label rows with asset names rather than symbols +-p, --percent-change Add percentage change columns +-P, --pager Pipe the output to a pager +-r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, + see ASSET SPECIFIERS below). Can also be used to supply + a USD exchange rate for missing assets. +-T, --thousands-comma Use comma as a thousands separator +-u, --update-time Include UPDATED (last update time) column +-U, --print-curl Print cURL command to standard output and exit +-w, --wide Display all optional columns (equivalent to -punT) +-x, --proxy=P Connect via proxy ‘P’. Set to the empty string to + disable. Consult the curl manpage for --proxy usage. +""", + 'notes': """ + +The script has two display modes: ‘overview’, the default, and ‘trading’, the +latter being enabled when a TRADE_SPECIFIER argument (see below) is supplied +on the command line. + +Overview mode displays prices of all configured assets, and optionally the +user’s portfolio, while trading mode displays the price of a given quantity +of an asset in relation to other assets, optionally comparing an offered +price to the spot price. + +ASSETS consist of either a symbol (e.g. ‘xmr’) or full ID consisting of +symbol plus label (e.g. ‘xmr-monero’). In cases where the symbol is +ambiguous, the full ID must be used. Examples: + + chf - specify asset by symbol + chf-swiss-franc-token - same as above, but use full ID instead of symbol + +ASSET SPECIFIERS consist of an ASSET followed by an optional colon and USD +rate. If the asset is not in the source data (see --list-ids), the label +part of the ID may be arbitrarily chosen by the user. Examples: + + inr:79.5 - INR is not in the source data, so supply USD rate + inr-indian-rupee:79.5 - same as above, but add an arbitrary label + ada-cardano:0.51 - ADA is in the source data, so use the listed ID + +A TRADE_SPECIFIER is a single argument in the format: + + ASSET:AMOUNT[:TO_ASSET[:TO_AMOUNT]] + + Examples: + + xmr:17.34 - price of 17.34 XMR in all configured assets + xmr-monero:17.34 - same as above, but with full ID + xmr:17.34:eur - price of 17.34 XMR in EUR only + xmr:17.34:eur:2800 - commission on an offer of 17.34 XMR for 2800 EUR + + TO_AMOUNT, if included, is used to calculate the percentage difference or + commission on an offer compared to the spot price. + + If either ASSET or TO_ASSET refer to assets not present in the source data, + a USD rate for the missing asset(s) must be supplied via the --add-columns + or --add-rows options. + + + PROXY NOTE + +The remote server used to obtain the price data, {api_host!r}, blocks +Tor behind a Captcha wall, so a Tor proxy cannot be used directly. If you’re +concerned about privacy, connect via a VPN, or better yet, VPN over Tor. Then +set up an HTTP proxy (e.g. Privoxy) on the VPN’ed host and set the ‘proxy’ +option in the config file or --proxy on the command line accordingly. Or run +the script directly on the VPN’ed host with ’proxy’ or --proxy set to the +null string. + +Alternatively, you may download the JSON source data in a Tor-proxied browser +from ‘{api_url}’, save it as ‘ticker.json’ in your +configured cache directory, and run the script with the --cached-data option. + + + RATE LIMITING NOTE + +To protect user privacy, all filtering and processing of data is performed +client side so that the remote server does not know which assets are being +examined. This means that data for ALL available assets (currently over 4000) +is fetched with each invocation of the script. A rate limit of {ratelimit} seconds +between calls is thus imposed to prevent abuse of the remote server. When the +--btc option is in effect, this limit is reduced to 10 seconds. To bypass the +rate limit entirely, use --cached-data. + + + EXAMPLES + +# Basic display in ‘overview’ mode: +$ mmnode-ticker + +# Display BTC price only: +$ mmnode-ticker --btc + +# Wide display, add EUR and INR columns, INR rate, extra precision and proxy: +$ mmnode-ticker -w -c eur,inr-indian-rupee:79.5 -e2 -x http://vpnhost:8118 + +# Wide display, use cached data from previous network query, show portfolio +# (see above), pipe output to pager, add DOGE row: +$ mmnode-ticker -wCFP -r doge + +# Display 17.234 XMR priced in all configured assets (‘trading’ mode): +$ mmnode-ticker xmr:17.234 + +# Same as above, but add INR price at specified USDINR rate: +$ mmnode-ticker -c inr:79.5 xmr:17.234 + +# Same as above, but view INR price only at specified rate, adding label: +$ mmnode-ticker -c inr-indian-rupee:79.5 xmr:17.234:inr + +# Calculate commission on an offer of 2700 USD for 0.123 BTC, compared to +# current spot price: +$ mmnode-ticker usd:2700:btc:0.123 + +# Calculate commission on an offer of 200000 INR for 0.1 BTC, compared to +# current spot price, at specified USDINR rate: +$ mmnode-ticker -n -c inr-indian-rupee:79.5 inr:200000:btc:0.1 + + +CONFIGURED ASSETS: +{assets} + +Customize output by editing the file + ~/{cfg} + +To add a portfolio, edit the file + ~/{pf_cfg} +""" + }, + 'code': { + 'notes': lambda s: s.format( + assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), + cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), + pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), + api_host = api_host, + api_url = api_url, + ratelimit = ratelimit, + ) + } +} + +cmd_args = opts.init(opts_data,do_post_init=True) + +cfg_in = get_cfg_in() + +cfg = make_cfg(cmd_args,cfg_in) + +opts.post_init() + +main(cfg,cfg_in) diff --git a/setup.cfg b/setup.cfg index 3f804e3..628ebe0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.2.dev10 + mmgen>=13.2.dev12 packages = mmgen_node_tools @@ -36,3 +36,4 @@ scripts = cmds/mmnode-halving-calculator cmds/mmnode-netrate cmds/mmnode-peerblocks + cmds/mmnode-ticker diff --git a/test/ref/ticker/ticker-btc.json b/test/ref/ticker/ticker-btc.json new file mode 100644 index 0000000..75c8909 --- /dev/null +++ b/test/ref/ticker/ticker-btc.json @@ -0,0 +1 @@ +{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"23368.859557988893","price_btc":"1","volume_24h_usd":"24116251608.791744","market_cap_usd":"446560795287","circulating_supply":"19109225","total_supply":"19109231","max_supply":"21000000","percent_change_1h":"-0.23","percent_change_24h":"-1.87","percent_change_7d":"6.05","last_updated":"1659346445"} \ No newline at end of file diff --git a/test/ref/ticker/ticker-cfg.yaml b/test/ref/ticker/ticker-cfg.yaml new file mode 100644 index 0000000..504a7fb --- /dev/null +++ b/test/ref/ticker/ticker-cfg.yaml @@ -0,0 +1,24 @@ +proxy: http://asdfzxcv:32459 + +cachedir: + +assets: + coin1: + - btc-bitcoin + - ltc-litecoin + - eth-ethereum + - xmr-monero + coin2: + - ada-cardano + - algo-algorand + commodity: + - xau-gold-spot-token + - xag-silver-spot-token + - xbr-brent-crude-oil-spot + fiat: + - chf-swiss-franc-token + - eur-euro-token + index: + - dj30-dow-jones-30-token + - spx-sp-500 + - ndx-nasdaq-100-token diff --git a/test/ref/ticker/ticker-portfolio-bad.yaml b/test/ref/ticker/ticker-portfolio-bad.yaml new file mode 100644 index 0000000..0a2740e --- /dev/null +++ b/test/ref/ticker/ticker-portfolio-bad.yaml @@ -0,0 +1,6 @@ +btc-bitcoin: '1.23456789' +eth-ethereum: '2.345678901234567890' +xmr-monero: '4.567890123456' +ada-cardano: '123.45678901' +algo-algorand: '234.5678901' +noc-nocoin: '777.1234' diff --git a/test/ref/ticker/ticker-portfolio.yaml b/test/ref/ticker/ticker-portfolio.yaml new file mode 100644 index 0000000..332257f --- /dev/null +++ b/test/ref/ticker/ticker-portfolio.yaml @@ -0,0 +1,5 @@ +btc-bitcoin: '1.23456789' +eth-ethereum: '2.345678901234567890' +xmr-monero: '4.567890123456' +ada-cardano: '123.45678901' +algo-algorand: '234.5678901' diff --git a/test/ref/ticker/ticker.json b/test/ref/ticker/ticker.json new file mode 100644 index 0000000..2484e6d --- /dev/null +++ b/test/ref/ticker/ticker.json @@ -0,0 +1 @@ +[{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"23250.774053363122","price_btc":"1","volume_24h_usd":"28307579008.4866","market_cap_usd":"444336521633","circulating_supply":"19110612","total_supply":"19110619","max_supply":"21000000","percent_change_1h":"-0.27","percent_change_24h":"0.89","percent_change_7d":"11.15","last_updated":"1659464759"},{"id":"eth-ethereum","name":"Ethereum","symbol":"ETH","rank":"2","price_usd":"1659.6621665887371","price_btc":"0.07146396683507168","volume_24h_usd":"18216561308.363518","market_cap_usd":"202151289827","circulating_supply":"121802674","total_supply":"121802721","max_supply":"","percent_change_1h":"-0.03","percent_change_24h":"1.82","percent_change_7d":"21.42","last_updated":"1659464759"},{"id":"ltc-litecoin","name":"Litecoin","symbol":"LTC","rank":"23","price_usd":"59.013589192027936","price_btc":"0.002541086532993605","volume_24h_usd":"510336800.72056556","market_cap_usd":"4181502638","circulating_supply":"70856606","total_supply":"70856631","max_supply":"84000000","percent_change_1h":"-0.43","percent_change_24h":"0.4","percent_change_7d":"12.79","last_updated":"1659464759"},{"id":"xmr-monero","name":"Monero","symbol":"XMR","rank":"30","price_usd":"158.97302629813817","price_btc":"0.006845274482813666","volume_24h_usd":"78159392.30003875","market_cap_usd":"2886277702","circulating_supply":"18155770","total_supply":"18155768","max_supply":"","percent_change_1h":"0.21","percent_change_24h":"1.21","percent_change_7d":"7.28","last_updated":"1659464759"},{"id":"ada-cardano","name":"Cardano","symbol":"ADA","rank":"8","price_usd":"0.5069722536476557","price_btc":"0.00002182989348696495","volume_24h_usd":"507973898.04662097","market_cap_usd":"17111613980","circulating_supply":"33752565071","total_supply":"34277702082","max_supply":"45000000000","percent_change_1h":"0.05","percent_change_24h":"-0.11","percent_change_7d":"11.68","last_updated":"1659464759"},{"id":"algo-algorand","name":"Algorand","symbol":"ALGO","rank":"33","price_usd":"0.33196893931152616","price_btc":"0.00001429436529121747","volume_24h_usd":"62207779.35759487","market_cap_usd":"2306909784","circulating_supply":"6949173585","total_supply":"7350412337","max_supply":"","percent_change_1h":"-0.06","percent_change_24h":"-0.82","percent_change_7d":"9.69","last_updated":"1659464759"},{"id":"xau-gold-spot-token","name":"Gold Spot Token","symbol":"XAU","rank":"2634","price_usd":"1767.0700000000002","price_btc":"0.07608887785567188","volume_24h_usd":"11881071.852000002","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.33","percent_change_24h":"-0.19","percent_change_7d":"2.89","last_updated":"1659464759"},{"id":"xag-silver-spot-token","name":"Silver Spot Token","symbol":"XAG","rank":"4753","price_usd":"20.0265","price_btc":"0.0008623279849562342","volume_24h_usd":"445379.34674999997","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.61","percent_change_24h":"-1.54","percent_change_7d":"7.49","last_updated":"1659464759"},{"id":"xbr-brent-crude-oil-spot","name":"Brent Crude Oil Spot","symbol":"XBR","rank":"2616","price_usd":"100.39000000000001","price_btc":"0.0043227277062770015","volume_24h_usd":"4527788.105237802","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.89","percent_change_24h":"0.87","percent_change_7d":"1.02","last_updated":"1659464759"},{"id":"chf-swiss-franc-token","name":"Swiss Franc Token","symbol":"CHF","rank":"3234","price_usd":"1.0453383927173951","price_btc":"0.00004501158713651311","volume_24h_usd":"360978.80080814153","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.08","percent_change_24h":"-0.66","percent_change_7d":"0.65","last_updated":"1659464759"},{"id":"eur-euro-token","name":"Euro Token","symbol":"EUR","rank":"5116","price_usd":"1.0185784743417672","price_btc":"0.00004385932256255119","volume_24h_usd":"40587878.087926075","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"0.05","percent_change_24h":"-0.82","percent_change_7d":"0.6","last_updated":"1659464759"},{"id":"gbp-pound-sterling-token","name":"Pound Sterling Token","symbol":"GBP","rank":"3424","price_usd":"1.2181792934946465","price_btc":"0.00005245400321946659","volume_24h_usd":"5428622.322314565","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.06","percent_change_24h":"-0.61","percent_change_7d":"1.29","last_updated":"1659464759"},{"id":"dj30-dow-jones-30-token","name":"Dow Jones 30 Token","symbol":"DJ30","rank":"3192","price_usd":"32546","price_btc":"1.401409462381624","volume_24h_usd":"293564.92","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.1","percent_change_24h":"-0.91","percent_change_7d":"2.5","last_updated":"1659464759"},{"id":"spx-sp-500","name":"S&P 500 Token","symbol":"SPX","rank":"3342","price_usd":"4110.6","price_btc":"0.17699974608449287","volume_24h_usd":"780397.41","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.03","percent_change_24h":"-0.27","percent_change_7d":"4.87","last_updated":"1659464759"},{"id":"ndx-nasdaq-100-token","name":"NASDAQ 100 Token","symbol":"NDX","rank":"5097","price_usd":"12948.199999999999","price_btc":"0.5575410188904857","volume_24h_usd":"11221816.494","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"0.08","percent_change_24h":"0.04","percent_change_7d":"7.21","last_updated":"1659464759"}] diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index fdc4800..f0c11d7 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -14,6 +14,7 @@ list_avail_tests() { echo " btc_rt - Bitcoin regtest" echo " bch_rt - Bitcoin Cash Node (BCH) regtest" echo " ltc_rt - Litecoin regtest" + echo " scripts - tests of scripts not requiring a coin daemon" echo " misc - miscellaneous tests that don't fit in the above categories" echo echo "AVAILABLE TEST GROUPS:" @@ -27,10 +28,10 @@ list_avail_tests() { } init_groups() { - dfl_tests='unit misc btc_rt bch_rt ltc_rt' + dfl_tests='unit misc scripts btc_rt bch_rt ltc_rt' extra_tests='' - noalt_tests='unit misc btc_rt' - quick_tests='unit misc btc_rt' + noalt_tests='unit misc scripts btc_rt' + quick_tests='unit misc scripts btc_rt' qskip_tests='bch_rt ltc_rt' } @@ -45,6 +46,11 @@ init_tests() { t_misc="- $test_py helpscreens" f_misc='Misc tests completed' + i_scripts='No-daemon scripts' + s_scripts="The following tests will test scripts not requiring a coin daemon" + t_scripts="- $test_py scripts" + f_scripts='No-daemon script tests completed' + i_btc_rt='Bitcoin regtest' s_btc_rt="The following tests will test various scripts using regtest mode" t_btc_rt="- $test_py regtest" diff --git a/test/test_py_d/cfg.py b/test/test_py_d/cfg.py index c56f74c..d2726c4 100755 --- a/test/test_py_d/cfg.py +++ b/test/test_py_d/cfg.py @@ -16,6 +16,7 @@ import os cmd_groups_dfl = { 'helpscreens': ('TestSuiteHelp',{'modname':'misc','full_data':True}), + 'scripts': ('TestSuiteScripts',{'modname':'misc'}), 'regtest': ('TestSuiteRegtest',{}), } @@ -23,6 +24,7 @@ cmd_groups_extra = {} cfgs = { '1': {}, # regtest + '2': {}, # scripts } def fixup_cfgs(): pass diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 707f32f..c76bad6 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -12,6 +12,7 @@ ts_misc.py: Miscellaneous test groups for the test.py test suite """ +import shutil from ..include.common import * from .common import * from .ts_base import * @@ -48,3 +49,225 @@ class TestSuiteHelp(TestSuiteBase): def longhelpscreens(self): return self.helpscreens(arg='--longhelp',expect='USAGE:.*LONG OPTIONS:') + +refdir = os.path.join('test','ref','ticker') + +class TestSuiteScripts(TestSuiteBase): + 'scripts not requiring a coin daemon' + networks = ('btc',) + tmpdir_nums = [2] + passthru_opts = () + color = True + + cmd_group_in = ( + ('subgroup.ticker_setup', []), + ('subgroup.ticker', ['ticker_setup']), + ) + cmd_subgroups = { + 'ticker_setup': ( + "setup for 'ticker' subgroup", + ('ticker_setup', 'ticker setup'), + ), + 'ticker': ( + "'mmnode-ticker' script", + ('ticker1', 'ticker [--help)'), + ('ticker2', 'ticker (bad proxy)'), + ('ticker3', 'ticker [--cached-data]'), + ('ticker4', 'ticker [--cached-data --wide]'), + ('ticker5', 'ticker [--cached-data --wide --adjust=-0.766] (usr cfg file)'), + ('ticker6', 'ticker [--cached-data --wide --portfolio] (missing portfolio)'), + ('ticker7', 'ticker [--cached-data --wide --portfolio]'), + ('ticker8', 'ticker [--cached-data --wide --elapsed]'), + ('ticker9', 'ticker [--cached-data --wide --portfolio --elapsed --add-rows=fake-fakecoin:0.0123 --add-precision=2]'), + ('ticker10', 'ticker [--cached-data xmr:17.234]'), + ('ticker11', 'ticker [--cached-data xmr:17.234:btc]'), + ('ticker12', 'ticker [--cached-data --adjust=1.23 xmr:17.234:btc]'), + ('ticker13', 'ticker [--cached-data --wide --elapsed -c inr-indian-rupee:79.5 inr:200000:btc:0.1]'), + ('ticker14', 'ticker [--cached-data --wide --btc]'), + ('ticker15', 'ticker [--cached-data --wide --btc btc:2:usd:45000]'), + ) + } + + @property + def ticker_args(self): + return [ f'--cachedir={self.tmpdir}', '--proxy=http://asdfzxcv:32459' ] + + @property + def nt_datadir(self): + return os.path.join( g.data_dir_root, 'node_tools' ) + + def ticker_setup(self): + self.spawn('',msg_only=True) + shutil.copy2(os.path.join(refdir,'ticker.json'),self.tmpdir) + shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) + return 'ok' + + def ticker(self,args=[],expect_list=None,cached=True): + t = self.spawn( + f'mmnode-ticker', + (['--cached-data'] if cached else []) + self.ticker_args + args ) + if expect_list: + t.match_expect_list(expect_list) + return t + + def ticker1(self): + t = self.ticker(['--help']) + t.expect('USAGE:') + return t + + def ticker2(self): + t = self.ticker(cached=False) + if not opt.skipping_deps: + t.expect('Creating') + t.expect('Creating') + t.expect('proxy host could not be resolved') + t.req_exit_val = 3 + return t + + def ticker3(self): + return self.ticker( + [], + [ + 'USD BTC', + 'BTC 23250.77 1.00000000 ETH 1659.66 0.07146397' + ]) + + + def ticker4(self): + return self.ticker( + ['--wide','--add-columns=eur,inr-indian-rupee:79.5'], + [ + r'EUR \(EURO TOKEN\) = 1.0186 USD ' + + r'INR \(INDIAN RUPEE\) = 0.012579 USD', + 'USD EUR INR BTC CHG_7d CHG_24h UPDATED', + 'BITCOIN', + r'ETHEREUM 1,659.66 1,629.3906 131,943.14 0.07146397 \+21.42 \+1.82', + r'MONERO 158.97 156.0734 12,638.36 0.00684527 \+7.28 \+1.21 2022-08-02 18:25:59', + r'INDIAN RUPEE 0.01 0.0123 1.00 0.00000054 -- --', + ]) + + def ticker5(self): + shutil.copy2(os.path.join(refdir,'ticker-cfg.yaml'),self.nt_datadir) + t = self.ticker( + ['--wide','--adjust=-0.766'], + [ + 'Adjusting prices by -0.77%', + 'USD BTC CHG_7d CHG_24h UPDATED', + r'LITECOIN 58.56 0.00252162 \+12.79 \+0.40 2022-08-02 18:25:59', + r'MONERO 157.76 0.00679284 \+7.28 \+1.21' + ]) + os.unlink(os.path.join(self.nt_datadir,'ticker-cfg.yaml')) + return t + + def ticker6(self): + t = self.ticker( ['--wide','--portfolio'], None ) + t.expect('No portfolio') + t.req_exit_val = 1 + return t + + def ticker7(self): # demo + shutil.copy2(os.path.join(refdir,'ticker-portfolio.yaml'),self.nt_datadir) + t = self.ticker( + ['--wide','--portfolio'], + [ + 'USD BTC CHG_7d CHG_24h UPDATED', + r'ETHEREUM 1,659.66 0.07146397 \+21.42 \+1.82 2022-08-02 18:25:59', + 'CARDANO','ALGORAND', + 'PORTFOLIO','BITCOIN','ETHEREUM','MONERO','CARDANO','ALGORAND','TOTAL' + ]) + os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) + return t + + def ticker8(self): + return self.ticker( + ['--wide','--elapsed'], + [ + 'USD BTC CHG_7d CHG_24h UPDATED', + r'BITCOIN 23,250.77 1.00000000 \+11.15 \+0.89 10 minutes ago' + ]) + + def ticker9(self): + shutil.copy2( + os.path.join(refdir,'ticker-portfolio-bad.yaml'), + os.path.join(self.nt_datadir,'ticker-portfolio.yaml') ) + t = self.ticker( + ['--wide','--portfolio','--elapsed','--add-rows=fake-fakecoin:0.0123','--add-precision=2'], + [ + 'USD BTC CHG_7d CHG_24h UPDATED', + r'BITCOIN 23,250.7741 1.0000000000 \+11.15 \+0.89 10 minutes ago', + r'FAKECOIN 81.3008 0.0034966927 -- -- just now', + r'\(no data for noc-nocoin\)', + ]) + os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) + return t + + def ticker10(self): + return self.ticker( + ['XMR:17.234'], + [ + r'XMR \(MONERO\) = 158.97 USD ' + + 'Amount: 17.234 XMR', + 'SPOT PRICE', + 'BTC 0.11783441', + 'XMR 17.23400000', + 'XAU','NDX', + ]) + + def ticker11(self): + return self.ticker( + ['XMR:17.234:BTC'], + [ + r'XMR \(MONERO\) = 158.97 USD ' + + r'BTC \(BITCOIN\) = 23250.77 USD ' + + 'Amount: 17.234 XMR', + 'SPOT PRICE', + 'XMR 17.23400000 BTC 0.11783441', + ]) + + def ticker12(self): + return self.ticker( + ['--adjust=1.23','--wide','XMR:17.234:BTC'], + [ + r'XMR \(MONERO\) = 158.97 USD ' + + r'BTC \(BITCOIN\) = 23,250.77 USD ' + + 'Amount: 17.234 XMR', + r'Adjusting prices by \+1.23%', + 'SPOT PRICE ADJUSTED PRICE', + 'MONERO 17.23400000 17.44597820 2022-08-02 18:25:59 ' + + 'BITCOIN 0.11783441 0.11928377 2022-08-02 18:25:59', + ]) + + def ticker13(self): + return self.ticker( + ['-wE','-c','inr-indian-rupee:79.5','inr:200000:btc:0.1'], + [ + 'Offer: 200,000 INR', + 'Offered price differs from spot by -7.58%', + 'SPOT PRICE OFFERED PRICE UPDATED', + 'INDIAN RUPEE 200,000.00000000 184,843.65372424 10 minutes ago ' + + 'BITCOIN 0.10819955 0.10000000 10 minutes ago' + ]) + + def ticker14(self): + shutil.copy2(os.path.join(refdir,'ticker-portfolio.yaml'),self.nt_datadir) + t = self.ticker( + ['--btc','--wide','--portfolio','--elapsed'], + [ + 'PRICES', + r'BITCOIN 23,368.86 \+6.05 -1.87 33 hours, 2 minutes ago', + 'PORTFOLIO', + r'BITCOIN 28,850.44 \+6.05 -1.87 1.23456789' + ]) + os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) + return t + + def ticker15(self): + return self.ticker( + ['--btc','--wide','--elapsed','-r','inr:79.5','btc:2:usd:45000'], + [ + r'BTC \(BITCOIN\) = 23,368.86 USD', + 'Offered price differs from spot by -3.72%', + 'SPOT PRICE OFFERED PRICE UPDATED', + 'BITCOIN 2.00000000 1.92563954 33 hours, 2 minutes ago ' + + 'US DOLLAR 46,737.71911598 45,000.00000000 33 hours, 2 minutes ago', + ]) From 015de30cdd4e5dcc2fad8c27dc36fd6a21e400f3 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 5 Aug 2022 11:57:23 +0000 Subject: [PATCH 076/175] `mmnode-ticker`: support reverse USD rate in asset specifier Testing / demo: $ test/test.py -e scripts.ticker --- mmgen_node_tools/Ticker.py | 13 +++++++++---- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 20 ++++++++++++-------- test/test_py_d/ts_misc.py | 12 ++++++++++++ 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index cafe815..618b701 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -295,13 +295,18 @@ def make_cfg(cmd_args,cfg_in): sym,id = (s.split('-')[0],s) if '-' in s else (s,None) return asset_tuple( sym.upper(), id.lower() if id else None ) - def parse_asset_triplet(s): + def parse_asset_triplet(s,reverse_ok=False): ss = s.split(':') - sym,amt = ( ss[0], Decimal(ss[1]) ) if len(ss) == 2 else ( s, None ) - return asset_triplet( *parse_asset_tuple(sym), amt ) + return asset_triplet( + *parse_asset_tuple(s if len(ss) == 1 else ss[0]), + ( + None if len(ss) == 1 else + 1 / Decimal(ss[1][:-1]) if reverse_ok and ss[1].lower().endswith('r') else + Decimal(ss[1]) + )) def parse_usr_asset_arg(s): - return tuple(parse_asset_triplet(ss) for ss in s.split(',')) if s else () + return tuple(parse_asset_triplet(ss,reverse_ok=True) for ss in s.split(',')) if s else () def parse_query_arg(s): ss = s.split(':') diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 7fc0f94..fb75e93 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev4 +3.1.dev5 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 1949a66..d4a5533 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -76,11 +76,13 @@ ambiguous, the full ID must be used. Examples: ASSET SPECIFIERS consist of an ASSET followed by an optional colon and USD rate. If the asset is not in the source data (see --list-ids), the label -part of the ID may be arbitrarily chosen by the user. Examples: +part of the ID may be arbitrarily chosen by the user. When the letter ‘r’ +is appended to the USD rate, the rate is reversed, i.e. treated as ‘USD per +asset’ instead of ‘asset per USD’. Asset specifier examples: inr:79.5 - INR is not in the source data, so supply USD rate inr-indian-rupee:79.5 - same as above, but add an arbitrary label - ada-cardano:0.51 - ADA is in the source data, so use the listed ID + omr-omani-rial:2.59r - OMR is pegged to USD with fixed value of 2.59 USD A TRADE_SPECIFIER is a single argument in the format: @@ -121,9 +123,9 @@ configured cache directory, and run the script with the --cached-data option. To protect user privacy, all filtering and processing of data is performed client side so that the remote server does not know which assets are being examined. This means that data for ALL available assets (currently over 4000) -is fetched with each invocation of the script. A rate limit of {ratelimit} seconds -between calls is thus imposed to prevent abuse of the remote server. When the ---btc option is in effect, this limit is reduced to 10 seconds. To bypass the +is fetched with each invocation of the script. A rate limit of {L} seconds +between calls is thus imposed to prevent abuse of the remote server. When the +--btc option is in effect, this limit is reduced to {B} seconds. To bypass the rate limit entirely, use --cached-data. @@ -135,8 +137,9 @@ $ mmnode-ticker # Display BTC price only: $ mmnode-ticker --btc -# Wide display, add EUR and INR columns, INR rate, extra precision and proxy: -$ mmnode-ticker -w -c eur,inr-indian-rupee:79.5 -e2 -x http://vpnhost:8118 +# Wide display, add EUR and OMR columns, OMRUSD rate, extra precision and +# proxy: +$ mmnode-ticker -w -c eur,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 # Wide display, use cached data from previous network query, show portfolio # (see above), pipe output to pager, add DOGE row: @@ -177,7 +180,8 @@ To add a portfolio, edit the file pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), api_host = api_host, api_url = api_url, - ratelimit = ratelimit, + L = ratelimit, + B = btc_ratelimit, ) } } diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index c76bad6..9815560 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -85,6 +85,7 @@ class TestSuiteScripts(TestSuiteBase): ('ticker13', 'ticker [--cached-data --wide --elapsed -c inr-indian-rupee:79.5 inr:200000:btc:0.1]'), ('ticker14', 'ticker [--cached-data --wide --btc]'), ('ticker15', 'ticker [--cached-data --wide --btc btc:2:usd:45000]'), + ('ticker16', 'ticker [--cached-data --wide --elapsed -c eur,omr-omani-rial:2.59r'), ) } @@ -271,3 +272,14 @@ class TestSuiteScripts(TestSuiteBase): 'BITCOIN 2.00000000 1.92563954 33 hours, 2 minutes ago ' + 'US DOLLAR 46,737.71911598 45,000.00000000 33 hours, 2 minutes ago', ]) + + def ticker16(self): + return self.ticker( + ['--wide','--elapsed','-c','eur,omr-omani-rial:2.59r'], + [ + r'EUR \(EURO TOKEN\) = 1.0186 USD ' + + r'OMR \(OMANI RIAL\) = 2.5900 USD', + 'USD EUR OMR BTC CHG_7d CHG_24h UPDATED', + 'BITCOIN', + 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- just now' + ]) From 3fd9c971bf6b76b85e82fb44378c502b6707b254 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 6 Aug 2022 09:48:48 +0000 Subject: [PATCH 077/175] minor cleanups --- README.md | 23 ++++++--- mmgen_node_tools/Ticker.py | 87 +++++++++++++++++++-------------- mmgen_node_tools/main_ticker.py | 30 +++++++----- test/test_py_d/ts_misc.py | 2 +- 4 files changed, 86 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 088a344..9c72f4e 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ First, install [MMGen][6]. Then, - $ git clone https://github.com/mmgen/mmgen-node-tools - $ cd mmgen-node-tools - $ python3 -m build --no-isolation - $ python3 -m pip install --user dist/*.whl + $ git clone https://github.com/mmgen/mmgen-node-tools + $ cd mmgen-node-tools + $ python3 -m build --no-isolation + $ python3 -m pip install --user dist/*.whl Also make sure that `~/.local/bin` is in `PATH`. @@ -24,9 +24,18 @@ Also make sure that `~/.local/bin` is in `PATH`. *NOTE: the tests require that the MMGen and MMGen Node Tools repositories be located in the same directory.* - $ test/init.sh - $ test/test-release.sh -A # BTC-only testing - $ test/test-release.sh # Full testing +Initialize the test framework (must be run at least once after cloning, and +possibly again after a pull if tests have been updated): + + $ test/init.sh + +BTC-only testing: + + $ test/test-release.sh -A + +Full testing: + + $ test/test-release.sh - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 618b701..b667ccd 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -87,11 +87,11 @@ def gen_data(data): usr_wants = { 'id': ( {a.id for a in usr_assets if a.id} - - {a.id for a in usr_assets if a.amount and a.id} - {'usd-us-dollar'} ) + {a.id for a in usr_assets if a.rate and a.id} - {'usd-us-dollar'} ) , 'symbol': ( {a.symbol for a in usr_assets if not a.id} - - {a.symbol for a in usr_assets if a.amount} - {'USD'} ), + {a.symbol for a in usr_assets if a.rate} - {'USD'} ), } found = { 'id': set(), 'symbol': set() } @@ -114,7 +114,7 @@ def gen_data(data): break for asset in (cfg.usr_rows + cfg.usr_columns): - if asset.amount: + if asset.rate: """ User-supplied rate overrides rate from source data. """ @@ -122,8 +122,8 @@ def gen_data(data): yield ( _id, { 'symbol': asset.symbol, 'id': _id, - 'price_usd': str(Decimal(1/asset.amount)), - 'price_btc': str(Decimal(1/asset.amount/btcusd)), + 'price_usd': str(Decimal(1/asset.rate)), + 'price_btc': str(Decimal(1/asset.rate/btcusd)), 'last_updated': int(now), }) @@ -280,47 +280,62 @@ def main(cfg_parm,cfg_in_parm): def make_cfg(cmd_args,cfg_in): def get_rows_from_cfg(add_data=None): - def create_row(e): - return asset_tuple(e.split('-')[0].upper(),e) def gen(): for n,(k,v) in enumerate(cfg_in.cfg['assets'].items()): yield(k) if add_data and k in add_data: v += tuple(add_data[k]) for e in v: - yield(create_row(e)) + yield parse_asset_id(e,True) return tuple(gen()) - def parse_asset_tuple(s): - sym,id = (s.split('-')[0],s) if '-' in s else (s,None) - return asset_tuple( sym.upper(), id.lower() if id else None ) - - def parse_asset_triplet(s,reverse_ok=False): - ss = s.split(':') - return asset_triplet( - *parse_asset_tuple(s if len(ss) == 1 else ss[0]), - ( - None if len(ss) == 1 else - 1 / Decimal(ss[1][:-1]) if reverse_ok and ss[1].lower().endswith('r') else - Decimal(ss[1]) - )) + def parse_asset_id(s,require_label=False): + sym,label = (*s.split('-',1),None)[:2] + if require_label and not label: + die(1,f'{s!r}: asset label is missing') + return asset_tuple( sym.upper(), (s.lower() if label else None) ) def parse_usr_asset_arg(s): - return tuple(parse_asset_triplet(ss,reverse_ok=True) for ss in s.split(',')) if s else () + """ + asset_id[:rate] + """ + def parse_parm(s): + ss = s.split(':') + assert len(ss) in (1,2), f'{s}: malformed argument' + asset_id,rate = (*ss,None)[:2] + parsed_id = parse_asset_id(asset_id) + + return asset_data( + symbol = parsed_id.symbol, + id = parsed_id.id, + amount = None, + rate = ( + None if rate is None else + 1 / Decimal(rate[:-1]) if rate.lower().endswith('r') else + Decimal(rate) )) + + return tuple(parse_parm(s2) for s2 in s.split(',')) if s else () def parse_query_arg(s): + """ + asset_id:amount[:to_asset_id[:to_amount]] + """ + def parse_query_asset(asset_id,amount): + parsed_id = parse_asset_id(asset_id) + return asset_data( + symbol = parsed_id.symbol, + id = parsed_id.id, + amount = None if amount is None else Decimal(amount), + rate = None ) + ss = s.split(':') - if len(ss) == 2: - return query_tuple( - asset = parse_asset_triplet(s), - to_asset = None ) - elif len(ss) in (3,4): - return query_tuple( - asset = parse_asset_triplet(':'.join(ss[:2])), - to_asset = parse_asset_triplet(':'.join(ss[2:])), - ) - else: - die(1,f'{s}: malformed argument') + assert len(ss) in (2,3,4), f'{s}: malformed argument' + asset_id,amount,to_asset_id,to_amount = (*ss,None,None)[:4] + + return query_tuple( + asset = parse_query_asset(asset_id,amount), + to_asset = parse_query_asset(to_asset_id,to_amount) if to_asset_id else None + ) def gen_uniq(obj_list,key,preload=None): found = set([getattr(obj,key) for obj in preload if hasattr(obj,key)] if preload else ()) @@ -339,7 +354,7 @@ def make_cfg(cmd_args,cfg_in): def get_portfolio_assets(ret=()): if cfg_in.portfolio and opt.portfolio: - ret = tuple( asset_tuple(e.split('-')[0].upper(),e) for e in cfg_in.portfolio ) + ret = (parse_asset_id(e,True) for e in cfg_in.portfolio) return ( 'portfolio', tuple(e for e in ret if (not opt.btc) or e.symbol == 'BTC') ) def get_portfolio(): @@ -357,7 +372,7 @@ def make_cfg(cmd_args,cfg_in): def create_rows(): rows = ( ('trade_pair',) + query if (query and query.to_asset) else - ('bitcoin',parse_asset_tuple('btc-bitcoin')) if opt.btc else + ('bitcoin',parse_asset_id('btc-bitcoin')) if opt.btc else get_rows_from_cfg( add_data={'fiat':['usd-us-dollar']} if opt.add_columns else None ) ) @@ -385,7 +400,7 @@ def make_cfg(cmd_args,cfg_in): 'portfolio' ]) query_tuple = namedtuple('query',['asset','to_asset']) - asset_triplet = namedtuple('asset_triplet',['symbol','id','amount']) + asset_data = namedtuple('asset_data',['symbol','id','amount','rate']) asset_tuple = namedtuple('asset_tuple',['symbol','id']) usr_rows = parse_usr_asset_arg(opt.add_rows) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index d4a5533..e843475 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -67,22 +67,28 @@ user’s portfolio, while trading mode displays the price of a given quantity of an asset in relation to other assets, optionally comparing an offered price to the spot price. -ASSETS consist of either a symbol (e.g. ‘xmr’) or full ID consisting of -symbol plus label (e.g. ‘xmr-monero’). In cases where the symbol is -ambiguous, the full ID must be used. Examples: +ASSETS consist of either a symbol (e.g. ‘xmr’) or full ID (see --list-ids) +consisting of symbol plus label (e.g. ‘xmr-monero’). In cases where the +symbol is ambiguous, the full ID must be used. Examples: chf - specify asset by symbol chf-swiss-franc-token - same as above, but use full ID instead of symbol -ASSET SPECIFIERS consist of an ASSET followed by an optional colon and USD -rate. If the asset is not in the source data (see --list-ids), the label -part of the ID may be arbitrarily chosen by the user. When the letter ‘r’ -is appended to the USD rate, the rate is reversed, i.e. treated as ‘USD per -asset’ instead of ‘asset per USD’. Asset specifier examples: +ASSET SPECIFIERS have the following format: - inr:79.5 - INR is not in the source data, so supply USD rate - inr-indian-rupee:79.5 - same as above, but add an arbitrary label - omr-omani-rial:2.59r - OMR is pegged to USD with fixed value of 2.59 USD + ASSET[:RATE] + +If the asset referred to by ASSET is not in the source data (see --list-ids), +an arbitrarily chosen label may be used. RATE is the USD exchange rate of +the asset. When RATE is postfixed with the letter ‘r’, its meaning is +reversed, i.e. interpreted as ‘ASSET/USD’ instead of ‘USD/ASSET’. Asset +specifier examples: + + inr:79.5 - INR is not in the source data, so supply rate of + 79.5 INR to the Dollar (USD/INR) + inr:0.01257r - same as above, but use reverse rate (INR/USD) + inr-indian-rupee:79.5 - same as first example, but add an arbitrary label + omr-omani-rial:2.59r - Omani Rial is pegged to the Dollar at 2.59 USD A TRADE_SPECIFIER is a single argument in the format: @@ -137,7 +143,7 @@ $ mmnode-ticker # Display BTC price only: $ mmnode-ticker --btc -# Wide display, add EUR and OMR columns, OMRUSD rate, extra precision and +# Wide display, add EUR and OMR columns, OMR/USD rate, extra precision and # proxy: $ mmnode-ticker -w -c eur,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 9815560..7295da3 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -280,6 +280,6 @@ class TestSuiteScripts(TestSuiteBase): r'EUR \(EURO TOKEN\) = 1.0186 USD ' + r'OMR \(OMANI RIAL\) = 2.5900 USD', 'USD EUR OMR BTC CHG_7d CHG_24h UPDATED', - 'BITCOIN', + r'BITCOIN 23,250.77 22,826.6890 8,977.1328 1.00000000 \+11.15 \+0.89 10 minutes ago', 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- just now' ]) From f9ba7b89c8bedb401b07812b856873ec60838bc5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 6 Aug 2022 09:48:48 +0000 Subject: [PATCH 078/175] `mmnode-ticker`: support rate asset in asset specifier This feature adds convenience for users whose native currencies are pegged to assets other than the US Dollar. Example: # Add a column for Bulgarian Lev, which is pegged to the Euro at 0.5113 EUR: $ mmnode-ticker -wE -c eur,bgn-bulgarian-lev:0.5113r:eur Testing / demo: $ test/test.py -e scripts.ticker --- mmgen_node_tools/Ticker.py | 31 +++++++++++++++++++++---------- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 14 +++++++++----- test/test_py_d/ts_misc.py | 23 +++++++++++++++++++++++ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index b667ccd..14163eb 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -83,18 +83,24 @@ def gen_data(data): 'id': {r.id for r in cfg.rows if getattr(r,'id',None)} - {'usd-us-dollar'}, 'symbol': {r.symbol for r in cfg.rows if isinstance(r,tuple) and r.id is None} - {'USD'}, } + usr_rate_assets = tuple(u.rate_asset for u in cfg.usr_rows + cfg.usr_columns if u.rate_asset) + usr_rate_assets_want = { + 'id': {a.id for a in usr_rate_assets if a.id}, + 'symbol': {a.symbol for a in usr_rate_assets if not a.id} + } usr_assets = cfg.usr_rows + cfg.usr_columns + tuple(c for c in (cfg.query or ()) if c) usr_wants = { 'id': ( - {a.id for a in usr_assets if a.id} - + {a.id for a in usr_assets + usr_rate_assets if a.id} - {a.id for a in usr_assets if a.rate and a.id} - {'usd-us-dollar'} ) , 'symbol': ( - {a.symbol for a in usr_assets if not a.id} - + {a.symbol for a in usr_assets + usr_rate_assets if not a.id} - {a.symbol for a in usr_assets if a.rate} - {'USD'} ), } found = { 'id': set(), 'symbol': set() } + rate_assets = {} for k in ['id','symbol']: wants = rows_want[k] | usr_wants[k] @@ -105,6 +111,8 @@ def gen_data(data): die(1,dup_sym_errmsg(d[k])) yield (d['id'],d) found[k].add(d[k]) + if d[k] in usr_rate_assets_want[k]: + rate_assets[d['symbol']] = d # NB: using symbol instead of ID if k == 'id' and len(found[k]) == len(wants): break @@ -119,11 +127,12 @@ def gen_data(data): User-supplied rate overrides rate from source data. """ _id = asset.id or f'{asset.symbol}-user-asset-{asset.symbol}'.lower() + ra_rate = Decimal(rate_assets[asset.rate_asset.symbol]['price_usd']) if asset.rate_asset else 1 yield ( _id, { 'symbol': asset.symbol, 'id': _id, - 'price_usd': str(Decimal(1/asset.rate)), - 'price_btc': str(Decimal(1/asset.rate/btcusd)), + 'price_usd': str(Decimal(ra_rate/asset.rate)), + 'price_btc': str(Decimal(ra_rate/asset.rate/btcusd)), 'last_updated': int(now), }) @@ -297,12 +306,12 @@ def make_cfg(cmd_args,cfg_in): def parse_usr_asset_arg(s): """ - asset_id[:rate] + asset_id[:rate[:rate_asset]] """ def parse_parm(s): ss = s.split(':') - assert len(ss) in (1,2), f'{s}: malformed argument' - asset_id,rate = (*ss,None)[:2] + assert len(ss) in (1,2,3), f'{s}: malformed argument' + asset_id,rate,rate_asset = (*ss,None,None)[:3] parsed_id = parse_asset_id(asset_id) return asset_data( @@ -312,7 +321,8 @@ def make_cfg(cmd_args,cfg_in): rate = ( None if rate is None else 1 / Decimal(rate[:-1]) if rate.lower().endswith('r') else - Decimal(rate) )) + Decimal(rate) ), + rate_asset = parse_asset_id(rate_asset) if rate_asset else None ) return tuple(parse_parm(s2) for s2 in s.split(',')) if s else () @@ -326,7 +336,8 @@ def make_cfg(cmd_args,cfg_in): symbol = parsed_id.symbol, id = parsed_id.id, amount = None if amount is None else Decimal(amount), - rate = None ) + rate = None, + rate_asset = None ) ss = s.split(':') assert len(ss) in (2,3,4), f'{s}: malformed argument' @@ -400,7 +411,7 @@ def make_cfg(cmd_args,cfg_in): 'portfolio' ]) query_tuple = namedtuple('query',['asset','to_asset']) - asset_data = namedtuple('asset_data',['symbol','id','amount','rate']) + asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset']) asset_tuple = namedtuple('asset_tuple',['symbol','id']) usr_rows = parse_usr_asset_arg(opt.add_rows) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index fb75e93..2590359 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev5 +3.1.dev6 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index e843475..9a32c04 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -76,19 +76,20 @@ symbol is ambiguous, the full ID must be used. Examples: ASSET SPECIFIERS have the following format: - ASSET[:RATE] + ASSET[:RATE[:RATE_ASSET]] If the asset referred to by ASSET is not in the source data (see --list-ids), -an arbitrarily chosen label may be used. RATE is the USD exchange rate of -the asset. When RATE is postfixed with the letter ‘r’, its meaning is -reversed, i.e. interpreted as ‘ASSET/USD’ instead of ‘USD/ASSET’. Asset -specifier examples: +an arbitrarily chosen label may be used. RATE is the exchange rate of the +asset in relation to RATE_ASSET, if present, otherwise USD. When RATE is +postfixed with the letter ‘r’, its meaning is reversed, i.e. interpreted as +‘ASSET/RATE_ASSET’ instead of ‘RATE_ASSET/ASSET’. Asset specifier examples: inr:79.5 - INR is not in the source data, so supply rate of 79.5 INR to the Dollar (USD/INR) inr:0.01257r - same as above, but use reverse rate (INR/USD) inr-indian-rupee:79.5 - same as first example, but add an arbitrary label omr-omani-rial:2.59r - Omani Rial is pegged to the Dollar at 2.59 USD + bgn-bg-lev:0.5113r:eur - Bulgarian Lev is pegged to the Euro at 0.5113 EUR A TRADE_SPECIFIER is a single argument in the format: @@ -147,6 +148,9 @@ $ mmnode-ticker --btc # proxy: $ mmnode-ticker -w -c eur,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 +# Wide display, elapsed update time, add EUR, BGN columns and BGN/EUR rate: +$ mmnode-ticker -wE -c eur,bgn-bulgarian-lev:0.5113r:eur + # Wide display, use cached data from previous network query, show portfolio # (see above), pipe output to pager, add DOGE row: $ mmnode-ticker -wCFP -r doge diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 7295da3..0d39c7e 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -86,6 +86,8 @@ class TestSuiteScripts(TestSuiteBase): ('ticker14', 'ticker [--cached-data --wide --btc]'), ('ticker15', 'ticker [--cached-data --wide --btc btc:2:usd:45000]'), ('ticker16', 'ticker [--cached-data --wide --elapsed -c eur,omr-omani-rial:2.59r'), + ('ticker17', 'ticker [--cached-data --wide --elapsed -c bgn-bulgarian-lev:0.5113r:eur'), + ('ticker18', 'ticker [--cached-data --wide --elapsed -c eur,bgn-bulgarian-lev:0.5113r:eur-euro-token'), ) } @@ -283,3 +285,24 @@ class TestSuiteScripts(TestSuiteBase): r'BITCOIN 23,250.77 22,826.6890 8,977.1328 1.00000000 \+11.15 \+0.89 10 minutes ago', 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- just now' ]) + + def ticker17(self): + # BGN pegged at 0.5113 EUR + return self.ticker( + ['--wide','--elapsed','-c','bgn-bulgarian-lev:0.5113r:eur'], + [ + r'BGN \(BULGARIAN LEV\) = 0.52080 USD', + 'USD BGN BTC CHG_7d CHG_24h UPDATED', + 'BITCOIN 23,250.77 44,644.414 1.00000000', + 'BULGARIAN LEV 0.52 1.000 0.00002240', + ]) + + def ticker18(self): + return self.ticker( + ['--wide','--elapsed','-c','eur,bgn-bulgarian-lev:0.5113r:eur-euro-token'], + [ + r'BGN \(BULGARIAN LEV\) = 0.52080 USD', + 'USD EUR BGN BTC CHG_7d CHG_24h UPDATED', + 'BITCOIN 23,250.77 22,826.6890 44,644.414 1.00000000', + 'BULGARIAN LEV 0.52 0.5113 1.000 0.00002240', + ]) From cb93e5275544b80469844018c73a2bb6bea443f5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 17 Oct 2022 18:35:55 +0000 Subject: [PATCH 079/175] update for MMGen v13.3.dev6 --- README.md | 2 +- mmgen_node_tools/Ticker.py | 12 +++++++----- mmgen_node_tools/Util.py | 13 ------------- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 6 +++--- mmgen_node_tools/main_blocks_info.py | 2 +- mmgen_node_tools/main_feeview.py | 9 ++++----- mmgen_node_tools/main_halving_calculator.py | 2 +- mmgen_node_tools/main_netrate.py | 2 +- mmgen_node_tools/main_peerblocks.py | 2 +- mmgen_node_tools/main_ticker.py | 1 + mmgen_node_tools/main_txfind.py | 2 +- setup.cfg | 2 +- test/test-release.d/cfg.sh | 10 ++++++++++ test/test_py_d/ts_misc.py | 6 +++--- 15 files changed, 36 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 9c72f4e..673155e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MMGen Node Tools -Helper utilities for Bitcoin and forkcoin full nodes. +### Terminal-based utilities for Bitcoin and forkcoin full nodes Requires modules from the [MMGen online/offline cryptocurrency wallet][6]. diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 14163eb..7fe8b1d 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -31,7 +31,7 @@ from collections import namedtuple from mmgen.opts import opt from mmgen.globalvars import g from mmgen.color import * -from mmgen.util import die,fmt_list,msg,msg_r,Msg,do_pager,suf,fmt +from mmgen.util import die,fmt_list,msg,msg_r,Msg,vmsg,suf,fmt,stdout_or_pager homedir = os.getenv('HOME') cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') @@ -189,6 +189,7 @@ def get_src_data(curl_cmd): elapsed = int(time.time() - os.stat(fn).st_mtime) if elapsed >= timeout: msg_r(f'Fetching data from {api_host}...') + vmsg('') try: cp = run(curl_cmd,check=True,stdout=PIPE) except CalledProcessError as e: @@ -244,10 +245,10 @@ def main(cfg_parm,cfg_in_parm): 'curl', '--tr-encoding', '--compressed', # adds 'Accept-Encoding: gzip' - '--silent', '--header', 'Accept: application/json', ] + (['--proxy', cfg.proxy] if cfg.proxy else []) + + (['--silent'] if not opt.verbose else []) + [api_url + ('/btc-bitcoin' if cfg.btc_only else '')] ) @@ -276,14 +277,15 @@ def main(cfg_parm,cfg_in_parm): parsed_json = [get_src_data(curl_cmd)] if cfg.btc_only else get_src_data(curl_cmd) if opt.list_ids: + from mmgen.ui import do_pager do_pager('\n'.join(e['id'] for e in parsed_json)) return global now now = 1659465400 if g.test_suite else time.time() # 1659524400 1659445900 - (do_pager if opt.pager else Msg)( - '\n'.join(getattr(Ticker,cfg.clsname)(dict(gen_data(parsed_json))).gen_output()) + stdout_or_pager( + '\n'.join(getattr(Ticker,cfg.clsname)(dict(gen_data(parsed_json))).gen_output()) + '\n' ) def make_cfg(cmd_args,cfg_in): @@ -481,7 +483,7 @@ class Ticker: def format_last_update_col(self,cross_assets=()): if opt.elapsed: - from .Util import format_elapsed_hr + from mmgen.util2 import format_elapsed_hr fmt_func = format_elapsed_hr else: fmt_func = lambda t,now: time.strftime('%F %X',time.gmtime(t)) # ticker API diff --git a/mmgen_node_tools/Util.py b/mmgen_node_tools/Util.py index 7fdc409..9060f68 100755 --- a/mmgen_node_tools/Util.py +++ b/mmgen_node_tools/Util.py @@ -33,19 +33,6 @@ def get_day_hms(t=None,utc=False): ret = (time.localtime,time.gmtime)[utc](secs) return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*ret[0:6]) -def format_elapsed_hr(t,now=None,cached={}): - now = now or time.time() - e = int(now - t) - if not e in cached: - h = abs(e) // 3600 - m = abs(e) // 60 % 60 - cached[e] = '{a}{b} minute{c} {d}'.format( - a = '{} hour{}, '.format(h,suf(h)) if h else '', - b = m, - c = suf(m), - d = 'ago' if e > 0 else 'in the future' ) if (h or m) else 'just now' - return cached[e] - def do_system(cmd,testing=False,shell=False): if testing: from mmgen.util import msg diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 2590359..58dfba3 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev6 +3.1.dev7 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index c638ed6..4218f6a 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -121,7 +121,7 @@ async def main(req_addrs): height = await rpc.call('getblockcount') Msg(f'{proto.coin} {proto.network.upper()} [height {height}]') - from mmgen.base_proto.bitcoin.misc import scantxoutset + from mmgen.proto.btc.misc import scantxoutset res = await scantxoutset( rpc, [f'addr({addr})' for addr in addrs] ) if not res['success']: @@ -138,7 +138,7 @@ async def main(req_addrs): addr = re.match('addr\((.*?)\)',unspent['desc'])[1] addr_data[addr].append(unspent) else: - from mmgen.base_proto.bitcoin.tx.base import scriptPubKey2addr + from mmgen.proto.btc.tx.base import scriptPubKey2addr for unspent in sorted(res['unspents'],key=lambda x: x['height']): addr = scriptPubKey2addr( proto, unspent['scriptPubKey'] )[0] addr_data[addr].append(unspent) @@ -165,6 +165,6 @@ if len(cmd_args) < 1: from mmgen.obj import CoinTxID,Int try: - run_session(main(cmd_args)) + async_run(main(cmd_args)) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index a7dff8d..117da98 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -176,4 +176,4 @@ async def main(): m.finalize_output() -run_session(main()) +async_run(main()) diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 276c870..e5f2f00 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -21,6 +21,7 @@ mmnode-feeview: Visualize the fee structure of a node’s mempool """ from mmgen.common import * +from mmgen.util2 import int2bytespec,parse_bytespec min_prec,max_prec,dfl_prec = (0,6,4) fee_brackets = [ @@ -213,10 +214,8 @@ async def main(): log(mempool,'mempool.json') data = create_data(proto.coin_amt,mempool) - - (do_pager if opt.pager else print)( + stdout_or_pager( '\n'.join(gen_header(c.host,await c.call('getblockcount'))) + '\n' + - '\n'.join(gen_body(data)) - ) + '\n'.join(gen_body(data)) + '\n' ) -run_session(main()) +async_run(main()) diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index 423df0e..c804d8f 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -179,4 +179,4 @@ async def main(): else: print_current_stats() -run_session(main()) +async_run(main()) diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index b1fb919..fd86983 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -68,6 +68,6 @@ async def main(): rs,ss,ts = r,s,t try: - run_session(main()) + async_run(main()) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index 87df582..b775b29 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -171,7 +171,7 @@ ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = ( '\033[J','\033[K','\033[H','\033[?25l','\033[?25h') try: - run_session(main()) + async_run(main()) except: from subprocess import run run(['stty','sane']) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 9a32c04..a25e884 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -52,6 +52,7 @@ opts_data = { -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column -U, --print-curl Print cURL command to standard output and exit +-v, --verbose Be more verbose -w, --wide Display all optional columns (equivalent to -punT) -x, --proxy=P Connect via proxy ‘P’. Set to the empty string to disable. Consult the curl manpage for --proxy usage. diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index 3c7935d..2672a11 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -91,4 +91,4 @@ msgs = msg_data['quiet' if opt.quiet else 'normal'] if len(cmd_args) != 1: die(1,'One transaction ID must be specified') -sys.exit(run_session(main(cmd_args[0]))) +sys.exit(async_run(main(cmd_args[0]))) diff --git a/setup.cfg b/setup.cfg index 628ebe0..f3d9a66 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.2.dev12 + mmgen>=13.3.dev6 packages = mmgen_node_tools diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index f0c11d7..53dd32a 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -8,6 +8,16 @@ # https://github.com/mmgen/mmgen-node-tools # https://gitlab.com/mmgen/mmgen-node-tools +# Testing status +# mmnode-addrbal OK +# mmnode-blocks-info - +# mmnode-feeview - +# mmnode-halving-calculator OK +# mmnode-netrate - +# mmnode-peerblocks - +# mmnode-ticker OK +# mmnode-txfind - + list_avail_tests() { echo "AVAILABLE TESTS:" echo " unit - unit tests" diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 0d39c7e..7801f64 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -257,7 +257,7 @@ class TestSuiteScripts(TestSuiteBase): ['--btc','--wide','--portfolio','--elapsed'], [ 'PRICES', - r'BITCOIN 23,368.86 \+6.05 -1.87 33 hours, 2 minutes ago', + r'BITCOIN 23,368.86 \+6.05 -1.87 1 day 9 hours 2 minutes ago', 'PORTFOLIO', r'BITCOIN 28,850.44 \+6.05 -1.87 1.23456789' ]) @@ -271,8 +271,8 @@ class TestSuiteScripts(TestSuiteBase): r'BTC \(BITCOIN\) = 23,368.86 USD', 'Offered price differs from spot by -3.72%', 'SPOT PRICE OFFERED PRICE UPDATED', - 'BITCOIN 2.00000000 1.92563954 33 hours, 2 minutes ago ' + - 'US DOLLAR 46,737.71911598 45,000.00000000 33 hours, 2 minutes ago', + 'BITCOIN 2.00000000 1.92563954 1 day 9 hours 2 minutes ago ' + + 'US DOLLAR 46,737.71911598 45,000.00000000 1 day 9 hours 2 minutes ago', ]) def ticker16(self): From 8ca7f43ae87c63678b0c6acdafc0c67c75f7c013 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 21 Oct 2022 10:39:02 +0000 Subject: [PATCH 080/175] mmnode-blocks-info: support BCH; add test --- mmgen_node_tools/BlocksInfo.py | 89 +++++++++++++++++----------- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_blocks_info.py | 7 ++- setup.cfg | 2 +- test/test-release.d/cfg.sh | 2 +- test/test_py_d/ts_regtest.py | 50 ++++++++++++++-- test/unit_tests_d/ut_BlocksInfo.py | 5 ++ 7 files changed, 113 insertions(+), 44 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 28395b4..27dfa08 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -23,7 +23,6 @@ mmgen_node_tools.BlocksInfo: Display information about a block or range of block import re,json from collections import namedtuple from time import strftime,gmtime -from decimal import Decimal from mmgen.common import * from mmgen.rpc import json_encoder @@ -46,14 +45,14 @@ class BlocksInfo: 'totalfee': bf( 'tf', 'bs', '{:>10}', '', 'Total Fee', 'totalfee', None ), 'size': bf( None, 'bs', '{:>7}', '', 'Size', 'total_size', None ), 'weight': bf( None, 'bs', '{:>7}', '', 'Weight', 'total_weight', None ), - 'fee90': bf( None, 'bs', '{:>3}', '90%', 'Fee', 'feerate_percentiles', 4 ), - 'fee75': bf( None, 'bs', '{:>3}', '75%', 'Fee', 'feerate_percentiles', 3 ), - 'fee50': bf( None, 'bs', '{:>3}', '50%', 'Fee', 'feerate_percentiles', 2 ), - 'fee25': bf( None, 'bs', '{:>3}', '25%', 'Fee', 'feerate_percentiles', 1 ), - 'fee10': bf( None, 'bs', '{:>3}', '10%', 'Fee', 'feerate_percentiles', 0 ), - 'fee_max': bf( None, 'bs', '{:>5}', 'Max', 'Fee', 'maxfeerate', None ), - 'fee_avg': bf( None, 'bs', '{:>3}', 'Avg', 'Fee', 'avgfeerate', None ), - 'fee_min': bf( None, 'bs', '{:>3}', 'Min', 'Fee', 'minfeerate', None ), + 'fee90': bf( 'fe', 'bs', '{:>3}', '90%', 'Fee', 'feerate_percentiles', 4 ), + 'fee75': bf( 'fe', 'bs', '{:>3}', '75%', 'Fee', 'feerate_percentiles', 3 ), + 'fee50': bf( 'fe', 'bs', '{:>3}', '50%', 'Fee', 'feerate_percentiles', 2 ), + 'fee25': bf( 'fe', 'bs', '{:>3}', '25%', 'Fee', 'feerate_percentiles', 1 ), + 'fee10': bf( 'fe', 'bs', '{:>3}', '10%', 'Fee', 'feerate_percentiles', 0 ), + 'fee_max': bf( 'fe', 'bs', '{:>5}', 'Max', 'Fee', 'maxfeerate', None ), + 'fee_avg': bf( 'fe', 'bs', '{:>3}', 'Avg', 'Fee', 'avgfeerate', None ), + 'fee_min': bf( 'fe', 'bs', '{:>3}', 'Min', 'Fee', 'minfeerate', None ), 'nTx': bf( None, 'bh', '{:>5}', '', ' nTx ', 'nTx', None ), 'inputs': bf( None, 'bs', '{:>5}', 'In- ', 'puts', 'ins', None ), 'outputs': bf( None, 'bs', '{:>5}', 'Out-', 'puts', 'outs', None ), @@ -101,24 +100,6 @@ class BlocksInfo: noindent_stats = ['col_avg'] avg_stats_skip = {'block', 'hash', 'date', 'version','miner'} - stats_deps = { - 'avg': set(fields) - avg_stats_skip, - 'col_avg':set(fields) - avg_stats_skip, - 'mini_avg':{'interval','size','weight'}, - 'total': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, - 'range': {}, - 'diff': {}, - } - - fmt_funcs = { - 'da': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), - 'td': lambda arg: ( - '-{:02}:{:02}'.format(abs(arg)//60,abs(arg)%60) if arg < 0 else - ' {:02}:{:02}'.format(arg//60,arg%60) ), - 'tf': lambda arg: '{:.8f}'.format(arg * Decimal('0.00000001')), - 'su': lambda arg: str(arg * Decimal('0.00000001')).rstrip('0').rstrip('.'), - 'di': lambda arg: '{:.2e}'.format(arg), - } range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) @@ -186,8 +167,44 @@ class BlocksInfo: self.opt = opt self.tip = rpc.blockcount + from_satoshi = self.rpc.proto.coin_amt.satoshi + to_satoshi = 1 / from_satoshi + self.block_list,self.first,self.last,self.step = parse_cmd_args() + have_segwit = self.rpc.info('segwit_is_active') + + if not have_segwit: + del self.fields['weight'] + self.dfl_fields = tuple(f for f in self.dfl_fields if f != 'weight') + + self.stats_deps = { + 'avg': set(self.fields) - self.avg_stats_skip, + 'col_avg': set(self.fields) - self.avg_stats_skip, + 'mini_avg': {'interval','size'} | ({'weight'} if have_segwit else set()), + 'total': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, + 'range': {}, + 'diff': {}, + } + + self.fmt_funcs = { + 'da': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), + 'td': lambda arg: ( + '-{:02}:{:02}'.format(abs(arg)//60,abs(arg)%60) if arg < 0 else + ' {:02}:{:02}'.format(arg//60,arg%60) ), + 'tf': lambda arg: '{:.8f}'.format(arg * from_satoshi), + 'su': lambda arg: str(arg * from_satoshi).rstrip('0').rstrip('.'), + 'fe': lambda arg: str(arg), + 'di': lambda arg: '{:.2e}'.format(arg), + } + + if g.coin == 'BCH': + self.fmt_funcs.update({ + 'su': lambda arg: str(arg).rstrip('0').rstrip('.'), + 'fe': lambda arg: str(int(arg * to_satoshi)), + 'tf': lambda arg: '{:.8f}'.format(arg), + }) + self.fnames = tuple( [f for f in self.fields if self.fields[f].src == 'bh' or f == 'interval'] if opt.header_info else get_fields() if opt.fields else @@ -222,7 +239,8 @@ class BlocksInfo: self.bs_keys = set( [v.key1 for v in self.fvals if v.src == 'bs'] + - ['total_size','total_weight'] + ['total_size'] + + (['total_weight'] if have_segwit else []) ) if 'miner' in self.fnames: @@ -406,7 +424,8 @@ class BlocksInfo: await self.rpc.call('getblockstats',hdr['hash'],list(self.bs_keys)) ) self.total_bytes += bs['total_size'] - self.total_weight += bs['total_weight'] + if 'total_weight' in bs: + self.total_weight += bs['total_weight'] blk_data['bs'] = bs if 'miner' in self.fnames: @@ -506,7 +525,7 @@ class BlocksInfo: async def create_diff_stats(self): c = self.rpc - rel = self.tip % 2016 + rel = self.tip % self.rpc.proto.diff_adjust_interval tip_hdr = ( self.hdrs[-1] if self.hdrs[-1]['height'] == self.tip else @@ -520,14 +539,14 @@ class BlocksInfo: sample_blks = rel bdi = ( tip_hdr['time'] - rel_hdr['time'] ) / rel else: - sample_blks = min_sample_blks + sample_blks = min(min_sample_blks,self.tip) start_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-sample_blks)) diff_adj = float(tip_hdr['difficulty'] / start_hdr['difficulty']) time1 = rel_hdr['time'] - start_hdr['time'] time2 = tip_hdr['time'] - rel_hdr['time'] bdi = ((time1 * diff_adj) + time2) / sample_blks - rem = 2016 - rel + rem = self.rpc.proto.diff_adjust_interval - rel return ( 'difficulty', ( 'Difficulty Statistics:', @@ -572,13 +591,15 @@ class BlocksInfo: spec_convs = { 'interval': spec_conv(0, lambda arg: secs_to_ms(arg)), 'utxo_inc': spec_conv(-1, '{:<+}'), - 'mb_per_hour': spec_conv(0, '{:.4f}'), + 'mb_per_hour': spec_conv(0, '{}'), }, spec_vals = ( spec_val( 'mb_per_hour', 'MB/hr', 'interval', lambda values: 'bs' in self.deps, - lambda values: (self.total_bytes / 10000) / (self.total_solve_time / 36) + lambda values: ( + '{:.4f}'.format((self.total_bytes / 10000) / (self.total_solve_time / 36)) + if self.total_solve_time else 'N/A' ), ), ) ) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 58dfba3..2d22ff0 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev7 +3.1.dev8 diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index 117da98..9295cd0 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -148,6 +148,11 @@ This program requires a txindex-enabled daemon for correct operation. f = fmt_list(BlocksInfo.fields,fmt='bare'), s = fmt_list(BlocksInfo.all_stats,fmt='bare'), p = g.prog_name ) + }, + 'code': { + 'notes': lambda proto,s: s.format( + adj_interval = proto.diff_adjust_interval, + ) } } @@ -158,7 +163,7 @@ async def main(): from mmgen.protocol import init_proto_from_opts from mmgen.rpc import rpc_init - proto = init_proto_from_opts() + proto = init_proto_from_opts(need_amt=True) cls = JSONBlocksInfo if opt.json else BlocksInfo diff --git a/setup.cfg b/setup.cfg index f3d9a66..327cad7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev6 + mmgen>=13.3.dev8 packages = mmgen_node_tools diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 53dd32a..59db1dd 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -10,7 +10,7 @@ # Testing status # mmnode-addrbal OK -# mmnode-blocks-info - +# mmnode-blocks-info OK # mmnode-feeview - # mmnode-halving-calculator OK # mmnode-netrate - diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 4fb3eec..d6b037e 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -45,6 +45,7 @@ class TestSuiteRegtest(TestSuiteBase): ('subgroup.halving_calculator', []), ('subgroup.fund_addrbal', []), ('subgroup.addrbal', ['fund_addrbal']), + ('subgroup.blocks_info', ['addrbal']), ('stop', 'stopping regtest daemon'), ) cmd_subgroups = { @@ -74,7 +75,14 @@ class TestSuiteRegtest(TestSuiteBase): ('addrbal_nobal3', 'getting address balances (one null balance)'), ('addrbal_nobal3_tabular1', 'getting address balances (one null balance, tabular output)'), ('addrbal_nobal3_tabular2', 'getting address balances (one null balance, tabular, show first block)'), - ) + ), + 'blocks_info': ( + "'mmnode-blocks-info' script", + ('blocks_info1', "blocks-info (--help)"), + ('blocks_info2', "blocks-info (no args)"), + ('blocks_info3', "blocks-info +100"), + ('blocks_info4', "blocks-info --miner-info --fields=all --stats=all +1"), + ), } def __init__(self,trunner,cfgs,spawn): @@ -86,11 +94,6 @@ class TestSuiteRegtest(TestSuiteBase): self.proto = init_proto(self.proto.coin,network='regtest',need_amt=True) self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) - os.environ['MMGEN_TEST_SUITE_REGTEST'] = '1' - - def __del__(self): - os.environ['MMGEN_TEST_SUITE_REGTEST'] = '' - def setup(self): stop_test_daemons(self.proto.network_id,force=True,remove_datadir=True) from shutil import rmtree @@ -207,6 +210,41 @@ class TestSuiteRegtest(TestSuiteBase): self.addrs[3] + ' - - - -', ]) + def blocks_info(self,args,expect_list): + t = self.spawn('mmnode-blocks-info',args) + t.match_expect_list(expect_list) + return t + + def blocks_info1(self): + return self.blocks_info( args1 + ['--help'], ['USAGE:','OPTIONS:']) + + def blocks_info2(self): + return self.blocks_info( args1, [ + 'Current height: 396', + ]) + + def blocks_info3(self): + return self.blocks_info( args1 + ['+100'], [ + 'Range: 297-396', + 'Current height: 396', + 'Next diff adjust: 2016' + ]) + + def blocks_info4(self): + n1,i1,o1,n2,i2,o2 = (2,1,3,6,3,9) if g.coin == 'BCH' else (2,1,4,6,3,12) + return self.blocks_info( args1 + ['--miner-info','--fields=all','--stats=all','+3'], [ + 'Averages', + f'nTx: {n1}', + f'Inputs: {i1}', + f'Outputs: {o1}', + 'Totals', + f'nTx: {n2}', + f'Inputs: {i2}', + f'Outputs: {o2}', + 'Current height: 396', + 'Next diff adjust: 2016' + ]) + def stop(self): if opt.no_daemon_stop: self.spawn('',msg_only=True) diff --git a/test/unit_tests_d/ut_BlocksInfo.py b/test/unit_tests_d/ut_BlocksInfo.py index 1448e97..e83cca0 100755 --- a/test/unit_tests_d/ut_BlocksInfo.py +++ b/test/unit_tests_d/ut_BlocksInfo.py @@ -65,6 +65,11 @@ fields_vecs = ( class dummyRPC: blockcount = tip + def info(self,arg): + return True + class proto: + class coin_amt: + satoshi = 0.00000001 class dummyOpt: fields = None From 38a55b57f127197229de9f47d47104d7ff74cc87 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 24 Oct 2022 17:38:11 +0000 Subject: [PATCH 081/175] minor fixes --- mmgen_node_tools/main_blocks_info.py | 19 +++++++++---------- test/test_py_d/ts_misc.py | 4 ++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index 9295cd0..eb12a5c 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -74,13 +74,13 @@ block number is expected. If the requested range ends at the current chain tip, an estimate of the next difficulty adjustment is also displayed. The estimate is based on the average -Block Discovery Interval from the beginning of the current 2016-block period. +Block Discovery Interval from the beginning of the current {I}-block period. All fee fields except for 'totalfee' are in satoshis per virtual byte. -AVAILABLE FIELDS: {f} +AVAILABLE FIELDS: {F} -AVAILABLE STATS: {s} +AVAILABLE STATS: {S} EXAMPLES: @@ -110,7 +110,7 @@ EXAMPLES: $ {p} -o block,date,hash 245798 170 624044 Display every difficulty adjustment from Genesis Block to chain tip: - $ {p} -o +difficulty 0-cur+2016 + $ {p} -o +difficulty 0-cur+{I} Display roughly a block a day over the last two weeks. Note that multiplication is allowed in the nBlocks spec: @@ -144,14 +144,13 @@ EXAMPLES: $ {p} --rpc-backend=aio -H +1000 This program requires a txindex-enabled daemon for correct operation. -""".format( - f = fmt_list(BlocksInfo.fields,fmt='bare'), - s = fmt_list(BlocksInfo.all_stats,fmt='bare'), - p = g.prog_name ) - }, +""" }, 'code': { 'notes': lambda proto,s: s.format( - adj_interval = proto.diff_adjust_interval, + I = proto.diff_adjust_interval, + F = fmt_list(BlocksInfo.fields,fmt='bare'), + S = fmt_list(BlocksInfo.all_stats,fmt='bare'), + p = g.prog_name, ) } } diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 7801f64..ee41377 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -17,6 +17,8 @@ from ..include.common import * from .common import * from .ts_base import * +refdir = os.path.join('test','ref','ticker') + class TestSuiteHelp(TestSuiteBase): 'help, info and usage screens' networks = ('btc','ltc','bch') @@ -50,8 +52,6 @@ class TestSuiteHelp(TestSuiteBase): def longhelpscreens(self): return self.helpscreens(arg='--longhelp',expect='USAGE:.*LONG OPTIONS:') -refdir = os.path.join('test','ref','ticker') - class TestSuiteScripts(TestSuiteBase): 'scripts not requiring a coin daemon' networks = ('btc',) From 11dede26ebdb9dde95284c68493657dd4f853da8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 24 Oct 2022 17:38:12 +0000 Subject: [PATCH 082/175] mmnode-peerblocks: reimplement, add test Testing/demo: $ test/test.py -e main.peerblocks --- mmgen_node_tools/PeerBlocks.py | 155 ++++++++ mmgen_node_tools/PollDisplay.py | 73 ++++ mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_peerblocks.py | 143 +------ setup.cfg | 2 +- test/init.sh | 3 +- .../fakemods/mmgen_node_tools/PeerBlocks.py | 355 ++++++++++++++++++ test/test-release.d/cfg.sh | 13 +- test/test_py_d/cfg.py | 2 + test/test_py_d/ts_main.py | 95 +++++ 10 files changed, 703 insertions(+), 140 deletions(-) create mode 100755 mmgen_node_tools/PeerBlocks.py create mode 100755 mmgen_node_tools/PollDisplay.py create mode 100644 test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py create mode 100755 test/test_py_d/ts_main.py diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py new file mode 100755 index 0000000..a6027ca --- /dev/null +++ b/mmgen_node_tools/PeerBlocks.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen +# https://gitlab.com/mmgen/mmgen + +""" +mmgen_node_tools.PeerBlocks: List blocks in flight, disconnect stalling nodes +""" + +import asyncio +from collections import namedtuple +from mmgen.globalvars import g +from mmgen.util import msg,msg_r,is_int +from mmgen.term import get_term,get_terminal_size,get_char +from mmgen.ui import line_input +from .PollDisplay import PollDisplay + +RED,RESET = ('\033[31m','\033[0m') +COLORS = ['\033[38;5;%s;1m' % c for c in list(range(247,256)) + [231]] +ERASE_ALL,CUR_HOME = ('\033[J','\033[H') +CUR_HIDE,CUR_SHOW = ('\033[?25l','\033[?25h') +term = None + +class Display(PollDisplay): + + poll_secs = 2 + + def __init__(self): + super().__init__() + + global term,term_width + if not term: + term = get_term() + term.init(noecho=True) + term_width = g.columns or get_terminal_size()[0] + msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) + + async def get_info(self,rpc): + return await rpc.call('getpeerinfo') + + def display(self,count): + msg_r( + CUR_HOME + + (ERASE_ALL if count == 1 else '') + + 'CONNECTED PEERS ({a}) {b} - poll {c}'.format( + a = len(self.info), + b = self.desc, + c = count ).ljust(term_width)[:term_width] + + '\n' + + ('\n'.join(self.gen_display()) + '\n' if self.info else '') + + ERASE_ALL + + f"Type a peer number to disconnect, 'q' to quit, or any other key for {self.other_desc} display:" + + '\b' ) + + async def disconnect_node(self,rpc,addr): + return await rpc.call('disconnectnode',addr) + + def get_input(self): + s = get_char(immed_chars='q0123456789',prehold_protect=False,num_chars=1) + if not is_int(s): + return s + with self.info_lock: + msg('') + term.reset() + ret = line_input('peer number> ',insert_txt=s,hold_protect=False) + term.init(noecho=True) + self.enable_display = False # prevent display from updating before process_input() + return ret + + async def process_input(self,rpc): + + ids = tuple(str(i['id']) for i in self.info) + ret = False + msg_r(CUR_HIDE) + + if self.input in ids: + from mmgen.exception import RPCFailure + addr = self.info[ids.index(self.input)]['addr'] + try: + await self.disconnect_node(rpc,addr) + except RPCFailure: + msg_r(f'Unable to disconnect peer {self.input} ({addr})') + else: + msg_r(f'Disconnecting peer {self.input} ({addr})') + await asyncio.sleep(1) + elif self.input and is_int(self.input[0]): + msg_r(f'{self.input}: invalid peer number ') + await asyncio.sleep(0.5) + else: + ret = True + + msg_r(CUR_SHOW) + return ret + +class BlocksDisplay(Display): + + desc = 'Blocks in Flight' + other_desc = 'address' + + def gen_display(self): + + pd = namedtuple('peer_data',['id','blks_data','blks_width']) + bd = namedtuple('block_datum',['num','disp']) + + def gen_block_data(): + global min_height + min_height = None + for d in self.info: + if d.get('inflight'): + blocks = d['inflight'] + min_height = min(blocks) if not min_height else min(min_height,min(blocks)) + line = ' '.join(map(str,blocks))[:blks_field_width] + blocks_disp = line.split() + yield pd( + d['id'], + [bd(blocks[i],blocks_disp[i]) for i in range(len(blocks_disp))], + len(line) ) + else: + yield pd(d['id'],[],0) + + def gen_line(peer_data): + for blk in peer_data.blks_data: + yield (RED if blk.num == min_height else COLORS[blk.num % 10]) + blk.disp + RESET + + id_width = max(2, max(len(str(i['id'])) for i in self.info)) + blks_field_width = term_width - 2 - id_width + fs = '{:>%s}: {}' % id_width + + # we must iterate through all data to get 'min_height' before calling gen_line(): + for peer_data in tuple(gen_block_data()): + yield fs.format( + peer_data.id, + ' '.join(gen_line(peer_data)) + ' ' * (blks_field_width - peer_data.blks_width) ) + +class PeersDisplay(Display): + + desc = 'Address Menu' + other_desc = 'blocks' + + def gen_display(self): + id_width = max(2, max(len(str(i['id'])) for i in self.info)) + addr_width = max(len(str(i['addr'])) for i in self.info) + for d in self.info: + yield '{a:>{A}}: {b:{B}} {c}'.format( + a = d['id'], + A = id_width, + b = d['addr'], + B = addr_width, + c = d['subver'] + ).ljust(term_width)[:term_width] diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py new file mode 100755 index 0000000..06b7d7f --- /dev/null +++ b/mmgen_node_tools/PollDisplay.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen +# https://gitlab.com/mmgen/mmgen + +""" +mmgen_node_tools.PollDisplay: update and display RPC data; get input from user +""" + +import sys,threading +from mmgen.util import msg +from mmgen.term import get_char + +class PollDisplay(): + + info = None + input = None + poll_secs = 1 + + def __init__(self): + self.info_lock = threading.Lock() # self.info accessed by 2 threads + self.display_kill_flag = threading.Event() + + def get_input(self): + return get_char(immed_chars='q',prehold_protect=False,num_chars=1) + + async def process_input(self,rpc): + return True + + async def run(self,rpc): + + async def do_display(): + with self.info_lock: + self.info = None + self.input = None + self.enable_display = True + count = 1 + while True: + with self.info_lock: + if self.enable_display: + self.info = await self.get_info(rpc) + self.display(count) + if self.display_kill_flag.wait(self.poll_secs): + self.display_kill_flag.clear() + return + count += 1 + + async def process_input(): + if self.input == None: + sys.exit(1) + elif self.input == 'q': + msg('') + sys.exit(0) + elif self.info: + if await self.process_input(rpc): + return True + else: + return True + + def get_input(): + self.input = self.get_input() + self.display_kill_flag.set() + + while True: + threading.Thread(target=get_input,daemon=True).start() + await do_display() + if await process_input(): + break diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 2d22ff0..1ebddf5 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev8 +3.1.dev9 diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index b775b29..9e6a0ec 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -20,9 +20,8 @@ mmnode-peerblocks: List blocks in flight, disconnect stalling nodes """ -import asyncio -from collections import namedtuple -from mmgen.common import * +from mmgen.opts import init +from mmgen.util import async_run opts_data = { 'text': { @@ -35,120 +34,9 @@ opts_data = { } } -def format_peer_info(peerinfo): - - pd = namedtuple('peer_data',['id','blocks_data','screen_width']) - - def gen_peers(peerinfo): - global min_height - min_height = None - for d in peerinfo: - if 'inflight' in d and d['inflight']: - blocks = d['inflight'] - if not min_height or min_height > blocks[0]: - min_height = blocks[0] - line = ' '.join(map(str,blocks))[:term_width - 2 - id_width] - blocks_disp = line.split() - yield pd( - d['id'], - [(blocks[i],blocks_disp[i]) for i in range(len(blocks_disp))], - len(line) ) - else: - yield pd( d['id'], [], 0 ) - - def gen_line(peer): - if peer.blocks_data: - if peer.blocks_data[0][0] == min_height: - yield RED + peer.blocks_data[0][1] + RESET - peer.blocks_data.pop(0) - for blk,blk_disp in peer.blocks_data: - yield COLORS[blk % 10] + blk_disp + RESET - - id_width = max(2,max(len(str(i['id'])) for i in peerinfo)) - - for peer in tuple(gen_peers(peerinfo)): - line = '{:>{iw}}: {}'.format( - peer.id, - ' '.join(gen_line(peer)), - iw = id_width ) - yield line + ' ' * (term_width - 2 - id_width - peer.screen_width) - -def test_format(): - import json - info = json.loads(open('test_data/peerinfo.json').read()) - print('\n'.join(format_peer_info(info)) + '\n') - sys.exit(0) - -async def inflight_display(rpc): - - count = 1 - while True: - info = await rpc.call('getpeerinfo') - msg_r( - CUR_HOME - + f'ACTIVE PEERS ({len(info)}) Blocks in Flight - poll {count} \n' - + ('\n'.join(format_peer_info(info)) + '\n' if info else '') - + ERASE_ALL + 'Hit ENTER for disconnect menu: ' ) - await asyncio.sleep(2) - count += 1 - -async def do_inflight(rpc): - task = asyncio.ensure_future(inflight_display(rpc)) # Python 3.7+: create_task() - from select import select - - while True: - key = select([sys.stdin], [], [], 0.1)[0] - if key: - sys.stdin.read(1) - task.cancel() - break - await asyncio.sleep(0.1) - - try: - await task - except asyncio.CancelledError: - pass - -async def do_disconnect_menu(rpc): - - while True: - peerinfo = await rpc.call('getpeerinfo') - ids = [str(d['id']) for d in peerinfo] - - msg(f'{CUR_HOME}ACTIVE PEERS ({len(peerinfo)}) Disconnect Menu' + ' '*16) - - def gen_peerinfo(): - for d in peerinfo: - line = f"{d['id']:>{id_width}}: {d['addr']:30} {d['subver']}" - yield line + ' ' * (term_width - len(line)) - - if peerinfo: - id_width = max(2,max(len(str(i['id'])) for i in peerinfo)) - msg('\n'.join(gen_peerinfo())) - - msg_r(ERASE_ALL) - reply = input("Type peer number to disconnect, ENTER to quit menu, 'u' to update peer list: ") - - if reply == '': - return - elif reply == 'u': - msg(f'Updating peer list') - await asyncio.sleep(0.5) - elif reply in ids: - addr = peerinfo[ids.index(reply)]['addr'] - msg(f'Disconnecting peer {reply} ({addr})') - try: - await rpc.call('disconnectnode',addr) - except RPCFailure: - msg(f'Unable to disconnect peer {addr}') - await asyncio.sleep(1.5) - else: - msg(f'{reply!r}: invalid peer number') - await asyncio.sleep(0.5) - async def main(): - msg_r(CUR_HOME+ERASE_ALL) + init(opts_data) from mmgen.protocol import init_proto_from_opts proto = init_proto_from_opts() @@ -156,23 +44,12 @@ async def main(): from mmgen.rpc import rpc_init rpc = await rpc_init(proto) + from .PeerBlocks import BlocksDisplay,PeersDisplay + blocks = BlocksDisplay() + peers = PeersDisplay() + while True: - await do_inflight(rpc) - await do_disconnect_menu(rpc) + await blocks.run(rpc) + await peers.run(rpc) -opts.init(opts_data) - -from mmgen.term import get_terminal_size -term_width = get_terminal_size()[0] - -RED,RESET = ('\033[31m','\033[0m') -COLORS = ['\033[38;5;%s;1m' % c for c in (247,248,249,250,251,252,253,254,255,231)] -ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = ( - '\033[J','\033[K','\033[H','\033[?25l','\033[?25h') - -try: - async_run(main()) -except: - from subprocess import run - run(['stty','sane']) - msg('') +async_run(main()) diff --git a/setup.cfg b/setup.cfg index 327cad7..208e03b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev8 + mmgen>=13.3.dev9 packages = mmgen_node_tools diff --git a/test/init.sh b/test/init.sh index 0979c76..a8d03a9 100755 --- a/test/init.sh +++ b/test/init.sh @@ -59,7 +59,8 @@ create_dir_links() { create_test_links() { sources=' test/include - test/overlay + test/overlay/__init__.py + test/overlay/fakemods/mmgen test/__init__.py test/test.py test/unit_tests.py diff --git a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py new file mode 100644 index 0000000..0170a2f --- /dev/null +++ b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py @@ -0,0 +1,355 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen +# https://gitlab.com/mmgen/mmgen + +""" +fakemods.mmgen_node_tools.PeerBlocks: List blocks in flight, disconnect stalling nodes - test data +""" + +from .PeerBlocks_orig import * + +class fake_data: + + gp_counter = 0 + dn_counter = 0 + poll_secs = 0.5 + + addresses = """ + 0 3tokm3se4omuhhqgaxiam3yhl474ekbg5xb45kydfno57k7ooapitogv.onion:8333 /Satoshi:24.0.0/ + 1 2rysrq5f2ec4zpnsohyb6mc6l6unosjwaydjefttt34ouogdpqtlo7a5.onion:8333 /Satoshi:0.21.1/ + 2 5bkcnkrihwowji7zwko7ddtpcetz6572zbwdh6aguyt2yz3wgvzqa3ne.onion:8333 /Satoshi:23.0.0/ + 3 ukzy7yvako4z6tvtocsm33yvdxwyx567ioqwfezzewlw2syawzkgrw64.onion:8333 /Satoshi:22.0.0/ + 4 b3r3hhxiauujwj7afmji63forvnd7uhq7ov5x2n7w7hvrhutoq3lhul7.onion:8333 /Satoshi:24.0.0/ + 5 ksownpb4zk4vuyfiowgyvz3kzc2djeiurnnh7pyal3if54n5wzup3afm.onion:8333 /Satoshi:22.0.0/ + 6 xh6vhun75w5y2s2xze2n4rarnduqvwowwthhyiaefly3df2t4guwvjrl.onion:8333 /Satoshi:22.0.0/ + 7 raqbaqcsoldmk7sn6fgoh5bicrlzjdoexz5b2z7jemxb2u6z62vpxto2.onion:8333 /Satoshi:23.0.0/ + 8 5gu3m7jr4tog5tuyisek2fxl5erjgasrxzarnii6cpemmvqhate36hrg.onion:8333 /Satoshi:25.1.0(Aldebaran 3.2.1)/ + 9 ayiyxqgckls4fmzrbbh35ppquhstzhm453xezvzmuoglhnibrxu5ebos.onion:8333 /Satoshi:22.0.0/ + 10 3zmyku3xqrb4mebi2r6l65l5ovmf5yjou2zeywgz5g2ehiqbclppwy6t.onion:8333 /Satoshi:23.0.0/ + 11 ljyfsi4wponyw52o5y75bn6oxktw7kiaes3vdtzf26scapei2ed6yw4m.onion:8333 /Satoshi:0.21.0/ + 12 n5hsupofuhis2xfx7neahntqjl2l3jjp7ait6jsegrp4utj7573qgqsp.onion:8333 /Satoshi:22.0.0/ + 13 klw3dgcuzr4etnkgw5ysnukfl46urhnqrlvjt53v544p32o7acakyps4.onion:8333 /Satoshi:23.0.0/ + 14 icmdqoko4nnqp4aiamiqihbvqlumqrmidyb7n54jdif2uki3qkprwsvd.onion:8333 /Satoshi:24.1.0/ + """ + + # connected peers for each iteration + iterations = """ + 01 0 1 2 3 4 5 6 7 8 + 02 0 1 2 3 4 5 6 7 + 03 0 1 2 3 4 5 6 7 8 + 04 0 2 3 4 5 6 7 8 9 + 05 0 2 3 4 5 6 7 8 9 + 06 0 2 3 4 5 6 7 8 9 10 + 07 0 2 3 4 5 6 7 8 9 10 + 08 0 2 3 4 5 6 7 8 9 10 + 09 0 2 3 4 5 6 7 8 9 10 + 10 0 2 3 4 5 6 7 8 9 10 + 11 2 3 4 5 6 7 8 9 10 + 12 2 3 4 5 6 7 8 9 10 + 13 2 3 4 5 6 7 8 9 10 + 14 2 3 4 5 6 7 8 9 10 + 15 2 3 4 5 6 7 8 9 10 12 + 16 2 3 4 5 6 7 8 9 10 12 + 17 2 3 4 5 6 7 8 9 10 12 + 18 2 3 4 5 6 7 8 9 10 12 + 19 2 3 5 6 7 8 9 10 12 + 20 2 3 5 6 7 8 9 10 12 + 21 2 3 5 6 7 8 9 10 12 + 22 2 3 5 6 7 8 9 10 12 + """ + + # blocks in flight for each iteration for each peer + blocks = { + '0': """ + 01 6917 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 + 02 6917 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 + 03 6917 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 + 04 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 + 05 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 + 06 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 + 07 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 + 08 6918 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 + 09 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 7183 + 10 6920 6937 6939 6942 6944 6946 6947 6950 6951 6953 6971 6976 6985 7083 6913 7183 + """, + '1': """ + 01 6906 6907 6908 6909 6910 6911 6913 6915 6919 6940 6948 6982 6988 7000 7023 7062 + 02 6906 6907 6908 6909 6910 6911 6913 6915 6919 6940 6948 6982 6988 7000 7023 7062 + 03 6908 6909 6910 6911 6913 6915 6919 6940 6948 6982 6988 7000 7023 7062 7088 7107 + """, + '2': """ + 01 6990 6994 6996 6997 6999 7017 7018 7019 7020 7021 7025 7059 7060 7061 7082 7084 + 02 6990 6994 6996 6997 6999 7017 7018 7019 7020 7021 7025 7059 7060 7061 7082 7084 + 03 6997 6999 7017 7018 7019 7020 7021 7025 7059 7060 7061 7082 7084 7085 7089 7108 + 04 7018 7019 7020 7021 7025 7059 7060 7061 7082 7084 7085 7089 7108 7113 6909 7000 + 05 7018 7019 7020 7021 7025 7059 7060 7061 7082 7084 7085 7089 7108 7113 6909 7000 + 06 7020 7021 7025 7059 7060 7061 7082 7084 7085 7089 7108 7113 6909 7000 7112 7142 + 07 7025 7059 7060 7061 7082 7084 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 + 08 7059 7060 7061 7082 7084 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 + 09 7060 7061 7082 7084 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 + 10 7061 7082 7084 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 7201 + 11 7084 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 + 12 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 6985 + 13 7085 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 6985 + 14 7089 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 6985 7217 + 15 7108 7113 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 6985 7217 7245 + 16 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 6985 7217 7245 7250 7261 + 17 6909 7000 7112 7142 7164 7173 7179 7190 7201 7209 6942 6985 7217 7245 7250 7261 + 18 7173 7179 7190 7201 7209 6942 6985 7217 7245 7250 7261 7289 7314 7319 7325 7360 + 19 7179 7190 7201 7209 6942 6985 7217 7245 7250 7261 7289 7314 7319 7325 7360 7379 + 20 7190 7201 7209 6942 6985 7217 7245 7250 7261 7289 7314 7319 7325 7360 7379 7188 + 21 7209 6942 6985 7217 7245 7250 7261 7289 7314 7319 7325 7360 7379 7188 7324 7388 + 22 7209 6942 6985 7217 7245 7250 7261 7289 7314 7319 7325 7360 7379 7188 7324 7388 + """, + '3': """ + 01 6974 6977 6978 6979 6983 6986 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 + 02 6974 6977 6978 6979 6983 6986 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 + 03 6977 6978 6979 6983 6986 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 7106 + 04 6979 6983 6986 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 + 05 6979 6983 6986 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 + 06 6986 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 + 07 6991 6992 6993 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 + 08 6992 6993 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 + 09 6993 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 + 10 6995 6998 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 + 11 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 + 12 7022 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 + 13 7024 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 7214 + 14 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 7214 + 15 7058 7063 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 7214 7223 + 16 7081 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 7214 7223 7247 7257 + 17 7106 7119 6982 7136 7161 7170 7177 7186 7195 6937 6946 7214 7223 7247 7257 7272 + 18 7257 7272 7279 7287 7297 7300 7305 7318 7326 7332 7339 7341 7352 7359 7369 7373 + 19 7279 7287 7297 7300 7305 7318 7326 7332 7339 7341 7352 7359 7369 7373 7381 7383 + 20 7287 7297 7300 7305 7318 7326 7332 7339 7341 7352 7359 7369 7373 7381 7383 7259 + 21 7297 7300 7305 7318 7326 7332 7339 7341 7352 7359 7369 7373 7381 7383 7259 7389 + 22 7297 7300 7305 7318 7326 7332 7339 7341 7352 7359 7369 7373 7381 7383 7259 7389 + """, + '4': """ + 01 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 + 02 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 + 03 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 + 04 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 + 05 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 + 06 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 + 07 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 + 08 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 + 09 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 + 10 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 + 11 7008 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 + 12 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 7083 + 13 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 7083 + 14 7009 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 7083 + 15 7010 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 7083 7225 + 16 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 7083 7225 7259 + 17 7011 7012 7013 7014 7015 7016 7080 7109 6915 7160 7172 7188 7205 7083 7225 7259 + 18 6915 7160 7172 7188 7205 7083 7225 7259 7278 7291 7317 7324 7345 7358 7364 7370 + """, + '5': """ + 01 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 + 02 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 + 03 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7086 7110 + 04 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 + 05 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 + 06 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 7143 + 07 7033 7034 7035 7036 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 7143 7167 + 08 7034 7035 7036 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 7143 7167 7180 + 09 7035 7036 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 7143 7167 7180 7185 + 10 7037 7038 7039 7040 7041 7086 7110 7116 6910 7088 7143 7167 7180 7185 7194 7200 + 11 7041 7086 7110 7116 6910 7088 7143 7167 7180 7185 7194 7200 7206 7207 6939 6944 + 12 7116 6910 7088 7143 7167 7180 7185 7194 7200 7206 7207 6939 6944 6950 6951 6953 + 13 6910 7088 7143 7167 7180 7185 7194 7200 7206 7207 6939 6944 6950 6951 6953 7211 + 14 7088 7143 7167 7180 7185 7194 7200 7206 7207 6939 6944 6950 6951 6953 7211 7220 + 15 7143 7167 7180 7185 7194 7200 7206 7207 6939 6944 6950 6951 6953 7211 7220 7224 + 16 7180 7185 7194 7200 7206 7207 6939 6944 6950 6951 6953 7211 7220 7224 7246 7263 + 17 7185 7194 7200 7206 7207 6939 6944 6950 6951 6953 7211 7220 7224 7246 7263 7268 + 18 7220 7224 7246 7263 7268 7275 7283 7296 7308 7321 7330 7335 7336 7349 7353 7371 + 19 7224 7246 7263 7268 7275 7283 7296 7308 7321 7330 7335 7336 7349 7353 7371 6915 + 20 7224 7246 7263 7268 7275 7283 7296 7308 7321 7330 7335 7336 7349 7353 7371 6915 + 21 7268 7275 7283 7296 7308 7321 7330 7335 7336 7349 7353 7371 6915 7385 7391 7392 + 22 7283 7296 7308 7321 7330 7335 7336 7349 7353 7371 6915 7385 7391 7392 7400 7408 + """, + '6': """ + 01 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 + 02 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 + 03 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7087 + 04 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7087 7115 6940 + 05 7049 7050 7051 7052 7053 7054 7055 7056 7057 7087 7115 6940 + 06 7051 7052 7053 7054 7055 7056 7057 7087 7115 6940 7141 + 07 7052 7053 7054 7055 7056 7057 7087 7115 6940 7141 + 08 7054 7055 7056 7057 7087 7115 6940 7141 7176 + 09 7055 7056 7057 7087 7115 6940 7141 7176 + 10 7056 7057 7087 7115 6940 7141 7176 + 11 7057 7087 7115 6940 7141 7176 + 12 7087 7115 6940 7141 7176 + 13 6940 7141 7176 7213 + 14 7141 7176 7213 + 15 7141 7176 7213 + 16 7141 7176 7213 + 17 7141 7176 7213 + 18 7141 7176 7213 + 19 7176 7213 + 20 7176 7213 + 21 7213 + 22 + """, + '7': """ + 01 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 + 02 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 + 03 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 + 04 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 + 05 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 + 06 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 + 07 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 + 08 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 + 09 7071 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 + 10 7072 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 + 11 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 + 12 7073 7074 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 + 13 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 7210 7212 + 14 7075 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 7210 7212 + 15 7076 7077 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 7210 7212 7226 + 16 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 7210 7212 7226 7254 7265 + 17 7078 7079 7111 7118 6988 7139 7166 7175 7187 7198 6913 7210 7212 7226 7254 7265 + 18 6913 7210 7212 7226 7254 7265 7276 7284 7285 7293 7304 7306 7323 7333 7346 7355 + 19 7212 7226 7254 7265 7276 7284 7285 7293 7304 7306 7323 7333 7346 7355 7375 7083 + 20 7212 7226 7254 7265 7276 7284 7285 7293 7304 7306 7323 7333 7346 7355 7375 7083 + 21 7254 7265 7276 7284 7285 7293 7304 7306 7323 7333 7346 7355 7375 7083 7345 7395 + 22 7276 7284 7285 7293 7304 7306 7323 7333 7346 7355 7375 7083 7345 7395 7396 7405 + """, + '8': """ + 03 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 + 04 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7114 7117 6911 6948 7023 + 05 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7114 7117 6911 6948 7023 + 06 7097 7098 7099 7100 7101 7102 7103 7104 7105 7114 7117 6911 6948 7023 7137 7140 + 07 7099 7100 7101 7102 7103 7104 7105 7114 7117 6911 6948 7023 7137 7140 7162 7169 + 08 7100 7101 7102 7103 7104 7105 7114 7117 6911 6948 7023 7137 7140 7162 7169 7178 + 09 7103 7104 7105 7114 7117 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 + 10 7105 7114 7117 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 7193 7199 + 11 7114 7117 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 7193 7199 6920 + 12 7117 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 7193 7199 6920 6971 + 13 7117 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 7193 7199 6920 6971 + 14 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 7193 7199 6920 6971 7222 + 15 6911 6948 7023 7137 7140 7162 7169 7178 7182 7189 7191 7193 7199 6920 6971 7222 + 16 7162 7169 7178 7182 7189 7191 7193 7199 6920 6971 7222 7248 7249 7256 7260 7264 + 17 7169 7178 7182 7189 7191 7193 7199 6920 6971 7222 7248 7249 7256 7260 7264 7270 + 18 7199 6920 6971 7222 7248 7249 7256 7260 7264 7270 7280 7290 7309 7329 7348 7366 + 19 6971 7222 7248 7249 7256 7260 7264 7270 7280 7290 7309 7329 7348 7366 7380 7172 + 20 6971 7222 7248 7249 7256 7260 7264 7270 7280 7290 7309 7329 7348 7366 7380 7172 + 21 7248 7249 7256 7260 7264 7270 7280 7290 7309 7329 7348 7366 7380 7172 7291 7387 + 22 7260 7264 7270 7280 7290 7309 7329 7348 7366 7380 7172 7291 7387 7399 7402 7406 + """, + '9': """ + 04 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 6919 7062 + 05 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 6919 7062 7107 + 06 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 6919 7062 7107 7138 + 07 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 6919 7062 7107 7138 7168 + 08 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 6919 7062 7107 7138 7168 7181 + 09 7127 7128 7129 7130 7131 7132 7133 7134 7135 6919 7062 7107 7138 7168 7181 7184 + 10 7131 7132 7133 7134 7135 6919 7062 7107 7138 7168 7181 7184 7196 7197 7202 7204 + 11 7132 7133 7134 7135 6919 7062 7107 7138 7168 7181 7184 7196 7197 7202 7204 7208 + 12 7134 7135 6919 7062 7107 7138 7168 7181 7184 7196 7197 7202 7204 7208 6947 6976 + 13 7135 6919 7062 7107 7138 7168 7181 7184 7196 7197 7202 7204 7208 6947 6976 7215 + 14 6919 7062 7107 7138 7168 7181 7184 7196 7197 7202 7204 7208 6947 6976 7215 7219 + 15 7062 7107 7138 7168 7181 7184 7196 7197 7202 7204 7208 6947 6976 7215 7219 7244 + 16 7138 7168 7181 7184 7196 7197 7202 7204 7208 6947 6976 7215 7219 7244 7252 7262 + 17 7168 7181 7184 7196 7197 7202 7204 7208 6947 6976 7215 7219 7244 7252 7262 7269 + 18 7215 7219 7244 7252 7262 7269 7286 7301 7313 7328 7337 7340 7351 7362 7367 7372 + 19 7244 7252 7262 7269 7286 7301 7313 7328 7337 7340 7351 7362 7367 7372 7382 7160 + 20 7252 7262 7269 7286 7301 7313 7328 7337 7340 7351 7362 7367 7372 7382 7160 7205 + 21 7301 7313 7328 7337 7340 7351 7362 7367 7372 7382 7160 7205 7278 7370 7390 7393 + 22 7337 7340 7351 7362 7367 7372 7382 7160 7205 7278 7370 7390 7393 7397 7404 7407 + """, + '10': """ + 06 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 + 07 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 + 08 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 + 09 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 + 10 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 7203 + 11 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 7203 + 12 7151 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 7203 7183 + 13 7152 7153 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 7203 7183 7216 + 14 7154 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 7203 7183 7216 7218 7221 + 15 7155 7156 7157 7158 7159 7163 7165 7171 7174 7192 7203 7183 7216 7218 7221 7227 + 16 7158 7159 7163 7165 7171 7174 7192 7203 7183 7216 7218 7221 7227 7251 7258 7267 + 17 7159 7163 7165 7171 7174 7192 7203 7183 7216 7218 7221 7227 7251 7258 7267 7273 + 18 7277 7282 7288 7294 7302 7312 7320 7327 7331 7338 7347 7354 7357 7361 7365 7368 + 19 7288 7294 7302 7312 7320 7327 7331 7338 7347 7354 7357 7361 7365 7368 7376 7378 + 20 7288 7294 7302 7312 7320 7327 7331 7338 7347 7354 7357 7361 7365 7368 7376 7378 + 21 7302 7312 7320 7327 7331 7338 7347 7354 7357 7361 7365 7368 7376 7378 7364 7394 + 22 7327 7331 7338 7347 7354 7357 7361 7365 7368 7376 7378 7364 7394 7401 7403 7410 + """, + '12': """ + 15 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 + 16 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7253 7266 + 17 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7253 7266 7274 + 18 7281 7292 7298 7299 7303 7307 7310 7311 7316 7322 7334 7343 7344 7350 7356 7363 + 19 7299 7303 7307 7310 7311 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 + 20 7303 7307 7310 7311 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 7225 + 21 7310 7311 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 7225 7317 7386 + 22 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 7225 7317 7386 7398 7409 + """ + } + + def make_address_data(): + for line in fake_data.addresses.strip().split('\n'): + data = line.split(maxsplit=2) + yield (data[0], {k:v for k,v in zip(('id','addr','subver'),data)}) + + def make_iterations_data(): + for line in fake_data.iterations.strip().split('\n'): + data = line.split(maxsplit=1) + yield (data[0], data[1].split()) + + def make_blocks_data(iterations): + for peer,blocks_str in fake_data.blocks.items(): + iter_strs = dict([s.lstrip().split(maxsplit=1) for s in blocks_str.strip().split('\n') if ' ' in s]) + yield (peer,dict((i,iter_strs.get(i,'').split()) for i in iterations)) + + def make_data(): + address_data = dict(fake_data.make_address_data()) + iterations_data = dict(fake_data.make_iterations_data()) + blocks_data = dict(fake_data.make_blocks_data(iterations_data)) + + def make_peerinfo(peer_id,blocks,iter_no): + d = address_data[peer_id] + return { + 'id': int(d['id']), + 'addr': d['addr'], + 'subver': d['subver'], + 'inflight': [int(n)+830000 for n in blocks[iter_no]], + } + + def gen_data(): + for iter_no in iterations_data: + yield ( + iter_no, + [make_peerinfo(peer_id,blocks,iter_no) for peer_id,blocks in blocks_data.items() + if peer_id in iterations_data[iter_no] ] + ) + + fake_data.peerinfo = dict(gen_data()) + + async def get_info(self,rpc): + fake_data.gp_counter = (fake_data.gp_counter % 22) + 1 + return fake_data.peerinfo[f'{fake_data.gp_counter:02d}'] + + async def disconnect_node(self,rpc,addr): + from mmgen.exception import RPCFailure + fake_data.dn_counter += 1 + if fake_data.dn_counter % 2: + raise RPCFailure + else: + pass + +fake_data.make_data() + +Display.poll_secs = fake_data.poll_secs +Display.get_info = fake_data.get_info +Display.disconnect_node = fake_data.disconnect_node diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 59db1dd..ba2f6ce 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -14,7 +14,7 @@ # mmnode-feeview - # mmnode-halving-calculator OK # mmnode-netrate - -# mmnode-peerblocks - +# mmnode-peerblocks OK # mmnode-ticker OK # mmnode-txfind - @@ -38,10 +38,10 @@ list_avail_tests() { } init_groups() { - dfl_tests='unit misc scripts btc_rt bch_rt ltc_rt' + dfl_tests='unit misc scripts btc btc_rt bch_rt ltc_rt' extra_tests='' - noalt_tests='unit misc scripts btc_rt' - quick_tests='unit misc scripts btc_rt' + noalt_tests='unit misc scripts btc btc_rt' + quick_tests='unit misc scripts btc btc_rt' qskip_tests='bch_rt ltc_rt' } @@ -61,6 +61,11 @@ init_tests() { t_scripts="- $test_py scripts" f_scripts='No-daemon script tests completed' + i_btc='Bitcoin fake RPC data' + s_btc="The following tests will test various scripts with fake RPC data" + t_btc="- $test_py main" + f_btc='Bitcoin fake RPC data tests completed' + i_btc_rt='Bitcoin regtest' s_btc_rt="The following tests will test various scripts using regtest mode" t_btc_rt="- $test_py regtest" diff --git a/test/test_py_d/cfg.py b/test/test_py_d/cfg.py index d2726c4..c2c72e3 100755 --- a/test/test_py_d/cfg.py +++ b/test/test_py_d/cfg.py @@ -15,6 +15,7 @@ test.test_py_d.cfg: configuration data for test.py import os cmd_groups_dfl = { + 'main': ('TestSuiteMain',{}), 'helpscreens': ('TestSuiteHelp',{'modname':'misc','full_data':True}), 'scripts': ('TestSuiteScripts',{'modname':'misc'}), 'regtest': ('TestSuiteRegtest',{}), @@ -25,6 +26,7 @@ cmd_groups_extra = {} cfgs = { '1': {}, # regtest '2': {}, # scripts + '3': {}, # main } def fixup_cfgs(): pass diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py new file mode 100755 index 0000000..7974c58 --- /dev/null +++ b/test/test_py_d/ts_main.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2022 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen +# https://gitlab.com/mmgen/mmgen + +""" +test_py_d.ts_main: Basic operations tests for the test.py test suite +""" + +import time + +from ..include.common import * +from .common import * +from .ts_base import * + +class TestSuiteMain(TestSuiteBase): + 'basic operations with fake RPC data' + tmpdir_nums = [3] + networks = ('btc',) # fake data, so test peerblocks for BTC mainnet only + passthru_opts = ('daemon_data_dir','rpc_port','coin','testnet','rpc_backend') + segwit_opts_ok = True + color = True + need_daemon = True + + cmd_group_in = ( + ('subgroup.peerblocks', []), + ) + + cmd_subgroups = { + 'peerblocks': ( + "'mmnode-peerblocks' script", + ('peerblocks1', '--help'), + ('peerblocks2', 'interactive'), + ('peerblocks3', 'interactive, 80 columns'), + ), + } + + def peerblocks(self,args,expect_list=None): + t = self.spawn( + f'mmnode-peerblocks', + args ) + if opt.exact_output: # disable echoing of input + t.p.logfile = None + t.p.logfile_read = sys.stdout + if expect_list: + t.match_expect_list(expect_list) + return t + + def peerblocks1(self): + t = self.peerblocks(['--help']) + if opt.pexpect_spawn: + t.send('q') + return t + + def peerblocks2(self,args=[]): + + t = self.peerblocks(args) + + for i in range(5): + t.expect('PEERS') + + t.send('x') + + for i in range(3): + t.expect('PEERS') + + t.send('0') + time.sleep(0.2) + t.send('\n') + t.expect('Unable to disconnect peer 0') + t.expect('PEERS') + + t.send('1') + time.sleep(0.2) + t.send('1\n') + t.expect('11: invalid peer number') + t.expect('PEERS') + + t.send('2') + time.sleep(0.2) + t.send('\n') + t.expect('Disconnecting peer 2') + t.expect('PEERS') + + t.send('q') + + return t + + def peerblocks3(self): + return self.peerblocks2(['--columns=80']) From b640dea1117b7dc2a2f8dd47778ed1919bcc3c04 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 25 Oct 2022 09:10:15 +0000 Subject: [PATCH 083/175] update for MMGen v13.3.dev11 --- mmgen_node_tools/PeerBlocks.py | 1 + mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/test-release.d/cfg.sh | 57 ++++++++++------------------------ test/test_py_d/ts_main.py | 6 ++-- 5 files changed, 23 insertions(+), 45 deletions(-) diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index a6027ca..e38af23 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -67,6 +67,7 @@ class Display(PollDisplay): with self.info_lock: msg('') term.reset() + # readline required for correct operation here; without it, user must re-type first digit ret = line_input('peer number> ',insert_txt=s,hold_protect=False) term.init(noecho=True) self.enable_display = False # prevent display from updating before process_input() diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1ebddf5..e2195ce 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev9 +3.1.dev10 diff --git a/setup.cfg b/setup.cfg index 208e03b..0be313f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev9 + mmgen>=13.3.dev11 packages = mmgen_node_tools diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index ba2f6ce..866f453 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -18,27 +18,18 @@ # mmnode-ticker OK # mmnode-txfind - -list_avail_tests() { - echo "AVAILABLE TESTS:" - echo " unit - unit tests" - echo " btc_rt - Bitcoin regtest" - echo " bch_rt - Bitcoin Cash Node (BCH) regtest" - echo " ltc_rt - Litecoin regtest" - echo " scripts - tests of scripts not requiring a coin daemon" - echo " misc - miscellaneous tests that don't fit in the above categories" - echo - echo "AVAILABLE TEST GROUPS:" - echo " default - All tests minus the extra tests" - echo " extra - All tests minus the default tests" - echo " noalt - BTC-only tests" - echo " quick - Default tests minus btc_tn, bch, bch_rt, ltc and ltc_rt" - echo " qskip - The tests skipped in the 'quick' test group" - echo - echo "By default, all tests are run" -} +all_tests='unit misc scripts btc btc_rt bch_rt ltc_rt' + +groups_desc=" + default - All tests minus the extra tests + extra - All tests minus the default tests + noalt - BTC-only tests + quick - Default tests minus btc_tn, bch, bch_rt, ltc and ltc_rt + qskip - The tests skipped in the 'quick' test group +" init_groups() { - dfl_tests='unit misc scripts btc btc_rt bch_rt ltc_rt' + dfl_tests=$all_tests extra_tests='' noalt_tests='unit misc scripts btc btc_rt' quick_tests='unit misc scripts btc btc_rt' @@ -46,38 +37,24 @@ init_groups() { } init_tests() { - i_unit='Unit' - s_unit="The following tests will test various low-level subsystems" + d_unit="low-level subsystems" t_unit="- $unit_tests_py" - f_unit='Unit tests completed' - i_misc='Misc' - s_misc="The following tests will test miscellaneous script features" + d_misc="miscellaneous features" t_misc="- $test_py helpscreens" - f_misc='Misc tests completed' - i_scripts='No-daemon scripts' - s_scripts="The following tests will test scripts not requiring a coin daemon" + d_scripts="scripts not requiring a coin daemon" t_scripts="- $test_py scripts" - f_scripts='No-daemon script tests completed' - i_btc='Bitcoin fake RPC data' - s_btc="The following tests will test various scripts with fake RPC data" + d_btc="Bitcoin with emulated RPC data" t_btc="- $test_py main" - f_btc='Bitcoin fake RPC data tests completed' - i_btc_rt='Bitcoin regtest' - s_btc_rt="The following tests will test various scripts using regtest mode" + d_btc_rt="Bitcoin regtest" t_btc_rt="- $test_py regtest" - f_btc_rt='Regtest mode tests for BTC completed' - i_bch_rt='BitcoinCashNode (BCH) regtest' - s_bch_rt="The following tests will test various scripts using regtest mode" + d_bch_rt="Bitcoin Cash Node (BCH) regtest" t_bch_rt="- $test_py --coin=bch regtest" - f_bch_rt='Regtest mode tests for BCH completed' - i_ltc_rt='Litecoin regtest' - s_ltc_rt="The following tests will test various scripts using regtest mode" + d_ltc_rt="Litecoin regtest" t_ltc_rt="- $test_py --coin=ltc regtest" - f_ltc_rt='Regtest mode tests for LTC completed' } diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index 7974c58..aa175cc 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -71,19 +71,19 @@ class TestSuiteMain(TestSuiteBase): t.send('0') time.sleep(0.2) - t.send('\n') + t.send('\n' if opt.pexpect_spawn else '0\n') # TODO: check for readline availability t.expect('Unable to disconnect peer 0') t.expect('PEERS') t.send('1') time.sleep(0.2) - t.send('1\n') + t.send('1\n' if opt.pexpect_spawn else '11\n') t.expect('11: invalid peer number') t.expect('PEERS') t.send('2') time.sleep(0.2) - t.send('\n') + t.send('\n' if opt.pexpect_spawn else '2\n') t.expect('Disconnecting peer 2') t.expect('PEERS') From 6ad22bdf5c5c92813313bbaeff1e080775f12bdf Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 27 Oct 2022 16:47:22 +0000 Subject: [PATCH 084/175] various fixes and cleanups --- mmgen_node_tools/PeerBlocks.py | 2 +- mmgen_node_tools/Term.py | 49 ------------------- mmgen_node_tools/main_feeview.py | 46 ++++++----------- .../fakemods/mmgen_node_tools/PeerBlocks.py | 37 +++++++------- test/test-release.d/cfg.sh | 2 +- test/test_py_d/ts_regtest.py | 2 + 6 files changed, 38 insertions(+), 100 deletions(-) delete mode 100755 mmgen_node_tools/Term.py diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index e38af23..4a4a940 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -37,7 +37,7 @@ class Display(PollDisplay): if not term: term = get_term() term.init(noecho=True) - term_width = g.columns or get_terminal_size()[0] + term_width = g.columns or get_terminal_size().width msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) async def get_info(self,rpc): diff --git a/mmgen_node_tools/Term.py b/mmgen_node_tools/Term.py deleted file mode 100755 index 7b5b7e0..0000000 --- a/mmgen_node_tools/Term.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2016 Philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -mmgen_node_tools.Term: terminal routines for MMGen node tools -""" - -import sys,os,termios - -def get_keypress(prompt="",esc_sequences=False): - - import time,tty,select - sys.stderr.write(prompt) - - fd = sys.stdin.fileno() -# old = termios.tcgetattr(fd) # see below - tty.setcbreak(fd) # must do this, even if it was set at program launch - - def osread_chk(n): - while True: - try: - return os.read(fd,n) - except: - time.sleep(0.1) - - # Must use os.read() for unbuffered read, otherwise select() will never return true - s = osread_chk(1) - if esc_sequences: - if s == '\x1b': - if select.select([sys.stdin],[],[],0)[0]: - s += osread_chk(2) - -# Leave the term in cbreak mode, restore at exit -# termios.tcsetattr(fd, termios.TCSADRAIN, old) - return s diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index e5f2f00..fd6d7d3 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -61,7 +61,6 @@ opts.init({ -P, --pager Pipe the output to a pager -r, --ranges Display fee brackets as ranges -s, --show-mb-col Display column with each fee bracket’s megabyte count --w, --width=W Force output width of 'W' columns (default: term width) """, 'notes': """ + By default, fee bracket row labels include only the top of the range. @@ -83,16 +82,12 @@ if opt.ignore_below: die(1,'Conflicting options: --ignore-below, --show-empty') ignore_below = parse_bytespec(opt.ignore_below) -if opt.precision: - precision = check_int_between(opt.precision,min_prec,max_prec,'--precision arg') -else: - precision = dfl_prec +precision = ( + check_int_between(opt.precision,min_prec,max_prec,'--precision arg') + if opt.precision else dfl_prec ) -if opt.width: - width = check_int_between(opt.width,40,1024,'--width arg') -else: - from mmgen.term import get_terminal_size - width = get_terminal_size()[0] +from mmgen.term import get_terminal_size +width = g.columns or get_terminal_size().width class fee_bracket: def __init__(self,top,bottom): @@ -102,12 +97,6 @@ class fee_bracket: self.tx_bytes_cum = 0 self.skip = False -def get_fake_data(fn): # for debugging - import json - from mmgen.rpc import json_encoder - from decimal import Decimal - return json.loads(open(os.path.join(fn)).read(),parse_float=Decimal) - def log(data,fn): import json from mmgen.rpc import json_encoder @@ -129,9 +118,6 @@ def create_data(coin_amt,mempool): while out and out[-1].tx_bytes == 0: out.pop() - if not out: - die(1,'No data!') - out.reverse() # cumulative totals and display are top-down # calculate cumulative byte totals, filter rows: @@ -152,6 +138,7 @@ def gen_header(host,blockcount): make_timestr(), blockcount, )) + if opt.show_empty: yield('Displaying all fee brackets') elif opt.ignore_below: @@ -159,29 +146,28 @@ def gen_header(host,blockcount): ignore_below, int2bytespec(ignore_below,'MB','0.6'), )) + if opt.include_current: yield('Including transactions in current fee bracket in Total MB amounts') def fmt_mb(n): - return int2bytespec(n,'MB',f'0.{precision}',False) + return int2bytespec(n,'MB',f'0.{precision}',print_sym=False) def gen_body(data): - tx_bytes_max = max(i.tx_bytes for i in data) - top_max = max(i.top for i in data if not i.skip) - bot_max = max(i.bottom for i in data if not i.skip) + tx_bytes_max = max((i.tx_bytes for i in data),default=0) + top_max = max((i.top for i in data),default=0) + bot_max = max((i.bottom for i in data),default=0) col1_w = max(len(f'{bot_max}-{top_max}') if opt.ranges else len(f'{top_max}'),6) col2_w = len(fmt_mb(tx_bytes_max)) if opt.show_mb_col else 0 - col3_w = len(fmt_mb(data[-1].tx_bytes_cum)) + col3_w = len(fmt_mb(data[-1].tx_bytes_cum)) if data else 0 col4_w = width - col1_w - col2_w - col3_w - (4 if col2_w else 3) if opt.show_mb_col: fs = '{a:<%i} {b:>%i} {c:>%i} {d}' % (col1_w,col2_w,col3_w) else: fs = '{a:<%i} {c:>%i} {d}' % (col1_w,col3_w) - yield( - '\n' + fs.format(a='', b='', c=f'{"Total":<{col3_w}}', d='') + - '\n' + fs.format(a='sat/B', b=f'{"MB":<{col2_w}}', c=f'{"MB":<{col3_w}}', d='') - ) + yield fs.format(a='', b='', c=f'{"Total":<{col3_w}}', d='') + yield fs.format(a='sat/B', b=f'{"MB":<{col2_w}}', c=f'{"MB":<{col3_w}}', d='') for i in data: if not i.skip: @@ -195,7 +181,7 @@ def gen_body(data): yield(fs.format( a = 'TOTAL', b = '', - c = fmt_mb(data[-1].tx_bytes_cum + data[-1].tx_bytes), + c = fmt_mb(data[-1].tx_bytes_cum + data[-1].tx_bytes if data else 0), d = '' )) async def main(): @@ -206,9 +192,7 @@ async def main(): from mmgen.rpc import rpc_init c = await rpc_init(proto) -# pmsg(await c.call('getmempoolinfo')) mempool = await c.call('getrawmempool',True) -# mempool = get_fake_data('test_data/mempool-sample.json') if opt.log: log(mempool,'mempool.json') diff --git a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py index 0170a2f..2484628 100644 --- a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py +++ b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py @@ -297,25 +297,22 @@ class fake_data: """ } - def make_address_data(): - for line in fake_data.addresses.strip().split('\n'): - data = line.split(maxsplit=2) - yield (data[0], {k:v for k,v in zip(('id','addr','subver'),data)}) - - def make_iterations_data(): - for line in fake_data.iterations.strip().split('\n'): - data = line.split(maxsplit=1) - yield (data[0], data[1].split()) - - def make_blocks_data(iterations): - for peer,blocks_str in fake_data.blocks.items(): - iter_strs = dict([s.lstrip().split(maxsplit=1) for s in blocks_str.strip().split('\n') if ' ' in s]) - yield (peer,dict((i,iter_strs.get(i,'').split()) for i in iterations)) - def make_data(): - address_data = dict(fake_data.make_address_data()) - iterations_data = dict(fake_data.make_iterations_data()) - blocks_data = dict(fake_data.make_blocks_data(iterations_data)) + + def gen_address_data(): + for line in fake_data.addresses.strip().split('\n'): + data = line.split(maxsplit=2) + yield (data[0], {k:v for k,v in zip(('id','addr','subver'),data)}) + + def gen_iterations_data(): + for line in fake_data.iterations.strip().split('\n'): + data = line.split(maxsplit=1) + yield (data[0], data[1].split()) + + def gen_blocks_data(iterations): + for peer,blocks_str in fake_data.blocks.items(): + iter_strs = dict([s.lstrip().split(maxsplit=1) for s in blocks_str.strip().split('\n') if ' ' in s]) + yield (peer,dict((i,iter_strs.get(i,'').split()) for i in iterations)) def make_peerinfo(peer_id,blocks,iter_no): d = address_data[peer_id] @@ -334,6 +331,10 @@ class fake_data: if peer_id in iterations_data[iter_no] ] ) + address_data = dict(gen_address_data()) + iterations_data = dict(gen_iterations_data()) + blocks_data = dict(gen_blocks_data(iterations_data)) + fake_data.peerinfo = dict(gen_data()) async def get_info(self,rpc): diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 866f453..e277ab6 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -24,7 +24,7 @@ groups_desc=" default - All tests minus the extra tests extra - All tests minus the default tests noalt - BTC-only tests - quick - Default tests minus btc_tn, bch, bch_rt, ltc and ltc_rt + quick - Default tests minus bch_rt and ltc_rt qskip - The tests skipped in the 'quick' test group " diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index d6b037e..737cb75 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -17,6 +17,7 @@ from mmgen.globalvars import g from mmgen.opts import opt from mmgen.util import die,gmsg from mmgen.protocol import init_proto +from mmgen.proto.btc.regtest import MMGenRegtest from ..include.common import * from .common import * @@ -93,6 +94,7 @@ class TestSuiteRegtest(TestSuiteBase): die(2,'--testnet and --regtest options incompatible with regtest test suite') self.proto = init_proto(self.proto.coin,network='regtest',need_amt=True) self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) + self.regtest = MMGenRegtest(self.proto.coin) def setup(self): stop_test_daemons(self.proto.network_id,force=True,remove_datadir=True) From a8a44ab646e6131c78a766de548c55b7fcab12d8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 27 Oct 2022 16:47:22 +0000 Subject: [PATCH 085/175] mmnode-feeview: display improvements, support BCH, add test Testing/demo: $ test/test.py -e regtest.feeview $ test/test.py -e --coin=ltc regtest.feeview $ test/test.py -e --coin=bch regtest.feeview --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_feeview.py | 43 ++++++---- setup.cfg | 2 +- test/test-release.d/cfg.sh | 2 +- test/test_py_d/ts_regtest.py | 135 +++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 17 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index e2195ce..9ddd38f 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev10 +3.1.dev11 diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index fd6d7d3..ce1fbdb 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -52,7 +52,8 @@ opts.init({ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -c, --include-current Include current bracket’s TXs in cumulative MB value --d, --detail Same as --ranges --show-mb-col --precision=6 +-d, --outdir=D Write log data to directory 'D' +-D, --detail Same as --ranges --show-mb-col --precision=6 -e, --show-empty Show all fee brackets, including empty ones -i, --ignore-below=B Ignore fee brackets with less than 'B' bytes of TXs -l, --log Log JSON-RPC mempool data to 'mempool.json' @@ -100,18 +101,24 @@ class fee_bracket: def log(data,fn): import json from mmgen.rpc import json_encoder - open(fn,'w').write(json.dumps(data,cls=json_encoder,sort_keys=True,indent=4)) + from mmgen.fileutil import write_data_to_file + write_data_to_file( + outfile = fn, + data = json.dumps(data,cls=json_encoder,sort_keys=True,indent=4), + desc = 'mempool', + ask_overwrite = False ) def create_data(coin_amt,mempool): out = [fee_bracket(fee_brackets[i],fee_brackets[i-1] if i else 0) for i in range(len(fee_brackets))] # populate fee brackets: + size_key = 'size' if proto.coin == 'BCH' else 'vsize' for tx in mempool.values(): fee = coin_amt(tx['fees']['base']).to_unit('satoshi') - vsize = tx['vsize'] + size = tx[size_key] for bracket in out: - if fee / vsize < bracket.top: - bracket.tx_bytes += vsize + if fee / size < bracket.top: + bracket.tx_bytes += size break # remove empty top brackets: @@ -132,19 +139,23 @@ def create_data(coin_amt,mempool): return out -def gen_header(host,blockcount): - yield('MEMPOOL FEE STRUCTURE ({})\n{} UTC\nBlock {}'.format( - host, - make_timestr(), - blockcount, - )) +def gen_header(host,mempool,blockcount): + + yield(fmt(f""" + Mempool Fee Structure + Date: {make_timestr()} UTC + Host: {host} + Network: {proto.coin.upper()} {proto.network.upper()} + Block: {blockcount} + TX count: {len(mempool)} + """)).strip() if opt.show_empty: yield('Displaying all fee brackets') elif opt.ignore_below: - yield('Ignoring fee brackets with less than {} bytes ({})'.format( + yield('Ignoring fee brackets with less than {:,} bytes ({})'.format( ignore_below, - int2bytespec(ignore_below,'MB','0.6'), + int2bytespec(ignore_below,'MB','0.6',strip=True,add_space=True), )) if opt.include_current: @@ -187,6 +198,7 @@ def gen_body(data): async def main(): from mmgen.protocol import init_proto_from_opts + global proto proto = init_proto_from_opts(need_amt=True) from mmgen.rpc import rpc_init @@ -199,7 +211,10 @@ async def main(): data = create_data(proto.coin_amt,mempool) stdout_or_pager( - '\n'.join(gen_header(c.host,await c.call('getblockcount'))) + '\n' + + '\n'.join(gen_header( + c.host, + mempool, + await c.call('getblockcount') )) + '\n\n' + '\n'.join(gen_body(data)) + '\n' ) async_run(main()) diff --git a/setup.cfg b/setup.cfg index 0be313f..3570d34 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev11 + mmgen>=13.3.dev12 packages = mmgen_node_tools diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index e277ab6..cfe98b7 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -11,7 +11,7 @@ # Testing status # mmnode-addrbal OK # mmnode-blocks-info OK -# mmnode-feeview - +# mmnode-feeview OK # mmnode-halving-calculator OK # mmnode-netrate - # mmnode-peerblocks OK diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 737cb75..2501160 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -47,6 +47,7 @@ class TestSuiteRegtest(TestSuiteBase): ('subgroup.fund_addrbal', []), ('subgroup.addrbal', ['fund_addrbal']), ('subgroup.blocks_info', ['addrbal']), + ('subgroup.feeview', []), ('stop', 'stopping regtest daemon'), ) cmd_subgroups = { @@ -84,6 +85,18 @@ class TestSuiteRegtest(TestSuiteBase): ('blocks_info3', "blocks-info +100"), ('blocks_info4', "blocks-info --miner-info --fields=all --stats=all +1"), ), + 'feeview': ( + "'mmnode-feeview' script", + ('feeview_setup', 'setting up feeview test'), + ('feeview1', "'mmnode-feeview'"), + ('feeview2', "'mmnode-feeview --columns=40 --include-current'"), + ('feeview3', "'mmnode-feeview --precision=6'"), + ('feeview4', "'mmnode-feeview --detail'"), + ('feeview5', "'mmnode-feeview --show-empty --log'"), + ('feeview6', "'mmnode-feeview --ignore-below=1MB'"), + ('feeview7', "'mmnode-feeview --ignore-below=20kB'"), + ('feeview8', "'mmnode-feeview' (empty mempool)"), + ), } def __init__(self,trunner,cfgs,spawn): @@ -247,6 +260,128 @@ class TestSuiteRegtest(TestSuiteBase): 'Next diff adjust: 2016' ]) + async def feeview_setup(self): + + def create_pairs(nPairs): + + from mmgen.tool.api import tool_api + from collections import namedtuple + + t = tool_api() + t.init_coin(self.proto.coin,self.proto.network) + t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32' + wp = namedtuple('wifaddrpair',['wif','addr']) + + def gen(): + for n in range(1,nPairs+1): + wif = t.hex2wif(f'{n:064x}') + yield wp( wif, t.wif2addr(wif) ) + + return list(gen()) + + def gen_fees(n_in,low,high): + + # very approximate tx size estimation: + ibytes,wbytes,obytes = (148,0,34) if self.proto.coin == 'BCH' else (43,108,31) + x = (ibytes + (wbytes//4) + (obytes * nPairs)) * self.proto.coin_amt(self.proto.coin_amt.satoshi) + + n = n_in - 1 + vmax = high - low + + for i in range(n_in): + yield (low + (i/n)**6 * vmax) * x + + async def do_tx(inputs,outputs,wif): + tx_hex = await r.rpc_call( 'createrawtransaction', inputs, outputs ) + tx = await r.rpc_call( 'signrawtransactionwithkey', tx_hex, [wif], [], self.proto.sighash_type ) + assert tx['complete'] == True + return tx['hex'] + + async def do_tx1(): + us = await r.rpc_call('listunspent',wallet='miner') + fee = self.proto.coin_amt('0.001') + outputs = {p.addr:tx1_amt for p in pairs[:nTxs]} + outputs.update({burn_addr: us[0]['amount'] - (tx1_amt*nTxs) - fee}) + return await do_tx( + [{ 'txid': us[0]['txid'], 'vout': 0 }], + outputs, + r.miner_wif ) + + async def do_tx2(tx,pairno): + fee = fees[pairno] + outputs = {p.addr:tx2_amt for p in pairs} + outputs.update({burn_addr: tx1_amt - (tx2_amt*len(pairs)) - fee}) + return await do_tx( + [{ 'txid': tx['txid'], 'vout': pairno }], + outputs, + pairs[pairno].wif ) + + async def do_txs(tx_in): + for pairno in range(nTxs): + tx_hex = await do_tx2(tx_in,pairno) + await r.rpc_call('sendrawtransaction',tx_hex) + + self.spawn('',msg_only=True) + + r = self.regtest + nPairs = 100 + nTxs = 25 + tx1_amt = self.proto.coin_amt('{:0.4f}'.format(24 / (nTxs+1))) # 25 BTC subsidy + tx2_amt = self.proto.coin_amt('0.0001') + + imsg(f'Creating {nPairs} key-address pairs') + pairs = create_pairs(nPairs+1) + burn_addr = pairs.pop()[1] + + imsg(f'Creating funding transaction with {nTxs} outputs of value {tx1_amt} {self.proto.coin}') + tx1_hex = await do_tx1() + + imsg(f'Relaying funding transaction') + await r.rpc_call('sendrawtransaction',tx1_hex) + + imsg(f'Mining a block') + await r.generate(1,silent=True) + + imsg(f'Generating fees for mempool transactions') + fees = list(gen_fees(nTxs,2,120)) + + imsg(f'Creating and relaying {nTxs} mempool transactions with {nPairs} outputs each') + await do_txs(await r.rpc_call('decoderawtransaction',tx1_hex)) + + return 'ok' + + def _feeview(self,args,expect_list=[]): + t = self.spawn('mmnode-feeview',args) + if expect_list: + t.match_expect_list(expect_list) + return t + + def feeview1(self): + return self._feeview([]) + + def feeview2(self): + return self._feeview(['--columns=40','--include-current']) + + def feeview3(self): + return self._feeview(['--precision=6']) + + def feeview4(self): + return self._feeview(['--detail']) + + def feeview5(self): + return self._feeview(['--show-empty','--log',f'--outdir={self.tmpdir}']) + + def feeview6(self): + return self._feeview(['--ignore-below=1MB']) + + def feeview7(self): + return self._feeview(['--ignore-below=4kB']) + + async def feeview8(self): + imsg('Clearing mempool') + await self.regtest.generate(1,silent=True) + return self._feeview([]) + def stop(self): if opt.no_daemon_stop: self.spawn('',msg_only=True) From 3a972f2149e4fddf24c0cc0d7dfa4e4c75fa7c1a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 28 Oct 2022 11:33:52 +0000 Subject: [PATCH 086/175] update for MMGen v13.3.dev13 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/test_py_d/ts_main.py | 31 +++++++++++++++++-------------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 9ddd38f..4913611 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev11 +3.1.dev12 diff --git a/setup.cfg b/setup.cfg index 3570d34..5c13b13 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev12 + mmgen>=13.3.dev13 packages = mmgen_node_tools diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index aa175cc..171ffc5 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -35,15 +35,16 @@ class TestSuiteMain(TestSuiteBase): 'peerblocks': ( "'mmnode-peerblocks' script", ('peerblocks1', '--help'), - ('peerblocks2', 'interactive'), - ('peerblocks3', 'interactive, 80 columns'), + ('peerblocks2', 'interactive (popen spawn)'), + ('peerblocks3', 'interactive, 80 columns (pexpect_spawn)'), ), } - def peerblocks(self,args,expect_list=None): + def peerblocks(self,args,expect_list=None,pexpect_spawn=False): t = self.spawn( f'mmnode-peerblocks', - args ) + args, + pexpect_spawn = pexpect_spawn ) if opt.exact_output: # disable echoing of input t.p.logfile = None t.p.logfile_read = sys.stdout @@ -53,13 +54,13 @@ class TestSuiteMain(TestSuiteBase): def peerblocks1(self): t = self.peerblocks(['--help']) - if opt.pexpect_spawn: + if t.pexpect_spawn: t.send('q') return t - def peerblocks2(self,args=[]): + def peerblocks2(self,args=[],pexpect_spawn=False): - t = self.peerblocks(args) + t = self.peerblocks(args,pexpect_spawn=pexpect_spawn) for i in range(5): t.expect('PEERS') @@ -69,21 +70,23 @@ class TestSuiteMain(TestSuiteBase): for i in range(3): t.expect('PEERS') + sleep_secs = 0.2 + t.send('0') - time.sleep(0.2) - t.send('\n' if opt.pexpect_spawn else '0\n') # TODO: check for readline availability + time.sleep(sleep_secs) + t.send('\n' if pexpect_spawn else '0\n') # TODO: check for readline availability t.expect('Unable to disconnect peer 0') t.expect('PEERS') t.send('1') - time.sleep(0.2) - t.send('1\n' if opt.pexpect_spawn else '11\n') + time.sleep(sleep_secs) + t.send('1\n' if pexpect_spawn else '11\n') t.expect('11: invalid peer number') t.expect('PEERS') t.send('2') - time.sleep(0.2) - t.send('\n' if opt.pexpect_spawn else '2\n') + time.sleep(sleep_secs) + t.send('\n' if pexpect_spawn else '2\n') t.expect('Disconnecting peer 2') t.expect('PEERS') @@ -92,4 +95,4 @@ class TestSuiteMain(TestSuiteBase): return t def peerblocks3(self): - return self.peerblocks2(['--columns=80']) + return self.peerblocks2(['--columns=80'],pexpect_spawn=True) From c53e7cc65f5d4a8a14e391461d04071df711392c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 29 Oct 2022 20:12:39 +0000 Subject: [PATCH 087/175] update for MMGen v13.3.dev14 --- mmgen_node_tools/PeerBlocks.py | 2 +- mmgen_node_tools/PollDisplay.py | 2 +- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index 4a4a940..899f962 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -61,7 +61,7 @@ class Display(PollDisplay): return await rpc.call('disconnectnode',addr) def get_input(self): - s = get_char(immed_chars='q0123456789',prehold_protect=False,num_chars=1) + s = get_char(immed_chars='q0123456789',prehold_protect=False,num_bytes=1) if not is_int(s): return s with self.info_lock: diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py index 06b7d7f..bff4683 100755 --- a/mmgen_node_tools/PollDisplay.py +++ b/mmgen_node_tools/PollDisplay.py @@ -27,7 +27,7 @@ class PollDisplay(): self.display_kill_flag = threading.Event() def get_input(self): - return get_char(immed_chars='q',prehold_protect=False,num_chars=1) + return get_char(immed_chars='q',prehold_protect=False,num_bytes=1) async def process_input(self,rpc): return True diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 4913611..102a890 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev12 +3.1.dev13 diff --git a/setup.cfg b/setup.cfg index 5c13b13..e9a2587 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev13 + mmgen>=13.3.dev14 packages = mmgen_node_tools From affa8910413a84cd77b4683be85c2b206ff7c2cf Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 13 Nov 2022 15:45:57 +0000 Subject: [PATCH 088/175] update for MMGen v13.3.dev20 --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 4 ++-- mmgen_node_tools/main_halving_calculator.py | 6 +++--- setup.cfg | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 102a890..1bc37c8 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev13 +3.1.dev14 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 4218f6a..b0ab4f8 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -55,7 +55,7 @@ def do_output(proto,addr_data,blk_hdrs): make_timestr( blk_hdrs[u['height']]['time'] ), CoinTxID(u['txid']).hl(), red(str(u['vout']).rjust(4)), - proto.coin_amt(u['amount']).fmt(color=True,fs='6.8') + proto.coin_amt(u['amount']).fmt(color=True,iwidth=6,prec=8) )) else: Msg(f'{indent}No balance') @@ -94,7 +94,7 @@ def do_output_tabular(proto,addr_data,blk_hdrs): t = make_timestr( blk_hdrs[unspents[0]['height']]['time'] ), B = unspents[-1]['height'], T = make_timestr( blk_hdrs[unspents[-1]['height']]['time'] ), - A = proto.coin_amt(sum(u['amount'] for u in unspents)).fmt(color=True,fs='7.8') + A = proto.coin_amt(sum(u['amount'] for u in unspents)).fmt(color=True,iwidth=7,prec=8) )) else: Msg(fs.format( diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index c804d8f..5cbf2b8 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -168,9 +168,9 @@ async def main(): c = date(t), d = ' P' if n > nhist else '' if n < nhist else ' E', e = f'{bdr:8.5f}', - f = proto.coin_amt(sub,from_unit='satoshi').fmt(fs='2.8'), - g = proto.coin_amt(mined,from_unit='satoshi').fmt(fs='8.8'), - h = proto.coin_amt(total_mined,from_unit='satoshi').fmt(fs='8.8') + f = proto.coin_amt(sub,from_unit='satoshi').fmt(iwidth=2,prec=8), + g = proto.coin_amt(mined,from_unit='satoshi').fmt(iwidth=8,prec=8), + h = proto.coin_amt(total_mined,from_unit='satoshi').fmt(iwidth=8,prec=8) ) for n,sub,blk,mined,total_mined,bdr,t in gen_data()) ) diff --git a/setup.cfg b/setup.cfg index e9a2587..65b9639 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev14 + mmgen>=13.3.dev20 packages = mmgen_node_tools From eecf8acd0d8ece32be7b046e80964cb84a5a0931 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 28 Mar 2023 18:16:33 +0000 Subject: [PATCH 089/175] minor fixes and cleanups --- mmgen_node_tools/PeerBlocks.py | 1 + mmgen_node_tools/main_txfind.py | 1 + test/test_py_d/ts_misc.py | 2 +- test/test_py_d/ts_regtest.py | 13 +++++++------ test/unit_tests_d/ut_BlocksInfo.py | 8 +++++--- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index 899f962..6df9894 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -31,6 +31,7 @@ class Display(PollDisplay): poll_secs = 2 def __init__(self): + super().__init__() global term,term_width diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index 2672a11..d0a04e2 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -86,6 +86,7 @@ async def main(txid): return exitval cmd_args = opts.init(opts_data) + msgs = msg_data['quiet' if opt.quiet else 'normal'] if len(cmd_args) != 1: diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index ee41377..cc4c22d 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -ts_misc.py: Miscellaneous test groups for the test.py test suite +test.test_py_d.ts_misc: Miscellaneous test groups for the test.py test suite """ import shutil diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 2501160..fd19099 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -ts_regtest.py: Regtest tests for the test.py test suite +test.test_py_d.ts_regtest: Regtest tests for the test.py test suite """ import os @@ -273,7 +273,7 @@ class TestSuiteRegtest(TestSuiteBase): wp = namedtuple('wifaddrpair',['wif','addr']) def gen(): - for n in range(1,nPairs+1): + for n in range(0xfaceface,nPairs+0xfaceface): wif = t.hex2wif(f'{n:064x}') yield wp( wif, t.wif2addr(wif) ) @@ -299,11 +299,12 @@ class TestSuiteRegtest(TestSuiteBase): async def do_tx1(): us = await r.rpc_call('listunspent',wallet='miner') + tx_input = us[7] # 25 BTC in coinbase -- us[0] could have < 25 BTC fee = self.proto.coin_amt('0.001') outputs = {p.addr:tx1_amt for p in pairs[:nTxs]} - outputs.update({burn_addr: us[0]['amount'] - (tx1_amt*nTxs) - fee}) + outputs.update({burn_addr: tx_input['amount'] - (tx1_amt*nTxs) - fee}) return await do_tx( - [{ 'txid': us[0]['txid'], 'vout': 0 }], + [{ 'txid': tx_input['txid'], 'vout': 0 }], outputs, r.miner_wif ) @@ -326,8 +327,8 @@ class TestSuiteRegtest(TestSuiteBase): r = self.regtest nPairs = 100 nTxs = 25 - tx1_amt = self.proto.coin_amt('{:0.4f}'.format(24 / (nTxs+1))) # 25 BTC subsidy - tx2_amt = self.proto.coin_amt('0.0001') + tx1_amt = self.proto.coin_amt('{:0.4f}'.format(24 / nTxs)) # 25 BTC subsidy, leave extra for fee + tx2_amt = self.proto.coin_amt('0.00005') # make this as small as possible imsg(f'Creating {nPairs} key-address pairs') pairs = create_pairs(nPairs+1) diff --git a/test/unit_tests_d/ut_BlocksInfo.py b/test/unit_tests_d/ut_BlocksInfo.py index e83cca0..ab09882 100755 --- a/test/unit_tests_d/ut_BlocksInfo.py +++ b/test/unit_tests_d/ut_BlocksInfo.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 """ -test.unit_tests_d.nt_BlocksInfo: - BlocksInfo unit test for the MMGen Node Tools suite +test.unit_tests_d.nt_BlocksInfo: BlocksInfo unit test for the MMGen Node Tools suite """ from mmgen.common import * @@ -108,7 +107,10 @@ class unit_tests: def parse_cmd_args(self,name,ut): def test(spec,foo,chk): - b = BlocksInfo(spec if type(spec) == tuple else [spec],dummyOpt(),dummyRPC()) + b = BlocksInfo( + spec if type(spec) == tuple else [spec], + dummyOpt(), + dummyRPC() ) ret = (b.first,b.last,b.block_list) vmsg('{:13} => {}'.format( (repr(spec) if type(spec) == tuple else spec), From 30d8a4a8717716a1d3ee2fad7ea74e2b8b7e4264 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 28 Mar 2023 18:16:33 +0000 Subject: [PATCH 090/175] update for MMGen v13.3.dev42 --- mmgen_node_tools/BlocksInfo.py | 38 +++++------ mmgen_node_tools/PeerBlocks.py | 9 ++- mmgen_node_tools/PollDisplay.py | 3 +- mmgen_node_tools/Ticker.py | 76 ++++++++++----------- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 17 +++-- mmgen_node_tools/main_blocks_info.py | 19 +++--- mmgen_node_tools/main_feeview.py | 44 ++++++------ mmgen_node_tools/main_halving_calculator.py | 17 +++-- mmgen_node_tools/main_netrate.py | 7 +- mmgen_node_tools/main_peerblocks.py | 13 ++-- mmgen_node_tools/main_ticker.py | 9 ++- mmgen_node_tools/main_txfind.py | 15 ++-- setup.cfg | 2 +- test/test_py_d/ts_main.py | 2 +- test/test_py_d/ts_misc.py | 4 +- test/test_py_d/ts_regtest.py | 18 +++-- test/unit_tests_d/ut_BlocksInfo.py | 9 ++- 18 files changed, 147 insertions(+), 157 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 27dfa08..70d9821 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -109,7 +109,7 @@ class BlocksInfo: def parse_cslist(cls,uarg,full_set,dfl_set,desc): def make_list(m,func): - groups_lc = [set(e.lower() for e in g.split(',')) for g in m.groups()] + groups_lc = [set(e.lower() for e in gi.split(',')) for gi in m.groups()] for group in groups_lc: for e in group: if e not in full_set_lc: @@ -135,7 +135,7 @@ class BlocksInfo: else: die(1,f'{uarg}: invalid parameter') - def __init__(self,cmd_args,opt,rpc): + def __init__(self,cfg,cmd_args,rpc): def parse_cs_uarg(uarg,full_set,dfl_set,desc): return ( @@ -144,10 +144,10 @@ class BlocksInfo: ) def get_fields(): - return parse_cs_uarg(opt.fields,list(self.fields),self.dfl_fields,'field') + return parse_cs_uarg(self.cfg.fields,list(self.fields),self.dfl_fields,'field') def get_stats(): - return parse_cs_uarg(opt.stats.lower(),self.all_stats,self.dfl_stats,'stat') + return parse_cs_uarg(self.cfg.stats.lower(),self.all_stats,self.dfl_stats,'stat') def parse_cmd_args(): # => (block_list, first, last, step) if not cmd_args: @@ -163,8 +163,8 @@ class BlocksInfo: else: return ([self.conv_blkspec(a) for a in cmd_args],None,None,None) + self.cfg = cfg self.rpc = rpc - self.opt = opt self.tip = rpc.blockcount from_satoshi = self.rpc.proto.coin_amt.satoshi @@ -198,7 +198,7 @@ class BlocksInfo: 'di': lambda arg: '{:.2e}'.format(arg), } - if g.coin == 'BCH': + if self.cfg.coin == 'BCH': self.fmt_funcs.update({ 'su': lambda arg: str(arg).rstrip('0').rstrip('.'), 'fe': lambda arg: str(int(arg * to_satoshi)), @@ -206,26 +206,26 @@ class BlocksInfo: }) self.fnames = tuple( - [f for f in self.fields if self.fields[f].src == 'bh' or f == 'interval'] if opt.header_info else - get_fields() if opt.fields else + [f for f in self.fields if self.fields[f].src == 'bh' or f == 'interval'] if self.cfg.header_info else + get_fields() if self.cfg.fields else self.dfl_fields ) - if opt.miner_info and 'miner' not in self.fnames: + if self.cfg.miner_info and 'miner' not in self.fnames: self.fnames += ('miner',) - self.stats = get_stats() if opt.stats else self.dfl_stats + self.stats = get_stats() if self.cfg.stats else self.dfl_stats # Display diff stats by default only if user-requested range ends with chain tip - if 'diff' in self.stats and not opt.stats and self.last != self.tip: + if 'diff' in self.stats and not self.cfg.stats and self.last != self.tip: self.stats.remove('diff') - if {'avg','col_avg'} <= set(self.stats) and opt.stats_only: + if {'avg','col_avg'} <= set(self.stats) and self.cfg.stats_only: self.stats.remove('col_avg') if {'avg','mini_avg'} <= set(self.stats): self.stats.remove('mini_avg') - if opt.full_stats: + if self.cfg.full_stats: add_fnames = {fname for sname in self.stats for fname in self.stats_deps[sname]} self.fnames = tuple(f for f in self.fields if f in {'block'} | set(self.fnames) | add_fnames ) else: @@ -397,7 +397,7 @@ class BlocksInfo: self.t_cur = self.prev_hdrs[n]['time'] ret = await self.process_block(self.hdrs[n]) self.res.append(ret) - if self.fnames and not self.opt.stats_only: + if self.fnames and not self.cfg.stats_only: self.output_block(ret,n) def output_block(self,data,n): @@ -447,7 +447,7 @@ class BlocksInfo: return '---' else: cb = bytes.fromhex(bd['vin'][0]['coinbase']) - if self.opt.raw_miner_info: + if self.cfg.raw_miner_info: return repr(cb) else: trmap_in = { @@ -670,7 +670,7 @@ class BlocksInfo: return ( sname, (d.hdr,) + tuple(gen()) ) def process_stats_pre(self,i): - if (self.fnames and not self.opt.stats_only) or i != 0: + if (self.fnames and not self.cfg.stats_only) or i != 0: Msg('') def finalize_output(self): pass @@ -707,9 +707,9 @@ class BlocksInfo: class JSONBlocksInfo(BlocksInfo): - def __init__(self,cmd_args,opt,rpc): - super().__init__(cmd_args,opt,rpc) - if opt.json_raw: + def __init__(self,cfg,cmd_args,opt,rpc): + super().__init__(cfg,cmd_args,opt,rpc) + if self.cfg.json_raw: self.output_block = self.output_block_raw self.fmt_stat_item = self.fmt_stat_item_raw Msg_r('{') diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index 6df9894..0b386fb 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -14,7 +14,6 @@ mmgen_node_tools.PeerBlocks: List blocks in flight, disconnect stalling nodes import asyncio from collections import namedtuple -from mmgen.globalvars import g from mmgen.util import msg,msg_r,is_int from mmgen.term import get_term,get_terminal_size,get_char from mmgen.ui import line_input @@ -30,15 +29,15 @@ class Display(PollDisplay): poll_secs = 2 - def __init__(self): + def __init__(self,cfg): - super().__init__() + super().__init__(cfg) global term,term_width if not term: term = get_term() term.init(noecho=True) - term_width = g.columns or get_terminal_size().width + term_width = self.cfg.columns or get_terminal_size().width msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) async def get_info(self,rpc): @@ -69,7 +68,7 @@ class Display(PollDisplay): msg('') term.reset() # readline required for correct operation here; without it, user must re-type first digit - ret = line_input('peer number> ',insert_txt=s,hold_protect=False) + ret = line_input( self.cfg, 'peer number> ', insert_txt=s, hold_protect=False ) term.init(noecho=True) self.enable_display = False # prevent display from updating before process_input() return ret diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py index bff4683..84a6023 100755 --- a/mmgen_node_tools/PollDisplay.py +++ b/mmgen_node_tools/PollDisplay.py @@ -22,7 +22,8 @@ class PollDisplay(): input = None poll_secs = 1 - def __init__(self): + def __init__(self,cfg): + self.cfg = cfg self.info_lock = threading.Lock() # self.info accessed by 2 threads self.display_kill_flag = threading.Event() diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 7fe8b1d..e4b147c 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -28,10 +28,8 @@ import sys,os,time,json,yaml from subprocess import run,PIPE,CalledProcessError from decimal import Decimal from collections import namedtuple -from mmgen.opts import opt -from mmgen.globalvars import g from mmgen.color import * -from mmgen.util import die,fmt_list,msg,msg_r,Msg,vmsg,suf,fmt,stdout_or_pager +from mmgen.util import die,fmt_list,msg,msg_r,suf,fmt homedir = os.getenv('HOME') cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') @@ -173,23 +171,23 @@ def get_src_data(curl_cmd): if cfg.btc_only: fn = os.path.join(cfg.cachedir,'ticker-btc.json') - timeout = 5 if g.test_suite else btc_ratelimit + timeout = 5 if gcfg.test_suite else btc_ratelimit else: fn = os.path.join(cfg.cachedir,'ticker.json') - timeout = 5 if g.test_suite else ratelimit + timeout = 5 if gcfg.test_suite else ratelimit fn_rel = os.path.relpath(fn,start=homedir) if not os.path.exists(fn): open(fn,'w').write('{}') - if opt.cached_data: + if gcfg.cached_data: json_text = open(fn).read() else: elapsed = int(time.time() - os.stat(fn).st_mtime) if elapsed >= timeout: msg_r(f'Fetching data from {api_host}...') - vmsg('') + gcfg._util.vmsg('') try: cp = run(curl_cmd,check=True,stdout=PIPE) except CalledProcessError as e: @@ -212,14 +210,14 @@ def get_src_data(curl_cmd): die(2,'Retrieved data is not valid JSON, exiting') if not data: - if opt.cached_data: + if gcfg.cached_data: die(1,'No cached data! Run command without --cached-data option to retrieve data from remote host') else: die(2,'Remote host returned no data!') elif 'error' in data: die(1,data['error']) - if opt.cached_data: + if gcfg.cached_data: msg(f'Using cached data from ~/{fn_rel}') else: open(fn,'w').write(json_text) @@ -248,7 +246,7 @@ def main(cfg_parm,cfg_in_parm): '--header', 'Accept: application/json', ] + (['--proxy', cfg.proxy] if cfg.proxy else []) + - (['--silent'] if not opt.verbose else []) + + (['--silent'] if not gcfg.verbose else []) + [api_url + ('/btc-bitcoin' if cfg.btc_only else '')] ) @@ -264,27 +262,27 @@ def main(cfg_parm,cfg_in_parm): update_sample_file(cfg_in.cfg_file) update_sample_file(cfg_in.portfolio_file) - if opt.portfolio and not cfg_in.portfolio: + if gcfg.portfolio and not cfg_in.portfolio: die(1,'No portfolio configured!\nTo configure a portfolio, edit the file ~/{}'.format( os.path.relpath(cfg_in.portfolio_file,start=homedir))) curl_cmd = get_curl_cmd() - if opt.print_curl: + if gcfg.print_curl: Msg(curl_cmd + '\n' + ' '.join(curl_cmd)) return parsed_json = [get_src_data(curl_cmd)] if cfg.btc_only else get_src_data(curl_cmd) - if opt.list_ids: + if gcfg.list_ids: from mmgen.ui import do_pager do_pager('\n'.join(e['id'] for e in parsed_json)) return global now - now = 1659465400 if g.test_suite else time.time() # 1659524400 1659445900 + now = 1659465400 if gcfg.test_suite else time.time() # 1659524400 1659445900 - stdout_or_pager( + gcfg._util.stdout_or_pager( '\n'.join(getattr(Ticker,cfg.clsname)(dict(gen_data(parsed_json))).gen_output()) + '\n' ) @@ -366,12 +364,12 @@ def make_cfg(cmd_args,cfg_in): usr_columns ) def get_portfolio_assets(ret=()): - if cfg_in.portfolio and opt.portfolio: + if cfg_in.portfolio and gcfg.portfolio: ret = (parse_asset_id(e,True) for e in cfg_in.portfolio) - return ( 'portfolio', tuple(e for e in ret if (not opt.btc) or e.symbol == 'BTC') ) + return ( 'portfolio', tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC') ) def get_portfolio(): - return {k:Decimal(v) for k,v in cfg_in.portfolio.items() if (not opt.btc) or k == 'btc-bitcoin'} + return {k:Decimal(v) for k,v in cfg_in.portfolio.items() if (not gcfg.btc) or k == 'btc-bitcoin'} def parse_add_precision(s): if not s: @@ -385,8 +383,8 @@ def make_cfg(cmd_args,cfg_in): def create_rows(): rows = ( ('trade_pair',) + query if (query and query.to_asset) else - ('bitcoin',parse_asset_id('btc-bitcoin')) if opt.btc else - get_rows_from_cfg( add_data={'fiat':['usd-us-dollar']} if opt.add_columns else None ) + ('bitcoin',parse_asset_id('btc-bitcoin')) if gcfg.btc else + get_rows_from_cfg( add_data={'fiat':['usd-us-dollar']} if gcfg.add_columns else None ) ) for hdr,data in ( @@ -416,8 +414,8 @@ def make_cfg(cmd_args,cfg_in): asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset']) asset_tuple = namedtuple('asset_tuple',['symbol','id']) - usr_rows = parse_usr_asset_arg(opt.add_rows) - usr_columns = parse_usr_asset_arg(opt.add_columns) + usr_rows = parse_usr_asset_arg(gcfg.add_rows) + usr_columns = parse_usr_asset_arg(gcfg.add_columns) query = parse_query_arg(cmd_args[0]) if cmd_args else None return cfg_tuple( @@ -425,19 +423,19 @@ def make_cfg(cmd_args,cfg_in): usr_rows = usr_rows, usr_columns = usr_columns, query = query, - adjust = ( lambda x: (100 + x) / 100 if x else 1 )( Decimal(opt.adjust or 0) ), + adjust = ( lambda x: (100 + x) / 100 if x else 1 )( Decimal(gcfg.adjust or 0) ), clsname = 'trading' if query else 'overview', - btc_only = opt.btc, - add_prec = parse_add_precision(opt.add_precision), - cachedir = opt.cachedir or cfg_in.cfg.get('cachedir') or cachedir, - proxy = None if opt.proxy == '' else (opt.proxy or cfg_in.cfg.get('proxy')), - portfolio = get_portfolio() if cfg_in.portfolio and opt.portfolio and not query else None + btc_only = gcfg.btc, + add_prec = parse_add_precision(gcfg.add_precision), + cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or cachedir, + proxy = None if gcfg.proxy == '' else (gcfg.proxy or cfg_in.cfg.get('proxy')), + portfolio = get_portfolio() if cfg_in.portfolio and gcfg.portfolio and not query else None ) def get_cfg_in(): ret = namedtuple('cfg_in_data',['cfg','portfolio','cfg_file','portfolio_file']) cfg_file,portfolio_file = ( - [os.path.join(g.data_dir_root,'node_tools',fn) for fn in (cfg_fn,portfolio_fn)] + [os.path.join(gcfg.data_dir_root,'node_tools',fn) for fn in (cfg_fn,portfolio_fn)] ) cfg_data,portfolio_data = ( [yaml.safe_load(open(fn).read()) if os.path.exists(fn) else None for fn in (cfg_file,portfolio_file)] @@ -466,10 +464,10 @@ class Ticker: def __init__(self,data): - self.comma = ',' if opt.thousands_comma else '' + self.comma = ',' if gcfg.thousands_comma else '' self.col1_wid = max(len('TOTAL'),( - max(len(self.create_label(d['id'])) for d in data.values()) if opt.name_labels else + max(len(self.create_label(d['id'])) for d in data.values()) if gcfg.name_labels else max(len(d['symbol']) for d in data.values()) )) + 1 @@ -482,7 +480,7 @@ class Ticker: def format_last_update_col(self,cross_assets=()): - if opt.elapsed: + if gcfg.elapsed: from mmgen.util2 import format_elapsed_hr fmt_func = format_elapsed_hr else: @@ -627,7 +625,7 @@ class Ticker: amt_fmt = amt_fmt.rstrip('0').rstrip('.') return self.fs_num.format( - lbl = (self.create_label(d['id']) if opt.name_labels else d['symbol']), + lbl = (self.create_label(d['id']) if gcfg.name_labels else d['symbol']), pc1 = fmt_pct(d.get('percent_change_7d')), pc2 = fmt_pct(d.get('percent_change_24h')), upd = d.get('last_updated_fmt'), @@ -670,13 +668,13 @@ class Ticker: [asset.id for asset in self.usr_col_assets] + [a for a,b in ( ( 'btc-bitcoin', not cfg.btc_only ), - ( 'pct7d', opt.percent_change ), - ( 'pct24h', opt.percent_change ), - ( 'update_time', opt.update_time ), + ( 'pct7d', gcfg.percent_change ), + ( 'pct24h', gcfg.percent_change ), + ( 'update_time', gcfg.update_time ), ) if b] ) cols2 = list(cols) - if opt.update_time: + if gcfg.update_time: cols2.pop() cols2.append('amt') @@ -759,7 +757,7 @@ class Ticker: if self.show_adj: self.fs_str += ' {p_adj}' self.hl_wid += self.max_wid + 1 - if opt.update_time: + if gcfg.update_time: self.fs_str += ' {upd}' self.hl_wid += self.upd_w + 2 @@ -772,7 +770,7 @@ class Ticker: if self.show_adj else '' ) return self.fs_str.format( - lbl = (self.create_label(id) if opt.name_labels else d['symbol']), + lbl = (self.create_label(id) if gcfg.name_labels else d['symbol']), p_spot = green(p_spot) if id in self.hl_ids else p_spot, p_adj = yellow(p_adj) if id in self.hl_ids else p_adj, upd = d.get('last_updated_fmt'), diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1bc37c8..4348263 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev14 +3.1.dev15 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index b0ab4f8..9f2e11e 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -71,7 +71,7 @@ def do_output_tabular(proto,addr_data,blk_hdrs): fs = ( ' {n:>%s} {a} {u} {b:>%s} {t:19} {B:>%s} {T:19} {A}' % (col1w,max(5,fb_w),max(4,lb_w)) - if opt.first_block else + if cfg.first_block else ' {n:>%s} {a} {u} {B:>%s} {T:19} {A}' % (col1w,max(4,lb_w)) ) Msg('\n' + fs.format( @@ -109,20 +109,19 @@ def do_output_tabular(proto,addr_data,blk_hdrs): async def main(req_addrs): - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) + proto = cfg._proto from mmgen.addr import CoinAddr addrs = [CoinAddr(proto,addr) for addr in req_addrs] from mmgen.rpc import rpc_init - rpc = await rpc_init(proto) + rpc = await rpc_init( cfg, proto ) height = await rpc.call('getblockcount') Msg(f'{proto.coin} {proto.network.upper()} [height {height}]') from mmgen.proto.btc.misc import scantxoutset - res = await scantxoutset( rpc, [f'addr({addr})' for addr in addrs] ) + res = await scantxoutset( cfg, rpc, [f'addr({addr})' for addr in addrs] ) if not res['success']: die(1,'UTXO scanning failed or was interrupted') @@ -155,16 +154,16 @@ async def main(req_addrs): blk_hashes = await rpc.batch_call('getblockhash', [(h,) for h in blk_heights]) blk_hdrs = await rpc.batch_call('getblockheader', [(H,) for H in blk_hashes]) - (do_output_tabular if opt.tabular else do_output)( proto, addr_data, dict(zip(blk_heights,blk_hdrs)) ) + (do_output_tabular if cfg.tabular else do_output)( proto, addr_data, dict(zip(blk_heights,blk_hdrs)) ) -cmd_args = opts.init(opts_data,init_opts={'rpc_backend':'aiohttp'}) +cfg = opts.init(opts_data,init_opts={'rpc_backend':'aiohttp'}) -if len(cmd_args) < 1: +if len(cfg._args) < 1: die(1,'This command requires at least one coin address argument') from mmgen.obj import CoinTxID,Int try: - async_run(main(cmd_args)) + async_run(main(cfg._args)) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index eb12a5c..38914c5 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -20,7 +20,9 @@ mmnode-blocks-info: Display information about a block or range of blocks """ -from mmgen.common import * +import mmgen.opts as opts +from mmgen.globalvars import gc +from mmgen.util import async_run,fmt_list from .BlocksInfo import BlocksInfo,JSONBlocksInfo opts_data = { @@ -146,29 +148,26 @@ EXAMPLES: This program requires a txindex-enabled daemon for correct operation. """ }, 'code': { - 'notes': lambda proto,s: s.format( + 'notes': lambda cfg,proto,s: s.format( I = proto.diff_adjust_interval, F = fmt_list(BlocksInfo.fields,fmt='bare'), S = fmt_list(BlocksInfo.all_stats,fmt='bare'), - p = g.prog_name, + p = gc.prog_name, ) } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) async def main(): - from mmgen.protocol import init_proto_from_opts from mmgen.rpc import rpc_init - proto = init_proto_from_opts(need_amt=True) + cls = JSONBlocksInfo if cfg.json else BlocksInfo - cls = JSONBlocksInfo if opt.json else BlocksInfo + m = cls( cfg, cfg._args, await rpc_init(cfg,cfg._proto) ) - m = cls( cmd_args, opt, await rpc_init(proto) ) - - if m.fnames and not opt.no_header: + if m.fnames and not cfg.no_header: m.print_header() await m.process_blocks() diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index ce1fbdb..f5212e3 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -39,7 +39,7 @@ fee_brackets = [ 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 2100000000000000, ] -opts.init({ +cfg = opts.init({ 'sets': [ ('detail',True,'ranges',True), ('detail',True,'show_mb_col',True), @@ -78,17 +78,17 @@ factors. } }) -if opt.ignore_below: - if opt.show_empty: +if cfg.ignore_below: + if cfg.show_empty: die(1,'Conflicting options: --ignore-below, --show-empty') - ignore_below = parse_bytespec(opt.ignore_below) + ignore_below = parse_bytespec(cfg.ignore_below) precision = ( - check_int_between(opt.precision,min_prec,max_prec,'--precision arg') - if opt.precision else dfl_prec ) + check_int_between(cfg.precision,min_prec,max_prec,'--precision arg') + if cfg.precision else dfl_prec ) from mmgen.term import get_terminal_size -width = g.columns or get_terminal_size().width +width = cfg.columns or get_terminal_size().width class fee_bracket: def __init__(self,top,bottom): @@ -103,6 +103,7 @@ def log(data,fn): from mmgen.rpc import json_encoder from mmgen.fileutil import write_data_to_file write_data_to_file( + cfg = cfg, outfile = fn, data = json.dumps(data,cls=json_encoder,sort_keys=True,indent=4), desc = 'mempool', @@ -130,9 +131,9 @@ def create_data(coin_amt,mempool): # calculate cumulative byte totals, filter rows: tBytes = 0 for i in out: - if not (i.tx_bytes or opt.show_empty): + if not (i.tx_bytes or cfg.show_empty): i.skip = True - if opt.ignore_below and i.tx_bytes < ignore_below: + if cfg.ignore_below and i.tx_bytes < ignore_below: i.skip = True i.tx_bytes_cum = tBytes tBytes += i.tx_bytes @@ -150,15 +151,15 @@ def gen_header(host,mempool,blockcount): TX count: {len(mempool)} """)).strip() - if opt.show_empty: + if cfg.show_empty: yield('Displaying all fee brackets') - elif opt.ignore_below: + elif cfg.ignore_below: yield('Ignoring fee brackets with less than {:,} bytes ({})'.format( ignore_below, int2bytespec(ignore_below,'MB','0.6',strip=True,add_space=True), )) - if opt.include_current: + if cfg.include_current: yield('Including transactions in current fee bracket in Total MB amounts') def fmt_mb(n): @@ -168,11 +169,11 @@ def gen_body(data): tx_bytes_max = max((i.tx_bytes for i in data),default=0) top_max = max((i.top for i in data),default=0) bot_max = max((i.bottom for i in data),default=0) - col1_w = max(len(f'{bot_max}-{top_max}') if opt.ranges else len(f'{top_max}'),6) - col2_w = len(fmt_mb(tx_bytes_max)) if opt.show_mb_col else 0 + col1_w = max(len(f'{bot_max}-{top_max}') if cfg.ranges else len(f'{top_max}'),6) + col2_w = len(fmt_mb(tx_bytes_max)) if cfg.show_mb_col else 0 col3_w = len(fmt_mb(data[-1].tx_bytes_cum)) if data else 0 col4_w = width - col1_w - col2_w - col3_w - (4 if col2_w else 3) - if opt.show_mb_col: + if cfg.show_mb_col: fs = '{a:<%i} {b:>%i} {c:>%i} {d}' % (col1_w,col2_w,col3_w) else: fs = '{a:<%i} {c:>%i} {d}' % (col1_w,col3_w) @@ -182,9 +183,9 @@ def gen_body(data): for i in data: if not i.skip: - cum_bytes = i.tx_bytes_cum + i.tx_bytes if opt.include_current else i.tx_bytes_cum + cum_bytes = i.tx_bytes_cum + i.tx_bytes if cfg.include_current else i.tx_bytes_cum yield(fs.format( - a = '{}-{}'.format(i.bottom,i.top) if opt.ranges else i.top, + a = '{}-{}'.format(i.bottom,i.top) if cfg.ranges else i.top, b = fmt_mb(i.tx_bytes), c = fmt_mb(cum_bytes), d = '-' * int(col4_w * ( i.tx_bytes / tx_bytes_max )) )) @@ -197,20 +198,19 @@ def gen_body(data): async def main(): - from mmgen.protocol import init_proto_from_opts global proto - proto = init_proto_from_opts(need_amt=True) + proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init(proto) + c = await rpc_init( cfg, proto ) mempool = await c.call('getrawmempool',True) - if opt.log: + if cfg.log: log(mempool,'mempool.json') data = create_data(proto.coin_amt,mempool) - stdout_or_pager( + cfg._util.stdout_or_pager( '\n'.join(gen_header( c.host, mempool, diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index 5cbf2b8..e92d161 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -26,7 +26,7 @@ from mmgen.common import * bdr_proj = 9.95 -opts.init({ +cfg = opts.init({ 'sets': [('mined',True,'list',True)], 'text': { 'desc': 'Estimate date(s) of future block subsidy halving(s)', @@ -43,8 +43,8 @@ opts.init({ """ } }) -if opt.bdr_proj: - bdr_proj = float(opt.bdr_proj) +if cfg.bdr_proj: + bdr_proj = float(cfg.bdr_proj) def date(t): return '{}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*time.gmtime(t)[:6]) @@ -61,16 +61,15 @@ def time_diff_warning(t_diff): async def main(): - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) + proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init(proto) + c = await rpc_init( cfg, proto ) tip = await c.call('getblockcount') assert tip > 1, 'block tip must be > 1' remaining = proto.halving_interval - tip % proto.halving_interval - sample_size = int(opt.sample_size) if opt.sample_size else min(tip-1,max(remaining,144)) + sample_size = int(cfg.sample_size) if cfg.sample_size else min(tip-1,max(remaining,144)) cur,old = await c.gathered_call('getblockstats',((tip,),(tip - sample_size,))) @@ -134,7 +133,7 @@ async def main(): fs = ( ' {a:<7} {b:>8} {c:19}{d:2} {e:10} {f}', ' {a:<7} {b:>8} {c:19}{d:2} {e:10} {f:17} {g:17} {h}' - )[bool(opt.mined)] + )[bool(cfg.mined)] print( f'Historical/Estimated/Projected Halvings ({proto.coin}):\n\n' @@ -174,7 +173,7 @@ async def main(): ) for n,sub,blk,mined,total_mined,bdr,t in gen_data()) ) - if opt.list: + if cfg.list: await print_halvings() else: print_current_stats() diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index fd86983..78c9823 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -34,17 +34,14 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) ERASE_LINE,CUR_UP = '\033[K','\033[1A' async def main(): - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts() - from mmgen.rpc import rpc_init - c = await rpc_init(proto) + c = await rpc_init( cfg, cfg._proto ) async def get_data(): d = await c.call('getnettotals') diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index 9e6a0ec..af723c9 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -20,7 +20,7 @@ mmnode-peerblocks: List blocks in flight, disconnect stalling nodes """ -from mmgen.opts import init +import mmgen.opts as opts from mmgen.util import async_run opts_data = { @@ -36,17 +36,14 @@ opts_data = { async def main(): - init(opts_data) - - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts() + cfg = opts.init(opts_data) from mmgen.rpc import rpc_init - rpc = await rpc_init(proto) + rpc = await rpc_init( cfg, cfg._proto ) from .PeerBlocks import BlocksDisplay,PeersDisplay - blocks = BlocksDisplay() - peers = PeersDisplay() + blocks = BlocksDisplay(cfg) + peers = PeersDisplay(cfg) while True: await blocks.run(rpc) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index a25e884..da96bb8 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -197,12 +197,15 @@ To add a portfolio, edit the file } } -cmd_args = opts.init(opts_data,do_post_init=True) +gcfg = opts.init(opts_data,do_post_init=True) + +import mmgen_node_tools.Ticker as Ticker +Ticker.gcfg = gcfg cfg_in = get_cfg_in() -cfg = make_cfg(cmd_args,cfg_in) +cfg = make_cfg(gcfg._args,cfg_in) -opts.post_init() +opts.post_init(gcfg) main(cfg,cfg_in) diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index d0a04e2..4c0bcc2 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -58,14 +58,11 @@ async def main(txid): if len(txid) != 64 or not is_hex_str(txid): die(2,f'{txid}: invalid transaction ID') - if opt.verbose: + if cfg.verbose: msg(f'TxID: {txid}') - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts() - from mmgen.rpc import rpc_init - c = await rpc_init(proto) + c = await rpc_init(cfg._proto) exitval = 0 try: @@ -85,11 +82,11 @@ async def main(txid): return exitval -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -msgs = msg_data['quiet' if opt.quiet else 'normal'] +msgs = msg_data['quiet' if cfg.quiet else 'normal'] -if len(cmd_args) != 1: +if len(cfg._args) != 1: die(1,'One transaction ID must be specified') -sys.exit(async_run(main(cmd_args[0]))) +sys.exit(async_run(main(cfg._args[0]))) diff --git a/setup.cfg b/setup.cfg index 65b9639..e669735 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev20 + mmgen>=13.3.dev42 packages = mmgen_node_tools diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index 171ffc5..162e3fb 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -45,7 +45,7 @@ class TestSuiteMain(TestSuiteBase): f'mmnode-peerblocks', args, pexpect_spawn = pexpect_spawn ) - if opt.exact_output: # disable echoing of input + if cfg.exact_output: # disable echoing of input t.p.logfile = None t.p.logfile_read = sys.stdout if expect_list: diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index cc4c22d..c4536d6 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -97,7 +97,7 @@ class TestSuiteScripts(TestSuiteBase): @property def nt_datadir(self): - return os.path.join( g.data_dir_root, 'node_tools' ) + return os.path.join( cfg.data_dir_root, 'node_tools' ) def ticker_setup(self): self.spawn('',msg_only=True) @@ -120,7 +120,7 @@ class TestSuiteScripts(TestSuiteBase): def ticker2(self): t = self.ticker(cached=False) - if not opt.skipping_deps: + if not cfg.skipping_deps: t.expect('Creating') t.expect('Creating') t.expect('proxy host could not be resolved') diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index fd19099..e208373 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -13,8 +13,6 @@ test.test_py_d.ts_regtest: Regtest tests for the test.py test suite """ import os -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.util import die,gmsg from mmgen.protocol import init_proto from mmgen.proto.btc.regtest import MMGenRegtest @@ -28,7 +26,7 @@ args2 = ['--bob','--rpc-backend=http'] def gen_addrs(proto,network,keys): from mmgen.tool.api import tool_api - tool = tool_api() + tool = tool_api(cfg) tool.init_coin(proto.coin,'regtest') tool.addrtype = proto.mmtypes[-1] return [tool.privhex2addr('{:064x}'.format(key)) for key in keys] @@ -105,9 +103,9 @@ class TestSuiteRegtest(TestSuiteBase): return if self.proto.testnet: die(2,'--testnet and --regtest options incompatible with regtest test suite') - self.proto = init_proto(self.proto.coin,network='regtest',need_amt=True) + self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) - self.regtest = MMGenRegtest(self.proto.coin) + self.regtest = MMGenRegtest(cfg,self.proto.coin) def setup(self): stop_test_daemons(self.proto.network_id,force=True,remove_datadir=True) @@ -128,7 +126,7 @@ class TestSuiteRegtest(TestSuiteBase): return self.halving_calculator(['--help'],['USAGE:']) def halving_calculator2(self): - return self.halving_calculator([],['Current block: 393',f'Current block subsidy: 12.5 {g.coin}']) + return self.halving_calculator([],['Current block: 393',f'Current block subsidy: 12.5 {cfg.coin}']) def halving_calculator3(self): return self.halving_calculator(['--list'],['33 4950','0']) @@ -158,7 +156,7 @@ class TestSuiteRegtest(TestSuiteBase): return self.addrbal( args2 + [self.addrs[0]], [ - f'Balance: 0.357 {g.coin}', + f'Balance: 0.357 {cfg.coin}', '2 unspent outputs in 2 blocks', '394','0.123', '395','0.234' @@ -246,7 +244,7 @@ class TestSuiteRegtest(TestSuiteBase): ]) def blocks_info4(self): - n1,i1,o1,n2,i2,o2 = (2,1,3,6,3,9) if g.coin == 'BCH' else (2,1,4,6,3,12) + n1,i1,o1,n2,i2,o2 = (2,1,3,6,3,9) if cfg.coin == 'BCH' else (2,1,4,6,3,12) return self.blocks_info( args1 + ['--miner-info','--fields=all','--stats=all','+3'], [ 'Averages', f'nTx: {n1}', @@ -267,7 +265,7 @@ class TestSuiteRegtest(TestSuiteBase): from mmgen.tool.api import tool_api from collections import namedtuple - t = tool_api() + t = tool_api(cfg) t.init_coin(self.proto.coin,self.proto.network) t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32' wp = namedtuple('wifaddrpair',['wif','addr']) @@ -384,7 +382,7 @@ class TestSuiteRegtest(TestSuiteBase): return self._feeview([]) def stop(self): - if opt.no_daemon_stop: + if cfg.no_daemon_stop: self.spawn('',msg_only=True) msg_r('(leaving daemon running by user request)') return 'ok' diff --git a/test/unit_tests_d/ut_BlocksInfo.py b/test/unit_tests_d/ut_BlocksInfo.py index ab09882..e65e314 100755 --- a/test/unit_tests_d/ut_BlocksInfo.py +++ b/test/unit_tests_d/ut_BlocksInfo.py @@ -7,6 +7,8 @@ from mmgen.common import * from mmgen.exception import * from mmgen_node_tools.BlocksInfo import BlocksInfo +from ..include.common import cfg,vmsg + tip = 50000 range_vecs = ( # First Last FromTip nBlocks Step First Last BlockList @@ -70,12 +72,13 @@ class dummyRPC: class coin_amt: satoshi = 0.00000001 -class dummyOpt: +class dummyCfg: fields = None stats = None miner_info = None header_info = None full_stats = None + coin = 'BTC' class unit_tests: @@ -91,7 +94,7 @@ class unit_tests: def parse_rangespec(self,name,ut): - b = BlocksInfo(0,dummyOpt(),dummyRPC()) + b = BlocksInfo(dummyCfg(),None,dummyRPC()) def test(spec,chk,foo): ret = b.parse_rangespec(spec) @@ -108,8 +111,8 @@ class unit_tests: def test(spec,foo,chk): b = BlocksInfo( + dummyCfg(), spec if type(spec) == tuple else [spec], - dummyOpt(), dummyRPC() ) ret = (b.first,b.last,b.block_list) vmsg('{:13} => {}'.format( From 89bda2f00fe45b9c481dd78cdf060df1cf4fd476 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 2 Apr 2023 10:12:47 +0000 Subject: [PATCH 091/175] update for MMGen v13.3.dev43 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 4348263..6496c42 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev15 +3.1.dev16 diff --git a/setup.cfg b/setup.cfg index e669735..6eb85ec 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev42 + mmgen>=13.3.dev43 packages = mmgen_node_tools From 435256a99affa5eb59b8d04c486610579b46bee7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 4 Apr 2023 16:00:08 +0000 Subject: [PATCH 092/175] test.py regtest: add test group for `mmnode-netrate` --- test/test_py_d/ts_regtest.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index e208373..9b72cb2 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -41,6 +41,7 @@ class TestSuiteRegtest(TestSuiteBase): deterministic = False cmd_group_in = ( ('setup', 'regtest mode setup'), + ('subgroup.netrate', []), ('subgroup.halving_calculator', []), ('subgroup.fund_addrbal', []), ('subgroup.addrbal', ['fund_addrbal']), @@ -49,6 +50,11 @@ class TestSuiteRegtest(TestSuiteBase): ('stop', 'stopping regtest daemon'), ) cmd_subgroups = { + 'netrate': ( + "'mmnode-netrate' script", + ('netrate1', "netrate (--help)"), + ('netrate2', "netrate"), + ), 'halving_calculator': ( "'mmnode-halving-calculator' script", ('halving_calculator1', "halving calculator (--help)"), @@ -117,6 +123,19 @@ class TestSuiteRegtest(TestSuiteBase): t.expect(s) return t + def netrate(self,add_args,expect_str): + t = self.spawn( 'mmnode-netrate', args1 + add_args ) + t.expect(expect_str,regex=True) + return t + + def netrate1(self): + return self.netrate( ['--help'], 'USAGE:.*' ) + + def netrate2(self): + t = self.netrate( [], 'sent:.*' ) + t.kill(2) + return t + def halving_calculator(self,add_args,expect_list): t = self.spawn('mmnode-halving-calculator',args1+add_args) t.match_expect_list(expect_list) From a1beac67e234c40a6869dac1ebb4a4d1ba9ed483 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 4 Apr 2023 16:00:29 +0000 Subject: [PATCH 093/175] update for MMGen v13.3.dev44 --- mmgen_node_tools/BlocksInfo.py | 5 +++-- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 11 ++++++----- mmgen_node_tools/main_blocks_info.py | 7 +++---- mmgen_node_tools/main_feeview.py | 11 +++++++---- mmgen_node_tools/main_halving_calculator.py | 10 +++++++--- mmgen_node_tools/main_netrate.py | 10 ++++++---- mmgen_node_tools/main_peerblocks.py | 9 ++++----- mmgen_node_tools/main_ticker.py | 6 +++--- mmgen_node_tools/main_txfind.py | 7 ++++--- setup.cfg | 2 +- 11 files changed, 45 insertions(+), 35 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 70d9821..25e1caf 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -24,7 +24,7 @@ import re,json from collections import namedtuple from time import strftime,gmtime -from mmgen.common import * +from mmgen.util import msg,Msg,Msg_r,die,suf,secs_to_ms,secs_to_dhms,is_int from mmgen.rpc import json_encoder class BlocksInfo: @@ -362,7 +362,8 @@ class BlocksInfo: first = self.conv_blkspec(first) last = self.conv_blkspec(last or first) - if p.debug: msg(repr(self.range_data(first,last,from_tip,nblocks,step))) + if p.debug: + msg(repr(self.range_data(first,last,from_tip,nblocks,step))) if first > last: die(1,f'{first}-{last}: invalid block range') diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 6496c42..623245b 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev16 +3.1.dev17 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 9f2e11e..177bac4 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -12,7 +12,10 @@ mmnode-addrbal: Get balances for arbitrary addresses in the blockchain """ -from mmgen.common import * +from mmgen.obj import CoinTxID,Int +from mmgen.cfg import Config +from mmgen.util import msg,Msg,die,suf,make_timestr,async_run +from mmgen.color import red opts_data = { 'text': { @@ -115,7 +118,7 @@ async def main(req_addrs): addrs = [CoinAddr(proto,addr) for addr in req_addrs] from mmgen.rpc import rpc_init - rpc = await rpc_init( cfg, proto ) + rpc = await rpc_init(cfg) height = await rpc.call('getblockcount') Msg(f'{proto.coin} {proto.network.upper()} [height {height}]') @@ -156,13 +159,11 @@ async def main(req_addrs): (do_output_tabular if cfg.tabular else do_output)( proto, addr_data, dict(zip(blk_heights,blk_hdrs)) ) -cfg = opts.init(opts_data,init_opts={'rpc_backend':'aiohttp'}) +cfg = Config( opts_data=opts_data, init_opts={'rpc_backend':'aiohttp'} ) if len(cfg._args) < 1: die(1,'This command requires at least one coin address argument') -from mmgen.obj import CoinTxID,Int - try: async_run(main(cfg._args)) except KeyboardInterrupt: diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index 38914c5..1fe5e82 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -20,8 +20,7 @@ mmnode-blocks-info: Display information about a block or range of blocks """ -import mmgen.opts as opts -from mmgen.globalvars import gc +from mmgen.cfg import gc,Config from mmgen.util import async_run,fmt_list from .BlocksInfo import BlocksInfo,JSONBlocksInfo @@ -157,7 +156,7 @@ This program requires a txindex-enabled daemon for correct operation. } } -cfg = opts.init(opts_data) +cfg = Config(opts_data=opts_data) async def main(): @@ -165,7 +164,7 @@ async def main(): cls = JSONBlocksInfo if cfg.json else BlocksInfo - m = cls( cfg, cfg._args, await rpc_init(cfg,cfg._proto) ) + m = cls( cfg, cfg._args, await rpc_init(cfg) ) if m.fnames and not cfg.no_header: m.print_header() diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index f5212e3..527dc85 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -20,7 +20,8 @@ mmnode-feeview: Visualize the fee structure of a node’s mempool """ -from mmgen.common import * +from mmgen.cfg import Config +from mmgen.util import async_run,die,fmt,make_timestr,check_int_between from mmgen.util2 import int2bytespec,parse_bytespec min_prec,max_prec,dfl_prec = (0,6,4) @@ -39,7 +40,7 @@ fee_brackets = [ 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 2100000000000000, ] -cfg = opts.init({ +opts_data = { 'sets': [ ('detail',True,'ranges',True), ('detail',True,'show_mb_col',True), @@ -75,8 +76,10 @@ Note that there is no global mempool in Bitcoin, and your node’s mempool may differ significantly from those of mining nodes depending on uptime and other factors. """ + } } -}) + +cfg = Config(opts_data=opts_data) if cfg.ignore_below: if cfg.show_empty: @@ -202,7 +205,7 @@ async def main(): proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init( cfg, proto ) + c = await rpc_init(cfg) mempool = await c.call('getrawmempool',True) diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index e92d161..1e35b2a 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -22,11 +22,13 @@ mmnode-halving-calculator: Estimate date(s) of future block subsidy halving(s) import time from decimal import Decimal -from mmgen.common import * + +from mmgen.cfg import Config +from mmgen.util import async_run bdr_proj = 9.95 -cfg = opts.init({ +opts_data = { 'sets': [('mined',True,'list',True)], 'text': { 'desc': 'Estimate date(s) of future block subsidy halving(s)', @@ -41,7 +43,9 @@ cfg = opts.init({ -s, --sample-size=N Block range to calculate block discovery interval for next halving estimate (default: dynamically calculated) """ } -}) +} + +cfg = Config(opts_data=opts_data) if cfg.bdr_proj: bdr_proj = float(cfg.bdr_proj) diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index 78c9823..da0b4dc 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -20,8 +20,10 @@ mmnode-netrate: Bitcoin daemon network rate monitor """ -import time -from mmgen.common import * +import sys,time + +from mmgen.cfg import Config +from mmgen.util import async_run opts_data = { 'text': { @@ -34,14 +36,14 @@ opts_data = { } } -cfg = opts.init(opts_data) +cfg = Config(opts_data=opts_data) ERASE_LINE,CUR_UP = '\033[K','\033[1A' async def main(): from mmgen.rpc import rpc_init - c = await rpc_init( cfg, cfg._proto ) + c = await rpc_init(cfg) async def get_data(): d = await c.call('getnettotals') diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index af723c9..56b412c 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -20,9 +20,6 @@ mmnode-peerblocks: List blocks in flight, disconnect stalling nodes """ -import mmgen.opts as opts -from mmgen.util import async_run - opts_data = { 'text': { 'desc': 'List blocks in flight, disconnect stalling nodes', @@ -36,10 +33,11 @@ opts_data = { async def main(): - cfg = opts.init(opts_data) + from mmgen.cfg import Config + cfg = Config(opts_data=opts_data) from mmgen.rpc import rpc_init - rpc = await rpc_init( cfg, cfg._proto ) + rpc = await rpc_init(cfg) from .PeerBlocks import BlocksDisplay,PeersDisplay blocks = BlocksDisplay(cfg) @@ -49,4 +47,5 @@ async def main(): await blocks.run(rpc) await peers.run(rpc) +from mmgen.util import async_run async_run(main()) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index da96bb8..83d0b1c 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -13,7 +13,6 @@ mmnode-ticker: Display price information for cryptocurrency and other assets """ import sys,os -from mmgen.common import * from .Ticker import * opts_data = { @@ -197,7 +196,8 @@ To add a portfolio, edit the file } } -gcfg = opts.init(opts_data,do_post_init=True) +from mmgen.cfg import Config +gcfg = Config( opts_data=opts_data, do_post_init=True ) import mmgen_node_tools.Ticker as Ticker Ticker.gcfg = gcfg @@ -206,6 +206,6 @@ cfg_in = get_cfg_in() cfg = make_cfg(gcfg._args,cfg_in) -opts.post_init(gcfg) +gcfg._post_init() main(cfg,cfg_in) diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index 4c0bcc2..cabed6f 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -20,7 +20,8 @@ mmnode-txfind: Find a transaction in the blockchain or mempool """ -from mmgen.common import * +from mmgen.cfg import Config +from mmgen.util import msg,Msg,die opts_data = { 'text': { @@ -62,7 +63,7 @@ async def main(txid): msg(f'TxID: {txid}') from mmgen.rpc import rpc_init - c = await rpc_init(cfg._proto) + c = await rpc_init(cfg) exitval = 0 try: @@ -82,7 +83,7 @@ async def main(txid): return exitval -cfg = opts.init(opts_data) +cfg = Config(opts_data=opts_data) msgs = msg_data['quiet' if cfg.quiet else 'normal'] diff --git a/setup.cfg b/setup.cfg index 6eb85ec..0cdbed1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev43 + mmgen>=13.3.dev44 packages = mmgen_node_tools From c373ed7a2db983442b8b9f20b19a7754e05c280c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 1 Jul 2023 16:47:49 +0000 Subject: [PATCH 094/175] rpc_init(): skip wallet initialization --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 2 +- mmgen_node_tools/main_blocks_info.py | 2 +- mmgen_node_tools/main_feeview.py | 2 +- mmgen_node_tools/main_halving_calculator.py | 2 +- mmgen_node_tools/main_netrate.py | 2 +- mmgen_node_tools/main_peerblocks.py | 2 +- mmgen_node_tools/main_txfind.py | 2 +- test/test_py_d/ts_regtest.py | 5 +++-- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 623245b..b473ce1 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev17 +3.2.dev0 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 177bac4..a461e9e 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -118,7 +118,7 @@ async def main(req_addrs): addrs = [CoinAddr(proto,addr) for addr in req_addrs] from mmgen.rpc import rpc_init - rpc = await rpc_init(cfg) + rpc = await rpc_init(cfg,ignore_wallet=True) height = await rpc.call('getblockcount') Msg(f'{proto.coin} {proto.network.upper()} [height {height}]') diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index 1fe5e82..c2bbf59 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -164,7 +164,7 @@ async def main(): cls = JSONBlocksInfo if cfg.json else BlocksInfo - m = cls( cfg, cfg._args, await rpc_init(cfg) ) + m = cls( cfg, cfg._args, await rpc_init(cfg,ignore_wallet=True) ) if m.fnames and not cfg.no_header: m.print_header() diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 527dc85..72692fc 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -205,7 +205,7 @@ async def main(): proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init(cfg) + c = await rpc_init(cfg,ignore_wallet=True) mempool = await c.call('getrawmempool',True) diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index 1e35b2a..1648d76 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -68,7 +68,7 @@ async def main(): proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init( cfg, proto ) + c = await rpc_init( cfg, proto, ignore_wallet=True ) tip = await c.call('getblockcount') assert tip > 1, 'block tip must be > 1' diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index da0b4dc..f1661c5 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -43,7 +43,7 @@ ERASE_LINE,CUR_UP = '\033[K','\033[1A' async def main(): from mmgen.rpc import rpc_init - c = await rpc_init(cfg) + c = await rpc_init(cfg,ignore_wallet=True) async def get_data(): d = await c.call('getnettotals') diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index 56b412c..6ebb5a3 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -37,7 +37,7 @@ async def main(): cfg = Config(opts_data=opts_data) from mmgen.rpc import rpc_init - rpc = await rpc_init(cfg) + rpc = await rpc_init(cfg,ignore_wallet=True) from .PeerBlocks import BlocksDisplay,PeersDisplay blocks = BlocksDisplay(cfg) diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index cabed6f..cc8df14 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -63,7 +63,7 @@ async def main(txid): msg(f'TxID: {txid}') from mmgen.rpc import rpc_init - c = await rpc_init(cfg) + c = await rpc_init(cfg,ignore_wallet=True) exitval = 0 try: diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 9b72cb2..f8859b8 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -132,8 +132,9 @@ class TestSuiteRegtest(TestSuiteBase): return self.netrate( ['--help'], 'USAGE:.*' ) def netrate2(self): - t = self.netrate( [], 'sent:.*' ) - t.kill(2) + t = self.netrate( [], r'sent:.*' ) + t.kill(15) + t.req_exit_val = -15 return t def halving_calculator(self,add_args,expect_list): From 6074a0e42a4b2b88e07ed47584553eb4c7b76125 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 25 Sep 2023 15:53:02 +0000 Subject: [PATCH 095/175] mmnode-ticker: fixes and cleanups throughout --- mmgen_node_tools/Ticker.py | 120 ++++++++++++++++---------- mmgen_node_tools/data/ticker-cfg.yaml | 13 +-- mmgen_node_tools/main_ticker.py | 28 +++--- test/test_py_d/ts_misc.py | 8 +- 4 files changed, 102 insertions(+), 67 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index e4b147c..a370b56 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -28,11 +28,12 @@ import sys,os,time,json,yaml from subprocess import run,PIPE,CalledProcessError from decimal import Decimal from collections import namedtuple + from mmgen.color import * -from mmgen.util import die,fmt_list,msg,msg_r,suf,fmt +from mmgen.util import msg,msg_r,Msg,die,Die,suf,fmt,fmt_list homedir = os.getenv('HOME') -cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') +dfl_cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') cfg_fn = 'ticker-cfg.yaml' portfolio_fn = 'ticker-portfolio.yaml' @@ -78,7 +79,7 @@ def gen_data(data): die(1,'Missing data, exiting') rows_want = { - 'id': {r.id for r in cfg.rows if getattr(r,'id',None)} - {'usd-us-dollar'}, + 'id': {r.id for r in cfg.rows if isinstance(r,tuple) and r.id} - {'usd-us-dollar'}, 'symbol': {r.symbol for r in cfg.rows if isinstance(r,tuple) and r.id is None} - {'USD'}, } usr_rate_assets = tuple(u.rate_asset for u in cfg.usr_rows + cfg.usr_columns if u.rate_asset) @@ -100,25 +101,29 @@ def gen_data(data): found = { 'id': set(), 'symbol': set() } rate_assets = {} - for k in ['id','symbol']: - wants = rows_want[k] | usr_wants[k] - if wants: - for d in data: - if d[k] in wants: - if d[k] in found[k]: - die(1,dup_sym_errmsg(d[k])) - yield (d['id'],d) - found[k].add(d[k]) - if d[k] in usr_rate_assets_want[k]: - rate_assets[d['symbol']] = d # NB: using symbol instead of ID - if k == 'id' and len(found[k]) == len(wants): - break + wants = {k:rows_want[k] | usr_wants[k] for k in ('id','symbol')} for d in data: if d['id'] == 'btc-bitcoin': btcusd = Decimal(d['price_usd']) break + for k in ('id','symbol'): + for d in data: + if wants[k]: + if d[k] in wants[k]: + if d[k] in found[k]: + die(1,dup_sym_errmsg(d[k])) + yield (d['id'],d) + found[k].add(d[k]) + wants[k].remove(d[k]) + if d[k] in usr_rate_assets_want[k]: + rate_assets[d['symbol']] = d # NB: using symbol instead of ID for key + else: + break + + check_assets_found(usr_wants,found) + for asset in (cfg.usr_rows + cfg.usr_columns): if asset.rate: """ @@ -129,21 +134,21 @@ def gen_data(data): yield ( _id, { 'symbol': asset.symbol, 'id': _id, + 'name': ' '.join(_id.split('-')[1:]), 'price_usd': str(Decimal(ra_rate/asset.rate)), 'price_btc': str(Decimal(ra_rate/asset.rate/btcusd)), - 'last_updated': int(now), + 'last_updated': None, }) yield ('usd-us-dollar', { 'symbol': 'USD', 'id': 'usd-us-dollar', + 'name': 'US Dollar', 'price_usd': '1.0', 'price_btc': str(Decimal(1/btcusd)), - 'last_updated': int(now), + 'last_updated': None, }) - check_assets_found(usr_wants,found) - def get_src_data(curl_cmd): tor_captcha_msg = f""" @@ -166,8 +171,8 @@ def get_src_data(curl_cmd): ('' if cfg.btc_only else ', or use --cached-data or --btc') ) - if not os.path.exists(cachedir): - os.makedirs(cachedir) + if not os.path.exists(cfg.cachedir): + os.makedirs(cfg.cachedir) if cfg.btc_only: fn = os.path.join(cfg.cachedir,'ticker-btc.json') @@ -225,18 +230,18 @@ def get_src_data(curl_cmd): return data -def main(cfg_parm,cfg_in_parm): +def main(): def update_sample_file(usr_cfg_file): - src_data = files('mmgen_node_tools').joinpath('data',os.path.basename(usr_cfg_file)).read_text() + usr_data = files('mmgen_node_tools').joinpath('data',os.path.basename(usr_cfg_file)).read_text() sample_file = usr_cfg_file + '.sample' sample_data = open(sample_file).read() if os.path.exists(sample_file) else None - if src_data != sample_data: + if usr_data != sample_data: os.makedirs(os.path.dirname(sample_file),exist_ok=True) msg('{} {}'.format( ('Updating','Creating')[sample_data is None], sample_file )) - open(sample_file,'w').write(src_data) + open(sample_file,'w').write(usr_data) def get_curl_cmd(): return ([ @@ -251,8 +256,6 @@ def main(cfg_parm,cfg_in_parm): ) global cfg,cfg_in - cfg = cfg_parm - cfg_in = cfg_in_parm try: from importlib.resources import files # Python 3.9 @@ -272,21 +275,23 @@ def main(cfg_parm,cfg_in_parm): Msg(curl_cmd + '\n' + ' '.join(curl_cmd)) return - parsed_json = [get_src_data(curl_cmd)] if cfg.btc_only else get_src_data(curl_cmd) + src_data = [get_src_data(curl_cmd)] if cfg.btc_only else get_src_data(curl_cmd) if gcfg.list_ids: from mmgen.ui import do_pager - do_pager('\n'.join(e['id'] for e in parsed_json)) + do_pager('\n'.join(e['id'] for e in src_data)) return global now now = 1659465400 if gcfg.test_suite else time.time() # 1659524400 1659445900 + data = dict(gen_data(src_data)) + gcfg._util.stdout_or_pager( - '\n'.join(getattr(Ticker,cfg.clsname)(dict(gen_data(parsed_json))).gen_output()) + '\n' + '\n'.join(getattr(Ticker,cfg.clsname)(data).gen_output()) + '\n' ) -def make_cfg(cmd_args,cfg_in): +def make_cfg(): def get_rows_from_cfg(add_data=None): def gen(): @@ -295,7 +300,7 @@ def make_cfg(cmd_args,cfg_in): if add_data and k in add_data: v += tuple(add_data[k]) for e in v: - yield parse_asset_id(e,True) + yield parse_asset_id(e,require_label=True) return tuple(gen()) def parse_asset_id(s,require_label=False): @@ -304,7 +309,7 @@ def make_cfg(cmd_args,cfg_in): die(1,f'{s!r}: asset label is missing') return asset_tuple( sym.upper(), (s.lower() if label else None) ) - def parse_usr_asset_arg(s): + def parse_usr_asset_arg(key,use_cf_file=False): """ asset_id[:rate[:rate_asset]] """ @@ -324,7 +329,9 @@ def make_cfg(cmd_args,cfg_in): Decimal(rate) ), rate_asset = parse_asset_id(rate_asset) if rate_asset else None ) - return tuple(parse_parm(s2) for s2 in s.split(',')) if s else () + cl_opt = getattr(gcfg,key) + cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else [] + return tuple( parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt) ) def parse_query_arg(s): """ @@ -365,7 +372,7 @@ def make_cfg(cmd_args,cfg_in): def get_portfolio_assets(ret=()): if cfg_in.portfolio and gcfg.portfolio: - ret = (parse_asset_id(e,True) for e in cfg_in.portfolio) + ret = (parse_asset_id(e,require_label=True) for e in cfg_in.portfolio) return ( 'portfolio', tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC') ) def get_portfolio(): @@ -410,15 +417,30 @@ def make_cfg(cmd_args,cfg_in): 'proxy', 'portfolio' ]) + global cfg_in,cfg + + cmd_args = gcfg._args + cfg_in = get_cfg_in() + query_tuple = namedtuple('query',['asset','to_asset']) asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset']) asset_tuple = namedtuple('asset_tuple',['symbol','id']) - usr_rows = parse_usr_asset_arg(gcfg.add_rows) - usr_columns = parse_usr_asset_arg(gcfg.add_columns) + usr_rows = parse_usr_asset_arg('add_rows') + usr_columns = parse_usr_asset_arg('add_columns',use_cf_file=True) query = parse_query_arg(cmd_args[0]) if cmd_args else None - return cfg_tuple( + def get_proxy(name): + proxy = getattr(gcfg,name) + return ( + '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none') + else (proxy or cfg_in.cfg.get(name)) + ) + + proxy = get_proxy('proxy') + proxy = None if proxy == 'none' else proxy + + cfg = cfg_tuple( rows = create_rows(), usr_rows = usr_rows, usr_columns = usr_columns, @@ -427,8 +449,8 @@ def make_cfg(cmd_args,cfg_in): clsname = 'trading' if query else 'overview', btc_only = gcfg.btc, add_prec = parse_add_precision(gcfg.add_precision), - cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or cachedir, - proxy = None if gcfg.proxy == '' else (gcfg.proxy or cfg_in.cfg.get('proxy')), + cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir, + proxy = proxy, portfolio = get_portfolio() if cfg_in.portfolio and gcfg.portfolio and not query else None ) @@ -489,13 +511,21 @@ class Ticker: d = self.data max_w = 0 - min_t = min( (int(d[a.id]['last_updated']) for a in cross_assets), default=None ) + + if cross_assets: + last_updated_x = [d[a.id]['last_updated'] for a in cross_assets] + min_t = min( (int(n) for n in last_updated_x if isinstance(n,int) ), default=None ) + else: + min_t = None for row in self.rows: if isinstance(row,tuple): try: - t = int(d[row.id]['last_updated']) - except KeyError: + t = int( d[row.id]['last_updated'] ) + except TypeError as e: + d[row.id]['last_updated_fmt'] = gray('--' if 'NoneType' in str(e) else str(e)) + except KeyError as e: + msg(str(e)) pass else: t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( (min(t,min_t) if min_t else t), now ) @@ -517,7 +547,7 @@ class Ticker: return d['id'] def create_label(self,id): - return ' '.join(id.split('-')[1:]).upper() + return self.data[id]['name'].upper() def gen_output(self): yield 'Current time: {} UTC'.format(time.strftime('%F %X',time.gmtime(now))) diff --git a/mmgen_node_tools/data/ticker-cfg.yaml b/mmgen_node_tools/data/ticker-cfg.yaml index 2566da4..6589bfc 100644 --- a/mmgen_node_tools/data/ticker-cfg.yaml +++ b/mmgen_node_tools/data/ticker-cfg.yaml @@ -1,14 +1,15 @@ -# Indentation must be consistent! Do not mix leading tabs and spaces. +### Indentation must be consistent! Do not mix leading tabs and spaces. -# See the curl manpage for supported --proxy parameters -# For a direct connection, leave the right-hand side blank +### See the curl manpage for supported --proxy parameters +### For a direct connection, leave the right-hand side blank proxy: http://vpn-gw:8118 -# Override the default cache directory (~/.cache/mmgen-node-tools) +### Override the default cache directory (~/.cache/mmgen-node-tools): cachedir: -# Asset labels are arbitrary strings. Use as many or few as you wish. -# Invoke ‘mmnode-ticker --list-ids’ for a full list of supported asset IDs. +### Asset rows +### Asset labels are arbitrary strings. Use as many or few as you wish. +### Invoke ‘mmnode-ticker --list-ids’ for a full list of supported asset IDs. assets: coin1: - btc-bitcoin diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 83d0b1c..b8df683 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -12,7 +12,6 @@ mmnode-ticker: Display price information for cryptocurrency and other assets """ -import sys,os from .Ticker import * opts_data = { @@ -36,8 +35,8 @@ opts_data = { used to supply a USD exchange rate for missing assets. -C, --cached-data Use cached data from previous network query instead of live data from server --d, --cachedir=D Read and write cached JSON data to directory ‘D’ - instead of ‘~/{os.path.relpath(cachedir,start=homedir)}’ +-D, --cachedir=D Read and write cached JSON data to directory ‘D’ + instead of ‘~/{dfl_cachedir}’ -e, --add-precision=N Add ‘N’ digits of precision to columns -E, --elapsed Show elapsed time in UPDATED column (see --update-time) -F, --portfolio Display portfolio data @@ -48,13 +47,14 @@ opts_data = { -r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, see ASSET SPECIFIERS below). Can also be used to supply a USD exchange rate for missing assets. +-t, --testing Print command to be executed to stdout and exit -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column --U, --print-curl Print cURL command to standard output and exit -v, --verbose Be more verbose -w, --wide Display all optional columns (equivalent to -punT) -x, --proxy=P Connect via proxy ‘P’. Set to the empty string to - disable. Consult the curl manpage for --proxy usage. + completely disable or ‘none’ to allow override from + environment. Consult the curl manpage for --proxy usage. """, 'notes': """ @@ -184,6 +184,9 @@ To add a portfolio, edit the file """ }, 'code': { + 'options': lambda s: s.format( + dfl_cachedir = os.path.relpath(dfl_cachedir,start=homedir), + ), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), @@ -196,16 +199,17 @@ To add a portfolio, edit the file } } +import os + from mmgen.cfg import Config -gcfg = Config( opts_data=opts_data, do_post_init=True ) +import mmgen_node_tools.Ticker as tck -import mmgen_node_tools.Ticker as Ticker -Ticker.gcfg = gcfg +tck.gcfg = Config( opts_data=opts_data, do_post_init=True ) -cfg_in = get_cfg_in() +tck.make_cfg() -cfg = make_cfg(gcfg._args,cfg_in) +from .Ticker import cfg_in -gcfg._post_init() +tck.gcfg._post_init() -main(cfg,cfg_in) +tck.main() diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index c4536d6..c1e925d 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -198,7 +198,7 @@ class TestSuiteScripts(TestSuiteBase): [ 'USD BTC CHG_7d CHG_24h UPDATED', r'BITCOIN 23,250.7741 1.0000000000 \+11.15 \+0.89 10 minutes ago', - r'FAKECOIN 81.3008 0.0034966927 -- -- just now', + r'FAKECOIN 81.3008 0.0034966927 -- -- --', r'\(no data for noc-nocoin\)', ]) os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) @@ -247,7 +247,7 @@ class TestSuiteScripts(TestSuiteBase): 'Offer: 200,000 INR', 'Offered price differs from spot by -7.58%', 'SPOT PRICE OFFERED PRICE UPDATED', - 'INDIAN RUPEE 200,000.00000000 184,843.65372424 10 minutes ago ' + + 'INDIAN RUPEE 200,000.00000000 184,843.65372424 -- ' + 'BITCOIN 0.10819955 0.10000000 10 minutes ago' ]) @@ -272,7 +272,7 @@ class TestSuiteScripts(TestSuiteBase): 'Offered price differs from spot by -3.72%', 'SPOT PRICE OFFERED PRICE UPDATED', 'BITCOIN 2.00000000 1.92563954 1 day 9 hours 2 minutes ago ' + - 'US DOLLAR 46,737.71911598 45,000.00000000 1 day 9 hours 2 minutes ago', + 'US DOLLAR 46,737.71911598 45,000.00000000 --', ]) def ticker16(self): @@ -283,7 +283,7 @@ class TestSuiteScripts(TestSuiteBase): r'OMR \(OMANI RIAL\) = 2.5900 USD', 'USD EUR OMR BTC CHG_7d CHG_24h UPDATED', r'BITCOIN 23,250.77 22,826.6890 8,977.1328 1.00000000 \+11.15 \+0.89 10 minutes ago', - 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- just now' + 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- --' ]) def ticker17(self): From d29e34c221b8f95099c6f56edb62bf7fc2b0ce95 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 25 Sep 2023 15:53:02 +0000 Subject: [PATCH 096/175] mmnode-ticker: support multiple data sources --- mmgen_node_tools/Ticker.py | 317 ++++++++++++++++++++------------ mmgen_node_tools/main_ticker.py | 29 ++- 2 files changed, 210 insertions(+), 136 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index a370b56..cafd407 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -12,11 +12,6 @@ mmgen_node_tools.Ticker: Display price information for cryptocurrency and other assets """ -api_host = 'api.coinpaprika.com' -api_url = f'https://{api_host}/v1/ticker' -ratelimit = 240 -btc_ratelimit = 10 - # We use deprecated coinpaprika ‘ticker’ API for now because it returns ~45% less data. # Old ‘ticker’ API (/v1/ticker): data['BTC']['price_usd'] # New ‘tickers’ API (/v1/tickers): data['BTC']['quotes']['USD']['price'] @@ -24,25 +19,193 @@ btc_ratelimit = 10 # Possible alternatives: # - https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,LTC&tsyms=USD,EUR -import sys,os,time,json,yaml +import sys,os,re,time,json,yaml,random from subprocess import run,PIPE,CalledProcessError from decimal import Decimal from collections import namedtuple from mmgen.color import * -from mmgen.util import msg,msg_r,Msg,die,Die,suf,fmt,fmt_list +from mmgen.util import msg,msg_r,Msg,die,Die,suf,fmt,fmt_list,fmt_dict,list_gen homedir = os.getenv('HOME') dfl_cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') cfg_fn = 'ticker-cfg.yaml' portfolio_fn = 'ticker-portfolio.yaml' +asset_tuple = namedtuple('asset_tuple',['symbol','id','source']) + +def fetch_delay(fetched_data=[]): + if not gcfg.testing: + if fetched_data: + delay = 1 + random.randrange(1,5000) / 1000 + msg(f'Waiting {delay:.3f} seconds...') + time.sleep(delay) + else: + fetched_data.append(None) + +class DataSource: + + sources = { + 'cc': 'coinpaprika', + } + + class base: + + def get_data_from_network(self): + + curl_cmd = list_gen( + ['curl', '--tr-encoding', '--header', 'Accept: application/json',True], + ['--compressed'], # adds 'Accept-Encoding: gzip' + ['--proxy', cfg.proxy, isinstance(cfg.proxy,str)], + ['--silent', not gcfg.verbose], + [self.api_url] + ) + + if gcfg.testing: + Msg(fmt_list(curl_cmd,fmt='bare')) + return + + try: + return run(curl_cmd,check=True,stdout=PIPE).stdout.decode() + except CalledProcessError as e: + msg('') + from .Misc import curl_exit_codes + msg(red(curl_exit_codes[e.returncode])) + msg(red('Command line:\n {}'.format( ' '.join((repr(i) if ' ' in i else i) for i in e.cmd) ))) + from mmgen.exception import MMGenCalledProcessError + raise MMGenCalledProcessError(f'Subprocess returned non-zero exit status {e.returncode}') + + def get_data(self): + + if not os.path.exists(cfg.cachedir): + os.makedirs(cfg.cachedir) + + if not os.path.exists(self.json_fn): + open(self.json_fn,'w').write('{}') + + if gcfg.cached_data: + data_type = 'json' + data_in = open(self.json_fn).read() + else: + data_type = self.net_data_type + elapsed = int(time.time() - os.stat(self.json_fn).st_mtime) + if elapsed >= self.timeout: + if gcfg.testing: + msg('') + fetch_delay() + msg_r(f'Fetching data from {self.api_host}...') + if self.has_verbose: + gcfg._util.vmsg('') + data_in = self.get_data_from_network() + msg('done') + if gcfg.testing: + return {} + else: + die(1,self.rate_limit_errmsg(elapsed)) + + if data_type == 'json': + try: + data = json.loads(data_in) + except: + self.json_data_error_msg(data_in) + die(2,'Retrieved data is not valid JSON, exiting') + json_text = data_in + elif data_type == 'python': + data = data_in + json_text = json.dumps(data_in) + + if not data: + if gcfg.cached_data: + die(1,'No cached data! Run command without --cached-data option to retrieve data from remote host') + else: + die(2,'Remote host returned no data!') + elif 'error' in data: + die(1,data['error']) + + if gcfg.cached_data: + msg(f'Using cached data from ~/{self.json_fn_rel}') + else: + open(self.json_fn,'w').write(json_text) + msg(f'JSON data cached to ~/{self.json_fn_rel}') + if gcfg.download: + sys.exit(0) + + return self.postprocess_data(data) + + def json_data_error_msg(self,json_text): + pass + + def postprocess_data(self,data): + return data + + @property + def json_fn_rel(self): + return os.path.relpath(self.json_fn,start=homedir) + + class coinpaprika(base): + desc = 'CoinPaprika' + api_host = 'api.coinpaprika.com' + ratelimit = 240 + btc_ratelimit = 10 + net_data_type = 'json' + has_verbose = True + + def rate_limit_errmsg(self,elapsed): + return ( + f'Rate limit exceeded! Retry in {self.timeout-elapsed} seconds' + + ('' if cfg.btc_only else ', or use --cached-data or --btc') + ) + + @property + def api_url(self): + return f'https://{self.api_host}/v1/ticker' + ('/btc-bitcoin' if cfg.btc_only else '') + + @property + def json_fn(self): + return os.path.join( + cfg.cachedir, + 'ticker-btc.json' if cfg.btc_only else 'ticker.json' ) + + @property + def timeout(self): + return 5 if gcfg.test_suite else self.btc_ratelimit if cfg.btc_only else self.ratelimit + + def json_data_error_msg(self,json_text): + tor_captcha_msg = f""" + If you’re using Tor, the API request may have failed due to Captcha protection. + A workaround for this issue is to retrieve the JSON data with a browser from + the following URL: + + {self.api_url} + + and save it to: + + ‘{cfg.cachedir}/ticker.json’ + + Then invoke the program with --cached-data and without --btc + """ + msg(json_text[:1024] + '...') + msg(orange(fmt(tor_captcha_msg,strip_char='\t'))) + + def postprocess_data(self,data): + return [data] if cfg.btc_only else data + + @staticmethod + def parse_asset_id(s,require_label): + sym,label = (*s.split('-',1),None)[:2] + if require_label and not label: + die(1,f'{s!r}: asset label is missing') + return asset_tuple( + symbol = sym.upper(), + id = (s.lower() if label else None), + source = 'cc' ) def assets_list_gen(cfg_in): for k,v in cfg_in.cfg['assets'].items(): yield('') yield(k.upper()) for e in v: - yield(' {:4s} {}'.format(*e.split('-',1))) + out = e.split('-',1) + yield(' {:5s} {}'.format(out[0],out[1] if len(out) == 2 else '')) def gen_data(data): """ @@ -59,7 +222,7 @@ def gen_data(data): def dup_sym_errmsg(dup_sym): return ( f'The symbol {dup_sym!r} is shared by the following assets:\n' + - '\n ' + '\n '.join(d['id'] for d in data if d['symbol'] == dup_sym) + + '\n ' + '\n '.join(d['id'] for d in data['cc'] if d['symbol'] == dup_sym) + '\n\nPlease specify the asset by one of the full IDs listed above\n' + f'instead of {dup_sym!r}' ) @@ -103,13 +266,13 @@ def gen_data(data): wants = {k:rows_want[k] | usr_wants[k] for k in ('id','symbol')} - for d in data: + for d in data['cc']: if d['id'] == 'btc-bitcoin': btcusd = Decimal(d['price_usd']) break for k in ('id','symbol'): - for d in data: + for d in data['cc']: if wants[k]: if d[k] in wants[k]: if d[k] in found[k]: @@ -149,87 +312,6 @@ def gen_data(data): 'last_updated': None, }) -def get_src_data(curl_cmd): - - tor_captcha_msg = f""" - If you’re using Tor, the API request may have failed due to Captcha protection. - A workaround for this issue is to retrieve the JSON data with a browser from - the following URL: - - {api_url} - - and save it to: - - ‘{cfg.cachedir}/ticker.json’ - - Then invoke the program with --cached-data and without --btc - """ - - def rate_limit_errmsg(timeout,elapsed): - return ( - f'Rate limit exceeded! Retry in {timeout-elapsed} seconds' + - ('' if cfg.btc_only else ', or use --cached-data or --btc') - ) - - if not os.path.exists(cfg.cachedir): - os.makedirs(cfg.cachedir) - - if cfg.btc_only: - fn = os.path.join(cfg.cachedir,'ticker-btc.json') - timeout = 5 if gcfg.test_suite else btc_ratelimit - else: - fn = os.path.join(cfg.cachedir,'ticker.json') - timeout = 5 if gcfg.test_suite else ratelimit - - fn_rel = os.path.relpath(fn,start=homedir) - - if not os.path.exists(fn): - open(fn,'w').write('{}') - - if gcfg.cached_data: - json_text = open(fn).read() - else: - elapsed = int(time.time() - os.stat(fn).st_mtime) - if elapsed >= timeout: - msg_r(f'Fetching data from {api_host}...') - gcfg._util.vmsg('') - try: - cp = run(curl_cmd,check=True,stdout=PIPE) - except CalledProcessError as e: - msg('') - from .Misc import curl_exit_codes - msg(red(curl_exit_codes[e.returncode])) - msg(red('Command line:\n {}'.format( ' '.join((repr(i) if ' ' in i else i) for i in e.cmd) ))) - from mmgen.exception import MMGenCalledProcessError - raise MMGenCalledProcessError(f'Subprocess returned non-zero exit status {e.returncode}') - json_text = cp.stdout.decode() - msg('done') - else: - die(1,rate_limit_errmsg(timeout,elapsed)) - - try: - data = json.loads(json_text) - except: - msg(json_text[:1024] + '...') - msg(orange(fmt(tor_captcha_msg,strip_char='\t'))) - die(2,'Retrieved data is not valid JSON, exiting') - - if not data: - if gcfg.cached_data: - die(1,'No cached data! Run command without --cached-data option to retrieve data from remote host') - else: - die(2,'Remote host returned no data!') - elif 'error' in data: - die(1,data['error']) - - if gcfg.cached_data: - msg(f'Using cached data from ~/{fn_rel}') - else: - open(fn,'w').write(json_text) - msg(f'JSON data cached to ~/{fn_rel}') - - return data - def main(): def update_sample_file(usr_cfg_file): @@ -243,20 +325,6 @@ def main(): sample_file )) open(sample_file,'w').write(usr_data) - def get_curl_cmd(): - return ([ - 'curl', - '--tr-encoding', - '--compressed', # adds 'Accept-Encoding: gzip' - '--header', 'Accept: application/json', - ] + - (['--proxy', cfg.proxy] if cfg.proxy else []) + - (['--silent'] if not gcfg.verbose else []) + - [api_url + ('/btc-bitcoin' if cfg.btc_only else '')] - ) - - global cfg,cfg_in - try: from importlib.resources import files # Python 3.9 except ImportError: @@ -269,17 +337,24 @@ def main(): die(1,'No portfolio configured!\nTo configure a portfolio, edit the file ~/{}'.format( os.path.relpath(cfg_in.portfolio_file,start=homedir))) - curl_cmd = get_curl_cmd() + if gcfg.list_ids: + src_ids = ['cc'] + elif gcfg.download: + assert gcfg.download in DataSource.sources, f'{gcfg.download!r}: invalid data source' + src_ids = [gcfg.download] + else: + src_ids = DataSource.sources - if gcfg.print_curl: - Msg(curl_cmd + '\n' + ' '.join(curl_cmd)) + ids = random.sample( list(src_ids), k=len(src_ids) ) # shuffle the ids + + src_data = { k: src_cls[k]().get_data() for k in ids } + + if gcfg.testing: return - src_data = [get_src_data(curl_cmd)] if cfg.btc_only else get_src_data(curl_cmd) - if gcfg.list_ids: from mmgen.ui import do_pager - do_pager('\n'.join(e['id'] for e in src_data)) + do_pager('\n'.join(e['id'] for e in src_data['cc'])) return global now @@ -304,10 +379,7 @@ def make_cfg(): return tuple(gen()) def parse_asset_id(s,require_label=False): - sym,label = (*s.split('-',1),None)[:2] - if require_label and not label: - die(1,f'{s!r}: asset label is missing') - return asset_tuple( sym.upper(), (s.lower() if label else None) ) + return src_cls['cc'].parse_asset_id(s,require_label) def parse_usr_asset_arg(key,use_cf_file=False): """ @@ -327,7 +399,8 @@ def make_cfg(): None if rate is None else 1 / Decimal(rate[:-1]) if rate.lower().endswith('r') else Decimal(rate) ), - rate_asset = parse_asset_id(rate_asset) if rate_asset else None ) + rate_asset = parse_asset_id(rate_asset) if rate_asset else None, + source = parsed_id.source ) cl_opt = getattr(gcfg,key) cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else [] @@ -344,7 +417,8 @@ def make_cfg(): id = parsed_id.id, amount = None if amount is None else Decimal(amount), rate = None, - rate_asset = None ) + rate_asset = None, + source = parsed_id.source ) ss = s.split(':') assert len(ss) in (2,3,4), f'{s}: malformed argument' @@ -417,14 +491,15 @@ def make_cfg(): 'proxy', 'portfolio' ]) - global cfg_in,cfg + global cfg_in,src_cls,cfg + + src_cls = { k: getattr(DataSource,v) for k,v in DataSource.sources.items() } cmd_args = gcfg._args cfg_in = get_cfg_in() query_tuple = namedtuple('query',['asset','to_asset']) - asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset']) - asset_tuple = namedtuple('asset_tuple',['symbol','id']) + asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset','source']) usr_rows = parse_usr_asset_arg('add_rows') usr_columns = parse_usr_asset_arg('add_columns',use_cf_file=True) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index b8df683..6553d2a 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -12,8 +12,6 @@ mmnode-ticker: Display price information for cryptocurrency and other assets """ -from .Ticker import * - opts_data = { 'sets': [ ('wide', True, 'percent_change', True), @@ -24,7 +22,7 @@ opts_data = { 'text': { 'desc': 'Display prices for cryptocurrency and other assets', 'usage': '[opts] [TRADE_SPECIFIER]', - 'options': f""" + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -A, --adjust=P Adjust prices by percentage ‘P’. In ‘trading’ mode, @@ -37,6 +35,8 @@ opts_data = { live data from server -D, --cachedir=D Read and write cached JSON data to directory ‘D’ instead of ‘~/{dfl_cachedir}’ +-d, --download=D Retrieve data ‘D’ from source, save to file and exit + (valid options: {ds}) -e, --add-precision=N Add ‘N’ digits of precision to columns -E, --elapsed Show elapsed time in UPDATED column (see --update-time) -F, --portfolio Display portfolio data @@ -112,7 +112,7 @@ A TRADE_SPECIFIER is a single argument in the format: PROXY NOTE -The remote server used to obtain the price data, {api_host!r}, blocks +The remote server used to obtain the price data, {cc.api_host!r}, blocks Tor behind a Captcha wall, so a Tor proxy cannot be used directly. If you’re concerned about privacy, connect via a VPN, or better yet, VPN over Tor. Then set up an HTTP proxy (e.g. Privoxy) on the VPN’ed host and set the ‘proxy’ @@ -121,7 +121,7 @@ the script directly on the VPN’ed host with ’proxy’ or --proxy set to the null string. Alternatively, you may download the JSON source data in a Tor-proxied browser -from ‘{api_url}’, save it as ‘ticker.json’ in your +from ‘{cc.api_url}’, save it as ‘ticker.json’ in your configured cache directory, and run the script with the --cached-data option. @@ -130,9 +130,9 @@ configured cache directory, and run the script with the --cached-data option. To protect user privacy, all filtering and processing of data is performed client side so that the remote server does not know which assets are being examined. This means that data for ALL available assets (currently over 4000) -is fetched with each invocation of the script. A rate limit of {L} seconds +is fetched with each invocation of the script. A rate limit of {cc.ratelimit} seconds between calls is thus imposed to prevent abuse of the remote server. When the ---btc option is in effect, this limit is reduced to {B} seconds. To bypass the +--btc option is in effect, this limit is reduced to {cc.btc_ratelimit} seconds. To bypass the rate limit entirely, use --cached-data. @@ -186,21 +186,20 @@ To add a portfolio, edit the file 'code': { 'options': lambda s: s.format( dfl_cachedir = os.path.relpath(dfl_cachedir,start=homedir), + ds = fmt_dict(DataSource.sources,fmt='equal'), ), 'notes': lambda s: s.format( - assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), - cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), - pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), - api_host = api_host, - api_url = api_url, - L = ratelimit, - B = btc_ratelimit, + assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), + cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), + pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), + cc = src_cls['cc'](), ) } } import os +from mmgen.util import fmt_list,fmt_dict from mmgen.cfg import Config import mmgen_node_tools.Ticker as tck @@ -208,7 +207,7 @@ tck.gcfg = Config( opts_data=opts_data, do_post_init=True ) tck.make_cfg() -from .Ticker import cfg_in +from .Ticker import dfl_cachedir,homedir,DataSource,assets_list_gen,cfg_in,src_cls tck.gcfg._post_init() From fd318909c21d602701c2a219bedb1aafa8ffb0d8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 25 Sep 2023 15:53:02 +0000 Subject: [PATCH 097/175] mmnode-ticker: retrieve financial data from Yahoo Finance --- mmgen_node_tools/Ticker.py | 96 +++++++++++++++++++++++++-- mmgen_node_tools/data/ticker-cfg.yaml | 23 ++++--- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 65 +++++++++++------- setup.cfg | 3 +- test/ref/ticker/ticker-cfg.yaml | 16 ++--- test/ref/ticker/ticker-finance.json | 1 + test/ref/ticker/ticker.json | 2 +- test/test_py_d/ts_misc.py | 48 ++++++-------- 9 files changed, 179 insertions(+), 77 deletions(-) create mode 100644 test/ref/ticker/ticker-finance.json diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index cafd407..e6f0650 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -46,6 +46,7 @@ class DataSource: sources = { 'cc': 'coinpaprika', + 'fi': 'yahooquery' } class base: @@ -199,6 +200,69 @@ class DataSource: id = (s.lower() if label else None), source = 'cc' ) + class yahooquery(base): + + desc = 'Yahoo Finance' + api_host = 'finance.yahoo.com' + ratelimit = 30 + net_data_type = 'python' + has_verbose = False + asset_id_pat = r'^\^.*|.*=[xf]$' + + @staticmethod + def get_id(sym,data): + return sym.lower() + + @staticmethod + def conv_data(sym,data,btcusd): + price_usd = Decimal( data['regularMarketPrice']['raw'] ) + return { + 'id': sym, + 'name': data['shortName'], + 'symbol': sym.upper(), + 'price_usd': str(price_usd), + 'price_btc': str(price_usd / btcusd), + 'percent_change_7d': None, + 'percent_change_24h': data['regularMarketChangePercent']['raw'] * 100, + 'last_updated': data['regularMarketTime'], + } + + def rate_limit_errmsg(self,elapsed): + return f'Rate limit exceeded! Retry in {self.timeout-elapsed} seconds, or use --cached-data' + + @property + def json_fn(self): + return os.path.join( cfg.cachedir, 'ticker-finance.json' ) + + @property + def timeout(self): + return 5 if gcfg.test_suite else self.ratelimit + + def get_data_from_network(self): + + arg = [r.symbol for r in cfg.rows if isinstance(r,tuple) and r.source == 'fi'] + + kwargs = { 'formatted': True, 'proxies': { 'https': cfg.proxy2 } } + + if gcfg.test_suite: + kwargs.update({ 'timeout': 1, 'retry': 0 }) + + if gcfg.testing: + Msg('\nyahooquery.Ticker(\n {},\n {}\n)'.format( + arg, + fmt_dict(kwargs,fmt='kwargs') )) + return + + from yahooquery import Ticker + return Ticker(arg,**kwargs).price + + @staticmethod + def parse_asset_id(s,require_label): + return asset_tuple( + symbol = s.upper(), + id = s.lower(), + source = 'fi' ) + def assets_list_gen(cfg_in): for k,v in cfg_in.cfg['assets'].items(): yield('') @@ -271,6 +335,23 @@ def gen_data(data): btcusd = Decimal(d['price_usd']) break + get_id = src_cls['fi'].get_id + conv_func = src_cls['fi'].conv_data + + for k,v in data['fi'].items(): + id = get_id(k,v) + if wants['id']: + if id in wants['id']: + if id in found['id']: + die(1,dup_sym_errmsg(id)) + yield ( id, conv_func(id,v,btcusd) ) + found['id'].add(id) + wants['id'].remove(id) + if id in usr_rate_assets_want['id']: + rate_assets[k] = conv_func(id,v,btcusd) # NB: using symbol instead of ID for key + else: + break + for k in ('id','symbol'): for d in data['cc']: if wants[k]: @@ -379,7 +460,7 @@ def make_cfg(): return tuple(gen()) def parse_asset_id(s,require_label=False): - return src_cls['cc'].parse_asset_id(s,require_label) + return src_cls['fi' if re.match(fi_pat,s) else 'cc'].parse_asset_id(s,require_label) def parse_usr_asset_arg(key,use_cf_file=False): """ @@ -489,11 +570,13 @@ def make_cfg(): 'add_prec', 'cachedir', 'proxy', + 'proxy2', 'portfolio' ]) global cfg_in,src_cls,cfg src_cls = { k: getattr(DataSource,v) for k,v in DataSource.sources.items() } + fi_pat = src_cls['fi'].asset_id_pat cmd_args = gcfg._args cfg_in = get_cfg_in() @@ -514,6 +597,7 @@ def make_cfg(): proxy = get_proxy('proxy') proxy = None if proxy == 'none' else proxy + proxy2 = get_proxy('proxy2') cfg = cfg_tuple( rows = create_rows(), @@ -526,6 +610,7 @@ def make_cfg(): add_prec = parse_add_precision(gcfg.add_precision), cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir, proxy = proxy, + proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), portfolio = get_portfolio() if cfg_in.portfolio and gcfg.portfolio and not query else None ) @@ -541,9 +626,12 @@ def get_cfg_in(): cfg = cfg_data or { 'assets': { 'coin': [ 'btc-bitcoin', 'eth-ethereum', 'xmr-monero' ], - 'commodity': [ 'xau-gold-spot-token', 'xag-silver-spot-token', 'xbr-brent-crude-oil-spot' ], - 'fiat': [ 'gbp-pound-sterling-token', 'eur-euro-token' ], - 'index': [ 'dj30-dow-jones-30-token', 'spx-sp-500', 'ndx-nasdaq-100-token' ], + # gold futures, silver futures, Brent futures + 'commodity': [ 'gc=f', 'si=f', 'bz=f' ], + # Pound Sterling, Euro, Swiss Franc + 'fiat': [ 'gbpusd=x', 'eurusd=x', 'chfusd=x' ], + # Dow Jones Industrials, Nasdaq 100, S&P 500 + 'index': [ '^dji', '^ixic', '^gspc' ], }, 'proxy': 'http://vpn-gw:8118' }, diff --git a/mmgen_node_tools/data/ticker-cfg.yaml b/mmgen_node_tools/data/ticker-cfg.yaml index 6589bfc..6344445 100644 --- a/mmgen_node_tools/data/ticker-cfg.yaml +++ b/mmgen_node_tools/data/ticker-cfg.yaml @@ -3,10 +3,16 @@ ### See the curl manpage for supported --proxy parameters ### For a direct connection, leave the right-hand side blank proxy: http://vpn-gw:8118 +# proxy2: http://gw2:8118 ### Override the default cache directory (~/.cache/mmgen-node-tools): cachedir: +### Additional asset columns: +# add_columns: +# - cnhusd=x # Yuan +# - 6j=f # Yen futures + ### Asset rows ### Asset labels are arbitrary strings. Use as many or few as you wish. ### Invoke ‘mmnode-ticker --list-ids’ for a full list of supported asset IDs. @@ -19,13 +25,14 @@ assets: - ada-cardano - bnb-binance-coin commodity: - - xau-gold-spot-token - - xag-silver-spot-token - - xbr-brent-crude-oil-spot + - gc=f # gold futures + - si=f # silver futures + - bz=f # Brent futures fiat: - - gbp-pound-sterling-token - - eur-euro-token + - gbpusd=x # Pound Sterling + - eurusd=x # Euro + - chfusd=x # Swiss Franc index: - - dj30-dow-jones-30-token - - spx-sp-500 - - ndx-nasdaq-100-token + - ^dji # Dow Jones Industrials + - ^ixic # Nasdaq 100 + - ^gspc # S&P 500 diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index b473ce1..0c84a84 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev0 +3.2.dev1 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 6553d2a..f329e03 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -47,7 +47,7 @@ opts_data = { -r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, see ASSET SPECIFIERS below). Can also be used to supply a USD exchange rate for missing assets. --t, --testing Print command to be executed to stdout and exit +-t, --testing Print command(s) to be executed to stdout and exit -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column -v, --verbose Be more verbose @@ -55,6 +55,8 @@ opts_data = { -x, --proxy=P Connect via proxy ‘P’. Set to the empty string to completely disable or ‘none’ to allow override from environment. Consult the curl manpage for --proxy usage. +-X, --proxy2=P Alternate proxy for non-crypto financial data. Defaults + to value of --proxy """, 'notes': """ @@ -69,10 +71,15 @@ price to the spot price. ASSETS consist of either a symbol (e.g. ‘xmr’) or full ID (see --list-ids) consisting of symbol plus label (e.g. ‘xmr-monero’). In cases where the -symbol is ambiguous, the full ID must be used. Examples: +symbol is ambiguous, the full ID must be used. For Yahoo Finance assets +the symbol and ID are identical: - chf - specify asset by symbol - chf-swiss-franc-token - same as above, but use full ID instead of symbol +Examples: + + ltc - specify asset by symbol + ltc-litecoin - same as above, but use full ID instead of symbol + ^dji - Dow Jones Industrial Average (Yahoo) + gc=f - gold futures (Yahoo) ASSET SPECIFIERS have the following format: @@ -89,7 +96,8 @@ postfixed with the letter ‘r’, its meaning is reversed, i.e. interpreted as inr:0.01257r - same as above, but use reverse rate (INR/USD) inr-indian-rupee:79.5 - same as first example, but add an arbitrary label omr-omani-rial:2.59r - Omani Rial is pegged to the Dollar at 2.59 USD - bgn-bg-lev:0.5113r:eur - Bulgarian Lev is pegged to the Euro at 0.5113 EUR + bgn-bulgarian-lev:0.5113r:eurusd=x + - Bulgarian Lev is pegged to the Euro at 0.5113 EUR A TRADE_SPECIFIER is a single argument in the format: @@ -99,8 +107,8 @@ A TRADE_SPECIFIER is a single argument in the format: xmr:17.34 - price of 17.34 XMR in all configured assets xmr-monero:17.34 - same as above, but with full ID - xmr:17.34:eur - price of 17.34 XMR in EUR only - xmr:17.34:eur:2800 - commission on an offer of 17.34 XMR for 2800 EUR + xmr:17.34:eurusd=x - price of 17.34 XMR in EUR only + xmr:17.34:eurusd=x:2800 - commission on an offer of 17.34 XMR for 2800 EUR TO_AMOUNT, if included, is used to calculate the percentage difference or commission on an offer compared to the spot price. @@ -112,28 +120,34 @@ A TRADE_SPECIFIER is a single argument in the format: PROXY NOTE -The remote server used to obtain the price data, {cc.api_host!r}, blocks -Tor behind a Captcha wall, so a Tor proxy cannot be used directly. If you’re -concerned about privacy, connect via a VPN, or better yet, VPN over Tor. Then -set up an HTTP proxy (e.g. Privoxy) on the VPN’ed host and set the ‘proxy’ -option in the config file or --proxy on the command line accordingly. Or run -the script directly on the VPN’ed host with ’proxy’ or --proxy set to the -null string. +The remote server used to obtain the crypto price data, {cc.api_host}, +blocks Tor behind a Captcha wall, so a Tor proxy cannot be used directly. +If you’re concerned about privacy, connect via a VPN, or better yet, VPN over +Tor. Then set up an HTTP proxy (e.g. Privoxy) on the VPN’ed host and set the +‘proxy’ option in the config file or --proxy on the command line accordingly. +Or run the script directly on the VPN’ed host with ’proxy’ or --proxy set to +the null string. Alternatively, you may download the JSON source data in a Tor-proxied browser -from ‘{cc.api_url}’, save it as ‘ticker.json’ in your -configured cache directory, and run the script with the --cached-data option. +from {cc.api_url}, save it as ‘ticker.json’ in your +configured cache directory and run the script with the --cached-data option. + +Financial data is obtained from {fi.desc}, which currently allows Tor. RATE LIMITING NOTE -To protect user privacy, all filtering and processing of data is performed -client side so that the remote server does not know which assets are being -examined. This means that data for ALL available assets (currently over 4000) -is fetched with each invocation of the script. A rate limit of {cc.ratelimit} seconds -between calls is thus imposed to prevent abuse of the remote server. When the ---btc option is in effect, this limit is reduced to {cc.btc_ratelimit} seconds. To bypass the -rate limit entirely, use --cached-data. +To protect user privacy, all filtering and processing of cryptocurrency data +is performed client side so that the remote server does not know which assets +are being examined. This means that data for ALL available crypto assets +(currently over 8000) is fetched with each invocation of the script. A rate +limit of {cc.ratelimit} seconds between calls is thus imposed to prevent abuse of the +remote server. When the --btc option is in effect, this limit is reduced to +{cc.btc_ratelimit} seconds. To bypass the rate limit entirely, use --cached-data. + +Note that financial data obtained from {fi.api_host} is filtered in the +request, which has privacy implications. The rate limit for financial data +is {fi.ratelimit} seconds. EXAMPLES @@ -146,10 +160,10 @@ $ mmnode-ticker --btc # Wide display, add EUR and OMR columns, OMR/USD rate, extra precision and # proxy: -$ mmnode-ticker -w -c eur,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 +$ mmnode-ticker -w -c eurusd=x,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 # Wide display, elapsed update time, add EUR, BGN columns and BGN/EUR rate: -$ mmnode-ticker -wE -c eur,bgn-bulgarian-lev:0.5113r:eur +$ mmnode-ticker -wE -c eurusd=x,bgn-bulgarian-lev:0.5113r:eurusd=x # Wide display, use cached data from previous network query, show portfolio # (see above), pipe output to pager, add DOGE row: @@ -193,6 +207,7 @@ To add a portfolio, edit the file cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), cc = src_cls['cc'](), + fi = src_cls['fi'](), ) } } diff --git a/setup.cfg b/setup.cfg index 0cdbed1..9539a29 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,8 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=13.3.dev44 + mmgen>=14.0.dev2 + yahooquery packages = mmgen_node_tools diff --git a/test/ref/ticker/ticker-cfg.yaml b/test/ref/ticker/ticker-cfg.yaml index 504a7fb..4b40a43 100644 --- a/test/ref/ticker/ticker-cfg.yaml +++ b/test/ref/ticker/ticker-cfg.yaml @@ -12,13 +12,13 @@ assets: - ada-cardano - algo-algorand commodity: - - xau-gold-spot-token - - xag-silver-spot-token - - xbr-brent-crude-oil-spot + - gc=f # gold futures + - si=f # silver futures + - bz=f # Brent futures fiat: - - chf-swiss-franc-token - - eur-euro-token + - chfusd=x # Swiss Franc + - eurusd=x # Euro index: - - dj30-dow-jones-30-token - - spx-sp-500 - - ndx-nasdaq-100-token + - ^dji # Dow Jones Industrials + - ^ixic # Nasdaq 100 + - ^gspc # S&P 500 diff --git a/test/ref/ticker/ticker-finance.json b/test/ref/ticker/ticker-finance.json new file mode 100644 index 0000000..ac0af29 --- /dev/null +++ b/test/ref/ticker/ticker-finance.json @@ -0,0 +1 @@ +{"GC=F":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.0015419408,"fmt":"-0.15%"},"regularMarketChange":{"raw":-3,"fmt":"-3.00"},"regularMarketTime":1659385900,"priceHint":{"raw":2,"fmt":"2","longFmt":"2"},"regularMarketPrice":{"raw":1942.6,"fmt":"1,942.60"},"regularMarketDayHigh":{"raw":1946.8,"fmt":"1,946.80"},"regularMarketDayLow":{"raw":1940.1,"fmt":"1,940.10"},"regularMarketVolume":{"raw":39539,"fmt":"39.54k","longFmt":"39,539.00"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":1945.6,"fmt":"1,945.60"},"regularMarketSource":"DELAYED","regularMarketOpen":{"raw":1944.7,"fmt":"1,944.70"},"strikePrice":{},"openInterest":{},"exchange":"CMX","exchangeName":"COMEX","exchangeDataDelayedBy":10,"marketState":"REGULAR","quoteType":"FUTURE","symbol":"GC=F","underlyingSymbol":null,"shortName":"Gold Dec 23","longName":null,"currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"SI=F":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.0014259518,"fmt":"-0.14%"},"regularMarketChange":{"raw":-0.034000397,"fmt":"-0.03"},"regularMarketTime":1659385900,"priceHint":{"raw":2,"fmt":"2","longFmt":"2"},"regularMarketPrice":{"raw":23.81,"fmt":"23.81"},"regularMarketDayHigh":{"raw":23.925,"fmt":"23.92"},"regularMarketDayLow":{"raw":23.67,"fmt":"23.67"},"regularMarketVolume":{"raw":11819,"fmt":"11.82k","longFmt":"11,819.00"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":23.844,"fmt":"23.84"},"regularMarketSource":"DELAYED","regularMarketOpen":{"raw":23.81,"fmt":"23.81"},"strikePrice":{},"openInterest":{},"exchange":"CMX","exchangeName":"COMEX","exchangeDataDelayedBy":10,"marketState":"REGULAR","quoteType":"FUTURE","symbol":"SI=F","underlyingSymbol":null,"shortName":"Silver Dec 23","longName":null,"currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"BZ=F":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":0.004610275,"fmt":"0.46%"},"regularMarketChange":{"raw":0.4300003,"fmt":"0.43"},"regularMarketTime":1659385900,"priceHint":{"raw":2,"fmt":"2","longFmt":"2"},"regularMarketPrice":{"raw":93.7,"fmt":"93.70"},"regularMarketDayHigh":{"raw":94.25,"fmt":"94.25"},"regularMarketDayLow":{"raw":93.24,"fmt":"93.24"},"regularMarketVolume":{"raw":2853,"fmt":"2.85k","longFmt":"2,853.00"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":93.27,"fmt":"93.27"},"regularMarketSource":"DELAYED","regularMarketOpen":{"raw":93.75,"fmt":"93.75"},"strikePrice":{},"openInterest":{},"exchange":"NYM","exchangeName":"NY Mercantile","exchangeDataDelayedBy":10,"marketState":"REGULAR","quoteType":"FUTURE","symbol":"BZ=F","underlyingSymbol":null,"shortName":"Brent Crude Oil Last Day Financ","longName":null,"currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"GBPUSD=X":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.00045271934,"fmt":"-0.0453%"},"regularMarketChange":{"raw":-0.0005540848,"fmt":"-0.0006"},"regularMarketTime":1659385900,"priceHint":{"raw":4,"fmt":"4","longFmt":"4"},"regularMarketPrice":{"raw":1.223481,"fmt":"1.2235"},"regularMarketDayHigh":{"raw":1.2263919,"fmt":"1.2264"},"regularMarketDayLow":{"raw":1.2213293,"fmt":"1.2213"},"regularMarketVolume":{"raw":0,"fmt":null,"longFmt":"0.0000"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":1.2240351,"fmt":"1.2240"},"regularMarketSource":"DELAYED","regularMarketOpen":{"raw":1.2240951,"fmt":"1.2241"},"strikePrice":{},"openInterest":{},"exchange":"CCY","exchangeName":"CCY","exchangeDataDelayedBy":0,"marketState":"REGULAR","quoteType":"CURRENCY","symbol":"GBPUSD=X","underlyingSymbol":null,"shortName":"GBP/USD","longName":"GBP/USD","currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"EURUSD=X":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.0005321096,"fmt":"-0.0532%"},"regularMarketChange":{"raw":-0.00056660175,"fmt":"-0.0006"},"regularMarketTime":1659385900,"priceHint":{"raw":4,"fmt":"4","longFmt":"4"},"regularMarketPrice":{"raw":1.0641694,"fmt":"1.0642"},"regularMarketDayHigh":{"raw":1.0658708,"fmt":"1.0659"},"regularMarketDayLow":{"raw":1.0626993,"fmt":"1.0627"},"regularMarketVolume":{"raw":0,"fmt":null,"longFmt":"0.0000"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":1.064736,"fmt":"1.0647"},"regularMarketSource":"DELAYED","regularMarketOpen":{"raw":1.064736,"fmt":"1.0647"},"strikePrice":{},"openInterest":{},"exchange":"CCY","exchangeName":"CCY","exchangeDataDelayedBy":0,"marketState":"REGULAR","quoteType":"CURRENCY","symbol":"EURUSD=X","underlyingSymbol":null,"shortName":"EUR/USD","longName":"EUR/USD","currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"CHFUSD=X":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.0028495365,"fmt":"-0.2850%"},"regularMarketChange":{"raw":-0.0031440258,"fmt":"-0.0031"},"regularMarketTime":1659385900,"priceHint":{"raw":4,"fmt":"4","longFmt":"4"},"regularMarketPrice":{"raw":1.1002069,"fmt":"1.1002"},"regularMarketDayHigh":{"raw":1.1046062,"fmt":"1.1046"},"regularMarketDayLow":{"raw":1.0982735,"fmt":"1.0983"},"regularMarketVolume":{"raw":0,"fmt":null,"longFmt":"0.0000"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":1.1033509,"fmt":"1.1034"},"regularMarketSource":"DELAYED","regularMarketOpen":{"raw":1.1043622,"fmt":"1.1044"},"strikePrice":{},"openInterest":{},"exchange":"CCY","exchangeName":"CCY","exchangeDataDelayedBy":0,"marketState":"REGULAR","quoteType":"CURRENCY","symbol":"CHFUSD=X","underlyingSymbol":null,"shortName":"CHF/USD","longName":"CHF/USD","currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"^DJI":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.0031276005,"fmt":"-0.31%"},"regularMarketChange":{"raw":-106.55859,"fmt":"-106.56"},"regularMarketTime":1659385900,"priceHint":{"raw":2,"fmt":"2","longFmt":"2"},"regularMarketPrice":{"raw":33963.84,"fmt":"33,963.84"},"regularMarketDayHigh":{"raw":34156.15,"fmt":"34,156.15"},"regularMarketDayLow":{"raw":33947.24,"fmt":"33,947.24"},"regularMarketVolume":{"raw":271273859,"fmt":"271.27M","longFmt":"271,273,859.00"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":34070.4,"fmt":"34,070.40"},"regularMarketSource":"FREE_REALTIME","regularMarketOpen":{"raw":34077.08,"fmt":"34,077.08"},"strikePrice":{},"openInterest":{},"exchange":"DJI","exchangeName":"DJI","exchangeDataDelayedBy":0,"marketState":"PRE","quoteType":"INDEX","symbol":"^DJI","underlyingSymbol":null,"shortName":"Dow Jones Industrial Average","longName":"Dow Jones Industrial Average","currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"^IXIC":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.00092206284,"fmt":"-0.09%"},"regularMarketChange":{"raw":-12.193359,"fmt":"-12.19"},"regularMarketTime":1659385900,"priceHint":{"raw":2,"fmt":"2","longFmt":"2"},"regularMarketPrice":{"raw":13211.807,"fmt":"13,211.81"},"regularMarketDayHigh":{"raw":13353.22,"fmt":"13,353.22"},"regularMarketDayLow":{"raw":13200.639,"fmt":"13,200.64"},"regularMarketVolume":{"raw":3887623000,"fmt":"3.89B","longFmt":"3,887,623,000.00"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":13224,"fmt":"13,224.00"},"regularMarketSource":"FREE_REALTIME","regularMarketOpen":{"raw":13287.171,"fmt":"13,287.17"},"strikePrice":{},"openInterest":{},"exchange":"NIM","exchangeName":"Nasdaq GIDS","exchangeDataDelayedBy":0,"marketState":"PRE","quoteType":"INDEX","symbol":"^IXIC","underlyingSymbol":null,"shortName":"NASDAQ Composite","longName":"NASDAQ Composite","currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}},"^GSPC":{"maxAge":1,"preMarketChange":{},"preMarketPrice":{},"postMarketChange":{},"postMarketPrice":{},"regularMarketChangePercent":{"raw":-0.0022955984,"fmt":"-0.23%"},"regularMarketChange":{"raw":-9.939941,"fmt":"-9.94"},"regularMarketTime":1659385900,"priceHint":{"raw":2,"fmt":"2","longFmt":"2"},"regularMarketPrice":{"raw":4320.06,"fmt":"4,320.06"},"regularMarketDayHigh":{"raw":4357.4,"fmt":"4,357.40"},"regularMarketDayLow":{"raw":4316.49,"fmt":"4,316.49"},"regularMarketVolume":{"raw":2135953000,"fmt":"2.14B","longFmt":"2,135,953,000.00"},"averageDailyVolume10Day":{},"averageDailyVolume3Month":{},"regularMarketPreviousClose":{"raw":4330,"fmt":"4,330.00"},"regularMarketSource":"FREE_REALTIME","regularMarketOpen":{"raw":4341.74,"fmt":"4,341.74"},"strikePrice":{},"openInterest":{},"exchange":"SNP","exchangeName":"SNP","exchangeDataDelayedBy":0,"marketState":"PRE","quoteType":"INDEX","symbol":"^GSPC","underlyingSymbol":null,"shortName":"S&P 500","longName":"S&P 500","currency":"USD","quoteSourceName":"Delayed Quote","currencySymbol":"$","fromCurrency":null,"toCurrency":null,"lastMarket":null,"volume24Hr":{},"volumeAllCurrencies":{},"circulatingSupply":{},"marketCap":{}}} diff --git a/test/ref/ticker/ticker.json b/test/ref/ticker/ticker.json index 2484e6d..880a666 100644 --- a/test/ref/ticker/ticker.json +++ b/test/ref/ticker/ticker.json @@ -1 +1 @@ -[{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"23250.774053363122","price_btc":"1","volume_24h_usd":"28307579008.4866","market_cap_usd":"444336521633","circulating_supply":"19110612","total_supply":"19110619","max_supply":"21000000","percent_change_1h":"-0.27","percent_change_24h":"0.89","percent_change_7d":"11.15","last_updated":"1659464759"},{"id":"eth-ethereum","name":"Ethereum","symbol":"ETH","rank":"2","price_usd":"1659.6621665887371","price_btc":"0.07146396683507168","volume_24h_usd":"18216561308.363518","market_cap_usd":"202151289827","circulating_supply":"121802674","total_supply":"121802721","max_supply":"","percent_change_1h":"-0.03","percent_change_24h":"1.82","percent_change_7d":"21.42","last_updated":"1659464759"},{"id":"ltc-litecoin","name":"Litecoin","symbol":"LTC","rank":"23","price_usd":"59.013589192027936","price_btc":"0.002541086532993605","volume_24h_usd":"510336800.72056556","market_cap_usd":"4181502638","circulating_supply":"70856606","total_supply":"70856631","max_supply":"84000000","percent_change_1h":"-0.43","percent_change_24h":"0.4","percent_change_7d":"12.79","last_updated":"1659464759"},{"id":"xmr-monero","name":"Monero","symbol":"XMR","rank":"30","price_usd":"158.97302629813817","price_btc":"0.006845274482813666","volume_24h_usd":"78159392.30003875","market_cap_usd":"2886277702","circulating_supply":"18155770","total_supply":"18155768","max_supply":"","percent_change_1h":"0.21","percent_change_24h":"1.21","percent_change_7d":"7.28","last_updated":"1659464759"},{"id":"ada-cardano","name":"Cardano","symbol":"ADA","rank":"8","price_usd":"0.5069722536476557","price_btc":"0.00002182989348696495","volume_24h_usd":"507973898.04662097","market_cap_usd":"17111613980","circulating_supply":"33752565071","total_supply":"34277702082","max_supply":"45000000000","percent_change_1h":"0.05","percent_change_24h":"-0.11","percent_change_7d":"11.68","last_updated":"1659464759"},{"id":"algo-algorand","name":"Algorand","symbol":"ALGO","rank":"33","price_usd":"0.33196893931152616","price_btc":"0.00001429436529121747","volume_24h_usd":"62207779.35759487","market_cap_usd":"2306909784","circulating_supply":"6949173585","total_supply":"7350412337","max_supply":"","percent_change_1h":"-0.06","percent_change_24h":"-0.82","percent_change_7d":"9.69","last_updated":"1659464759"},{"id":"xau-gold-spot-token","name":"Gold Spot Token","symbol":"XAU","rank":"2634","price_usd":"1767.0700000000002","price_btc":"0.07608887785567188","volume_24h_usd":"11881071.852000002","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.33","percent_change_24h":"-0.19","percent_change_7d":"2.89","last_updated":"1659464759"},{"id":"xag-silver-spot-token","name":"Silver Spot Token","symbol":"XAG","rank":"4753","price_usd":"20.0265","price_btc":"0.0008623279849562342","volume_24h_usd":"445379.34674999997","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.61","percent_change_24h":"-1.54","percent_change_7d":"7.49","last_updated":"1659464759"},{"id":"xbr-brent-crude-oil-spot","name":"Brent Crude Oil Spot","symbol":"XBR","rank":"2616","price_usd":"100.39000000000001","price_btc":"0.0043227277062770015","volume_24h_usd":"4527788.105237802","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.89","percent_change_24h":"0.87","percent_change_7d":"1.02","last_updated":"1659464759"},{"id":"chf-swiss-franc-token","name":"Swiss Franc Token","symbol":"CHF","rank":"3234","price_usd":"1.0453383927173951","price_btc":"0.00004501158713651311","volume_24h_usd":"360978.80080814153","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.08","percent_change_24h":"-0.66","percent_change_7d":"0.65","last_updated":"1659464759"},{"id":"eur-euro-token","name":"Euro Token","symbol":"EUR","rank":"5116","price_usd":"1.0185784743417672","price_btc":"0.00004385932256255119","volume_24h_usd":"40587878.087926075","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"0.05","percent_change_24h":"-0.82","percent_change_7d":"0.6","last_updated":"1659464759"},{"id":"gbp-pound-sterling-token","name":"Pound Sterling Token","symbol":"GBP","rank":"3424","price_usd":"1.2181792934946465","price_btc":"0.00005245400321946659","volume_24h_usd":"5428622.322314565","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.06","percent_change_24h":"-0.61","percent_change_7d":"1.29","last_updated":"1659464759"},{"id":"dj30-dow-jones-30-token","name":"Dow Jones 30 Token","symbol":"DJ30","rank":"3192","price_usd":"32546","price_btc":"1.401409462381624","volume_24h_usd":"293564.92","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.1","percent_change_24h":"-0.91","percent_change_7d":"2.5","last_updated":"1659464759"},{"id":"spx-sp-500","name":"S&P 500 Token","symbol":"SPX","rank":"3342","price_usd":"4110.6","price_btc":"0.17699974608449287","volume_24h_usd":"780397.41","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"-0.03","percent_change_24h":"-0.27","percent_change_7d":"4.87","last_updated":"1659464759"},{"id":"ndx-nasdaq-100-token","name":"NASDAQ 100 Token","symbol":"NDX","rank":"5097","price_usd":"12948.199999999999","price_btc":"0.5575410188904857","volume_24h_usd":"11221816.494","market_cap_usd":"","circulating_supply":"","total_supply":"","max_supply":"","percent_change_1h":"0.08","percent_change_24h":"0.04","percent_change_7d":"7.21","last_updated":"1659464759"}] +[{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"23250.774053363122","price_btc":"1","volume_24h_usd":"28307579008.4866","market_cap_usd":"444336521633","circulating_supply":"19110612","total_supply":"19110619","max_supply":"21000000","percent_change_1h":"-0.27","percent_change_24h":"0.89","percent_change_7d":"11.15","last_updated":"1659464759"},{"id":"eth-ethereum","name":"Ethereum","symbol":"ETH","rank":"2","price_usd":"1659.6621665887371","price_btc":"0.07146396683507168","volume_24h_usd":"18216561308.363518","market_cap_usd":"202151289827","circulating_supply":"121802674","total_supply":"121802721","max_supply":"","percent_change_1h":"-0.03","percent_change_24h":"1.82","percent_change_7d":"21.42","last_updated":"1659464759"},{"id":"ltc-litecoin","name":"Litecoin","symbol":"LTC","rank":"23","price_usd":"59.013589192027936","price_btc":"0.002541086532993605","volume_24h_usd":"510336800.72056556","market_cap_usd":"4181502638","circulating_supply":"70856606","total_supply":"70856631","max_supply":"84000000","percent_change_1h":"-0.43","percent_change_24h":"0.4","percent_change_7d":"12.79","last_updated":"1659464759"},{"id":"xmr-monero","name":"Monero","symbol":"XMR","rank":"30","price_usd":"158.97302629813817","price_btc":"0.006845274482813666","volume_24h_usd":"78159392.30003875","market_cap_usd":"2886277702","circulating_supply":"18155770","total_supply":"18155768","max_supply":"","percent_change_1h":"0.21","percent_change_24h":"1.21","percent_change_7d":"7.28","last_updated":"1659464759"},{"id":"ada-cardano","name":"Cardano","symbol":"ADA","rank":"8","price_usd":"0.5069722536476557","price_btc":"0.00002182989348696495","volume_24h_usd":"507973898.04662097","market_cap_usd":"17111613980","circulating_supply":"33752565071","total_supply":"34277702082","max_supply":"45000000000","percent_change_1h":"0.05","percent_change_24h":"-0.11","percent_change_7d":"11.68","last_updated":"1659464759"},{"id":"algo-algorand","name":"Algorand","symbol":"ALGO","rank":"33","price_usd":"0.33196893931152616","price_btc":"0.00001429436529121747","volume_24h_usd":"62207779.35759487","market_cap_usd":"2306909784","circulating_supply":"6949173585","total_supply":"7350412337","max_supply":"","percent_change_1h":"-0.06","percent_change_24h":"-0.82","percent_change_7d":"9.69","last_updated":"1659464759"}] diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index c1e925d..28a9e8d 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -87,7 +87,6 @@ class TestSuiteScripts(TestSuiteBase): ('ticker15', 'ticker [--cached-data --wide --btc btc:2:usd:45000]'), ('ticker16', 'ticker [--cached-data --wide --elapsed -c eur,omr-omani-rial:2.59r'), ('ticker17', 'ticker [--cached-data --wide --elapsed -c bgn-bulgarian-lev:0.5113r:eur'), - ('ticker18', 'ticker [--cached-data --wide --elapsed -c eur,bgn-bulgarian-lev:0.5113r:eur-euro-token'), ) } @@ -102,6 +101,7 @@ class TestSuiteScripts(TestSuiteBase): def ticker_setup(self): self.spawn('',msg_only=True) shutil.copy2(os.path.join(refdir,'ticker.json'),self.tmpdir) + shutil.copy2(os.path.join(refdir,'ticker-finance.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) return 'ok' @@ -123,8 +123,8 @@ class TestSuiteScripts(TestSuiteBase): if not cfg.skipping_deps: t.expect('Creating') t.expect('Creating') - t.expect('proxy host could not be resolved') - t.req_exit_val = 3 + ret = t.expect(['proxy host could not be resolved','ProxyError']) + t.req_exit_val = 3 if ret == 0 else 1 return t def ticker3(self): @@ -138,15 +138,15 @@ class TestSuiteScripts(TestSuiteBase): def ticker4(self): return self.ticker( - ['--wide','--add-columns=eur,inr-indian-rupee:79.5'], + ['--wide','--add-columns=eurusd=x,inr-indian-rupee:79.5'], [ - r'EUR \(EURO TOKEN\) = 1.0186 USD ' + + r'EURUSD=X \(EUR/USD\) = 1.0642 USD ' + r'INR \(INDIAN RUPEE\) = 0.012579 USD', - 'USD EUR INR BTC CHG_7d CHG_24h UPDATED', + 'USD EURUSD=X INR BTC CHG_7d CHG_24h UPDATED', 'BITCOIN', - r'ETHEREUM 1,659.66 1,629.3906 131,943.14 0.07146397 \+21.42 \+1.82', - r'MONERO 158.97 156.0734 12,638.36 0.00684527 \+7.28 \+1.21 2022-08-02 18:25:59', - r'INDIAN RUPEE 0.01 0.0123 1.00 0.00000054 -- --', + r'ETHEREUM 1,659.66 1,559.5846 131,943.14 0.07146397 \+21.42 \+1.82', + r'MONERO 158.97 149.3870 12,638.36 0.00684527 \+7.28 \+1.21 2022-08-02 18:25:59', + r'INDIAN RUPEE 0.01 0.0118 1.00 0.00000054 -- --', ]) def ticker5(self): @@ -213,7 +213,7 @@ class TestSuiteScripts(TestSuiteBase): 'SPOT PRICE', 'BTC 0.11783441', 'XMR 17.23400000', - 'XAU','NDX', + 'GC=F',r'\^IXIC', ]) def ticker11(self): @@ -277,32 +277,22 @@ class TestSuiteScripts(TestSuiteBase): def ticker16(self): return self.ticker( - ['--wide','--elapsed','-c','eur,omr-omani-rial:2.59r'], + ['--wide','--elapsed','-c','eurusd=x,omr-omani-rial:2.59r'], [ - r'EUR \(EURO TOKEN\) = 1.0186 USD ' + + r'EURUSD=X \(EUR/USD\) = 1.0642 USD ' + r'OMR \(OMANI RIAL\) = 2.5900 USD', - 'USD EUR OMR BTC CHG_7d CHG_24h UPDATED', - r'BITCOIN 23,250.77 22,826.6890 8,977.1328 1.00000000 \+11.15 \+0.89 10 minutes ago', - 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- --' + 'USD EURUSD=X OMR BTC CHG_7d CHG_24h UPDATED', + r'BITCOIN 23,250.77 21,848.7527 8,977.1328 1.00000000 \+11.15 \+0.89 10 minutes ago', + 'OMANI RIAL 2.59 2.4338 1.0000 0.00011139 -- -- --' ]) def ticker17(self): # BGN pegged at 0.5113 EUR return self.ticker( - ['--wide','--elapsed','-c','bgn-bulgarian-lev:0.5113r:eur'], + ['--wide','--elapsed','-c','bgn-bulgarian-lev:0.5113r:eurusd=x'], [ - r'BGN \(BULGARIAN LEV\) = 0.52080 USD', + r'BGN \(BULGARIAN LEV\) = 0.54411 USD', 'USD BGN BTC CHG_7d CHG_24h UPDATED', - 'BITCOIN 23,250.77 44,644.414 1.00000000', - 'BULGARIAN LEV 0.52 1.000 0.00002240', - ]) - - def ticker18(self): - return self.ticker( - ['--wide','--elapsed','-c','eur,bgn-bulgarian-lev:0.5113r:eur-euro-token'], - [ - r'BGN \(BULGARIAN LEV\) = 0.52080 USD', - 'USD EUR BGN BTC CHG_7d CHG_24h UPDATED', - 'BITCOIN 23,250.77 22,826.6890 44,644.414 1.00000000', - 'BULGARIAN LEV 0.52 0.5113 1.000 0.00002240', + 'BITCOIN 23,250.77 42,731.767 1.00000000', + 'BULGARIAN LEV 0.54 1.000 0.00002340', ]) From df620f923f472ef9d03fd3673395511e8b473c8b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 3 Oct 2023 14:35:57 +0000 Subject: [PATCH 098/175] minor fixes and cleanups --- mmgen_node_tools/Sound.py | 5 ++++- mmgen_node_tools/Ticker.py | 28 +++++++++++++++------------- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_feeview.py | 24 ++++++++++++------------ mmgen_node_tools/main_txfind.py | 2 +- setup.cfg | 2 +- test/test_py_d/ts_main.py | 7 +++---- test/test_py_d/ts_misc.py | 8 ++++---- test/test_py_d/ts_regtest.py | 8 ++++---- test/unit_tests_d/ut_BlocksInfo.py | 4 +--- 10 files changed, 46 insertions(+), 44 deletions(-) diff --git a/mmgen_node_tools/Sound.py b/mmgen_node_tools/Sound.py index 271253c..88d3b02 100755 --- a/mmgen_node_tools/Sound.py +++ b/mmgen_node_tools/Sound.py @@ -20,7 +20,10 @@ mmgen_node_tools.Sound: audio-related functions for MMGen node tools """ import sys,os,time -from mmgen_node_tools.Util import * + +from mmgen.util import die + +from mmgen_node_tools.Util import do_system _alsa_config_file = '/tmp/alsa-config-' + os.path.basename(sys.argv[0]) _dvols = { 'Master': 78, 'Speaker': 78, 'Headphone': 15, 'PCM': 190 } diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index e6f0650..97b5dc5 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -24,7 +24,7 @@ from subprocess import run,PIPE,CalledProcessError from decimal import Decimal from collections import namedtuple -from mmgen.color import * +from mmgen.color import red,yellow,green,blue,orange,gray from mmgen.util import msg,msg_r,Msg,die,Die,suf,fmt,fmt_list,fmt_dict,list_gen homedir = os.getenv('HOME') @@ -37,8 +37,9 @@ def fetch_delay(fetched_data=[]): if not gcfg.testing: if fetched_data: delay = 1 + random.randrange(1,5000) / 1000 - msg(f'Waiting {delay:.3f} seconds...') + msg_r(f'Waiting {delay:.3f} seconds...') time.sleep(delay) + msg('') else: fetched_data.append(None) @@ -265,11 +266,11 @@ class DataSource: def assets_list_gen(cfg_in): for k,v in cfg_in.cfg['assets'].items(): - yield('') - yield(k.upper()) + yield '' + yield k.upper() for e in v: out = e.split('-',1) - yield(' {:5s} {}'.format(out[0],out[1] if len(out) == 2 else '')) + yield ' {:5s} {}'.format(out[0],out[1] if len(out) == 2 else '') def gen_data(data): """ @@ -421,7 +422,8 @@ def main(): if gcfg.list_ids: src_ids = ['cc'] elif gcfg.download: - assert gcfg.download in DataSource.sources, f'{gcfg.download!r}: invalid data source' + if not gcfg.download in DataSource.sources: + die(1,f'{gcfg.download!r}: invalid data source') src_ids = [gcfg.download] else: src_ids = DataSource.sources @@ -449,19 +451,22 @@ def main(): def make_cfg(): + query_tuple = namedtuple('query',['asset','to_asset']) + asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset','source']) + + def parse_asset_id(s,require_label=False): + return src_cls['fi' if re.match(fi_pat,s) else 'cc'].parse_asset_id(s,require_label) + def get_rows_from_cfg(add_data=None): def gen(): for n,(k,v) in enumerate(cfg_in.cfg['assets'].items()): - yield(k) + yield k if add_data and k in add_data: v += tuple(add_data[k]) for e in v: yield parse_asset_id(e,require_label=True) return tuple(gen()) - def parse_asset_id(s,require_label=False): - return src_cls['fi' if re.match(fi_pat,s) else 'cc'].parse_asset_id(s,require_label) - def parse_usr_asset_arg(key,use_cf_file=False): """ asset_id[:rate[:rate_asset]] @@ -581,9 +586,6 @@ def make_cfg(): cmd_args = gcfg._args cfg_in = get_cfg_in() - query_tuple = namedtuple('query',['asset','to_asset']) - asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset','source']) - usr_rows = parse_usr_asset_arg('add_rows') usr_columns = parse_usr_asset_arg('add_columns',use_cf_file=True) query = parse_query_arg(cmd_args[0]) if cmd_args else None diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 0c84a84..4b653e3 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev1 +3.2.dev2 diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 72692fc..7bee773 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -145,25 +145,25 @@ def create_data(coin_amt,mempool): def gen_header(host,mempool,blockcount): - yield(fmt(f""" + yield fmt(f""" Mempool Fee Structure Date: {make_timestr()} UTC Host: {host} Network: {proto.coin.upper()} {proto.network.upper()} Block: {blockcount} TX count: {len(mempool)} - """)).strip() + """).strip() if cfg.show_empty: - yield('Displaying all fee brackets') + yield 'Displaying all fee brackets' elif cfg.ignore_below: - yield('Ignoring fee brackets with less than {:,} bytes ({})'.format( - ignore_below, - int2bytespec(ignore_below,'MB','0.6',strip=True,add_space=True), - )) + yield 'Ignoring fee brackets with less than {:,} bytes ({})'.format( + ignore_below, + int2bytespec(ignore_below,'MB','0.6',strip=True,add_space=True), + ) if cfg.include_current: - yield('Including transactions in current fee bracket in Total MB amounts') + yield 'Including transactions in current fee bracket in Total MB amounts' def fmt_mb(n): return int2bytespec(n,'MB',f'0.{precision}',print_sym=False) @@ -187,17 +187,17 @@ def gen_body(data): for i in data: if not i.skip: cum_bytes = i.tx_bytes_cum + i.tx_bytes if cfg.include_current else i.tx_bytes_cum - yield(fs.format( + yield fs.format( a = '{}-{}'.format(i.bottom,i.top) if cfg.ranges else i.top, b = fmt_mb(i.tx_bytes), c = fmt_mb(cum_bytes), - d = '-' * int(col4_w * ( i.tx_bytes / tx_bytes_max )) )) + d = '-' * int(col4_w * ( i.tx_bytes / tx_bytes_max )) ) - yield(fs.format( + yield fs.format( a = 'TOTAL', b = '', c = fmt_mb(data[-1].tx_bytes_cum + data[-1].tx_bytes if data else 0), - d = '' )) + d = '' ) async def main(): diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index cc8df14..57b691a 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -21,7 +21,7 @@ mmnode-txfind: Find a transaction in the blockchain or mempool """ from mmgen.cfg import Config -from mmgen.util import msg,Msg,die +from mmgen.util import msg,Msg,die,is_hex_str opts_data = { 'text': { diff --git a/setup.cfg b/setup.cfg index 9539a29..aeebff5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=14.0.dev2 + mmgen>=14.0.dev6 yahooquery packages = diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index 162e3fb..903981c 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -12,11 +12,10 @@ test_py_d.ts_main: Basic operations tests for the test.py test suite """ -import time +import sys,time -from ..include.common import * -from .common import * -from .ts_base import * +from ..include.common import cfg +from .ts_base import TestSuiteBase class TestSuiteMain(TestSuiteBase): 'basic operations with fake RPC data' diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 28a9e8d..06674ed 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -12,10 +12,10 @@ test.test_py_d.ts_misc: Miscellaneous test groups for the test.py test suite """ -import shutil -from ..include.common import * -from .common import * -from .ts_base import * +import os,shutil + +from ..include.common import cfg +from .ts_base import TestSuiteBase refdir = os.path.join('test','ref','ticker') diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index f8859b8..2ed45b4 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -13,13 +13,13 @@ test.test_py_d.ts_regtest: Regtest tests for the test.py test suite """ import os -from mmgen.util import die,gmsg + +from mmgen.util import msg_r,die,gmsg from mmgen.protocol import init_proto from mmgen.proto.btc.regtest import MMGenRegtest -from ..include.common import * -from .common import * -from .ts_base import * +from ..include.common import cfg,imsg,stop_test_daemons,joinpath +from .ts_base import TestSuiteBase args1 = ['--bob'] args2 = ['--bob','--rpc-backend=http'] diff --git a/test/unit_tests_d/ut_BlocksInfo.py b/test/unit_tests_d/ut_BlocksInfo.py index e65e314..b301a66 100755 --- a/test/unit_tests_d/ut_BlocksInfo.py +++ b/test/unit_tests_d/ut_BlocksInfo.py @@ -3,11 +3,9 @@ test.unit_tests_d.nt_BlocksInfo: BlocksInfo unit test for the MMGen Node Tools suite """ -from mmgen.common import * -from mmgen.exception import * from mmgen_node_tools.BlocksInfo import BlocksInfo -from ..include.common import cfg,vmsg +from ..include.common import vmsg tip = 50000 range_vecs = ( From 666b27c04200ff4312b04a857af0ab9ea88c2518 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 13 Oct 2023 09:50:15 +0000 Subject: [PATCH 099/175] test rename: test.py -> cmdtest.py --- test/{test_py_d => cmdtest_py_d}/cfg.py | 10 +-- .../ts_main.py => cmdtest_py_d/ct_main.py} | 6 +- .../ts_misc.py => cmdtest_py_d/ct_misc.py} | 8 +-- .../ct_regtest.py} | 8 +-- test/init.sh | 65 ++++++++++++------- test/test-release.d/cfg.sh | 12 ++-- 6 files changed, 64 insertions(+), 45 deletions(-) rename test/{test_py_d => cmdtest_py_d}/cfg.py (67%) rename test/{test_py_d/ts_main.py => cmdtest_py_d/ct_main.py} (93%) rename test/{test_py_d/ts_misc.py => cmdtest_py_d/ct_misc.py} (97%) rename test/{test_py_d/ts_regtest.py => cmdtest_py_d/ct_regtest.py} (98%) diff --git a/test/test_py_d/cfg.py b/test/cmdtest_py_d/cfg.py similarity index 67% rename from test/test_py_d/cfg.py rename to test/cmdtest_py_d/cfg.py index c2c72e3..5aacbf9 100755 --- a/test/test_py_d/cfg.py +++ b/test/cmdtest_py_d/cfg.py @@ -9,16 +9,16 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.test_py_d.cfg: configuration data for test.py +test.cmdtest_py_d.cfg: configuration data for cmdtest.py """ import os cmd_groups_dfl = { - 'main': ('TestSuiteMain',{}), - 'helpscreens': ('TestSuiteHelp',{'modname':'misc','full_data':True}), - 'scripts': ('TestSuiteScripts',{'modname':'misc'}), - 'regtest': ('TestSuiteRegtest',{}), + 'main': ('CmdTestMain',{}), + 'helpscreens': ('CmdTestHelp',{'modname':'misc','full_data':True}), + 'scripts': ('CmdTestScripts',{'modname':'misc'}), + 'regtest': ('CmdTestRegtest',{}), } cmd_groups_extra = {} diff --git a/test/test_py_d/ts_main.py b/test/cmdtest_py_d/ct_main.py similarity index 93% rename from test/test_py_d/ts_main.py rename to test/cmdtest_py_d/ct_main.py index 903981c..ad1ce41 100755 --- a/test/test_py_d/ts_main.py +++ b/test/cmdtest_py_d/ct_main.py @@ -9,15 +9,15 @@ # https://gitlab.com/mmgen/mmgen """ -test_py_d.ts_main: Basic operations tests for the test.py test suite +cmdtest_py_d.ct_main: Basic operations tests for the cmdtest.py test suite """ import sys,time from ..include.common import cfg -from .ts_base import TestSuiteBase +from .ct_base import CmdTestBase -class TestSuiteMain(TestSuiteBase): +class CmdTestMain(CmdTestBase): 'basic operations with fake RPC data' tmpdir_nums = [3] networks = ('btc',) # fake data, so test peerblocks for BTC mainnet only diff --git a/test/test_py_d/ts_misc.py b/test/cmdtest_py_d/ct_misc.py similarity index 97% rename from test/test_py_d/ts_misc.py rename to test/cmdtest_py_d/ct_misc.py index 06674ed..966fb91 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -9,17 +9,17 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.test_py_d.ts_misc: Miscellaneous test groups for the test.py test suite +test.cmdtest_py_d.ct_misc: Miscellaneous test groups for the cmdtest.py test suite """ import os,shutil from ..include.common import cfg -from .ts_base import TestSuiteBase +from .ct_base import CmdTestBase refdir = os.path.join('test','ref','ticker') -class TestSuiteHelp(TestSuiteBase): +class CmdTestHelp(CmdTestBase): 'help, info and usage screens' networks = ('btc','ltc','bch') tmpdir_nums = [] @@ -52,7 +52,7 @@ class TestSuiteHelp(TestSuiteBase): def longhelpscreens(self): return self.helpscreens(arg='--longhelp',expect='USAGE:.*LONG OPTIONS:') -class TestSuiteScripts(TestSuiteBase): +class CmdTestScripts(CmdTestBase): 'scripts not requiring a coin daemon' networks = ('btc',) tmpdir_nums = [2] diff --git a/test/test_py_d/ts_regtest.py b/test/cmdtest_py_d/ct_regtest.py similarity index 98% rename from test/test_py_d/ts_regtest.py rename to test/cmdtest_py_d/ct_regtest.py index 2ed45b4..a03e3ff 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.test_py_d.ts_regtest: Regtest tests for the test.py test suite +test.cmdtest_py_d.ct_regtest: Regtest tests for the cmdtest.py test suite """ import os @@ -19,7 +19,7 @@ from mmgen.protocol import init_proto from mmgen.proto.btc.regtest import MMGenRegtest from ..include.common import cfg,imsg,stop_test_daemons,joinpath -from .ts_base import TestSuiteBase +from .ct_base import CmdTestBase args1 = ['--bob'] args2 = ['--bob','--rpc-backend=http'] @@ -31,7 +31,7 @@ def gen_addrs(proto,network,keys): tool.addrtype = proto.mmtypes[-1] return [tool.privhex2addr('{:064x}'.format(key)) for key in keys] -class TestSuiteRegtest(TestSuiteBase): +class CmdTestRegtest(CmdTestBase): 'various operations via regtest mode' networks = ('btc','ltc','bch') passthru_opts = ('coin',) @@ -104,7 +104,7 @@ class TestSuiteRegtest(TestSuiteBase): } def __init__(self,trunner,cfgs,spawn): - TestSuiteBase.__init__(self,trunner,cfgs,spawn) + CmdTestBase.__init__(self,trunner,cfgs,spawn) if trunner == None: return if self.proto.testnet: diff --git a/test/init.sh b/test/init.sh index a8d03a9..913ac9c 100755 --- a/test/init.sh +++ b/test/init.sh @@ -45,37 +45,56 @@ build_mmgen_extmod() { } create_dir_links() { - for target in 'mmgen' 'scripts'; do - src="$mm_repo/$target" - if [ -e $target ]; then - [ $(realpath --relative-to=. $target) == $src ] || die "'$target' does not point to '$src'" + for link_name in 'mmgen' 'scripts'; do + target="$mm_repo/$link_name" + if [ -e $link_name ]; then + [ $(realpath --relative-to=. $link_name) == $target ] || die "'$link_name' does not point to '$target'" else - echo "Creating symlink: $target" - ln -s $src + echo "Creating symlink: $link_name" + ln -s $target fi done } create_test_links() { - sources=' - test/include - test/overlay/__init__.py - test/overlay/fakemods/mmgen - test/__init__.py - test/test.py - test/unit_tests.py - test/test-release.sh - test/test_py_d/common.py - test/test_py_d/ts_base.py - cmds/mmgen-regtest + paths=' + test/include symbolic + test/overlay/__init__.py symbolic + test/overlay/fakemods/mmgen symbolic + test/__init__.py symbolic + test/cmdtest.py hard + test/unit_tests.py hard + test/test-release.sh symbolic + test/cmdtest_py_d/common.py symbolic + test/cmdtest_py_d/ct_base.py symbolic + cmds/mmgen-regtest symbolic ' - for src in $sources; do - pfx=$(echo $src | sed -r 's/[^/]//g' | sed 's/\//..\//g') - if [ ! -e $src ]; then - echo "Creating symlink: $src" - ( cd "$(dirname $src)" && ln -s "$pfx$mm_repo/$src" ) + while read path type; do + [ "$path" ] || continue + pfx=$(echo $path | sed -r 's/[^/]//g' | sed 's/\//..\//g') + symlink_arg=$(if [ $type == 'symbolic' ]; then echo --symbolic; fi) + target="$mm_repo/$path" + if [ ! -e "$target" ]; then + echo "Target path $target is missing! Cannot proceed" + exit 1 fi - done + fs="%-8s %-16s %s -> %s\n" + if [ $type == 'hard' ]; then + if [ -L $path ]; then + printf "$fs" "Deleting" "symbolic link:" $path $target + rm -rf $path + elif [ -e $path ]; then + if [ "$(stat --printf=%i $path)" -ne "$(stat --printf=%i $target)" ]; then + printf "$fs" "Deleting" "stale hard link:" $path "?" + rm -rf $path + fi + fi + fi + if [ ! -e $path ]; then # link is either absent or a broken symlink + printf "$fs" "Creating" "$type link:" $path $target + ( cd "$(dirname $path)" && ln -f $symlink_arg $pfx$target ) + fi + done <<<$paths } set -e diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index cfe98b7..54d8edd 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -41,20 +41,20 @@ init_tests() { t_unit="- $unit_tests_py" d_misc="miscellaneous features" - t_misc="- $test_py helpscreens" + t_misc="- $cmdtest_py helpscreens" d_scripts="scripts not requiring a coin daemon" - t_scripts="- $test_py scripts" + t_scripts="- $cmdtest_py scripts" d_btc="Bitcoin with emulated RPC data" - t_btc="- $test_py main" + t_btc="- $cmdtest_py main" d_btc_rt="Bitcoin regtest" - t_btc_rt="- $test_py regtest" + t_btc_rt="- $cmdtest_py regtest" d_bch_rt="Bitcoin Cash Node (BCH) regtest" - t_bch_rt="- $test_py --coin=bch regtest" + t_bch_rt="- $cmdtest_py --coin=bch regtest" d_ltc_rt="Litecoin regtest" - t_ltc_rt="- $test_py --coin=ltc regtest" + t_ltc_rt="- $cmdtest_py --coin=ltc regtest" } From 3f76be1ab8e948a341688522573767f17fe5f9aa Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 13 Oct 2023 09:50:15 +0000 Subject: [PATCH 100/175] minor fixes and cleanups --- mmgen_node_tools/BlocksInfo.py | 93 +++++++++++++++++--------------- mmgen_node_tools/PollDisplay.py | 2 +- mmgen_node_tools/Ticker.py | 6 ++- mmgen_node_tools/main_addrbal.py | 2 + mmgen_node_tools/main_netrate.py | 4 +- mmgen_node_tools/main_ticker.py | 10 ++-- mmgen_node_tools/main_txfind.py | 4 +- setup.cfg | 1 + test/cmdtest_py_d/ct_regtest.py | 2 +- 9 files changed, 68 insertions(+), 56 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 25e1caf..40cc6e5 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -27,6 +27,52 @@ from time import strftime,gmtime from mmgen.util import msg,Msg,Msg_r,die,suf,secs_to_ms,secs_to_dhms,is_int from mmgen.rpc import json_encoder +class RangeParser: + + debug = False + + def __init__(self,caller,arg): + self.caller = caller + self.arg = self.orig_arg = arg + + def parse(self,target): + ret = getattr(self,'parse_'+target)() + if self.debug: + msg(f'arg after parse({target}): {self.arg}') + return ret + + def finalize(self): + if self.arg: + die(1,f'{self.orig_arg!r}: invalid range specifier') + + def parse_from_tip(self): + m = re.match(r'-([0-9]+)(.*)',self.arg) + if m: + res,self.arg = (m[1],m[2]) + return self.caller.check_nblocks(int(res)) + + def parse_abs_range(self): + m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',self.arg) + if m: + if self.debug: + msg(f'abs_range parse: first={m[1]}, last={m[3]}') + self.arg = m[4] + return ( + self.caller.conv_blkspec(m[1]), + self.caller.conv_blkspec(m[3]) if m[3] else None + ) + return (None,None) + + def parse_add(self): + m = re.match(r'\+([0-9*]+)(.*)',self.arg) + if m: + res,self.arg = (m[1],m[2]) + if res.strip('*') != res: + die(1,f"'+{res}': malformed nBlocks specifier") + if len(res) > 30: + die(1,f"'+{res}': overly long nBlocks specifier") + return self.caller.check_nblocks(eval(res)) # res is only digits plus '*', so eval safe + class BlocksInfo: total_bytes = 0 @@ -298,49 +344,8 @@ class BlocksInfo: def parse_rangespec(self,arg): - class RangeParser: - debug = False + p = RangeParser(self,arg) - def __init__(rp,arg): - rp.arg = rp.orig_arg = arg - - def parse(rp,target): - ret = getattr(rp,'parse_'+target)() - if rp.debug: msg(f'arg after parse({target}): {rp.arg}') - return ret - - def finalize(rp): - if rp.arg: - die(1,f'{rp.orig_arg!r}: invalid range specifier') - - def parse_from_tip(rp): - m = re.match(r'-([0-9]+)(.*)',rp.arg) - if m: - res,rp.arg = (m[1],m[2]) - return self.check_nblocks(int(res)) - - def parse_abs_range(rp): - m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',rp.arg) - if m: - if rp.debug: msg(f'abs_range parse: first={m[1]}, last={m[3]}') - rp.arg = m[4] - return ( - self.conv_blkspec(m[1]), - self.conv_blkspec(m[3]) if m[3] else None - ) - return (None,None) - - def parse_add(rp): - m = re.match(r'\+([0-9*]+)(.*)',rp.arg) - if m: - res,rp.arg = (m[1],m[2]) - if res.strip('*') != res: - die(1,f"'+{res}': malformed nBlocks specifier") - if len(res) > 30: - die(1,f"'+{res}': overly long nBlocks specifier") - return self.check_nblocks(eval(res)) # res is only digits plus '*', so eval safe - - p = RangeParser(arg) from_tip = p.parse('from_tip') first,last = (self.tip-from_tip,None) if from_tip else p.parse('abs_range') add1 = p.parse('add') @@ -708,8 +713,8 @@ class BlocksInfo: class JSONBlocksInfo(BlocksInfo): - def __init__(self,cfg,cmd_args,opt,rpc): - super().__init__(cfg,cmd_args,opt,rpc) + def __init__(self,cfg,cmd_args,rpc): + super().__init__(cfg,cmd_args,rpc) if self.cfg.json_raw: self.output_block = self.output_block_raw self.fmt_stat_item = self.fmt_stat_item_raw diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py index 84a6023..5bbfecd 100755 --- a/mmgen_node_tools/PollDisplay.py +++ b/mmgen_node_tools/PollDisplay.py @@ -16,7 +16,7 @@ import sys,threading from mmgen.util import msg from mmgen.term import get_char -class PollDisplay(): +class PollDisplay: info = None input = None diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 97b5dc5..1e21192 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -449,7 +449,7 @@ def main(): '\n'.join(getattr(Ticker,cfg.clsname)(data).gen_output()) + '\n' ) -def make_cfg(): +def make_cfg(gcfg_arg): query_tuple = namedtuple('query',['asset','to_asset']) asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset','source']) @@ -578,7 +578,9 @@ def make_cfg(): 'proxy2', 'portfolio' ]) - global cfg_in,src_cls,cfg + global gcfg,cfg_in,src_cls,cfg + + gcfg = gcfg_arg src_cls = { k: getattr(DataSource,v) for k,v in DataSource.sources.items() } fi_pat = src_cls['fi'].asset_id_pat diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index a461e9e..34c6236 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -12,6 +12,8 @@ mmnode-addrbal: Get balances for arbitrary addresses in the blockchain """ +import sys + from mmgen.obj import CoinTxID,Int from mmgen.cfg import Config from mmgen.util import msg,Msg,die,suf,make_timestr,async_run diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index f1661c5..52b509d 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -49,7 +49,7 @@ async def main(): d = await c.call('getnettotals') return [float(e) for e in (d['totalbytesrecv'],d['totalbytessent'],d['timemillis'])] - rs = None + rs,ss,ts = (None,None,None) while True: r,s,t = await get_data() @@ -64,7 +64,7 @@ async def main(): if rs is not None: sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE)) - rs,ss,ts = r,s,t + rs,ss,ts = (r,s,t) try: async_run(main()) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index f329e03..6a9d3aa 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -216,14 +216,14 @@ import os from mmgen.util import fmt_list,fmt_dict from mmgen.cfg import Config -import mmgen_node_tools.Ticker as tck +from . import Ticker -tck.gcfg = Config( opts_data=opts_data, do_post_init=True ) +gcfg = Config( opts_data=opts_data, do_post_init=True ) -tck.make_cfg() +Ticker.make_cfg(gcfg) from .Ticker import dfl_cachedir,homedir,DataSource,assets_list_gen,cfg_in,src_cls -tck.gcfg._post_init() +gcfg._post_init() -tck.main() +Ticker.main() diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index 57b691a..4a54430 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -20,8 +20,10 @@ mmnode-txfind: Find a transaction in the blockchain or mempool """ +import sys + from mmgen.cfg import Config -from mmgen.util import msg,Msg,die,is_hex_str +from mmgen.util import msg,Msg,die,is_hex_str,async_run opts_data = { 'text': { diff --git a/setup.cfg b/setup.cfg index aeebff5..7e91ea5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,6 +24,7 @@ include_package_data = True install_requires = mmgen>=14.0.dev6 + pyyaml yahooquery packages = diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index a03e3ff..b17b4c6 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -107,7 +107,7 @@ class CmdTestRegtest(CmdTestBase): CmdTestBase.__init__(self,trunner,cfgs,spawn) if trunner == None: return - if self.proto.testnet: + if cfg._proto.testnet: die(2,'--testnet and --regtest options incompatible with regtest test suite') self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) From ada2cecadae5807678f27d34f8a871a49cfa9c65 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 13 Oct 2023 09:50:16 +0000 Subject: [PATCH 101/175] Check the MMGen Node Tools with the pylint static code analyzer: # Install pylint: $ python3 -m pip install pylint # Perform the check: $ test/test-release.sh lint The linting operation should complete without error if running Python v3.9 or greater. --- mmgen_node_tools/data/version | 2 +- pyproject.toml | 22 ++++++++++++++++++++++ setup.cfg | 2 +- test/test-release.d/cfg.sh | 16 ++++++++++++---- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 4b653e3..5223fbd 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev2 +3.2.dev3 diff --git a/pyproject.toml b/pyproject.toml index 374b58c..81515d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,25 @@ requires = [ "wheel" ] build-backend = "setuptools.build_meta" + +[tool.pylint.format] +indent-string = "\t" +indent-after-paren = 2 +max-line-length = 110 + +[tool.pylint.main] +py-version = "3.7" +recursive = true +jobs = 0 + +[tool.pylint."messages control"] +ignored-modules = [ + "mmgen.term", + "mmgen.color", +] +ignored-classes = [ + "mmgen_node_tools.Ticker.Ticker.base", + "mmgen_node_tools.Ticker.DataSource.base", + "mmgen_node_tools.PeerBlocks.Display", + "mmgen_node_tools.PollDisplay.PollDisplay", +] diff --git a/setup.cfg b/setup.cfg index 7e91ea5..55c315a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.7 include_package_data = True install_requires = - mmgen>=14.0.dev6 + mmgen>=14.0.dev9 pyyaml yahooquery diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 54d8edd..b371e72 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -18,7 +18,7 @@ # mmnode-ticker OK # mmnode-txfind - -all_tests='unit misc scripts btc btc_rt bch_rt ltc_rt' +all_tests='unit lint misc scripts btc btc_rt bch_rt ltc_rt' groups_desc=" default - All tests minus the extra tests @@ -29,14 +29,22 @@ groups_desc=" " init_groups() { - dfl_tests=$all_tests - extra_tests='' + dfl_tests='unit misc scripts btc btc_rt bch_rt ltc_rt' + extra_tests='lint' noalt_tests='unit misc scripts btc btc_rt' quick_tests='unit misc scripts btc btc_rt' - qskip_tests='bch_rt ltc_rt' + qskip_tests='lint bch_rt ltc_rt' } init_tests() { + + d_lint="code errors with static code analyzer" + t_lint=" + - $pylint --errors-only mmgen_node_tools + - $pylint --errors-only test + - $pylint --errors-only --disable=relative-beyond-top-level test/cmdtest_py_d + " + d_unit="low-level subsystems" t_unit="- $unit_tests_py" From e94f036c73abe57b41e1ffbf3f715198249fddda Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 5 Nov 2023 13:40:22 +0000 Subject: [PATCH 102/175] mmnode-ticker: cleanups --- mmgen_node_tools/Ticker.py | 80 ++++++++++++++++++++------------- mmgen_node_tools/main_ticker.py | 14 +++--- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 1e21192..fc0b8da 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -32,26 +32,34 @@ dfl_cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') cfg_fn = 'ticker-cfg.yaml' portfolio_fn = 'ticker-portfolio.yaml' asset_tuple = namedtuple('asset_tuple',['symbol','id','source']) - -def fetch_delay(fetched_data=[]): - if not gcfg.testing: - if fetched_data: - delay = 1 + random.randrange(1,5000) / 1000 - msg_r(f'Waiting {delay:.3f} seconds...') - time.sleep(delay) - msg('') - else: - fetched_data.append(None) +last_api_host = None class DataSource: - sources = { - 'cc': 'coinpaprika', - 'fi': 'yahooquery' - } + source_groups = [ + { + 'cc': 'coinpaprika' + }, { + 'fi': 'yahoospot', + } + ] + + @classmethod + def get_sources(cls,randomize=False): + g = random.sample(cls.source_groups,k=len(cls.source_groups)) if randomize else cls.source_groups + return {k:v for a in g for k,v in a.items()} class base: + def fetch_delay(self): + global last_api_host + if not gcfg.testing and last_api_host and last_api_host != self.api_host: + delay = 1 + random.randrange(1,5000) / 1000 + msg_r(f'Waiting {delay:.3f} seconds...') + time.sleep(delay) + msg('') + last_api_host = self.api_host + def get_data_from_network(self): curl_cmd = list_gen( @@ -90,11 +98,11 @@ class DataSource: else: data_type = self.net_data_type elapsed = int(time.time() - os.stat(self.json_fn).st_mtime) - if elapsed >= self.timeout: + if elapsed >= self.timeout or gcfg.testing: if gcfg.testing: msg('') - fetch_delay() - msg_r(f'Fetching data from {self.api_host}...') + self.fetch_delay() + msg_r(f'Fetching {self.data_desc} from {self.api_host}...') if self.has_verbose: gcfg._util.vmsg('') data_in = self.get_data_from_network() @@ -145,6 +153,7 @@ class DataSource: class coinpaprika(base): desc = 'CoinPaprika' + data_desc = 'cryptocurrency data' api_host = 'api.coinpaprika.com' ratelimit = 240 btc_ratelimit = 10 @@ -201,14 +210,16 @@ class DataSource: id = (s.lower() if label else None), source = 'cc' ) - class yahooquery(base): + class yahoospot(base): desc = 'Yahoo Finance' + data_desc = 'spot financial data' api_host = 'finance.yahoo.com' ratelimit = 30 net_data_type = 'python' has_verbose = False asset_id_pat = r'^\^.*|.*=[xf]$' + json_fn_basename = 'ticker-finance.json' @staticmethod def get_id(sym,data): @@ -233,29 +244,37 @@ class DataSource: @property def json_fn(self): - return os.path.join( cfg.cachedir, 'ticker-finance.json' ) + return os.path.join( cfg.cachedir, self.json_fn_basename ) @property def timeout(self): return 5 if gcfg.test_suite else self.ratelimit + @property + def symbols(self): + return [r.symbol for r in cfg.rows if isinstance(r,tuple) and r.source == 'fi'] + def get_data_from_network(self): - arg = [r.symbol for r in cfg.rows if isinstance(r,tuple) and r.source == 'fi'] - - kwargs = { 'formatted': True, 'proxies': { 'https': cfg.proxy2 } } + kwargs = { + 'formatted': True, + 'proxies': { 'https': cfg.proxy2 }, + } if gcfg.test_suite: kwargs.update({ 'timeout': 1, 'retry': 0 }) if gcfg.testing: Msg('\nyahooquery.Ticker(\n {},\n {}\n)'.format( - arg, + self.symbols, fmt_dict(kwargs,fmt='kwargs') )) return from yahooquery import Ticker - return Ticker(arg,**kwargs).price + return self.process_network_data( Ticker(self.symbols,**kwargs) ) + + def process_network_data(self,ticker): + return ticker.price @staticmethod def parse_asset_id(s,require_label): @@ -422,15 +441,13 @@ def main(): if gcfg.list_ids: src_ids = ['cc'] elif gcfg.download: - if not gcfg.download in DataSource.sources: + if not gcfg.download in DataSource.get_sources(): die(1,f'{gcfg.download!r}: invalid data source') src_ids = [gcfg.download] else: - src_ids = DataSource.sources + src_ids = DataSource.get_sources(randomize=True) - ids = random.sample( list(src_ids), k=len(src_ids) ) # shuffle the ids - - src_data = { k: src_cls[k]().get_data() for k in ids } + src_data = { k: src_cls[k]().get_data() for k in src_ids } if gcfg.testing: return @@ -582,7 +599,7 @@ def make_cfg(gcfg_arg): gcfg = gcfg_arg - src_cls = { k: getattr(DataSource,v) for k,v in DataSource.sources.items() } + src_cls = { k: getattr(DataSource,v) for k,v in DataSource.get_sources().items() } fi_pat = src_cls['fi'].asset_id_pat cmd_args = gcfg._args @@ -673,8 +690,7 @@ class Ticker: from mmgen.util2 import format_elapsed_hr fmt_func = format_elapsed_hr else: - fmt_func = lambda t,now: time.strftime('%F %X',time.gmtime(t)) # ticker API - # t.replace('T',' ').replace('Z','') # tickers API + fmt_func = lambda t,now: time.strftime('%F %X',time.gmtime(t)) d = self.data max_w = 0 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 6a9d3aa..13175b8 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -14,10 +14,10 @@ mmnode-ticker: Display price information for cryptocurrency and other assets opts_data = { 'sets': [ - ('wide', True, 'percent_change', True), - ('wide', True, 'name_labels', True), - ('wide', True, 'thousands_comma', True), - ('wide', True, 'update_time', True), + ('wide', True, 'percent_change', True), + ('wide', True, 'name_labels', True), + ('wide', True, 'thousands_comma', True), + ('wide', True, 'update_time', True), ], 'text': { 'desc': 'Display prices for cryptocurrency and other assets', @@ -35,8 +35,8 @@ opts_data = { live data from server -D, --cachedir=D Read and write cached JSON data to directory ‘D’ instead of ‘~/{dfl_cachedir}’ --d, --download=D Retrieve data ‘D’ from source, save to file and exit - (valid options: {ds}) +-d, --download=D Retrieve and cache asset data ‘D’ from network (valid + options: {ds}) -e, --add-precision=N Add ‘N’ digits of precision to columns -E, --elapsed Show elapsed time in UPDATED column (see --update-time) -F, --portfolio Display portfolio data @@ -200,7 +200,7 @@ To add a portfolio, edit the file 'code': { 'options': lambda s: s.format( dfl_cachedir = os.path.relpath(dfl_cachedir,start=homedir), - ds = fmt_dict(DataSource.sources,fmt='equal'), + ds = fmt_dict(DataSource.get_sources(),fmt='equal_compact'), ), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), From 94c61d680702cc1139d75c58f3105cad1b05446b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 5 Nov 2023 13:40:23 +0000 Subject: [PATCH 103/175] mmnode-ticker: convert amounts to type Decimal on input --- mmgen_node_tools/Ticker.py | 48 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index fc0b8da..b2fc86a 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -232,8 +232,8 @@ class DataSource: 'id': sym, 'name': data['shortName'], 'symbol': sym.upper(), - 'price_usd': str(price_usd), - 'price_btc': str(price_usd / btcusd), + 'price_usd': price_usd, + 'price_btc': price_usd / btcusd, 'percent_change_7d': None, 'percent_change_24h': data['regularMarketChangePercent']['raw'] * 100, 'last_updated': data['regularMarketTime'], @@ -378,6 +378,13 @@ def gen_data(data): if d[k] in wants[k]: if d[k] in found[k]: die(1,dup_sym_errmsg(d[k])) + if not d.get('_converted'): + d['price_usd'] = Decimal(d['price_usd']) + d['price_btc'] = Decimal(d['price_btc']) + d['percent_change_7d'] = Decimal(d['percent_change_7d']) + d['percent_change_24h'] = Decimal(d['percent_change_24h']) + d['last_updated'] = int(d['last_updated']) + d['_converted'] = True yield (d['id'],d) found[k].add(d[k]) wants[k].remove(d[k]) @@ -394,13 +401,13 @@ def gen_data(data): User-supplied rate overrides rate from source data. """ _id = asset.id or f'{asset.symbol}-user-asset-{asset.symbol}'.lower() - ra_rate = Decimal(rate_assets[asset.rate_asset.symbol]['price_usd']) if asset.rate_asset else 1 + ra_rate = rate_assets[asset.rate_asset.symbol]['price_usd'] if asset.rate_asset else 1 yield ( _id, { 'symbol': asset.symbol, 'id': _id, 'name': ' '.join(_id.split('-')[1:]), - 'price_usd': str(Decimal(ra_rate/asset.rate)), - 'price_btc': str(Decimal(ra_rate/asset.rate/btcusd)), + 'price_usd': ra_rate / asset.rate, + 'price_btc': ra_rate / asset.rate / btcusd, 'last_updated': None, }) @@ -408,8 +415,8 @@ def gen_data(data): 'symbol': 'USD', 'id': 'usd-us-dollar', 'name': 'US Dollar', - 'price_usd': '1.0', - 'price_btc': str(Decimal(1/btcusd)), + 'price_usd': Decimal(1), + 'price_btc': Decimal(1) / btcusd, 'last_updated': None, }) @@ -678,7 +685,7 @@ class Ticker: )) + 1 self.rows = [row._replace(id=self.get_id(row)) if isinstance(row,tuple) else row for row in cfg.rows] - self.col_usd_prices = {k:Decimal(self.data[k]['price_usd']) for k in self.col_ids} + self.col_usd_prices = {k:self.data[k]['price_usd'] for k in self.col_ids} self.prices = {row.id:self.get_row_prices(row.id) for row in self.rows if isinstance(row,tuple) and row.id in data} @@ -717,7 +724,7 @@ class Ticker: self.upd_w = max_w def init_prec(self): - exp = [(a.id,Decimal.adjusted(self.prices[a.id]['usd-us-dollar'])) for a in self.usr_col_assets] + exp = [(a.id, self.prices[a.id]['usd-us-dollar'].adjusted() ) for a in self.usr_col_assets] self.uprec = { k: max(0,v+4) + cfg.add_prec for k,v in exp } self.uwid = { k: 12 + max(0, abs(v)-6) + cfg.add_prec for k,v in exp } @@ -737,13 +744,13 @@ class Ticker: for asset in self.usr_col_assets: if asset.symbol != 'USD': - usdprice = Decimal(self.data[asset.id]['price_usd']) + usdprice = self.data[asset.id]['price_usd'] yield '{} ({}) = {:{}.{}f} USD'.format( asset.symbol, self.create_label(asset.id), usdprice, self.comma, - max(2,int(-usdprice.adjusted())+4) ) + max(2, 4-usdprice.adjusted()) ) if hasattr(self,'subhdr'): yield self.subhdr @@ -818,17 +825,14 @@ class Ticker: if id in self.data: d = self.data[id] return { k: ( - Decimal(d['price_btc']) if k == 'btc-bitcoin' else - Decimal(d['price_usd']) / self.col_usd_prices[k] + d['price_btc'] if k == 'btc-bitcoin' else + d['price_usd'] / self.col_usd_prices[k] ) * self.adjust for k in self.col_ids } def fmt_row(self,d,amt=None,amt_fmt=None): - def fmt_pct(d): - if d in ('',None): - return gray(' --') - n = Decimal(d) - return (red,green)[n>=0](f'{n:+7.2f}') + def fmt_pct(n): + return gray(' --') if n == None else (red,green)[n>=0](f'{n:+7.2f}') p = self.prices[d['id']] @@ -927,8 +931,8 @@ class Ticker: if self.offer: real_price = ( self.asset.amount - * Decimal(data[self.asset.id]['price_usd']) - / Decimal(data[self.to_asset.id]['price_usd']) + * data[self.asset.id]['price_usd'] + / data[self.to_asset.id]['price_usd'] ) if self.adjust != 1: die(1,'the --adjust option may not be combined with TO_AMOUNT in the trade specifier') @@ -943,7 +947,7 @@ class Ticker: self.usr_col_assets = [self.asset] + ([self.to_asset] if self.to_asset else []) for a in self.usr_col_assets: - self.prices[a.id]['usd-us-dollar'] = Decimal(data[a.id]['price_usd']) + self.prices[a.id]['usd-us-dollar'] = data[a.id]['price_usd'] self.format_last_update_col(cross_assets=self.usr_col_assets) @@ -953,7 +957,7 @@ class Ticker: def get_row_prices(self,id): if id in self.data: d = self.data[id] - return { k: self.col_usd_prices[self.asset.id] / Decimal(d['price_usd']) for k in self.col_ids } + return { k: self.col_usd_prices[self.asset.id] / d['price_usd'] for k in self.col_ids } def init_fs(self): self.max_wid = max( From 77d8ffb7b9accfa086a1b8e89f494b393c2940de Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 5 Nov 2023 13:40:23 +0000 Subject: [PATCH 104/175] mmnode-ticker: use coinpaprika `tickers` API call --- mmgen_node_tools/Ticker.py | 28 +++++++++++++++++----------- mmgen_node_tools/main_ticker.py | 20 +++++++++++++------- setup.cfg | 2 +- test/cmdtest_py_d/ct_misc.py | 12 ++++++------ test/ref/ticker/ticker-btc.json | 2 +- test/ref/ticker/ticker.json | 2 +- 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index b2fc86a..40bede8 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -12,14 +12,14 @@ mmgen_node_tools.Ticker: Display price information for cryptocurrency and other assets """ -# We use deprecated coinpaprika ‘ticker’ API for now because it returns ~45% less data. +# v3.2.dev4: switch to new coinpaprika ‘tickers’ API call (supports ‘limit’ parameter, more historical data) # Old ‘ticker’ API (/v1/ticker): data['BTC']['price_usd'] # New ‘tickers’ API (/v1/tickers): data['BTC']['quotes']['USD']['price'] # Possible alternatives: # - https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,LTC&tsyms=USD,EUR -import sys,os,re,time,json,yaml,random +import sys,os,re,time,datetime,json,yaml,random from subprocess import run,PIPE,CalledProcessError from decimal import Decimal from collections import namedtuple @@ -159,6 +159,10 @@ class DataSource: btc_ratelimit = 10 net_data_type = 'json' has_verbose = True + dfl_asset_limit = 2000 + + def __init__(self): + self.asset_limit = int(gcfg.asset_limit or self.dfl_asset_limit) def rate_limit_errmsg(self,elapsed): return ( @@ -168,7 +172,10 @@ class DataSource: @property def api_url(self): - return f'https://{self.api_host}/v1/ticker' + ('/btc-bitcoin' if cfg.btc_only else '') + return ( + f'https://{self.api_host}/v1/tickers/btc-bitcoin' if cfg.btc_only else + f'https://{self.api_host}/v1/tickers?limit={self.asset_limit}' if self.asset_limit else + f'https://{self.api_host}/v1/tickers' ) @property def json_fn(self): @@ -352,7 +359,7 @@ def gen_data(data): for d in data['cc']: if d['id'] == 'btc-bitcoin': - btcusd = Decimal(d['price_usd']) + btcusd = Decimal(str(d['quotes']['USD']['price'])) break get_id = src_cls['fi'].get_id @@ -378,13 +385,12 @@ def gen_data(data): if d[k] in wants[k]: if d[k] in found[k]: die(1,dup_sym_errmsg(d[k])) - if not d.get('_converted'): - d['price_usd'] = Decimal(d['price_usd']) - d['price_btc'] = Decimal(d['price_btc']) - d['percent_change_7d'] = Decimal(d['percent_change_7d']) - d['percent_change_24h'] = Decimal(d['percent_change_24h']) - d['last_updated'] = int(d['last_updated']) - d['_converted'] = True + if not 'price_usd' in d: + d['price_usd'] = Decimal(str(d['quotes']['USD']['price'])) + d['price_btc'] = Decimal(str(d['quotes']['USD']['price'])) / btcusd + d['percent_change_24h'] = d['quotes']['USD']['percent_change_24h'] + d['percent_change_7d'] = d['quotes']['USD']['percent_change_7d'] + d['last_updated'] = int(datetime.datetime.fromisoformat(d['last_updated']).timestamp()) yield (d['id'],d) found[k].add(d[k]) wants[k].remove(d[k]) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 13175b8..91bc293 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -25,6 +25,9 @@ opts_data = { 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) +-a, --asset-limit=N Retrieve data for top ‘N’ cryptocurrencies by market + cap (default: {al}). To retrieve all available data, + specify a value of zero. -A, --adjust=P Adjust prices by percentage ‘P’. In ‘trading’ mode, spot and adjusted prices are shown in separate columns. -b, --btc Fetch and display data for Bitcoin only @@ -137,13 +140,14 @@ Financial data is obtained from {fi.desc}, which currently allows Tor. RATE LIMITING NOTE -To protect user privacy, all filtering and processing of cryptocurrency data -is performed client side so that the remote server does not know which assets -are being examined. This means that data for ALL available crypto assets -(currently over 8000) is fetched with each invocation of the script. A rate -limit of {cc.ratelimit} seconds between calls is thus imposed to prevent abuse of the -remote server. When the --btc option is in effect, this limit is reduced to -{cc.btc_ratelimit} seconds. To bypass the rate limit entirely, use --cached-data. +To protect user privacy, filtering and processing of cryptocurrency data is +performed client side so that the remote server does not know which assets +are being examined. This is done by fetching data for the top {al} crypto +assets by market cap (configurable via the --asset-limit option) with each +invocation of the script. A rate limit of {cc.ratelimit} seconds between calls is thus +imposed to prevent abuse of the remote server. When the --btc option is in +effect, this limit is reduced to {cc.btc_ratelimit} seconds. To bypass the rate limit +entirely, use --cached-data. Note that financial data obtained from {fi.api_host} is filtered in the request, which has privacy implications. The rate limit for financial data @@ -201,11 +205,13 @@ To add a portfolio, edit the file 'options': lambda s: s.format( dfl_cachedir = os.path.relpath(dfl_cachedir,start=homedir), ds = fmt_dict(DataSource.get_sources(),fmt='equal_compact'), + al = DataSource.coinpaprika.dfl_asset_limit, ), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), + al = DataSource.coinpaprika.dfl_asset_limit, cc = src_cls['cc'](), fi = src_cls['fi'](), ) diff --git a/setup.cfg b/setup.cfg index 55c315a..0c1e362 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,7 +19,7 @@ classifiers = Operating System :: Microsoft :: Windows [options] -python_requires = >=3.7 +python_requires = >=3.8 include_package_data = True install_requires = diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 966fb91..3a9c47c 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -132,7 +132,7 @@ class CmdTestScripts(CmdTestBase): [], [ 'USD BTC', - 'BTC 23250.77 1.00000000 ETH 1659.66 0.07146397' + 'BTC 23250.77 1.00000000 ETH 1659.66 0.07138094' ]) @@ -144,8 +144,8 @@ class CmdTestScripts(CmdTestBase): r'INR \(INDIAN RUPEE\) = 0.012579 USD', 'USD EURUSD=X INR BTC CHG_7d CHG_24h UPDATED', 'BITCOIN', - r'ETHEREUM 1,659.66 1,559.5846 131,943.14 0.07146397 \+21.42 \+1.82', - r'MONERO 158.97 149.3870 12,638.36 0.00684527 \+7.28 \+1.21 2022-08-02 18:25:59', + r'ETHEREUM 1,659.66 1,559.5846 131,943.14 0.07138094 \+21.42 \+1.82', + r'MONERO 158.97 149.3870 12,638.36 0.00683732 \+7.28 \+1.21 2022-08-02 18:25:59', r'INDIAN RUPEE 0.01 0.0118 1.00 0.00000054 -- --', ]) @@ -156,8 +156,8 @@ class CmdTestScripts(CmdTestBase): [ 'Adjusting prices by -0.77%', 'USD BTC CHG_7d CHG_24h UPDATED', - r'LITECOIN 58.56 0.00252162 \+12.79 \+0.40 2022-08-02 18:25:59', - r'MONERO 157.76 0.00679284 \+7.28 \+1.21' + r'LITECOIN 58.56 0.00251869 \+12.79 \+0.40 2022-08-02 18:25:59', + r'MONERO 157.76 0.00678495 \+7.28 \+1.21' ]) os.unlink(os.path.join(self.nt_datadir,'ticker-cfg.yaml')) return t @@ -174,7 +174,7 @@ class CmdTestScripts(CmdTestBase): ['--wide','--portfolio'], [ 'USD BTC CHG_7d CHG_24h UPDATED', - r'ETHEREUM 1,659.66 0.07146397 \+21.42 \+1.82 2022-08-02 18:25:59', + r'ETHEREUM 1,659.66 0.07138094 \+21.42 \+1.82 2022-08-02 18:25:59', 'CARDANO','ALGORAND', 'PORTFOLIO','BITCOIN','ETHEREUM','MONERO','CARDANO','ALGORAND','TOTAL' ]) diff --git a/test/ref/ticker/ticker-btc.json b/test/ref/ticker/ticker-btc.json index 75c8909..d0549b1 100644 --- a/test/ref/ticker/ticker-btc.json +++ b/test/ref/ticker/ticker-btc.json @@ -1 +1 @@ -{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"23368.859557988893","price_btc":"1","volume_24h_usd":"24116251608.791744","market_cap_usd":"446560795287","circulating_supply":"19109225","total_supply":"19109231","max_supply":"21000000","percent_change_1h":"-0.23","percent_change_24h":"-1.87","percent_change_7d":"6.05","last_updated":"1659346445"} \ No newline at end of file +{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","circulating_supply":"19109225","total_supply":"19109231","max_supply":"21000000","last_updated":"2022-08-01T09:34:05Z","quotes":{"USD":{"price":23368.859557988893,"percent_change_1h":-0.23,"percent_change_6h":-1.4960000000000002,"percent_change_24h":-1.87,"percent_change_7d":6.05,"percent_change_30d":8.469999999999999,"percent_change_1y":10.285,"volume_24h":24116251608.791744,"market_cap":446560795287}}} diff --git a/test/ref/ticker/ticker.json b/test/ref/ticker/ticker.json index 880a666..4535b12 100644 --- a/test/ref/ticker/ticker.json +++ b/test/ref/ticker/ticker.json @@ -1 +1 @@ -[{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","price_usd":"23250.774053363122","price_btc":"1","volume_24h_usd":"28307579008.4866","market_cap_usd":"444336521633","circulating_supply":"19110612","total_supply":"19110619","max_supply":"21000000","percent_change_1h":"-0.27","percent_change_24h":"0.89","percent_change_7d":"11.15","last_updated":"1659464759"},{"id":"eth-ethereum","name":"Ethereum","symbol":"ETH","rank":"2","price_usd":"1659.6621665887371","price_btc":"0.07146396683507168","volume_24h_usd":"18216561308.363518","market_cap_usd":"202151289827","circulating_supply":"121802674","total_supply":"121802721","max_supply":"","percent_change_1h":"-0.03","percent_change_24h":"1.82","percent_change_7d":"21.42","last_updated":"1659464759"},{"id":"ltc-litecoin","name":"Litecoin","symbol":"LTC","rank":"23","price_usd":"59.013589192027936","price_btc":"0.002541086532993605","volume_24h_usd":"510336800.72056556","market_cap_usd":"4181502638","circulating_supply":"70856606","total_supply":"70856631","max_supply":"84000000","percent_change_1h":"-0.43","percent_change_24h":"0.4","percent_change_7d":"12.79","last_updated":"1659464759"},{"id":"xmr-monero","name":"Monero","symbol":"XMR","rank":"30","price_usd":"158.97302629813817","price_btc":"0.006845274482813666","volume_24h_usd":"78159392.30003875","market_cap_usd":"2886277702","circulating_supply":"18155770","total_supply":"18155768","max_supply":"","percent_change_1h":"0.21","percent_change_24h":"1.21","percent_change_7d":"7.28","last_updated":"1659464759"},{"id":"ada-cardano","name":"Cardano","symbol":"ADA","rank":"8","price_usd":"0.5069722536476557","price_btc":"0.00002182989348696495","volume_24h_usd":"507973898.04662097","market_cap_usd":"17111613980","circulating_supply":"33752565071","total_supply":"34277702082","max_supply":"45000000000","percent_change_1h":"0.05","percent_change_24h":"-0.11","percent_change_7d":"11.68","last_updated":"1659464759"},{"id":"algo-algorand","name":"Algorand","symbol":"ALGO","rank":"33","price_usd":"0.33196893931152616","price_btc":"0.00001429436529121747","volume_24h_usd":"62207779.35759487","market_cap_usd":"2306909784","circulating_supply":"6949173585","total_supply":"7350412337","max_supply":"","percent_change_1h":"-0.06","percent_change_24h":"-0.82","percent_change_7d":"9.69","last_updated":"1659464759"}] +[{"id":"btc-bitcoin","name":"Bitcoin","symbol":"BTC","rank":"1","circulating_supply":"19110612","total_supply":"19110619","max_supply":"21000000","last_updated":"2022-08-02T18:25:59Z","quotes":{"USD":{"price":23250.774053363122,"percent_change_1h":-0.27,"percent_change_6h":0.7120000000000001,"percent_change_24h":0.89,"percent_change_7d":11.15,"percent_change_30d":15.61,"percent_change_1y":18.955000000000002,"volume_24h":28307579008.4866,"market_cap":444336521633}}},{"id":"eth-ethereum","name":"Ethereum","symbol":"ETH","rank":"2","circulating_supply":"121802674","total_supply":"121802721","max_supply":"","last_updated":"2022-08-02T18:25:59Z","quotes":{"USD":{"price":1659.6621665887371,"percent_change_1h":-0.03,"percent_change_6h":1.4560000000000002,"percent_change_24h":1.82,"percent_change_7d":21.42,"percent_change_30d":29.988,"percent_change_1y":36.414,"volume_24h":18216561308.363518,"market_cap":202151289827}}},{"id":"ltc-litecoin","name":"Litecoin","symbol":"LTC","rank":"23","circulating_supply":"70856606","total_supply":"70856631","max_supply":"84000000","last_updated":"2022-08-02T18:25:59Z","quotes":{"USD":{"price":59.013589192027936,"percent_change_1h":-0.43,"percent_change_6h":0.32000000000000006,"percent_change_24h":0.4,"percent_change_7d":12.79,"percent_change_30d":17.906,"percent_change_1y":21.743,"volume_24h":510336800.72056556,"market_cap":4181502638}}},{"id":"xmr-monero","name":"Monero","symbol":"XMR","rank":"30","circulating_supply":"18155770","total_supply":"18155768","max_supply":"","last_updated":"2022-08-02T18:25:59Z","quotes":{"USD":{"price":158.97302629813817,"percent_change_1h":0.21,"percent_change_6h":0.968,"percent_change_24h":1.21,"percent_change_7d":7.28,"percent_change_30d":10.192,"percent_change_1y":12.376,"volume_24h":78159392.30003875,"market_cap":2886277702}}},{"id":"ada-cardano","name":"Cardano","symbol":"ADA","rank":"8","circulating_supply":"33752565071","total_supply":"34277702082","max_supply":"45000000000","last_updated":"2022-08-02T18:25:59Z","quotes":{"USD":{"price":0.5069722536476557,"percent_change_1h":0.05,"percent_change_6h":-0.08800000000000001,"percent_change_24h":-0.11,"percent_change_7d":11.68,"percent_change_30d":16.352,"percent_change_1y":19.855999999999998,"volume_24h":507973898.04662097,"market_cap":17111613980}}},{"id":"algo-algorand","name":"Algorand","symbol":"ALGO","rank":"33","circulating_supply":"6949173585","total_supply":"7350412337","max_supply":"","last_updated":"2022-08-02T18:25:59Z","quotes":{"USD":{"price":0.33196893931152616,"percent_change_1h":-0.06,"percent_change_6h":-0.656,"percent_change_24h":-0.82,"percent_change_7d":9.69,"percent_change_30d":13.565999999999999,"percent_change_1y":16.473,"volume_24h":62207779.35759487,"market_cap":2306909784}}}] From 545bc044c6e89d48e814cb64e8649ffbba637d85 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 5 Nov 2023 13:40:23 +0000 Subject: [PATCH 105/175] mmnode-ticker: add year and month percentage change columns --- mmgen_node_tools/Ticker.py | 42 +++++++++++++++++++++++++++------ mmgen_node_tools/main_ticker.py | 21 ++++++++++++----- test/cmdtest_py_d/ct_misc.py | 11 +++++---- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 40bede8..4011c90 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -34,6 +34,13 @@ portfolio_fn = 'ticker-portfolio.yaml' asset_tuple = namedtuple('asset_tuple',['symbol','id','source']) last_api_host = None +percent_cols = { + 'd': 'day', + 'w': 'week', + 'm': 'month', + 'y': 'year', +} + class DataSource: source_groups = [ @@ -390,6 +397,8 @@ def gen_data(data): d['price_btc'] = Decimal(str(d['quotes']['USD']['price'])) / btcusd d['percent_change_24h'] = d['quotes']['USD']['percent_change_24h'] d['percent_change_7d'] = d['quotes']['USD']['percent_change_7d'] + d['percent_change_30d'] = d['quotes']['USD']['percent_change_30d'] + d['percent_change_1y'] = d['quotes']['USD']['percent_change_1y'] d['last_updated'] = int(datetime.datetime.fromisoformat(d['last_updated']).timestamp()) yield (d['id'],d) found[k].add(d[k]) @@ -497,6 +506,15 @@ def make_cfg(gcfg_arg): yield parse_asset_id(e,require_label=True) return tuple(gen()) + def parse_percent_cols(arg): + if arg is None: + return [] + res = arg.lower().split(',') + for s in res: + if s not in percent_cols: + die(1,f'{arg!r}: invalid --percent-cols parameter (valid letters: {fmt_list(percent_cols)})') + return res + def parse_usr_asset_arg(key,use_cf_file=False): """ asset_id[:rate[:rate_asset]] @@ -606,7 +624,8 @@ def make_cfg(gcfg_arg): 'cachedir', 'proxy', 'proxy2', - 'portfolio' ]) + 'portfolio', + 'percent_cols' ]) global gcfg,cfg_in,src_cls,cfg @@ -645,7 +664,8 @@ def make_cfg(gcfg_arg): cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir, proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), - portfolio = get_portfolio() if cfg_in.portfolio and gcfg.portfolio and not query else None + portfolio = get_portfolio() if cfg_in.portfolio and gcfg.portfolio and not query else None, + percent_cols = parse_percent_cols(gcfg.percent_cols) ) def get_cfg_in(): @@ -802,7 +822,7 @@ class Ticker: yield '-' * self.hl_wid if not cfg.btc_only: yield self.fs_num.format( - lbl = 'TOTAL', pc1='', pc2='', upd='', amt='', + lbl = 'TOTAL', pc3='', pc4='', pc1='', pc2='', upd='', amt='', **{ k.replace('-','_'): v for k,v in self.prices['total'].items() } ) @@ -851,6 +871,8 @@ class Ticker: lbl = (self.create_label(d['id']) if gcfg.name_labels else d['symbol']), pc1 = fmt_pct(d.get('percent_change_7d')), pc2 = fmt_pct(d.get('percent_change_24h')), + pc3 = fmt_pct(d.get('percent_change_1y')), + pc4 = fmt_pct(d.get('percent_change_30d')), upd = d.get('last_updated_fmt'), amt = amt_fmt, **{ k.replace('-','_'): v * (1 if amt is None else amt) for k,v in p.items() } @@ -873,8 +895,10 @@ class Ticker: col_fs_data = { 'label': fd(f'{{lbl:{self.col1_wid}}}',f'{{lbl:{self.col1_wid}}}',self.col1_wid), - 'pct7d': fd(' {pc1:7}', ' {pc1:7}', 8), - 'pct24h': fd(' {pc2:7}', ' {pc2:7}', 8), + 'pct1y': fd(' {pc3:7}', ' {pc3:7}', 8), + 'pct1m': fd(' {pc4:7}', ' {pc4:7}', 8), + 'pct1w': fd(' {pc1:7}', ' {pc1:7}', 8), + 'pct1d': fd(' {pc2:7}', ' {pc2:7}', 8), 'update_time': fd(' {upd}', ' {upd}', max((19 if cfg.portfolio else 0),self.upd_w) + 2), 'amt': fd(' {amt}', ' {amt}', 21), } @@ -891,8 +915,10 @@ class Ticker: [asset.id for asset in self.usr_col_assets] + [a for a,b in ( ( 'btc-bitcoin', not cfg.btc_only ), - ( 'pct7d', gcfg.percent_change ), - ( 'pct24h', gcfg.percent_change ), + ( 'pct1y', 'y' in cfg.percent_cols ), + ( 'pct1m', 'm' in cfg.percent_cols ), + ( 'pct1w', 'w' in cfg.percent_cols ), + ( 'pct1d', 'd' in cfg.percent_cols ), ( 'update_time', gcfg.update_time ), ) if b] ) @@ -915,6 +941,8 @@ class Ticker: lbl = '', pc1 = ' CHG_7d', pc2 = 'CHG_24h', + pc3 = 'CHG_1y', + pc4 = 'CHG_30d', upd = 'UPDATED', amt = ' AMOUNT', usd_us_dollar = 'USD', diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 91bc293..eafa34a 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -14,7 +14,11 @@ mmnode-ticker: Display price information for cryptocurrency and other assets opts_data = { 'sets': [ - ('wide', True, 'percent_change', True), + ('widest', True, 'percent_cols', 'd,w,m,y'), + ('widest', True, 'name_labels', True), + ('widest', True, 'thousands_comma', True), + ('widest', True, 'update_time', True), + ('wide', True, 'percent_cols', 'd,w'), ('wide', True, 'name_labels', True), ('wide', True, 'thousands_comma', True), ('wide', True, 'update_time', True), @@ -45,7 +49,9 @@ opts_data = { -F, --portfolio Display portfolio data -l, --list-ids List IDs of all available assets -n, --name-labels Label rows with asset names rather than symbols --p, --percent-change Add percentage change columns +-p, --percent-cols=C Add daily, weekly, monthly, or yearly percentage change + columns ‘C’ (specify with comma-separated letters + {pc}) -P, --pager Pipe the output to a pager -r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, see ASSET SPECIFIERS below). Can also be used to supply @@ -54,7 +60,8 @@ opts_data = { -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column -v, --verbose Be more verbose --w, --wide Display all optional columns (equivalent to -punT) +-w, --wide Display most optional columns (same as -unT -p d,w) +-W, --widest Display all optional columns (same as -unT -p d,w,m,y) -x, --proxy=P Connect via proxy ‘P’. Set to the empty string to completely disable or ‘none’ to allow override from environment. Consult the curl manpage for --proxy usage. @@ -169,9 +176,10 @@ $ mmnode-ticker -w -c eurusd=x,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 # Wide display, elapsed update time, add EUR, BGN columns and BGN/EUR rate: $ mmnode-ticker -wE -c eurusd=x,bgn-bulgarian-lev:0.5113r:eurusd=x -# Wide display, use cached data from previous network query, show portfolio -# (see above), pipe output to pager, add DOGE row: -$ mmnode-ticker -wCFP -r doge +# Widest display with all percentage change columns, use cached data from +# previous network query, show portfolio (see above), pipe output to pager, +# add DOGE row: +$ mmnode-ticker -WCFP -r doge # Display 17.234 XMR priced in all configured assets (‘trading’ mode): $ mmnode-ticker xmr:17.234 @@ -206,6 +214,7 @@ To add a portfolio, edit the file dfl_cachedir = os.path.relpath(dfl_cachedir,start=homedir), ds = fmt_dict(DataSource.get_sources(),fmt='equal_compact'), al = DataSource.coinpaprika.dfl_asset_limit, + pc = fmt_list(Ticker.percent_cols,fmt='bare'), ), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 3a9c47c..8046e51 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -138,15 +138,16 @@ class CmdTestScripts(CmdTestBase): def ticker4(self): return self.ticker( - ['--wide','--add-columns=eurusd=x,inr-indian-rupee:79.5'], + ['--widest','--add-columns=eurusd=x,inr-indian-rupee:79.5'], [ r'EURUSD=X \(EUR/USD\) = 1.0642 USD ' + r'INR \(INDIAN RUPEE\) = 0.012579 USD', - 'USD EURUSD=X INR BTC CHG_7d CHG_24h UPDATED', + 'USD EURUSD=X INR BTC CHG_1y CHG_30d CHG_7d CHG_24h UPDATED', 'BITCOIN', - r'ETHEREUM 1,659.66 1,559.5846 131,943.14 0.07138094 \+21.42 \+1.82', - r'MONERO 158.97 149.3870 12,638.36 0.00683732 \+7.28 \+1.21 2022-08-02 18:25:59', - r'INDIAN RUPEE 0.01 0.0118 1.00 0.00000054 -- --', + r'ETHEREUM 1,659.66 1,559.5846 131,943.14 0.07138094 \+36.41 \+29.99 \+21.42 \+1.82', + r'MONERO 158.97 149.3870 12,638.36 0.00683732 \+12.38 \+10.19 \+7.28 \+1.21 2022-08-02 18:25:59', + r'S&P 500 4,320.06 4,059.5604 343,444.77 0.18580285 -- -- -- -0.23', + r'INDIAN RUPEE 0.01 0.0118 1.00 0.00000054 -- -- -- --', ]) def ticker5(self): From af7c14fe369fc37d4d465e9a4bcb9f35b1952173 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 5 Nov 2023 13:40:23 +0000 Subject: [PATCH 106/175] mmnode-ticker: get historical financial data from Yahoo Finance --- mmgen_node_tools/Ticker.py | 43 ++++++++++++++++++++- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/cmdtest_py_d/ct_misc.py | 3 +- test/ref/ticker/ticker-finance-history.json | 1 + 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 test/ref/ticker/ticker-finance-history.json diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 4011c90..6c15906 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -48,6 +48,7 @@ class DataSource: 'cc': 'coinpaprika' }, { 'fi': 'yahoospot', + 'hi': 'yahoohist', } ] @@ -248,7 +249,9 @@ class DataSource: 'symbol': sym.upper(), 'price_usd': price_usd, 'price_btc': price_usd / btcusd, - 'percent_change_7d': None, + 'percent_change_1y': data['pct_chg_1y'], + 'percent_change_30d': data['pct_chg_4wks'], + 'percent_change_7d': data['pct_chg_1wk'], 'percent_change_24h': data['regularMarketChangePercent']['raw'] * 100, 'last_updated': data['regularMarketTime'], } @@ -272,6 +275,7 @@ class DataSource: kwargs = { 'formatted': True, + 'asynchronous': True, 'proxies': { 'https': cfg.proxy2 }, } @@ -297,6 +301,33 @@ class DataSource: id = s.lower(), source = 'fi' ) + class yahoohist(yahoospot): + + json_fn_basename = 'ticker-finance-history.json' + data_desc = 'historical financial data' + net_data_type = 'json' + period = '1y' + interval = '1wk' + + def process_network_data(self,ticker): + return ticker.history( + period = self.period, + interval = self.interval).to_json(orient='index') + + def postprocess_data(self,data): + def gen(): + keys = set() + for key,val in data.items(): + if m := re.match(r"\('(.*?)', datetime\.date\((.*)\)\)$",key): + date = '{}-{:>02}-{:>02}'.format(*m[2].split(', ')) + if (sym := m[1]) in keys: + d[date] = val + else: + keys.add(sym) + d = {date:val} + yield (sym,d) + return dict(gen()) + def assets_list_gen(cfg_in): for k,v in cfg_in.cfg['assets'].items(): yield '' @@ -378,6 +409,16 @@ def gen_data(data): if id in wants['id']: if id in found['id']: die(1,dup_sym_errmsg(id)) + if m := data['hi'].get(k): + spot = v['regularMarketPrice']['raw'] + hist = tuple(m.values()) + v['pct_chg_1wk'], v['pct_chg_4wks'], v['pct_chg_1y'] = ( + (spot / hist[-2]['close'] - 1) * 100, + (spot / hist[-5]['close'] - 1) * 100, # 4 weeks ≈ 1 month + (spot / hist[0]['close'] - 1) * 100, + ) + else: + v['pct_chg_1wk'] = v['pct_chg_4wks'] = v['pct_chg_1y'] = None yield ( id, conv_func(id,v,btcusd) ) found['id'].add(id) wants['id'].remove(id) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 5223fbd..ce7b703 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev3 +3.2.dev4 diff --git a/setup.cfg b/setup.cfg index 0c1e362..339e7f7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.8 include_package_data = True install_requires = - mmgen>=14.0.dev9 + mmgen>=14.0.dev11 pyyaml yahooquery diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 8046e51..3dbac50 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -102,6 +102,7 @@ class CmdTestScripts(CmdTestBase): self.spawn('',msg_only=True) shutil.copy2(os.path.join(refdir,'ticker.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-finance.json'),self.tmpdir) + shutil.copy2(os.path.join(refdir,'ticker-finance-history.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) return 'ok' @@ -146,7 +147,7 @@ class CmdTestScripts(CmdTestBase): 'BITCOIN', r'ETHEREUM 1,659.66 1,559.5846 131,943.14 0.07138094 \+36.41 \+29.99 \+21.42 \+1.82', r'MONERO 158.97 149.3870 12,638.36 0.00683732 \+12.38 \+10.19 \+7.28 \+1.21 2022-08-02 18:25:59', - r'S&P 500 4,320.06 4,059.5604 343,444.77 0.18580285 -- -- -- -0.23', + r'S&P 500 4,320.06 4,059.5604 343,444.77 0.18580285 -1.71 \+12.93 \+9.05 -0.23', r'INDIAN RUPEE 0.01 0.0118 1.00 0.00000054 -- -- -- --', ]) diff --git a/test/ref/ticker/ticker-finance-history.json b/test/ref/ticker/ticker-finance-history.json new file mode 100644 index 0000000..e8b0bd3 --- /dev/null +++ b/test/ref/ticker/ticker-finance-history.json @@ -0,0 +1 @@ +{"('BZ=F', datetime.date(2021, 8, 2))":{"open":75.1800003052,"high":75.3300018311,"low":69.75,"close":70.6999969482,"volume":192386,"adjclose":70.6999969482},"('BZ=F', datetime.date(2022, 6, 27))":{"open":112.4599990845,"high":120.3799972534,"low":108.0100021362,"close":111.6299972534,"volume":64094,"adjclose":111.6299972534},"('BZ=F', datetime.date(2022, 7, 4))":{"open":111.6100006104,"high":114.6900024414,"low":98.4700012207,"close":107.0199966431,"volume":115623,"adjclose":107.0199966431},"('BZ=F', datetime.date(2022, 7, 11))":{"open":106.7600021362,"high":107.6600036621,"low":94.5,"close":101.1600036621,"volume":109537,"adjclose":101.1600036621},"('BZ=F', datetime.date(2022, 7, 18))":{"open":100.9199981689,"high":107.5999984741,"low":99.4800033569,"close":103.1999969482,"volume":110290,"adjclose":103.1999969482},"('BZ=F', datetime.date(2022, 7, 25))":{"open":103.4400024414,"high":110.4300003052,"low":101.6699981689,"close":110.0100021362,"volume":77572,"adjclose":110.0100021362},"('BZ=F', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":102.4400024414,"high":111.4300003052,"low":101.6699981689,"close":111.0100021362,"volume":77572,"adjclose":111.0100021362},"('CHFUSD=X', datetime.date(2021, 7, 26))":{"open":1.0939244032,"high":1.1064150333,"low":1.0908932686,"close":1.1047636271,"volume":0,"adjclose":1.1047636271},"('CHFUSD=X', datetime.date(2022, 7, 4))":{"open":1.043405652,"high":1.0446264744,"low":1.020783186,"close":1.0241703987,"volume":0,"adjclose":1.0241703987},"('CHFUSD=X', datetime.date(2022, 7, 11))":{"open":1.022777319,"high":1.0247056484,"low":1.0118077993,"close":1.0238809586,"volume":0,"adjclose":1.0238809586},"('CHFUSD=X', datetime.date(2022, 7, 18))":{"open":1.0250102282,"high":1.0417101383,"low":1.0216904879,"close":1.0396090746,"volume":0,"adjclose":1.0396090746},"('CHFUSD=X', datetime.date(2022, 7, 25))":{"open":1.0384216309,"high":1.0522992611,"low":1.0345113277,"close":1.0506275892,"volume":0,"adjclose":1.0506275892},"('CHFUSD=X', datetime.date(2022, 8, 1))":{"open":1.0492409468,"high":1.0547411442,"low":1.0495601892,"close":1.0492409468,"volume":0,"adjclose":1.0492409468},"('CHFUSD=X', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":1.0492409468,"high":1.0547411442,"low":1.0495601892,"close":1.0492409468,"volume":0,"adjclose":1.0492409468},"('EURUSD=X', datetime.date(2021, 7, 26))":{"open":1.1822398901,"high":1.1911001205,"low":1.1785781384,"close":1.1867604256,"volume":0,"adjclose":1.1867604256},"('EURUSD=X', datetime.date(2022, 7, 4))":{"open":1.043394804,"high":1.0463534594,"low":1.0079730749,"close":1.0187449455,"volume":0,"adjclose":1.0187449455},"('EURUSD=X', datetime.date(2022, 7, 11))":{"open":1.0166114569,"high":1.0167768002,"low":0.9953616261,"close":1.008867979,"volume":0,"adjclose":1.008867979},"('EURUSD=X', datetime.date(2022, 7, 18))":{"open":1.0096318722,"high":1.0275379419,"low":1.0082373619,"close":1.0215548277,"volume":0,"adjclose":1.0215548277},"('EURUSD=X', datetime.date(2022, 7, 25))":{"open":1.0200231075,"high":1.0256409645,"low":1.0107442141,"close":1.0227040052,"volume":0,"adjclose":1.0227040052},"('EURUSD=X', datetime.date(2022, 8, 1))":{"open":1.02082479,"high":1.0277491808,"low":1.020960331,"close":1.02082479,"volume":0,"adjclose":1.02082479},"('EURUSD=X', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":1.02082479,"high":1.0277491808,"low":1.020960331,"close":1.02082479,"volume":0,"adjclose":1.02082479},"('GBPUSD=X', datetime.date(2021, 7, 26))":{"open":1.3886767626,"high":1.3984057903,"low":1.3846387863,"close":1.3910005093,"volume":0,"adjclose":1.3910005093},"('GBPUSD=X', datetime.date(2022, 7, 4))":{"open":1.2105804682,"high":1.2165154219,"low":1.1877613068,"close":1.2031002045,"volume":0,"adjclose":1.2031002045},"('GBPUSD=X', datetime.date(2022, 7, 11))":{"open":1.2017786503,"high":1.2019952536,"low":1.1763184071,"close":1.1855996847,"volume":0,"adjclose":1.1855996847},"('GBPUSD=X', datetime.date(2022, 7, 18))":{"open":1.1887921095,"high":1.2064181566,"low":1.1875497103,"close":1.2005999088,"volume":0,"adjclose":1.2005999088},"('GBPUSD=X', datetime.date(2022, 7, 25))":{"open":1.1984229088,"high":1.2242598534,"low":1.1962151527,"close":1.2180001736,"volume":0,"adjclose":1.2180001736},"('GBPUSD=X', datetime.date(2022, 8, 1))":{"open":1.2167818546,"high":1.2291958332,"low":1.2164857388,"close":1.2167373896,"volume":0,"adjclose":1.2167373896},"('GBPUSD=X', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":1.2167818546,"high":1.2291958332,"low":1.2164857388,"close":1.2167373896,"volume":0,"adjclose":1.2167373896},"('GC=F', datetime.date(2021, 7, 26))":{"open":1803.6999511719,"high":1832.5999755859,"low":1799.5,"close":1812.5999755859,"volume":243380,"adjclose":1812.5999755859},"('GC=F', datetime.date(2022, 6, 27))":{"open":1830.5,"high":1830.6999511719,"low":1791.5999755859,"close":1798.9000244141,"volume":1046,"adjclose":1798.9000244141},"('GC=F', datetime.date(2022, 7, 4))":{"open":1805.4000244141,"high":1805.4000244141,"low":1732.0999755859,"close":1740.5999755859,"volume":4032,"adjclose":1740.5999755859},"('GC=F', datetime.date(2022, 7, 11))":{"open":1732.5,"high":1736.6999511719,"low":1701.0999755859,"close":1702.4000244141,"volume":2904,"adjclose":1702.4000244141},"('GC=F', datetime.date(2022, 7, 18))":{"open":1712.1999511719,"high":1735,"low":1679.8000488281,"close":1727.0999755859,"volume":2322,"adjclose":1727.0999755859},"('GC=F', datetime.date(2022, 7, 25))":{"open":1727,"high":1765.6999511719,"low":1717.6999511719,"close":1762.9000244141,"volume":185654,"adjclose":1762.9000244141},"('GC=F', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":1727,"high":1765.6999511719,"low":1717.6999511719,"close":1762.9000244141,"volume":185654,"adjclose":1762.9000244141},"('SI=F', datetime.date(2021, 7, 26))":{"open":25.2000007629,"high":25.8549995422,"low":24.6319999695,"close":25.5279998779,"volume":1000,"adjclose":25.5279998779},"('SI=F', datetime.date(2022, 6, 27))":{"open":21.2950000763,"high":21.3299999237,"low":19.2649993896,"close":19.5970001221,"volume":48988,"adjclose":19.5970001221},"('SI=F', datetime.date(2022, 7, 4))":{"open":19.8099994659,"high":20.0699996948,"low":18.8400001526,"close":19.1669998169,"volume":566,"adjclose":19.1669998169},"('SI=F', datetime.date(2022, 7, 11))":{"open":19.1650009155,"high":19.1749992371,"low":18,"close":18.5480003357,"volume":1108,"adjclose":18.5480003357},"('SI=F', datetime.date(2022, 7, 18))":{"open":18.8099994659,"high":18.9400005341,"low":18.1100006104,"close":18.5849990845,"volume":690,"adjclose":18.5849990845},"('SI=F', datetime.date(2022, 7, 25))":{"open":18.4099998474,"high":20.2900009155,"low":18.1849994659,"close":20.1560001373,"volume":980,"adjclose":20.1560001373},"('SI=F', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":18.4099998474,"high":20.2900009155,"low":18.1849994659,"close":20.1560001373,"volume":980,"adjclose":20.1560001373},"('^DJI', datetime.date(2021, 7, 26))":{"open":35078.8984375,"high":35171.51953125,"low":34871.12890625,"close":34935.46875,"volume":1172870000,"adjclose":34935.46875},"('^DJI', datetime.date(2022, 6, 27))":{"open":31533.599609375,"high":31885.08984375,"low":30431.869140625,"close":31097.259765625,"volume":1634590000,"adjclose":31097.259765625},"('^DJI', datetime.date(2022, 7, 4))":{"open":30903.119140625,"high":31511.4609375,"low":30355.119140625,"close":31338.150390625,"volume":1147620000,"adjclose":31338.150390625},"('^DJI', datetime.date(2022, 7, 11))":{"open":31277.98046875,"high":31367.55078125,"low":30143.9296875,"close":31288.259765625,"volume":1507740000,"adjclose":31288.259765625},"('^DJI', datetime.date(2022, 7, 18))":{"open":31475.98046875,"high":32219.25,"low":30982.970703125,"close":31899.2890625,"volume":1624400000,"adjclose":31899.2890625},"('^DJI', datetime.date(2022, 7, 25))":{"open":31950.9296875,"high":32910.1796875,"low":31705.359375,"close":32845.12890625,"volume":1764330000,"adjclose":32845.12890625},"('^DJI', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":31950.9296875,"high":32910.1796875,"low":31705.359375,"close":32845.12890625,"volume":1764330000,"adjclose":32845.12890625},"('^GSPC', datetime.date(2021, 7, 26))":{"open":4416.3798828125,"high":4429.9702148438,"low":4372.509765625,"close":4395.259765625,"volume":16458580000,"adjclose":4395.259765625},"('^GSPC', datetime.date(2022, 6, 27))":{"open":3920.7600097656,"high":3945.8601074219,"low":3738.669921875,"close":3825.330078125,"volume":21693690000,"adjclose":3825.330078125},"('^GSPC', datetime.date(2022, 7, 4))":{"open":3792.6101074219,"high":3918.5,"low":3742.0600585938,"close":3899.3798828125,"volume":17073700000,"adjclose":3899.3798828125},"('^GSPC', datetime.date(2022, 7, 11))":{"open":3880.9399414062,"high":3880.9399414062,"low":3721.5600585938,"close":3863.1599121094,"volume":19693570000,"adjclose":3863.1599121094},"('^GSPC', datetime.date(2022, 7, 18))":{"open":3883.7900390625,"high":4012.4399414062,"low":3818.6298828125,"close":3961.6298828125,"volume":20385270000,"adjclose":3961.6298828125},"('^GSPC', datetime.date(2022, 7, 25))":{"open":3965.7199707031,"high":4140.1499023438,"low":3910.7399902344,"close":4130.2900390625,"volume":20488830000,"adjclose":4130.2900390625},"('^GSPC', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":3965.7199707031,"high":4140.1499023438,"low":3910.7399902344,"close":4130.2900390625,"volume":20488830000,"adjclose":4130.2900390625},"('^IXIC', datetime.date(2021, 7, 26))":{"open":14807.9501953125,"high":14833.740234375,"low":14503.759765625,"close":14672.6796875,"volume":16171130000,"adjclose":14672.6796875},"('^IXIC', datetime.date(2022, 6, 27))":{"open":11661.01953125,"high":11677.490234375,"low":10850.009765625,"close":11127.849609375,"volume":26652190000,"adjclose":11127.849609375},"('^IXIC', datetime.date(2022, 7, 4))":{"open":10964.1796875,"high":11689.7001953125,"low":10911.4501953125,"close":11635.3095703125,"volume":19116360000,"adjclose":11635.3095703125},"('^IXIC', datetime.date(2022, 7, 11))":{"open":11524.490234375,"high":11541.099609375,"low":11005.9296875,"close":11452.419921875,"volume":21963960000,"adjclose":11452.419921875},"('^IXIC', datetime.date(2022, 7, 18))":{"open":11561.6396484375,"high":12093.01953125,"low":11322.83984375,"close":11834.1103515625,"volume":25230550000,"adjclose":11834.1103515625},"('^IXIC', datetime.date(2022, 7, 25))":{"open":11837.9599609375,"high":12426.259765625,"low":11533.3701171875,"close":12390.6904296875,"volume":23117120000,"adjclose":12390.6904296875},"('^IXIC', datetime.datetime(2022, 8, 2, 7, 7, 7, tzinfo=))":{"open":11837.9599609375,"high":12426.259765625,"low":11533.3701171875,"close":12390.6904296875,"volume":23117120000,"adjclose":12390.6904296875}} From 1215085fbd78e89bc3786c6af29f996a5fa4b220 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 8 Nov 2023 08:02:05 +0000 Subject: [PATCH 107/175] setup.cfg: update dependency to `mmgen-wallet` --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index ce7b703..67288ca 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev4 +3.2.dev5 diff --git a/setup.cfg b/setup.cfg index 339e7f7..3cb1afb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ python_requires = >=3.8 include_package_data = True install_requires = - mmgen>=14.0.dev11 + mmgen-wallet>=14.0.dev12 pyyaml yahooquery From b2dc247429927471aa3d402894c2bd692a027684 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 8 Nov 2023 10:31:42 +0000 Subject: [PATCH 108/175] update package name, install instructions in README --- README.md | 8 +++++++- setup.cfg | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 673155e..96807ad 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,13 @@ Currently tested on Linux only. Some scripts may not work under Windows/MSYS2. ## Install: -First, install [MMGen][6]. +### Stable version: + + $ python3 -m pip install --user --upgrade mmgen-node-tools + +### Development version: + +First, install the latest development version of [MMGen][6]. Then, diff --git a/setup.cfg b/setup.cfg index 3cb1afb..ae7676f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = MMGen Node Tools +name = mmgen-node-tools version = file: mmgen_node_tools/data/version description = Optional online tools for the MMGen wallet suite long_description = file: README.md From 554f78690384cf297a9770036a87a701d9e55766 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 9 Nov 2023 05:19:01 +0000 Subject: [PATCH 109/175] update install instructions in README.md --- README.md | 13 +++++++++---- mmgen_node_tools/data/version | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 96807ad..c7ecc29 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,15 @@ Currently tested on Linux only. Some scripts may not work under Windows/MSYS2. ## Install: +If installing as user (without venv), Make sure that `~/.local/bin` is in `PATH`. + ### Stable version: - $ python3 -m pip install --user --upgrade mmgen-node-tools +First, install the [required MMGen packages][7] for your Linux distribution: + +Then, + + $ python3 -m pip install --upgrade mmgen-node-tools ### Development version: @@ -21,9 +27,7 @@ Then, $ git clone https://github.com/mmgen/mmgen-node-tools $ cd mmgen-node-tools $ python3 -m build --no-isolation - $ python3 -m pip install --user dist/*.whl - -Also make sure that `~/.local/bin` is in `PATH`. + $ python3 -m pip install dist/*.whl ## Test: @@ -52,3 +56,4 @@ Donate: 15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w [4]: https://bitcointalk.org/index.php?topic=567069.0 [5]: https://github.com/mmgen/mmgen/wiki/MMGen-Signing-Keys [6]: https://github.com/mmgen/mmgen/ +[7]: https://github.com/mmgen/mmgen/wiki/Install-MMGen-on-Linux diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 67288ca..5d6bce8 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev5 +3.2.dev6 From 56327412ca910fd44ba278e9292c31c8160dda36 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 20 Nov 2023 14:45:15 +0000 Subject: [PATCH 110/175] update for mmgen-wallet v14.0.dev22 --- README.md | 14 +++++++------- mmgen_node_tools/Misc.py | 4 ++-- mmgen_node_tools/PeerBlocks.py | 4 ++-- mmgen_node_tools/PollDisplay.py | 4 ++-- mmgen_node_tools/Ticker.py | 4 ++-- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 4 ++-- mmgen_node_tools/main_ticker.py | 4 ++-- setup.cfg | 7 ++++++- test/cmdtest_py_d/ct_main.py | 4 ++-- test/init.sh | 12 ++++++------ .../fakemods/mmgen_node_tools/PeerBlocks.py | 4 ++-- 12 files changed, 36 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index c7ecc29..c95e206 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ Currently tested on Linux only. Some scripts may not work under Windows/MSYS2. ## Install: -If installing as user (without venv), Make sure that `~/.local/bin` is in `PATH`. +If installing as user (without venv), make sure that `~/.local/bin` is in `PATH`. ### Stable version: -First, install the [required MMGen packages][7] for your Linux distribution: +First, install the [required MMGen Wallet packages][7] for your Linux distribution: Then, @@ -20,7 +20,7 @@ Then, ### Development version: -First, install the latest development version of [MMGen][6]. +First, install the latest development version of [MMGen Wallet][6]. Then, @@ -31,7 +31,7 @@ Then, ## Test: -*NOTE: the tests require that the MMGen and MMGen Node Tools repositories be +*NOTE: the tests require that the MMGen Wallet and MMGen Node Tools repositories be located in the same directory.* Initialize the test framework (must be run at least once after cloning, and @@ -54,6 +54,6 @@ Full testing: Donate: 15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w [4]: https://bitcointalk.org/index.php?topic=567069.0 -[5]: https://github.com/mmgen/mmgen/wiki/MMGen-Signing-Keys -[6]: https://github.com/mmgen/mmgen/ -[7]: https://github.com/mmgen/mmgen/wiki/Install-MMGen-on-Linux +[5]: https://github.com/mmgen/mmgen-wallet/wiki/MMGen-Signing-Keys +[6]: https://github.com/mmgen/mmgen-wallet/ +[7]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-on-Linux diff --git a/mmgen_node_tools/Misc.py b/mmgen_node_tools/Misc.py index 999b68b..5aa27f5 100755 --- a/mmgen_node_tools/Misc.py +++ b/mmgen_node_tools/Misc.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools -# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools +# https://github.com/mmgen/mmgen-wallet https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-wallet https://gitlab.com/mmgen/mmgen-node-tools """ mmgen_node_tools.Misc: miscellaneous data and functions for the MMGen Node Tools suite diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index 0b386fb..c3d7c68 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen -# https://gitlab.com/mmgen/mmgen +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet """ mmgen_node_tools.PeerBlocks: List blocks in flight, disconnect stalling nodes diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py index 5bbfecd..72bedac 100755 --- a/mmgen_node_tools/PollDisplay.py +++ b/mmgen_node_tools/PollDisplay.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen -# https://gitlab.com/mmgen/mmgen +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet """ mmgen_node_tools.PollDisplay: update and display RPC data; get input from user diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 6c15906..5a6a6a2 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools -# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools +# https://github.com/mmgen/mmgen-wallet https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-wallet https://gitlab.com/mmgen/mmgen-node-tools """ mmgen_node_tools.Ticker: Display price information for cryptocurrency and other assets diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 5d6bce8..2c19032 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev6 +3.2.dev7 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 34c6236..7af2b77 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools -# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools +# https://github.com/mmgen/mmgen-wallet https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-wallet https://gitlab.com/mmgen/mmgen-node-tools """ mmnode-addrbal: Get balances for arbitrary addresses in the blockchain diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index eafa34a..f758089 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen https://github.com/mmgen/mmgen-node-tools -# https://gitlab.com/mmgen/mmgen https://gitlab.com/mmgen/mmgen-node-tools +# https://github.com/mmgen/mmgen-wallet https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-wallet https://gitlab.com/mmgen/mmgen-node-tools """ mmnode-ticker: Display price information for cryptocurrency and other assets diff --git a/setup.cfg b/setup.cfg index ae7676f..4cb6a92 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,19 +11,24 @@ license = GNU GPL v3 platforms = Linux, Armbian, Raspbian, MS Windows keywords = file: mmgen_node_tools/data/keywords project_urls = + Website = https://mmgen.org Bug Tracker = https://github.com/mmgen/mmgen-node-tools/issues classifiers = Programming Language :: Python :: 3 License :: OSI Approved :: GNU General Public License v3 (GPLv3) Operating System :: POSIX :: Linux Operating System :: Microsoft :: Windows + Environment :: Console + Topic :: Office/Business :: Financial + Topic :: Security :: Cryptography + Development Status :: 5 - Production/Stable [options] python_requires = >=3.8 include_package_data = True install_requires = - mmgen-wallet>=14.0.dev12 + mmgen-wallet>=14.0.dev22 pyyaml yahooquery diff --git a/test/cmdtest_py_d/ct_main.py b/test/cmdtest_py_d/ct_main.py index ad1ce41..5f5c691 100755 --- a/test/cmdtest_py_d/ct_main.py +++ b/test/cmdtest_py_d/ct_main.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen -# https://gitlab.com/mmgen/mmgen +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet """ cmdtest_py_d.ct_main: Basic operations tests for the cmdtest.py test suite diff --git a/test/init.sh b/test/init.sh index 913ac9c..769490a 100755 --- a/test/init.sh +++ b/test/init.sh @@ -31,22 +31,22 @@ done shift $((OPTIND-1)) -mm_repo='../mmgen' +wallet_repo='../mmgen-wallet' die() { echo -e ${YELLOW}ERROR: $1$RESET; false; } becho() { echo -e $BLUE$1$RESET; } check_mmgen_repo() { - ( cd $mm_repo; python3 ./setup.py --url | grep -iq 'mmgen' ) + ( cd $wallet_repo; python3 ./setup.py --url | grep -iq 'mmgen' ) } build_mmgen_extmod() { - ( cd $mm_repo; python3 ./setup.py build_ext --inplace ) + ( cd $wallet_repo; python3 ./setup.py build_ext --inplace ) } create_dir_links() { for link_name in 'mmgen' 'scripts'; do - target="$mm_repo/$link_name" + target="$wallet_repo/$link_name" if [ -e $link_name ]; then [ $(realpath --relative-to=. $link_name) == $target ] || die "'$link_name' does not point to '$target'" else @@ -73,7 +73,7 @@ create_test_links() { [ "$path" ] || continue pfx=$(echo $path | sed -r 's/[^/]//g' | sed 's/\//..\//g') symlink_arg=$(if [ $type == 'symbolic' ]; then echo --symbolic; fi) - target="$mm_repo/$path" + target="$wallet_repo/$path" if [ ! -e "$target" ]; then echo "Target path $target is missing! Cannot proceed" exit 1 @@ -101,7 +101,7 @@ set -e becho 'Initializing MMGen Node Tools Test Suite' -check_mmgen_repo || die "MMGen repository not found at $mm_repo!" +check_mmgen_repo || die "MMGen Wallet repository not found at $wallet_repo!" build_mmgen_extmod diff --git a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py index 2484628..7c2fdf3 100644 --- a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py +++ b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py @@ -5,8 +5,8 @@ # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: -# https://github.com/mmgen/mmgen -# https://gitlab.com/mmgen/mmgen +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet """ fakemods.mmgen_node_tools.PeerBlocks: List blocks in flight, disconnect stalling nodes - test data From d0874f9a2d6475d81e50f6809aca6a05c4dced88 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 21 Nov 2023 15:30:42 +0000 Subject: [PATCH 111/175] test/init.sh: handle broken dir symlinks --- mmgen_node_tools/data/version | 2 +- test/cmdtest_py_d/cfg.py | 5 +++-- test/init.sh | 12 +++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 2c19032..21ca9e7 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev7 +3.2.dev8 diff --git a/test/cmdtest_py_d/cfg.py b/test/cmdtest_py_d/cfg.py index 5aacbf9..96bfe9f 100755 --- a/test/cmdtest_py_d/cfg.py +++ b/test/cmdtest_py_d/cfg.py @@ -12,7 +12,7 @@ test.cmdtest_py_d.cfg: configuration data for cmdtest.py """ -import os +cmd_groups_altcoin = [] cmd_groups_dfl = { 'main': ('CmdTestMain',{}), @@ -29,4 +29,5 @@ cfgs = { '3': {}, # main } -def fixup_cfgs(): pass +def fixup_cfgs(): + pass diff --git a/test/init.sh b/test/init.sh index 769490a..81727bb 100755 --- a/test/init.sh +++ b/test/init.sh @@ -47,9 +47,15 @@ build_mmgen_extmod() { create_dir_links() { for link_name in 'mmgen' 'scripts'; do target="$wallet_repo/$link_name" - if [ -e $link_name ]; then - [ $(realpath --relative-to=. $link_name) == $target ] || die "'$link_name' does not point to '$target'" - else + if [ -L $link_name ]; then + [ "$(realpath --relative-to=. $link_name 2>/dev/null)" == $target ] || { + echo "Removing broken symlink '$link_name'" + rm $link_name + } + elif [ -e $link_name ]; then + die "'$link_name' is not a symbolic link. Please remove or relocate it and re-run this script" + fi + if [ ! -e $link_name ]; then echo "Creating symlink: $link_name" ln -s $target fi From 55fdcb29f7ef476676cb8194dcdab690b08e3b46 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 27 Nov 2023 09:18:43 +0000 Subject: [PATCH 112/175] Support Windows/MSYS2 --- README.md | 82 ++++++++++++++++++++++++++------- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/cmdtest_py_d/ct_main.py | 6 ++- test/cmdtest_py_d/ct_regtest.py | 4 +- 5 files changed, 74 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c95e206..d3b18ba 100644 --- a/README.md +++ b/README.md @@ -4,36 +4,66 @@ Requires modules from the [MMGen online/offline cryptocurrency wallet][6]. -Currently tested on Linux only. Some scripts may not work under Windows/MSYS2. - ## Install: If installing as user (without venv), make sure that `~/.local/bin` is in `PATH`. +#### Windows/MSYS2: + +> Install [MSYS2 and the MMGen Wallet dependencies][8], skipping installation of +> scrypt, libsecp256k1 and the wallet itself if desired. + +> Install some additional dependencies: + +```bash + $ pacman -S \ + mingw-w64-ucrt-x86_64-python-pandas \ + mingw-w64-ucrt-x86_64-python-tqdm \ + mingw-w64-ucrt-x86_64-python-lxml + $ python3 -m pip install requests-futures + $ python3 -m pip install --no-deps yahooquery +``` + +#### Linux: + +> Install the [required MMGen Wallet packages][7] for your Linux distribution. + ### Stable version: -First, install the [required MMGen Wallet packages][7] for your Linux distribution: - -Then, - - $ python3 -m pip install --upgrade mmgen-node-tools +```bash +$ python3 -m pip install --upgrade mmgen-node-tools +``` ### Development version: -First, install the latest development version of [MMGen Wallet][6]. +Install the latest development version of [MMGen Wallet][6] for your platform. -Then, - - $ git clone https://github.com/mmgen/mmgen-node-tools - $ cd mmgen-node-tools - $ python3 -m build --no-isolation - $ python3 -m pip install dist/*.whl +```bash +$ git clone https://github.com/mmgen/mmgen-node-tools +$ cd mmgen-node-tools +$ python3 -m build --no-isolation +$ python3 -m pip install dist/*.whl +``` ## Test: *NOTE: the tests require that the MMGen Wallet and MMGen Node Tools repositories be located in the same directory.* +#### Windows/MSYS2: + +> *Tested only on NTFS – with ReFS your mileage may vary* + +> Turn on Developer Mode to enable symlinks: +``` + Settings -> Update & Security -> For developers -> Developer Mode: On +``` +> and add this to your `~/.bashrc`: +```bash + export MSYS=winsymlinks:nativestrict +``` +> Close and reopen the MSYS2 terminal to update your environment. + Initialize the test framework (must be run at least once after cloning, and possibly again after a pull if tests have been updated): @@ -49,11 +79,29 @@ Full testing: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[**Forum**][4] | -[PGP Public Key][5] | -Donate: 15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w +Homepage: +[Clearnet](https://mmgen-wallet.cc) | +[I2P](http://mmgen-wallet.i2p) | +[Onion](http://mmgen55rtcahqfp2hn3v7syqv2wqanks5oeezqg3ykwfkebmouzjxlad.onion) +Code repository: +[Clearnet](https://mmgen.org/project/mmgen/mmgen-wallet) | +[I2P](http://mmgen-wallet.i2p/project/mmgen/mmgen-wallet) | +[Onion](http://mmgen55rtcahqfp2hn3v7syqv2wqanks5oeezqg3ykwfkebmouzjxlad.onion/project/mmgen/mmgen-wallet) +Code repository mirrors: +[Github](https://github.com/mmgen/mmgen-wallet) | +[Gitlab](https://gitlab.com/mmgen/mmgen-wallet) | +[Gitflic](https://gitflic.ru/project/mmgen/mmgen-wallet) +[Keybase](https://keybase.io/mmgen) | +[Reddit](https://www.reddit.com/user/mmgen-py) | +[Bitcointalk](https://bitcointalk.org/index.php?topic=567069.new#new) +[PGP Signing Key][5]: 5C84 CB45 AEE2 250F 31A6 A570 3F8B 1861 E32B 7DA2 +Donate: + ⊙ BTC: *bc1qxmymxf8p5ckvlxkmkwgw8ap5t2xuaffmrpexap* + ⊙ BCH: *15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w* + ⊙ XMR: *8B14zb8wgLuKDdse5p8f3aKpFqRdB4i4xj83b7BHYABHMvHifWxiDXeKRELnaxL5FySfeRRS5girgUvgy8fQKsYMEzPUJ8h* [4]: https://bitcointalk.org/index.php?topic=567069.0 [5]: https://github.com/mmgen/mmgen-wallet/wiki/MMGen-Signing-Keys [6]: https://github.com/mmgen/mmgen-wallet/ [7]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-on-Linux +[8]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-on-Microsoft-Windows#a_m diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 21ca9e7..a91fcc8 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev8 +3.2.dev9 diff --git a/setup.cfg b/setup.cfg index 4cb6a92..62f7074 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.8 include_package_data = True install_requires = - mmgen-wallet>=14.0.dev22 + mmgen-wallet>=14.0.dev25 pyyaml yahooquery diff --git a/test/cmdtest_py_d/ct_main.py b/test/cmdtest_py_d/ct_main.py index 5f5c691..7c38bcf 100755 --- a/test/cmdtest_py_d/ct_main.py +++ b/test/cmdtest_py_d/ct_main.py @@ -35,7 +35,7 @@ class CmdTestMain(CmdTestBase): "'mmnode-peerblocks' script", ('peerblocks1', '--help'), ('peerblocks2', 'interactive (popen spawn)'), - ('peerblocks3', 'interactive, 80 columns (pexpect_spawn)'), + ('peerblocks3', 'interactive, 80 columns (pexpect_spawn [on Linux])'), ), } @@ -94,4 +94,6 @@ class CmdTestMain(CmdTestBase): return t def peerblocks3(self): - return self.peerblocks2(['--columns=80'],pexpect_spawn=True) + return self.peerblocks2( + ['--columns=80'], + pexpect_spawn = sys.platform != 'win32' ) diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index b17b4c6..e554930 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -12,7 +12,7 @@ test.cmdtest_py_d.ct_regtest: Regtest tests for the cmdtest.py test suite """ -import os +import sys,os from mmgen.util import msg_r,die,gmsg from mmgen.protocol import init_proto @@ -134,6 +134,8 @@ class CmdTestRegtest(CmdTestBase): def netrate2(self): t = self.netrate( [], r'sent:.*' ) t.kill(15) + if sys.platform == 'win32': + return 'ok' t.req_exit_val = -15 return t From 4b22d67e37d22a73701ff7b90236389b448bda45 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 27 Nov 2023 09:32:24 +0000 Subject: [PATCH 113/175] update README.md --- README.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index d3b18ba..4d3fe28 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,16 @@ If installing as user (without venv), make sure that `~/.local/bin` is in `PATH` > Install [MSYS2 and the MMGen Wallet dependencies][8], skipping installation of > scrypt, libsecp256k1 and the wallet itself if desired. - +> > Install some additional dependencies: - -```bash - $ pacman -S \ - mingw-w64-ucrt-x86_64-python-pandas \ - mingw-w64-ucrt-x86_64-python-tqdm \ - mingw-w64-ucrt-x86_64-python-lxml - $ python3 -m pip install requests-futures - $ python3 -m pip install --no-deps yahooquery -``` +> ```bash +> $ pacman -S \ +> mingw-w64-ucrt-x86_64-python-pandas \ +> mingw-w64-ucrt-x86_64-python-tqdm \ +> mingw-w64-ucrt-x86_64-python-lxml +> $ python3 -m pip install requests-futures +> $ python3 -m pip install --no-deps yahooquery +> ``` #### Linux: @@ -53,15 +52,15 @@ located in the same directory.* #### Windows/MSYS2: > *Tested only on NTFS – with ReFS your mileage may vary* - +> > Turn on Developer Mode to enable symlinks: -``` - Settings -> Update & Security -> For developers -> Developer Mode: On -``` +> ``` +> Settings -> Update & Security -> For developers -> Developer Mode: On +> ``` > and add this to your `~/.bashrc`: -```bash - export MSYS=winsymlinks:nativestrict -``` +> ```bash +> export MSYS=winsymlinks:nativestrict +> ``` > Close and reopen the MSYS2 terminal to update your environment. Initialize the test framework (must be run at least once after cloning, and From ef0acc254225d712465cfca9558b30bbd628f41c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 27 Nov 2023 10:55:22 +0000 Subject: [PATCH 114/175] Version 3.2.0 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index a91fcc8..944880f 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.dev9 +3.2.0 diff --git a/setup.cfg b/setup.cfg index 62f7074..f737c62 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.8 include_package_data = True install_requires = - mmgen-wallet>=14.0.dev25 + mmgen-wallet==14.0.0 pyyaml yahooquery From 3dace6b188aaed1909f9bc4a6973df996efaecaa Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 30 Nov 2023 09:19:10 +0000 Subject: [PATCH 115/175] README.md: fix MSWin deps, fix repo URLs, add Twitter link --- README.md | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 4d3fe28..f72051f 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,7 @@ If installing as user (without venv), make sure that `~/.local/bin` is in `PATH` > > Install some additional dependencies: > ```bash -> $ pacman -S \ -> mingw-w64-ucrt-x86_64-python-pandas \ -> mingw-w64-ucrt-x86_64-python-tqdm \ -> mingw-w64-ucrt-x86_64-python-lxml +> $ pacman -S mingw-w64-ucrt-x86_64-python-pandas > $ python3 -m pip install requests-futures > $ python3 -m pip install --no-deps yahooquery > ``` @@ -35,7 +32,7 @@ $ python3 -m pip install --upgrade mmgen-node-tools ### Development version: -Install the latest development version of [MMGen Wallet][6] for your platform. +Install the latest development version of [MMGen Wallet][6] for your platform: ```bash $ git clone https://github.com/mmgen/mmgen-node-tools @@ -65,32 +62,34 @@ located in the same directory.* Initialize the test framework (must be run at least once after cloning, and possibly again after a pull if tests have been updated): - - $ test/init.sh - +``` +$ test/init.sh +``` BTC-only testing: - - $ test/test-release.sh -A - +``` +$ test/test-release.sh -A +``` Full testing: - - $ test/test-release.sh +``` +$ test/test-release.sh +``` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Homepage: -[Clearnet](https://mmgen-wallet.cc) | +[Clearnet](https://mmgen.org) | [I2P](http://mmgen-wallet.i2p) | [Onion](http://mmgen55rtcahqfp2hn3v7syqv2wqanks5oeezqg3ykwfkebmouzjxlad.onion) Code repository: -[Clearnet](https://mmgen.org/project/mmgen/mmgen-wallet) | -[I2P](http://mmgen-wallet.i2p/project/mmgen/mmgen-wallet) | -[Onion](http://mmgen55rtcahqfp2hn3v7syqv2wqanks5oeezqg3ykwfkebmouzjxlad.onion/project/mmgen/mmgen-wallet) +[Clearnet](https://mmgen.org/project/mmgen/mmgen-node-tools) | +[I2P](http://mmgen-wallet.i2p/project/mmgen/mmgen-node-tools) | +[Onion](http://mmgen55rtcahqfp2hn3v7syqv2wqanks5oeezqg3ykwfkebmouzjxlad.onion/project/mmgen/mmgen-node-tools) Code repository mirrors: -[Github](https://github.com/mmgen/mmgen-wallet) | -[Gitlab](https://gitlab.com/mmgen/mmgen-wallet) | -[Gitflic](https://gitflic.ru/project/mmgen/mmgen-wallet) +[Github](https://github.com/mmgen/mmgen-node-tools) | +[Gitlab](https://gitlab.com/mmgen/mmgen-node-tools) | +[Gitflic](https://gitflic.ru/project/mmgen/mmgen-node-tools) [Keybase](https://keybase.io/mmgen) | +[Twitter](https://twitter.com/TheMMGenProject) | [Reddit](https://www.reddit.com/user/mmgen-py) | [Bitcointalk](https://bitcointalk.org/index.php?topic=567069.new#new) [PGP Signing Key][5]: 5C84 CB45 AEE2 250F 31A6 A570 3F8B 1861 E32B 7DA2 @@ -99,7 +98,6 @@ Donate:  ⊙ BCH: *15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w*  ⊙ XMR: *8B14zb8wgLuKDdse5p8f3aKpFqRdB4i4xj83b7BHYABHMvHifWxiDXeKRELnaxL5FySfeRRS5girgUvgy8fQKsYMEzPUJ8h* -[4]: https://bitcointalk.org/index.php?topic=567069.0 [5]: https://github.com/mmgen/mmgen-wallet/wiki/MMGen-Signing-Keys [6]: https://github.com/mmgen/mmgen-wallet/ [7]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-on-Linux From de29b213ff824ee247017c16c52a48378f1c5e17 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 30 Nov 2023 11:25:34 +0000 Subject: [PATCH 116/175] bump version and wallet dependency --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 944880f..0e5a99a 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.2.0 +3.3.dev0 diff --git a/setup.cfg b/setup.cfg index f737c62..7827398 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.8 include_package_data = True install_requires = - mmgen-wallet==14.0.0 + mmgen-wallet>=14.0.0 pyyaml yahooquery From cc0f4729c6efa8db40e93b43d88f569cd9442c03 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 12 Dec 2023 09:58:01 +0000 Subject: [PATCH 117/175] update for mmgen-wallet v14.1.dev2 --- cmds/mmnode-addrbal | 2 +- cmds/mmnode-blocks-info | 2 +- cmds/mmnode-feeview | 2 +- cmds/mmnode-halving-calculator | 2 +- cmds/mmnode-netrate | 2 +- cmds/mmnode-peerblocks | 2 +- cmds/mmnode-ticker | 2 +- cmds/mmnode-txfind | 2 +- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/unit_tests_d/ut_dep.py | 21 +++++++++++++++++++++ 11 files changed, 31 insertions(+), 10 deletions(-) create mode 100755 test/unit_tests_d/ut_dep.py diff --git a/cmds/mmnode-addrbal b/cmds/mmnode-addrbal index 3a5bc68..a14edc0 100755 --- a/cmds/mmnode-addrbal +++ b/cmds/mmnode-addrbal @@ -14,4 +14,4 @@ mmnode-addrbal: Get balances for arbitrary addresses in the blockchain from mmgen.main import launch -launch('addrbal',package='mmgen_node_tools') +launch(mod='addrbal',package='mmgen_node_tools') diff --git a/cmds/mmnode-blocks-info b/cmds/mmnode-blocks-info index a1c855e..d542d1a 100755 --- a/cmds/mmnode-blocks-info +++ b/cmds/mmnode-blocks-info @@ -14,4 +14,4 @@ mmnode-blocks-info: Display information about a block or range of blocks from mmgen.main import launch -launch('blocks_info',package='mmgen_node_tools') +launch(mod='blocks_info',package='mmgen_node_tools') diff --git a/cmds/mmnode-feeview b/cmds/mmnode-feeview index 5a4625e..8b639bb 100755 --- a/cmds/mmnode-feeview +++ b/cmds/mmnode-feeview @@ -14,4 +14,4 @@ mmnode-feeview: Visualize the fee structure of a node’s mempool from mmgen.main import launch -launch('feeview',package='mmgen_node_tools') +launch(mod='feeview',package='mmgen_node_tools') diff --git a/cmds/mmnode-halving-calculator b/cmds/mmnode-halving-calculator index ebf1f6d..1759f77 100755 --- a/cmds/mmnode-halving-calculator +++ b/cmds/mmnode-halving-calculator @@ -14,4 +14,4 @@ mmnode-halving-calculator: Estimate date(s) of future block subsidy halving(s) from mmgen.main import launch -launch('halving_calculator',package='mmgen_node_tools') +launch(mod='halving_calculator',package='mmgen_node_tools') diff --git a/cmds/mmnode-netrate b/cmds/mmnode-netrate index cfefab2..f8c4ad1 100755 --- a/cmds/mmnode-netrate +++ b/cmds/mmnode-netrate @@ -14,4 +14,4 @@ mmnode-netrate: Bitcoin daemon network rate monitor from mmgen.main import launch -launch('netrate',package='mmgen_node_tools') +launch(mod='netrate',package='mmgen_node_tools') diff --git a/cmds/mmnode-peerblocks b/cmds/mmnode-peerblocks index be4bb41..5a02741 100755 --- a/cmds/mmnode-peerblocks +++ b/cmds/mmnode-peerblocks @@ -14,4 +14,4 @@ mmnode-peerblocks: List blocks in flight, disconnect stalling nodes from mmgen.main import launch -launch('peerblocks',package='mmgen_node_tools') +launch(mod='peerblocks',package='mmgen_node_tools') diff --git a/cmds/mmnode-ticker b/cmds/mmnode-ticker index d6e91fb..f0160e7 100755 --- a/cmds/mmnode-ticker +++ b/cmds/mmnode-ticker @@ -14,4 +14,4 @@ mmnode-ticker: Display price information for cryptocurrency and other assets from mmgen.main import launch -launch('ticker',package='mmgen_node_tools') +launch(mod='ticker',package='mmgen_node_tools') diff --git a/cmds/mmnode-txfind b/cmds/mmnode-txfind index 05213a8..fd48cf4 100755 --- a/cmds/mmnode-txfind +++ b/cmds/mmnode-txfind @@ -14,4 +14,4 @@ mmnode-txfind: Find a transaction in the blockchain or mempool from mmgen.main import launch -launch('txfind',package='mmgen_node_tools') +launch(mod='txfind',package='mmgen_node_tools') diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 0e5a99a..f5f664c 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.3.dev0 +3.3.dev1 diff --git a/setup.cfg b/setup.cfg index 7827398..c99a9ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.8 include_package_data = True install_requires = - mmgen-wallet>=14.0.0 + mmgen-wallet>=14.1.dev2 pyyaml yahooquery diff --git a/test/unit_tests_d/ut_dep.py b/test/unit_tests_d/ut_dep.py new file mode 100755 index 0000000..641c3c3 --- /dev/null +++ b/test/unit_tests_d/ut_dep.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +""" +test.unit_tests_d.ut_dep: dependency unit tests for the MMGen Node Tools + + Test whether dependencies are installed and functional. + No data verification is performed. +""" + +from ..include.common import vmsg,imsg +from mmgen.color import yellow + +class unit_tests: + + def yahooquery(self,name,ut): + try: + from yahooquery import Ticker + return True + except ImportError: + imsg(yellow('Unable to import Ticker from yahooquery')) + return False From 6f2925f7973b9918a5582c35167bcf9ffe09b92c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 10 Feb 2024 15:15:18 +0000 Subject: [PATCH 118/175] test/init.sh, MANIFEST.in: fixes, cleanups --- MANIFEST.in | 10 ++++++++-- mmgen_node_tools/Ticker.py | 4 ++-- test/init.sh | 31 +++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 7bdab90..370da07 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,10 @@ include README.md LICENSE -include test/test-release.sh -include test/unit_tests_d/*.py + include mmgen_node_tools/data/* + +include test/init.sh +include test/test-release.d/*.sh +include test/unit_tests_d/*.py +include test/cmdtest_py_d/*.py +include test/overlay/fakemods/mmgen_node_tools/*.py +include test/ref/*/* diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 5a6a6a2..e8b704f 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -193,7 +193,7 @@ class DataSource: @property def timeout(self): - return 5 if gcfg.test_suite else self.btc_ratelimit if cfg.btc_only else self.ratelimit + return 0 if gcfg.test_suite else self.btc_ratelimit if cfg.btc_only else self.ratelimit def json_data_error_msg(self,json_text): tor_captcha_msg = f""" @@ -265,7 +265,7 @@ class DataSource: @property def timeout(self): - return 5 if gcfg.test_suite else self.ratelimit + return 0 if gcfg.test_suite else self.ratelimit @property def symbols(self): diff --git a/test/init.sh b/test/init.sh index 81727bb..51e4e9e 100755 --- a/test/init.sh +++ b/test/init.sh @@ -10,6 +10,7 @@ RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" RESET="\e[0m" +set -e set -o errtrace set -o functrace @@ -17,14 +18,19 @@ trap 'echo -e "${GREEN}Exiting at user request$RESET"; exit' INT trap 'echo -e "${RED}Node Tools test suite initialization exited with error (line $BASH_LINENO) $RESET"' ERR umask 0022 +STDOUT_DEVNULL='>/dev/null' +STDERR_DEVNULL='2>/dev/null' + PROGNAME=$(basename $0) -while getopts h OPT +while getopts hv OPT do case "$OPT" in h) printf " %-16s Initialize the MMGen Node Tools test suite\n" "${PROGNAME}:" echo " USAGE: $PROGNAME" echo " OPTIONS: '-h' Print this help message" + echo " -v Be more verbose" exit ;; + v) VERBOSE=1 STDOUT_DEVNULL='' STDERR_DEVNULL='' ;; *) exit ;; esac done @@ -41,7 +47,10 @@ check_mmgen_repo() { } build_mmgen_extmod() { - ( cd $wallet_repo; python3 ./setup.py build_ext --inplace ) + ( + cd $wallet_repo + eval "python3 ./setup.py build_ext --inplace $STDOUT_DEVNULL $STDERR_DEVNULL" + ) } create_dir_links() { @@ -49,14 +58,14 @@ create_dir_links() { target="$wallet_repo/$link_name" if [ -L $link_name ]; then [ "$(realpath --relative-to=. $link_name 2>/dev/null)" == $target ] || { - echo "Removing broken symlink '$link_name'" + [ "$VERBOSE" ] && echo "Removing broken symlink '$link_name'" rm $link_name } elif [ -e $link_name ]; then die "'$link_name' is not a symbolic link. Please remove or relocate it and re-run this script" fi if [ ! -e $link_name ]; then - echo "Creating symlink: $link_name" + [ "$VERBOSE" ] && echo "Creating symlink: $link_name" ln -s $target fi done @@ -87,32 +96,34 @@ create_test_links() { fs="%-8s %-16s %s -> %s\n" if [ $type == 'hard' ]; then if [ -L $path ]; then - printf "$fs" "Deleting" "symbolic link:" $path $target + [ "$VERBOSE" ] && printf "$fs" "Deleting" "symbolic link:" $path $target rm -rf $path elif [ -e $path ]; then if [ "$(stat --printf=%i $path)" -ne "$(stat --printf=%i $target)" ]; then - printf "$fs" "Deleting" "stale hard link:" $path "?" + [ "$VERBOSE" ] && printf "$fs" "Deleting" "stale hard link:" $path "?" rm -rf $path fi fi fi if [ ! -e $path ]; then # link is either absent or a broken symlink - printf "$fs" "Creating" "$type link:" $path $target + [ "$VERBOSE" ] && printf "$fs" "Creating" "$type link:" $path $target ( cd "$(dirname $path)" && ln -f $symlink_arg $pfx$target ) fi done <<<$paths } -set -e - becho 'Initializing MMGen Node Tools Test Suite' check_mmgen_repo || die "MMGen Wallet repository not found at $wallet_repo!" build_mmgen_extmod +[ "$VERBOSE" ] && becho 'Creating links to mmgen-wallet repo' + create_dir_links create_test_links -becho 'OK' +[ "$VERBOSE" ] && becho 'OK' + +true From 7c7c2c7da839ce3139c2260eb293d0e67fa1acf7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 10 Feb 2024 15:15:18 +0000 Subject: [PATCH 119/175] mmnode-ticker: backport fix for Python 3.9 --- mmgen_node_tools/Ticker.py | 3 ++- mmgen_node_tools/data/version | 2 +- setup.cfg | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index e8b704f..33fb1fa 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -440,7 +440,8 @@ def gen_data(data): d['percent_change_7d'] = d['quotes']['USD']['percent_change_7d'] d['percent_change_30d'] = d['quotes']['USD']['percent_change_30d'] d['percent_change_1y'] = d['quotes']['USD']['percent_change_1y'] - d['last_updated'] = int(datetime.datetime.fromisoformat(d['last_updated']).timestamp()) + # .replace('Z','+00:00') -- Python 3.9 backport + d['last_updated'] = int(datetime.datetime.fromisoformat(d['last_updated'].replace('Z','+00:00')).timestamp()) yield (d['id'],d) found[k].add(d[k]) wants[k].remove(d[k]) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index f5f664c..0ed23d0 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.3.dev1 +3.3.dev2 diff --git a/setup.cfg b/setup.cfg index c99a9ef..82704a4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,11 +24,11 @@ classifiers = Development Status :: 5 - Production/Stable [options] -python_requires = >=3.8 +python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=14.1.dev2 + mmgen-wallet>=14.1.dev8 pyyaml yahooquery From db4ba26dced535ac60465a71c7ff939305b3ae6a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 8 Mar 2024 15:32:54 +0000 Subject: [PATCH 120/175] update testing for MMGen Wallet v14.1.dev19 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/cmdtest_py_d/ct_misc.py | 13 ++++++------- test/cmdtest_py_d/ct_regtest.py | 26 +++++++++++++++++--------- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 0ed23d0..3170c6e 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.3.dev2 +3.3.dev3 diff --git a/setup.cfg b/setup.cfg index 82704a4..f0c6629 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=14.1.dev8 + mmgen-wallet>=14.1.dev19 pyyaml yahooquery diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 3dbac50..b4b83e9 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -106,10 +106,11 @@ class CmdTestScripts(CmdTestBase): shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) return 'ok' - def ticker(self,args=[],expect_list=None,cached=True): + def ticker(self, args=[], expect_list=None, cached=True, exit_val=None): t = self.spawn( f'mmnode-ticker', - (['--cached-data'] if cached else []) + self.ticker_args + args ) + (['--cached-data'] if cached else []) + self.ticker_args + args, + exit_val = exit_val) if expect_list: t.match_expect_list(expect_list) return t @@ -120,12 +121,11 @@ class CmdTestScripts(CmdTestBase): return t def ticker2(self): - t = self.ticker(cached=False) + t = self.ticker(cached=False, exit_val=3) if not cfg.skipping_deps: t.expect('Creating') t.expect('Creating') - ret = t.expect(['proxy host could not be resolved','ProxyError']) - t.req_exit_val = 3 if ret == 0 else 1 + t.expect(['proxy host could not be resolved', 'ProxyError']) return t def ticker3(self): @@ -165,9 +165,8 @@ class CmdTestScripts(CmdTestBase): return t def ticker6(self): - t = self.ticker( ['--wide','--portfolio'], None ) + t = self.ticker(['--wide','--portfolio'], None, exit_val=1) t.expect('No portfolio') - t.req_exit_val = 1 return t def ticker7(self): # demo diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index e554930..c89f8fa 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -39,6 +39,8 @@ class CmdTestRegtest(CmdTestBase): tmpdir_nums = [1] color = True deterministic = False + bdb_wallet = True + cmd_group_in = ( ('setup', 'regtest mode setup'), ('subgroup.netrate', []), @@ -111,20 +113,27 @@ class CmdTestRegtest(CmdTestBase): die(2,'--testnet and --regtest options incompatible with regtest test suite') self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) - self.regtest = MMGenRegtest(cfg,self.proto.coin) + + self.use_bdb_wallet = self.bdb_wallet or self.proto.coin != 'BTC' + self.regtest = MMGenRegtest(cfg, self.proto.coin, bdb_wallet=self.use_bdb_wallet) def setup(self): stop_test_daemons(self.proto.network_id,force=True,remove_datadir=True) from shutil import rmtree - try: rmtree(joinpath(self.tr.data_dir,'regtest')) - except: pass - t = self.spawn('mmgen-regtest',['-n','setup']) + try: + rmtree(joinpath(self.tr.data_dir,'regtest')) + except: + pass + t = self.spawn( + 'mmgen-regtest', + (['--bdb-wallet'] if self.use_bdb_wallet else []) + + ['--setup-no-stop-daemon', 'setup']) for s in ('Starting','Creating','Creating','Creating','Mined','Setup complete'): t.expect(s) return t - def netrate(self,add_args,expect_str): - t = self.spawn( 'mmnode-netrate', args1 + add_args ) + def netrate(self, add_args, expect_str, exit_val=None): + t = self.spawn('mmnode-netrate', args1 + add_args, exit_val=exit_val) t.expect(expect_str,regex=True) return t @@ -132,11 +141,10 @@ class CmdTestRegtest(CmdTestBase): return self.netrate( ['--help'], 'USAGE:.*' ) def netrate2(self): - t = self.netrate( [], r'sent:.*' ) + t = self.netrate([], r'sent:.*', exit_val=-15) t.kill(15) if sys.platform == 'win32': return 'ok' - t.req_exit_val = -15 return t def halving_calculator(self,add_args,expect_list): @@ -326,7 +334,7 @@ class CmdTestRegtest(CmdTestBase): return await do_tx( [{ 'txid': tx_input['txid'], 'vout': 0 }], outputs, - r.miner_wif ) + await r.miner_wif) async def do_tx2(tx,pairno): fee = fees[pairno] From c4ace71049ca48e00bdd0a6d204df96ddf71526c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 10 Mar 2024 14:44:57 +0000 Subject: [PATCH 121/175] minor fixes and cleanups --- mmgen_node_tools/Ticker.py | 16 ++++++++++++---- test/cmdtest_py_d/ct_misc.py | 5 +++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 33fb1fa..806b0e9 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -26,6 +26,7 @@ from collections import namedtuple from mmgen.color import red,yellow,green,blue,orange,gray from mmgen.util import msg,msg_r,Msg,die,Die,suf,fmt,fmt_list,fmt_dict,list_gen +from mmgen.ui import do_pager homedir = os.getenv('HOME') dfl_cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') @@ -407,6 +408,8 @@ def gen_data(data): id = get_id(k,v) if wants['id']: if id in wants['id']: + if not isinstance(v,dict): + die(2, str(v)) if id in found['id']: die(1,dup_sym_errmsg(id)) if m := data['hi'].get(k): @@ -517,7 +520,6 @@ def main(): return if gcfg.list_ids: - from mmgen.ui import do_pager do_pager('\n'.join(e['id'] for e in src_data['cc'])) return @@ -628,9 +630,10 @@ def make_cfg(gcfg_arg): def get_portfolio(): return {k:Decimal(v) for k,v in cfg_in.portfolio.items() if (not gcfg.btc) or k == 'btc-bitcoin'} - def parse_add_precision(s): - if not s: + def parse_add_precision(arg): + if not arg: return 0 + s = str(arg) if not (s.isdigit() and s.isascii()): die(1,f'{s}: invalid parameter for --add-precision (not an integer)') if int(s) > 30: @@ -706,7 +709,12 @@ def make_cfg(gcfg_arg): cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir, proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), - portfolio = get_portfolio() if cfg_in.portfolio and gcfg.portfolio and not query else None, + portfolio = + get_portfolio() + if cfg_in.portfolio + and gcfg.portfolio + and not query + else None, percent_cols = parse_percent_cols(gcfg.percent_cols) ) diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index b4b83e9..8127e1d 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -121,11 +121,12 @@ class CmdTestScripts(CmdTestBase): return t def ticker2(self): - t = self.ticker(cached=False, exit_val=3) + t = self.ticker(cached=False) if not cfg.skipping_deps: t.expect('Creating') t.expect('Creating') - t.expect(['proxy host could not be resolved', 'ProxyError']) + ret = t.expect(['proxy host could not be resolved', 'ProxyError']) + t.exit_val = 1 if ret else 3 return t def ticker3(self): From 867026e8edd370dbbf655d2ea20ad384a50d4f4e Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 10 Mar 2024 14:44:57 +0000 Subject: [PATCH 122/175] mmnode-ticker: add config file vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following command-line opts are now configurable in ‘ticker-cfg.yaml’: add_precision asset_limit btc cached_data elapsed name_labels pager percent_cols thousands_comma update_time verbose --- mmgen_node_tools/Ticker.py | 65 ++++++++++++++++++++++------------- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 806b0e9..fd05f99 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -25,7 +25,7 @@ from decimal import Decimal from collections import namedtuple from mmgen.color import red,yellow,green,blue,orange,gray -from mmgen.util import msg,msg_r,Msg,die,Die,suf,fmt,fmt_list,fmt_dict,list_gen +from mmgen.util import msg,msg_r,Msg,Msg_r,die,Die,suf,fmt,fmt_list,fmt_dict,list_gen from mmgen.ui import do_pager homedir = os.getenv('HOME') @@ -75,7 +75,7 @@ class DataSource: ['curl', '--tr-encoding', '--header', 'Accept: application/json',True], ['--compressed'], # adds 'Accept-Encoding: gzip' ['--proxy', cfg.proxy, isinstance(cfg.proxy,str)], - ['--silent', not gcfg.verbose], + ['--silent', not cfg.verbose], [self.api_url] ) @@ -101,7 +101,9 @@ class DataSource: if not os.path.exists(self.json_fn): open(self.json_fn,'w').write('{}') - if gcfg.cached_data: + use_cached_data = cfg.cached_data and not gcfg.download + + if use_cached_data: data_type = 'json' data_in = open(self.json_fn).read() else: @@ -112,8 +114,8 @@ class DataSource: msg('') self.fetch_delay() msg_r(f'Fetching {self.data_desc} from {self.api_host}...') - if self.has_verbose: - gcfg._util.vmsg('') + if self.has_verbose and cfg.verbose: + msg('') data_in = self.get_data_from_network() msg('done') if gcfg.testing: @@ -133,14 +135,14 @@ class DataSource: json_text = json.dumps(data_in) if not data: - if gcfg.cached_data: + if use_cached_data: die(1,'No cached data! Run command without --cached-data option to retrieve data from remote host') else: die(2,'Remote host returned no data!') elif 'error' in data: die(1,data['error']) - if gcfg.cached_data: + if use_cached_data: msg(f'Using cached data from ~/{self.json_fn_rel}') else: open(self.json_fn,'w').write(json_text) @@ -171,7 +173,7 @@ class DataSource: dfl_asset_limit = 2000 def __init__(self): - self.asset_limit = int(gcfg.asset_limit or self.dfl_asset_limit) + self.asset_limit = int(cfg.asset_limit or self.dfl_asset_limit) def rate_limit_errmsg(self,elapsed): return ( @@ -528,9 +530,8 @@ def main(): data = dict(gen_data(src_data)) - gcfg._util.stdout_or_pager( - '\n'.join(getattr(Ticker,cfg.clsname)(data).gen_output()) + '\n' - ) + (do_pager if cfg.pager else Msg_r)( + '\n'.join(getattr(Ticker,cfg.clsname)(data).gen_output()) + '\n') def make_cfg(gcfg_arg): @@ -670,7 +671,15 @@ def make_cfg(gcfg_arg): 'proxy', 'proxy2', 'portfolio', - 'percent_cols' ]) + 'percent_cols', + 'asset_limit', + 'cached_data', + 'elapsed', + 'name_labels', + 'pager', + 'thousands_comma', + 'update_time', + 'verbose']) global gcfg,cfg_in,src_cls,cfg @@ -704,18 +713,26 @@ def make_cfg(gcfg_arg): query = query, adjust = ( lambda x: (100 + x) / 100 if x else 1 )( Decimal(gcfg.adjust or 0) ), clsname = 'trading' if query else 'overview', - btc_only = gcfg.btc, - add_prec = parse_add_precision(gcfg.add_precision), + btc_only = gcfg.btc or cfg_in.cfg.get('btc'), + add_prec = parse_add_precision(gcfg.add_precision or cfg_in.cfg.get('add_precision')), cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir, proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), portfolio = get_portfolio() if cfg_in.portfolio - and gcfg.portfolio + and (gcfg.portfolio or cfg_in.cfg.get('portfolio')) and not query else None, - percent_cols = parse_percent_cols(gcfg.percent_cols) + percent_cols = parse_percent_cols(gcfg.percent_cols or cfg_in.cfg.get('percent_cols')), + asset_limit = gcfg.asset_limit or cfg_in.cfg.get('asset_limit'), + cached_data = gcfg.cached_data or cfg_in.cfg.get('cached_data'), + elapsed = gcfg.elapsed or cfg_in.cfg.get('elapsed'), + name_labels = gcfg.name_labels or cfg_in.cfg.get('name_labels'), + pager = gcfg.pager or cfg_in.cfg.get('pager'), + thousands_comma = gcfg.thousands_comma or cfg_in.cfg.get('thousands_comma'), + update_time = gcfg.update_time or cfg_in.cfg.get('update_time'), + verbose = gcfg.verbose or cfg_in.cfg.get('verbose'), ) def get_cfg_in(): @@ -753,10 +770,10 @@ class Ticker: def __init__(self,data): - self.comma = ',' if gcfg.thousands_comma else '' + self.comma = ',' if cfg.thousands_comma else '' self.col1_wid = max(len('TOTAL'),( - max(len(self.create_label(d['id'])) for d in data.values()) if gcfg.name_labels else + max(len(self.create_label(d['id'])) for d in data.values()) if cfg.name_labels else max(len(d['symbol']) for d in data.values()) )) + 1 @@ -769,7 +786,7 @@ class Ticker: def format_last_update_col(self,cross_assets=()): - if gcfg.elapsed: + if cfg.elapsed: from mmgen.util2 import format_elapsed_hr fmt_func = format_elapsed_hr else: @@ -918,7 +935,7 @@ class Ticker: amt_fmt = amt_fmt.rstrip('0').rstrip('.') return self.fs_num.format( - lbl = (self.create_label(d['id']) if gcfg.name_labels else d['symbol']), + lbl = self.create_label(d['id']) if cfg.name_labels else d['symbol'], pc1 = fmt_pct(d.get('percent_change_7d')), pc2 = fmt_pct(d.get('percent_change_24h')), pc3 = fmt_pct(d.get('percent_change_1y')), @@ -969,11 +986,11 @@ class Ticker: ( 'pct1m', 'm' in cfg.percent_cols ), ( 'pct1w', 'w' in cfg.percent_cols ), ( 'pct1d', 'd' in cfg.percent_cols ), - ( 'update_time', gcfg.update_time ), + ( 'update_time', cfg.update_time ), ) if b] ) cols2 = list(cols) - if gcfg.update_time: + if cfg.update_time: cols2.pop() cols2.append('amt') @@ -1058,7 +1075,7 @@ class Ticker: if self.show_adj: self.fs_str += ' {p_adj}' self.hl_wid += self.max_wid + 1 - if gcfg.update_time: + if cfg.update_time: self.fs_str += ' {upd}' self.hl_wid += self.upd_w + 2 @@ -1071,7 +1088,7 @@ class Ticker: if self.show_adj else '' ) return self.fs_str.format( - lbl = (self.create_label(id) if gcfg.name_labels else d['symbol']), + lbl = self.create_label(id) if cfg.name_labels else d['symbol'], p_spot = green(p_spot) if id in self.hl_ids else p_spot, p_adj = yellow(p_adj) if id in self.hl_ids else p_adj, upd = d.get('last_updated_fmt'), diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 3170c6e..1876a63 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.3.dev3 +3.3.dev4 diff --git a/setup.cfg b/setup.cfg index f0c6629..e3ef90e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=14.1.dev19 + mmgen-wallet>=14.1.dev21 pyyaml yahooquery From 18a92cd461c88cc1b1b48b3cf8f969d20fc79088 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 19 Jul 2024 09:53:52 +0000 Subject: [PATCH 123/175] mmnode-ticker: add `--quiet` option, back up cached JSON data --- mmgen_node_tools/Ticker.py | 17 +++++++++++++---- mmgen_node_tools/main_ticker.py | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index fd05f99..bae27c2 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -136,17 +136,24 @@ class DataSource: if not data: if use_cached_data: - die(1,'No cached data! Run command without --cached-data option to retrieve data from remote host') + die(1, + f'No cached {self.data_desc}! Run command without the --cached-data option, ' + 'or use --download to retrieve data from remote host') else: die(2,'Remote host returned no data!') elif 'error' in data: die(1,data['error']) if use_cached_data: - msg(f'Using cached data from ~/{self.json_fn_rel}') + if not cfg.quiet: + msg(f'Using cached data from ~/{self.json_fn_rel}') else: - open(self.json_fn,'w').write(json_text) - msg(f'JSON data cached to ~/{self.json_fn_rel}') + if os.path.exists(self.json_fn): + os.rename(self.json_fn, self.json_fn + '.bak') + with open(self.json_fn, 'w') as fh: + fh.write(json_text) + if not cfg.quiet: + msg(f'JSON data cached to ~/{self.json_fn_rel}') if gcfg.download: sys.exit(0) @@ -679,6 +686,7 @@ def make_cfg(gcfg_arg): 'pager', 'thousands_comma', 'update_time', + 'quiet', 'verbose']) global gcfg,cfg_in,src_cls,cfg @@ -732,6 +740,7 @@ def make_cfg(gcfg_arg): pager = gcfg.pager or cfg_in.cfg.get('pager'), thousands_comma = gcfg.thousands_comma or cfg_in.cfg.get('thousands_comma'), update_time = gcfg.update_time or cfg_in.cfg.get('update_time'), + quiet = gcfg.quiet or cfg_in.cfg.get('quiet'), verbose = gcfg.verbose or cfg_in.cfg.get('verbose'), ) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index f758089..c3f7325 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -53,6 +53,7 @@ opts_data = { columns ‘C’ (specify with comma-separated letters {pc}) -P, --pager Pipe the output to a pager +-q, --quiet Produce quieter output -r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, see ASSET SPECIFIERS below). Can also be used to supply a USD exchange rate for missing assets. From 0c54cb2c974943ef3a2301b3c6f5892af1e32cf8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 19 Jul 2024 09:54:03 +0000 Subject: [PATCH 124/175] Version 3.3.0 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1876a63..15a2799 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.3.dev4 +3.3.0 diff --git a/setup.cfg b/setup.cfg index e3ef90e..1945c49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=14.1.dev21 + mmgen-wallet==14.1.0 pyyaml yahooquery From 1a9184debc2f09205abd13a406619e368d7c54dc Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 20 Jul 2024 16:45:56 +0000 Subject: [PATCH 125/175] version bump --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 15a2799..49cbc5f 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.3.0 +3.4.dev0 diff --git a/setup.cfg b/setup.cfg index 1945c49..def5f65 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet==14.1.0 + mmgen-wallet>=14.1.0 pyyaml yahooquery From c9d6d8f0475f003a7a86f7d5d005ed94495d5bbd Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 22 Sep 2024 16:10:08 +0000 Subject: [PATCH 126/175] Version 3.4.0 - released concurrently with MMGen Wallet v15.0.0 - adds macOS support --- README.md | 6 +++--- mmgen_node_tools/data/keywords | 2 +- mmgen_node_tools/data/version | 2 +- setup.cfg | 16 +++++++++++++--- test/cmdtest_py_d/cfg.py | 7 ++++++- test/cmdtest_py_d/ct_main.py | 2 +- test/init.sh | 13 ++++++++++--- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f72051f..2751da7 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ If installing as user (without venv), make sure that `~/.local/bin` is in `PATH` > $ python3 -m pip install --no-deps yahooquery > ``` -#### Linux: +#### Linux, macOS: -> Install the [required MMGen Wallet packages][7] for your Linux distribution. +> Install some [required packages][7] with your package manager and pip. ### Stable version: @@ -100,5 +100,5 @@ Donate: [5]: https://github.com/mmgen/mmgen-wallet/wiki/MMGen-Signing-Keys [6]: https://github.com/mmgen/mmgen-wallet/ -[7]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-on-Linux +[7]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-Wallet-on-Linux-or-macOS [8]: https://github.com/mmgen/mmgen-wallet/wiki/Install-MMGen-on-Microsoft-Windows#a_m diff --git a/mmgen_node_tools/data/keywords b/mmgen_node_tools/data/keywords index 3c3c563..f26b502 100644 --- a/mmgen_node_tools/data/keywords +++ b/mmgen_node_tools/data/keywords @@ -1 +1 @@ -Bitcoin, BTC, Ethereum, ETH, Monero, XMR, ERC20, cryptocurrency, wallet, BIP32, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MSYS2, MinGW, MinGW64, MSWin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, DASH, Dashpay, SHA256Compress, monerod, EMC, Emercoin, token, deploy, contract, gas, fee, smart contract, solidity, Parity, OpenEthereum, testnet, devmode, Kovan +Bitcoin, BTC, Ethereum, ETH, Monero, XMR, ERC20, cryptocurrency, wallet, cold storage, offline, signing, online, security, privacy, spending, financial, investment, open-source, command-line, Python, Linux, Microsoft Windows, macOS, Bitcoin Core, BIP32, BIP39, BIP44, BIP69, BIP125, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, MSYS2, MSWin, Armbian, Raspbian, Raspberry Pi, Orange Pi, Rock Pi, BCash, Bitcoin Cash Node, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, SHA256Compress, monerod, token, deploy, contract, gas, fee, smart contract, solidity, Parity, OpenEthereum, testnet, devmode, regtest diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 49cbc5f..1809198 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.4.dev0 +3.4.0 diff --git a/setup.cfg b/setup.cfg index def5f65..9a0ab04 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,19 +8,29 @@ author = The MMGen Project author_email = mmgen@tuta.io url = https://github.com/mmgen/mmgen-node-tools license = GNU GPL v3 -platforms = Linux, Armbian, Raspbian, MS Windows +platforms = Linux, Armbian, Raspbian, MS Windows, MacOS keywords = file: mmgen_node_tools/data/keywords project_urls = Website = https://mmgen.org Bug Tracker = https://github.com/mmgen/mmgen-node-tools/issues classifiers = - Programming Language :: Python :: 3 License :: OSI Approved :: GNU General Public License v3 (GPLv3) Operating System :: POSIX :: Linux Operating System :: Microsoft :: Windows + Operating System :: MacOS Environment :: Console + Programming Language :: Python :: 3 + Programming Language :: C + Framework :: AsyncIO + Framework :: aiohttp Topic :: Office/Business :: Financial Topic :: Security :: Cryptography + Topic :: Software Development :: Libraries :: Python Modules + Topic :: Utilities + Intended Audience :: Developers + Intended Audience :: End Users/Desktop + Intended Audience :: Financial and Insurance Industry + Intended Audience :: System Administrators Development Status :: 5 - Production/Stable [options] @@ -28,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=14.1.0 + mmgen-wallet==15.0.0 pyyaml yahooquery diff --git a/test/cmdtest_py_d/cfg.py b/test/cmdtest_py_d/cfg.py index 96bfe9f..ef52880 100755 --- a/test/cmdtest_py_d/cfg.py +++ b/test/cmdtest_py_d/cfg.py @@ -30,4 +30,9 @@ cfgs = { } def fixup_cfgs(): - pass + import os + + for k in cfgs: + cfgs[k]['tmpdir'] = os.path.join('test', 'tmp', str(k)) + +fixup_cfgs() diff --git a/test/cmdtest_py_d/ct_main.py b/test/cmdtest_py_d/ct_main.py index 7c38bcf..d52a2e3 100755 --- a/test/cmdtest_py_d/ct_main.py +++ b/test/cmdtest_py_d/ct_main.py @@ -96,4 +96,4 @@ class CmdTestMain(CmdTestBase): def peerblocks3(self): return self.peerblocks2( ['--columns=80'], - pexpect_spawn = sys.platform != 'win32' ) + pexpect_spawn = sys.platform == 'linux') diff --git a/test/init.sh b/test/init.sh index 51e4e9e..a379adf 100755 --- a/test/init.sh +++ b/test/init.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet # Copyright (C)2013-2022 The MMGen Project @@ -18,6 +18,12 @@ trap 'echo -e "${GREEN}Exiting at user request$RESET"; exit' INT trap 'echo -e "${RED}Node Tools test suite initialization exited with error (line $BASH_LINENO) $RESET"' ERR umask 0022 +for i in '-c' '-f'; do + stat $i %i / >/dev/null 2>&1 && stat_fmt_opt=$i +done + +[ "$stat_fmt_opt" ] || { echo 'No suitable ‘stat’ binary found. Cannot proceed'; exit; } + STDOUT_DEVNULL='>/dev/null' STDERR_DEVNULL='2>/dev/null' @@ -77,6 +83,7 @@ create_test_links() { test/overlay/__init__.py symbolic test/overlay/fakemods/mmgen symbolic test/__init__.py symbolic + test/clean.py symbolic test/cmdtest.py hard test/unit_tests.py hard test/test-release.sh symbolic @@ -87,7 +94,7 @@ create_test_links() { while read path type; do [ "$path" ] || continue pfx=$(echo $path | sed -r 's/[^/]//g' | sed 's/\//..\//g') - symlink_arg=$(if [ $type == 'symbolic' ]; then echo --symbolic; fi) + symlink_arg=$(if [ $type == 'symbolic' ]; then echo -s; fi) target="$wallet_repo/$path" if [ ! -e "$target" ]; then echo "Target path $target is missing! Cannot proceed" @@ -99,7 +106,7 @@ create_test_links() { [ "$VERBOSE" ] && printf "$fs" "Deleting" "symbolic link:" $path $target rm -rf $path elif [ -e $path ]; then - if [ "$(stat --printf=%i $path)" -ne "$(stat --printf=%i $target)" ]; then + if [ "$(stat $stat_fmt_opt %i $path)" -ne "$(stat $stat_fmt_opt %i $target)" ]; then [ "$VERBOSE" ] && printf "$fs" "Deleting" "stale hard link:" $path "?" rm -rf $path fi From 92fdfc047e213aed42fdb7a71c29aef32707404f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 29 Sep 2024 14:29:10 +0000 Subject: [PATCH 127/175] Support BCH cashaddr format --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 6 +++--- setup.cfg | 2 +- test/cmdtest_py_d/ct_regtest.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1809198..3f8a888 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.4.0 +3.5.dev1 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 7af2b77..e78a53e 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -38,7 +38,7 @@ def do_output(proto,addr_data,blk_hdrs): indent = ' ' * (col1w + 2) for n,(addr,unspents) in enumerate(addr_data.items(),1): - Msg(f'\n{n:{col1w}}) Address: {addr.hl()}') + Msg(f'\n{n:{col1w}}) Address: {addr.hl(addr.view_pref)}') if unspents: heights = { u['height'] for u in unspents } @@ -93,7 +93,7 @@ def do_output_tabular(proto,addr_data,blk_hdrs): if unspents: Msg(fs.format( n = str(n) + ')', - a = addr.fmt(width=max_addrw,color=True), + a = addr.fmt(addr.view_pref, width=max_addrw, color=True), u = red(str(len(unspents)).rjust(5)), b = unspents[0]['height'], t = make_timestr( blk_hdrs[unspents[0]['height']]['time'] ), @@ -104,7 +104,7 @@ def do_output_tabular(proto,addr_data,blk_hdrs): else: Msg(fs.format( n = str(n) + ')', - a = addr.fmt(width=max_addrw,color=True), + a = addr.fmt(addr.view_pref, width=max_addrw, color=True), u = ' -', b = '-', t = '', diff --git a/setup.cfg b/setup.cfg index 9a0ab04..875bb01 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet==15.0.0 + mmgen-wallet>=15.1.dev1 pyyaml yahooquery diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index c89f8fa..ee59b31 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -112,7 +112,7 @@ class CmdTestRegtest(CmdTestBase): if cfg._proto.testnet: die(2,'--testnet and --regtest options incompatible with regtest test suite') self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) - self.addrs = gen_addrs(self.proto,'regtest',[1,2,3,4,5]) + self.addrs = [a.views[a.view_pref] for a in gen_addrs(self.proto,'regtest',[1,2,3,4,5])] self.use_bdb_wallet = self.bdb_wallet or self.proto.coin != 'BTC' self.regtest = MMGenRegtest(cfg, self.proto.coin, bdb_wallet=self.use_bdb_wallet) From cb42eaa8cdc602be0d3718d73331b94745558d36 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 8 Oct 2024 13:19:15 +0000 Subject: [PATCH 128/175] update for MMGen Wallet 15.1.dev3 --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 2 +- setup.cfg | 2 +- test/cmdtest_py_d/ct_misc.py | 2 +- test/cmdtest_py_d/ct_regtest.py | 93 +++++++++++++++++---------------- 5 files changed, 53 insertions(+), 48 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 3f8a888..ecc3a64 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev1 +3.5.dev2 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index c3f7325..351cb1e 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -234,7 +234,7 @@ from mmgen.util import fmt_list,fmt_dict from mmgen.cfg import Config from . import Ticker -gcfg = Config( opts_data=opts_data, do_post_init=True ) +gcfg = Config(opts_data=opts_data, caller_post_init=True) Ticker.make_cfg(gcfg) diff --git a/setup.cfg b/setup.cfg index 875bb01..9b82f69 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev1 + mmgen-wallet>=15.1.dev3 pyyaml yahooquery diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 8127e1d..6ac7d37 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -50,7 +50,7 @@ class CmdTestHelp(CmdTestBase): return t def longhelpscreens(self): - return self.helpscreens(arg='--longhelp',expect='USAGE:.*LONG OPTIONS:') + return self.helpscreens(arg='--longhelp',expect='USAGE:.*GLOBAL OPTIONS:') class CmdTestScripts(CmdTestBase): 'scripts not requiring a coin daemon' diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index ee59b31..0807a16 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -35,7 +35,6 @@ class CmdTestRegtest(CmdTestBase): 'various operations via regtest mode' networks = ('btc','ltc','bch') passthru_opts = ('coin',) - extra_spawn_args = ['--regtest=1'] tmpdir_nums = [1] color = True deterministic = False @@ -177,57 +176,57 @@ class CmdTestRegtest(CmdTestBase): def sendto2(self): return self.sendto(self.addrs[0],'0.234') def sendto3(self): return self.sendto(self.addrs[1],'0.345') - def addrbal(self,args,expect_list): - t = self.spawn('mmnode-addrbal',args) + def addrbal(self, args, expect_list): + t = self.spawn('mmnode-addrbal', args2 + args) t.match_expect_list(expect_list) return t def addrbal_single(self): return self.addrbal( - args2 + [self.addrs[0]], + [self.addrs[0]], [ f'Balance: 0.357 {cfg.coin}', '2 unspent outputs in 2 blocks', - '394','0.123', - '395','0.234' + '394', '0.123', + '395', '0.234' ]) def addrbal_multiple(self): return self.addrbal( - args2 + [self.addrs[1],self.addrs[0]], + [self.addrs[1], self.addrs[0]], [ - '396','0.345', - '394','0.123', - '395','0.234' + '396', '0.345', + '394', '0.123', + '395', '0.234' ]) def addrbal_multiple_tabular1(self): return self.addrbal( - args2 + ['--tabular',self.addrs[1],self.addrs[0]], + ['--tabular', self.addrs[1], self.addrs[0]], [ - self.addrs[1] + ' 1 396','0.345', - self.addrs[0] + ' 2 395','0.357' + self.addrs[1] + ' 1 396', '0.345', + self.addrs[0] + ' 2 395', '0.357' ]) def addrbal_multiple_tabular2(self): return self.addrbal( - args2 + ['--tabular','--first-block',self.addrs[1],self.addrs[0]], + ['--tabular', '--first-block', self.addrs[1], self.addrs[0]], [ - self.addrs[1] + ' 1 396','396','0.345', - self.addrs[0] + ' 2 394','395','0.357' + self.addrs[1] + ' 1 396', '396', '0.345', + self.addrs[0] + ' 2 394', '395', '0.357' ]) def addrbal_nobal1(self): return self.addrbal( - args2 + [self.addrs[2]], ['Address has no balance'] ) + [self.addrs[2]], ['Address has no balance']) def addrbal_nobal2(self): return self.addrbal( - args2 + [self.addrs[2],self.addrs[3]], ['Addresses have no balances'] ) + [self.addrs[2], self.addrs[3]], ['Addresses have no balances']) def addrbal_nobal3(self): return self.addrbal( - args2 + [self.addrs[4],self.addrs[0],self.addrs[3]], + [self.addrs[4], self.addrs[0], self.addrs[3]], [ 'No balance', '2 unspent outputs in 2 blocks', @@ -237,7 +236,7 @@ class CmdTestRegtest(CmdTestBase): def addrbal_nobal3_tabular1(self): return self.addrbal( - args2 + ['--tabular',self.addrs[4],self.addrs[0],self.addrs[3]], + ['--tabular', self.addrs[4], self.addrs[0], self.addrs[3]], [ self.addrs[4] + ' - - -', self.addrs[0] + ' 2 395','0.357', @@ -246,7 +245,7 @@ class CmdTestRegtest(CmdTestBase): def addrbal_nobal3_tabular2(self): return self.addrbal( - args2 + ['--tabular','--first-block',self.addrs[4],self.addrs[0],self.addrs[3]], + ['--tabular', '--first-block', self.addrs[4], self.addrs[0], self.addrs[3]], [ self.addrs[4] + ' - - - -', self.addrs[0] + ' 2 394','395','0.357', @@ -254,39 +253,45 @@ class CmdTestRegtest(CmdTestBase): ]) def blocks_info(self,args,expect_list): - t = self.spawn('mmnode-blocks-info',args) + t = self.spawn('mmnode-blocks-info', args1 + args) t.match_expect_list(expect_list) return t def blocks_info1(self): - return self.blocks_info( args1 + ['--help'], ['USAGE:','OPTIONS:']) + return self.blocks_info( + ['--help'], + ['USAGE:','OPTIONS:']) def blocks_info2(self): - return self.blocks_info( args1, [ - 'Current height: 396', - ]) + return self.blocks_info( + [], + ['Current height: 396']) def blocks_info3(self): - return self.blocks_info( args1 + ['+100'], [ - 'Range: 297-396', - 'Current height: 396', - 'Next diff adjust: 2016' - ]) + return self.blocks_info( + ['+100'], + [ + 'Range: 297-396', + 'Current height: 396', + 'Next diff adjust: 2016' + ]) def blocks_info4(self): n1,i1,o1,n2,i2,o2 = (2,1,3,6,3,9) if cfg.coin == 'BCH' else (2,1,4,6,3,12) - return self.blocks_info( args1 + ['--miner-info','--fields=all','--stats=all','+3'], [ - 'Averages', - f'nTx: {n1}', - f'Inputs: {i1}', - f'Outputs: {o1}', - 'Totals', - f'nTx: {n2}', - f'Inputs: {i2}', - f'Outputs: {o2}', - 'Current height: 396', - 'Next diff adjust: 2016' - ]) + return self.blocks_info( + ['--miner-info', '--fields=all', '--stats=all', '+3'], + [ + 'Averages', + f'nTx: {n1}', + f'Inputs: {i1}', + f'Outputs: {o1}', + 'Totals', + f'nTx: {n2}', + f'Inputs: {i2}', + f'Outputs: {o2}', + 'Current height: 396', + 'Next diff adjust: 2016' + ]) async def feeview_setup(self): @@ -380,7 +385,7 @@ class CmdTestRegtest(CmdTestBase): return 'ok' def _feeview(self,args,expect_list=[]): - t = self.spawn('mmnode-feeview',args) + t = self.spawn('mmnode-feeview', args1 + args) if expect_list: t.match_expect_list(expect_list) return t From dee57d88869a8c9c6c22bbf01bc07de35de7545e Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 18 Oct 2024 10:33:47 +0000 Subject: [PATCH 129/175] update for MMGen Wallet CoinAmt changes --- mmgen_node_tools/BlocksInfo.py | 27 +++++++++++++++++++-------- mmgen_node_tools/main_addrbal.py | 4 ++-- test/cmdtest_py_d/ct_regtest.py | 9 +++++---- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 40cc6e5..26e6cfa 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -23,6 +23,7 @@ mmgen_node_tools.BlocksInfo: Display information about a block or range of block import re,json from collections import namedtuple from time import strftime,gmtime +from decimal import Decimal from mmgen.util import msg,Msg,Msg_r,die,suf,secs_to_ms,secs_to_dhms,is_int from mmgen.rpc import json_encoder @@ -241,14 +242,14 @@ class BlocksInfo: 'tf': lambda arg: '{:.8f}'.format(arg * from_satoshi), 'su': lambda arg: str(arg * from_satoshi).rstrip('0').rstrip('.'), 'fe': lambda arg: str(arg), - 'di': lambda arg: '{:.2e}'.format(arg), + 'di': lambda arg: '{:.2e}'.format(Decimal(arg)), } if self.cfg.coin == 'BCH': self.fmt_funcs.update({ 'su': lambda arg: str(arg).rstrip('0').rstrip('.'), - 'fe': lambda arg: str(int(arg * to_satoshi)), - 'tf': lambda arg: '{:.8f}'.format(arg), + 'fe': lambda arg: str(int(Decimal(arg) * to_satoshi)), + 'tf': lambda arg: '{:.8f}'.format(Decimal(arg)), }) self.fnames = tuple( @@ -547,7 +548,7 @@ class BlocksInfo: else: sample_blks = min(min_sample_blks,self.tip) start_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-sample_blks)) - diff_adj = float(tip_hdr['difficulty'] / start_hdr['difficulty']) + diff_adj = Decimal(tip_hdr['difficulty']) / Decimal(start_hdr['difficulty']) time1 = rel_hdr['time'] - start_hdr['time'] time2 = tip_hdr['time'] - rel_hdr['time'] bdi = ((time1 * diff_adj) + time2) / sample_blks @@ -570,17 +571,26 @@ class BlocksInfo: 'sample_blks': ('{}', sample_blks) } ), - ('Cur difficulty: {}', 'cur_diff', '{:.2e}', tip_hdr['difficulty']), + ('Cur difficulty: {}', 'cur_diff', '{:.2e}', Decimal(tip_hdr['difficulty'])), ('Est. diff adjust: {}%', 'est_diff_adjust_pct', '{:+.2f}', ((600 / bdi) - 1) * 100), )) + def sum_field_avg(self, field): + return self.sum_field_total(field) // len(self.res) + + def sum_field_total(self, field): + if isinstance(getattr(self.res[0], field), str): + return sum(Decimal(getattr(block, field)) for block in self.res) + else: + return sum(getattr(block, field) for block in self.res) + async def create_col_avg_stats(self): def gen(): for field in self.fnames: if field in self.avg_stats_skip: yield ( field, ('{}','') ) else: - ret = sum(getattr(block,field) for block in self.res) // len(self.res) + ret = self.sum_field_avg(field) func = self.fields[field].fmt_func yield ( field, ( (self.fmt_funcs[func] if func else '{}'), ret )) if not self.header_printed: @@ -590,9 +600,10 @@ class BlocksInfo: def avg_stats_data(self,data,spec_conv,spec_val): coin = self.rpc.proto.coin + return data( hdr = 'Averages for processed blocks:', - func = lambda field: sum(getattr(block,field) for block in self.res) // len(self.res), + func = self.sum_field_avg, spec_sufs = { 'subsidy': f' {coin}', 'totalfee': f' {coin}' }, spec_convs = { 'interval': spec_conv(0, lambda arg: secs_to_ms(arg)), @@ -616,7 +627,7 @@ class BlocksInfo: coin = self.rpc.proto.coin return data( hdr = 'Totals for processed blocks:', - func = lambda field: sum(getattr(block,field) for block in self.res), + func = self.sum_field_total, spec_sufs = { 'subsidy': f' {coin}', 'totalfee': f' {coin}', 'reward': f' {coin}' }, spec_convs = { 'interval': spec_conv(0, lambda arg: secs_to_dhms(arg)), diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index e78a53e..7dd37a3 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -44,7 +44,7 @@ def do_output(proto,addr_data,blk_hdrs): heights = { u['height'] for u in unspents } Msg('{}Balance: {}'.format( indent, - proto.coin_amt(sum(u['amount'] for u in unspents)).hl2(unit=True,fs='{:,}') )), + sum(proto.coin_amt(u['amount']) for u in unspents).hl2(unit=True, fs='{:,}'))), Msg('{}{} unspent output{} in {} block{}'.format( indent, red(str(len(unspents))), @@ -99,7 +99,7 @@ def do_output_tabular(proto,addr_data,blk_hdrs): t = make_timestr( blk_hdrs[unspents[0]['height']]['time'] ), B = unspents[-1]['height'], T = make_timestr( blk_hdrs[unspents[-1]['height']]['time'] ), - A = proto.coin_amt(sum(u['amount'] for u in unspents)).fmt(color=True,iwidth=7,prec=8) + A = sum(proto.coin_amt(u['amount']) for u in unspents).fmt(color=True, iwidth=7, prec=8) )) else: Msg(fs.format( diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index 0807a16..f1b2774 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -13,6 +13,7 @@ test.cmdtest_py_d.ct_regtest: Regtest tests for the cmdtest.py test suite """ import sys,os +from decimal import Decimal from mmgen.util import msg_r,die,gmsg from mmgen.protocol import init_proto @@ -316,13 +317,13 @@ class CmdTestRegtest(CmdTestBase): # very approximate tx size estimation: ibytes,wbytes,obytes = (148,0,34) if self.proto.coin == 'BCH' else (43,108,31) - x = (ibytes + (wbytes//4) + (obytes * nPairs)) * self.proto.coin_amt(self.proto.coin_amt.satoshi) + x = (ibytes + (wbytes//4) + (obytes * nPairs)) * self.proto.coin_amt.satoshi n = n_in - 1 vmax = high - low for i in range(n_in): - yield (low + (i/n)**6 * vmax) * x + yield Decimal(low + (i/n)**6 * vmax) * x async def do_tx(inputs,outputs,wif): tx_hex = await r.rpc_call( 'createrawtransaction', inputs, outputs ) @@ -335,14 +336,14 @@ class CmdTestRegtest(CmdTestBase): tx_input = us[7] # 25 BTC in coinbase -- us[0] could have < 25 BTC fee = self.proto.coin_amt('0.001') outputs = {p.addr:tx1_amt for p in pairs[:nTxs]} - outputs.update({burn_addr: tx_input['amount'] - (tx1_amt*nTxs) - fee}) + outputs.update({burn_addr: self.proto.coin_amt(tx_input['amount']) - (tx1_amt*nTxs) - fee}) return await do_tx( [{ 'txid': tx_input['txid'], 'vout': 0 }], outputs, await r.miner_wif) async def do_tx2(tx,pairno): - fee = fees[pairno] + fee = self.proto.coin_amt(fees[pairno], from_decimal=True) outputs = {p.addr:tx2_amt for p in pairs} outputs.update({burn_addr: tx1_amt - (tx2_amt*len(pairs)) - fee}) return await do_tx( From b9b289c56db227cfc42e541dffc7767bce129c78 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 18 Oct 2024 10:33:48 +0000 Subject: [PATCH 130/175] directory rename: - test/cmdtest_py_d -> test/cmdtest_d --- MANIFEST.in | 2 +- test/{cmdtest_py_d => cmdtest_d}/cfg.py | 2 +- test/{cmdtest_py_d => cmdtest_d}/ct_main.py | 2 +- test/{cmdtest_py_d => cmdtest_d}/ct_misc.py | 2 +- test/{cmdtest_py_d => cmdtest_d}/ct_regtest.py | 2 +- test/init.sh | 4 ++-- test/test-release.d/cfg.sh | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) rename test/{cmdtest_py_d => cmdtest_d}/cfg.py (93%) rename test/{cmdtest_py_d => cmdtest_d}/ct_main.py (96%) rename test/{cmdtest_py_d => cmdtest_d}/ct_misc.py (99%) rename test/{cmdtest_py_d => cmdtest_d}/ct_regtest.py (99%) diff --git a/MANIFEST.in b/MANIFEST.in index 370da07..1fe78fa 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,6 @@ include mmgen_node_tools/data/* include test/init.sh include test/test-release.d/*.sh include test/unit_tests_d/*.py -include test/cmdtest_py_d/*.py +include test/cmdtest_d/*.py include test/overlay/fakemods/mmgen_node_tools/*.py include test/ref/*/* diff --git a/test/cmdtest_py_d/cfg.py b/test/cmdtest_d/cfg.py similarity index 93% rename from test/cmdtest_py_d/cfg.py rename to test/cmdtest_d/cfg.py index ef52880..c28ce5d 100755 --- a/test/cmdtest_py_d/cfg.py +++ b/test/cmdtest_d/cfg.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.cmdtest_py_d.cfg: configuration data for cmdtest.py +test.cmdtest_d.cfg: configuration data for cmdtest.py """ cmd_groups_altcoin = [] diff --git a/test/cmdtest_py_d/ct_main.py b/test/cmdtest_d/ct_main.py similarity index 96% rename from test/cmdtest_py_d/ct_main.py rename to test/cmdtest_d/ct_main.py index d52a2e3..3009d2f 100755 --- a/test/cmdtest_py_d/ct_main.py +++ b/test/cmdtest_d/ct_main.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-wallet """ -cmdtest_py_d.ct_main: Basic operations tests for the cmdtest.py test suite +cmdtest_d.ct_main: Basic operations tests for the cmdtest.py test suite """ import sys,time diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_d/ct_misc.py similarity index 99% rename from test/cmdtest_py_d/ct_misc.py rename to test/cmdtest_d/ct_misc.py index 6ac7d37..15b109e 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_d/ct_misc.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.cmdtest_py_d.ct_misc: Miscellaneous test groups for the cmdtest.py test suite +test.cmdtest_d.ct_misc: Miscellaneous test groups for the cmdtest.py test suite """ import os,shutil diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_d/ct_regtest.py similarity index 99% rename from test/cmdtest_py_d/ct_regtest.py rename to test/cmdtest_d/ct_regtest.py index f1b2774..24f19b9 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_d/ct_regtest.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.cmdtest_py_d.ct_regtest: Regtest tests for the cmdtest.py test suite +test.cmdtest_d.ct_regtest: Regtest tests for the cmdtest.py test suite """ import sys,os diff --git a/test/init.sh b/test/init.sh index a379adf..8f77c82 100755 --- a/test/init.sh +++ b/test/init.sh @@ -87,8 +87,8 @@ create_test_links() { test/cmdtest.py hard test/unit_tests.py hard test/test-release.sh symbolic - test/cmdtest_py_d/common.py symbolic - test/cmdtest_py_d/ct_base.py symbolic + test/cmdtest_d/common.py symbolic + test/cmdtest_d/ct_base.py symbolic cmds/mmgen-regtest symbolic ' while read path type; do diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index b371e72..7a393df 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -42,7 +42,7 @@ init_tests() { t_lint=" - $pylint --errors-only mmgen_node_tools - $pylint --errors-only test - - $pylint --errors-only --disable=relative-beyond-top-level test/cmdtest_py_d + - $pylint --errors-only --disable=relative-beyond-top-level test/cmdtest_d " d_unit="low-level subsystems" From 30772b369999c6b5deae7d4ea653d46cc09680ed Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 18 Oct 2024 10:33:48 +0000 Subject: [PATCH 131/175] Update for MMGen Wallet 15.1.dev6 --- MANIFEST.in | 2 +- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/init.sh | 8 +++++++- test/{unit_tests_d => modtest_d}/ut_BlocksInfo.py | 0 test/{unit_tests_d => modtest_d}/ut_dep.py | 0 test/test-release.d/cfg.sh | 12 ++++++------ 7 files changed, 16 insertions(+), 10 deletions(-) rename test/{unit_tests_d => modtest_d}/ut_BlocksInfo.py (100%) rename test/{unit_tests_d => modtest_d}/ut_dep.py (100%) diff --git a/MANIFEST.in b/MANIFEST.in index 1fe78fa..0443a99 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,7 +4,7 @@ include mmgen_node_tools/data/* include test/init.sh include test/test-release.d/*.sh -include test/unit_tests_d/*.py +include test/modtest_d/*.py include test/cmdtest_d/*.py include test/overlay/fakemods/mmgen_node_tools/*.py include test/ref/*/* diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index ecc3a64..f19fa82 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev2 +3.5.dev3 diff --git a/setup.cfg b/setup.cfg index 9b82f69..edbad57 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev3 + mmgen-wallet>=15.1.dev6 pyyaml yahooquery diff --git a/test/init.sh b/test/init.sh index 8f77c82..f6a6f00 100755 --- a/test/init.sh +++ b/test/init.sh @@ -77,6 +77,10 @@ create_dir_links() { done } +delete_old_stuff() { + rm -rf test/unit_tests.py +} + create_test_links() { paths=' test/include symbolic @@ -85,7 +89,7 @@ create_test_links() { test/__init__.py symbolic test/clean.py symbolic test/cmdtest.py hard - test/unit_tests.py hard + test/modtest.py hard test/test-release.sh symbolic test/cmdtest_d/common.py symbolic test/cmdtest_d/ct_base.py symbolic @@ -121,6 +125,8 @@ create_test_links() { becho 'Initializing MMGen Node Tools Test Suite' +delete_old_stuff + check_mmgen_repo || die "MMGen Wallet repository not found at $wallet_repo!" build_mmgen_extmod diff --git a/test/unit_tests_d/ut_BlocksInfo.py b/test/modtest_d/ut_BlocksInfo.py similarity index 100% rename from test/unit_tests_d/ut_BlocksInfo.py rename to test/modtest_d/ut_BlocksInfo.py diff --git a/test/unit_tests_d/ut_dep.py b/test/modtest_d/ut_dep.py similarity index 100% rename from test/unit_tests_d/ut_dep.py rename to test/modtest_d/ut_dep.py diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 7a393df..a34c324 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -18,7 +18,7 @@ # mmnode-ticker OK # mmnode-txfind - -all_tests='unit lint misc scripts btc btc_rt bch_rt ltc_rt' +all_tests='mod lint misc scripts btc btc_rt bch_rt ltc_rt' groups_desc=" default - All tests minus the extra tests @@ -29,10 +29,10 @@ groups_desc=" " init_groups() { - dfl_tests='unit misc scripts btc btc_rt bch_rt ltc_rt' + dfl_tests='mod misc scripts btc btc_rt bch_rt ltc_rt' extra_tests='lint' - noalt_tests='unit misc scripts btc btc_rt' - quick_tests='unit misc scripts btc btc_rt' + noalt_tests='mod misc scripts btc btc_rt' + quick_tests='mod misc scripts btc btc_rt' qskip_tests='lint bch_rt ltc_rt' } @@ -45,8 +45,8 @@ init_tests() { - $pylint --errors-only --disable=relative-beyond-top-level test/cmdtest_d " - d_unit="low-level subsystems" - t_unit="- $unit_tests_py" + d_mod="low-level subsystems" + t_mod="- $modtest_py" d_misc="miscellaneous features" t_misc="- $cmdtest_py helpscreens" From bec7df1d6b5b3ea342fc28292ceaf83dc7498054 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 3 Jan 2025 16:42:11 +0300 Subject: [PATCH 132/175] support Nix and NixOS Quick Start for BTC: $ git clone https://github.com/mmgen/mmgen-wallet $ git clone https://github.com/mmgen/mmgen-node-tools $ cd mmgen-node-tools $ test/init.sh $ nix-shell --pure nix Enable altcoins and additional packages: # From the mmgen-node-tools repository root: $ mkdir -p ~/.mmgen $ cp ../mmgen-wallet/nix/user-packages.nix ~/.mmgen # ... edit ~/.mmgen/user-packages.nix as required ... $ nix-shell --pure nix For NixOS installation and other information, see: nix/README.node-tools ../mmgen-wallet/nix/README --- mmgen_node_tools/data/version | 2 +- nix/README.node-tools | 11 ++++++++ nix/default.nix | 6 +++++ nix/node-tools-packages.nix | 12 +++++++++ nix/shell.nix | 1 + nix/yahooquery-noversioning.patch | 45 +++++++++++++++++++++++++++++++ nix/yahooquery.nix | 44 ++++++++++++++++++++++++++++++ setup.cfg | 2 +- 8 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 nix/README.node-tools create mode 100644 nix/default.nix create mode 100644 nix/node-tools-packages.nix create mode 100644 nix/shell.nix create mode 100644 nix/yahooquery-noversioning.patch create mode 100644 nix/yahooquery.nix diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index f19fa82..cf8acd2 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev3 +3.5.dev4 diff --git a/nix/README.node-tools b/nix/README.node-tools new file mode 100644 index 0000000..9308181 --- /dev/null +++ b/nix/README.node-tools @@ -0,0 +1,11 @@ +Nix configuration directory for the MMGen Node Tools suite + +Usage is as described in ‘nix/README’ in the mmgen-wallet repository, with the +following differences: + + a) all commands are executed from the repository root of mmgen-node-tools + instead of mmgen-wallet + + b) for NixOS, complete the steps as described up until the rebuild step. + Copy the contents of this directory to ‘/etc/nixos/mmgen-project’ (this + will overwrite ‘default.nix’), and continue with the rebuild step. diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 0000000..7b4b214 --- /dev/null +++ b/nix/default.nix @@ -0,0 +1,6 @@ +import ( + if builtins.pathExists ./merged-packages.nix then + ./merged-packages.nix + else + ../../mmgen-wallet/nix/merged-packages.nix + ) { add_pkgs_path = ./node-tools-packages.nix; } diff --git a/nix/node-tools-packages.nix b/nix/node-tools-packages.nix new file mode 100644 index 0000000..19792c7 --- /dev/null +++ b/nix/node-tools-packages.nix @@ -0,0 +1,12 @@ +{ pkgs, python }: + +{ + system-packages = with pkgs; { + cacert = cacert; # ticker (curl) + }; + + python-packages = with python.pkgs; { + yahooquery = (pkgs.callPackage ./yahooquery.nix {}); # ticker + pyyaml = pyyaml; # ticker + }; +} diff --git a/nix/shell.nix b/nix/shell.nix new file mode 100644 index 0000000..01991d2 --- /dev/null +++ b/nix/shell.nix @@ -0,0 +1 @@ +import ../../mmgen-wallet/nix/shell.nix { add_pkgs_path = ./node-tools-packages.nix; } diff --git a/nix/yahooquery-noversioning.patch b/nix/yahooquery-noversioning.patch new file mode 100644 index 0000000..4bd3297 --- /dev/null +++ b/nix/yahooquery-noversioning.patch @@ -0,0 +1,45 @@ +diff --git a/pyproject.toml b/pyproject.toml +index 9d3fb29..399c215 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -10,24 +10,24 @@ readme = "README.md" + + [tool.poetry.dependencies] + python = ">=3.8.1,<4.0" +-requests = "^2.31.0" +-pandas = "^2.0.3" +-requests-futures = "^1.0.1" +-tqdm = "^4.65.0" +-lxml = "^4.9.3" +-selenium = {version = "^4.10.0", optional = true} +-beautifulsoup4 = "^4.12.2" ++requests = ">=2.31.0" ++pandas = ">=2.0.3" ++requests-futures = ">=1.0.1" ++tqdm = ">=4.65.0" ++lxml = ">=4.9.3" ++selenium = {version = ">=4.10.0", optional = true} ++beautifulsoup4 = ">=4.12.2" + + [tool.poetry.dev-dependencies] +-pytest = "^7.4.0" +-isort = "^5.0.0" +-flake8 = "^6.0.0" +-mypy = "^1.4.1" +-pytest-cov = "^4.1.0" +-black = "^23.7.0" +-pre-commit = "^3.3.3" +-ipython = "^8.0.0" +-mkdocs-material = "^9.1.18" ++pytest = ">=7.4.0" ++isort = ">=5.0.0" ++flake8 = ">=6.0.0" ++mypy = ">=1.4.1" ++pytest-cov = ">=4.1.0" ++black = ">=23.7.0" ++pre-commit = ">=3.3.3" ++ipython = ">=8.0.0" ++mkdocs-material = ">=9.1.18" + + [build-system] + requires = ["poetry-core>=1.0.0"] diff --git a/nix/yahooquery.nix b/nix/yahooquery.nix new file mode 100644 index 0000000..8b1535d --- /dev/null +++ b/nix/yahooquery.nix @@ -0,0 +1,44 @@ +{ + lib, + pkgs, + fetchFromGitHub, +}: + +with pkgs.python312.pkgs; + +buildPythonPackage rec { + pname = "yahooquery"; + version = "2.3.7"; + pyproject = true; + + disabled = pythonOlder "3.8.1"; + + src = fetchFromGitHub { + owner = "dpguthrie"; + repo = "yahooquery"; + rev = "refs/tags/v${version}"; + hash = "sha256-Iyuni1SoTB6f7nNFhN5A8Gnv9kV78frjpqvvW8qd+/M="; + }; + + patches = [ ./yahooquery-noversioning.patch ]; + + build-system = [ poetry-core ]; + + dependencies = [ + requests # ^2.31.0 + pandas # ^2.0.3 + requests-futures # ^1.0.1 + tqdm # ^4.65.0 + lxml # ^4.9.3 + selenium # {version = ^4.10.0, optional = true} + beautifulsoup4 # ^4.12.2 + ]; + + doCheck = false; # skip tests + + meta = with lib; { + description = "Python wrapper for an unofficial Yahoo Finance API"; + homepage = "https://yahooquery.dpguthrie.com"; + license = licenses.mit; + }; +} diff --git a/setup.cfg b/setup.cfg index edbad57..f63dd91 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev6 + mmgen-wallet>=15.1.dev9 pyyaml yahooquery From 7cb2fc5b08722b3a2ac1436fb9ce2d7b4fd12852 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 16 Jan 2025 11:06:28 +0000 Subject: [PATCH 133/175] nix/shell.nix: add `repo` arg; test/init.sh: add `-c` option --- mmgen_node_tools/data/version | 2 +- nix/shell.nix | 5 ++++- setup.cfg | 2 +- test/init.sh | 17 ++++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index cf8acd2..c087ce9 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev4 +3.5.dev5 diff --git a/nix/shell.nix b/nix/shell.nix index 01991d2..acfdee3 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1 +1,4 @@ -import ../../mmgen-wallet/nix/shell.nix { add_pkgs_path = ./node-tools-packages.nix; } +import ../../mmgen-wallet/nix/shell.nix { + repo = "mmgen-node-tools"; + add_pkgs_path = ./node-tools-packages.nix; +} diff --git a/setup.cfg b/setup.cfg index f63dd91..db836e8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev9 + mmgen-wallet>=15.1.dev10 pyyaml yahooquery diff --git a/test/init.sh b/test/init.sh index f6a6f00..15eeddb 100755 --- a/test/init.sh +++ b/test/init.sh @@ -28,15 +28,17 @@ STDOUT_DEVNULL='>/dev/null' STDERR_DEVNULL='2>/dev/null' PROGNAME=$(basename $0) -while getopts hv OPT +while getopts hcv OPT do case "$OPT" in h) printf " %-16s Initialize the MMGen Node Tools test suite\n" "${PROGNAME}:" echo " USAGE: $PROGNAME" echo " OPTIONS: '-h' Print this help message" + echo " -c Create links from mmgen-wallet ‘cmds’ subdirectory" echo " -v Be more verbose" exit ;; v) VERBOSE=1 STDOUT_DEVNULL='' STDERR_DEVNULL='' ;; + c) CMD_LINKS=1 ;; *) exit ;; esac done @@ -123,6 +125,17 @@ create_test_links() { done <<<$paths } +create_cmd_links() { + [ "$VERBOSE" ] && becho 'Creating links to mmgen-wallet repo ‘cmds’ subdirectory' + ( + filenames=$(cd $wallet_repo/cmds && ls) + cd cmds + for filename in $filenames; do + [ -e $filename ] || ln -s "../$wallet_repo/cmds/$filename" + done + ) +} + becho 'Initializing MMGen Node Tools Test Suite' delete_old_stuff @@ -137,6 +150,8 @@ create_dir_links create_test_links +[ "$CMD_LINKS" ] && create_cmd_links + [ "$VERBOSE" ] && becho 'OK' true From b62ff991fe030448c52440f5ccf722b070ed2d41 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 6 Feb 2025 10:07:57 +0000 Subject: [PATCH 134/175] update for MMGen Wallet v15.1.dev14 --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 6 +++--- setup.cfg | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index c087ce9..426110c 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev5 +3.5.dev6 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 7dd37a3..7fafac1 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -142,10 +142,10 @@ async def main(req_addrs): addr = re.match('addr\((.*?)\)',unspent['desc'])[1] addr_data[addr].append(unspent) else: - from mmgen.proto.btc.tx.base import scriptPubKey2addr + from mmgen.proto.btc.tx.base import decodeScriptPubKey for unspent in sorted(res['unspents'],key=lambda x: x['height']): - addr = scriptPubKey2addr( proto, unspent['scriptPubKey'] )[0] - addr_data[addr].append(unspent) + ds = decodeScriptPubKey(proto, unspent['scriptPubKey']) + addr_data[ds.addr].append(unspent) good_addrs = len([v for v in addr_data.values() if v]) diff --git a/setup.cfg b/setup.cfg index db836e8..e140080 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev10 + mmgen-wallet>=15.1.dev14 pyyaml yahooquery From 966c1716505f63829bb70c063560e0d3b1cff67f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 15 Mar 2025 18:23:16 +0000 Subject: [PATCH 135/175] update for MMGen Wallet v15.1.dev20 --- README.md | 2 +- mmgen_node_tools/Ticker.py | 14 ++++++++------ mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 8 ++++---- mmgen_node_tools/main_feeview.py | 2 +- mmgen_node_tools/main_halving_calculator.py | 8 ++++---- setup.cfg | 2 +- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 2751da7..89c43b2 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Code repository: Code repository mirrors: [Github](https://github.com/mmgen/mmgen-node-tools) | [Gitlab](https://gitlab.com/mmgen/mmgen-node-tools) | -[Gitflic](https://gitflic.ru/project/mmgen/mmgen-node-tools) +[Codeberg](https://codeberg.org/mmgen/mmgen-node-tools) [Keybase](https://keybase.io/mmgen) | [Twitter](https://twitter.com/TheMMGenProject) | [Reddit](https://www.reddit.com/user/mmgen-py) | diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index bae27c2..252ba6c 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -799,7 +799,7 @@ class Ticker: from mmgen.util2 import format_elapsed_hr fmt_func = format_elapsed_hr else: - fmt_func = lambda t,now: time.strftime('%F %X',time.gmtime(t)) + fmt_func = lambda t,now: time.strftime('%F %X', time.gmtime(t)) d = self.data max_w = 0 @@ -820,15 +820,17 @@ class Ticker: msg(str(e)) pass else: - t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( (min(t,min_t) if min_t else t), now ) - max_w = max(len(t_fmt),max_w) + t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( + (min(t,min_t) if min_t else t), + now = now) + max_w = max(len(t_fmt), max_w) self.upd_w = max_w def init_prec(self): - exp = [(a.id, self.prices[a.id]['usd-us-dollar'].adjusted() ) for a in self.usr_col_assets] - self.uprec = { k: max(0,v+4) + cfg.add_prec for k,v in exp } - self.uwid = { k: 12 + max(0, abs(v)-6) + cfg.add_prec for k,v in exp } + exp = [(a.id, self.prices[a.id]['usd-us-dollar'].adjusted()) for a in self.usr_col_assets] + self.uprec = {k: max(0, v+4) + cfg.add_prec for k, v in exp} + self.uwid = {k: 12 + max(0, abs(v)-6) + cfg.add_prec for k, v in exp} def get_id(self,asset): if asset.id: diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 426110c..aa6a660 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev6 +3.5.dev7 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 7fafac1..ecf2b1a 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -60,7 +60,7 @@ def do_output(proto,addr_data,blk_hdrs): make_timestr( blk_hdrs[u['height']]['time'] ), CoinTxID(u['txid']).hl(), red(str(u['vout']).rjust(4)), - proto.coin_amt(u['amount']).fmt(color=True,iwidth=6,prec=8) + proto.coin_amt(u['amount']).fmt(6, color=True, prec=8) )) else: Msg(f'{indent}No balance') @@ -93,18 +93,18 @@ def do_output_tabular(proto,addr_data,blk_hdrs): if unspents: Msg(fs.format( n = str(n) + ')', - a = addr.fmt(addr.view_pref, width=max_addrw, color=True), + a = addr.fmt(addr.view_pref, max_addrw, color=True), u = red(str(len(unspents)).rjust(5)), b = unspents[0]['height'], t = make_timestr( blk_hdrs[unspents[0]['height']]['time'] ), B = unspents[-1]['height'], T = make_timestr( blk_hdrs[unspents[-1]['height']]['time'] ), - A = sum(proto.coin_amt(u['amount']) for u in unspents).fmt(color=True, iwidth=7, prec=8) + A = sum(proto.coin_amt(u['amount']) for u in unspents).fmt(7, color=True, prec=8) )) else: Msg(fs.format( n = str(n) + ')', - a = addr.fmt(addr.view_pref, width=max_addrw, color=True), + a = addr.fmt(addr.view_pref, max_addrw, color=True), u = ' -', b = '-', t = '', diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 7bee773..16591b8 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -87,7 +87,7 @@ if cfg.ignore_below: ignore_below = parse_bytespec(cfg.ignore_below) precision = ( - check_int_between(cfg.precision,min_prec,max_prec,'--precision arg') + check_int_between(cfg.precision, min_prec, max_prec, desc='--precision arg') if cfg.precision else dfl_prec ) from mmgen.term import get_terminal_size diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index 1648d76..caeb178 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -171,10 +171,10 @@ async def main(): c = date(t), d = ' P' if n > nhist else '' if n < nhist else ' E', e = f'{bdr:8.5f}', - f = proto.coin_amt(sub,from_unit='satoshi').fmt(iwidth=2,prec=8), - g = proto.coin_amt(mined,from_unit='satoshi').fmt(iwidth=8,prec=8), - h = proto.coin_amt(total_mined,from_unit='satoshi').fmt(iwidth=8,prec=8) - ) for n,sub,blk,mined,total_mined,bdr,t in gen_data()) + f = proto.coin_amt(sub, from_unit='satoshi').fmt(2, prec=8), + g = proto.coin_amt(mined, from_unit='satoshi').fmt(8, prec=8), + h = proto.coin_amt(total_mined, from_unit='satoshi').fmt(8, prec=8) + ) for n, sub, blk, mined, total_mined, bdr, t in gen_data()) ) if cfg.list: diff --git a/setup.cfg b/setup.cfg index e140080..b43abad 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev14 + mmgen-wallet>=15.1.dev20 pyyaml yahooquery From d63cb9817a249618931f526e9d5029bfe45adb0d Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 23 Mar 2025 10:12:56 +0000 Subject: [PATCH 136/175] update for MMGen Wallet v15.1.dev23 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/cmdtest_d/ct_regtest.py | 4 ++-- test/init.sh | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index aa6a660..1e3f513 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev7 +3.5.dev8 diff --git a/setup.cfg b/setup.cfg index b43abad..c67cb47 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev20 + mmgen-wallet>=15.1.dev23 pyyaml yahooquery diff --git a/test/cmdtest_d/ct_regtest.py b/test/cmdtest_d/ct_regtest.py index 24f19b9..f043641 100755 --- a/test/cmdtest_d/ct_regtest.py +++ b/test/cmdtest_d/ct_regtest.py @@ -105,8 +105,8 @@ class CmdTestRegtest(CmdTestBase): ), } - def __init__(self,trunner,cfgs,spawn): - CmdTestBase.__init__(self,trunner,cfgs,spawn) + def __init__(self, cfg, trunner, cfgs, spawn): + CmdTestBase.__init__(self, cfg, trunner, cfgs, spawn) if trunner == None: return if cfg._proto.testnet: diff --git a/test/init.sh b/test/init.sh index 15eeddb..94671f9 100755 --- a/test/init.sh +++ b/test/init.sh @@ -95,6 +95,8 @@ create_test_links() { test/test-release.sh symbolic test/cmdtest_d/common.py symbolic test/cmdtest_d/ct_base.py symbolic + test/cmdtest_d/runner.py symbolic + test/cmdtest_d/group_mgr.py symbolic cmds/mmgen-regtest symbolic ' while read path type; do From 4688ee94c0dfa1a73165e72a520182e724e47c47 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 29 Mar 2025 12:12:35 +0000 Subject: [PATCH 137/175] update test suite for MMGen Wallet v15.1.dev25 --- README.md | 3 +- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/cmdtest_d/{ => include}/cfg.py | 2 +- test/cmdtest_d/{ct_main.py => main.py} | 6 ++-- test/cmdtest_d/{ct_misc.py => misc.py} | 6 ++-- test/cmdtest_d/{ct_regtest.py => regtest.py} | 10 +++---- test/init.sh | 31 ++++++++++++-------- 8 files changed, 34 insertions(+), 28 deletions(-) rename test/cmdtest_d/{ => include}/cfg.py (93%) rename test/cmdtest_d/{ct_main.py => main.py} (94%) rename test/cmdtest_d/{ct_misc.py => misc.py} (98%) rename test/cmdtest_d/{ct_regtest.py => regtest.py} (98%) diff --git a/README.md b/README.md index 89c43b2..a536cce 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ $ python3 -m pip install --upgrade mmgen-node-tools ### Development version: -Install the latest development version of [MMGen Wallet][6] for your platform: +First install the latest development version of [MMGen Wallet][6] for your +platform. Then perform the following steps: ```bash $ git clone https://github.com/mmgen/mmgen-node-tools diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1e3f513..0602083 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev8 +3.5.dev9 diff --git a/setup.cfg b/setup.cfg index c67cb47..905e659 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev23 + mmgen-wallet>=15.1.dev25 pyyaml yahooquery diff --git a/test/cmdtest_d/cfg.py b/test/cmdtest_d/include/cfg.py similarity index 93% rename from test/cmdtest_d/cfg.py rename to test/cmdtest_d/include/cfg.py index c28ce5d..5a4263d 100755 --- a/test/cmdtest_d/cfg.py +++ b/test/cmdtest_d/include/cfg.py @@ -9,7 +9,7 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.cmdtest_d.cfg: configuration data for cmdtest.py +test.cmdtest_d.include.cfg: configuration data for cmdtest.py """ cmd_groups_altcoin = [] diff --git a/test/cmdtest_d/ct_main.py b/test/cmdtest_d/main.py similarity index 94% rename from test/cmdtest_d/ct_main.py rename to test/cmdtest_d/main.py index 3009d2f..6e35b7d 100755 --- a/test/cmdtest_d/ct_main.py +++ b/test/cmdtest_d/main.py @@ -9,13 +9,13 @@ # https://gitlab.com/mmgen/mmgen-wallet """ -cmdtest_d.ct_main: Basic operations tests for the cmdtest.py test suite +cmdtest_d.main: Basic operations tests for the cmdtest.py test suite """ -import sys,time +import sys, time from ..include.common import cfg -from .ct_base import CmdTestBase +from .base import CmdTestBase class CmdTestMain(CmdTestBase): 'basic operations with fake RPC data' diff --git a/test/cmdtest_d/ct_misc.py b/test/cmdtest_d/misc.py similarity index 98% rename from test/cmdtest_d/ct_misc.py rename to test/cmdtest_d/misc.py index 15b109e..1a8175e 100755 --- a/test/cmdtest_d/ct_misc.py +++ b/test/cmdtest_d/misc.py @@ -9,13 +9,13 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.cmdtest_d.ct_misc: Miscellaneous test groups for the cmdtest.py test suite +test.cmdtest_d.misc: Miscellaneous test groups for the cmdtest.py test suite """ -import os,shutil +import os, shutil from ..include.common import cfg -from .ct_base import CmdTestBase +from .base import CmdTestBase refdir = os.path.join('test','ref','ticker') diff --git a/test/cmdtest_d/ct_regtest.py b/test/cmdtest_d/regtest.py similarity index 98% rename from test/cmdtest_d/ct_regtest.py rename to test/cmdtest_d/regtest.py index f043641..3b77836 100755 --- a/test/cmdtest_d/ct_regtest.py +++ b/test/cmdtest_d/regtest.py @@ -9,18 +9,18 @@ # https://gitlab.com/mmgen/mmgen-node-tools """ -test.cmdtest_d.ct_regtest: Regtest tests for the cmdtest.py test suite +test.cmdtest_d.regtest: Regtest tests for the cmdtest.py test suite """ -import sys,os +import sys, os from decimal import Decimal -from mmgen.util import msg_r,die,gmsg +from mmgen.util import msg_r, die, gmsg from mmgen.protocol import init_proto from mmgen.proto.btc.regtest import MMGenRegtest -from ..include.common import cfg,imsg,stop_test_daemons,joinpath -from .ct_base import CmdTestBase +from ..include.common import cfg, imsg, stop_test_daemons, joinpath +from .base import CmdTestBase args1 = ['--bob'] args2 = ['--bob','--rpc-backend=http'] diff --git a/test/init.sh b/test/init.sh index 94671f9..134f4f3 100755 --- a/test/init.sh +++ b/test/init.sh @@ -81,23 +81,28 @@ create_dir_links() { delete_old_stuff() { rm -rf test/unit_tests.py + rm -rf test/cmdtest_d/common.py + rm -rf test/cmdtest_d/ct_base.py + rm -rf test/cmdtest_d/group_mgr.py + rm -rf test/cmdtest_d/runner.py } create_test_links() { paths=' - test/include symbolic - test/overlay/__init__.py symbolic - test/overlay/fakemods/mmgen symbolic - test/__init__.py symbolic - test/clean.py symbolic - test/cmdtest.py hard - test/modtest.py hard - test/test-release.sh symbolic - test/cmdtest_d/common.py symbolic - test/cmdtest_d/ct_base.py symbolic - test/cmdtest_d/runner.py symbolic - test/cmdtest_d/group_mgr.py symbolic - cmds/mmgen-regtest symbolic + test/include symbolic + test/overlay/__init__.py symbolic + test/overlay/fakemods/mmgen symbolic + test/__init__.py symbolic + test/clean.py symbolic + test/cmdtest.py hard + test/modtest.py hard + test/test-release.sh symbolic + test/cmdtest_d/base.py symbolic + test/cmdtest_d/include/common.py symbolic + test/cmdtest_d/include/runner.py symbolic + test/cmdtest_d/include/group_mgr.py symbolic + test/cmdtest_d/include/pexpect.py symbolic + cmds/mmgen-regtest symbolic ' while read path type; do [ "$path" ] || continue From ab93f54d541d4ee6511f9bf37e703d50fc2745f5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 30 Mar 2025 10:18:52 +0000 Subject: [PATCH 138/175] update test suite for MMGen Wallet v15.1.dev26 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 0602083..de6f13c 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev9 +3.5.dev10 diff --git a/setup.cfg b/setup.cfg index 905e659..0ab2734 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev25 + mmgen-wallet>=15.1.dev26 pyyaml yahooquery From 106f201300d7dadb3a70e979db9373e9f6ca0e34 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 24 May 2025 10:10:24 +0000 Subject: [PATCH 139/175] update for MMGen Wallet v15.1.dev39 --- MANIFEST.in | 1 + mmgen_node_tools/BlocksInfo.py | 2 +- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_feeview.py | 2 +- setup.cfg | 3 +-- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0443a99..77b53ac 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,5 +6,6 @@ include test/init.sh include test/test-release.d/*.sh include test/modtest_d/*.py include test/cmdtest_d/*.py +include test/cmdtest_d/include/cfg.py include test/overlay/fakemods/mmgen_node_tools/*.py include test/ref/*/* diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 26e6cfa..f19089a 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -26,7 +26,7 @@ from time import strftime,gmtime from decimal import Decimal from mmgen.util import msg,Msg,Msg_r,die,suf,secs_to_ms,secs_to_dhms,is_int -from mmgen.rpc import json_encoder +from mmgen.rpc.util import json_encoder class RangeParser: diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index de6f13c..70beee3 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev10 +3.5.dev11 diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 16591b8..e7b9952 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -103,7 +103,7 @@ class fee_bracket: def log(data,fn): import json - from mmgen.rpc import json_encoder + from mmgen.rpc.util import json_encoder from mmgen.fileutil import write_data_to_file write_data_to_file( cfg = cfg, diff --git a/setup.cfg b/setup.cfg index 0ab2734..cd27742 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,6 @@ project_urls = Website = https://mmgen.org Bug Tracker = https://github.com/mmgen/mmgen-node-tools/issues classifiers = - License :: OSI Approved :: GNU General Public License v3 (GPLv3) Operating System :: POSIX :: Linux Operating System :: Microsoft :: Windows Operating System :: MacOS @@ -38,7 +37,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev26 + mmgen-wallet>=15.1.dev39 pyyaml yahooquery From 5a24cbfc0a50d03203710c387763a1b893f3d5f3 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 24 Sep 2025 10:45:50 +0000 Subject: [PATCH 140/175] Version 3.5.0 - released concurrently with MMGen Wallet v16.0.0 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 70beee3..1545d96 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.dev11 +3.5.0 diff --git a/setup.cfg b/setup.cfg index cd27742..9bca61c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ python_requires = >=3.9 include_package_data = True install_requires = - mmgen-wallet>=15.1.dev39 + mmgen-wallet==16.0.0 pyyaml yahooquery From cab8be016707b6886cbacf56666611b9a3fdad98 Mon Sep 17 00:00:00 2001 From: "MMGen@trixie" Date: Wed, 1 Oct 2025 15:26:37 +0000 Subject: [PATCH 141/175] pyproject.toml: add ruff data --- pyproject.toml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 81515d2..25af44f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,41 @@ requires = [ ] build-backend = "setuptools.build_meta" +[tool.ruff] +line-length = 106 +indent-width = 4 + +[tool.ruff.format] +quote-style = "single" +indent-style = "tab" + +[tool.ruff.lint] +ignore = [ + "E401", # multiple imports per line + "E701", # multiple statements per line + "E721", # use isinstance() + "E731", # lambda instead of def + "E402", # module import not top of file + "E722", # bare except + "E713", # membership 'not in' + "E741", # ambiguous variable name +] + +[tool.ruff.lint.per-file-ignores] +"test/include/common.py" = [ "F821" ] # undefined name 'cfg' +"test/misc/input_func.py" = [ "F401" ] # imported but unused +"test/modtest_d/cashaddr.py" = [ "F841" ] # assigned to but never used +"test/modtest_d/dep.py" = [ "F401" ] # imported but unused +"test/modtest_d/testdep.py" = [ "F401" ] # imported but unused +"test/modtest_d/obj.py" = [ "F841" ] # assigned to but never used +"test/objtest_d/*" = [ "F401" ] # imported but unused +"test/objattrtest_d/*" = [ "F401" ] # imported but unused +"test/overlay/fakemods/*" = [ "F403", "F405" ] # `import *` used +"test/*.py" = [ "F401" ] # imported but unused +"test/colortest.py" = [ "F403", "F405" ] # `import *` used +"test/tooltest2.py" = [ "F403", "F405" ] # `import *` used +"test/overlay/tree/*" = [ "ALL" ] + [tool.pylint.format] indent-string = "\t" indent-after-paren = 2 From 5c9d7053017f463193b9b5aa9dfb036f1844d697 Mon Sep 17 00:00:00 2001 From: "MMGen@trixie" Date: Wed, 1 Oct 2025 15:26:41 +0000 Subject: [PATCH 142/175] test suite: lint, whitespace --- test/cmdtest_d/main.py | 2 +- test/cmdtest_d/misc.py | 4 ++-- test/cmdtest_d/regtest.py | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/cmdtest_d/main.py b/test/cmdtest_d/main.py index 6e35b7d..f1881f8 100755 --- a/test/cmdtest_d/main.py +++ b/test/cmdtest_d/main.py @@ -41,7 +41,7 @@ class CmdTestMain(CmdTestBase): def peerblocks(self,args,expect_list=None,pexpect_spawn=False): t = self.spawn( - f'mmnode-peerblocks', + 'mmnode-peerblocks', args, pexpect_spawn = pexpect_spawn ) if cfg.exact_output: # disable echoing of input diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 1a8175e..8a39462 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -32,7 +32,7 @@ class CmdTestHelp(CmdTestBase): color = True def version(self): - t = self.spawn(f'mmnode-netrate',['--version']) + t = self.spawn('mmnode-netrate', ['--version']) t.expect('MMNODE-NETRATE version') return t @@ -108,7 +108,7 @@ class CmdTestScripts(CmdTestBase): def ticker(self, args=[], expect_list=None, cached=True, exit_val=None): t = self.spawn( - f'mmnode-ticker', + 'mmnode-ticker', (['--cached-data'] if cached else []) + self.ticker_args + args, exit_val = exit_val) if expect_list: diff --git a/test/cmdtest_d/regtest.py b/test/cmdtest_d/regtest.py index 3b77836..4792dee 100755 --- a/test/cmdtest_d/regtest.py +++ b/test/cmdtest_d/regtest.py @@ -107,7 +107,7 @@ class CmdTestRegtest(CmdTestBase): def __init__(self, cfg, trunner, cfgs, spawn): CmdTestBase.__init__(self, cfg, trunner, cfgs, spawn) - if trunner == None: + if trunner is None: return if cfg._proto.testnet: die(2,'--testnet and --regtest options incompatible with regtest test suite') @@ -328,7 +328,7 @@ class CmdTestRegtest(CmdTestBase): async def do_tx(inputs,outputs,wif): tx_hex = await r.rpc_call( 'createrawtransaction', inputs, outputs ) tx = await r.rpc_call( 'signrawtransactionwithkey', tx_hex, [wif], [], self.proto.sighash_type ) - assert tx['complete'] == True + assert tx['complete'] return tx['hex'] async def do_tx1(): @@ -371,13 +371,13 @@ class CmdTestRegtest(CmdTestBase): imsg(f'Creating funding transaction with {nTxs} outputs of value {tx1_amt} {self.proto.coin}') tx1_hex = await do_tx1() - imsg(f'Relaying funding transaction') + imsg('Relaying funding transaction') await r.rpc_call('sendrawtransaction',tx1_hex) - imsg(f'Mining a block') + imsg('Mining a block') await r.generate(1,silent=True) - imsg(f'Generating fees for mempool transactions') + imsg('Generating fees for mempool transactions') fees = list(gen_fees(nTxs,2,120)) imsg(f'Creating and relaying {nTxs} mempool transactions with {nPairs} outputs each') From 4d1f4577a7758c0c028166d7f59538ec53d74d8d Mon Sep 17 00:00:00 2001 From: "MMGen@trixie" Date: Wed, 1 Oct 2025 15:26:41 +0000 Subject: [PATCH 143/175] lint, whitespace --- mmgen_node_tools/BlocksInfo.py | 2 +- mmgen_node_tools/PollDisplay.py | 2 +- mmgen_node_tools/Sound.py | 2 +- mmgen_node_tools/Ticker.py | 23 ++++++++++++--------- mmgen_node_tools/Util.py | 1 - mmgen_node_tools/main_addrbal.py | 2 +- mmgen_node_tools/main_halving_calculator.py | 5 ++--- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index f19089a..d5eac9e 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -361,7 +361,7 @@ class BlocksInfo: if p.debug: msg(repr(self.range_data(first,last,from_tip,nblocks,step))) if nblocks: - if first == None: + if first is None: first = self.tip - nblocks + 1 last = first + nblocks - 1 diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py index 72bedac..1de8a1a 100755 --- a/mmgen_node_tools/PollDisplay.py +++ b/mmgen_node_tools/PollDisplay.py @@ -52,7 +52,7 @@ class PollDisplay: count += 1 async def process_input(): - if self.input == None: + if self.input is None: sys.exit(1) elif self.input == 'q': msg('') diff --git a/mmgen_node_tools/Sound.py b/mmgen_node_tools/Sound.py index 88d3b02..df9f8ae 100755 --- a/mmgen_node_tools/Sound.py +++ b/mmgen_node_tools/Sound.py @@ -33,7 +33,7 @@ def timespec2secs(ts): mul = { 's': 1, 'm': 60, 'h': 60*60, 'd': 60*60*24 } pat = r'^([0-9]+)([smhd]*)$' m = re.match(pat,ts) - if m == None: + if m is None: die(2,"'%s': invalid time specifier" % ts) a,b = m.groups() return int(a) * (mul[b] if b else 1) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 252ba6c..6c8bc08 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -19,13 +19,13 @@ mmgen_node_tools.Ticker: Display price information for cryptocurrency and other # Possible alternatives: # - https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,LTC&tsyms=USD,EUR -import sys,os,re,time,datetime,json,yaml,random -from subprocess import run,PIPE,CalledProcessError +import sys, os, re, time, datetime, json, yaml, random +from subprocess import run, PIPE, CalledProcessError from decimal import Decimal from collections import namedtuple -from mmgen.color import red,yellow,green,blue,orange,gray -from mmgen.util import msg,msg_r,Msg,Msg_r,die,Die,suf,fmt,fmt_list,fmt_dict,list_gen +from mmgen.color import red, yellow, green, blue, orange, gray +from mmgen.util import msg, msg_r, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen from mmgen.ui import do_pager homedir = os.getenv('HOME') @@ -89,9 +89,11 @@ class DataSource: msg('') from .Misc import curl_exit_codes msg(red(curl_exit_codes[e.returncode])) - msg(red('Command line:\n {}'.format( ' '.join((repr(i) if ' ' in i else i) for i in e.cmd) ))) + msg(red('Command line:\n {}'.format( + ' '.join((repr(i) if ' ' in i else i) for i in e.cmd)))) from mmgen.exception import MMGenCalledProcessError - raise MMGenCalledProcessError(f'Subprocess returned non-zero exit status {e.returncode}') + raise MMGenCalledProcessError( + f'Subprocess returned non-zero exit status {e.returncode}') def get_data(self): @@ -327,15 +329,16 @@ class DataSource: def postprocess_data(self,data): def gen(): keys = set() - for key,val in data.items(): + d = {} + for key, val in data.items(): if m := re.match(r"\('(.*?)', datetime\.date\((.*)\)\)$",key): date = '{}-{:>02}-{:>02}'.format(*m[2].split(', ')) if (sym := m[1]) in keys: d[date] = val else: keys.add(sym) - d = {date:val} - yield (sym,d) + d = {date: val} + yield (sym, d) return dict(gen()) def assets_list_gen(cfg_in): @@ -936,7 +939,7 @@ class Ticker: def fmt_row(self,d,amt=None,amt_fmt=None): def fmt_pct(n): - return gray(' --') if n == None else (red,green)[n>=0](f'{n:+7.2f}') + return gray(' --') if n is None else (red,green)[n>=0](f'{n:+7.2f}') p = self.prices[d['id']] diff --git a/mmgen_node_tools/Util.py b/mmgen_node_tools/Util.py index 9060f68..1d5426b 100755 --- a/mmgen_node_tools/Util.py +++ b/mmgen_node_tools/Util.py @@ -20,7 +20,6 @@ mmgen_node_tools.Util: utility functions for MMGen node tools """ import time -from mmgen.util import suf def get_hms(t=None,utc=False,no_secs=False): secs = t or time.time() diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index ecf2b1a..747d0bb 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -14,7 +14,7 @@ mmnode-addrbal: Get balances for arbitrary addresses in the blockchain import sys -from mmgen.obj import CoinTxID,Int +from mmgen.obj import CoinTxID from mmgen.cfg import Config from mmgen.util import msg,Msg,die,suf,make_timestr,async_run from mmgen.color import red diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index caeb178..50916ab 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -21,7 +21,6 @@ mmnode-halving-calculator: Estimate date(s) of future block subsidy halving(s) """ import time -from decimal import Decimal from mmgen.cfg import Config from mmgen.util import async_run @@ -148,8 +147,8 @@ async def main(): b = 'BLOCK', c = 'DATE', d = '', - e = f'BDI (mins)', - f = f'SUBSIDY ({proto.coin})', + e = 'BDI (mins)', + f = 'SUBSIDY ({proto.coin})', g = f'MINED ({proto.coin})', h = f'TOTAL MINED ({proto.coin})' ) From bfb2dd839aab1d221e38d6e6532dca20f2904632 Mon Sep 17 00:00:00 2001 From: "MMGen@trixie" Date: Wed, 1 Oct 2025 15:26:41 +0000 Subject: [PATCH 144/175] setup.cfg: bump requirements --- setup.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 9bca61c..9af335b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,11 +33,11 @@ classifiers = Development Status :: 5 - Production/Stable [options] -python_requires = >=3.9 +python_requires = >=3.11 include_package_data = True install_requires = - mmgen-wallet==16.0.0 + mmgen-wallet>=16.0.0 pyyaml yahooquery From 37c74e361c0f695a43b3f08b4b85cc2106b92665 Mon Sep 17 00:00:00 2001 From: "MMGen@trixie" Date: Wed, 1 Oct 2025 15:26:42 +0000 Subject: [PATCH 145/175] use match statement where practicable --- mmgen_node_tools/BlocksInfo.py | 102 ++++++++++++++++++--------------- mmgen_node_tools/Ticker.py | 21 +++---- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index d5eac9e..42ccc23 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -197,18 +197,18 @@ class BlocksInfo: return parse_cs_uarg(self.cfg.stats.lower(),self.all_stats,self.dfl_stats,'stat') def parse_cmd_args(): # => (block_list, first, last, step) - if not cmd_args: - return (None,self.tip,self.tip,None) - elif len(cmd_args) == 1: - r = self.parse_rangespec(cmd_args[0]) - return ( - list(range(r.first,r.last+1,r.step)) if r.step else None, - r.first, - r.last, - r.step - ) - else: - return ([self.conv_blkspec(a) for a in cmd_args],None,None,None) + match cmd_args: + case [] | None: + return (None, self.tip, self.tip, None) + case [arg]: + r = self.parse_rangespec(arg) + return ( + list(range(r.first, r.last+1, r.step)) if r.step else None, + r.first, + r.last, + r.step) + case [*args]: + return ([self.conv_blkspec(a) for a in args], None, None, None) self.cfg = cfg self.rpc = rpc @@ -323,25 +323,29 @@ class BlocksInfo: repl = (name if add_name else '') + ':' + (fill_char if name in fill else '') yield (ls + self.fields[name].fs.replace(':',repl) + rs) - def conv_blkspec(self,arg): - if str(arg).lower() == 'cur': - return self.tip - elif is_int(arg): - if int(arg) < 0: - die(1,f'{arg}: block number must be non-negative') - elif int(arg) > self.tip: - die(1,f'{arg}: requested block height greater than current chain tip!') - else: - return int(arg) - else: - die(1,f'{arg}: invalid block specifier') + def conv_blkspec(self, arg): + match arg: + case str() if arg.lower() == 'cur': + return self.tip + case x if is_int(x): + match int(arg): + case x if x < 0: + die(1, f'{x}: block number must be non-negative') + case x if x > self.tip: + die(1, f'{x}: requested block height greater than current chain tip!') + case x: + return x + case _: + die(1, f'{arg}: invalid block specifier') - def check_nblocks(self,arg): - if arg <= 0: - die(1,'nBlocks must be a positive integer') - elif arg > self.tip: - die(1, f"'{arg}': nBlocks must be less than current chain height") - return arg + def check_nblocks(self, arg): + match arg: + case x if x <= 0: + die(1, 'nBlocks must be a positive integer') + case x if x > self.tip: + die(1, f'{arg}: nBlocks must be less than current chain height') + case _: + return arg def parse_rangespec(self,arg): @@ -487,20 +491,21 @@ class BlocksInfo: def fmt_stat_item(self,fs,s): return fs.format(s) if type(fs) == str else fs(s) - async def output_stats(self,res,sname): + async def output_stats(self, res, sname): def gen(data): for d in data: - if len(d) == 2: - yield (indent+d[0]).format(**{k:self.fmt_stat_item(*v) for k,v in d[1].items()}) - elif len(d) == 4: - yield (indent+d[0]).format(self.fmt_stat_item(d[2],d[3])) - elif type(d) == str: - yield d - else: - assert False, f'{d}: invalid stats data' + match d: + case [a, b]: + yield (indent + a).format(**{k:self.fmt_stat_item(*v) for k, v in b.items()}) + case [a, _, b, c]: + yield (indent + a).format(self.fmt_stat_item(b, c)) + case str(): + yield d + case _: + assert False, f'{d}: invalid stats data' - foo,data = await res + foo, data = await res indent = '' if sname in self.noindent_stats else ' ' Msg('\n'.join(gen(data))) @@ -755,13 +760,16 @@ class JSONBlocksInfo(BlocksInfo): def gen(data): for d in data: - if len(d) == 2: - for k,v in d[1].items(): - yield (k,self.fmt_stat_item(*v)) - elif len(d) == 4: - yield (d[1],self.fmt_stat_item(d[2],d[3])) - elif type(d) != str: - assert False, f'{d}: invalid stats data' + match d: + case [_, a]: + for k, v in a.items(): + yield (k, self.fmt_stat_item(*v)) + case [_, a, b, c]: + yield (a, self.fmt_stat_item(b, c)) + case str(): + pass + case _: + assert False, f'{d}: invalid stats data' varname,data = await res Msg_r(', "{}_data": {}'.format( varname, json.dumps(dict(gen(data)),cls=json_encoder) )) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 6c8bc08..95e03f2 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -125,16 +125,17 @@ class DataSource: else: die(1,self.rate_limit_errmsg(elapsed)) - if data_type == 'json': - try: - data = json.loads(data_in) - except: - self.json_data_error_msg(data_in) - die(2,'Retrieved data is not valid JSON, exiting') - json_text = data_in - elif data_type == 'python': - data = data_in - json_text = json.dumps(data_in) + match data_type: + case 'json': + try: + data = json.loads(data_in) + except: + self.json_data_error_msg(data_in) + die(2,'Retrieved data is not valid JSON, exiting') + json_text = data_in + case 'python': + data = data_in + json_text = json.dumps(data_in) if not data: if use_cached_data: From fe45fbaa239ae6c28ed056cd1c2f0977550bad45 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 1 Oct 2025 15:26:42 +0000 Subject: [PATCH 146/175] minor cleanups --- mmgen_node_tools/main_blocks_info.py | 4 ++-- mmgen_node_tools/main_halving_calculator.py | 4 ++-- test/modtest_d/__init__.py | 7 +++++++ 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100755 test/modtest_d/__init__.py diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index c2bbf59..18136c4 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -164,7 +164,7 @@ async def main(): cls = JSONBlocksInfo if cfg.json else BlocksInfo - m = cls( cfg, cfg._args, await rpc_init(cfg,ignore_wallet=True) ) + m = cls(cfg, cfg._args, await rpc_init(cfg, ignore_wallet=True)) if m.fnames and not cfg.no_header: m.print_header() @@ -178,4 +178,4 @@ async def main(): m.finalize_output() -async_run(main()) +async_run(cfg, main) diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index 50916ab..8f0a3a3 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -67,7 +67,7 @@ async def main(): proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init( cfg, proto, ignore_wallet=True ) + c = await rpc_init(cfg, proto, ignore_wallet=True) tip = await c.call('getblockcount') assert tip > 1, 'block tip must be > 1' @@ -181,4 +181,4 @@ async def main(): else: print_current_stats() -async_run(main()) +async_run(cfg, main) diff --git a/test/modtest_d/__init__.py b/test/modtest_d/__init__.py new file mode 100755 index 0000000..86190b5 --- /dev/null +++ b/test/modtest_d/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +""" +test.modtest_d: shared data for module tests for the MMGen Node Tools suite +""" + +altcoin_tests = [] From 1f12baac4c246f812942e4817dfd8541d5ee8913 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 1 Oct 2025 15:26:42 +0000 Subject: [PATCH 147/175] update for MMGen Wallet v16.1.dev3 --- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_addrbal.py | 2 +- mmgen_node_tools/main_feeview.py | 2 +- mmgen_node_tools/main_netrate.py | 2 +- mmgen_node_tools/main_peerblocks.py | 8 ++++---- mmgen_node_tools/main_txfind.py | 2 +- setup.cfg | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1545d96..229c4d8 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.5.0 +3.6.dev0 diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 747d0bb..37f0482 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -167,6 +167,6 @@ if len(cfg._args) < 1: die(1,'This command requires at least one coin address argument') try: - async_run(main(cfg._args)) + async_run(cfg, main, args=[cfg._args]) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index e7b9952..3211f7f 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -220,4 +220,4 @@ async def main(): await c.call('getblockcount') )) + '\n\n' + '\n'.join(gen_body(data)) + '\n' ) -async_run(main()) +async_run(cfg, main) diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index 52b509d..b273ac6 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -67,6 +67,6 @@ async def main(): rs,ss,ts = (r,s,t) try: - async_run(main()) + async_run(cfg, main) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index 6ebb5a3..35292ea 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -31,10 +31,10 @@ opts_data = { } } -async def main(): +from mmgen.cfg import Config +cfg = Config(opts_data=opts_data) - from mmgen.cfg import Config - cfg = Config(opts_data=opts_data) +async def main(): from mmgen.rpc import rpc_init rpc = await rpc_init(cfg,ignore_wallet=True) @@ -48,4 +48,4 @@ async def main(): await peers.run(rpc) from mmgen.util import async_run -async_run(main()) +async_run(cfg, main) diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index 4a54430..2411f36 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -92,4 +92,4 @@ msgs = msg_data['quiet' if cfg.quiet else 'normal'] if len(cfg._args) != 1: die(1,'One transaction ID must be specified') -sys.exit(async_run(main(cfg._args[0]))) +sys.exit(async_run(cfg, main, args=[cfg._args[0]])) diff --git a/setup.cfg b/setup.cfg index 9af335b..29481b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ python_requires = >=3.11 include_package_data = True install_requires = - mmgen-wallet>=16.0.0 + mmgen-wallet>=16.1.dev3 pyyaml yahooquery From 0bad23b77bd2327937ec82ef7e8d4dba127e44be Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 3 Oct 2025 10:31:49 +0000 Subject: [PATCH 148/175] update for MMGen Wallet v16.1.dev4 --- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/cmdtest_d/include/cfg.py | 12 ++++++++---- test/cmdtest_d/misc.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 229c4d8..9f32e9d 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev0 +3.6.dev1 diff --git a/setup.cfg b/setup.cfg index 29481b2..d48a8c8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ python_requires = >=3.11 include_package_data = True install_requires = - mmgen-wallet>=16.1.dev3 + mmgen-wallet>=16.1.dev4 pyyaml yahooquery diff --git a/test/cmdtest_d/include/cfg.py b/test/cmdtest_d/include/cfg.py index 5a4263d..08b6263 100755 --- a/test/cmdtest_d/include/cfg.py +++ b/test/cmdtest_d/include/cfg.py @@ -12,13 +12,17 @@ test.cmdtest_d.include.cfg: configuration data for cmdtest.py """ +from collections import namedtuple + cmd_groups_altcoin = [] +gd = namedtuple('cmd_groups_data', ['clsname', 'params']) + cmd_groups_dfl = { - 'main': ('CmdTestMain',{}), - 'helpscreens': ('CmdTestHelp',{'modname':'misc','full_data':True}), - 'scripts': ('CmdTestScripts',{'modname':'misc'}), - 'regtest': ('CmdTestRegtest',{}), + 'main': gd('CmdTestMain', {}), + 'helpscreens': gd('CmdTestHelp', {'modname': 'misc', 'full_data': True}), + 'scripts': gd('CmdTestScripts', {'modname': 'misc'}), + 'regtest': gd('CmdTestRegtest', {}), } cmd_groups_extra = {} diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 8a39462..9528b76 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -125,7 +125,7 @@ class CmdTestScripts(CmdTestBase): if not cfg.skipping_deps: t.expect('Creating') t.expect('Creating') - ret = t.expect(['proxy host could not be resolved', 'ProxyError']) + ret = t.expect(['proxy host could not be resolved', 'unexpected keyword']) t.exit_val = 1 if ret else 3 return t From 0b79ef719b3712b64571b63dbd3177ab247aa8b3 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 4 Oct 2025 09:56:55 +0000 Subject: [PATCH 149/175] whitespace, minor changes (16 files) --- mmgen_node_tools/BlocksInfo.py | 402 +++++++-------- mmgen_node_tools/PeerBlocks.py | 55 +- mmgen_node_tools/PollDisplay.py | 12 +- mmgen_node_tools/Sound.py | 32 +- mmgen_node_tools/Ticker.py | 485 ++++++++---------- mmgen_node_tools/Util.py | 60 ++- mmgen_node_tools/main_addrbal.py | 66 ++- mmgen_node_tools/main_blocks_info.py | 19 +- mmgen_node_tools/main_feeview.py | 65 ++- mmgen_node_tools/main_halving_calculator.py | 56 +- mmgen_node_tools/main_netrate.py | 21 +- mmgen_node_tools/main_peerblocks.py | 7 +- mmgen_node_tools/main_ticker.py | 23 +- mmgen_node_tools/main_txfind.py | 17 +- test/cmdtest_d/regtest.py | 8 +- .../fakemods/mmgen_node_tools/PeerBlocks.py | 8 +- 16 files changed, 629 insertions(+), 707 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index 42ccc23..a59bf50 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -20,58 +20,57 @@ mmgen_node_tools.BlocksInfo: Display information about a block or range of blocks """ -import re,json +import re, json from collections import namedtuple -from time import strftime,gmtime +from time import strftime, gmtime from decimal import Decimal -from mmgen.util import msg,Msg,Msg_r,die,suf,secs_to_ms,secs_to_dhms,is_int +from mmgen.util import msg, Msg, Msg_r, die, suf, secs_to_ms, secs_to_dhms, is_int from mmgen.rpc.util import json_encoder class RangeParser: debug = False - def __init__(self,caller,arg): + def __init__(self, caller, arg): self.caller = caller self.arg = self.orig_arg = arg - def parse(self,target): - ret = getattr(self,'parse_'+target)() + def parse(self, target): + ret = getattr(self, 'parse_'+target)() if self.debug: msg(f'arg after parse({target}): {self.arg}') return ret def finalize(self): if self.arg: - die(1,f'{self.orig_arg!r}: invalid range specifier') + die(1, f'{self.orig_arg!r}: invalid range specifier') def parse_from_tip(self): - m = re.match(r'-([0-9]+)(.*)',self.arg) + m = re.match(r'-([0-9]+)(.*)', self.arg) if m: - res,self.arg = (m[1],m[2]) + res, self.arg = (m[1], m[2]) return self.caller.check_nblocks(int(res)) def parse_abs_range(self): - m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)',self.arg) + m = re.match(r'([^+-]+)(-([^+-]+)){0,1}(.*)', self.arg) if m: if self.debug: msg(f'abs_range parse: first={m[1]}, last={m[3]}') self.arg = m[4] return ( self.caller.conv_blkspec(m[1]), - self.caller.conv_blkspec(m[3]) if m[3] else None - ) - return (None,None) + self.caller.conv_blkspec(m[3]) if m[3] else None) + return (None, None) def parse_add(self): - m = re.match(r'\+([0-9*]+)(.*)',self.arg) + m = re.match(r'\+([0-9*]+)(.*)', self.arg) if m: - res,self.arg = (m[1],m[2]) + res, self.arg = (m[1], m[2]) if res.strip('*') != res: - die(1,f"'+{res}': malformed nBlocks specifier") + die(1, f"'+{res}': malformed nBlocks specifier") if len(res) > 30: - die(1,f"'+{res}': overly long nBlocks specifier") + die(1, f"'+{res}': overly long nBlocks specifier") return self.caller.check_nblocks(eval(res)) # res is only digits plus '*', so eval safe class BlocksInfo: @@ -81,33 +80,33 @@ class BlocksInfo: total_solve_time = 0 header_printed = False - bf = namedtuple('block_info_fields',['fmt_func','src','fs','hdr1','hdr2','key1','key2']) + bf = namedtuple('block_info_fields', ['fmt_func', 'src', 'fs', 'hdr1', 'hdr2', 'key1', 'key2']) # bh=getblockheader, bs=getblockstats, lo=local fields = { - 'block': bf( None, 'bh', '{:<6}', '', 'Block', 'height', None ), - 'hash': bf( None, 'bh', '{:<64}', '', 'Hash', 'hash', None ), - 'date': bf( 'da', 'bh', '{:<19}', '', 'Date', 'time', None ), - 'interval': bf( 'td', 'lo', '{:>8}', 'Solve', 'Time ', 'interval', None ), - 'subsidy': bf( 'su', 'bs', '{:<5}', 'Sub-', 'sidy', 'subsidy', None ), - 'totalfee': bf( 'tf', 'bs', '{:>10}', '', 'Total Fee', 'totalfee', None ), - 'size': bf( None, 'bs', '{:>7}', '', 'Size', 'total_size', None ), - 'weight': bf( None, 'bs', '{:>7}', '', 'Weight', 'total_weight', None ), - 'fee90': bf( 'fe', 'bs', '{:>3}', '90%', 'Fee', 'feerate_percentiles', 4 ), - 'fee75': bf( 'fe', 'bs', '{:>3}', '75%', 'Fee', 'feerate_percentiles', 3 ), - 'fee50': bf( 'fe', 'bs', '{:>3}', '50%', 'Fee', 'feerate_percentiles', 2 ), - 'fee25': bf( 'fe', 'bs', '{:>3}', '25%', 'Fee', 'feerate_percentiles', 1 ), - 'fee10': bf( 'fe', 'bs', '{:>3}', '10%', 'Fee', 'feerate_percentiles', 0 ), - 'fee_max': bf( 'fe', 'bs', '{:>5}', 'Max', 'Fee', 'maxfeerate', None ), - 'fee_avg': bf( 'fe', 'bs', '{:>3}', 'Avg', 'Fee', 'avgfeerate', None ), - 'fee_min': bf( 'fe', 'bs', '{:>3}', 'Min', 'Fee', 'minfeerate', None ), - 'nTx': bf( None, 'bh', '{:>5}', '', ' nTx ', 'nTx', None ), - 'inputs': bf( None, 'bs', '{:>5}', 'In- ', 'puts', 'ins', None ), - 'outputs': bf( None, 'bs', '{:>5}', 'Out-', 'puts', 'outs', None ), - 'utxo_inc': bf( None, 'bs', '{:>6}', ' UTXO', ' Incr', 'utxo_increase', None ), - 'version': bf( None, 'bh', '{:<8}', '', 'Version', 'versionHex', None ), - 'difficulty': bf( 'di', 'bh', '{:<8}', 'Diffi-','culty', 'difficulty', None ), - 'miner': bf( None, 'lo', '{:<5}', '', 'Miner', 'miner', None ), - } + 'block': bf(None, 'bh', '{:<6}', '', 'Block', 'height', None), + 'hash': bf(None, 'bh', '{:<64}', '', 'Hash', 'hash', None), + 'date': bf('da', 'bh', '{:<19}', '', 'Date', 'time', None), + 'interval': bf('td', 'lo', '{:>8}', 'Solve', 'Time ', 'interval', None), + 'subsidy': bf('su', 'bs', '{:<5}', 'Sub-', 'sidy', 'subsidy', None), + 'totalfee': bf('tf', 'bs', '{:>10}', '', 'Total Fee', 'totalfee', None), + 'size': bf(None, 'bs', '{:>7}', '', 'Size', 'total_size', None), + 'weight': bf(None, 'bs', '{:>7}', '', 'Weight', 'total_weight', None), + 'fee90': bf('fe', 'bs', '{:>3}', '90%', 'Fee', 'feerate_percentiles', 4), + 'fee75': bf('fe', 'bs', '{:>3}', '75%', 'Fee', 'feerate_percentiles', 3), + 'fee50': bf('fe', 'bs', '{:>3}', '50%', 'Fee', 'feerate_percentiles', 2), + 'fee25': bf('fe', 'bs', '{:>3}', '25%', 'Fee', 'feerate_percentiles', 1), + 'fee10': bf('fe', 'bs', '{:>3}', '10%', 'Fee', 'feerate_percentiles', 0), + 'fee_max': bf('fe', 'bs', '{:>5}', 'Max', 'Fee', 'maxfeerate', None), + 'fee_avg': bf('fe', 'bs', '{:>3}', 'Avg', 'Fee', 'avgfeerate', None), + 'fee_min': bf('fe', 'bs', '{:>3}', 'Min', 'Fee', 'minfeerate', None), + 'nTx': bf(None, 'bh', '{:>5}', '', ' nTx ', 'nTx', None), + 'inputs': bf(None, 'bs', '{:>5}', 'In- ', 'puts', 'ins', None), + 'outputs': bf(None, 'bs', '{:>5}', 'Out-', 'puts', 'outs', None), + 'utxo_inc': bf(None, 'bs', '{:>6}', ' UTXO', ' Incr', 'utxo_increase', None), + 'version': bf(None, 'bh', '{:<8}', '', 'Version', 'versionHex', None), + 'difficulty': bf('di', 'bh', '{:<8}', 'Diffi-','culty', 'difficulty', None), + 'miner': bf(None, 'lo', '{:<5}', '', 'Miner', 'miner', None)} + dfl_fields = ( 'block', 'date', @@ -121,8 +120,8 @@ class BlocksInfo: 'fee10', 'fee_avg', 'fee_min', - 'version', - ) + 'version') + fixed_fields = ( 'block', # until ≈ 09/01/2028 (block 1000000) 'hash', @@ -131,36 +130,34 @@ class BlocksInfo: 'weight', # until ≈ 2.5x block size increase 'version', 'subsidy', # until ≈ 01/04/2028 (increases by 1 digit per halving until 9th halving [max 10 digits]) - 'difficulty', # until 1.00e+100 (i.e. never) - ) + 'difficulty') # until 1.00e+100 (i.e. never) # column width adjustment data: - fs_lsqueeze = ('totalfee','inputs','outputs','nTx') + fs_lsqueeze = ('totalfee', 'inputs', 'outputs', 'nTx') fs_rsqueeze = () fs_groups = ( - ('fee10','fee25','fee50','fee75','fee90','fee_avg','fee_min','fee_max'), - ) + ('fee10', 'fee25', 'fee50', 'fee75', 'fee90', 'fee_avg', 'fee_min', 'fee_max')) fs_lsqueeze2 = ('interval',) - all_stats = ['col_avg','range','avg','mini_avg','total','diff'] - dfl_stats = ['range','mini_avg','diff'] + all_stats = ['col_avg', 'range', 'avg', 'mini_avg', 'total', 'diff'] + dfl_stats = ['range', 'mini_avg', 'diff'] noindent_stats = ['col_avg'] - avg_stats_skip = {'block', 'hash', 'date', 'version','miner'} + avg_stats_skip = {'block', 'hash', 'date', 'version', 'miner'} - range_data = namedtuple('parsed_range_data',['first','last','from_tip','nblocks','step']) + range_data = namedtuple('parsed_range_data', ['first', 'last', 'from_tip', 'nblocks', 'step']) - t_fmt = lambda self,t: f'{t/86400:.2f} days' if t > 172800 else f'{t/3600:.2f} hrs' + t_fmt = lambda self, t: f'{t/86400:.2f} days' if t > 172800 else f'{t/3600:.2f} hrs' @classmethod - def parse_cslist(cls,uarg,full_set,dfl_set,desc): + def parse_cslist(cls, uarg, full_set, dfl_set, desc): - def make_list(m,func): + def make_list(m, func): groups_lc = [set(e.lower() for e in gi.split(',')) for gi in m.groups()] for group in groups_lc: for e in group: if e not in full_set_lc: - die(1,f'{e!r}: unrecognized {desc}') + die(1, f'{e!r}: unrecognized {desc}') # display elements in order: return [e for e in full_set if e.lower() in func(groups_lc)] @@ -168,33 +165,31 @@ class BlocksInfo: dfl_set_lc = set(e.lower() for e in dfl_set) cspat = r'(\w+(?:,\w+)*)' - for pat,func in ( - ( rf'{cspat}$', lambda g: g[0] ), - ( rf'\+{cspat}$', lambda g: dfl_set_lc | g[0] ), - ( rf'\-{cspat}$', lambda g: dfl_set_lc - g[0] ), - ( rf'\+{cspat}\-{cspat}$', lambda g: ( dfl_set_lc | g[0] ) - g[1] ), - ( rf'\-{cspat}\+{cspat}$', lambda g: ( dfl_set_lc - g[0] ) | g[1] ), - ( rf'all\-{cspat}$', lambda g: full_set_lc - g[0] ) - ): - m = re.match(pat,uarg,re.ASCII|re.IGNORECASE) + for pat, func in ( + (rf'{cspat}$', lambda g: g[0]), + (rf'\+{cspat}$', lambda g: dfl_set_lc | g[0]), + (rf'\-{cspat}$', lambda g: dfl_set_lc - g[0]), + (rf'\+{cspat}\-{cspat}$', lambda g: (dfl_set_lc | g[0]) - g[1]), + (rf'\-{cspat}\+{cspat}$', lambda g: (dfl_set_lc - g[0]) | g[1]), + (rf'all\-{cspat}$', lambda g: full_set_lc - g[0])): + m = re.match(pat, uarg, re.ASCII|re.IGNORECASE) if m: - return make_list(m,func) + return make_list(m, func) else: - die(1,f'{uarg}: invalid parameter') + die(1, f'{uarg}: invalid parameter') - def __init__(self,cfg,cmd_args,rpc): + def __init__(self, cfg, cmd_args, rpc): - def parse_cs_uarg(uarg,full_set,dfl_set,desc): + def parse_cs_uarg(uarg, full_set, dfl_set, desc): return ( full_set if uarg == 'all' else [] if uarg == 'none' else - self.parse_cslist(uarg,full_set,dfl_set,desc) - ) + self.parse_cslist(uarg, full_set, dfl_set, desc)) def get_fields(): - return parse_cs_uarg(self.cfg.fields,list(self.fields),self.dfl_fields,'field') + return parse_cs_uarg(self.cfg.fields, list(self.fields), self.dfl_fields, 'field') def get_stats(): - return parse_cs_uarg(self.cfg.stats.lower(),self.all_stats,self.dfl_stats,'stat') + return parse_cs_uarg(self.cfg.stats.lower(), self.all_stats, self.dfl_stats, 'stat') def parse_cmd_args(): # => (block_list, first, last, step) match cmd_args: @@ -217,7 +212,7 @@ class BlocksInfo: from_satoshi = self.rpc.proto.coin_amt.satoshi to_satoshi = 1 / from_satoshi - self.block_list,self.first,self.last,self.step = parse_cmd_args() + self.block_list, self.first, self.last, self.step = parse_cmd_args() have_segwit = self.rpc.info('segwit_is_active') @@ -228,35 +223,33 @@ class BlocksInfo: self.stats_deps = { 'avg': set(self.fields) - self.avg_stats_skip, 'col_avg': set(self.fields) - self.avg_stats_skip, - 'mini_avg': {'interval','size'} | ({'weight'} if have_segwit else set()), - 'total': {'interval','subsidy','totalfee','nTx','inputs','outputs','utxo_inc'}, + 'mini_avg': {'interval', 'size'} | ({'weight'} if have_segwit else set()), + 'total': {'interval', 'subsidy', 'totalfee', 'nTx', 'inputs', 'outputs', 'utxo_inc'}, 'range': {}, - 'diff': {}, - } + 'diff': {}} self.fmt_funcs = { - 'da': lambda arg: strftime('%Y-%m-%d %X',gmtime(arg)), + 'da': lambda arg: strftime('%Y-%m-%d %X', gmtime(arg)), 'td': lambda arg: ( - '-{:02}:{:02}'.format(abs(arg)//60,abs(arg)%60) if arg < 0 else - ' {:02}:{:02}'.format(arg//60,arg%60) ), + '-{:02}:{:02}'.format(abs(arg)//60, abs(arg)%60) if arg < 0 else + ' {:02}:{:02}'.format(arg//60, arg%60)), 'tf': lambda arg: '{:.8f}'.format(arg * from_satoshi), 'su': lambda arg: str(arg * from_satoshi).rstrip('0').rstrip('.'), 'fe': lambda arg: str(arg), - 'di': lambda arg: '{:.2e}'.format(Decimal(arg)), - } + 'di': lambda arg: '{:.2e}'.format(Decimal(arg))} if self.cfg.coin == 'BCH': self.fmt_funcs.update({ 'su': lambda arg: str(arg).rstrip('0').rstrip('.'), 'fe': lambda arg: str(int(Decimal(arg) * to_satoshi)), - 'tf': lambda arg: '{:.8f}'.format(Decimal(arg)), - }) + 'tf': lambda arg: '{:.8f}'.format(Decimal(arg))}) self.fnames = tuple( - [f for f in self.fields if self.fields[f].src == 'bh' or f == 'interval'] if self.cfg.header_info else - get_fields() if self.cfg.fields else - self.dfl_fields - ) + [f for f in self.fields if self.fields[f].src == 'bh' or f == 'interval'] + if self.cfg.header_info + else get_fields() if self.cfg.fields + else self.dfl_fields) + if self.cfg.miner_info and 'miner' not in self.fnames: self.fnames += ('miner',) @@ -266,15 +259,15 @@ class BlocksInfo: if 'diff' in self.stats and not self.cfg.stats and self.last != self.tip: self.stats.remove('diff') - if {'avg','col_avg'} <= set(self.stats) and self.cfg.stats_only: + if {'avg', 'col_avg'} <= set(self.stats) and self.cfg.stats_only: self.stats.remove('col_avg') - if {'avg','mini_avg'} <= set(self.stats): + if {'avg', 'mini_avg'} <= set(self.stats): self.stats.remove('mini_avg') if self.cfg.full_stats: add_fnames = {fname for sname in self.stats for fname in self.stats_deps[sname]} - self.fnames = tuple(f for f in self.fields if f in {'block'} | set(self.fnames) | add_fnames ) + self.fnames = tuple(f for f in self.fields if f in {'block'} | set(self.fnames) | add_fnames) else: if 'col_avg' in self.stats and not self.fnames: self.stats.remove('col_avg') @@ -287,8 +280,7 @@ class BlocksInfo: self.bs_keys = set( [v.key1 for v in self.fvals if v.src == 'bs'] + ['total_size'] + - (['total_weight'] if have_segwit else []) - ) + (['total_weight'] if have_segwit else [])) if 'miner' in self.fnames: # capturing parens must contain only ASCII chars! @@ -302,17 +294,16 @@ class BlocksInfo: rb'([\x20-\x7e]{9,})', rb'[/^]([a-zA-Z0-9&. #/-]{5,})', rb'[/^]([_a-zA-Z0-9&. #/-]+)/', - rb'^\x03...\W{0,5}([\\_a-zA-Z0-9&. #/-]+)[/\\]', - )] + rb'^\x03...\W{0,5}([\\_a-zA-Z0-9&. #/-]+)[/\\]')] - self.block_data = namedtuple('block_data',self.fnames) - self.deps = { v.src for v in self.fvals } + self.block_data = namedtuple('block_data', self.fnames) + self.deps = {v.src for v in self.fvals} - def gen_fs(self,fnames,fill=[],fill_char='-',add_name=False): + def gen_fs(self, fnames, fill=[], fill_char='-', add_name=False): for i in range(len(fnames)): name = fnames[i] - ls = (' ','')[name in self.fs_lsqueeze + self.fs_lsqueeze2] - rs = (' ','')[name in self.fs_rsqueeze] + ls = (' ', '')[name in self.fs_lsqueeze + self.fs_lsqueeze2] + rs = (' ', '')[name in self.fs_rsqueeze] if i < len(fnames) - 1 and fnames[i+1] in self.fs_lsqueeze2: rs = '' if i: @@ -321,7 +312,7 @@ class BlocksInfo: ls = '' break repl = (name if add_name else '') + ':' + (fill_char if name in fill else '') - yield (ls + self.fields[name].fs.replace(':',repl) + rs) + yield (ls + self.fields[name].fs.replace(':', repl) + rs) def conv_blkspec(self, arg): match arg: @@ -347,22 +338,22 @@ class BlocksInfo: case _: return arg - def parse_rangespec(self,arg): + def parse_rangespec(self, arg): - p = RangeParser(self,arg) + p = RangeParser(self, arg) - from_tip = p.parse('from_tip') - first,last = (self.tip-from_tip,None) if from_tip else p.parse('abs_range') - add1 = p.parse('add') - add2 = p.parse('add') + from_tip = p.parse('from_tip') + first, last = (self.tip-from_tip, None) if from_tip else p.parse('abs_range') + add1 = p.parse('add') + add2 = p.parse('add') p.finalize() if add2 and last is not None: - die(1,f'{arg!r}: invalid range specifier') + die(1, f'{arg!r}: invalid range specifier') - nblocks,step = (add1,add2) if last is None else (None,add1) + nblocks, step = (add1, add2) if last is None else (None, add1) - if p.debug: msg(repr(self.range_data(first,last,from_tip,nblocks,step))) + if p.debug: msg(repr(self.range_data(first, last, from_tip, nblocks, step))) if nblocks: if first is None: @@ -373,12 +364,12 @@ class BlocksInfo: last = self.conv_blkspec(last or first) if p.debug: - msg(repr(self.range_data(first,last,from_tip,nblocks,step))) + msg(repr(self.range_data(first, last, from_tip, nblocks, step))) if first > last: - die(1,f'{first}-{last}: invalid block range') + die(1, f'{first}-{last}: invalid block range') - return self.range_data(first,last,from_tip,nblocks,step) + return self.range_data(first, last, from_tip, nblocks, step) async def process_blocks(self): @@ -388,7 +379,7 @@ class BlocksInfo: c = self.rpc - heights = self.block_list or range(self.first,self.last+1) + heights = self.block_list or range(self.first, self.last+1) self.hdrs = await get_hdrs(heights) if self.block_list: @@ -397,8 +388,7 @@ class BlocksInfo: else: self.first_prev_hdr = ( self.hdrs[0] if heights[0] == 0 else - await c.call('getblockheader',await c.call('getblockhash',heights[0]-1)) - ) + await c.call('getblockheader', await c.call('getblockhash', heights[0]-1))) self.t_cur = self.first_prev_hdr['time'] self.res = [] @@ -409,16 +399,16 @@ class BlocksInfo: ret = await self.process_block(self.hdrs[n]) self.res.append(ret) if self.fnames and not self.cfg.stats_only: - self.output_block(ret,n) + self.output_block(ret, n) - def output_block(self,data,n): + def output_block(self, data, n): def gen(): - for k,v in data._asdict().items(): + for k, v in data._asdict().items(): func = self.fields[k].fmt_func yield self.fmt_funcs[func](v) if func else v Msg(self.fs.format(*gen())) - async def process_block(self,hdr): + async def process_block(self, hdr): self.t_diff = hdr['time'] - self.t_cur self.t_cur = hdr['time'] @@ -426,14 +416,12 @@ class BlocksInfo: blk_data = { 'bh': hdr, - 'lo': { 'interval': self.t_diff } - } + 'lo': {'interval': self.t_diff}} if 'bs' in self.deps: bs = ( self.genesis_stats if hdr['height'] == 0 else - await self.rpc.call('getblockstats',hdr['hash'],list(self.bs_keys)) - ) + await self.rpc.call('getblockstats', hdr['hash'], list(self.bs_keys))) self.total_bytes += bs['total_size'] if 'total_weight' in bs: self.total_weight += bs['total_weight'] @@ -446,14 +434,13 @@ class BlocksInfo: for v in self.fvals: yield ( blk_data[v.src][v.key1] if v.key2 is None else - blk_data[v.src][v.key1][v.key2] - ) + blk_data[v.src][v.key1][v.key2]) return self.block_data(*gen()) - async def get_miner_string(self,H): - tx0 = (await self.rpc.call('getblock',H))['tx'][0] - bd = await self.rpc.call('getrawtransaction',tx0,1) + async def get_miner_string(self, H): + tx0 = (await self.rpc.call('getblock', H))['tx'][0] + bd = await self.rpc.call('getrawtransaction', tx0, 1) if type(bd) == tuple: return '---' else: @@ -464,13 +451,12 @@ class BlocksInfo: trmap_in = { '\\': ' ', '/': ' ', - ',': ' ', - } - trmap = { ord(a):b for a,b in trmap_in.items() } + ',': ' '} + trmap = {ord(a): b for a, b in trmap_in.items()} for pat in self.miner_pats: m = pat.search(cb) if m: - return re.sub( r'\s+', ' ', m[1].decode().strip('^').translate(trmap).strip() ) + return re.sub(r'\s+', ' ', m[1].decode().strip('^').translate(trmap).strip()) return '' def print_header(self): @@ -484,11 +470,11 @@ class BlocksInfo: yield self.fs.format(*hdr1) yield self.fs.format(*hdr2) - def process_stats(self,sname): - method = getattr(self,f'create_{sname}_stats',None) - return self.output_stats(method() if method else self.create_stats(sname),sname) + def process_stats(self, sname): + method = getattr(self, f'create_{sname}_stats', None) + return self.output_stats(method() if method else self.create_stats(sname), sname) - def fmt_stat_item(self,fs,s): + def fmt_stat_item(self, fs, s): return fs.format(s) if type(fs) == str else fs(s) async def output_stats(self, res, sname): @@ -497,7 +483,7 @@ class BlocksInfo: for d in data: match d: case [a, b]: - yield (indent + a).format(**{k:self.fmt_stat_item(*v) for k, v in b.items()}) + yield (indent + a).format(**{k: self.fmt_stat_item(*v) for k, v in b.items()}) case [a, _, b, c]: yield (indent + a).format(self.fmt_stat_item(b, c)) case str(): @@ -524,15 +510,14 @@ class BlocksInfo: 'range': ('{}', self.hdrs[-1]['height'] - self.hdrs[0]['height'] + 1), 'elapsed': (self.t_fmt, elapsed), 'nBlocks': ('{}', total_blks), - 'step': ('{}', self.step), - } - ) - if elapsed: - yield ( 'Start: {}', 'start_date', self.fmt_funcs['da'], self.hdrs[0]['time'] ) - yield ( 'End: {}', 'end_date', self.fmt_funcs['da'], self.hdrs[-1]['time'] ) - yield ( 'Avg BDI: {} min', 'avg_bdi', '{:.2f}', elapsed / nblocks / 60 ) + 'step': ('{}', self.step)}) - return ( 'range', gen() ) + if elapsed: + yield ('Start: {}', 'start_date', self.fmt_funcs['da'], self.hdrs[0]['time']) + yield ('End: {}', 'end_date', self.fmt_funcs['da'], self.hdrs[-1]['time']) + yield ('Avg BDI: {} min', 'avg_bdi', '{:.2f}', elapsed / nblocks / 60) + + return ('range', gen()) async def create_diff_stats(self): @@ -541,18 +526,17 @@ class BlocksInfo: tip_hdr = ( self.hdrs[-1] if self.hdrs[-1]['height'] == self.tip else - await c.call('getblockheader',await c.call('getblockhash',self.tip)) - ) + await c.call('getblockheader', await c.call('getblockhash', self.tip))) min_sample_blks = 432 # ≈3 days - rel_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-rel)) + rel_hdr = await c.call('getblockheader', await c.call('getblockhash', self.tip-rel)) if rel >= min_sample_blks: sample_blks = rel - bdi = ( tip_hdr['time'] - rel_hdr['time'] ) / rel + bdi = (tip_hdr['time'] - rel_hdr['time']) / rel else: - sample_blks = min(min_sample_blks,self.tip) - start_hdr = await c.call('getblockheader',await c.call('getblockhash',self.tip-sample_blks)) + sample_blks = min(min_sample_blks, self.tip) + start_hdr = await c.call('getblockheader', await c.call('getblockhash', self.tip-sample_blks)) diff_adj = Decimal(tip_hdr['difficulty']) / Decimal(start_hdr['difficulty']) time1 = rel_hdr['time'] - start_hdr['time'] time2 = tip_hdr['time'] - rel_hdr['time'] @@ -560,7 +544,7 @@ class BlocksInfo: rem = self.rpc.proto.diff_adjust_interval - rel - return ( 'difficulty', ( + return ('difficulty', ( 'Difficulty Statistics:', ('Current height: {}', 'chain_tip', '{}', self.tip), ('Next diff adjust: {next_diff_adjust} (in {blks_remaining} block%s [{time_remaining}])' % suf(rem), @@ -593,105 +577,98 @@ class BlocksInfo: def gen(): for field in self.fnames: if field in self.avg_stats_skip: - yield ( field, ('{}','') ) + yield (field, ('{}', '')) else: ret = self.sum_field_avg(field) func = self.fields[field].fmt_func - yield ( field, ( (self.fmt_funcs[func] if func else '{}'), ret )) + yield (field, ((self.fmt_funcs[func] if func else '{}'), ret)) if not self.header_printed: self.print_header() - fs = ''.join(self.gen_fs(self.fnames,fill=self.avg_stats_skip,add_name=True)).strip() - return ('column_averages', ('Column averages:', (fs, dict(gen())) )) + fs = ''.join(self.gen_fs(self.fnames, fill=self.avg_stats_skip, add_name=True)).strip() + return ('column_averages', ('Column averages:', (fs, dict(gen())))) - def avg_stats_data(self,data,spec_conv,spec_val): + def avg_stats_data(self, data, spec_conv, spec_val): coin = self.rpc.proto.coin return data( hdr = 'Averages for processed blocks:', func = self.sum_field_avg, - spec_sufs = { 'subsidy': f' {coin}', 'totalfee': f' {coin}' }, + spec_sufs = {'subsidy': f' {coin}', 'totalfee': f' {coin}'}, spec_convs = { - 'interval': spec_conv(0, lambda arg: secs_to_ms(arg)), + 'interval': spec_conv(0, lambda arg: secs_to_ms(arg)), 'utxo_inc': spec_conv(-1, '{:<+}'), - 'mb_per_hour': spec_conv(0, '{}'), - }, + 'mb_per_hour': spec_conv(0, '{}')}, spec_vals = ( spec_val( 'mb_per_hour', 'MB/hr', 'interval', lambda values: 'bs' in self.deps, lambda values: ( '{:.4f}'.format((self.total_bytes / 10000) / (self.total_solve_time / 36)) - if self.total_solve_time else 'N/A' ), - ), - ) - ) + if self.total_solve_time else 'N/A')), + )) mini_avg_stats_data = avg_stats_data - def total_stats_data(self,data,spec_conv,spec_val): + def total_stats_data(self, data, spec_conv, spec_val): coin = self.rpc.proto.coin return data( hdr = 'Totals for processed blocks:', func = self.sum_field_total, - spec_sufs = { 'subsidy': f' {coin}', 'totalfee': f' {coin}', 'reward': f' {coin}' }, + spec_sufs = {'subsidy': f' {coin}', 'totalfee': f' {coin}', 'reward': f' {coin}'}, spec_convs = { - 'interval': spec_conv(0, lambda arg: secs_to_dhms(arg)), + 'interval': spec_conv(0, lambda arg: secs_to_dhms(arg)), 'utxo_inc': spec_conv(-1, '{:<+}'), - 'reward': spec_conv(0, self.fmt_funcs['tf']), - }, + 'reward': spec_conv(0, self.fmt_funcs['tf'])}, spec_vals = ( spec_val( 'reward', 'Reward', 'totalfee', - lambda values: {'subsidy','totalfee'} <= set(values), - lambda values: values['subsidy'] + values['totalfee'] - ), - ) - ) + lambda values: {'subsidy', 'totalfee'} <= set(values), + lambda values: values['subsidy'] + values['totalfee']), + )) - async def create_stats(self,sname): + async def create_stats(self, sname): def convert_stats_hdr(field): v = self.fields[field] - return '{} {}'.format(v.hdr1.strip(), v.hdr2.strip()).replace('- ','') if v.hdr1 else v.hdr2.strip() + return '{} {}'.format( + v.hdr1.strip(), v.hdr2.strip()).replace('- ', '') if v.hdr1 else v.hdr2.strip() - d = getattr(self,f'{sname}_stats_data')( - namedtuple('stats_data',['hdr','func','spec_sufs','spec_convs','spec_vals']), - namedtuple('spec_conv',['width_adj','conv']), - namedtuple('spec_val',['name','lbl','insert_after','condition','code']) - ) + d = getattr(self, f'{sname}_stats_data')( + namedtuple('stats_data', ['hdr', 'func', 'spec_sufs', 'spec_convs', 'spec_vals']), + namedtuple('spec_conv', ['width_adj', 'conv']), + namedtuple('spec_val', ['name', 'lbl', 'insert_after', 'condition', 'code'])) fnames = [n for n in self.fnames if n in self.stats_deps[sname]] - lbls = {n:convert_stats_hdr(n) for n in fnames} - values = {n:d.func(n) for n in fnames} - col1_w = max((len(l) for l in lbls.values()),default=0) + 2 + lbls = {n: convert_stats_hdr(n) for n in fnames} + values = {n: d.func(n) for n in fnames} + col1_w = max((len(l) for l in lbls.values()), default=0) + 2 + print(d.spec_vals) for v in d.spec_vals: + print(v) if v.condition(values): try: idx = fnames.index(v.insert_after) + 1 except: idx = 0 - fnames.insert(idx,v.name) + fnames.insert(idx, v.name) lbls[v.name] = v.lbl values[v.name] = v.code(values) def gen(): - for n,fname in enumerate(fnames): + for n, fname in enumerate(fnames): spec_conv = d.spec_convs.get(fname) yield ( '{lbl:{wid}} {{}}{suf}'.format( lbl = lbls[fname] + ':', wid = col1_w + (spec_conv.width_adj if spec_conv else 0), - suf = d.spec_sufs.get(fname) or '' - ), + suf = d.spec_sufs.get(fname) or ''), fname, spec_conv.conv if spec_conv else ( - (lambda x: self.fmt_funcs[x] if x else '{}')(self.fields[fname].fmt_func) - ), - values[fname] - ) + (lambda x: self.fmt_funcs[x] if x else '{}')(self.fields[fname].fmt_func)), + values[fname]) - return ( sname, (d.hdr,) + tuple(gen()) ) + return (sname, (d.hdr,) + tuple(gen())) - def process_stats_pre(self,i): + def process_stats_pre(self, i): if (self.fnames and not self.cfg.stats_only) or i != 0: Msg('') @@ -724,13 +701,12 @@ class BlocksInfo: 'totalfee': 0, 'txs': 1, 'utxo_increase': 1, - 'utxo_size_inc': 117 - } + 'utxo_size_inc': 117} class JSONBlocksInfo(BlocksInfo): - def __init__(self,cfg,cmd_args,rpc): - super().__init__(cfg,cmd_args,rpc) + def __init__(self, cfg, cmd_args, rpc): + super().__init__(cfg, cmd_args, rpc) if self.cfg.json_raw: self.output_block = self.output_block_raw self.fmt_stat_item = self.fmt_stat_item_raw @@ -741,22 +717,22 @@ class JSONBlocksInfo(BlocksInfo): await super().process_blocks() Msg_r(']') - def output_block_raw(self,data,n): - Msg_r( (', ','')[n==0] + json.dumps(data._asdict(),cls=json_encoder) ) + def output_block_raw(self, data, n): + Msg_r((', ', '')[n==0] + json.dumps(data._asdict(), cls=json_encoder)) - def output_block(self,data,n): + def output_block(self, data, n): def gen(): - for k,v in data._asdict().items(): + for k, v in data._asdict().items(): func = self.fields[k].fmt_func - yield ( k, (self.fmt_funcs[func](v) if func else v) ) - Msg_r( (', ','')[n==0] + json.dumps(dict(gen()),cls=json_encoder) ) + yield (k, (self.fmt_funcs[func](v) if func else v)) + Msg_r((', ', '')[n==0] + json.dumps(dict(gen()), cls=json_encoder)) def print_header(self): pass - def fmt_stat_item_raw(self,fs,s): + def fmt_stat_item_raw(self, fs, s): return s - async def output_stats(self,res,sname): + async def output_stats(self, res, sname): def gen(data): for d in data: @@ -771,10 +747,10 @@ class JSONBlocksInfo(BlocksInfo): case _: assert False, f'{d}: invalid stats data' - varname,data = await res - Msg_r(', "{}_data": {}'.format( varname, json.dumps(dict(gen(data)),cls=json_encoder) )) + varname, data = await res + Msg_r(', "{}_data": {}'.format(varname, json.dumps(dict(gen(data)), cls=json_encoder))) - def process_stats_pre(self,i): pass + def process_stats_pre(self, i): pass def finalize_output(self): Msg('}') diff --git a/mmgen_node_tools/PeerBlocks.py b/mmgen_node_tools/PeerBlocks.py index c3d7c68..3329f07 100755 --- a/mmgen_node_tools/PeerBlocks.py +++ b/mmgen_node_tools/PeerBlocks.py @@ -14,66 +14,66 @@ mmgen_node_tools.PeerBlocks: List blocks in flight, disconnect stalling nodes import asyncio from collections import namedtuple -from mmgen.util import msg,msg_r,is_int -from mmgen.term import get_term,get_terminal_size,get_char +from mmgen.util import msg, msg_r, is_int +from mmgen.term import get_term, get_terminal_size, get_char from mmgen.ui import line_input from .PollDisplay import PollDisplay -RED,RESET = ('\033[31m','\033[0m') -COLORS = ['\033[38;5;%s;1m' % c for c in list(range(247,256)) + [231]] -ERASE_ALL,CUR_HOME = ('\033[J','\033[H') -CUR_HIDE,CUR_SHOW = ('\033[?25l','\033[?25h') +RED, RESET = ('\033[31m', '\033[0m') +COLORS = ['\033[38;5;%s;1m' % c for c in list(range(247, 256)) + [231]] +ERASE_ALL, CUR_HOME = ('\033[J', '\033[H') +CUR_HIDE, CUR_SHOW = ('\033[?25l', '\033[?25h') term = None class Display(PollDisplay): poll_secs = 2 - def __init__(self,cfg): + def __init__(self, cfg): super().__init__(cfg) - global term,term_width + global term, term_width if not term: term = get_term() term.init(noecho=True) term_width = self.cfg.columns or get_terminal_size().width msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) - async def get_info(self,rpc): + async def get_info(self, rpc): return await rpc.call('getpeerinfo') - def display(self,count): + def display(self, count): msg_r( CUR_HOME + (ERASE_ALL if count == 1 else '') + 'CONNECTED PEERS ({a}) {b} - poll {c}'.format( a = len(self.info), b = self.desc, - c = count ).ljust(term_width)[:term_width] + c = count).ljust(term_width)[:term_width] + '\n' + ('\n'.join(self.gen_display()) + '\n' if self.info else '') + ERASE_ALL + f"Type a peer number to disconnect, 'q' to quit, or any other key for {self.other_desc} display:" - + '\b' ) + + '\b') - async def disconnect_node(self,rpc,addr): - return await rpc.call('disconnectnode',addr) + async def disconnect_node(self, rpc, addr): + return await rpc.call('disconnectnode', addr) def get_input(self): - s = get_char(immed_chars='q0123456789',prehold_protect=False,num_bytes=1) + s = get_char(immed_chars='q0123456789', prehold_protect=False, num_bytes=1) if not is_int(s): return s with self.info_lock: msg('') term.reset() # readline required for correct operation here; without it, user must re-type first digit - ret = line_input( self.cfg, 'peer number> ', insert_txt=s, hold_protect=False ) + ret = line_input(self.cfg, 'peer number> ', insert_txt=s, hold_protect=False) term.init(noecho=True) self.enable_display = False # prevent display from updating before process_input() return ret - async def process_input(self,rpc): + async def process_input(self, rpc): ids = tuple(str(i['id']) for i in self.info) ret = False @@ -83,7 +83,7 @@ class Display(PollDisplay): from mmgen.exception import RPCFailure addr = self.info[ids.index(self.input)]['addr'] try: - await self.disconnect_node(rpc,addr) + await self.disconnect_node(rpc, addr) except RPCFailure: msg_r(f'Unable to disconnect peer {self.input} ({addr})') else: @@ -105,8 +105,8 @@ class BlocksDisplay(Display): def gen_display(self): - pd = namedtuple('peer_data',['id','blks_data','blks_width']) - bd = namedtuple('block_datum',['num','disp']) + pd = namedtuple('peer_data', ['id', 'blks_data', 'blks_width']) + bd = namedtuple('block_datum', ['num', 'disp']) def gen_block_data(): global min_height @@ -114,15 +114,15 @@ class BlocksDisplay(Display): for d in self.info: if d.get('inflight'): blocks = d['inflight'] - min_height = min(blocks) if not min_height else min(min_height,min(blocks)) - line = ' '.join(map(str,blocks))[:blks_field_width] + min_height = min(blocks) if not min_height else min(min_height, min(blocks)) + line = ' '.join(map(str, blocks))[:blks_field_width] blocks_disp = line.split() yield pd( d['id'], - [bd(blocks[i],blocks_disp[i]) for i in range(len(blocks_disp))], - len(line) ) + [bd(blocks[i], blocks_disp[i]) for i in range(len(blocks_disp))], + len(line)) else: - yield pd(d['id'],[],0) + yield pd(d['id'], [], 0) def gen_line(peer_data): for blk in peer_data.blks_data: @@ -136,7 +136,7 @@ class BlocksDisplay(Display): for peer_data in tuple(gen_block_data()): yield fs.format( peer_data.id, - ' '.join(gen_line(peer_data)) + ' ' * (blks_field_width - peer_data.blks_width) ) + ' '.join(gen_line(peer_data)) + ' ' * (blks_field_width - peer_data.blks_width)) class PeersDisplay(Display): @@ -152,5 +152,4 @@ class PeersDisplay(Display): A = id_width, b = d['addr'], B = addr_width, - c = d['subver'] - ).ljust(term_width)[:term_width] + c = d['subver']).ljust(term_width)[:term_width] diff --git a/mmgen_node_tools/PollDisplay.py b/mmgen_node_tools/PollDisplay.py index 1de8a1a..8274e40 100755 --- a/mmgen_node_tools/PollDisplay.py +++ b/mmgen_node_tools/PollDisplay.py @@ -12,7 +12,7 @@ mmgen_node_tools.PollDisplay: update and display RPC data; get input from user """ -import sys,threading +import sys, threading from mmgen.util import msg from mmgen.term import get_char @@ -22,18 +22,18 @@ class PollDisplay: input = None poll_secs = 1 - def __init__(self,cfg): + def __init__(self, cfg): self.cfg = cfg self.info_lock = threading.Lock() # self.info accessed by 2 threads self.display_kill_flag = threading.Event() def get_input(self): - return get_char(immed_chars='q',prehold_protect=False,num_bytes=1) + return get_char(immed_chars='q', prehold_protect=False, num_bytes=1) - async def process_input(self,rpc): + async def process_input(self, rpc): return True - async def run(self,rpc): + async def run(self, rpc): async def do_display(): with self.info_lock: @@ -68,7 +68,7 @@ class PollDisplay: self.display_kill_flag.set() while True: - threading.Thread(target=get_input,daemon=True).start() + threading.Thread(target=get_input, daemon=True).start() await do_display() if await process_input(): break diff --git a/mmgen_node_tools/Sound.py b/mmgen_node_tools/Sound.py index df9f8ae..557b97a 100755 --- a/mmgen_node_tools/Sound.py +++ b/mmgen_node_tools/Sound.py @@ -19,28 +19,28 @@ mmgen_node_tools.Sound: audio-related functions for MMGen node tools """ -import sys,os,time +import sys, os, time from mmgen.util import die from mmgen_node_tools.Util import do_system _alsa_config_file = '/tmp/alsa-config-' + os.path.basename(sys.argv[0]) -_dvols = { 'Master': 78, 'Speaker': 78, 'Headphone': 15, 'PCM': 190 } +_dvols = {'Master': 78, 'Speaker': 78, 'Headphone': 15, 'PCM': 190} def timespec2secs(ts): import re - mul = { 's': 1, 'm': 60, 'h': 60*60, 'd': 60*60*24 } + mul = {'s': 1, 'm': 60, 'h': 60*60, 'd': 60*60*24} pat = r'^([0-9]+)([smhd]*)$' - m = re.match(pat,ts) + m = re.match(pat, ts) if m is None: die(2,"'%s': invalid time specifier" % ts) - a,b = m.groups() + a, b = m.groups() return int(a) * (mul[b] if b else 1) def parse_repeat_spec(rs): - return [(timespec2secs(i),timespec2secs(j)) - for i,j in [a.split(':') for a in rs.split(',')]] + return [(timespec2secs(i), timespec2secs(j)) + for i, j in [a.split(':') for a in rs.split(',')]] def init_sound(): def _restore_sound(): @@ -51,33 +51,33 @@ def init_sound(): atexit.register(_restore_sound) do_system('sudo alsactl store -f ' + _alsa_config_file) -def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False): +def play_sound(fn, vol, repeat_spec='', remote_host='', kill_flg=None, testing=False): if not remote_host: do_system('sudo alsactl store -f ' + _alsa_config_file) - for k in 'Master','Speaker','Headphone': - do_system(('sudo amixer -q set %s on' % k),testing) + for k in 'Master', 'Speaker', 'Headphone': + do_system(('sudo amixer -q set %s on' % k), testing) # do_system('amixer -q set Headphone off') - vols = dict([(k,int(_dvols[k] * float(vol) / 100)) for k in _dvols]) + vols = dict([(k, int(_dvols[k] * float(vol) / 100)) for k in _dvols]) for k in vols: - do_system('sudo amixer -q set %s %s' % (k,vols[k]),testing) + do_system('sudo amixer -q set %s %s' % (k, vols[k]), testing) fn = os.path.expanduser(fn) cmd = ( 'aplay -q %s' % fn, - 'ssh %s mmnode-play-sound -v%d %s' % (remote_host,vol,fn) + 'ssh %s mmnode-play-sound -v%d %s' % (remote_host, vol, fn) )[bool(remote_host)] if repeat_spec and kill_flg: - for interval,duration in parse_repeat_spec(repeat_spec): + for interval, duration in parse_repeat_spec(repeat_spec): start = time.time() while time.time() < start + duration: - do_system(cmd,testing) + do_system(cmd, testing) if kill_flg.wait(interval): if not remote_host: do_system('sudo alsactl restore -f ' + _alsa_config_file) return else: # Play once - do_system(cmd,testing) + do_system(cmd, testing) if not remote_host: do_system('sudo alsactl restore -f ' + _alsa_config_file) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 95e03f2..ebefcf3 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -29,10 +29,10 @@ from mmgen.util import msg, msg_r, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, lis from mmgen.ui import do_pager homedir = os.getenv('HOME') -dfl_cachedir = os.path.join(homedir,'.cache','mmgen-node-tools') +dfl_cachedir = os.path.join(homedir, '.cache', 'mmgen-node-tools') cfg_fn = 'ticker-cfg.yaml' portfolio_fn = 'ticker-portfolio.yaml' -asset_tuple = namedtuple('asset_tuple',['symbol','id','source']) +asset_tuple = namedtuple('asset_tuple', ['symbol', 'id', 'source']) last_api_host = None percent_cols = { @@ -50,20 +50,19 @@ class DataSource: }, { 'fi': 'yahoospot', 'hi': 'yahoohist', - } - ] + }] @classmethod - def get_sources(cls,randomize=False): - g = random.sample(cls.source_groups,k=len(cls.source_groups)) if randomize else cls.source_groups - return {k:v for a in g for k,v in a.items()} + def get_sources(cls, randomize=False): + g = random.sample(cls.source_groups, k=len(cls.source_groups)) if randomize else cls.source_groups + return {k: v for a in g for k, v in a.items()} class base: def fetch_delay(self): global last_api_host if not gcfg.testing and last_api_host and last_api_host != self.api_host: - delay = 1 + random.randrange(1,5000) / 1000 + delay = 1 + random.randrange(1, 5000) / 1000 msg_r(f'Waiting {delay:.3f} seconds...') time.sleep(delay) msg('') @@ -72,19 +71,18 @@ class DataSource: def get_data_from_network(self): curl_cmd = list_gen( - ['curl', '--tr-encoding', '--header', 'Accept: application/json',True], + ['curl', '--tr-encoding', '--header', 'Accept: application/json', True], ['--compressed'], # adds 'Accept-Encoding: gzip' - ['--proxy', cfg.proxy, isinstance(cfg.proxy,str)], + ['--proxy', cfg.proxy, isinstance(cfg.proxy, str)], ['--silent', not cfg.verbose], - [self.api_url] - ) + [self.api_url]) if gcfg.testing: - Msg(fmt_list(curl_cmd,fmt='bare')) + Msg(fmt_list(curl_cmd, fmt='bare')) return try: - return run(curl_cmd,check=True,stdout=PIPE).stdout.decode() + return run(curl_cmd, check=True, stdout=PIPE).stdout.decode() except CalledProcessError as e: msg('') from .Misc import curl_exit_codes @@ -101,7 +99,7 @@ class DataSource: os.makedirs(cfg.cachedir) if not os.path.exists(self.json_fn): - open(self.json_fn,'w').write('{}') + open(self.json_fn, 'w').write('{}') use_cached_data = cfg.cached_data and not gcfg.download @@ -123,7 +121,7 @@ class DataSource: if gcfg.testing: return {} else: - die(1,self.rate_limit_errmsg(elapsed)) + die(1, self.rate_limit_errmsg(elapsed)) match data_type: case 'json': @@ -131,7 +129,7 @@ class DataSource: data = json.loads(data_in) except: self.json_data_error_msg(data_in) - die(2,'Retrieved data is not valid JSON, exiting') + die(2, 'Retrieved data is not valid JSON, exiting') json_text = data_in case 'python': data = data_in @@ -143,9 +141,9 @@ class DataSource: f'No cached {self.data_desc}! Run command without the --cached-data option, ' 'or use --download to retrieve data from remote host') else: - die(2,'Remote host returned no data!') + die(2, 'Remote host returned no data!') elif 'error' in data: - die(1,data['error']) + die(1, data['error']) if use_cached_data: if not cfg.quiet: @@ -162,15 +160,15 @@ class DataSource: return self.postprocess_data(data) - def json_data_error_msg(self,json_text): + def json_data_error_msg(self, json_text): pass - def postprocess_data(self,data): + def postprocess_data(self, data): return data @property def json_fn_rel(self): - return os.path.relpath(self.json_fn,start=homedir) + return os.path.relpath(self.json_fn, start=homedir) class coinpaprika(base): desc = 'CoinPaprika' @@ -185,30 +183,29 @@ class DataSource: def __init__(self): self.asset_limit = int(cfg.asset_limit or self.dfl_asset_limit) - def rate_limit_errmsg(self,elapsed): + def rate_limit_errmsg(self, elapsed): return ( f'Rate limit exceeded! Retry in {self.timeout-elapsed} seconds' + - ('' if cfg.btc_only else ', or use --cached-data or --btc') - ) + ('' if cfg.btc_only else ', or use --cached-data or --btc')) @property def api_url(self): return ( f'https://{self.api_host}/v1/tickers/btc-bitcoin' if cfg.btc_only else f'https://{self.api_host}/v1/tickers?limit={self.asset_limit}' if self.asset_limit else - f'https://{self.api_host}/v1/tickers' ) + f'https://{self.api_host}/v1/tickers') @property def json_fn(self): return os.path.join( cfg.cachedir, - 'ticker-btc.json' if cfg.btc_only else 'ticker.json' ) + 'ticker-btc.json' if cfg.btc_only else 'ticker.json') @property def timeout(self): return 0 if gcfg.test_suite else self.btc_ratelimit if cfg.btc_only else self.ratelimit - def json_data_error_msg(self,json_text): + def json_data_error_msg(self, json_text): tor_captcha_msg = f""" If you’re using Tor, the API request may have failed due to Captcha protection. A workaround for this issue is to retrieve the JSON data with a browser from @@ -223,20 +220,20 @@ class DataSource: Then invoke the program with --cached-data and without --btc """ msg(json_text[:1024] + '...') - msg(orange(fmt(tor_captcha_msg,strip_char='\t'))) + msg(orange(fmt(tor_captcha_msg, strip_char='\t'))) - def postprocess_data(self,data): + def postprocess_data(self, data): return [data] if cfg.btc_only else data @staticmethod - def parse_asset_id(s,require_label): - sym,label = (*s.split('-',1),None)[:2] + def parse_asset_id(s, require_label): + sym, label = (*s.split('-', 1), None)[:2] if require_label and not label: - die(1,f'{s!r}: asset label is missing') + die(1, f'{s!r}: asset label is missing') return asset_tuple( symbol = sym.upper(), id = (s.lower() if label else None), - source = 'cc' ) + source = 'cc') class yahoospot(base): @@ -250,12 +247,12 @@ class DataSource: json_fn_basename = 'ticker-finance.json' @staticmethod - def get_id(sym,data): + def get_id(sym, data): return sym.lower() @staticmethod - def conv_data(sym,data,btcusd): - price_usd = Decimal( data['regularMarketPrice']['raw'] ) + def conv_data(sym, data, btcusd): + price_usd = Decimal(data['regularMarketPrice']['raw']) return { 'id': sym, 'name': data['shortName'], @@ -266,15 +263,14 @@ class DataSource: 'percent_change_30d': data['pct_chg_4wks'], 'percent_change_7d': data['pct_chg_1wk'], 'percent_change_24h': data['regularMarketChangePercent']['raw'] * 100, - 'last_updated': data['regularMarketTime'], - } + 'last_updated': data['regularMarketTime']} - def rate_limit_errmsg(self,elapsed): + def rate_limit_errmsg(self, elapsed): return f'Rate limit exceeded! Retry in {self.timeout-elapsed} seconds, or use --cached-data' @property def json_fn(self): - return os.path.join( cfg.cachedir, self.json_fn_basename ) + return os.path.join(cfg.cachedir, self.json_fn_basename) @property def timeout(self): @@ -282,37 +278,36 @@ class DataSource: @property def symbols(self): - return [r.symbol for r in cfg.rows if isinstance(r,tuple) and r.source == 'fi'] + return [r.symbol for r in cfg.rows if isinstance(r, tuple) and r.source == 'fi'] def get_data_from_network(self): kwargs = { 'formatted': True, 'asynchronous': True, - 'proxies': { 'https': cfg.proxy2 }, - } + 'proxies': {'https': cfg.proxy2}} if gcfg.test_suite: - kwargs.update({ 'timeout': 1, 'retry': 0 }) + kwargs.update({'timeout': 1, 'retry': 0}) if gcfg.testing: Msg('\nyahooquery.Ticker(\n {},\n {}\n)'.format( self.symbols, - fmt_dict(kwargs,fmt='kwargs') )) + fmt_dict(kwargs, fmt='kwargs'))) return from yahooquery import Ticker - return self.process_network_data( Ticker(self.symbols,**kwargs) ) + return self.process_network_data(Ticker(self.symbols,**kwargs)) - def process_network_data(self,ticker): + def process_network_data(self, ticker): return ticker.price @staticmethod - def parse_asset_id(s,require_label): + def parse_asset_id(s, require_label): return asset_tuple( symbol = s.upper(), id = s.lower(), - source = 'fi' ) + source = 'fi') class yahoohist(yahoospot): @@ -322,17 +317,17 @@ class DataSource: period = '1y' interval = '1wk' - def process_network_data(self,ticker): + def process_network_data(self, ticker): return ticker.history( period = self.period, interval = self.interval).to_json(orient='index') - def postprocess_data(self,data): + def postprocess_data(self, data): def gen(): keys = set() d = {} for key, val in data.items(): - if m := re.match(r"\('(.*?)', datetime\.date\((.*)\)\)$",key): + if m := re.match(r"\('(.*?)', datetime\.date\((.*)\)\)$", key): date = '{}-{:>02}-{:>02}'.format(*m[2].split(', ')) if (sym := m[1]) in keys: d[date] = val @@ -343,12 +338,12 @@ class DataSource: return dict(gen()) def assets_list_gen(cfg_in): - for k,v in cfg_in.cfg['assets'].items(): + for k, v in cfg_in.cfg['assets'].items(): yield '' yield k.upper() for e in v: - out = e.split('-',1) - yield ' {:5s} {}'.format(out[0],out[1] if len(out) == 2 else '') + out = e.split('-', 1) + yield ' {:5s} {}'.format(out[0], out[1] if len(out) == 2 else '') def gen_data(data): """ @@ -367,10 +362,9 @@ def gen_data(data): f'The symbol {dup_sym!r} is shared by the following assets:\n' + '\n ' + '\n '.join(d['id'] for d in data['cc'] if d['symbol'] == dup_sym) + '\n\nPlease specify the asset by one of the full IDs listed above\n' + - f'instead of {dup_sym!r}' - ) + f'instead of {dup_sym!r}') - def check_assets_found(wants,found,keys=['symbol','id']): + def check_assets_found(wants, found, keys=['symbol', 'id']): error = False for k in keys: missing = wants[k] - found[k] @@ -378,36 +372,32 @@ def gen_data(data): msg( ('The following IDs were not found in source data:\n{}' if k == 'id' else 'The following symbols could not be resolved:\n{}').format( - fmt_list(missing,fmt='col',indent=' ') - )) + fmt_list(missing, fmt='col', indent=' '))) error = True if error: - die(1,'Missing data, exiting') + die(1, 'Missing data, exiting') rows_want = { - 'id': {r.id for r in cfg.rows if isinstance(r,tuple) and r.id} - {'usd-us-dollar'}, - 'symbol': {r.symbol for r in cfg.rows if isinstance(r,tuple) and r.id is None} - {'USD'}, - } + 'id': {r.id for r in cfg.rows if isinstance(r, tuple) and r.id} - {'usd-us-dollar'}, + 'symbol': {r.symbol for r in cfg.rows if isinstance(r, tuple) and r.id is None} - {'USD'}} usr_rate_assets = tuple(u.rate_asset for u in cfg.usr_rows + cfg.usr_columns if u.rate_asset) usr_rate_assets_want = { 'id': {a.id for a in usr_rate_assets if a.id}, - 'symbol': {a.symbol for a in usr_rate_assets if not a.id} - } + 'symbol': {a.symbol for a in usr_rate_assets if not a.id}} usr_assets = cfg.usr_rows + cfg.usr_columns + tuple(c for c in (cfg.query or ()) if c) usr_wants = { 'id': ( {a.id for a in usr_assets + usr_rate_assets if a.id} - - {a.id for a in usr_assets if a.rate and a.id} - {'usd-us-dollar'} ) + {a.id for a in usr_assets if a.rate and a.id} - {'usd-us-dollar'}) , 'symbol': ( {a.symbol for a in usr_assets + usr_rate_assets if not a.id} - - {a.symbol for a in usr_assets if a.rate} - {'USD'} ), - } + {a.symbol for a in usr_assets if a.rate} - {'USD'})} - found = { 'id': set(), 'symbol': set() } + found = {'id': set(), 'symbol': set()} rate_assets = {} - wants = {k:rows_want[k] | usr_wants[k] for k in ('id','symbol')} + wants = {k: rows_want[k] | usr_wants[k] for k in ('id', 'symbol')} for d in data['cc']: if d['id'] == 'btc-bitcoin': @@ -417,38 +407,37 @@ def gen_data(data): get_id = src_cls['fi'].get_id conv_func = src_cls['fi'].conv_data - for k,v in data['fi'].items(): - id = get_id(k,v) + for k, v in data['fi'].items(): + id = get_id(k, v) if wants['id']: if id in wants['id']: - if not isinstance(v,dict): + if not isinstance(v, dict): die(2, str(v)) if id in found['id']: - die(1,dup_sym_errmsg(id)) + die(1, dup_sym_errmsg(id)) if m := data['hi'].get(k): spot = v['regularMarketPrice']['raw'] hist = tuple(m.values()) v['pct_chg_1wk'], v['pct_chg_4wks'], v['pct_chg_1y'] = ( (spot / hist[-2]['close'] - 1) * 100, (spot / hist[-5]['close'] - 1) * 100, # 4 weeks ≈ 1 month - (spot / hist[0]['close'] - 1) * 100, - ) + (spot / hist[0]['close'] - 1) * 100) else: v['pct_chg_1wk'] = v['pct_chg_4wks'] = v['pct_chg_1y'] = None - yield ( id, conv_func(id,v,btcusd) ) + yield (id, conv_func(id, v, btcusd)) found['id'].add(id) wants['id'].remove(id) if id in usr_rate_assets_want['id']: - rate_assets[k] = conv_func(id,v,btcusd) # NB: using symbol instead of ID for key + rate_assets[k] = conv_func(id, v, btcusd) # NB: using symbol instead of ID for key else: break - for k in ('id','symbol'): + for k in ('id', 'symbol'): for d in data['cc']: if wants[k]: if d[k] in wants[k]: if d[k] in found[k]: - die(1,dup_sym_errmsg(d[k])) + die(1, dup_sym_errmsg(d[k])) if not 'price_usd' in d: d['price_usd'] = Decimal(str(d['quotes']['USD']['price'])) d['price_btc'] = Decimal(str(d['quotes']['USD']['price'])) / btcusd @@ -457,8 +446,9 @@ def gen_data(data): d['percent_change_30d'] = d['quotes']['USD']['percent_change_30d'] d['percent_change_1y'] = d['quotes']['USD']['percent_change_1y'] # .replace('Z','+00:00') -- Python 3.9 backport - d['last_updated'] = int(datetime.datetime.fromisoformat(d['last_updated'].replace('Z','+00:00')).timestamp()) - yield (d['id'],d) + d['last_updated'] = int(datetime.datetime.fromisoformat( + d['last_updated'].replace('Z', '+00:00')).timestamp()) + yield (d['id'], d) found[k].add(d[k]) wants[k].remove(d[k]) if d[k] in usr_rate_assets_want[k]: @@ -466,7 +456,7 @@ def gen_data(data): else: break - check_assets_found(usr_wants,found) + check_assets_found(usr_wants, found) for asset in (cfg.usr_rows + cfg.usr_columns): if asset.rate: @@ -475,14 +465,13 @@ def gen_data(data): """ _id = asset.id or f'{asset.symbol}-user-asset-{asset.symbol}'.lower() ra_rate = rate_assets[asset.rate_asset.symbol]['price_usd'] if asset.rate_asset else 1 - yield ( _id, { + yield (_id, { 'symbol': asset.symbol, 'id': _id, 'name': ' '.join(_id.split('-')[1:]), 'price_usd': ra_rate / asset.rate, 'price_btc': ra_rate / asset.rate / btcusd, - 'last_updated': None, - }) + 'last_updated': None}) yield ('usd-us-dollar', { 'symbol': 'USD', @@ -490,21 +479,20 @@ def gen_data(data): 'name': 'US Dollar', 'price_usd': Decimal(1), 'price_btc': Decimal(1) / btcusd, - 'last_updated': None, - }) + 'last_updated': None}) def main(): def update_sample_file(usr_cfg_file): - usr_data = files('mmgen_node_tools').joinpath('data',os.path.basename(usr_cfg_file)).read_text() + usr_data = files('mmgen_node_tools').joinpath('data', os.path.basename(usr_cfg_file)).read_text() sample_file = usr_cfg_file + '.sample' sample_data = open(sample_file).read() if os.path.exists(sample_file) else None if usr_data != sample_data: - os.makedirs(os.path.dirname(sample_file),exist_ok=True) + os.makedirs(os.path.dirname(sample_file), exist_ok=True) msg('{} {}'.format( - ('Updating','Creating')[sample_data is None], - sample_file )) - open(sample_file,'w').write(usr_data) + ('Updating', 'Creating')[sample_data is None], + sample_file)) + open(sample_file, 'w').write(usr_data) try: from importlib.resources import files # Python 3.9 @@ -515,19 +503,19 @@ def main(): update_sample_file(cfg_in.portfolio_file) if gcfg.portfolio and not cfg_in.portfolio: - die(1,'No portfolio configured!\nTo configure a portfolio, edit the file ~/{}'.format( - os.path.relpath(cfg_in.portfolio_file,start=homedir))) + die(1, 'No portfolio configured!\nTo configure a portfolio, edit the file ~/{}'.format( + os.path.relpath(cfg_in.portfolio_file, start=homedir))) if gcfg.list_ids: src_ids = ['cc'] elif gcfg.download: if not gcfg.download in DataSource.get_sources(): - die(1,f'{gcfg.download!r}: invalid data source') + die(1, f'{gcfg.download!r}: invalid data source') src_ids = [gcfg.download] else: src_ids = DataSource.get_sources(randomize=True) - src_data = { k: src_cls[k]().get_data() for k in src_ids } + src_data = {k: src_cls[k]().get_data() for k in src_ids} if gcfg.testing: return @@ -542,24 +530,24 @@ def main(): data = dict(gen_data(src_data)) (do_pager if cfg.pager else Msg_r)( - '\n'.join(getattr(Ticker,cfg.clsname)(data).gen_output()) + '\n') + '\n'.join(getattr(Ticker, cfg.clsname)(data).gen_output()) + '\n') def make_cfg(gcfg_arg): - query_tuple = namedtuple('query',['asset','to_asset']) - asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset','source']) + query_tuple = namedtuple('query', ['asset', 'to_asset']) + asset_data = namedtuple('asset_data', ['symbol', 'id', 'amount', 'rate', 'rate_asset', 'source']) - def parse_asset_id(s,require_label=False): - return src_cls['fi' if re.match(fi_pat,s) else 'cc'].parse_asset_id(s,require_label) + def parse_asset_id(s, require_label=False): + return src_cls['fi' if re.match(fi_pat, s) else 'cc'].parse_asset_id(s, require_label) def get_rows_from_cfg(add_data=None): def gen(): - for n,(k,v) in enumerate(cfg_in.cfg['assets'].items()): + for n, (k, v) in enumerate(cfg_in.cfg['assets'].items()): yield k if add_data and k in add_data: v += tuple(add_data[k]) for e in v: - yield parse_asset_id(e,require_label=True) + yield parse_asset_id(e, require_label=True) return tuple(gen()) def parse_percent_cols(arg): @@ -568,17 +556,19 @@ def make_cfg(gcfg_arg): res = arg.lower().split(',') for s in res: if s not in percent_cols: - die(1,f'{arg!r}: invalid --percent-cols parameter (valid letters: {fmt_list(percent_cols)})') + die(1, '{!r}: invalid --percent-cols parameter (valid letters: {})'.format( + arg, + fmt_list(percent_cols))) return res - def parse_usr_asset_arg(key,use_cf_file=False): + def parse_usr_asset_arg(key, use_cf_file=False): """ asset_id[:rate[:rate_asset]] """ def parse_parm(s): ss = s.split(':') - assert len(ss) in (1,2,3), f'{s}: malformed argument' - asset_id,rate,rate_asset = (*ss,None,None)[:3] + assert len(ss) in (1, 2, 3), f'{s}: malformed argument' + asset_id, rate, rate_asset = (*ss, None, None)[:3] parsed_id = parse_asset_id(asset_id) return asset_data( @@ -588,19 +578,19 @@ def make_cfg(gcfg_arg): rate = ( None if rate is None else 1 / Decimal(rate[:-1]) if rate.lower().endswith('r') else - Decimal(rate) ), + Decimal(rate)), rate_asset = parse_asset_id(rate_asset) if rate_asset else None, - source = parsed_id.source ) + source = parsed_id.source) - cl_opt = getattr(gcfg,key) + cl_opt = getattr(gcfg, key) cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else [] - return tuple( parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt) ) + return tuple(parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt)) def parse_query_arg(s): """ asset_id:amount[:to_asset_id[:to_amount]] """ - def parse_query_asset(asset_id,amount): + def parse_query_asset(asset_id, amount): parsed_id = parse_asset_id(asset_id) return asset_data( symbol = parsed_id.symbol, @@ -608,21 +598,20 @@ def make_cfg(gcfg_arg): amount = None if amount is None else Decimal(amount), rate = None, rate_asset = None, - source = parsed_id.source ) + source = parsed_id.source) ss = s.split(':') - assert len(ss) in (2,3,4), f'{s}: malformed argument' - asset_id,amount,to_asset_id,to_amount = (*ss,None,None)[:4] + assert len(ss) in (2, 3, 4), f'{s}: malformed argument' + asset_id, amount, to_asset_id, to_amount = (*ss, None, None)[:4] return query_tuple( - asset = parse_query_asset(asset_id,amount), - to_asset = parse_query_asset(to_asset_id,to_amount) if to_asset_id else None - ) + asset = parse_query_asset(asset_id, amount), + to_asset = parse_query_asset(to_asset_id, to_amount) if to_asset_id else None) - def gen_uniq(obj_list,key,preload=None): - found = set([getattr(obj,key) for obj in preload if hasattr(obj,key)] if preload else ()) + def gen_uniq(obj_list, key, preload=None): + found = set([getattr(obj, key) for obj in preload if hasattr(obj, key)] if preload else ()) for obj in obj_list: - id = getattr(obj,key) + id = getattr(obj, key) if id not in found: yield obj found.add(id) @@ -632,39 +621,38 @@ def make_cfg(gcfg_arg): 'user_added', usr_rows + (tuple(asset for asset in query if asset) if query else ()) + - usr_columns ) + usr_columns) def get_portfolio_assets(ret=()): if cfg_in.portfolio and gcfg.portfolio: - ret = (parse_asset_id(e,require_label=True) for e in cfg_in.portfolio) - return ( 'portfolio', tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC') ) + ret = (parse_asset_id(e, require_label=True) for e in cfg_in.portfolio) + return ('portfolio', tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC')) def get_portfolio(): - return {k:Decimal(v) for k,v in cfg_in.portfolio.items() if (not gcfg.btc) or k == 'btc-bitcoin'} + return {k: Decimal(v) for k, v in cfg_in.portfolio.items() + if (not gcfg.btc) or k == 'btc-bitcoin'} def parse_add_precision(arg): if not arg: return 0 s = str(arg) if not (s.isdigit() and s.isascii()): - die(1,f'{s}: invalid parameter for --add-precision (not an integer)') + die(1, f'{s}: invalid parameter for --add-precision (not an integer)') if int(s) > 30: - die(1,f'{s}: invalid parameter for --add-precision (value >30)') + die(1, f'{s}: invalid parameter for --add-precision (value >30)') return int(s) def create_rows(): rows = ( ('trade_pair',) + query if (query and query.to_asset) else - ('bitcoin',parse_asset_id('btc-bitcoin')) if gcfg.btc else - get_rows_from_cfg( add_data={'fiat':['usd-us-dollar']} if gcfg.add_columns else None ) - ) + ('bitcoin', parse_asset_id('btc-bitcoin')) if gcfg.btc else + get_rows_from_cfg(add_data={'fiat':['usd-us-dollar']} if gcfg.add_columns else None)) - for hdr,data in ( - (get_usr_assets(),) if query else - (get_usr_assets(), get_portfolio_assets()) - ): + for hdr, data in ( + (get_usr_assets(),) if query else + (get_usr_assets(), get_portfolio_assets())): if data: - uniq_data = tuple(gen_uniq(data,'symbol',preload=rows)) + uniq_data = tuple(gen_uniq(data, 'symbol', preload=rows)) if uniq_data: rows += (hdr,) + uniq_data return rows @@ -693,26 +681,25 @@ def make_cfg(gcfg_arg): 'quiet', 'verbose']) - global gcfg,cfg_in,src_cls,cfg + global gcfg, cfg_in, src_cls, cfg gcfg = gcfg_arg - src_cls = { k: getattr(DataSource,v) for k,v in DataSource.get_sources().items() } + src_cls = {k: getattr(DataSource, v) for k, v in DataSource.get_sources().items()} fi_pat = src_cls['fi'].asset_id_pat cmd_args = gcfg._args cfg_in = get_cfg_in() usr_rows = parse_usr_asset_arg('add_rows') - usr_columns = parse_usr_asset_arg('add_columns',use_cf_file=True) + usr_columns = parse_usr_asset_arg('add_columns', use_cf_file=True) query = parse_query_arg(cmd_args[0]) if cmd_args else None def get_proxy(name): - proxy = getattr(gcfg,name) + proxy = getattr(gcfg, name) return ( '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none') - else (proxy or cfg_in.cfg.get(name)) - ) + else (proxy or cfg_in.cfg.get(name))) proxy = get_proxy('proxy') proxy = None if proxy == 'none' else proxy @@ -723,7 +710,7 @@ def make_cfg(gcfg_arg): usr_rows = usr_rows, usr_columns = usr_columns, query = query, - adjust = ( lambda x: (100 + x) / 100 if x else 1 )( Decimal(gcfg.adjust or 0) ), + adjust = (lambda x: (100 + x) / 100 if x else 1)(Decimal(gcfg.adjust or 0)), clsname = 'trading' if query else 'overview', btc_only = gcfg.btc or cfg_in.cfg.get('btc'), add_prec = parse_add_precision(gcfg.add_precision or cfg_in.cfg.get('add_precision')), @@ -745,17 +732,16 @@ def make_cfg(gcfg_arg): thousands_comma = gcfg.thousands_comma or cfg_in.cfg.get('thousands_comma'), update_time = gcfg.update_time or cfg_in.cfg.get('update_time'), quiet = gcfg.quiet or cfg_in.cfg.get('quiet'), - verbose = gcfg.verbose or cfg_in.cfg.get('verbose'), - ) + verbose = gcfg.verbose or cfg_in.cfg.get('verbose')) def get_cfg_in(): - ret = namedtuple('cfg_in_data',['cfg','portfolio','cfg_file','portfolio_file']) - cfg_file,portfolio_file = ( - [os.path.join(gcfg.data_dir_root,'node_tools',fn) for fn in (cfg_fn,portfolio_fn)] - ) - cfg_data,portfolio_data = ( - [yaml.safe_load(open(fn).read()) if os.path.exists(fn) else None for fn in (cfg_file,portfolio_file)] - ) + ret = namedtuple('cfg_in_data', ['cfg', 'portfolio', 'cfg_file', 'portfolio_file']) + cfg_file, portfolio_file = ( + [os.path.join(gcfg.data_dir_root, 'node_tools', fn) + for fn in (cfg_fn, portfolio_fn)]) + cfg_data, portfolio_data = ( + [yaml.safe_load(open(fn).read()) if os.path.exists(fn) else None + for fn in (cfg_file, portfolio_file)]) return ret( cfg = cfg_data or { 'assets': { @@ -765,14 +751,11 @@ def get_cfg_in(): # Pound Sterling, Euro, Swiss Franc 'fiat': [ 'gbpusd=x', 'eurusd=x', 'chfusd=x' ], # Dow Jones Industrials, Nasdaq 100, S&P 500 - 'index': [ '^dji', '^ixic', '^gspc' ], - }, - 'proxy': 'http://vpn-gw:8118' - }, - portfolio = portfolio_data, - cfg_file = cfg_file, - portfolio_file = portfolio_file, - ) + 'index': [ '^dji', '^ixic', '^gspc' ]}, + 'proxy': 'http://vpn-gw:8118'}, + portfolio = portfolio_data, + cfg_file = cfg_file, + portfolio_file = portfolio_file) class Ticker: @@ -781,43 +764,43 @@ class Ticker: offer = None to_asset = None - def __init__(self,data): + def __init__(self, data): self.comma = ',' if cfg.thousands_comma else '' - self.col1_wid = max(len('TOTAL'),( + self.col1_wid = max(len('TOTAL'), ( max(len(self.create_label(d['id'])) for d in data.values()) if cfg.name_labels else - max(len(d['symbol']) for d in data.values()) - )) + 1 + max(len(d['symbol']) for d in data.values()))) + 1 - self.rows = [row._replace(id=self.get_id(row)) if isinstance(row,tuple) else row for row in cfg.rows] - self.col_usd_prices = {k:self.data[k]['price_usd'] for k in self.col_ids} + self.rows = [row._replace(id=self.get_id(row)) if isinstance(row, tuple) else row + for row in cfg.rows] + self.col_usd_prices = {k: self.data[k]['price_usd'] for k in self.col_ids} - self.prices = {row.id:self.get_row_prices(row.id) - for row in self.rows if isinstance(row,tuple) and row.id in data} + self.prices = {row.id: self.get_row_prices(row.id) + for row in self.rows if isinstance(row, tuple) and row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') - def format_last_update_col(self,cross_assets=()): + def format_last_update_col(self, cross_assets=()): if cfg.elapsed: from mmgen.util2 import format_elapsed_hr fmt_func = format_elapsed_hr else: - fmt_func = lambda t,now: time.strftime('%F %X', time.gmtime(t)) + fmt_func = lambda t, now: time.strftime('%F %X', time.gmtime(t)) d = self.data max_w = 0 if cross_assets: last_updated_x = [d[a.id]['last_updated'] for a in cross_assets] - min_t = min( (int(n) for n in last_updated_x if isinstance(n,int) ), default=None ) + min_t = min((int(n) for n in last_updated_x if isinstance(n, int)), default=None) else: min_t = None for row in self.rows: - if isinstance(row,tuple): + if isinstance(row, tuple): try: - t = int( d[row.id]['last_updated'] ) + t = int(d[row.id]['last_updated']) except TypeError as e: d[row.id]['last_updated_fmt'] = gray('--' if 'NoneType' in str(e) else str(e)) except KeyError as e: @@ -825,7 +808,7 @@ class Ticker: pass else: t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( - (min(t,min_t) if min_t else t), + (min(t, min_t) if min_t else t), now = now) max_w = max(len(t_fmt), max_w) @@ -836,7 +819,7 @@ class Ticker: self.uprec = {k: max(0, v+4) + cfg.add_prec for k, v in exp} self.uwid = {k: 12 + max(0, abs(v)-6) + cfg.add_prec for k, v in exp} - def get_id(self,asset): + def get_id(self, asset): if asset.id: return asset.id else: @@ -844,11 +827,11 @@ class Ticker: if d['symbol'] == asset.symbol: return d['id'] - def create_label(self,id): + def create_label(self, id): return self.data[id]['name'].upper() def gen_output(self): - yield 'Current time: {} UTC'.format(time.strftime('%F %X',time.gmtime(now))) + yield 'Current time: {} UTC'.format(time.strftime('%F %X', time.gmtime(now))) for asset in self.usr_col_assets: if asset.symbol != 'USD': @@ -858,17 +841,16 @@ class Ticker: self.create_label(asset.id), usdprice, self.comma, - max(2, 4-usdprice.adjusted()) ) + max(2, 4-usdprice.adjusted())) - if hasattr(self,'subhdr'): + if hasattr(self, 'subhdr'): yield self.subhdr if self.show_adj: yield ( ('Offered price differs from spot' if self.offer else 'Adjusting prices') + ' by ' - + yellow('{:+.2f}%'.format( (self.adjust-1) * 100 )) - ) + + yellow('{:+.2f}%'.format((self.adjust-1) * 100))) yield '' @@ -879,7 +861,7 @@ class Ticker: yield self.table_hdr for row in self.rows: - if isinstance(row,str): + if isinstance(row, str): yield ('-' * self.hl_wid) else: try: @@ -896,21 +878,20 @@ class Ticker: yield blue('PORTFOLIO') yield self.table_hdr yield '-' * self.hl_wid - for sym,amt in cfg.portfolio.items(): + for sym, amt in cfg.portfolio.items(): try: - yield self.fmt_row(self.data[sym],amt=amt) + yield self.fmt_row(self.data[sym], amt=amt) except KeyError: yield gray(f'(no data for {sym})') yield '-' * self.hl_wid if not cfg.btc_only: yield self.fs_num.format( lbl = 'TOTAL', pc3='', pc4='', pc1='', pc2='', upd='', amt='', - **{ k.replace('-','_'): v for k,v in self.prices['total'].items() } - ) + **{k.replace('-', '_'): v for k, v in self.prices['total'].items()}) class overview(base): - def __init__(self,data): + def __init__(self, data): self.data = data self.adjust = cfg.adjust self.show_adj = self.adjust != 1 @@ -922,25 +903,26 @@ class Ticker: self.format_last_update_col() if cfg.portfolio: - self.prices['total'] = { col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] - for row in self.rows if isinstance(row,tuple) and row.id in cfg.portfolio and row.id in data) - for col_id in self.col_ids } + self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] + for row in self.rows + if isinstance(row, tuple) and row.id in cfg.portfolio and row.id in data) + for col_id in self.col_ids} self.init_prec() self.init_fs() - def get_row_prices(self,id): + def get_row_prices(self, id): if id in self.data: d = self.data[id] - return { k: ( + return {k: ( d['price_btc'] if k == 'btc-bitcoin' else d['price_usd'] / self.col_usd_prices[k] - ) * self.adjust for k in self.col_ids } + ) * self.adjust for k in self.col_ids} - def fmt_row(self,d,amt=None,amt_fmt=None): + def fmt_row(self, d, amt=None, amt_fmt=None): def fmt_pct(n): - return gray(' --') if n is None else (red,green)[n>=0](f'{n:+7.2f}') + return gray(' --') if n is None else (red, green)[n>=0](f'{n:+7.2f}') p = self.prices[d['id']] @@ -957,53 +939,45 @@ class Ticker: pc4 = fmt_pct(d.get('percent_change_30d')), upd = d.get('last_updated_fmt'), amt = amt_fmt, - **{ k.replace('-','_'): v * (1 if amt is None else amt) for k,v in p.items() } - ) + **{k.replace('-', '_'): v * (1 if amt is None else amt) for k, v in p.items()}) def init_fs(self): - col_prec = {'usd-us-dollar':2+cfg.add_prec,'btc-bitcoin':8+cfg.add_prec } # | self.uprec # Python 3.9 - col_prec.update(self.uprec) - col_wid = {'usd-us-dollar':8+cfg.add_prec,'btc-bitcoin':12+cfg.add_prec } # """ - col_wid.update(self.uwid) + col_prec = {'usd-us-dollar': 2+cfg.add_prec, 'btc-bitcoin': 8+cfg.add_prec} | self.uprec max_row = max( - ( (k,v['btc-bitcoin']) for k,v in self.prices.items() ), - key = lambda a: a[1] - ) - widths = { k: len('{:{}.{}f}'.format( self.prices[max_row[0]][k], self.comma, col_prec[k] )) - for k in self.col_ids } + ((k, v['btc-bitcoin']) for k, v in self.prices.items()), + key = lambda a: a[1]) + widths = {k: len('{:{}.{}f}'.format(self.prices[max_row[0]][k], self.comma, col_prec[k])) + for k in self.col_ids} - fd = namedtuple('format_str_data',['fs_str','fs_num','wid']) + fd = namedtuple('format_str_data', ['fs_str', 'fs_num', 'wid']) col_fs_data = { - 'label': fd(f'{{lbl:{self.col1_wid}}}',f'{{lbl:{self.col1_wid}}}',self.col1_wid), + 'label': fd(f'{{lbl:{self.col1_wid}}}', f'{{lbl:{self.col1_wid}}}', self.col1_wid), 'pct1y': fd(' {pc3:7}', ' {pc3:7}', 8), 'pct1m': fd(' {pc4:7}', ' {pc4:7}', 8), 'pct1w': fd(' {pc1:7}', ' {pc1:7}', 8), 'pct1d': fd(' {pc2:7}', ' {pc2:7}', 8), - 'update_time': fd(' {upd}', ' {upd}', max((19 if cfg.portfolio else 0),self.upd_w) + 2), - 'amt': fd(' {amt}', ' {amt}', 21), - } -# } | { k: fd( # Python 3.9 - col_fs_data.update({ k: fd( - ' {{{}:>{}}}'.format( k.replace('-','_'), widths[k] ), - ' {{{}:{}{}.{}f}}'.format( k.replace('-','_'), widths[k], self.comma, col_prec[k] ), - widths[k]+2 - ) for k in self.col_ids - }) + 'update_time': fd(' {upd}', ' {upd}', + max((19 if cfg.portfolio else 0), self.upd_w) + 2), + 'amt': fd(' {amt}', ' {amt}', 21) + } | {k: fd( + ' {{{}:>{}}}'.format(k.replace('-', '_'), widths[k]), + ' {{{}:{}{}.{}f}}'.format(k.replace('-', '_'), widths[k], self.comma, col_prec[k]), + widths[k] + 2 + ) for k in self.col_ids} cols = ( - ['label','usd-us-dollar'] + + ['label', 'usd-us-dollar'] + [asset.id for asset in self.usr_col_assets] + - [a for a,b in ( - ( 'btc-bitcoin', not cfg.btc_only ), - ( 'pct1y', 'y' in cfg.percent_cols ), - ( 'pct1m', 'm' in cfg.percent_cols ), - ( 'pct1w', 'w' in cfg.percent_cols ), - ( 'pct1d', 'd' in cfg.percent_cols ), - ( 'update_time', cfg.update_time ), - ) if b] - ) + [a for a, b in ( + ('btc-bitcoin', not cfg.btc_only), + ('pct1y', 'y' in cfg.percent_cols), + ('pct1m', 'm' in cfg.percent_cols), + ('pct1w', 'w' in cfg.percent_cols), + ('pct1d', 'd' in cfg.percent_cols), + ('update_time', cfg.update_time)) + if b]) cols2 = list(cols) if cfg.update_time: cols2.pop() @@ -1029,17 +1003,16 @@ class Ticker: amt = ' AMOUNT', usd_us_dollar = 'USD', btc_bitcoin = ' BTC', - **{ a.id.replace('-','_'): a.symbol for a in self.usr_col_assets } - ) + **{a.id.replace('-', '_'): a.symbol for a in self.usr_col_assets}) class trading(base): - def __init__(self,data): + def __init__(self, data): self.data = data self.asset = cfg.query.asset._replace(id=self.get_id(cfg.query.asset)) self.to_asset = ( cfg.query.to_asset._replace(id=self.get_id(cfg.query.to_asset)) - if cfg.query.to_asset else None ) + if cfg.query.to_asset else None) self.col_ids = [self.asset.id] self.adjust = cfg.adjust if self.to_asset: @@ -1048,12 +1021,13 @@ class Ticker: real_price = ( self.asset.amount * data[self.asset.id]['price_usd'] - / data[self.to_asset.id]['price_usd'] - ) + / data[self.to_asset.id]['price_usd']) if self.adjust != 1: - die(1,'the --adjust option may not be combined with TO_AMOUNT in the trade specifier') + die(1, + 'the --adjust option may not be combined with TO_AMOUNT ' + 'in the trade specifier') self.adjust = self.offer / real_price - self.hl_ids = [self.asset.id,self.to_asset.id] + self.hl_ids = [self.asset.id, self.to_asset.id] else: self.hl_ids = [self.asset.id] @@ -1070,10 +1044,10 @@ class Ticker: self.init_prec() self.init_fs() - def get_row_prices(self,id): + def get_row_prices(self, id): if id in self.data: d = self.data[id] - return { k: self.col_usd_prices[self.asset.id] / d['price_usd'] for k in self.col_ids } + return {k: self.col_usd_prices[self.asset.id] / d['price_usd'] for k in self.col_ids} def init_fs(self): self.max_wid = max( @@ -1081,10 +1055,8 @@ class Ticker: v[self.asset.id] * self.asset.amount, 16 + cfg.add_prec, self.comma, - 8 + cfg.add_prec - )) - for v in self.prices.values() - ) + 8 + cfg.add_prec)) + for v in self.prices.values()) self.fs_str = '{lbl:%s} {p_spot}' % self.col1_wid self.hl_wid = self.col1_wid + self.max_wid + 1 if self.show_adj: @@ -1094,20 +1066,19 @@ class Ticker: self.fs_str += ' {upd}' self.hl_wid += self.upd_w + 2 - def fmt_row(self,d): + def fmt_row(self, d): id = d['id'] p = self.prices[id][self.asset.id] * self.asset.amount - p_spot = '{:{}{}.{}f}'.format( p, self.max_wid, self.comma, 8+cfg.add_prec ) + p_spot = '{:{}{}.{}f}'.format(p, self.max_wid, self.comma, 8+cfg.add_prec) p_adj = ( - '{:{}{}.{}f}'.format( p*self.adjust, self.max_wid, self.comma, 8+cfg.add_prec ) - if self.show_adj else '' ) + '{:{}{}.{}f}'.format(p*self.adjust, self.max_wid, self.comma, 8+cfg.add_prec) + if self.show_adj else '') return self.fs_str.format( lbl = self.create_label(id) if cfg.name_labels else d['symbol'], p_spot = green(p_spot) if id in self.hl_ids else p_spot, p_adj = yellow(p_adj) if id in self.hl_ids else p_adj, - upd = d.get('last_updated_fmt'), - ) + upd = d.get('last_updated_fmt')) @property def table_hdr(self): @@ -1115,12 +1086,11 @@ class Ticker: lbl = '', p_spot = '{t:>{w}}'.format( t = 'SPOT PRICE', - w = self.max_wid ), + w = self.max_wid), p_adj = '{t:>{w}}'.format( t = ('OFFERED' if self.offer else 'ADJUSTED') + ' PRICE', - w = self.max_wid ), - upd = 'UPDATED' - ) + w = self.max_wid), + upd = 'UPDATED') @property def subhdr(self): @@ -1133,9 +1103,8 @@ class Ticker: ) + ( ( ' =>' + - (' {:{}}'.format(self.offer,self.comma) if self.offer else '') + + (' {:{}}'.format(self.offer, self.comma) if self.offer else '') + ' {} ({})'.format( self.to_asset.symbol, - self.create_label(self.to_asset.id) ) - ) if self.to_asset else '' ) - ) + self.create_label(self.to_asset.id)) + ) if self.to_asset else '')) diff --git a/mmgen_node_tools/Util.py b/mmgen_node_tools/Util.py index 1d5426b..f36faad 100755 --- a/mmgen_node_tools/Util.py +++ b/mmgen_node_tools/Util.py @@ -21,53 +21,52 @@ mmgen_node_tools.Util: utility functions for MMGen node tools import time -def get_hms(t=None,utc=False,no_secs=False): +def get_hms(t=None, utc=False, no_secs=False): secs = t or time.time() - ret = (time.localtime,time.gmtime)[utc](secs) - fs,n = (('{:02}:{:02}:{:02}',6),('{:02}:{:02}',5))[no_secs] + ret = (time.localtime, time.gmtime)[utc](secs) + fs, n = (('{:02}:{:02}:{:02}', 6), ('{:02}:{:02}', 5))[no_secs] return fs.format(*ret[3:n]) -def get_day_hms(t=None,utc=False): +def get_day_hms(t=None, utc=False): secs = t or time.time() - ret = (time.localtime,time.gmtime)[utc](secs) + ret = (time.localtime, time.gmtime)[utc](secs) return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*ret[0:6]) -def do_system(cmd,testing=False,shell=False): +def do_system(cmd, testing=False, shell=False): if testing: from mmgen.util import msg msg("Would execute: '%s'" % cmd) return True else: import subprocess - return subprocess.call((cmd if shell else cmd.split()),shell,stderr=subprocess.PIPE) + return subprocess.call((cmd if shell else cmd.split()), shell, stderr=subprocess.PIPE) -def get_url(url,gzip_ok=False,proxy=None,timeout=60,verbose=False,debug=False): +def get_url(url, gzip_ok=False, proxy=None, timeout=60, verbose=False, debug=False): if debug: print('get_url():') print(' url', url) - print(' gzip_ok:',gzip_ok, 'proxy:',proxy, 'timeout:',timeout, 'verbose:',verbose) - import pycurl,io + print(' gzip_ok:', gzip_ok, 'proxy:', proxy, 'timeout:', timeout, 'verbose:', verbose) + import pycurl, io c = pycurl.Curl() c_out = io.StringIO() - c.setopt(pycurl.WRITEFUNCTION,c_out.write) - c.setopt(pycurl.TIMEOUT,timeout) - c.setopt(pycurl.FOLLOWLOCATION,True) - c.setopt(pycurl.COOKIEFILE,'') - c.setopt(pycurl.VERBOSE,verbose) + c.setopt(pycurl.WRITEFUNCTION, c_out.write) + c.setopt(pycurl.TIMEOUT, timeout) + c.setopt(pycurl.FOLLOWLOCATION, True) + c.setopt(pycurl.COOKIEFILE, '') + c.setopt(pycurl.VERBOSE, verbose) if gzip_ok: - c.setopt(pycurl.USERAGENT,'Lynx/2.8.9dev.8 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.4.9') + c.setopt(pycurl.USERAGENT, 'Lynx/2.8.9dev.8 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.4.9') c.setopt(pycurl.HTTPHEADER, [ 'Accept: text/html, text/plain, text/sgml, text/css, application/xhtml+xml, */*;q=0.01', 'Accept-Encoding: gzip', - 'Accept-Language: en'] - ) + 'Accept-Language: en']) if proxy: - c.setopt(pycurl.PROXY,proxy) - c.setopt(pycurl.URL,url) + c.setopt(pycurl.PROXY, proxy) + c.setopt(pycurl.URL, url) c.perform() text = c_out.getvalue() if text[:2] == '\x1f\x8b': # gzip magic number - c_out.seek(0,0) + c_out.seek(0, 0) import gzip with gzip.GzipFile(fileobj=c_out) as f: text = f.read() @@ -103,20 +102,19 @@ big_digits = { """ } -_bnums_c,_bpunc_c = [[l.strip('\n') + ' ' * (big_digits[m]*big_digits['n']) +_bnums_c, _bpunc_c = [[l.strip('\n') + ' ' * (big_digits[m]*big_digits['n']) for l in big_digits[k][1:].split('\n')] - for k,m in (('nums','w'),('punc','pw'))] + for k, m in (('nums', 'w'), ('punc', 'pw'))] -_bnums_n,_bpunc_n = [[[l[0+(j*w):w+(j*w)] for l in i] - for j in range(big_digits[n])] for n,w,i in - (('n',big_digits['w'],_bnums_c),('pn',big_digits['pw'],_bpunc_c))] +_bnums_n, _bpunc_n = [[[l[0+(j*w):w+(j*w)] for l in i] + for j in range(big_digits[n])] for n, w, i in + (('n', big_digits['w'], _bnums_c), ('pn', big_digits['pw'], _bpunc_c))] -def display_big_digits(s,pre='',suf=''): - s = [int((d,10,11)[(d in '.:')+(d==':')]) for d in s] +def display_big_digits(s, pre='', suf=''): + s = [int((d, 10, 11)[(d in '.:')+(d==':')]) for d in s] return pre + ('\n'+pre).join( - [''.join([(_bnums_n+_bpunc_n)[d][l] for d in s]) + suf for l in range(big_digits['h'])] - ) + [''.join([(_bnums_n+_bpunc_n)[d][l] for d in s]) + suf for l in range(big_digits['h'])]) if __name__ == '__main__': num = '2345.17' - print(display_big_digits(num,pre='+ ',suf=' +')) + print(display_big_digits(num, pre='+ ', suf=' +')) diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 37f0482..4647029 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -16,7 +16,7 @@ import sys from mmgen.obj import CoinTxID from mmgen.cfg import Config -from mmgen.util import msg,Msg,die,suf,make_timestr,async_run +from mmgen.util import msg, Msg, die, suf, make_timestr, async_run from mmgen.color import red opts_data = { @@ -32,16 +32,16 @@ opts_data = { } } -def do_output(proto,addr_data,blk_hdrs): +def do_output(proto, addr_data, blk_hdrs): col1w = len(str(len(addr_data))) indent = ' ' * (col1w + 2) - for n,(addr,unspents) in enumerate(addr_data.items(),1): + for n, (addr, unspents) in enumerate(addr_data.items(), 1): Msg(f'\n{n:{col1w}}) Address: {addr.hl(addr.view_pref)}') if unspents: - heights = { u['height'] for u in unspents } + heights = {u['height'] for u in unspents} Msg('{}Balance: {}'.format( indent, sum(proto.coin_amt(u['amount']) for u in unspents).hl2(unit=True, fs='{:,}'))), @@ -50,22 +50,21 @@ def do_output(proto,addr_data,blk_hdrs): red(str(len(unspents))), suf(unspents), red(str(len(heights))), - suf(heights) )) + suf(heights))) blk_w = len(str(unspents[-1]['height'])) - fs = '%s{:%s} {:19} {:64} {:4} {}' % (indent,max(5,blk_w)) - Msg(fs.format('Block','Date','TxID','Vout',' Amount')) + fs = '%s{:%s} {:19} {:64} {:4} {}' % (indent, max(5, blk_w)) + Msg(fs.format('Block', 'Date', 'TxID', 'Vout', ' Amount')) for u in unspents: Msg(fs.format( u['height'], - make_timestr( blk_hdrs[u['height']]['time'] ), + make_timestr(blk_hdrs[u['height']]['time']), CoinTxID(u['txid']).hl(), red(str(u['vout']).rjust(4)), - proto.coin_amt(u['amount']).fmt(6, color=True, prec=8) - )) + proto.coin_amt(u['amount']).fmt(6, color=True, prec=8))) else: Msg(f'{indent}No balance') -def do_output_tabular(proto,addr_data,blk_hdrs): +def do_output_tabular(proto, addr_data, blk_hdrs): col1w = len(str(len(addr_data))) + 1 max_addrw = max(len(addr) for addr in addr_data) @@ -75,9 +74,9 @@ def do_output_tabular(proto,addr_data,blk_hdrs): lb_w = max(len(h) for h in lb_heights) fs = ( - ' {n:>%s} {a} {u} {b:>%s} {t:19} {B:>%s} {T:19} {A}' % (col1w,max(5,fb_w),max(4,lb_w)) + ' {n:>%s} {a} {u} {b:>%s} {t:19} {B:>%s} {T:19} {A}' % (col1w, max(5, fb_w), max(4, lb_w)) if cfg.first_block else - ' {n:>%s} {a} {u} {B:>%s} {T:19} {A}' % (col1w,max(4,lb_w)) ) + ' {n:>%s} {a} {u} {B:>%s} {T:19} {A}' % (col1w, max(4, lb_w))) Msg('\n' + fs.format( n = '', @@ -87,20 +86,19 @@ def do_output_tabular(proto,addr_data,blk_hdrs): t = 'Block', B = 'Last', T = 'Block', - A = ' Amount' )) + A = ' Amount')) - for n,(addr,unspents) in enumerate(addr_data.items(),1): + for n, (addr, unspents) in enumerate(addr_data.items(), 1): if unspents: Msg(fs.format( n = str(n) + ')', a = addr.fmt(addr.view_pref, max_addrw, color=True), u = red(str(len(unspents)).rjust(5)), b = unspents[0]['height'], - t = make_timestr( blk_hdrs[unspents[0]['height']]['time'] ), + t = make_timestr(blk_hdrs[unspents[0]['height']]['time']), B = unspents[-1]['height'], - T = make_timestr( blk_hdrs[unspents[-1]['height']]['time'] ), - A = sum(proto.coin_amt(u['amount']) for u in unspents).fmt(7, color=True, prec=8) - )) + T = make_timestr(blk_hdrs[unspents[-1]['height']]['time']), + A = sum(proto.coin_amt(u['amount']) for u in unspents).fmt(7, color=True, prec=8))) else: Msg(fs.format( n = str(n) + ')', @@ -110,61 +108,61 @@ def do_output_tabular(proto,addr_data,blk_hdrs): t = '', B = '-', T = '', - A = ' -' )) + A = ' -')) async def main(req_addrs): proto = cfg._proto from mmgen.addr import CoinAddr - addrs = [CoinAddr(proto,addr) for addr in req_addrs] + addrs = [CoinAddr(proto, addr) for addr in req_addrs] from mmgen.rpc import rpc_init - rpc = await rpc_init(cfg,ignore_wallet=True) + rpc = await rpc_init(cfg, ignore_wallet=True) height = await rpc.call('getblockcount') Msg(f'{proto.coin} {proto.network.upper()} [height {height}]') from mmgen.proto.btc.misc import scantxoutset - res = await scantxoutset( cfg, rpc, [f'addr({addr})' for addr in addrs] ) + res = await scantxoutset(cfg, rpc, [f'addr({addr})' for addr in addrs]) if not res['success']: - die(1,'UTXO scanning failed or was interrupted') + die(1, 'UTXO scanning failed or was interrupted') elif not res['unspents']: msg('Address has no balance' if len(addrs) == 1 else - 'Addresses have no balances' ) + 'Addresses have no balances') else: addr_data = {k:[] for k in addrs} if 'desc' in res['unspents'][0]: import re - for unspent in sorted(res['unspents'],key=lambda x: x['height']): - addr = re.match('addr\((.*?)\)',unspent['desc'])[1] + for unspent in sorted(res['unspents'], key=lambda x: x['height']): + addr = re.match('addr\((.*?)\)', unspent['desc'])[1] addr_data[addr].append(unspent) else: from mmgen.proto.btc.tx.base import decodeScriptPubKey - for unspent in sorted(res['unspents'],key=lambda x: x['height']): + for unspent in sorted(res['unspents'], key=lambda x: x['height']): ds = decodeScriptPubKey(proto, unspent['scriptPubKey']) addr_data[ds.addr].append(unspent) good_addrs = len([v for v in addr_data.values() if v]) Msg('Total: {} in {} address{}'.format( - proto.coin_amt(res['total_amount']).hl2(unit=True,fs='{:,}'), + proto.coin_amt(res['total_amount']).hl2(unit=True, fs='{:,}'), red(str(good_addrs)), - suf(good_addrs,'es') - )) + suf(good_addrs, 'es'))) blk_heights = {i['height'] for i in res['unspents']} blk_hashes = await rpc.batch_call('getblockhash', [(h,) for h in blk_heights]) blk_hdrs = await rpc.batch_call('getblockheader', [(H,) for H in blk_hashes]) - (do_output_tabular if cfg.tabular else do_output)( proto, addr_data, dict(zip(blk_heights,blk_hdrs)) ) + (do_output_tabular if cfg.tabular else do_output)( + proto, addr_data, dict(zip(blk_heights, blk_hdrs))) -cfg = Config( opts_data=opts_data, init_opts={'rpc_backend':'aiohttp'} ) +cfg = Config(opts_data=opts_data, init_opts={'rpc_backend': 'aiohttp'}) if len(cfg._args) < 1: - die(1,'This command requires at least one coin address argument') + die(1, 'This command requires at least one coin address argument') try: async_run(cfg, main, args=[cfg._args]) diff --git a/mmgen_node_tools/main_blocks_info.py b/mmgen_node_tools/main_blocks_info.py index 18136c4..eaed15b 100755 --- a/mmgen_node_tools/main_blocks_info.py +++ b/mmgen_node_tools/main_blocks_info.py @@ -20,9 +20,9 @@ mmnode-blocks-info: Display information about a block or range of blocks """ -from mmgen.cfg import gc,Config -from mmgen.util import async_run,fmt_list -from .BlocksInfo import BlocksInfo,JSONBlocksInfo +from mmgen.cfg import gc, Config +from mmgen.util import async_run, fmt_list +from .BlocksInfo import BlocksInfo, JSONBlocksInfo opts_data = { 'sets': [ @@ -145,14 +145,13 @@ EXAMPLES: $ {p} --rpc-backend=aio -H +1000 This program requires a txindex-enabled daemon for correct operation. -""" }, +"""}, 'code': { - 'notes': lambda cfg,proto,s: s.format( + 'notes': lambda cfg, proto, s: s.format( I = proto.diff_adjust_interval, - F = fmt_list(BlocksInfo.fields,fmt='bare'), - S = fmt_list(BlocksInfo.all_stats,fmt='bare'), - p = gc.prog_name, - ) + F = fmt_list(BlocksInfo.fields, fmt='bare'), + S = fmt_list(BlocksInfo.all_stats, fmt='bare'), + p = gc.prog_name) } } @@ -172,7 +171,7 @@ async def main(): await m.process_blocks() if m.last: - for i,sname in enumerate(m.stats): + for i, sname in enumerate(m.stats): m.process_stats_pre(i) await m.process_stats(sname) diff --git a/mmgen_node_tools/main_feeview.py b/mmgen_node_tools/main_feeview.py index 3211f7f..188f60e 100755 --- a/mmgen_node_tools/main_feeview.py +++ b/mmgen_node_tools/main_feeview.py @@ -21,10 +21,10 @@ mmnode-feeview: Visualize the fee structure of a node’s mempool """ from mmgen.cfg import Config -from mmgen.util import async_run,die,fmt,make_timestr,check_int_between -from mmgen.util2 import int2bytespec,parse_bytespec +from mmgen.util import async_run, die, fmt, make_timestr, check_int_between +from mmgen.util2 import int2bytespec, parse_bytespec -min_prec,max_prec,dfl_prec = (0,6,4) +min_prec, max_prec, dfl_prec = (0, 6, 4) fee_brackets = [ 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, @@ -42,9 +42,9 @@ fee_brackets = [ opts_data = { 'sets': [ - ('detail',True,'ranges',True), - ('detail',True,'show_mb_col',True), - ('detail',True,'precision',6), + ('detail', True, 'ranges', True), + ('detail', True, 'show_mb_col', True), + ('detail', True, 'precision', 6), ], 'text': { 'desc': 'Visualize the fee structure of a node’s mempool', @@ -83,37 +83,37 @@ cfg = Config(opts_data=opts_data) if cfg.ignore_below: if cfg.show_empty: - die(1,'Conflicting options: --ignore-below, --show-empty') + die(1, 'Conflicting options: --ignore-below, --show-empty') ignore_below = parse_bytespec(cfg.ignore_below) precision = ( check_int_between(cfg.precision, min_prec, max_prec, desc='--precision arg') - if cfg.precision else dfl_prec ) + if cfg.precision else dfl_prec) from mmgen.term import get_terminal_size width = cfg.columns or get_terminal_size().width class fee_bracket: - def __init__(self,top,bottom): + def __init__(self, top, bottom): self.top = top self.bottom = bottom self.tx_bytes = 0 self.tx_bytes_cum = 0 self.skip = False -def log(data,fn): +def log(data, fn): import json from mmgen.rpc.util import json_encoder from mmgen.fileutil import write_data_to_file write_data_to_file( cfg = cfg, outfile = fn, - data = json.dumps(data,cls=json_encoder,sort_keys=True,indent=4), + data = json.dumps(data, cls=json_encoder, sort_keys=True, indent=4), desc = 'mempool', - ask_overwrite = False ) + ask_overwrite = False) -def create_data(coin_amt,mempool): - out = [fee_bracket(fee_brackets[i],fee_brackets[i-1] if i else 0) for i in range(len(fee_brackets))] +def create_data(coin_amt, mempool): + out = [fee_bracket(fee_brackets[i], fee_brackets[i-1] if i else 0) for i in range(len(fee_brackets))] # populate fee brackets: size_key = 'size' if proto.coin == 'BCH' else 'vsize' @@ -143,7 +143,7 @@ def create_data(coin_amt,mempool): return out -def gen_header(host,mempool,blockcount): +def gen_header(host, mempool, blockcount): yield fmt(f""" Mempool Fee Structure @@ -159,27 +159,26 @@ def gen_header(host,mempool,blockcount): elif cfg.ignore_below: yield 'Ignoring fee brackets with less than {:,} bytes ({})'.format( ignore_below, - int2bytespec(ignore_below,'MB','0.6',strip=True,add_space=True), - ) + int2bytespec(ignore_below, 'MB', '0.6', strip=True, add_space=True)) if cfg.include_current: yield 'Including transactions in current fee bracket in Total MB amounts' def fmt_mb(n): - return int2bytespec(n,'MB',f'0.{precision}',print_sym=False) + return int2bytespec(n, 'MB', f'0.{precision}', print_sym=False) def gen_body(data): - tx_bytes_max = max((i.tx_bytes for i in data),default=0) - top_max = max((i.top for i in data),default=0) - bot_max = max((i.bottom for i in data),default=0) - col1_w = max(len(f'{bot_max}-{top_max}') if cfg.ranges else len(f'{top_max}'),6) + tx_bytes_max = max((i.tx_bytes for i in data), default=0) + top_max = max((i.top for i in data), default=0) + bot_max = max((i.bottom for i in data), default=0) + col1_w = max(len(f'{bot_max}-{top_max}') if cfg.ranges else len(f'{top_max}'), 6) col2_w = len(fmt_mb(tx_bytes_max)) if cfg.show_mb_col else 0 col3_w = len(fmt_mb(data[-1].tx_bytes_cum)) if data else 0 col4_w = width - col1_w - col2_w - col3_w - (4 if col2_w else 3) if cfg.show_mb_col: - fs = '{a:<%i} {b:>%i} {c:>%i} {d}' % (col1_w,col2_w,col3_w) + fs = '{a:<%i} {b:>%i} {c:>%i} {d}' % (col1_w, col2_w, col3_w) else: - fs = '{a:<%i} {c:>%i} {d}' % (col1_w,col3_w) + fs = '{a:<%i} {c:>%i} {d}' % (col1_w, col3_w) yield fs.format(a='', b='', c=f'{"Total":<{col3_w}}', d='') yield fs.format(a='sat/B', b=f'{"MB":<{col2_w}}', c=f'{"MB":<{col3_w}}', d='') @@ -188,16 +187,16 @@ def gen_body(data): if not i.skip: cum_bytes = i.tx_bytes_cum + i.tx_bytes if cfg.include_current else i.tx_bytes_cum yield fs.format( - a = '{}-{}'.format(i.bottom,i.top) if cfg.ranges else i.top, + a = '{}-{}'.format(i.bottom, i.top) if cfg.ranges else i.top, b = fmt_mb(i.tx_bytes), c = fmt_mb(cum_bytes), - d = '-' * int(col4_w * ( i.tx_bytes / tx_bytes_max )) ) + d = '-' * int(col4_w * (i.tx_bytes / tx_bytes_max))) yield fs.format( a = 'TOTAL', b = '', c = fmt_mb(data[-1].tx_bytes_cum + data[-1].tx_bytes if data else 0), - d = '' ) + d = '') async def main(): @@ -205,19 +204,19 @@ async def main(): proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init(cfg,ignore_wallet=True) + c = await rpc_init(cfg, ignore_wallet=True) - mempool = await c.call('getrawmempool',True) + mempool = await c.call('getrawmempool', True) if cfg.log: - log(mempool,'mempool.json') + log(mempool, 'mempool.json') - data = create_data(proto.coin_amt,mempool) + data = create_data(proto.coin_amt, mempool) cfg._util.stdout_or_pager( '\n'.join(gen_header( c.host, mempool, - await c.call('getblockcount') )) + '\n\n' + - '\n'.join(gen_body(data)) + '\n' ) + await c.call('getblockcount'))) + '\n\n' + + '\n'.join(gen_body(data)) + '\n') async_run(cfg, main) diff --git a/mmgen_node_tools/main_halving_calculator.py b/mmgen_node_tools/main_halving_calculator.py index 8f0a3a3..60c5440 100755 --- a/mmgen_node_tools/main_halving_calculator.py +++ b/mmgen_node_tools/main_halving_calculator.py @@ -28,7 +28,7 @@ from mmgen.util import async_run bdr_proj = 9.95 opts_data = { - 'sets': [('mined',True,'list',True)], + 'sets': [('mined', True, 'list', True)], 'text': { 'desc': 'Estimate date(s) of future block subsidy halving(s)', 'usage':'[opts]', @@ -41,7 +41,7 @@ opts_data = { {bdr_proj:.5f} min) -s, --sample-size=N Block range to calculate block discovery interval for next halving estimate (default: dynamically calculated) -""" } +"""} } cfg = Config(opts_data=opts_data) @@ -53,14 +53,14 @@ def date(t): return '{}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*time.gmtime(t)[:6]) def dhms(t): - t,neg = (-t,'-') if t < 0 else (t,' ') + t, neg = (-t, '-') if t < 0 else (t, ' ') return f'{neg}{t//60//60//24} days, {t//60//60%24:02}:{t//60%60:02}:{t%60:02} h/m/s' def time_diff_warning(t_diff): if abs(t_diff) > 60*60: print('Warning: block tip time is {} {} clock time!'.format( dhms(abs(t_diff)), - ('behind','ahead of')[t_diff<0])) + ('behind', 'ahead of')[t_diff<0])) async def main(): @@ -72,9 +72,9 @@ async def main(): tip = await c.call('getblockcount') assert tip > 1, 'block tip must be > 1' remaining = proto.halving_interval - tip % proto.halving_interval - sample_size = int(cfg.sample_size) if cfg.sample_size else min(tip-1,max(remaining,144)) + sample_size = int(cfg.sample_size) if cfg.sample_size else min(tip-1, max(remaining, 144)) - cur,old = await c.gathered_call('getblockstats',((tip,),(tip - sample_size,))) + cur, old = await c.gathered_call('getblockstats', ((tip,), (tip - sample_size,))) clock_time = int(time.time()) time_diff_warning(clock_time - cur['time']) @@ -98,8 +98,7 @@ async def main(): f'Current block discovery interval (over last {sample_size} blocks): {bdr/60:0.2f} min\n\n' f'Current clock time (UTC): {date(clock_time)}\n' f'Est. halving date (UTC): {date(t_next)}\n' - f'Est. time until halving: {dhms(cur["time"] + t_rem - clock_time)}' - ) + f'Est. time until halving: {dhms(cur["time"] + t_rem - clock_time)}') async def print_halvings(): halving_blocknums = [i*proto.halving_interval for i in range(proto.max_halvings+1)][1:] @@ -108,13 +107,13 @@ async def main(): nhist = len(hist_halvings) nSubsidy = int(proto.start_subsidy / proto.coin_amt.satoshi) - block0_hash = await c.call('getblockhash',0) - block0_date = (await c.call('getblock',block0_hash))['time'] + block0_hash = await c.call('getblockhash', 0) + block0_date = (await c.call('getblock', block0_hash))['time'] def gen_data(): total_mined = 0 date = block0_date - for n,blk in enumerate(halving_blocknums): + for n, blk in enumerate(halving_blocknums): mined = (nSubsidy >> n) * proto.halving_interval if n == 0: mined -= nSubsidy # subtract unspendable genesis block subsidy @@ -123,13 +122,11 @@ async def main(): bdi = ( (hist_halvings[n]['time'] - date) / (proto.halving_interval * 60) if n < nhist else bdr/60 if n == nhist - else bdr_proj - ) + else bdr_proj) date = ( hist_halvings[n]['time'] if n < nhist - else t_next + int((n - nhist) * halving_secs) - ) - yield ( n, sub, blk, mined, total_mined, bdi, date ) + else t_next + int((n - nhist) * halving_secs)) + yield (n, sub, blk, mined, total_mined, bdi, date) if sub == 0: break @@ -150,8 +147,7 @@ async def main(): e = 'BDI (mins)', f = 'SUBSIDY ({proto.coin})', g = f'MINED ({proto.coin})', - h = f'TOTAL MINED ({proto.coin})' - ) + h = f'TOTAL MINED ({proto.coin})') + '\n' + fs.format( a = '-' * 7, @@ -159,22 +155,20 @@ async def main(): c = '-' * 19, d = '-' * 2, e = '-' * 10, - f = '-' * 13, + f = '-' * 17, g = '-' * 17, - h = '-' * 17 - ) + h = '-' * 17) + '\n' + '\n'.join(fs.format( - a = n + 1, - b = blk, - c = date(t), - d = ' P' if n > nhist else '' if n < nhist else ' E', - e = f'{bdr:8.5f}', - f = proto.coin_amt(sub, from_unit='satoshi').fmt(2, prec=8), - g = proto.coin_amt(mined, from_unit='satoshi').fmt(8, prec=8), - h = proto.coin_amt(total_mined, from_unit='satoshi').fmt(8, prec=8) - ) for n, sub, blk, mined, total_mined, bdr, t in gen_data()) - ) + a = n + 1, + b = blk, + c = date(t), + d = ' P' if n > nhist else '' if n < nhist else ' E', + e = f'{bdr:8.5f}', + f = proto.coin_amt(sub, from_unit='satoshi').fmt(2, prec=8), + g = proto.coin_amt(mined, from_unit='satoshi').fmt(8, prec=8), + h = proto.coin_amt(total_mined, from_unit='satoshi').fmt(8, prec=8) + ) for n, sub, blk, mined, total_mined, bdr, t in gen_data())) if cfg.list: await print_halvings() diff --git a/mmgen_node_tools/main_netrate.py b/mmgen_node_tools/main_netrate.py index b273ac6..f5cf992 100755 --- a/mmgen_node_tools/main_netrate.py +++ b/mmgen_node_tools/main_netrate.py @@ -20,7 +20,7 @@ mmnode-netrate: Bitcoin daemon network rate monitor """ -import sys,time +import sys, time from mmgen.cfg import Config from mmgen.util import async_run @@ -32,39 +32,38 @@ opts_data = { 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -""" - } +"""} } cfg = Config(opts_data=opts_data) -ERASE_LINE,CUR_UP = '\033[K','\033[1A' +ERASE_LINE, CUR_UP = '\033[K', '\033[1A' async def main(): from mmgen.rpc import rpc_init - c = await rpc_init(cfg,ignore_wallet=True) + c = await rpc_init(cfg, ignore_wallet=True) async def get_data(): d = await c.call('getnettotals') - return [float(e) for e in (d['totalbytesrecv'],d['totalbytessent'],d['timemillis'])] + return [float(e) for e in (d['totalbytesrecv'], d['totalbytessent'], d['timemillis'])] - rs,ss,ts = (None,None,None) + rs, ss, ts = (None, None, None) while True: - r,s,t = await get_data() + r, s, t = await get_data() if rs is not None: sys.stderr.write( '\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format( (r-rs)/(t-ts), - (s-ss)/(t-ts) )) + (s-ss)/(t-ts))) time.sleep(2) if rs is not None: - sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE)) + sys.stderr.write('{}{}{}'.format(ERASE_LINE, CUR_UP, ERASE_LINE)) - rs,ss,ts = (r,s,t) + rs, ss, ts = (r, s, t) try: async_run(cfg, main) diff --git a/mmgen_node_tools/main_peerblocks.py b/mmgen_node_tools/main_peerblocks.py index 35292ea..6a7cb38 100755 --- a/mmgen_node_tools/main_peerblocks.py +++ b/mmgen_node_tools/main_peerblocks.py @@ -27,8 +27,7 @@ opts_data = { 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -""" - } +"""} } from mmgen.cfg import Config @@ -37,9 +36,9 @@ cfg = Config(opts_data=opts_data) async def main(): from mmgen.rpc import rpc_init - rpc = await rpc_init(cfg,ignore_wallet=True) + rpc = await rpc_init(cfg, ignore_wallet=True) - from .PeerBlocks import BlocksDisplay,PeersDisplay + from .PeerBlocks import BlocksDisplay, PeersDisplay blocks = BlocksDisplay(cfg) peers = PeersDisplay(cfg) diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 351cb1e..c36d886 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -208,29 +208,26 @@ Customize output by editing the file To add a portfolio, edit the file ~/{pf_cfg} -""" - }, +"""}, 'code': { 'options': lambda s: s.format( - dfl_cachedir = os.path.relpath(dfl_cachedir,start=homedir), - ds = fmt_dict(DataSource.get_sources(),fmt='equal_compact'), + dfl_cachedir = os.path.relpath(dfl_cachedir, start=homedir), + ds = fmt_dict(DataSource.get_sources(), fmt='equal_compact'), al = DataSource.coinpaprika.dfl_asset_limit, - pc = fmt_list(Ticker.percent_cols,fmt='bare'), - ), + pc = fmt_list(Ticker.percent_cols, fmt='bare')), 'notes': lambda s: s.format( - assets = fmt_list(assets_list_gen(cfg_in),fmt='col',indent=' '), - cfg = os.path.relpath(cfg_in.cfg_file,start=homedir), - pf_cfg = os.path.relpath(cfg_in.portfolio_file,start=homedir), + assets = fmt_list(assets_list_gen(cfg_in), fmt='col', indent=' '), + cfg = os.path.relpath(cfg_in.cfg_file, start=homedir), + pf_cfg = os.path.relpath(cfg_in.portfolio_file, start=homedir), al = DataSource.coinpaprika.dfl_asset_limit, cc = src_cls['cc'](), - fi = src_cls['fi'](), - ) + fi = src_cls['fi']()) } } import os -from mmgen.util import fmt_list,fmt_dict +from mmgen.util import fmt_list, fmt_dict from mmgen.cfg import Config from . import Ticker @@ -238,7 +235,7 @@ gcfg = Config(opts_data=opts_data, caller_post_init=True) Ticker.make_cfg(gcfg) -from .Ticker import dfl_cachedir,homedir,DataSource,assets_list_gen,cfg_in,src_cls +from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen, cfg_in, src_cls gcfg._post_init() diff --git a/mmgen_node_tools/main_txfind.py b/mmgen_node_tools/main_txfind.py index 2411f36..0f9db5b 100755 --- a/mmgen_node_tools/main_txfind.py +++ b/mmgen_node_tools/main_txfind.py @@ -23,7 +23,7 @@ mmnode-txfind: Find a transaction in the blockchain or mempool import sys from mmgen.cfg import Config -from mmgen.util import msg,Msg,die,is_hex_str,async_run +from mmgen.util import msg, Msg, die, is_hex_str, async_run opts_data = { 'text': { @@ -48,29 +48,26 @@ msg_data = { 'normal': { 'none': 'Transaction not found in blockchain or mempool', 'block': 'Transaction is in block {b} ({c} confirmations)', - 'mem': 'Transaction is in mempool', - }, + 'mem': 'Transaction is in mempool'}, 'quiet': { 'none': 'None', 'block': '{b} {c}', - 'mem': 'mempool', - } -} + 'mem': 'mempool'}} async def main(txid): if len(txid) != 64 or not is_hex_str(txid): - die(2,f'{txid}: invalid transaction ID') + die(2, f'{txid}: invalid transaction ID') if cfg.verbose: msg(f'TxID: {txid}') from mmgen.rpc import rpc_init - c = await rpc_init(cfg,ignore_wallet=True) + c = await rpc_init(cfg, ignore_wallet=True) exitval = 0 try: tip1 = await c.call('getblockcount') - ret = await c.call('getrawtransaction',txid,True) + ret = await c.call('getrawtransaction', txid, True) tip2 = await c.call('getblockcount') except: Msg('\r' + msgs['none']) @@ -90,6 +87,6 @@ cfg = Config(opts_data=opts_data) msgs = msg_data['quiet' if cfg.quiet else 'normal'] if len(cfg._args) != 1: - die(1,'One transaction ID must be specified') + die(1, 'One transaction ID must be specified') sys.exit(async_run(cfg, main, args=[cfg._args[0]])) diff --git a/test/cmdtest_d/regtest.py b/test/cmdtest_d/regtest.py index 4792dee..a504678 100755 --- a/test/cmdtest_d/regtest.py +++ b/test/cmdtest_d/regtest.py @@ -335,19 +335,19 @@ class CmdTestRegtest(CmdTestBase): us = await r.rpc_call('listunspent',wallet='miner') tx_input = us[7] # 25 BTC in coinbase -- us[0] could have < 25 BTC fee = self.proto.coin_amt('0.001') - outputs = {p.addr:tx1_amt for p in pairs[:nTxs]} + outputs = {p.addr: tx1_amt for p in pairs[:nTxs]} outputs.update({burn_addr: self.proto.coin_amt(tx_input['amount']) - (tx1_amt*nTxs) - fee}) return await do_tx( - [{ 'txid': tx_input['txid'], 'vout': 0 }], + [{'txid': tx_input['txid'], 'vout': 0}], outputs, await r.miner_wif) async def do_tx2(tx,pairno): fee = self.proto.coin_amt(fees[pairno], from_decimal=True) - outputs = {p.addr:tx2_amt for p in pairs} + outputs = {p.addr: tx2_amt for p in pairs} outputs.update({burn_addr: tx1_amt - (tx2_amt*len(pairs)) - fee}) return await do_tx( - [{ 'txid': tx['txid'], 'vout': pairno }], + [{'txid': tx['txid'], 'vout': pairno}], outputs, pairs[pairno].wif ) diff --git a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py index 7c2fdf3..444ae08 100644 --- a/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py +++ b/test/overlay/fakemods/mmgen_node_tools/PeerBlocks.py @@ -294,15 +294,14 @@ class fake_data: 20 7303 7307 7310 7311 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 7225 21 7310 7311 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 7225 7317 7386 22 7316 7322 7334 7343 7344 7350 7356 7363 7374 7377 7384 7225 7317 7386 7398 7409 - """ - } + """} def make_data(): def gen_address_data(): for line in fake_data.addresses.strip().split('\n'): data = line.split(maxsplit=2) - yield (data[0], {k:v for k,v in zip(('id','addr','subver'),data)}) + yield (data[0], {k: v for k, v in zip(('id', 'addr', 'subver'), data)}) def gen_iterations_data(): for line in fake_data.iterations.strip().split('\n'): @@ -320,8 +319,7 @@ class fake_data: 'id': int(d['id']), 'addr': d['addr'], 'subver': d['subver'], - 'inflight': [int(n)+830000 for n in blocks[iter_no]], - } + 'inflight': [int(n)+830000 for n in blocks[iter_no]]} def gen_data(): for iter_no in iterations_data: From b9957f918298329ab9057a94946abccdaad9d038 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 4 Oct 2025 09:57:02 +0000 Subject: [PATCH 150/175] mmnode-ticker: support `--http-timeout` option --- mmgen_node_tools/Ticker.py | 4 ++++ mmgen_node_tools/data/version | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index ebefcf3..caee658 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -75,6 +75,7 @@ class DataSource: ['--compressed'], # adds 'Accept-Encoding: gzip' ['--proxy', cfg.proxy, isinstance(cfg.proxy, str)], ['--silent', not cfg.verbose], + ['--connect-timeout', str(gcfg.http_timeout), gcfg.http_timeout], [self.api_url]) if gcfg.testing: @@ -290,6 +291,9 @@ class DataSource: if gcfg.test_suite: kwargs.update({'timeout': 1, 'retry': 0}) + if gcfg.http_timeout: + kwargs.update({'timeout': gcfg.http_timeout}) + if gcfg.testing: Msg('\nyahooquery.Ticker(\n {},\n {}\n)'.format( self.symbols, diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 9f32e9d..6f735af 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev1 +3.6.dev2 From 06340ed521de5ba0333e2d4bf20105cde60b0e8c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 4 Oct 2025 12:44:54 +0000 Subject: [PATCH 151/175] restore Nix support --- mmgen_node_tools/data/version | 2 +- nix/curl-cffi.nix | 50 +++++++++++++++++++++++++++++++ nix/node-tools-packages.nix | 4 +-- nix/use-system-libs.patch | 23 ++++++++++++++ nix/yahooquery-noversioning.patch | 45 ---------------------------- nix/yahooquery.nix | 39 ++++++++++-------------- 6 files changed, 92 insertions(+), 71 deletions(-) create mode 100644 nix/curl-cffi.nix create mode 100644 nix/use-system-libs.patch delete mode 100644 nix/yahooquery-noversioning.patch diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 6f735af..f9d163c 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev2 +3.6.dev3 diff --git a/nix/curl-cffi.nix b/nix/curl-cffi.nix new file mode 100644 index 0000000..15f9764 --- /dev/null +++ b/nix/curl-cffi.nix @@ -0,0 +1,50 @@ +{ + stdenv, + lib, + python, + buildPythonPackage, + fetchPypi, + curl-impersonate-chrome, +}: + +buildPythonPackage rec { + pname = "curl-cffi"; + # version = "0.13.0"; # uses option PROXY_CREDENTIAL_NO_REUSE, unavailable in current libcurl + version = "0.10.0"; + pyproject = true; + + src = fetchPypi { + pname = "curl_cffi"; + version = version; + # hash = "sha256-YuzZCjgr1QI3UONgbgqnyxo6i6QcFCcLjl4Unr9yxco="; # 0.13.0 + hash = "sha256-PjezUmjKWEkvVO0CCuS1DDPuDeutQUXbn3RvBO1GbrA="; # 0.10.0 + }; + + patches = [ ./use-system-libs.patch ]; + + buildInputs = [ curl-impersonate-chrome ]; + + build-system = with python.pkgs; [ + cffi + setuptools + ]; + + dependencies = with python.pkgs; [ + cffi + certifi + typing-extensions + ]; + + env = lib.optionalAttrs stdenv.cc.isGNU { + NIX_CFLAGS_COMPILE = "-Wno-error=incompatible-pointer-types"; + }; + + pythonImportsCheck = [ "curl_cffi" ]; + + meta = with lib; { + description = "Python binding for curl-impersonate via cffi"; + homepage = "https://curl-cffi.readthedocs.io"; + license = licenses.mit; + maintainers = with maintainers; [ chuangzhu ]; + }; +} diff --git a/nix/node-tools-packages.nix b/nix/node-tools-packages.nix index 19792c7..1244425 100644 --- a/nix/node-tools-packages.nix +++ b/nix/node-tools-packages.nix @@ -6,7 +6,7 @@ }; python-packages = with python.pkgs; { - yahooquery = (pkgs.callPackage ./yahooquery.nix {}); # ticker - pyyaml = pyyaml; # ticker + yahooquery = (callPackage ./yahooquery.nix {}); # ticker + pyyaml = pyyaml; # ticker }; } diff --git a/nix/use-system-libs.patch b/nix/use-system-libs.patch new file mode 100644 index 0000000..1d04487 --- /dev/null +++ b/nix/use-system-libs.patch @@ -0,0 +1,23 @@ +diff --git a/scripts/build.py b/scripts/build.py +index b705a0d..9bfcaab 100644 +--- a/scripts/build.py ++++ b/scripts/build.py +@@ -105,7 +105,6 @@ def get_curl_libraries(): + ffibuilder = FFI() + system = platform.system() + root_dir = Path(__file__).parent.parent +-download_libcurl() + + + ffibuilder.set_source( +@@ -114,9 +113,7 @@ ffibuilder.set_source( + #include "shim.h" + """, + # FIXME from `curl-impersonate` +- libraries=get_curl_libraries(), +- extra_objects=get_curl_archives(), +- library_dirs=[arch["libdir"]], ++ libraries=["curl-impersonate-chrome"], + source_extension=".c", + include_dirs=[ + str(root_dir / "include"), diff --git a/nix/yahooquery-noversioning.patch b/nix/yahooquery-noversioning.patch deleted file mode 100644 index 4bd3297..0000000 --- a/nix/yahooquery-noversioning.patch +++ /dev/null @@ -1,45 +0,0 @@ -diff --git a/pyproject.toml b/pyproject.toml -index 9d3fb29..399c215 100644 ---- a/pyproject.toml -+++ b/pyproject.toml -@@ -10,24 +10,24 @@ readme = "README.md" - - [tool.poetry.dependencies] - python = ">=3.8.1,<4.0" --requests = "^2.31.0" --pandas = "^2.0.3" --requests-futures = "^1.0.1" --tqdm = "^4.65.0" --lxml = "^4.9.3" --selenium = {version = "^4.10.0", optional = true} --beautifulsoup4 = "^4.12.2" -+requests = ">=2.31.0" -+pandas = ">=2.0.3" -+requests-futures = ">=1.0.1" -+tqdm = ">=4.65.0" -+lxml = ">=4.9.3" -+selenium = {version = ">=4.10.0", optional = true} -+beautifulsoup4 = ">=4.12.2" - - [tool.poetry.dev-dependencies] --pytest = "^7.4.0" --isort = "^5.0.0" --flake8 = "^6.0.0" --mypy = "^1.4.1" --pytest-cov = "^4.1.0" --black = "^23.7.0" --pre-commit = "^3.3.3" --ipython = "^8.0.0" --mkdocs-material = "^9.1.18" -+pytest = ">=7.4.0" -+isort = ">=5.0.0" -+flake8 = ">=6.0.0" -+mypy = ">=1.4.1" -+pytest-cov = ">=4.1.0" -+black = ">=23.7.0" -+pre-commit = ">=3.3.3" -+ipython = ">=8.0.0" -+mkdocs-material = ">=9.1.18" - - [build-system] - requires = ["poetry-core>=1.0.0"] diff --git a/nix/yahooquery.nix b/nix/yahooquery.nix index 8b1535d..3e8925f 100644 --- a/nix/yahooquery.nix +++ b/nix/yahooquery.nix @@ -1,37 +1,30 @@ { lib, - pkgs, - fetchFromGitHub, + buildPythonPackage, + fetchPypi, + python, }: -with pkgs.python312.pkgs; - buildPythonPackage rec { pname = "yahooquery"; - version = "2.3.7"; + version = "2.4.1"; pyproject = true; - disabled = pythonOlder "3.8.1"; - - src = fetchFromGitHub { - owner = "dpguthrie"; - repo = "yahooquery"; - rev = "refs/tags/v${version}"; - hash = "sha256-Iyuni1SoTB6f7nNFhN5A8Gnv9kV78frjpqvvW8qd+/M="; + src = fetchPypi { + pname = "yahooquery"; + version = version; + hash = "sha256-GQPGXq5qEtlelFAGNHkhbAeEbwE7riojkXkTUxt/rls="; }; - patches = [ ./yahooquery-noversioning.patch ]; + build-system = with python.pkgs; [ hatchling ]; - build-system = [ poetry-core ]; - - dependencies = [ - requests # ^2.31.0 - pandas # ^2.0.3 - requests-futures # ^1.0.1 - tqdm # ^4.65.0 - lxml # ^4.9.3 - selenium # {version = ^4.10.0, optional = true} - beautifulsoup4 # ^4.12.2 + propagatedBuildInputs = with python.pkgs; [ + (callPackage ./curl-cffi.nix {}) # >=0.10.0 + pandas + requests-futures + tqdm + lxml + beautifulsoup4 ]; doCheck = false; # skip tests From de833d75afb3fa313f2541eb1a71819a9c1ae141 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 12 Oct 2025 10:01:47 +0000 Subject: [PATCH 152/175] whitespace, variable renames --- mmgen_node_tools/Ticker.py | 6 ++++-- test/cmdtest_d/misc.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index caee658..d681a81 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -192,8 +192,10 @@ class DataSource: @property def api_url(self): return ( - f'https://{self.api_host}/v1/tickers/btc-bitcoin' if cfg.btc_only else - f'https://{self.api_host}/v1/tickers?limit={self.asset_limit}' if self.asset_limit else + f'https://{self.api_host}/v1/tickers/btc-bitcoin' + if cfg.btc_only else + f'https://{self.api_host}/v1/tickers?limit={self.asset_limit}' + if self.asset_limit else f'https://{self.api_host}/v1/tickers') @property diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 9528b76..fb48282 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -106,10 +106,17 @@ class CmdTestScripts(CmdTestBase): shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) return 'ok' - def ticker(self, args=[], expect_list=None, cached=True, exit_val=None): + def ticker( + self, + args = [], + expect_list = None, + cached_data = True, + exit_val = None): t = self.spawn( 'mmnode-ticker', - (['--cached-data'] if cached else []) + self.ticker_args + args, + (['--cached-data'] if cached_data else []) + + self.ticker_args + + args, exit_val = exit_val) if expect_list: t.match_expect_list(expect_list) @@ -121,7 +128,7 @@ class CmdTestScripts(CmdTestBase): return t def ticker2(self): - t = self.ticker(cached=False) + t = self.ticker(cached_data=False) if not cfg.skipping_deps: t.expect('Creating') t.expect('Creating') @@ -137,7 +144,6 @@ class CmdTestScripts(CmdTestBase): 'BTC 23250.77 1.00000000 ETH 1659.66 0.07138094' ]) - def ticker4(self): return self.ticker( ['--widest','--add-columns=eurusd=x,inr-indian-rupee:79.5'], From 313c7af4bbf165ed65e5acd56e2461663d54bec2 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 12 Oct 2025 10:01:51 +0000 Subject: [PATCH 153/175] mmgen-ticker: test caching JSON data --- MANIFEST.in | 1 + mmgen_node_tools/Ticker.py | 12 ++++++--- test/cmdtest_d/httpd/ticker.py | 27 +++++++++++++++++++ test/cmdtest_d/misc.py | 27 ++++++++++++++----- test/init.sh | 1 + .../fakemods/mmgen_node_tools/Ticker.py | 23 ++++++++++++++++ 6 files changed, 82 insertions(+), 9 deletions(-) create mode 100755 test/cmdtest_d/httpd/ticker.py create mode 100644 test/overlay/fakemods/mmgen_node_tools/Ticker.py diff --git a/MANIFEST.in b/MANIFEST.in index 77b53ac..2581100 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,5 +7,6 @@ include test/test-release.d/*.sh include test/modtest_d/*.py include test/cmdtest_d/*.py include test/cmdtest_d/include/cfg.py +include test/cmdtest_d/httpd/ticker.py include test/overlay/fakemods/mmgen_node_tools/*.py include test/ref/*/* diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index d681a81..bfe899b 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -175,6 +175,7 @@ class DataSource: desc = 'CoinPaprika' data_desc = 'cryptocurrency data' api_host = 'api.coinpaprika.com' + api_proto = 'https' ratelimit = 240 btc_ratelimit = 10 net_data_type = 'json' @@ -192,11 +193,11 @@ class DataSource: @property def api_url(self): return ( - f'https://{self.api_host}/v1/tickers/btc-bitcoin' + f'{self.api_proto}://{self.api_host}/v1/tickers/btc-bitcoin' if cfg.btc_only else - f'https://{self.api_host}/v1/tickers?limit={self.asset_limit}' + f'{self.api_proto}://{self.api_host}/v1/tickers?limit={self.asset_limit}' if self.asset_limit else - f'https://{self.api_host}/v1/tickers') + f'{self.api_proto}://{self.api_host}/v1/tickers') @property def json_fn(self): @@ -697,6 +698,11 @@ def make_cfg(gcfg_arg): cmd_args = gcfg._args cfg_in = get_cfg_in() + if gcfg.test_suite: # required for testing with overlay + from . import Ticker as this_mod + this_mod.src_cls = src_cls + this_mod.cfg_in = cfg_in + usr_rows = parse_usr_asset_arg('add_rows') usr_columns = parse_usr_asset_arg('add_columns', use_cf_file=True) query = parse_query_arg(cmd_args[0]) if cmd_args else None diff --git a/test/cmdtest_d/httpd/ticker.py b/test/cmdtest_d/httpd/ticker.py new file mode 100755 index 0000000..2d946ae --- /dev/null +++ b/test/cmdtest_d/httpd/ticker.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# MMGen Node Tools, terminal-based programs for Bitcoin and forkcoin nodes +# Copyright (C)2013-2025 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +""" +test.cmdtest_d.httpd.ticker: Ticker WSGI http server +""" + +from . import HTTPD + +class TickerServer(HTTPD): + name = 'ticker server' + port = 19900 + content_type = 'application/json' + + def make_response_body(self, method, environ): + + with open(f'test/ref/ticker/ticker.json') as fh: + text = fh.read() + + return text.encode() diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index fb48282..e9d4c32 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -16,6 +16,7 @@ import os, shutil from ..include.common import cfg from .base import CmdTestBase +from .httpd.ticker import TickerServer refdir = os.path.join('test','ref','ticker') @@ -70,8 +71,9 @@ class CmdTestScripts(CmdTestBase): ), 'ticker': ( "'mmnode-ticker' script", - ('ticker1', 'ticker [--help)'), + ('ticker1', 'ticker [--help]'), ('ticker2', 'ticker (bad proxy)'), + ('ticker2a', 'ticker [--download=cc]'), ('ticker3', 'ticker [--cached-data]'), ('ticker4', 'ticker [--cached-data --wide]'), ('ticker5', 'ticker [--cached-data --wide --adjust=-0.766] (usr cfg file)'), @@ -90,9 +92,12 @@ class CmdTestScripts(CmdTestBase): ) } - @property - def ticker_args(self): - return [ f'--cachedir={self.tmpdir}', '--proxy=http://asdfzxcv:32459' ] + def __init__(self, cfg, trunner, cfgs, spawn): + if not trunner: + return + self.ticker_server = TickerServer(cfg) + self.ticker_server.start() + return super().__init__(cfg, trunner, cfgs, spawn) @property def nt_datadir(self): @@ -100,7 +105,6 @@ class CmdTestScripts(CmdTestBase): def ticker_setup(self): self.spawn('',msg_only=True) - shutil.copy2(os.path.join(refdir,'ticker.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-finance.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-finance-history.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) @@ -111,11 +115,15 @@ class CmdTestScripts(CmdTestBase): args = [], expect_list = None, cached_data = True, + add_opts = [], + use_proxy = True, exit_val = None): t = self.spawn( 'mmnode-ticker', (['--cached-data'] if cached_data else []) - + self.ticker_args + + [f'--cachedir={self.tmpdir}'] + + (['--proxy=http://asdfzxcv:32459'] if use_proxy else []) + + add_opts + args, exit_val = exit_val) if expect_list: @@ -136,6 +144,13 @@ class CmdTestScripts(CmdTestBase): t.exit_val = 1 if ret else 3 return t + def ticker2a(self): + t = self.ticker( + add_opts = ['--proxy', '', '--download=cc'], + cached_data = False, + use_proxy = False) + return t + def ticker3(self): return self.ticker( [], diff --git a/test/init.sh b/test/init.sh index 134f4f3..eec6ec3 100755 --- a/test/init.sh +++ b/test/init.sh @@ -98,6 +98,7 @@ create_test_links() { test/modtest.py hard test/test-release.sh symbolic test/cmdtest_d/base.py symbolic + test/cmdtest_d/httpd/__init__.py symbolic test/cmdtest_d/include/common.py symbolic test/cmdtest_d/include/runner.py symbolic test/cmdtest_d/include/group_mgr.py symbolic diff --git a/test/overlay/fakemods/mmgen_node_tools/Ticker.py b/test/overlay/fakemods/mmgen_node_tools/Ticker.py new file mode 100644 index 0000000..8d1ede4 --- /dev/null +++ b/test/overlay/fakemods/mmgen_node_tools/Ticker.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# +# MMGen Node Tools, terminal-based programs for Bitcoin and forkcoin nodes +# Copyright (C)2013-2025 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-node-tools +# https://gitlab.com/mmgen/mmgen-node-tools + +""" +fakemods.mmgen_node_tools.Ticker: fake module for Ticker class +""" + +from .Ticker_orig import * + +class overlay_fake_DataSource: + class coinpaprika: + api_host = 'localhost:19900' + api_proto = 'http' + +DataSource.coinpaprika.api_host = overlay_fake_DataSource.coinpaprika.api_host +DataSource.coinpaprika.api_proto = overlay_fake_DataSource.coinpaprika.api_proto From a8adef0be54c752c10a8f34d594ed3faaa23de0c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 12 Oct 2025 10:01:51 +0000 Subject: [PATCH 154/175] mmgen-ticker: various fixes and cleanups --- mmgen_node_tools/Ticker.py | 39 +++++++++++++++++++--------------- test/cmdtest_d/httpd/ticker.py | 2 +- test/cmdtest_d/misc.py | 36 +++++++++++++++---------------- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index bfe899b..9aee86c 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -25,7 +25,7 @@ from decimal import Decimal from collections import namedtuple from mmgen.color import red, yellow, green, blue, orange, gray -from mmgen.util import msg, msg_r, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen +from mmgen.util import msg, msg_r, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf from mmgen.ui import do_pager homedir = os.getenv('HOME') @@ -99,18 +99,21 @@ class DataSource: if not os.path.exists(cfg.cachedir): os.makedirs(cfg.cachedir) - if not os.path.exists(self.json_fn): - open(self.json_fn, 'w').write('{}') - use_cached_data = cfg.cached_data and not gcfg.download if use_cached_data: data_type = 'json' - data_in = open(self.json_fn).read() + try: + data_in = open(self.json_fn).read() + except FileNotFoundError: + die(1, f'Cannot use cached data, because {self.json_fn_disp} does not exist') else: data_type = self.net_data_type - elapsed = int(time.time() - os.stat(self.json_fn).st_mtime) - if elapsed >= self.timeout or gcfg.testing: + try: + mtime = os.stat(self.json_fn).st_mtime + except FileNotFoundError: + mtime = 0 + if (elapsed := int(time.time() - mtime)) >= self.timeout or gcfg.testing: if gcfg.testing: msg('') self.fetch_delay() @@ -148,14 +151,14 @@ class DataSource: if use_cached_data: if not cfg.quiet: - msg(f'Using cached data from ~/{self.json_fn_rel}') + msg(f'Using cached data from {self.json_fn_disp}') else: if os.path.exists(self.json_fn): os.rename(self.json_fn, self.json_fn + '.bak') with open(self.json_fn, 'w') as fh: fh.write(json_text) if not cfg.quiet: - msg(f'JSON data cached to ~/{self.json_fn_rel}') + msg(f'JSON data cached to {self.json_fn_disp}') if gcfg.download: sys.exit(0) @@ -168,8 +171,8 @@ class DataSource: return data @property - def json_fn_rel(self): - return os.path.relpath(self.json_fn, start=homedir) + def json_fn_disp(self): + return '~/' + os.path.relpath(self.json_fn, start=homedir) class coinpaprika(base): desc = 'CoinPaprika' @@ -186,8 +189,9 @@ class DataSource: self.asset_limit = int(cfg.asset_limit or self.dfl_asset_limit) def rate_limit_errmsg(self, elapsed): + rem = self.timeout - elapsed return ( - f'Rate limit exceeded! Retry in {self.timeout-elapsed} seconds' + + f'Rate limit exceeded! Retry in {rem} second{suf(rem)}' + ('' if cfg.btc_only else ', or use --cached-data or --btc')) @property @@ -270,7 +274,8 @@ class DataSource: 'last_updated': data['regularMarketTime']} def rate_limit_errmsg(self, elapsed): - return f'Rate limit exceeded! Retry in {self.timeout-elapsed} seconds, or use --cached-data' + rem = self.timeout - elapsed + return f'Rate limit exceeded! Retry in {rem} second{suf(rem)}, or use --cached-data' @property def json_fn(self): @@ -364,10 +369,10 @@ def gen_data(data): checking for duplicates. """ - def dup_sym_errmsg(dup_sym): + def dup_sym_errmsg(data_type, dup_sym): return ( f'The symbol {dup_sym!r} is shared by the following assets:\n' + - '\n ' + '\n '.join(d['id'] for d in data['cc'] if d['symbol'] == dup_sym) + + '\n ' + '\n '.join(d['id'] for d in data[data_type].data if d['symbol'] == dup_sym) + '\n\nPlease specify the asset by one of the full IDs listed above\n' + f'instead of {dup_sym!r}') @@ -421,7 +426,7 @@ def gen_data(data): if not isinstance(v, dict): die(2, str(v)) if id in found['id']: - die(1, dup_sym_errmsg(id)) + die(1, dup_sym_errmsg('fi', id)) if m := data['hi'].get(k): spot = v['regularMarketPrice']['raw'] hist = tuple(m.values()) @@ -444,7 +449,7 @@ def gen_data(data): if wants[k]: if d[k] in wants[k]: if d[k] in found[k]: - die(1, dup_sym_errmsg(d[k])) + die(1, dup_sym_errmsg('cc', d[k])) if not 'price_usd' in d: d['price_usd'] = Decimal(str(d['quotes']['USD']['price'])) d['price_btc'] = Decimal(str(d['quotes']['USD']['price'])) / btcusd diff --git a/test/cmdtest_d/httpd/ticker.py b/test/cmdtest_d/httpd/ticker.py index 2d946ae..6131875 100755 --- a/test/cmdtest_d/httpd/ticker.py +++ b/test/cmdtest_d/httpd/ticker.py @@ -21,7 +21,7 @@ class TickerServer(HTTPD): def make_response_body(self, method, environ): - with open(f'test/ref/ticker/ticker.json') as fh: + with open('test/ref/ticker/ticker.json') as fh: text = fh.read() return text.encode() diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index e9d4c32..1cde41e 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -61,19 +61,16 @@ class CmdTestScripts(CmdTestBase): color = True cmd_group_in = ( - ('subgroup.ticker_setup', []), - ('subgroup.ticker', ['ticker_setup']), + ('subgroup.ticker', []), ) cmd_subgroups = { - 'ticker_setup': ( - "setup for 'ticker' subgroup", - ('ticker_setup', 'ticker setup'), - ), 'ticker': ( "'mmnode-ticker' script", ('ticker1', 'ticker [--help]'), + ('copy_files', 'copying JSON files to cache'), + ('ticker1a', 'ticker [--download=cc] (early caching)'), + ('ticker1b', 'ticker [--download=cc] (late caching)'), ('ticker2', 'ticker (bad proxy)'), - ('ticker2a', 'ticker [--download=cc]'), ('ticker3', 'ticker [--cached-data]'), ('ticker4', 'ticker [--cached-data --wide]'), ('ticker5', 'ticker [--cached-data --wide --adjust=-0.766] (usr cfg file)'), @@ -103,7 +100,7 @@ class CmdTestScripts(CmdTestBase): def nt_datadir(self): return os.path.join( cfg.data_dir_root, 'node_tools' ) - def ticker_setup(self): + def copy_files(self): self.spawn('',msg_only=True) shutil.copy2(os.path.join(refdir,'ticker-finance.json'),self.tmpdir) shutil.copy2(os.path.join(refdir,'ticker-finance-history.json'),self.tmpdir) @@ -135,20 +132,23 @@ class CmdTestScripts(CmdTestBase): t.expect('USAGE:') return t - def ticker2(self): - t = self.ticker(cached_data=False) - if not cfg.skipping_deps: - t.expect('Creating') - t.expect('Creating') - ret = t.expect(['proxy host could not be resolved', 'unexpected keyword']) - t.exit_val = 1 if ret else 3 - return t - - def ticker2a(self): + def ticker1a(self, first_run=True): t = self.ticker( add_opts = ['--proxy', '', '--download=cc'], cached_data = False, use_proxy = False) + if first_run and not cfg.skipping_deps: + t.expect('Creating') + t.expect('Creating') + return t + + def ticker1b(self): + return self.ticker1a(first_run=False) + + def ticker2(self): + t = self.ticker(cached_data=False) + ret = t.expect(['proxy host could not be resolved', 'unexpected keyword']) + t.exit_val = 1 if ret else 3 return t def ticker3(self): From 3b8aede9bad83e4187e888d7be939ae909508075 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 12 Oct 2025 10:01:51 +0000 Subject: [PATCH 155/175] Ticker.gen_data(): new `process_data` class --- mmgen_node_tools/Ticker.py | 135 ++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 55 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 9aee86c..8580553 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -25,7 +25,7 @@ from decimal import Decimal from collections import namedtuple from mmgen.color import red, yellow, green, blue, orange, gray -from mmgen.util import msg, msg_r, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf +from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf from mmgen.ui import do_pager homedir = os.getenv('HOME') @@ -389,6 +389,73 @@ def gen_data(data): if error: die(1, 'Missing data, exiting') + class process_data: + + def cc(): + nonlocal btcusd + for d in data['cc']: + if d['id'] == 'btc-bitcoin': + btcusd = Decimal(str(d['quotes']['USD']['price'])) + break + else: + raise ValueError('malformed cryptocurrency data') + for k in ('id', 'symbol'): + for d in data['cc']: + if wants[k]: + if d[k] in wants[k]: + if d[k] in found[k]: + die(1, dup_sym_errmsg('cc', d[k])) + if not 'price_usd' in d: + d['price_usd'] = Decimal(str(d['quotes']['USD']['price'])) + d['price_btc'] = Decimal(str(d['quotes']['USD']['price'])) / btcusd + d['percent_change_24h'] = d['quotes']['USD']['percent_change_24h'] + d['percent_change_7d'] = d['quotes']['USD']['percent_change_7d'] + d['percent_change_30d'] = d['quotes']['USD']['percent_change_30d'] + d['percent_change_1y'] = d['quotes']['USD']['percent_change_1y'] + d['last_updated'] = int(datetime.datetime.fromisoformat( + d['last_updated']).timestamp()) + yield (d['id'], d) + found[k].add(d[k]) + wants[k].remove(d[k]) + if d[k] in usr_rate_assets_want[k]: + rate_assets[d['symbol']] = d # NB: using symbol instead of ID for key + else: + break + + def fi(): + get_id = src_cls['fi'].get_id + conv_func = src_cls['fi'].conv_data + for k, v in data['fi'].items(): + id = get_id(k, v) + if wants['id']: + if id in wants['id']: + if not isinstance(v, dict): + die(2, str(v)) + if id in found['id']: + die(1, dup_sym_errmsg('fi', id)) + if hist := hist_close.get(k): + spot = v['regularMarketPrice']['raw'] + v['pct_chg_1wk'] = (spot / hist.close_1wk - 1) * 100 + v['pct_chg_4wks'] = (spot / hist.close_4wks - 1) * 100 # 4 weeks ≈ 1 month + v['pct_chg_1y'] = (spot / hist.close_1y - 1) * 100 + else: + v['pct_chg_1wk'] = v['pct_chg_4wks'] = v['pct_chg_1y'] = None + yield (id, conv_func(id, v, btcusd)) + found['id'].add(id) + wants['id'].remove(id) + if id in usr_rate_assets_want['id']: # NB: using symbol instead of ID for key: + rate_assets[k] = conv_func(id, v, btcusd) + else: + break + + def hi(): + ret = namedtuple('historical_closing_prices', ['close_1wk', 'close_4wks', 'close_1y']) + nonlocal hist_close + for k, v in data['hi'].items(): + hist = tuple(v.values()) + hist_close[k] = ret(hist[-2]['close'], hist[-5]['close'], hist[0]['close']) + return () + rows_want = { 'id': {r.id for r in cfg.rows if isinstance(r, tuple) and r.id} - {'usd-us-dollar'}, 'symbol': {r.symbol for r in cfg.rows if isinstance(r, tuple) and r.id is None} - {'USD'}} @@ -411,62 +478,20 @@ def gen_data(data): wants = {k: rows_want[k] | usr_wants[k] for k in ('id', 'symbol')} - for d in data['cc']: - if d['id'] == 'btc-bitcoin': - btcusd = Decimal(str(d['quotes']['USD']['price'])) - break + btcusd = Decimal('1') # dummy + hist_close = {} - get_id = src_cls['fi'].get_id - conv_func = src_cls['fi'].conv_data + parse_fail = False + for data_type in ('cc', 'hi', 'fi'): # 'fi' depends on 'cc' and 'hi' so must go last + if data_type in data: + try: + yield from getattr(process_data, data_type)() + except Exception as e: + rmsg(f'Error in source data {data_type!r}: {e}') + parse_fail = True - for k, v in data['fi'].items(): - id = get_id(k, v) - if wants['id']: - if id in wants['id']: - if not isinstance(v, dict): - die(2, str(v)) - if id in found['id']: - die(1, dup_sym_errmsg('fi', id)) - if m := data['hi'].get(k): - spot = v['regularMarketPrice']['raw'] - hist = tuple(m.values()) - v['pct_chg_1wk'], v['pct_chg_4wks'], v['pct_chg_1y'] = ( - (spot / hist[-2]['close'] - 1) * 100, - (spot / hist[-5]['close'] - 1) * 100, # 4 weeks ≈ 1 month - (spot / hist[0]['close'] - 1) * 100) - else: - v['pct_chg_1wk'] = v['pct_chg_4wks'] = v['pct_chg_1y'] = None - yield (id, conv_func(id, v, btcusd)) - found['id'].add(id) - wants['id'].remove(id) - if id in usr_rate_assets_want['id']: - rate_assets[k] = conv_func(id, v, btcusd) # NB: using symbol instead of ID for key - else: - break - - for k in ('id', 'symbol'): - for d in data['cc']: - if wants[k]: - if d[k] in wants[k]: - if d[k] in found[k]: - die(1, dup_sym_errmsg('cc', d[k])) - if not 'price_usd' in d: - d['price_usd'] = Decimal(str(d['quotes']['USD']['price'])) - d['price_btc'] = Decimal(str(d['quotes']['USD']['price'])) / btcusd - d['percent_change_24h'] = d['quotes']['USD']['percent_change_24h'] - d['percent_change_7d'] = d['quotes']['USD']['percent_change_7d'] - d['percent_change_30d'] = d['quotes']['USD']['percent_change_30d'] - d['percent_change_1y'] = d['quotes']['USD']['percent_change_1y'] - # .replace('Z','+00:00') -- Python 3.9 backport - d['last_updated'] = int(datetime.datetime.fromisoformat( - d['last_updated'].replace('Z', '+00:00')).timestamp()) - yield (d['id'], d) - found[k].add(d[k]) - wants[k].remove(d[k]) - if d[k] in usr_rate_assets_want[k]: - rate_assets[d['symbol']] = d # NB: using symbol instead of ID for key - else: - break + if parse_fail: + die(2, 'Invalid data encountered, exiting') check_assets_found(usr_wants, found) From e7de6890796505f2fa9499fae80c459455dc7aa2 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 12 Oct 2025 10:01:51 +0000 Subject: [PATCH 156/175] mmnode-ticker: cache network data after parsing --- mmgen_node_tools/Ticker.py | 48 ++++++++++++++++++++++++----------- mmgen_node_tools/data/version | 2 +- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 8580553..bbca8ef 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -19,7 +19,7 @@ mmgen_node_tools.Ticker: Display price information for cryptocurrency and other # Possible alternatives: # - https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,LTC&tsyms=USD,EUR -import sys, os, re, time, datetime, json, yaml, random +import os, re, time, datetime, json, yaml, random from subprocess import run, PIPE, CalledProcessError from decimal import Decimal from collections import namedtuple @@ -149,20 +149,18 @@ class DataSource: elif 'error' in data: die(1, data['error']) + self.data = self.postprocess_data(data) + if use_cached_data: + self.json_text = None if not cfg.quiet: msg(f'Using cached data from {self.json_fn_disp}') else: - if os.path.exists(self.json_fn): - os.rename(self.json_fn, self.json_fn + '.bak') - with open(self.json_fn, 'w') as fh: - fh.write(json_text) - if not cfg.quiet: - msg(f'JSON data cached to {self.json_fn_disp}') - if gcfg.download: - sys.exit(0) + self.json_text = json_text + if cache_data(self, no_overwrite=True): + self.json_text = None - return self.postprocess_data(data) + return self def json_data_error_msg(self, json_text): pass @@ -393,14 +391,14 @@ def gen_data(data): def cc(): nonlocal btcusd - for d in data['cc']: + for d in data['cc'].data: if d['id'] == 'btc-bitcoin': btcusd = Decimal(str(d['quotes']['USD']['price'])) break else: raise ValueError('malformed cryptocurrency data') for k in ('id', 'symbol'): - for d in data['cc']: + for d in data['cc'].data: if wants[k]: if d[k] in wants[k]: if d[k] in found[k]: @@ -425,7 +423,7 @@ def gen_data(data): def fi(): get_id = src_cls['fi'].get_id conv_func = src_cls['fi'].conv_data - for k, v in data['fi'].items(): + for k, v in data['fi'].data.items(): id = get_id(k, v) if wants['id']: if id in wants['id']: @@ -451,7 +449,7 @@ def gen_data(data): def hi(): ret = namedtuple('historical_closing_prices', ['close_1wk', 'close_4wks', 'close_1y']) nonlocal hist_close - for k, v in data['hi'].items(): + for k, v in data['hi'].data.items(): hist = tuple(v.values()) hist_close[k] = ret(hist[-2]['close'], hist[-5]['close'], hist[0]['close']) return () @@ -489,10 +487,15 @@ def gen_data(data): except Exception as e: rmsg(f'Error in source data {data_type!r}: {e}') parse_fail = True + else: + cache_data(data[data_type]) if parse_fail: die(2, 'Invalid data encountered, exiting') + if gcfg.download: + return + check_assets_found(usr_wants, found) for asset in (cfg.usr_rows + cfg.usr_columns): @@ -518,6 +521,18 @@ def gen_data(data): 'price_btc': Decimal(1) / btcusd, 'last_updated': None}) +def cache_data(data_src, no_overwrite=False): + if data_src.json_text: + if os.path.exists(data_src.json_fn): + if no_overwrite: + return False + os.rename(data_src.json_fn, data_src.json_fn + '.bak') + with open(data_src.json_fn, 'w') as fh: + fh.write(data_src.json_text) + if not cfg.quiet: + msg(f'JSON data cached to {data_src.json_fn_disp}') + return True + def main(): def update_sample_file(usr_cfg_file): @@ -558,7 +573,7 @@ def main(): return if gcfg.list_ids: - do_pager('\n'.join(e['id'] for e in src_data['cc'])) + do_pager('\n'.join(e.data['id'] for e in src_data['cc'])) return global now @@ -566,6 +581,9 @@ def main(): data = dict(gen_data(src_data)) + if gcfg.download: + return + (do_pager if cfg.pager else Msg_r)( '\n'.join(getattr(Ticker, cfg.clsname)(data).gen_output()) + '\n') diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index f9d163c..4e5cebc 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev3 +3.6.dev4 From 9ffaed6a912a4a42c84f5e9675a0d70d5edd9fae Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 13 Oct 2025 14:58:54 +0000 Subject: [PATCH 157/175] mmnode-ticker: improve options setting Options set in the cfg file can now be unset on the command line, e.g.: $ mmnode-ticker --no-quiet --no-cached-data --- mmgen_node_tools/Ticker.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index bbca8ef..49ce857 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -712,6 +712,12 @@ def make_cfg(gcfg_arg): rows += (hdr,) + uniq_data return rows + def get_cfg_var(name): + if name in gcfg._uopts: + return getattr(gcfg, name) + else: + return getattr(gcfg, name) or cfg_in.cfg.get(name) + cfg_tuple = namedtuple('global_cfg',[ 'rows', 'usr_rows', @@ -772,27 +778,24 @@ def make_cfg(gcfg_arg): query = query, adjust = (lambda x: (100 + x) / 100 if x else 1)(Decimal(gcfg.adjust or 0)), clsname = 'trading' if query else 'overview', - btc_only = gcfg.btc or cfg_in.cfg.get('btc'), - add_prec = parse_add_precision(gcfg.add_precision or cfg_in.cfg.get('add_precision')), - cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir, + btc_only = get_cfg_var('btc'), + add_prec = parse_add_precision(get_cfg_var('add_precision')), + cachedir = get_cfg_var('cachedir') or dfl_cachedir, proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), portfolio = - get_portfolio() - if cfg_in.portfolio - and (gcfg.portfolio or cfg_in.cfg.get('portfolio')) - and not query + get_portfolio() if cfg_in.portfolio and get_cfg_var('portfolio') and not query else None, - percent_cols = parse_percent_cols(gcfg.percent_cols or cfg_in.cfg.get('percent_cols')), - asset_limit = gcfg.asset_limit or cfg_in.cfg.get('asset_limit'), - cached_data = gcfg.cached_data or cfg_in.cfg.get('cached_data'), - elapsed = gcfg.elapsed or cfg_in.cfg.get('elapsed'), - name_labels = gcfg.name_labels or cfg_in.cfg.get('name_labels'), - pager = gcfg.pager or cfg_in.cfg.get('pager'), - thousands_comma = gcfg.thousands_comma or cfg_in.cfg.get('thousands_comma'), - update_time = gcfg.update_time or cfg_in.cfg.get('update_time'), - quiet = gcfg.quiet or cfg_in.cfg.get('quiet'), - verbose = gcfg.verbose or cfg_in.cfg.get('verbose')) + percent_cols = parse_percent_cols(get_cfg_var('percent_cols')), + asset_limit = get_cfg_var('asset_limit'), + cached_data = get_cfg_var('cached_data'), + elapsed = get_cfg_var('elapsed'), + name_labels = get_cfg_var('name_labels'), + pager = get_cfg_var('pager'), + thousands_comma = get_cfg_var('thousands_comma'), + update_time = get_cfg_var('update_time'), + quiet = get_cfg_var('quiet'), + verbose = get_cfg_var('verbose')) def get_cfg_in(): ret = namedtuple('cfg_in_data', ['cfg', 'portfolio', 'cfg_file', 'portfolio_file']) From 083b29eae829d24be0714355f759d1258300da87 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 13 Oct 2025 14:59:00 +0000 Subject: [PATCH 158/175] mmnode-ticker: minor fixes and cleanups --- mmgen_node_tools/Ticker.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 49ce857..9d1c9df 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -25,7 +25,7 @@ from decimal import Decimal from collections import namedtuple from mmgen.color import red, yellow, green, blue, orange, gray -from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf +from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf, is_int from mmgen.ui import do_pager homedir = os.getenv('HOME') @@ -184,7 +184,7 @@ class DataSource: dfl_asset_limit = 2000 def __init__(self): - self.asset_limit = int(cfg.asset_limit or self.dfl_asset_limit) + self.asset_limit = int(cfg.asset_limit) if is_int(cfg.asset_limit) else self.dfl_asset_limit def rate_limit_errmsg(self, elapsed): rem = self.timeout - elapsed @@ -573,7 +573,7 @@ def main(): return if gcfg.list_ids: - do_pager('\n'.join(e.data['id'] for e in src_data['cc'])) + do_pager('\n'.join(e['id'] for e in src_data['cc'].data)) return global now @@ -606,7 +606,7 @@ def make_cfg(gcfg_arg): return tuple(gen()) def parse_percent_cols(arg): - if arg is None: + if arg is None or arg.lower() in ('none', ''): return [] res = arg.lower().split(',') for s in res: @@ -638,6 +638,8 @@ def make_cfg(gcfg_arg): source = parsed_id.source) cl_opt = getattr(gcfg, key) + if (cl_opt or '').lower() in ('', 'none'): + return () cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else [] return tuple(parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt)) @@ -718,6 +720,12 @@ def make_cfg(gcfg_arg): else: return getattr(gcfg, name) or cfg_in.cfg.get(name) + def get_proxy(name): + proxy = getattr(gcfg, name) + return ( + '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none') + else (proxy or cfg_in.cfg.get(name))) + cfg_tuple = namedtuple('global_cfg',[ 'rows', 'usr_rows', @@ -749,7 +757,6 @@ def make_cfg(gcfg_arg): src_cls = {k: getattr(DataSource, v) for k, v in DataSource.get_sources().items()} fi_pat = src_cls['fi'].asset_id_pat - cmd_args = gcfg._args cfg_in = get_cfg_in() if gcfg.test_suite: # required for testing with overlay @@ -757,15 +764,15 @@ def make_cfg(gcfg_arg): this_mod.src_cls = src_cls this_mod.cfg_in = cfg_in + if cmd_args := gcfg._args: + if len(cmd_args) > 1: + die(1, 'Only one command-line argument is allowed') + query = parse_query_arg(cmd_args[0]) + else: + query = None + usr_rows = parse_usr_asset_arg('add_rows') usr_columns = parse_usr_asset_arg('add_columns', use_cf_file=True) - query = parse_query_arg(cmd_args[0]) if cmd_args else None - - def get_proxy(name): - proxy = getattr(gcfg, name) - return ( - '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none') - else (proxy or cfg_in.cfg.get(name))) proxy = get_proxy('proxy') proxy = None if proxy == 'none' else proxy From 060b968ad4b88f845b9cd95fa89174a69b4b9743 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 13 Oct 2025 14:59:00 +0000 Subject: [PATCH 159/175] mmnode-ticker: display crypto assets by market cap Examples: # Display top 2000 assets by market cap: $ mmnode-ticker 2000 # Display assets 201-300 by market cap, displaying all available columns: $ mmnode-ticker --widest 201-300 # Display asset 32 by market cap: $ mmnode-ticker 32-32 Testing/demo: $ test/cmdtest.py -e scripts.ticker --- mmgen_node_tools/Ticker.py | 53 +++++++++++++++++++++++++++++---- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 25 +++++++++++----- test/cmdtest_d/misc.py | 42 ++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 13 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 9d1c9df..bf3398b 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -182,6 +182,7 @@ class DataSource: net_data_type = 'json' has_verbose = True dfl_asset_limit = 2000 + max_asset_idx = 1_000_000 def __init__(self): self.asset_limit = int(cfg.asset_limit) if is_int(cfg.asset_limit) else self.dfl_asset_limit @@ -576,6 +577,17 @@ def main(): do_pager('\n'.join(e['id'] for e in src_data['cc'].data)) return + global cfg + + if cfg.asset_range: + func = DataSource.coinpaprika.parse_asset_id + n, m = cfg.asset_range + cfg = cfg._replace(rows = + tuple(func(e['id'], require_label=False) for e in src_data['cc'].data[n-1:m]) + + tuple(['-']) + + tuple([func('btc-bitcoin', require_label=True)]) + + tuple(r for r in cfg.rows if isinstance(r, tuple) and r.source == 'fi')) + global now now = 1659465400 if gcfg.test_suite else time.time() # 1659524400 1659445900 @@ -643,6 +655,21 @@ def make_cfg(gcfg_arg): cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else [] return tuple(parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt)) + def parse_asset_range(s): + max_idx = DataSource.coinpaprika.max_asset_idx + match s.split('-'): + case [a, b] if is_int(a) and is_int(b): + n, m = (int(a), int(b)) + case [a] if is_int(a): + n, m = (1, int(a)) + case _: + return None + if n < 1 or m < 1 or n > m: + raise ValueError(f'‘{s}’: invalid asset range specifier') + if m > max_idx: + raise ValueError(f'‘{s}’: end of range must be <= {max_idx}') + return (n, m) + def parse_query_arg(s): """ asset_id:amount[:to_asset_id[:to_amount]] @@ -731,6 +758,7 @@ def make_cfg(gcfg_arg): 'usr_rows', 'usr_columns', 'query', + 'asset_range', 'adjust', 'clsname', 'btc_only', @@ -767,8 +795,10 @@ def make_cfg(gcfg_arg): if cmd_args := gcfg._args: if len(cmd_args) > 1: die(1, 'Only one command-line argument is allowed') - query = parse_query_arg(cmd_args[0]) + asset_range = parse_asset_range(cmd_args[0]) + query = None if asset_range else parse_query_arg(cmd_args[0]) else: + asset_range = None query = None usr_rows = parse_usr_asset_arg('add_rows') @@ -783,6 +813,7 @@ def make_cfg(gcfg_arg): usr_rows = usr_rows, usr_columns = usr_columns, query = query, + asset_range = asset_range, adjust = (lambda x: (100 + x) / 100 if x else 1)(Decimal(gcfg.adjust or 0)), clsname = 'trading' if query else 'overview', btc_only = get_cfg_var('btc'), @@ -930,12 +961,14 @@ class Ticker: if self.table_hdr: yield self.table_hdr - for row in self.rows: + for n, row in enumerate(self.rows, cfg.asset_range[0] if cfg.asset_range else 1): if isinstance(row, str): + if cfg.asset_range: + return yield ('-' * self.hl_wid) else: try: - yield self.fmt_row(self.data[row.id]) + yield self.fmt_row(self.data[row.id], idx=n) except KeyError: yield gray(f'(no data for {row.id})') @@ -989,7 +1022,7 @@ class Ticker: d['price_usd'] / self.col_usd_prices[k] ) * self.adjust for k in self.col_ids} - def fmt_row(self, d, amt=None, amt_fmt=None): + def fmt_row(self, d, amt=None, amt_fmt=None, idx=None): def fmt_pct(n): return gray(' --') if n is None else (red, green)[n>=0](f'{n:+7.2f}') @@ -1002,6 +1035,7 @@ class Ticker: amt_fmt = amt_fmt.rstrip('0').rstrip('.') return self.fs_num.format( + idx = idx, lbl = self.create_label(d['id']) if cfg.name_labels else d['symbol'], pc1 = fmt_pct(d.get('percent_change_7d')), pc2 = fmt_pct(d.get('percent_change_24h')), @@ -1061,6 +1095,15 @@ class Ticker: self.fs_num2 = ''.join(col_fs_data[c].fs_num for c in cols2) self.hl_wid2 = sum(col_fs_data[c].wid for c in cols2) + if cfg.asset_range: + def get_col1_w(): + for n, r in enumerate(cfg.rows): + if isinstance(r, str): + return len(str(n)) + col1_w = get_col1_w() + self.fs_str = ' ' * (col1_w + 2) + self.fs_str + self.fs_num = f'{{idx:{col1_w}}}) ' + self.fs_num + @property def table_hdr(self): return self.fs_str.format( @@ -1136,7 +1179,7 @@ class Ticker: self.fs_str += ' {upd}' self.hl_wid += self.upd_w + 2 - def fmt_row(self, d): + def fmt_row(self, d, idx=None): id = d['id'] p = self.prices[id][self.asset.id] * self.asset.amount p_spot = '{:{}{}.{}f}'.format(p, self.max_wid, self.comma, 8+cfg.add_prec) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 4e5cebc..636c831 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev4 +3.6.dev5 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index c36d886..8efb97a 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -25,7 +25,7 @@ opts_data = { ], 'text': { 'desc': 'Display prices for cryptocurrency and other assets', - 'usage': '[opts] [TRADE_SPECIFIER]', + 'usage': '[opts] [TRADE_SPECIFIER | ASSET_RANGE]', 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) @@ -71,14 +71,19 @@ opts_data = { """, 'notes': """ -The script has two display modes: ‘overview’, the default, and ‘trading’, the -latter being enabled when a TRADE_SPECIFIER argument (see below) is supplied -on the command line. +The script has three display modes: ‘overview’, enabled when no arguments are +given on the command line; ‘trading’, when a TRADE_SPECIFIER argument (see +below) is given; and ‘market cap’, when an ASSET_RANGE (see below) is given. Overview mode displays prices of all configured assets, and optionally the -user’s portfolio, while trading mode displays the price of a given quantity -of an asset in relation to other assets, optionally comparing an offered -price to the spot price. +user’s portfolio; trading mode displays the price of a given quantity of an +asset in relation to other assets, optionally comparing an offered price to +the spot price; and market cap mode lists a range of crypto assets selected +by current market cap. + +The ASSET_RANGE argument can be either an integer N, in which case the top +N assets by market cap will be displayed, or a hyphen-separated range N-M, +in which case assets from N to M by market cap will be displayed. ASSETS consist of either a symbol (e.g. ‘xmr’) or full ID (see --list-ids) consisting of symbol plus label (e.g. ‘xmr-monero’). In cases where the @@ -199,6 +204,12 @@ $ mmnode-ticker usd:2700:btc:0.123 # current spot price, at specified USDINR rate: $ mmnode-ticker -n -c inr-indian-rupee:79.5 inr:200000:btc:0.1 +# Display top 20 crypto assets by market cap, adding a Euro column: +$ mmnode-ticker -c eurusd=x 20 + +# Same as above, specifying assets using a range: +$ mmnode-ticker -c eurusd=x 1-20 + CONFIGURED ASSETS: {assets} diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 1cde41e..c2dceff 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -86,6 +86,10 @@ class CmdTestScripts(CmdTestBase): ('ticker15', 'ticker [--cached-data --wide --btc btc:2:usd:45000]'), ('ticker16', 'ticker [--cached-data --wide --elapsed -c eur,omr-omani-rial:2.59r'), ('ticker17', 'ticker [--cached-data --wide --elapsed -c bgn-bulgarian-lev:0.5113r:eur'), + ('ticker18', 'ticker [--cached-data --widest --add-columns eurusd=x 10]'), + ('ticker19', 'ticker [--cached-data 1-5]'), + ('ticker20', 'ticker [--cached-data 2-5]'), + ('ticker21', 'ticker [--cached-data 5-5]'), ) } @@ -319,3 +323,41 @@ class CmdTestScripts(CmdTestBase): 'BITCOIN 23,250.77 42,731.767 1.00000000', 'BULGARIAN LEV 0.54 1.000 0.00002340', ]) + + def ticker18(self): + return self.ticker( + ['10'], + [ + r'1\) BITCOIN 23,250.77 21,848.7527 1.00000000 \+18.96 \+15.61 \+11.15 \+0.89', + r'6\) ALGORAND 0.33 0.3120 0.00001428 \+16.47 \+13.57 \+9.69 \-0.82' + ], + add_opts = ['--widest', '--add-columns=eurusd=x']) + + def ticker19(self): + return self.ticker( + ['1-5'], + [ + 'USD EURUSD=X BTC ' + r'1\) BTC 23250.77 21848.7527 1.00000000', + r'5\) ADA 0.51 0.4764 0.00002180', + ], + add_opts = ['--add-columns=eurusd=x']) + + def ticker20(self): + return self.ticker( + ['2-5'], + [ + 'USD EURUSD=X BTC ' + r'2\) ETH 1659.66 1559.5846 0.07138094', + r'5\) ADA 0.51 0.4764 0.00002180', + ], + add_opts = ['--add-columns=eurusd=x']) + + def ticker21(self): + return self.ticker( + ['5-5'], + [ + 'USD EURUSD=X BTC ' + r'5\) ADA 0.51 0.4764 0.00002180', + ], + add_opts = ['--add-columns=eurusd=x']) From c1f42fc25b123c334262e8cb53b460753e1c0be2 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 15 Oct 2025 10:14:14 +0000 Subject: [PATCH 160/175] mmnode-ticker: various fixes and cleanups --- mmgen_node_tools/Ticker.py | 135 +++++++++++++++++--------------- mmgen_node_tools/main_ticker.py | 4 +- test/cmdtest_d/misc.py | 6 +- 3 files changed, 81 insertions(+), 64 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index bf3398b..7122e44 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -607,12 +607,10 @@ def make_cfg(gcfg_arg): def parse_asset_id(s, require_label=False): return src_cls['fi' if re.match(fi_pat, s) else 'cc'].parse_asset_id(s, require_label) - def get_rows_from_cfg(add_data=None): + def get_rows_from_cfg(): def gen(): - for n, (k, v) in enumerate(cfg_in.cfg['assets'].items()): + for k, v in cfg_in.cfg['assets'].items(): yield k - if add_data and k in add_data: - v += tuple(add_data[k]) for e in v: yield parse_asset_id(e, require_label=True) return tuple(gen()) @@ -650,7 +648,7 @@ def make_cfg(gcfg_arg): source = parsed_id.source) cl_opt = getattr(gcfg, key) - if (cl_opt or '').lower() in ('', 'none'): + if cl_opt is None or cl_opt.lower() in ('none', ''): return () cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else [] return tuple(parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt)) @@ -702,15 +700,16 @@ def make_cfg(gcfg_arg): def get_usr_assets(): return ( - 'user_added', - usr_rows + - (tuple(asset for asset in query if asset) if query else ()) + - usr_columns) + usr_rows + + (tuple(asset for asset in query if asset) if query else ()) + + usr_columns) - def get_portfolio_assets(ret=()): + def get_portfolio_assets(): if cfg_in.portfolio and gcfg.portfolio: ret = (parse_asset_id(e, require_label=True) for e in cfg_in.portfolio) - return ('portfolio', tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC')) + return tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC') + else: + return () def get_portfolio(): return {k: Decimal(v) for k, v in cfg_in.portfolio.items() @@ -730,14 +729,12 @@ def make_cfg(gcfg_arg): rows = ( ('trade_pair',) + query if (query and query.to_asset) else ('bitcoin', parse_asset_id('btc-bitcoin')) if gcfg.btc else - get_rows_from_cfg(add_data={'fiat':['usd-us-dollar']} if gcfg.add_columns else None)) - + get_rows_from_cfg()) for hdr, data in ( - (get_usr_assets(),) if query else - (get_usr_assets(), get_portfolio_assets())): + ('user_uniq', get_usr_assets()), + ('portfolio_uniq', get_portfolio_assets())): if data: - uniq_data = tuple(gen_uniq(data, 'symbol', preload=rows)) - if uniq_data: + if uniq_data := tuple(gen_uniq(data, 'symbol', preload=rows)): rows += (hdr,) + uniq_data return rows @@ -787,11 +784,6 @@ def make_cfg(gcfg_arg): cfg_in = get_cfg_in() - if gcfg.test_suite: # required for testing with overlay - from . import Ticker as this_mod - this_mod.src_cls = src_cls - this_mod.cfg_in = cfg_in - if cmd_args := gcfg._args: if len(cmd_args) > 1: die(1, 'Only one command-line argument is allowed') @@ -808,6 +800,13 @@ def make_cfg(gcfg_arg): proxy = None if proxy == 'none' else proxy proxy2 = get_proxy('proxy2') + portfolio = ( + get_portfolio() if cfg_in.portfolio and get_cfg_var('portfolio') and not query + else None) + + if portfolio and asset_range: + die(1, '--portfolio not supported in market cap view') + cfg = cfg_tuple( rows = create_rows(), usr_rows = usr_rows, @@ -821,9 +820,7 @@ def make_cfg(gcfg_arg): cachedir = get_cfg_var('cachedir') or dfl_cachedir, proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), - portfolio = - get_portfolio() if cfg_in.portfolio and get_cfg_var('portfolio') and not query - else None, + portfolio = portfolio, percent_cols = parse_percent_cols(get_cfg_var('percent_cols')), asset_limit = get_cfg_var('asset_limit'), cached_data = get_cfg_var('cached_data'), @@ -835,6 +832,8 @@ def make_cfg(gcfg_arg): quiet = get_cfg_var('quiet'), verbose = get_cfg_var('verbose')) + return (src_cls, cfg_in) + def get_cfg_in(): ret = namedtuple('cfg_in_data', ['cfg', 'portfolio', 'cfg_file', 'portfolio_file']) cfg_file, portfolio_file = ( @@ -871,7 +870,7 @@ class Ticker: self.col1_wid = max(len('TOTAL'), ( max(len(self.create_label(d['id'])) for d in data.values()) if cfg.name_labels else - max(len(d['symbol']) for d in data.values()))) + 1 + max(len(d['symbol']) for d in data.values()))) self.rows = [row._replace(id=self.get_id(row)) if isinstance(row, tuple) else row for row in cfg.rows] @@ -881,7 +880,7 @@ class Ticker: for row in self.rows if isinstance(row, tuple) and row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') - def format_last_update_col(self, cross_assets=()): + def format_last_updated_col(self, cross_assets=()): if cfg.elapsed: from mmgen.util2 import format_elapsed_hr @@ -899,19 +898,20 @@ class Ticker: min_t = None for row in self.rows: - if isinstance(row, tuple): - try: - t = int(d[row.id]['last_updated']) - except TypeError as e: - d[row.id]['last_updated_fmt'] = gray('--' if 'NoneType' in str(e) else str(e)) - except KeyError as e: - msg(str(e)) - pass - else: - t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( - (min(t, min_t) if min_t else t), - now = now) - max_w = max(len(t_fmt), max_w) + if not isinstance(row, tuple): + continue + try: + t = int(d[row.id]['last_updated']) + except TypeError as e: + d[row.id]['last_updated_fmt'] = gray('--' if 'NoneType' in str(e) else str(e)) + except KeyError as e: + msg(str(e)) + pass + else: + t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( + (min(t, min_t) if min_t else t), + now = now) + max_w = max(len(t_fmt), max_w) self.upd_w = max_w @@ -924,8 +924,9 @@ class Ticker: if asset.id: return asset.id else: + m = asset.symbol for d in self.data.values(): - if d['symbol'] == asset.symbol: + if m == d['symbol']: return d['id'] def create_label(self, id): @@ -961,16 +962,26 @@ class Ticker: if self.table_hdr: yield self.table_hdr - for n, row in enumerate(self.rows, cfg.asset_range[0] if cfg.asset_range else 1): - if isinstance(row, str): - if cfg.asset_range: - return - yield ('-' * self.hl_wid) - else: + if cfg.asset_range: + yield '-' * self.hl_wid + for n, row in enumerate(self.rows, cfg.asset_range[0]): + if isinstance(row, str): + break try: yield self.fmt_row(self.data[row.id], idx=n) except KeyError: yield gray(f'(no data for {row.id})') + else: + for row in self.rows: + if isinstance(row, str): + if cfg.asset_range: + return + yield ('-' * self.hl_wid) + else: + try: + yield self.fmt_row(self.data[row.id]) + except KeyError: + yield gray(f'(no data for {row.id})') yield '-' * self.hl_wid @@ -1003,7 +1014,7 @@ class Ticker: super().__init__(data) - self.format_last_update_col() + self.format_last_updated_col() if cfg.portfolio: self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] @@ -1072,9 +1083,9 @@ class Ticker: ) for k in self.col_ids} cols = ( - ['label', 'usd-us-dollar'] + - [asset.id for asset in self.usr_col_assets] + - [a for a, b in ( + ['label', 'usd-us-dollar'] + + [asset.id for asset in self.usr_col_assets] + + [a for a, b in ( ('btc-bitcoin', not cfg.btc_only), ('pct1y', 'y' in cfg.percent_cols), ('pct1m', 'm' in cfg.percent_cols), @@ -1082,6 +1093,17 @@ class Ticker: ('pct1d', 'd' in cfg.percent_cols), ('update_time', cfg.update_time)) if b]) + + if cfg.asset_range: + def get_num_w(): + for n, r in enumerate(cfg.rows): + if isinstance(r, str): + return len(str(n)) + num_w = get_num_w() + col_fs_data.update({ + 'idx': fd(' ' * (num_w + 2), f'{{idx:{num_w}}}) ', num_w + 2)}) + cols = ['idx'] + cols + cols2 = list(cols) if cfg.update_time: cols2.pop() @@ -1095,15 +1117,6 @@ class Ticker: self.fs_num2 = ''.join(col_fs_data[c].fs_num for c in cols2) self.hl_wid2 = sum(col_fs_data[c].wid for c in cols2) - if cfg.asset_range: - def get_col1_w(): - for n, r in enumerate(cfg.rows): - if isinstance(r, str): - return len(str(n)) - col1_w = get_col1_w() - self.fs_str = ' ' * (col1_w + 2) + self.fs_str - self.fs_num = f'{{idx:{col1_w}}}) ' + self.fs_num - @property def table_hdr(self): return self.fs_str.format( @@ -1152,7 +1165,7 @@ class Ticker: for a in self.usr_col_assets: self.prices[a.id]['usd-us-dollar'] = data[a.id]['price_usd'] - self.format_last_update_col(cross_assets=self.usr_col_assets) + self.format_last_updated_col(cross_assets=self.usr_col_assets) self.init_prec() self.init_fs() diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 8efb97a..c6b41b3 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -244,9 +244,9 @@ from . import Ticker gcfg = Config(opts_data=opts_data, caller_post_init=True) -Ticker.make_cfg(gcfg) +src_cls, cfg_in = Ticker.make_cfg(gcfg) -from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen, cfg_in, src_cls +from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen gcfg._post_init() diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index c2dceff..b64e33c 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -338,8 +338,10 @@ class CmdTestScripts(CmdTestBase): ['1-5'], [ 'USD EURUSD=X BTC ' + '--------------------------------------------- ' r'1\) BTC 23250.77 21848.7527 1.00000000', - r'5\) ADA 0.51 0.4764 0.00002180', + r'5\) ADA 0.51 0.4764 0.00002180' + ' ---------------------------------------------' ], add_opts = ['--add-columns=eurusd=x']) @@ -348,6 +350,7 @@ class CmdTestScripts(CmdTestBase): ['2-5'], [ 'USD EURUSD=X BTC ' + '--------------------------------------------- ' r'2\) ETH 1659.66 1559.5846 0.07138094', r'5\) ADA 0.51 0.4764 0.00002180', ], @@ -358,6 +361,7 @@ class CmdTestScripts(CmdTestBase): ['5-5'], [ 'USD EURUSD=X BTC ' + '--------------------------------------------- ' r'5\) ADA 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) From 3f921d333ccba378a6309123214e0a851e707ae8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 15 Oct 2025 10:14:20 +0000 Subject: [PATCH 161/175] Ticker.py: `parse_asset_id()`: unify call signature --- mmgen_node_tools/Ticker.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 7122e44..76d9d2a 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -233,7 +233,7 @@ class DataSource: return [data] if cfg.btc_only else data @staticmethod - def parse_asset_id(s, require_label): + def parse_asset_id(s, require_label=True): sym, label = (*s.split('-', 1), None)[:2] if require_label and not label: die(1, f'{s!r}: asset label is missing') @@ -314,7 +314,7 @@ class DataSource: return ticker.price @staticmethod - def parse_asset_id(s, require_label): + def parse_asset_id(s, require_label=True): return asset_tuple( symbol = s.upper(), id = s.lower(), @@ -604,7 +604,7 @@ def make_cfg(gcfg_arg): query_tuple = namedtuple('query', ['asset', 'to_asset']) asset_data = namedtuple('asset_data', ['symbol', 'id', 'amount', 'rate', 'rate_asset', 'source']) - def parse_asset_id(s, require_label=False): + def parse_asset_id(s, require_label=True): return src_cls['fi' if re.match(fi_pat, s) else 'cc'].parse_asset_id(s, require_label) def get_rows_from_cfg(): @@ -634,7 +634,7 @@ def make_cfg(gcfg_arg): ss = s.split(':') assert len(ss) in (1, 2, 3), f'{s}: malformed argument' asset_id, rate, rate_asset = (*ss, None, None)[:3] - parsed_id = parse_asset_id(asset_id) + parsed_id = parse_asset_id(asset_id, require_label=False) return asset_data( symbol = parsed_id.symbol, @@ -644,7 +644,7 @@ def make_cfg(gcfg_arg): None if rate is None else 1 / Decimal(rate[:-1]) if rate.lower().endswith('r') else Decimal(rate)), - rate_asset = parse_asset_id(rate_asset) if rate_asset else None, + rate_asset = parse_asset_id(rate_asset, require_label=False) if rate_asset else None, source = parsed_id.source) cl_opt = getattr(gcfg, key) @@ -673,7 +673,7 @@ def make_cfg(gcfg_arg): asset_id:amount[:to_asset_id[:to_amount]] """ def parse_query_asset(asset_id, amount): - parsed_id = parse_asset_id(asset_id) + parsed_id = parse_asset_id(asset_id, require_label=False) return asset_data( symbol = parsed_id.symbol, id = parsed_id.id, @@ -706,7 +706,7 @@ def make_cfg(gcfg_arg): def get_portfolio_assets(): if cfg_in.portfolio and gcfg.portfolio: - ret = (parse_asset_id(e, require_label=True) for e in cfg_in.portfolio) + ret = (parse_asset_id(e) for e in cfg_in.portfolio) return tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC') else: return () From 253aa14a265f66de3b97f826c0bef4d5ff3a369d Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 15 Oct 2025 10:14:20 +0000 Subject: [PATCH 162/175] Ticker.py: new `RowDict` class --- mmgen_node_tools/Ticker.py | 86 +++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 76d9d2a..ed68eda 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -42,6 +42,11 @@ percent_cols = { 'y': 'year', } +class RowDict(dict): + + def __iter__(self): + return (e for v in self.values() for e in v) + class DataSource: source_groups = [ @@ -286,7 +291,7 @@ class DataSource: @property def symbols(self): - return [r.symbol for r in cfg.rows if isinstance(r, tuple) and r.source == 'fi'] + return [r.symbol for r in cfg.rows if r.source == 'fi'] def get_data_from_network(self): @@ -456,8 +461,8 @@ def gen_data(data): return () rows_want = { - 'id': {r.id for r in cfg.rows if isinstance(r, tuple) and r.id} - {'usd-us-dollar'}, - 'symbol': {r.symbol for r in cfg.rows if isinstance(r, tuple) and r.id is None} - {'USD'}} + 'id': {r.id for r in cfg.rows if r.id} - {'usd-us-dollar'}, + 'symbol': {r.symbol for r in cfg.rows if r.id is None} - {'USD'}} usr_rate_assets = tuple(u.rate_asset for u in cfg.usr_rows + cfg.usr_columns if u.rate_asset) usr_rate_assets_want = { 'id': {a.id for a in usr_rate_assets if a.id}, @@ -580,13 +585,16 @@ def main(): global cfg if cfg.asset_range: - func = DataSource.coinpaprika.parse_asset_id n, m = cfg.asset_range - cfg = cfg._replace(rows = - tuple(func(e['id'], require_label=False) for e in src_data['cc'].data[n-1:m]) - + tuple(['-']) - + tuple([func('btc-bitcoin', require_label=True)]) - + tuple(r for r in cfg.rows if isinstance(r, tuple) and r.source == 'fi')) + cfg = cfg._replace(rows = RowDict({ + 'asset_list': + tuple( + asset_tuple(e['symbol'], e['id'], source='cc') + for e in src_data['cc'].data[n-1:m]), + 'extra': + tuple( + [asset_tuple('BTC', 'btc-bitcoin', source='cc')] + + [r for r in cfg.rows if r.source == 'fi'])})) global now now = 1659465400 if gcfg.test_suite else time.time() # 1659524400 1659445900 @@ -607,14 +615,6 @@ def make_cfg(gcfg_arg): def parse_asset_id(s, require_label=True): return src_cls['fi' if re.match(fi_pat, s) else 'cc'].parse_asset_id(s, require_label) - def get_rows_from_cfg(): - def gen(): - for k, v in cfg_in.cfg['assets'].items(): - yield k - for e in v: - yield parse_asset_id(e, require_label=True) - return tuple(gen()) - def parse_percent_cols(arg): if arg is None or arg.lower() in ('none', ''): return [] @@ -726,16 +726,18 @@ def make_cfg(gcfg_arg): return int(s) def create_rows(): - rows = ( - ('trade_pair',) + query if (query and query.to_asset) else - ('bitcoin', parse_asset_id('btc-bitcoin')) if gcfg.btc else - get_rows_from_cfg()) + rows = RowDict( + {'trade_pair': query} if (query and query.to_asset) else + {'bitcoin': [parse_asset_id('btc-bitcoin')]} if gcfg.btc else + {k: tuple(parse_asset_id(e) for e in v) for k, v in cfg_in.cfg['assets'].items()}) for hdr, data in ( ('user_uniq', get_usr_assets()), ('portfolio_uniq', get_portfolio_assets())): if data: if uniq_data := tuple(gen_uniq(data, 'symbol', preload=rows)): - rows += (hdr,) + uniq_data + rows[hdr] = uniq_data + else: + rows[hdr] = () return rows def get_cfg_var(name): @@ -872,12 +874,10 @@ class Ticker: max(len(self.create_label(d['id'])) for d in data.values()) if cfg.name_labels else max(len(d['symbol']) for d in data.values()))) - self.rows = [row._replace(id=self.get_id(row)) if isinstance(row, tuple) else row - for row in cfg.rows] + self.rows = RowDict( + {k: tuple(row._replace(id=self.get_id(row)) for row in v) for k, v in cfg.rows.items()}) self.col_usd_prices = {k: self.data[k]['price_usd'] for k in self.col_ids} - - self.prices = {row.id: self.get_row_prices(row.id) - for row in self.rows if isinstance(row, tuple) and row.id in data} + self.prices = {row.id: self.get_row_prices(row.id) for row in self.rows if row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') def format_last_updated_col(self, cross_assets=()): @@ -898,8 +898,6 @@ class Ticker: min_t = None for row in self.rows: - if not isinstance(row, tuple): - continue try: t = int(d[row.id]['last_updated']) except TypeError as e: @@ -964,24 +962,20 @@ class Ticker: if cfg.asset_range: yield '-' * self.hl_wid - for n, row in enumerate(self.rows, cfg.asset_range[0]): - if isinstance(row, str): - break + for n, row in enumerate(self.rows['asset_list'], cfg.asset_range[0]): try: yield self.fmt_row(self.data[row.id], idx=n) except KeyError: yield gray(f'(no data for {row.id})') else: - for row in self.rows: - if isinstance(row, str): - if cfg.asset_range: - return - yield ('-' * self.hl_wid) - else: - try: - yield self.fmt_row(self.data[row.id]) - except KeyError: - yield gray(f'(no data for {row.id})') + for rows in self.rows.values(): + if rows: + yield '-' * self.hl_wid + for row in rows: + try: + yield self.fmt_row(self.data[row.id]) + except KeyError: + yield gray(f'(no data for {row.id})') yield '-' * self.hl_wid @@ -1019,7 +1013,7 @@ class Ticker: if cfg.portfolio: self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] for row in self.rows - if isinstance(row, tuple) and row.id in cfg.portfolio and row.id in data) + if row.id in cfg.portfolio and row.id in data) for col_id in self.col_ids} self.init_prec() @@ -1095,11 +1089,7 @@ class Ticker: if b]) if cfg.asset_range: - def get_num_w(): - for n, r in enumerate(cfg.rows): - if isinstance(r, str): - return len(str(n)) - num_w = get_num_w() + num_w = len(str(len(cfg.rows['asset_list']))) col_fs_data.update({ 'idx': fd(' ' * (num_w + 2), f'{{idx:{num_w}}}) ', num_w + 2)}) cols = ['idx'] + cols From 9aa4b4dcfef0d95e96513042e42cc86c6bc997ba Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 15 Oct 2025 10:14:20 +0000 Subject: [PATCH 163/175] mmnode-ticker: add `MarketCap` column Column is enabled automatically when script is invoked in market cap mode. --- mmgen_node_tools/Ticker.py | 8 ++++++-- mmgen_node_tools/data/version | 2 +- test/cmdtest_d/misc.py | 28 ++++++++++++++-------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index ed68eda..9e598de 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -416,6 +416,7 @@ def gen_data(data): d['percent_change_7d'] = d['quotes']['USD']['percent_change_7d'] d['percent_change_30d'] = d['quotes']['USD']['percent_change_30d'] d['percent_change_1y'] = d['quotes']['USD']['percent_change_1y'] + d['market_cap'] = d['quotes']['USD']['market_cap'] d['last_updated'] = int(datetime.datetime.fromisoformat( d['last_updated']).timestamp()) yield (d['id'], d) @@ -1041,6 +1042,7 @@ class Ticker: return self.fs_num.format( idx = idx, + mcap = d.get('market_cap') / 1_000_000_000 if cfg.asset_range else None, lbl = self.create_label(d['id']) if cfg.name_labels else d['symbol'], pc1 = fmt_pct(d.get('percent_change_7d')), pc2 = fmt_pct(d.get('percent_change_24h')), @@ -1091,8 +1093,9 @@ class Ticker: if cfg.asset_range: num_w = len(str(len(cfg.rows['asset_list']))) col_fs_data.update({ - 'idx': fd(' ' * (num_w + 2), f'{{idx:{num_w}}}) ', num_w + 2)}) - cols = ['idx'] + cols + 'idx': fd(' ' * (num_w + 2), f'{{idx:{num_w}}}) ', num_w + 2), + 'mcap': fd('{mcap:>12}', '{mcap:12.5f}', 12)}) + cols = ['idx', 'label', 'mcap'] + cols[1:] cols2 = list(cols) if cfg.update_time: @@ -1111,6 +1114,7 @@ class Ticker: def table_hdr(self): return self.fs_str.format( lbl = '', + mcap = 'MarketCap(B)', pc1 = ' CHG_7d', pc2 = 'CHG_24h', pc3 = 'CHG_1y', diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 636c831..921f776 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev5 +3.6.dev6 diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index b64e33c..a8d259e 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -328,8 +328,8 @@ class CmdTestScripts(CmdTestBase): return self.ticker( ['10'], [ - r'1\) BITCOIN 23,250.77 21,848.7527 1.00000000 \+18.96 \+15.61 \+11.15 \+0.89', - r'6\) ALGORAND 0.33 0.3120 0.00001428 \+16.47 \+13.57 \+9.69 \-0.82' + r'1\) BITCOIN 444.33652 23,250.77 21,848.7527 1.00000000 \+18.96 \+15.61 \+11.15 \+0.89', + r'6\) ALGORAND 2.30691 0.33 0.3120 0.00001428 \+16.47 \+13.57 \+9.69 \-0.82' ], add_opts = ['--widest', '--add-columns=eurusd=x']) @@ -337,11 +337,11 @@ class CmdTestScripts(CmdTestBase): return self.ticker( ['1-5'], [ - 'USD EURUSD=X BTC ' - '--------------------------------------------- ' - r'1\) BTC 23250.77 21848.7527 1.00000000', - r'5\) ADA 0.51 0.4764 0.00002180' - ' ---------------------------------------------' + r'MarketCap\(B\) USD EURUSD=X BTC ' + '--------------------------------------------------------- ' + r'1\) BTC 444.33652 23250.77 21848.7527 1.00000000', + r'5\) ADA 17.11161 0.51 0.4764 0.00002180' + ' ---------------------------------------------------------' ], add_opts = ['--add-columns=eurusd=x']) @@ -349,10 +349,10 @@ class CmdTestScripts(CmdTestBase): return self.ticker( ['2-5'], [ - 'USD EURUSD=X BTC ' - '--------------------------------------------- ' - r'2\) ETH 1659.66 1559.5846 0.07138094', - r'5\) ADA 0.51 0.4764 0.00002180', + r'MarketCap\(B\) USD EURUSD=X BTC ' + '--------------------------------------------------------- ' + r'2\) ETH 202.15129 1659.66 1559.5846 0.07138094', + r'5\) ADA 17.11161 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) @@ -360,8 +360,8 @@ class CmdTestScripts(CmdTestBase): return self.ticker( ['5-5'], [ - 'USD EURUSD=X BTC ' - '--------------------------------------------- ' - r'5\) ADA 0.51 0.4764 0.00002180', + r'MarketCap\(B\) USD EURUSD=X BTC ' + '--------------------------------------------------------- ' + r'5\) ADA 17.11161 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) From e71ef141bffd8f44998e1510edf197f423cb2e3b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 16 Oct 2025 17:09:09 +0000 Subject: [PATCH 164/175] mmnode-ticker: minor cleanups --- mmgen_node_tools/Ticker.py | 20 ++++++++++---------- mmgen_node_tools/main_ticker.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 9e598de..6aef36f 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -24,7 +24,7 @@ from subprocess import run, PIPE, CalledProcessError from decimal import Decimal from collections import namedtuple -from mmgen.color import red, yellow, green, blue, orange, gray +from mmgen.color import red, yellow, green, blue, orange, gray, cyan from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf, is_int from mmgen.ui import do_pager @@ -39,8 +39,7 @@ percent_cols = { 'd': 'day', 'w': 'week', 'm': 'month', - 'y': 'year', -} + 'y': 'year'} class RowDict(dict): @@ -713,8 +712,8 @@ def make_cfg(gcfg_arg): return () def get_portfolio(): - return {k: Decimal(v) for k, v in cfg_in.portfolio.items() - if (not gcfg.btc) or k == 'btc-bitcoin'} + return tuple((k, Decimal(v)) for k, v in cfg_in.portfolio.items() + if (not gcfg.btc) or k == 'btc-bitcoin') def parse_add_precision(arg): if not arg: @@ -932,7 +931,7 @@ class Ticker: return self.data[id]['name'].upper() def gen_output(self): - yield 'Current time: {} UTC'.format(time.strftime('%F %X', time.gmtime(now))) + yield 'Current time: {}'.format(cyan(time.strftime('%F %X', time.gmtime(now)) + ' UTC')) for asset in self.usr_col_assets: if asset.symbol != 'USD': @@ -987,7 +986,7 @@ class Ticker: yield blue('PORTFOLIO') yield self.table_hdr yield '-' * self.hl_wid - for sym, amt in cfg.portfolio.items(): + for sym, amt in cfg.portfolio: try: yield self.fmt_row(self.data[sym], amt=amt) except KeyError: @@ -1005,16 +1004,17 @@ class Ticker: self.adjust = cfg.adjust self.show_adj = self.adjust != 1 self.usr_col_assets = [asset._replace(id=self.get_id(asset)) for asset in cfg.usr_columns] - self.col_ids = ('usd-us-dollar',) + tuple(a.id for a in self.usr_col_assets) + ('btc-bitcoin',) + self.col_ids = ('usd-us-dollar', 'btc-bitcoin') + tuple(a.id for a in self.usr_col_assets) super().__init__(data) self.format_last_updated_col() if cfg.portfolio: - self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] + pf_dict = dict(cfg.portfolio) + self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * pf_dict[row.id] for row in self.rows - if row.id in cfg.portfolio and row.id in data) + if row.id in pf_dict and row.id in data) for col_id in self.col_ids} self.init_prec() diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index c6b41b3..e533e62 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -225,7 +225,7 @@ To add a portfolio, edit the file dfl_cachedir = os.path.relpath(dfl_cachedir, start=homedir), ds = fmt_dict(DataSource.get_sources(), fmt='equal_compact'), al = DataSource.coinpaprika.dfl_asset_limit, - pc = fmt_list(Ticker.percent_cols, fmt='bare')), + pc = fmt_list(Ticker.percent_cols, fmt='fancy')), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in), fmt='col', indent=' '), cfg = os.path.relpath(cfg_in.cfg_file, start=homedir), From e6d62fd18b2d9e5885ce4452c843850871550f60 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 16 Oct 2025 17:09:15 +0000 Subject: [PATCH 165/175] mmnode-ticker: display coin ranking in first column --- mmgen_node_tools/Ticker.py | 40 +++++++++++++++++++++++--------------- test/cmdtest_d/misc.py | 14 ++++++------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 6aef36f..a8b6595 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -865,6 +865,7 @@ class Ticker: offer = None to_asset = None + hidden_groups = ('extra',) def __init__(self, data): @@ -876,6 +877,14 @@ class Ticker: self.rows = RowDict( {k: tuple(row._replace(id=self.get_id(row)) for row in v) for k, v in cfg.rows.items()}) + + if cfg.asset_range: + self.max_rank = 0 + for group, rows in self.rows.items(): + if group not in self.hidden_groups: + for row in rows: + self.max_rank = max(self.max_rank, int(data[row.id]['rank'])) + self.col_usd_prices = {k: self.data[k]['price_usd'] for k in self.col_ids} self.prices = {row.id: self.get_row_prices(row.id) for row in self.rows if row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') @@ -931,6 +940,15 @@ class Ticker: return self.data[id]['name'].upper() def gen_output(self): + + def process_rows(rows): + yield '-' * self.hl_wid + for row in rows: + try: + yield self.fmt_row(self.data[row.id]) + except KeyError: + yield gray(f'(no data for {row.id})') + yield 'Current time: {}'.format(cyan(time.strftime('%F %X', time.gmtime(now)) + ' UTC')) for asset in self.usr_col_assets: @@ -961,21 +979,11 @@ class Ticker: yield self.table_hdr if cfg.asset_range: - yield '-' * self.hl_wid - for n, row in enumerate(self.rows['asset_list'], cfg.asset_range[0]): - try: - yield self.fmt_row(self.data[row.id], idx=n) - except KeyError: - yield gray(f'(no data for {row.id})') + yield from process_rows(self.rows['asset_list']) else: for rows in self.rows.values(): if rows: - yield '-' * self.hl_wid - for row in rows: - try: - yield self.fmt_row(self.data[row.id]) - except KeyError: - yield gray(f'(no data for {row.id})') + yield from process_rows(rows) yield '-' * self.hl_wid @@ -1028,7 +1036,7 @@ class Ticker: d['price_usd'] / self.col_usd_prices[k] ) * self.adjust for k in self.col_ids} - def fmt_row(self, d, amt=None, amt_fmt=None, idx=None): + def fmt_row(self, d, amt=None, amt_fmt=None): def fmt_pct(n): return gray(' --') if n is None else (red, green)[n>=0](f'{n:+7.2f}') @@ -1041,7 +1049,7 @@ class Ticker: amt_fmt = amt_fmt.rstrip('0').rstrip('.') return self.fs_num.format( - idx = idx, + idx = int(d['rank']) if cfg.asset_range else None, mcap = d.get('market_cap') / 1_000_000_000 if cfg.asset_range else None, lbl = self.create_label(d['id']) if cfg.name_labels else d['symbol'], pc1 = fmt_pct(d.get('percent_change_7d')), @@ -1091,7 +1099,7 @@ class Ticker: if b]) if cfg.asset_range: - num_w = len(str(len(cfg.rows['asset_list']))) + num_w = len(str(self.max_rank)) col_fs_data.update({ 'idx': fd(' ' * (num_w + 2), f'{{idx:{num_w}}}) ', num_w + 2), 'mcap': fd('{mcap:>12}', '{mcap:12.5f}', 12)}) @@ -1186,7 +1194,7 @@ class Ticker: self.fs_str += ' {upd}' self.hl_wid += self.upd_w + 2 - def fmt_row(self, d, idx=None): + def fmt_row(self, d): id = d['id'] p = self.prices[id][self.asset.id] * self.asset.amount p_spot = '{:{}{}.{}f}'.format(p, self.max_wid, self.comma, 8+cfg.add_prec) diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index a8d259e..3e909f4 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -329,7 +329,7 @@ class CmdTestScripts(CmdTestBase): ['10'], [ r'1\) BITCOIN 444.33652 23,250.77 21,848.7527 1.00000000 \+18.96 \+15.61 \+11.15 \+0.89', - r'6\) ALGORAND 2.30691 0.33 0.3120 0.00001428 \+16.47 \+13.57 \+9.69 \-0.82' + r'33\) ALGORAND 2.30691 0.33 0.3120 0.00001428 \+16.47 \+13.57 \+9.69 \-0.82' ], add_opts = ['--widest', '--add-columns=eurusd=x']) @@ -338,10 +338,10 @@ class CmdTestScripts(CmdTestBase): ['1-5'], [ r'MarketCap\(B\) USD EURUSD=X BTC ' - '--------------------------------------------------------- ' + '---------------------------------------------------------- ' r'1\) BTC 444.33652 23250.77 21848.7527 1.00000000', - r'5\) ADA 17.11161 0.51 0.4764 0.00002180' - ' ---------------------------------------------------------' + r'8\) ADA 17.11161 0.51 0.4764 0.00002180' + ' ----------------------------------------------------------' ], add_opts = ['--add-columns=eurusd=x']) @@ -350,9 +350,9 @@ class CmdTestScripts(CmdTestBase): ['2-5'], [ r'MarketCap\(B\) USD EURUSD=X BTC ' - '--------------------------------------------------------- ' + '---------------------------------------------------------- ' r'2\) ETH 202.15129 1659.66 1559.5846 0.07138094', - r'5\) ADA 17.11161 0.51 0.4764 0.00002180', + r'8\) ADA 17.11161 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) @@ -362,6 +362,6 @@ class CmdTestScripts(CmdTestBase): [ r'MarketCap\(B\) USD EURUSD=X BTC ' '--------------------------------------------------------- ' - r'5\) ADA 17.11161 0.51 0.4764 0.00002180', + r'8\) ADA 17.11161 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) From 0a953e3ca039040b9ca4dbff501913bc39b760bb Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 16 Oct 2025 17:09:15 +0000 Subject: [PATCH 166/175] mmnode-ticker: sort output by various parameters Supported parameters: d - 1-day % change w - 1-week % change m - 1-month % change y - 1-year % change p - asset price c - market cap Examples: # Display top 50 assets by market cap, sorting by price change # in last 24 hours: $ mmnode-ticker --sort=d 50 --- mmgen_node_tools/Ticker.py | 45 ++++++++++++++++++++++- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 11 +++++- test/cmdtest_d/misc.py | 63 +++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index a8b6595..d3045d4 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -24,7 +24,7 @@ from subprocess import run, PIPE, CalledProcessError from decimal import Decimal from collections import namedtuple -from mmgen.color import red, yellow, green, blue, orange, gray, cyan +from mmgen.color import red, yellow, green, blue, orange, gray, cyan, pink from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf, is_int from mmgen.ui import do_pager @@ -41,6 +41,15 @@ percent_cols = { 'm': 'month', 'y': 'year'} +sp = namedtuple('sort_parameter', ['key', 'desc']) +sort_params = { + 'd': sp('percent_change_24h', '1-day % change'), + 'w': sp('percent_change_7d', '1-week % change'), + 'm': sp('percent_change_30d', '1-month % change'), + 'y': sp('percent_change_1y', '1-year % change'), + 'p': sp('price_usd', 'asset price'), + 'c': sp('market_cap', 'market cap')} + class RowDict(dict): def __iter__(self): @@ -274,6 +283,7 @@ class DataSource: 'percent_change_30d': data['pct_chg_4wks'], 'percent_change_7d': data['pct_chg_1wk'], 'percent_change_24h': data['regularMarketChangePercent']['raw'] * 100, + 'market_cap': 0, # dummy - required for sorting 'last_updated': data['regularMarketTime']} def rate_limit_errmsg(self, elapsed): @@ -752,6 +762,19 @@ def make_cfg(gcfg_arg): '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none') else (proxy or cfg_in.cfg.get(name))) + def get_sort_opt(): + match get_cfg_var('sort'): + case None: + return None + case s if s in sort_params: + return (s, True) + case s if s in ['r' + ch for ch in sort_params]: + return (s[1], False) + case s: + die(1, + f'{s!r}: invalid parameter for --sort option (must be one of {fmt_list(sort_params)})' + '\nTo reverse the sort, prefix the code letter with ‘r’') + cfg_tuple = namedtuple('global_cfg',[ 'rows', 'usr_rows', @@ -766,6 +789,7 @@ def make_cfg(gcfg_arg): 'proxy', 'proxy2', 'portfolio', + 'sort', 'percent_cols', 'asset_limit', 'cached_data', @@ -823,6 +847,7 @@ def make_cfg(gcfg_arg): proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), portfolio = portfolio, + sort = get_sort_opt(), percent_cols = parse_percent_cols(get_cfg_var('percent_cols')), asset_limit = get_cfg_var('asset_limit'), cached_data = get_cfg_var('cached_data'), @@ -869,6 +894,8 @@ class Ticker: def __init__(self, data): + global cfg + self.comma = ',' if cfg.thousands_comma else '' self.col1_wid = max(len('TOTAL'), ( @@ -885,6 +912,18 @@ class Ticker: for row in rows: self.max_rank = max(self.max_rank, int(data[row.id]['rank'])) + if cfg.sort: + code, reverse = cfg.sort + key = sort_params[code].key + sort_func = lambda row: data[row.id][key] + pf_sort_func = lambda row: data[row[0]][key] + for group in self.rows.keys(): + if group not in self.hidden_groups: + self.rows[group] = sorted(self.rows[group], key=sort_func, reverse=reverse) + if cfg.portfolio: + cfg = cfg._replace( + portfolio = sorted(cfg.portfolio, key=pf_sort_func, reverse=reverse)) + self.col_usd_prices = {k: self.data[k]['price_usd'] for k in self.col_ids} self.prices = {row.id: self.get_row_prices(row.id) for row in self.rows if row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') @@ -951,6 +990,10 @@ class Ticker: yield 'Current time: {}'.format(cyan(time.strftime('%F %X', time.gmtime(now)) + ' UTC')) + if cfg.sort: + text = sort_params[cfg.sort[0]].desc + ('' if cfg.sort[1] else ' [reversed]') + yield f'Sort order: {pink(text.upper())}' + for asset in self.usr_col_assets: if asset.symbol != 'USD': usdprice = self.data[asset.id]['price_usd'] diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 921f776..1de14a5 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev6 +3.6.dev7 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index e533e62..7be5e90 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -57,6 +57,9 @@ opts_data = { -r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, see ASSET SPECIFIERS below). Can also be used to supply a USD exchange rate for missing assets. +-s, --sort=P Sort output according to parameter P. Valid parameters + are {sp_codes}. See SORT PARAMETERS below. + To reverse the sort, prefix the parameter with ‘r’. -t, --testing Print command(s) to be executed to stdout and exit -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column @@ -133,6 +136,10 @@ A TRADE_SPECIFIER is a single argument in the format: a USD rate for the missing asset(s) must be supplied via the --add-columns or --add-rows options. +SORT PARAMETERS: + + {sp_fmt} + PROXY NOTE @@ -225,6 +232,7 @@ To add a portfolio, edit the file dfl_cachedir = os.path.relpath(dfl_cachedir, start=homedir), ds = fmt_dict(DataSource.get_sources(), fmt='equal_compact'), al = DataSource.coinpaprika.dfl_asset_limit, + sp_codes = fmt_list(sort_params, fmt='fancy'), pc = fmt_list(Ticker.percent_cols, fmt='fancy')), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in), fmt='col', indent=' '), @@ -232,6 +240,7 @@ To add a portfolio, edit the file pf_cfg = os.path.relpath(cfg_in.portfolio_file, start=homedir), al = DataSource.coinpaprika.dfl_asset_limit, cc = src_cls['cc'](), + sp_fmt = '\n '.join(f'‘{k}’ - {v.desc}' for k, v in sort_params.items()), fi = src_cls['fi']()) } } @@ -246,7 +255,7 @@ gcfg = Config(opts_data=opts_data, caller_post_init=True) src_cls, cfg_in = Ticker.make_cfg(gcfg) -from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen +from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen, sort_params gcfg._post_init() diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 3e909f4..275117f 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -90,6 +90,14 @@ class CmdTestScripts(CmdTestBase): ('ticker19', 'ticker [--cached-data 1-5]'), ('ticker20', 'ticker [--cached-data 2-5]'), ('ticker21', 'ticker [--cached-data 5-5]'), + ('ticker22', 'ticker [--sort=rp]'), + ('ticker23', 'ticker [--sort=rp xmr:10]'), + ('ticker24', 'ticker [--sort=p]'), + ('ticker25', 'ticker [--sort=p 200]'), + ('ticker26', 'ticker [--sort=c -r algo,ada]'), + ('ticker27', 'ticker [--sort=rp -r algo,ada]'), + ('ticker28', 'ticker [--sort=d -r algo,ada]'), + ('ticker29', 'ticker [--sort=y -r algo,ada]'), ) } @@ -365,3 +373,58 @@ class CmdTestScripts(CmdTestBase): r'8\) ADA 17.11161 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) + + def ticker22(self): + return self.ticker( + [], + ['MONERO', 'ETHEREUM', 'BITCOIN', 'SILVER', 'BRENT', 'GOLD'], + add_opts = ['--name-labels', '--sort=rp']) + + def ticker23(self): + return self.ticker( + [], + ['MONERO', 'ETHEREUM', 'BITCOIN', 'SILVER', 'BRENT', 'GOLD'], + add_opts = ['--name-labels', '--sort=rp', 'xmr:10']) + + def ticker24(self): + return self.ticker( + [], + ['BITCOIN', 'ETHEREUM', 'MONERO', 'GOLD', 'BRENT', 'SILVER'], + add_opts = ['--name-labels', '--sort=p']) + + def ticker25(self): + return self.ticker( + [], + [ + r' 1\) BITCOIN', + r' 2\) ETHEREUM', + r'30\) MONERO', + r'23\) LITECOIN', + r' 8\) CARDANO', + r'33\) ALGORAND' + ], + add_opts = ['--name-labels', '--sort=p', '200']) + + def ticker26(self): + return self.ticker( + [], + ['BITCOIN', 'ETHEREUM', 'MONERO', 'CARDANO', 'ALGORAND'], + add_opts = ['--name-labels', '--sort=c', '-r', 'ada,algo']) + + def ticker27(self): + return self.ticker( + [], + ['MONERO', 'ETHEREUM', 'BITCOIN', 'S&P', 'NASDAQ', 'DOW', 'ALGORAND', 'CARDANO'], + add_opts = ['--name-labels', '--sort=rp', '--add-rows=ada-cardano,algo-algorand']) + + def ticker28(self): + return self.ticker( + [], + ['ETHEREUM', 'MONERO', 'BITCOIN', 'NASDAQ', 'S&P', 'DOW', 'CARDANO', 'ALGORAND'], + add_opts = ['--widest', '--sort=d', '-r', 'ada,algo']) + + def ticker29(self): + return self.ticker( + [], + ['ETHEREUM', 'BITCOIN', 'MONERO', 'S&P', 'DOW', 'NASDAQ', 'CARDANO', 'ALGORAND'], + add_opts = ['--widest', '-s', 'y', '-r', 'ada,algo']) From 13234e990bc70b2cd53b78faaed121fc584f8cd8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 17 Oct 2025 09:07:55 +0000 Subject: [PATCH 167/175] cmdtest.py misc.ticker: cleanups --- test/cmdtest_d/misc.py | 43 +++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 275117f..9060731 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -67,7 +67,7 @@ class CmdTestScripts(CmdTestBase): 'ticker': ( "'mmnode-ticker' script", ('ticker1', 'ticker [--help]'), - ('copy_files', 'copying JSON files to cache'), + ('copy_cache_files', 'copying JSON files to cache'), ('ticker1a', 'ticker [--download=cc] (early caching)'), ('ticker1b', 'ticker [--download=cc] (late caching)'), ('ticker2', 'ticker (bad proxy)'), @@ -106,17 +106,24 @@ class CmdTestScripts(CmdTestBase): return self.ticker_server = TickerServer(cfg) self.ticker_server.start() + self.dests = { + 'nt_datadir': os.path.join(cfg.data_dir_root, 'node_tools'), + 'cache': self.tmpdir} return super().__init__(cfg, trunner, cfgs, spawn) - @property - def nt_datadir(self): - return os.path.join( cfg.data_dir_root, 'node_tools' ) + def rm_file(self, fn, dest='nt_datadir'): + os.unlink(os.path.join(self.dests[dest], fn)) - def copy_files(self): - self.spawn('',msg_only=True) - shutil.copy2(os.path.join(refdir,'ticker-finance.json'),self.tmpdir) - shutil.copy2(os.path.join(refdir,'ticker-finance-history.json'),self.tmpdir) - shutil.copy2(os.path.join(refdir,'ticker-btc.json'),self.tmpdir) + def copy_file(self, src_fn, dest_fn=None, dest='nt_datadir'): + shutil.copy2( + os.path.join(refdir, src_fn), + os.path.join(self.dests[dest], dest_fn or src_fn)) + + def copy_cache_files(self): + self.spawn('', msg_only=True) + self.copy_file('ticker-finance.json', dest='cache') + self.copy_file('ticker-finance-history.json', dest='cache') + self.copy_file('ticker-btc.json', dest='cache') return 'ok' def ticker( @@ -186,7 +193,7 @@ class CmdTestScripts(CmdTestBase): ]) def ticker5(self): - shutil.copy2(os.path.join(refdir,'ticker-cfg.yaml'),self.nt_datadir) + self.copy_file('ticker-cfg.yaml') t = self.ticker( ['--wide','--adjust=-0.766'], [ @@ -195,7 +202,7 @@ class CmdTestScripts(CmdTestBase): r'LITECOIN 58.56 0.00251869 \+12.79 \+0.40 2022-08-02 18:25:59', r'MONERO 157.76 0.00678495 \+7.28 \+1.21' ]) - os.unlink(os.path.join(self.nt_datadir,'ticker-cfg.yaml')) + self.rm_file('ticker-cfg.yaml') return t def ticker6(self): @@ -204,7 +211,7 @@ class CmdTestScripts(CmdTestBase): return t def ticker7(self): # demo - shutil.copy2(os.path.join(refdir,'ticker-portfolio.yaml'),self.nt_datadir) + self.copy_file('ticker-portfolio.yaml') t = self.ticker( ['--wide','--portfolio'], [ @@ -213,7 +220,7 @@ class CmdTestScripts(CmdTestBase): 'CARDANO','ALGORAND', 'PORTFOLIO','BITCOIN','ETHEREUM','MONERO','CARDANO','ALGORAND','TOTAL' ]) - os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) + self.rm_file('ticker-portfolio.yaml') return t def ticker8(self): @@ -225,9 +232,7 @@ class CmdTestScripts(CmdTestBase): ]) def ticker9(self): - shutil.copy2( - os.path.join(refdir,'ticker-portfolio-bad.yaml'), - os.path.join(self.nt_datadir,'ticker-portfolio.yaml') ) + self.copy_file('ticker-portfolio-bad.yaml', 'ticker-portfolio.yaml') t = self.ticker( ['--wide','--portfolio','--elapsed','--add-rows=fake-fakecoin:0.0123','--add-precision=2'], [ @@ -236,7 +241,7 @@ class CmdTestScripts(CmdTestBase): r'FAKECOIN 81.3008 0.0034966927 -- -- --', r'\(no data for noc-nocoin\)', ]) - os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) + self.rm_file('ticker-portfolio.yaml') return t def ticker10(self): @@ -287,7 +292,7 @@ class CmdTestScripts(CmdTestBase): ]) def ticker14(self): - shutil.copy2(os.path.join(refdir,'ticker-portfolio.yaml'),self.nt_datadir) + self.copy_file('ticker-portfolio.yaml') t = self.ticker( ['--btc','--wide','--portfolio','--elapsed'], [ @@ -296,7 +301,7 @@ class CmdTestScripts(CmdTestBase): 'PORTFOLIO', r'BITCOIN 28,850.44 \+6.05 -1.87 1.23456789' ]) - os.unlink(os.path.join(self.nt_datadir,'ticker-portfolio.yaml')) + self.rm_file('ticker-portfolio.yaml') return t def ticker15(self): From 8f9c4ba48ccf695b48d718c6d95a8b717ef85a4c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 17 Oct 2025 09:08:00 +0000 Subject: [PATCH 168/175] mmnode-ticker --sort: handle missing entries in source data --- mmgen_node_tools/Ticker.py | 19 ++++++++++--------- mmgen_node_tools/data/version | 2 +- test/cmdtest_d/misc.py | 5 ++++- test/ref/ticker/ticker-cfg-bad.yaml | 11 +++++++++++ 4 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 test/ref/ticker/ticker-cfg-bad.yaml diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index d3045d4..925a4f9 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -41,14 +41,14 @@ percent_cols = { 'm': 'month', 'y': 'year'} -sp = namedtuple('sort_parameter', ['key', 'desc']) +sp = namedtuple('sort_parameter', ['key', 'sort_dfl', 'desc']) sort_params = { - 'd': sp('percent_change_24h', '1-day % change'), - 'w': sp('percent_change_7d', '1-week % change'), - 'm': sp('percent_change_30d', '1-month % change'), - 'y': sp('percent_change_1y', '1-year % change'), - 'p': sp('price_usd', 'asset price'), - 'c': sp('market_cap', 'market cap')} + 'd': sp('percent_change_24h', 0.0, '1-day % change'), + 'w': sp('percent_change_7d', 0.0, '1-week % change'), + 'm': sp('percent_change_30d', 0.0, '1-month % change'), + 'y': sp('percent_change_1y', 0.0, '1-year % change'), + 'p': sp('price_usd', Decimal(0), 'asset price'), + 'c': sp('market_cap', 0, 'market cap')} class RowDict(dict): @@ -915,8 +915,9 @@ class Ticker: if cfg.sort: code, reverse = cfg.sort key = sort_params[code].key - sort_func = lambda row: data[row.id][key] - pf_sort_func = lambda row: data[row[0]][key] + sort_dfl = sort_params[code].sort_dfl + sort_func = lambda row: data.get(row.id, {key: sort_dfl})[key] + pf_sort_func = lambda row: data.get(row[0], {key: sort_dfl})[key] for group in self.rows.keys(): if group not in self.hidden_groups: self.rows[group] = sorted(self.rows[group], key=sort_func, reverse=reverse) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 1de14a5..6eb528d 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev7 +3.6.dev8 diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 9060731..a3edd34 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -380,10 +380,13 @@ class CmdTestScripts(CmdTestBase): add_opts = ['--add-columns=eurusd=x']) def ticker22(self): - return self.ticker( + self.copy_file('ticker-cfg-bad.yaml', 'ticker-cfg.yaml') + t = self.ticker( [], ['MONERO', 'ETHEREUM', 'BITCOIN', 'SILVER', 'BRENT', 'GOLD'], add_opts = ['--name-labels', '--sort=rp']) + self.rm_file('ticker-cfg.yaml') + return t def ticker23(self): return self.ticker( diff --git a/test/ref/ticker/ticker-cfg-bad.yaml b/test/ref/ticker/ticker-cfg-bad.yaml new file mode 100644 index 0000000..da670c6 --- /dev/null +++ b/test/ref/ticker/ticker-cfg-bad.yaml @@ -0,0 +1,11 @@ +assets: + coin1: + - btc-bitcoin + - ltc-litecoin + - eth-ethereum + - xmr-monero + - bad-badcoin + commodity: + - gc=f + - si=f + - bz=f From 2647fa1fe3d511bec6ca841101c8e1472f479551 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 20 Oct 2025 09:14:30 +0000 Subject: [PATCH 169/175] mmnode-ticker: minor cleanups --- mmgen_node_tools/Ticker.py | 30 ++++++++++++++++-------------- mmgen_node_tools/main_ticker.py | 4 ++-- test/cmdtest_d/misc.py | 2 ++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 925a4f9..7bcb567 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -43,10 +43,10 @@ percent_cols = { sp = namedtuple('sort_parameter', ['key', 'sort_dfl', 'desc']) sort_params = { - 'd': sp('percent_change_24h', 0.0, '1-day % change'), - 'w': sp('percent_change_7d', 0.0, '1-week % change'), - 'm': sp('percent_change_30d', 0.0, '1-month % change'), - 'y': sp('percent_change_1y', 0.0, '1-year % change'), + 'd': sp('percent_change_24h', 0.0, '1-day percent change'), + 'w': sp('percent_change_7d', 0.0, '1-week percent change'), + 'm': sp('percent_change_30d', 0.0, '1-month percent change'), + 'y': sp('percent_change_1y', 0.0, '1-year percent change'), 'p': sp('price_usd', Decimal(0), 'asset price'), 'c': sp('market_cap', 0, 'market cap')} @@ -1025,8 +1025,8 @@ class Ticker: if cfg.asset_range: yield from process_rows(self.rows['asset_list']) else: - for rows in self.rows.values(): - if rows: + for group, rows in self.rows.items(): + if rows and group not in self.hidden_groups: yield from process_rows(rows) yield '-' * self.hl_wid @@ -1082,8 +1082,10 @@ class Ticker: def fmt_row(self, d, amt=None, amt_fmt=None): - def fmt_pct(n): - return gray(' --') if n is None else (red, green)[n>=0](f'{n:+7.2f}') + def fmt_pct(d, key, wid=7): + if (n := d.get(key)) is None: + return gray(' --') + return (red, green)[n>=0](f'{n:+{wid}.2f}') p = self.prices[d['id']] @@ -1096,10 +1098,10 @@ class Ticker: idx = int(d['rank']) if cfg.asset_range else None, mcap = d.get('market_cap') / 1_000_000_000 if cfg.asset_range else None, lbl = self.create_label(d['id']) if cfg.name_labels else d['symbol'], - pc1 = fmt_pct(d.get('percent_change_7d')), - pc2 = fmt_pct(d.get('percent_change_24h')), - pc3 = fmt_pct(d.get('percent_change_1y')), - pc4 = fmt_pct(d.get('percent_change_30d')), + pc1 = fmt_pct(d, 'percent_change_7d'), + pc2 = fmt_pct(d, 'percent_change_24h'), + pc3 = fmt_pct(d, 'percent_change_1y', wid=8), + pc4 = fmt_pct(d, 'percent_change_30d'), upd = d.get('last_updated_fmt'), amt = amt_fmt, **{k.replace('-', '_'): v * (1 if amt is None else amt) for k, v in p.items()}) @@ -1117,7 +1119,7 @@ class Ticker: col_fs_data = { 'label': fd(f'{{lbl:{self.col1_wid}}}', f'{{lbl:{self.col1_wid}}}', self.col1_wid), - 'pct1y': fd(' {pc3:7}', ' {pc3:7}', 8), + 'pct1y': fd(' {pc3:8}', ' {pc3:8}', 9), 'pct1m': fd(' {pc4:7}', ' {pc4:7}', 8), 'pct1w': fd(' {pc1:7}', ' {pc1:7}', 8), 'pct1d': fd(' {pc2:7}', ' {pc2:7}', 8), @@ -1169,7 +1171,7 @@ class Ticker: mcap = 'MarketCap(B)', pc1 = ' CHG_7d', pc2 = 'CHG_24h', - pc3 = 'CHG_1y', + pc3 = ' CHG_1y', pc4 = 'CHG_30d', upd = 'UPDATED', amt = ' AMOUNT', diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index 7be5e90..d4c0055 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -40,10 +40,10 @@ opts_data = { used to supply a USD exchange rate for missing assets. -C, --cached-data Use cached data from previous network query instead of live data from server --D, --cachedir=D Read and write cached JSON data to directory ‘D’ - instead of ‘~/{dfl_cachedir}’ -d, --download=D Retrieve and cache asset data ‘D’ from network (valid options: {ds}) +-D, --cachedir=D Read and write cached JSON data to directory ‘D’ + instead of ‘~/{dfl_cachedir}’ -e, --add-precision=N Add ‘N’ digits of precision to columns -E, --elapsed Show elapsed time in UPDATED column (see --update-time) -F, --portfolio Display portfolio data diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index a3edd34..c2a4188 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -133,6 +133,7 @@ class CmdTestScripts(CmdTestBase): cached_data = True, add_opts = [], use_proxy = True, + no_msg = False, exit_val = None): t = self.spawn( 'mmnode-ticker', @@ -141,6 +142,7 @@ class CmdTestScripts(CmdTestBase): + (['--proxy=http://asdfzxcv:32459'] if use_proxy else []) + add_opts + args, + no_msg = no_msg, exit_val = exit_val) if expect_list: t.match_expect_list(expect_list) From 84e8ea65d0b86cab2896125e1f1022d4848742a8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 20 Oct 2025 09:14:34 +0000 Subject: [PATCH 170/175] mmnode-ticker: display percent change columns in terms of non-USD assets Any crypto or finance asset may be specified. Examples: # Display percentage changes in relation to Bitcoin: $ mmnode-ticker --widest --pchg-unit=btc # In relation to Gold: $ mmnode-ticker --widest --pchg-unit=gc=f # In relation to Euros: $ mmnode-ticker --widest --pchg-unit=eurusd=x # In relation to the Nasdaq Index: $ mmnode-ticker --widest --pchg-unit=^ixic --- mmgen_node_tools/Ticker.py | 29 +++++++++++++++-- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 2 ++ test/cmdtest_d/misc.py | 38 +++++++++++++++++++++++ test/ref/ticker/ticker-cfg-sort-pchg.yaml | 14 +++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 test/ref/ticker/ticker-cfg-sort-pchg.yaml diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 7bcb567..4b49b44 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -535,6 +535,10 @@ def gen_data(data): 'name': 'US Dollar', 'price_usd': Decimal(1), 'price_btc': Decimal(1) / btcusd, + 'percent_change_24h': 0.0, + 'percent_change_7d': 0.0, + 'percent_change_30d': 0.0, + 'percent_change_1y': 0.0, 'last_updated': None}) def cache_data(data_src, no_overwrite=False): @@ -742,7 +746,8 @@ def make_cfg(gcfg_arg): {k: tuple(parse_asset_id(e) for e in v) for k, v in cfg_in.cfg['assets'].items()}) for hdr, data in ( ('user_uniq', get_usr_assets()), - ('portfolio_uniq', get_portfolio_assets())): + ('portfolio_uniq', get_portfolio_assets()), + ('pchg_unit_uniq', [pchg_unit] if pchg_unit else None)): if data: if uniq_data := tuple(gen_uniq(data, 'symbol', preload=rows)): rows[hdr] = uniq_data @@ -791,6 +796,7 @@ def make_cfg(gcfg_arg): 'portfolio', 'sort', 'percent_cols', + 'pchg_unit', 'asset_limit', 'cached_data', 'elapsed', @@ -833,6 +839,9 @@ def make_cfg(gcfg_arg): if portfolio and asset_range: die(1, '--portfolio not supported in market cap view') + pchg_unit = (lambda s: parse_asset_id(s, require_label=False) if s else None)( + get_cfg_var('pchg_unit')) + cfg = cfg_tuple( rows = create_rows(), usr_rows = usr_rows, @@ -849,6 +858,7 @@ def make_cfg(gcfg_arg): portfolio = portfolio, sort = get_sort_opt(), percent_cols = parse_percent_cols(get_cfg_var('percent_cols')), + pchg_unit = pchg_unit, asset_limit = get_cfg_var('asset_limit'), cached_data = get_cfg_var('cached_data'), elapsed = get_cfg_var('elapsed'), @@ -890,7 +900,7 @@ class Ticker: offer = None to_asset = None - hidden_groups = ('extra',) + hidden_groups = ('extra', 'pchg_unit_uniq') def __init__(self, data): @@ -925,6 +935,14 @@ class Ticker: cfg = cfg._replace( portfolio = sorted(cfg.portfolio, key=pf_sort_func, reverse=reverse)) + if cfg.pchg_unit: + self.pchg_data = self.data[self.get_id(cfg.pchg_unit)] + self.pchg_factors = {k: (self.pchg_data[k] / 100) + 1 for k in ( + 'percent_change_24h', + 'percent_change_7d', + 'percent_change_30d', + 'percent_change_1y')} + self.col_usd_prices = {k: self.data[k]['price_usd'] for k in self.col_ids} self.prices = {row.id: self.get_row_prices(row.id) for row in self.rows if row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') @@ -995,6 +1013,11 @@ class Ticker: text = sort_params[cfg.sort[0]].desc + ('' if cfg.sort[1] else ' [reversed]') yield f'Sort order: {pink(text.upper())}' + if cfg.pchg_unit: + yield 'Percent change unit: {}'.format(orange('{} ({})'.format( + self.pchg_data['symbol'], + self.pchg_data['name'].upper()))) + for asset in self.usr_col_assets: if asset.symbol != 'USD': usdprice = self.data[asset.id]['price_usd'] @@ -1085,6 +1108,8 @@ class Ticker: def fmt_pct(d, key, wid=7): if (n := d.get(key)) is None: return gray(' --') + if cfg.pchg_unit: + n = ((((n / 100) + 1) / self.pchg_factors[key]) - 1) * 100 return (red, green)[n>=0](f'{n:+{wid}.2f}') p = self.prices[d['id']] diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 6eb528d..47203f7 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev8 +3.6.dev9 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index d4c0055..cfca2ac 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -63,6 +63,8 @@ opts_data = { -t, --testing Print command(s) to be executed to stdout and exit -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column +-U, --pchg-unit=A Use asset ‘A’ as unit of reference for percentage + change columns (default: USD) -v, --verbose Be more verbose -w, --wide Display most optional columns (same as -unT -p d,w) -W, --widest Display all optional columns (same as -unT -p d,w,m,y) diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index c2a4188..9112f48 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -98,6 +98,9 @@ class CmdTestScripts(CmdTestBase): ('ticker27', 'ticker [--sort=rp -r algo,ada]'), ('ticker28', 'ticker [--sort=d -r algo,ada]'), ('ticker29', 'ticker [--sort=y -r algo,ada]'), + ('ticker30', 'ticker [--cached-data --wide --pchg-unit=btc --sort=d] (cf with config file)'), + ('ticker31', 'ticker [--cached-data --wide --pchg-unit=usd] (cf with no USD)'), + ('ticker32', 'ticker [--cached-data --wide --pchg-unit=gc=f]'), ) } @@ -438,3 +441,38 @@ class CmdTestScripts(CmdTestBase): [], ['ETHEREUM', 'BITCOIN', 'MONERO', 'S&P', 'DOW', 'NASDAQ', 'CARDANO', 'ALGORAND'], add_opts = ['--widest', '-s', 'y', '-r', 'ada,algo']) + + def ticker30(self): + self.copy_file('ticker-cfg-sort-pchg.yaml', 'ticker-cfg.yaml') + t = self.ticker(add_opts=['--wide']) + chk1 = '\n'.join(t.read().splitlines()[5:-2]) + self.rm_file('ticker-cfg.yaml') + + self.copy_file('ticker-cfg-bad.yaml', 'ticker-cfg.yaml') + t = self.ticker(add_opts=['--wide', '--pchg-unit=btc', '--sort=d'], no_msg=True) + chk2 = '\n'.join(t.read().splitlines()[5:-2]) + self.rm_file('ticker-cfg.yaml') + + assert chk1 == chk2, f'\nOUTPUT 1\n{chk1}\n!= OUTPUT 2\n{chk2}\n' + return t + + def ticker31(self): + t = self.ticker(add_opts=['--wide']) + chk1 = '\n'.join(t.read().splitlines()[5:-2]) + + t = self.ticker(add_opts=['--wide', '--pchg-unit=usd'], no_msg=True) + chk2 = '\n'.join(t.read().splitlines()[6:-2]) + + assert chk1 == chk2, f'\nOUTPUT 1\n{chk1}\n!= OUTPUT 2\n{chk2}\n' + return t + + def ticker32(self): + return self.ticker( + [], + [ + 'BITCOIN', r'\+10.99', r'\+7.06', '-1.18', r'\+1.05', + 'ETHEREUM', + 'GOLD', r'\+0.00', r'\+0.00', r'\+0.00', r'\+0.00', + 'SILVER' + ], + add_opts = ['--widest', '--pchg-unit=gc=f']) diff --git a/test/ref/ticker/ticker-cfg-sort-pchg.yaml b/test/ref/ticker/ticker-cfg-sort-pchg.yaml new file mode 100644 index 0000000..4660ae4 --- /dev/null +++ b/test/ref/ticker/ticker-cfg-sort-pchg.yaml @@ -0,0 +1,14 @@ +sort: d +pchg_unit: btc + +assets: + coin1: + - btc-bitcoin + - ltc-litecoin + - eth-ethereum + - xmr-monero + - bad-badcoin + commodity: + - gc=f + - si=f + - bz=f From d02c9936ef7504e70b07e82401042ae5fc58901a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 22 Oct 2025 10:33:16 +0000 Subject: [PATCH 171/175] mmnode-ticker: fix market cap sort with USD row --- mmgen_node_tools/Ticker.py | 1 + mmgen_node_tools/data/version | 2 +- test/cmdtest_d/misc.py | 26 +++++++++++++++++++++++++- test/ref/ticker/ticker-cfg-usd.yaml | 11 +++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 test/ref/ticker/ticker-cfg-usd.yaml diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 4b49b44..15e0e9c 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -539,6 +539,7 @@ def gen_data(data): 'percent_change_7d': 0.0, 'percent_change_30d': 0.0, 'percent_change_1y': 0.0, + 'market_cap': 0, 'last_updated': None}) def cache_data(data_src, no_overwrite=False): diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 47203f7..bd10d35 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev9 +3.6.dev10 diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 9112f48..d5cb51d 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -101,6 +101,9 @@ class CmdTestScripts(CmdTestBase): ('ticker30', 'ticker [--cached-data --wide --pchg-unit=btc --sort=d] (cf with config file)'), ('ticker31', 'ticker [--cached-data --wide --pchg-unit=usd] (cf with no USD)'), ('ticker32', 'ticker [--cached-data --wide --pchg-unit=gc=f]'), + ('ticker33', 'ticker [--cached-data --wide --pchg-unit=btc --sort=c] (cfg file with USD)'), + ('ticker34', 'ticker [--cached-data --wide --pchg-unit=btc --sort=y] (cfg file with USD)'), + ('ticker35', 'ticker [--cached-data --wide --pchg-unit=btc --sort=p] (cfg file with USD)'), ) } @@ -475,4 +478,25 @@ class CmdTestScripts(CmdTestBase): 'GOLD', r'\+0.00', r'\+0.00', r'\+0.00', r'\+0.00', 'SILVER' ], - add_opts = ['--widest', '--pchg-unit=gc=f']) + add_opts = ['--widest', '--pchg-unit=gc=f', '--sort=c']) + + def _ticker_cur(self, sort): + self.copy_file('ticker-cfg-usd.yaml', 'ticker-cfg.yaml') + t = self.ticker( + [], + [ + 'BITCOIN 23,250.77 1.00000000 \+0.00', + 'US DOLLAR 1.00 0.00004301 -15.93', + ], + add_opts = ['--widest', '--pchg-unit=btc', f'--sort={sort}']) + self.rm_file('ticker-cfg.yaml') + return t + + def ticker33(self): + return self._ticker_cur(sort='c') + + def ticker34(self): + return self._ticker_cur(sort='y') + + def ticker35(self): + return self._ticker_cur(sort='p') diff --git a/test/ref/ticker/ticker-cfg-usd.yaml b/test/ref/ticker/ticker-cfg-usd.yaml new file mode 100644 index 0000000..c31f85f --- /dev/null +++ b/test/ref/ticker/ticker-cfg-usd.yaml @@ -0,0 +1,11 @@ +assets: + coin1: + - btc-bitcoin + - eth-ethereum + - xmr-monero + commodity: + - gc=f + - si=f + currency: + - usd-us-dollar + - eurusd=x From f5c165c36201c0b251298fd08cef9566c42b37ee Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 8 Dec 2025 14:57:31 +0000 Subject: [PATCH 172/175] whitespace, minor cleanups and fixes --- mmgen_node_tools/BlocksInfo.py | 5 +- mmgen_node_tools/Ticker.py | 3 +- test/cmdtest_d/regtest.py | 110 +++++++++++++++++---------------- 3 files changed, 61 insertions(+), 57 deletions(-) diff --git a/mmgen_node_tools/BlocksInfo.py b/mmgen_node_tools/BlocksInfo.py index a59bf50..c068017 100755 --- a/mmgen_node_tools/BlocksInfo.py +++ b/mmgen_node_tools/BlocksInfo.py @@ -353,7 +353,8 @@ class BlocksInfo: nblocks, step = (add1, add2) if last is None else (None, add1) - if p.debug: msg(repr(self.range_data(first, last, from_tip, nblocks, step))) + if p.debug: + msg(repr(self.range_data(first, last, from_tip, nblocks, step))) if nblocks: if first is None: @@ -643,9 +644,7 @@ class BlocksInfo: values = {n: d.func(n) for n in fnames} col1_w = max((len(l) for l in lbls.values()), default=0) + 2 - print(d.spec_vals) for v in d.spec_vals: - print(v) if v.condition(values): try: idx = fnames.index(v.insert_after) + 1 except: idx = 0 diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 15e0e9c..50bb85b 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -970,8 +970,7 @@ class Ticker: t = int(d[row.id]['last_updated']) except TypeError as e: d[row.id]['last_updated_fmt'] = gray('--' if 'NoneType' in str(e) else str(e)) - except KeyError as e: - msg(str(e)) + except KeyError: pass else: t_fmt = d[row.id]['last_updated_fmt'] = fmt_func( diff --git a/test/cmdtest_d/regtest.py b/test/cmdtest_d/regtest.py index a504678..b35ec7a 100755 --- a/test/cmdtest_d/regtest.py +++ b/test/cmdtest_d/regtest.py @@ -23,18 +23,18 @@ from ..include.common import cfg, imsg, stop_test_daemons, joinpath from .base import CmdTestBase args1 = ['--bob'] -args2 = ['--bob','--rpc-backend=http'] +args2 = ['--bob', '--rpc-backend=http'] -def gen_addrs(proto,network,keys): +def gen_addrs(proto, network, keys): from mmgen.tool.api import tool_api tool = tool_api(cfg) - tool.init_coin(proto.coin,'regtest') + tool.init_coin(proto.coin, 'regtest') tool.addrtype = proto.mmtypes[-1] return [tool.privhex2addr('{:064x}'.format(key)) for key in keys] class CmdTestRegtest(CmdTestBase): 'various operations via regtest mode' - networks = ('btc','ltc','bch') + networks = ('btc', 'ltc', 'bch') passthru_opts = ('coin',) tmpdir_nums = [1] color = True @@ -110,31 +110,31 @@ class CmdTestRegtest(CmdTestBase): if trunner is None: return if cfg._proto.testnet: - die(2,'--testnet and --regtest options incompatible with regtest test suite') + die(2, '--testnet and --regtest options incompatible with regtest test suite') self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) - self.addrs = [a.views[a.view_pref] for a in gen_addrs(self.proto,'regtest',[1,2,3,4,5])] + self.addrs = [a.views[a.view_pref] for a in gen_addrs(self.proto, 'regtest', [1, 2, 3, 4, 5])] self.use_bdb_wallet = self.bdb_wallet or self.proto.coin != 'BTC' self.regtest = MMGenRegtest(cfg, self.proto.coin, bdb_wallet=self.use_bdb_wallet) def setup(self): - stop_test_daemons(self.proto.network_id,force=True,remove_datadir=True) + stop_test_daemons(self.proto.network_id, force=True, remove_datadir=True) from shutil import rmtree try: - rmtree(joinpath(self.tr.data_dir,'regtest')) + rmtree(joinpath(self.tr.data_dir, 'regtest')) except: pass t = self.spawn( 'mmgen-regtest', (['--bdb-wallet'] if self.use_bdb_wallet else []) + ['--setup-no-stop-daemon', 'setup']) - for s in ('Starting','Creating','Creating','Creating','Mined','Setup complete'): + for s in ('Starting', 'Creating', 'Creating', 'Creating', 'Mined', 'Setup complete'): t.expect(s) return t def netrate(self, add_args, expect_str, exit_val=None): t = self.spawn('mmnode-netrate', args1 + add_args, exit_val=exit_val) - t.expect(expect_str,regex=True) + t.expect(expect_str, regex=True) return t def netrate1(self): @@ -147,35 +147,35 @@ class CmdTestRegtest(CmdTestBase): return 'ok' return t - def halving_calculator(self,add_args,expect_list): - t = self.spawn('mmnode-halving-calculator',args1+add_args) + def halving_calculator(self, add_args, expect_list): + t = self.spawn('mmnode-halving-calculator', args1+add_args) t.match_expect_list(expect_list) return t def halving_calculator1(self): - return self.halving_calculator(['--help'],['USAGE:']) + return self.halving_calculator(['--help'], ['USAGE:']) def halving_calculator2(self): - return self.halving_calculator([],['Current block: 393',f'Current block subsidy: 12.5 {cfg.coin}']) + return self.halving_calculator([], ['Current block: 393', f'Current block subsidy: 12.5 {cfg.coin}']) def halving_calculator3(self): - return self.halving_calculator(['--list'],['33 4950','0']) + return self.halving_calculator(['--list'], ['33 4950', '0']) def halving_calculator4(self): - return self.halving_calculator(['--mined'],['0 0.0000015 14949.9999835']) + return self.halving_calculator(['--mined'], ['0 0.0000015 14949.9999835']) def halving_calculator5(self): - return self.halving_calculator(['--mined','--bdr-proj=5'],['5.00000 0 0.0000015 14949.9999835']) + return self.halving_calculator(['--mined', '--bdr-proj=5'], ['5.00000 0 0.0000015 14949.9999835']) def halving_calculator6(self): - return self.halving_calculator(['--mined','--sample-size=20'],['33 4950','0 0.0000015 14949.9999835']) + return self.halving_calculator(['--mined', '--sample-size=20'], ['33 4950', '0 0.0000015 14949.9999835']) - def sendto(self,addr,amt): - return self.spawn('mmgen-regtest',['send',addr,amt]) + def sendto(self, addr, amt): + return self.spawn('mmgen-regtest', ['send', addr, amt]) - def sendto1(self): return self.sendto(self.addrs[0],'0.123') - def sendto2(self): return self.sendto(self.addrs[0],'0.234') - def sendto3(self): return self.sendto(self.addrs[1],'0.345') + def sendto1(self): return self.sendto(self.addrs[0], '0.123') + def sendto2(self): return self.sendto(self.addrs[0], '0.234') + def sendto3(self): return self.sendto(self.addrs[1], '0.345') def addrbal(self, args, expect_list): t = self.spawn('mmnode-addrbal', args2 + args) @@ -231,7 +231,7 @@ class CmdTestRegtest(CmdTestBase): [ 'No balance', '2 unspent outputs in 2 blocks', - '394','0.123','395','0.234', + '394', '0.123', '395', '0.234', 'No balance' ]) @@ -240,7 +240,7 @@ class CmdTestRegtest(CmdTestBase): ['--tabular', self.addrs[4], self.addrs[0], self.addrs[3]], [ self.addrs[4] + ' - - -', - self.addrs[0] + ' 2 395','0.357', + self.addrs[0] + ' 2 395', '0.357', self.addrs[3] + ' - - -', ]) @@ -249,11 +249,11 @@ class CmdTestRegtest(CmdTestBase): ['--tabular', '--first-block', self.addrs[4], self.addrs[0], self.addrs[3]], [ self.addrs[4] + ' - - - -', - self.addrs[0] + ' 2 394','395','0.357', + self.addrs[0] + ' 2 394', '395', '0.357', self.addrs[3] + ' - - - -', ]) - def blocks_info(self,args,expect_list): + def blocks_info(self, args, expect_list): t = self.spawn('mmnode-blocks-info', args1 + args) t.match_expect_list(expect_list) return t @@ -261,7 +261,7 @@ class CmdTestRegtest(CmdTestBase): def blocks_info1(self): return self.blocks_info( ['--help'], - ['USAGE:','OPTIONS:']) + ['USAGE:', 'OPTIONS:']) def blocks_info2(self): return self.blocks_info( @@ -278,7 +278,7 @@ class CmdTestRegtest(CmdTestBase): ]) def blocks_info4(self): - n1,i1,o1,n2,i2,o2 = (2,1,3,6,3,9) if cfg.coin == 'BCH' else (2,1,4,6,3,12) + n1, i1, o1, n2, i2, o2 = (2, 1, 3, 6, 3, 9) if cfg.coin == 'BCH' else (2, 1, 4, 6, 3, 12) return self.blocks_info( ['--miner-info', '--fields=all', '--stats=all', '+3'], [ @@ -302,21 +302,21 @@ class CmdTestRegtest(CmdTestBase): from collections import namedtuple t = tool_api(cfg) - t.init_coin(self.proto.coin,self.proto.network) + t.init_coin(self.proto.coin, self.proto.network) t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32' - wp = namedtuple('wifaddrpair',['wif','addr']) + wp = namedtuple('wifaddrpair', ['wif', 'addr']) def gen(): - for n in range(0xfaceface,nPairs+0xfaceface): + for n in range(0xfaceface, nPairs+0xfaceface): wif = t.hex2wif(f'{n:064x}') yield wp( wif, t.wif2addr(wif) ) return list(gen()) - def gen_fees(n_in,low,high): + def gen_fees(n_in, low, high): # very approximate tx size estimation: - ibytes,wbytes,obytes = (148,0,34) if self.proto.coin == 'BCH' else (43,108,31) + ibytes, wbytes, obytes = (148, 0, 34) if self.proto.coin == 'BCH' else (43, 108, 31) x = (ibytes + (wbytes//4) + (obytes * nPairs)) * self.proto.coin_amt.satoshi n = n_in - 1 @@ -325,14 +325,20 @@ class CmdTestRegtest(CmdTestBase): for i in range(n_in): yield Decimal(low + (i/n)**6 * vmax) * x - async def do_tx(inputs,outputs,wif): - tx_hex = await r.rpc_call( 'createrawtransaction', inputs, outputs ) - tx = await r.rpc_call( 'signrawtransactionwithkey', tx_hex, [wif], [], self.proto.sighash_type ) + async def do_tx(inputs, outputs, wif): + tx_hex = await r.rpc_call('createrawtransaction', inputs, outputs) + if wif: + tx = await r.rpc_call( + 'signrawtransactionwithkey', + tx_hex, + [wif], + [], + self.proto.sighash_type) assert tx['complete'] return tx['hex'] async def do_tx1(): - us = await r.rpc_call('listunspent',wallet='miner') + us = await r.rpc_call('listunspent', wallet='miner') tx_input = us[7] # 25 BTC in coinbase -- us[0] could have < 25 BTC fee = self.proto.coin_amt('0.001') outputs = {p.addr: tx1_amt for p in pairs[:nTxs]} @@ -342,7 +348,7 @@ class CmdTestRegtest(CmdTestBase): outputs, await r.miner_wif) - async def do_tx2(tx,pairno): + async def do_tx2(tx, pairno): fee = self.proto.coin_amt(fees[pairno], from_decimal=True) outputs = {p.addr: tx2_amt for p in pairs} outputs.update({burn_addr: tx1_amt - (tx2_amt*len(pairs)) - fee}) @@ -353,10 +359,10 @@ class CmdTestRegtest(CmdTestBase): async def do_txs(tx_in): for pairno in range(nTxs): - tx_hex = await do_tx2(tx_in,pairno) - await r.rpc_call('sendrawtransaction',tx_hex) + tx_hex = await do_tx2(tx_in, pairno) + await r.rpc_call('sendrawtransaction', tx_hex) - self.spawn('',msg_only=True) + self.spawn('', msg_only=True) r = self.regtest nPairs = 100 @@ -372,20 +378,20 @@ class CmdTestRegtest(CmdTestBase): tx1_hex = await do_tx1() imsg('Relaying funding transaction') - await r.rpc_call('sendrawtransaction',tx1_hex) + await r.rpc_call('sendrawtransaction', tx1_hex) imsg('Mining a block') - await r.generate(1,silent=True) + await r.generate(1, silent=True) imsg('Generating fees for mempool transactions') - fees = list(gen_fees(nTxs,2,120)) + fees = list(gen_fees(nTxs, 2, 120)) imsg(f'Creating and relaying {nTxs} mempool transactions with {nPairs} outputs each') - await do_txs(await r.rpc_call('decoderawtransaction',tx1_hex)) + await do_txs(await r.rpc_call('decoderawtransaction', tx1_hex)) return 'ok' - def _feeview(self,args,expect_list=[]): + def _feeview(self, args, expect_list=[]): t = self.spawn('mmnode-feeview', args1 + args) if expect_list: t.match_expect_list(expect_list) @@ -395,7 +401,7 @@ class CmdTestRegtest(CmdTestBase): return self._feeview([]) def feeview2(self): - return self._feeview(['--columns=40','--include-current']) + return self._feeview(['--columns=40', '--include-current']) def feeview3(self): return self._feeview(['--precision=6']) @@ -404,7 +410,7 @@ class CmdTestRegtest(CmdTestBase): return self._feeview(['--detail']) def feeview5(self): - return self._feeview(['--show-empty','--log',f'--outdir={self.tmpdir}']) + return self._feeview(['--show-empty', '--log', f'--outdir={self.tmpdir}']) def feeview6(self): return self._feeview(['--ignore-below=1MB']) @@ -414,13 +420,13 @@ class CmdTestRegtest(CmdTestBase): async def feeview8(self): imsg('Clearing mempool') - await self.regtest.generate(1,silent=True) + await self.regtest.generate(1, silent=True) return self._feeview([]) def stop(self): if cfg.no_daemon_stop: - self.spawn('',msg_only=True) + self.spawn('', msg_only=True) msg_r('(leaving daemon running by user request)') return 'ok' else: - return self.spawn('mmgen-regtest',['stop']) + return self.spawn('mmgen-regtest', ['stop']) From e5bc33a6d6267ec320ddf4ae3511fc4590e4ec1b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 8 Dec 2025 14:57:32 +0000 Subject: [PATCH 173/175] update for MMGen Wallet v16.1.dev21 --- mmgen_node_tools/main_addrbal.py | 4 ++-- setup.cfg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mmgen_node_tools/main_addrbal.py b/mmgen_node_tools/main_addrbal.py index 4647029..743b36e 100755 --- a/mmgen_node_tools/main_addrbal.py +++ b/mmgen_node_tools/main_addrbal.py @@ -44,7 +44,7 @@ def do_output(proto, addr_data, blk_hdrs): heights = {u['height'] for u in unspents} Msg('{}Balance: {}'.format( indent, - sum(proto.coin_amt(u['amount']) for u in unspents).hl2(unit=True, fs='{:,}'))), + sum(proto.coin_amt(u['amount']) for u in unspents).hl3(unit=True, fs='{:,}'))), Msg('{}{} unspent output{} in {} block{}'.format( indent, red(str(len(unspents))), @@ -148,7 +148,7 @@ async def main(req_addrs): good_addrs = len([v for v in addr_data.values() if v]) Msg('Total: {} in {} address{}'.format( - proto.coin_amt(res['total_amount']).hl2(unit=True, fs='{:,}'), + proto.coin_amt(res['total_amount']).hl3(unit=True, fs='{:,}'), red(str(good_addrs)), suf(good_addrs, 'es'))) diff --git a/setup.cfg b/setup.cfg index d48a8c8..91b63b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ python_requires = >=3.11 include_package_data = True install_requires = - mmgen-wallet>=16.1.dev4 + mmgen-wallet>=16.1.dev21 pyyaml yahooquery From b97b4f5f634f2498947f4ea4b25f5ac71d08db85 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 8 Dec 2025 14:57:32 +0000 Subject: [PATCH 174/175] update for Bitcoin Core v30.0, nixpkgs 25.11 --- mmgen_node_tools/data/version | 2 +- nix/curl-cffi.nix | 50 ----------------------------------- nix/yahooquery.nix | 2 +- test/cmdtest_d/regtest.py | 9 ++++++- 4 files changed, 10 insertions(+), 53 deletions(-) delete mode 100644 nix/curl-cffi.nix diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index bd10d35..4e5cab0 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev10 +3.6.dev11 diff --git a/nix/curl-cffi.nix b/nix/curl-cffi.nix deleted file mode 100644 index 15f9764..0000000 --- a/nix/curl-cffi.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ - stdenv, - lib, - python, - buildPythonPackage, - fetchPypi, - curl-impersonate-chrome, -}: - -buildPythonPackage rec { - pname = "curl-cffi"; - # version = "0.13.0"; # uses option PROXY_CREDENTIAL_NO_REUSE, unavailable in current libcurl - version = "0.10.0"; - pyproject = true; - - src = fetchPypi { - pname = "curl_cffi"; - version = version; - # hash = "sha256-YuzZCjgr1QI3UONgbgqnyxo6i6QcFCcLjl4Unr9yxco="; # 0.13.0 - hash = "sha256-PjezUmjKWEkvVO0CCuS1DDPuDeutQUXbn3RvBO1GbrA="; # 0.10.0 - }; - - patches = [ ./use-system-libs.patch ]; - - buildInputs = [ curl-impersonate-chrome ]; - - build-system = with python.pkgs; [ - cffi - setuptools - ]; - - dependencies = with python.pkgs; [ - cffi - certifi - typing-extensions - ]; - - env = lib.optionalAttrs stdenv.cc.isGNU { - NIX_CFLAGS_COMPILE = "-Wno-error=incompatible-pointer-types"; - }; - - pythonImportsCheck = [ "curl_cffi" ]; - - meta = with lib; { - description = "Python binding for curl-impersonate via cffi"; - homepage = "https://curl-cffi.readthedocs.io"; - license = licenses.mit; - maintainers = with maintainers; [ chuangzhu ]; - }; -} diff --git a/nix/yahooquery.nix b/nix/yahooquery.nix index 3e8925f..ecb2494 100644 --- a/nix/yahooquery.nix +++ b/nix/yahooquery.nix @@ -19,7 +19,7 @@ buildPythonPackage rec { build-system = with python.pkgs; [ hatchling ]; propagatedBuildInputs = with python.pkgs; [ - (callPackage ./curl-cffi.nix {}) # >=0.10.0 + curl-cffi pandas requests-futures tqdm diff --git a/test/cmdtest_d/regtest.py b/test/cmdtest_d/regtest.py index b35ec7a..387594b 100755 --- a/test/cmdtest_d/regtest.py +++ b/test/cmdtest_d/regtest.py @@ -114,7 +114,7 @@ class CmdTestRegtest(CmdTestBase): self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) self.addrs = [a.views[a.view_pref] for a in gen_addrs(self.proto, 'regtest', [1, 2, 3, 4, 5])] - self.use_bdb_wallet = self.bdb_wallet or self.proto.coin != 'BTC' + self.use_bdb_wallet = self.bdb_wallet and self.proto.coin != 'BTC' self.regtest = MMGenRegtest(cfg, self.proto.coin, bdb_wallet=self.use_bdb_wallet) def setup(self): @@ -334,6 +334,13 @@ class CmdTestRegtest(CmdTestBase): [wif], [], self.proto.sighash_type) + else: + tx = await r.rpc_call( + 'signrawtransactionwithwallet', + tx_hex, + None, # prevtxs + self.proto.sighash_type, + wallet = 'miner') assert tx['complete'] return tx['hex'] From f88b70c2e2413d6261dcc3d5d5d930ec778d9eff Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 27 Jan 2026 08:46:15 +0000 Subject: [PATCH 175/175] mmnode-ticker: support nested assets in `ticker-portfolio.yaml` --- mmgen_node_tools/Ticker.py | 39 ++++++++++++++------- mmgen_node_tools/data/ticker-portfolio.yaml | 10 +++++- mmgen_node_tools/data/version | 2 +- setup.cfg | 2 +- test/ref/ticker/ticker-portfolio.yaml | 9 +++-- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index 50bb85b..a0adb39 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -720,15 +720,28 @@ def make_cfg(gcfg_arg): + usr_columns) def get_portfolio_assets(): - if cfg_in.portfolio and gcfg.portfolio: - ret = (parse_asset_id(e) for e in cfg_in.portfolio) + if portfolio: + ret = (parse_asset_id(e) for e in portfolio) return tuple(e for e in ret if (not gcfg.btc) or e.symbol == 'BTC') else: return () - def get_portfolio(): - return tuple((k, Decimal(v)) for k, v in cfg_in.portfolio.items() - if (not gcfg.btc) or k == 'btc-bitcoin') + def parse_portfolio(): + ret = {} + def add(k, v): + if gcfg.btc and k != 'btc-bitcoin': + return + if k in ret: + ret[k] += Decimal(v) + else: + ret[k] = Decimal(v) + for k, v in cfg_in.portfolio.items(): + if isinstance(v, dict): + for k2, v2 in v.items(): + add(k2, v2) + else: + add(k, v) + return ret def parse_add_precision(arg): if not arg: @@ -834,7 +847,7 @@ def make_cfg(gcfg_arg): proxy2 = get_proxy('proxy2') portfolio = ( - get_portfolio() if cfg_in.portfolio and get_cfg_var('portfolio') and not query + parse_portfolio() if cfg_in.portfolio and get_cfg_var('portfolio') and not query else None) if portfolio and asset_range: @@ -928,13 +941,14 @@ class Ticker: key = sort_params[code].key sort_dfl = sort_params[code].sort_dfl sort_func = lambda row: data.get(row.id, {key: sort_dfl})[key] - pf_sort_func = lambda row: data.get(row[0], {key: sort_dfl})[key] + pf_sort_func = lambda row: data.get(row, {key: sort_dfl})[key] for group in self.rows.keys(): if group not in self.hidden_groups: self.rows[group] = sorted(self.rows[group], key=sort_func, reverse=reverse) if cfg.portfolio: - cfg = cfg._replace( - portfolio = sorted(cfg.portfolio, key=pf_sort_func, reverse=reverse)) + cfg = cfg._replace(portfolio = + {k: cfg.portfolio[k] + for k in sorted(cfg.portfolio, key=pf_sort_func, reverse=reverse)}) if cfg.pchg_unit: self.pchg_data = self.data[self.get_id(cfg.pchg_unit)] @@ -1061,7 +1075,7 @@ class Ticker: yield blue('PORTFOLIO') yield self.table_hdr yield '-' * self.hl_wid - for sym, amt in cfg.portfolio: + for sym, amt in cfg.portfolio.items(): try: yield self.fmt_row(self.data[sym], amt=amt) except KeyError: @@ -1086,10 +1100,9 @@ class Ticker: self.format_last_updated_col() if cfg.portfolio: - pf_dict = dict(cfg.portfolio) - self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * pf_dict[row.id] + self.prices['total'] = {col_id: sum(self.prices[row.id][col_id] * cfg.portfolio[row.id] for row in self.rows - if row.id in pf_dict and row.id in data) + if row.id in cfg.portfolio and row.id in data) for col_id in self.col_ids} self.init_prec() diff --git a/mmgen_node_tools/data/ticker-portfolio.yaml b/mmgen_node_tools/data/ticker-portfolio.yaml index fcadd30..b63e626 100644 --- a/mmgen_node_tools/data/ticker-portfolio.yaml +++ b/mmgen_node_tools/data/ticker-portfolio.yaml @@ -3,4 +3,12 @@ # Invoke `mmnode-ticker --list-ids` for a full list of supported asset IDs. btc-bitcoin: '1.23456789' eth-ethereum: '2.3456789012' -xmr-monero: '4.5678901234' +xmr-monero: '4.567890123456' + +# Nested values are supported. Values for each asset will be summed. +wallet2: + btc-bitcoin: '0.12345678' + ltc-litecoin: '1.23456789' + +exchange1: + xmr-monero: '12.345678901234' diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 4e5cab0..6b99850 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev11 +3.6.dev12 diff --git a/setup.cfg b/setup.cfg index 91b63b2..0753d4f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ python_requires = >=3.11 include_package_data = True install_requires = - mmgen-wallet>=16.1.dev21 + mmgen-wallet>=16.1.dev26 pyyaml yahooquery diff --git a/test/ref/ticker/ticker-portfolio.yaml b/test/ref/ticker/ticker-portfolio.yaml index 332257f..ca0c2b2 100644 --- a/test/ref/ticker/ticker-portfolio.yaml +++ b/test/ref/ticker/ticker-portfolio.yaml @@ -1,5 +1,10 @@ btc-bitcoin: '1.23456789' eth-ethereum: '2.345678901234567890' -xmr-monero: '4.567890123456' +xmr-monero: '3.333390123456' ada-cardano: '123.45678901' -algo-algorand: '234.5678901' + +wallet2: + algo-algorand: '234.5678901' + +exchange1: + xmr-monero: '1.2345'