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')) } }