From 5c9437cd7063e396d8afa14018c500cd0a9ec97c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 23 Oct 2020 17:13:21 +0000 Subject: [PATCH] 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