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())