From a4eee3ef4bc73bd95ee21c24cdc6aa2520f33048 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 19 Feb 2021 20:22:42 +0300 Subject: [PATCH] Daemon version checking - abort RPC initialization if version of running coin daemon is greater than latest tested version - version checking may be overridden with --ignore-daemon-version option --- mmgen/daemon.py | 76 +++++++++++++++++++++++++++++++----- mmgen/opts.py | 1 + mmgen/rpc.py | 28 ++++++++++++- mmgen/tool.py | 6 +++ test/test_py_d/ts_ethdev.py | 6 +++ test/test_py_d/ts_regtest.py | 6 +++ 6 files changed, 112 insertions(+), 11 deletions(-) diff --git a/mmgen/daemon.py b/mmgen/daemon.py index eaf26aed..5fdd927b 100755 --- a/mmgen/daemon.py +++ b/mmgen/daemon.py @@ -293,16 +293,74 @@ class CoinDaemon(Daemon): network_ids = ('btc','btc_tn','btc_rt','bch','bch_tn','bch_rt','ltc','ltc_tn','ltc_rt','xmr','eth','etc') - cd = namedtuple('daemon_data', - ['coin','cls_pfx','coind_exec','cli_exec','cfg_file','testnet_dir','dfl_rpc','dfl_rpc_tn','dfl_rpc_rt']) + cd = namedtuple('daemon_data', [ + 'coin', + 'cls_pfx', + 'coind_name', + 'coind_exec', + 'coind_version', 'coind_version_str', # latest tested version + 'cli_exec', + 'cfg_file', + 'testnet_dir', + 'dfl_rpc', + 'dfl_rpc_tn', + 'dfl_rpc_rt' ]) - daemon_ids = { # for BCH we use non-standard RPC ports -'btc': cd('Bitcoin', 'Bitcoin', 'bitcoind', 'bitcoin-cli', 'bitcoin.conf', 'testnet3',8332,18332,18444), -'bch': cd('BitcoinCashNode', 'Bitcoin', 'bitcoind-bchn','bitcoin-cli-bchn','bitcoin.conf','testnet3',8442,18442,18553), -'ltc': cd('Litecoin', 'Bitcoin', 'litecoind', 'litecoin-cli','litecoin.conf','testnet4',9332,19332,19444), -'xmr': cd('Monero', 'Monero', 'monerod', 'monerod', 'bitmonero.conf',None, 18081,None,None), -'eth': cd('Ethereum', 'Ethereum','openethereum','openethereum','parity.conf', None, 8545, 8545,8545), -'etc': cd('Ethereum Classic','Ethereum','openethereum','openethereum','parity.conf', None, 8545, 8545,8545) + daemon_ids = { + 'btc': cd( + 'Bitcoin', + 'Bitcoin', + 'Bitcoin Core', + 'bitcoind', 200100, '0.20.1', + 'bitcoin-cli', + 'bitcoin.conf', + 'testnet3', + 8332, 18332, 18444), + 'bch': cd( + 'BitcoinCashNode', + 'Bitcoin', + 'Bitcoin Cash Node', + 'bitcoind-bchn', 22020000, '22.2.0', + 'bitcoin-cli-bchn', + 'bitcoin.conf', + 'testnet3', + 8442, 18442, 18553), # for BCH we use non-standard RPC ports + 'ltc': cd( + 'Litecoin', + 'Bitcoin', + 'Litecoin Core', + 'litecoind', 180100, '0.18.1', + 'litecoin-cli', + 'litecoin.conf', + 'testnet4', + 9332, 19332, 19444), + 'xmr': cd( + 'Monero', + 'Monero', + 'Monero', + 'monerod', 'N/A', 'N/A', + 'monerod', + 'bitmonero.conf', + None, + 18081, None, None), + 'eth': cd( + 'Ethereum', + 'Ethereum', + 'OpenEthereum', + 'openethereum', 3000001, '3.0.1', + 'openethereum', + 'parity.conf', + None, + 8545, 8545, 8545), + 'etc': cd( + 'Ethereum Classic', + 'Ethereum', + 'OpenEthereum', + 'openethereum', 3000001, '3.0.1', + 'openethereum', + 'parity.conf', + None, + 8545, 8545, 8545) } def __new__(cls,network_id=None,test_suite=False,flags=None,proto=None): diff --git a/mmgen/opts.py b/mmgen/opts.py index b5d3a9d7..c94c0638 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -197,6 +197,7 @@ common_opts_data = { --, --force-256-color Force 256-color output when color is enabled --, --data-dir=path Specify {pnm} data directory location --, --daemon-data-dir=path Specify {dn} data directory location +--, --ignore-daemon-version Ignore {dn} version check --, --no-license Suppress the GPL license prompt --, --rpc-host=host Communicate with {dn} running on host 'host' --, --rpc-port=port Communicate with {dn} listening on port 'port' diff --git a/mmgen/rpc.py b/mmgen/rpc.py index b38cd38a..e0ea4e28 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -355,6 +355,7 @@ class BitcoinRPCClient(RPCClient,metaclass=aInitMeta): ('getblockhash',(0,)), )) self.daemon_version = self.cached['networkinfo']['version'] + self.daemon_version_str = self.cached['networkinfo']['subversion'] self.chain = self.cached['blockchaininfo']['chain'] tip = await self.call('getblockhash',self.blockcount) @@ -505,7 +506,12 @@ class EthereumRPCClient(RPCClient,metaclass=aInitMeta): ('parity_nodeKind',()), )) - self.daemon_version = vi['version'] + self.daemon_version = int(( + lambda v: '{:d}{:03d}{:03d}'.format(v['major'],v['minor'],v['patch']) + )(vi['version'])) + self.daemon_version_str = ( + lambda v: '{}.{}.{}'.format(v['major'],v['minor'],v['patch']) + )(vi['version']) self.cur_date = int(bh['timestamp'],16) self.chain = ch.replace(' ','_') self.caps = ('full_node',) if nk['capability'] == 'full' else () @@ -590,7 +596,7 @@ class MoneroWalletRPCClient(RPCClient): 'refresh', # start_height ) -async def rpc_init(proto,backend=None): +async def rpc_init(proto,backend=None,ignore_daemon_version=False): if not 'rpc' in proto.mmcaps: die(1,f'Coin daemon operations not supported for {proto.name} protocol!') @@ -604,6 +610,24 @@ async def rpc_init(proto,backend=None): daemon = CoinDaemon(proto=proto,test_suite=g.test_suite), backend = backend or opt.rpc_backend ) + if not (ignore_daemon_version or opt.ignore_daemon_version): + if rpc.daemon_version > rpc.daemon.coind_version: + rdie(1,fmt( + """ + The running {} daemon has version {}. + This version of MMGen is tested only on {} v{} and below. + + To avoid this error, downgrade your daemon to a supported version. + + Alternatively, you may invoke the command with the --ignore-daemon-version + option, in which case you proceed at your own risk. + """.format( + rpc.daemon.coind_name, + rpc.daemon_version_str, + rpc.daemon.coind_name, + rpc.daemon.coind_version_str, + ),indent=' ').rstrip()) + if proto.chain_name != rpc.chain: raise RPCChainMismatch( '{} protocol chain is {}, but coin daemon chain is {}'.format( diff --git a/mmgen/tool.py b/mmgen/tool.py index 05284403..e2fe15c6 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -901,6 +901,12 @@ from .tw import TwAddrList,TwUnspentOutputs class MMGenToolCmdRPC(MMGenToolCmds): "tracking wallet commands using the JSON-RPC interface" + async def daemon_version(self): + "print coin daemon version" + from .rpc import rpc_init + r = await rpc_init(self.proto,ignore_daemon_version=True) + return f'{r.daemon.coind_name} version {r.daemon_version} ({r.daemon_version_str})' + async def getbalance(self,minconf=1,quiet=False,pager=False): "list confirmed/unconfirmed, spendable/unspendable balances in tracking wallet" from .tw import TwGetBalance diff --git a/test/test_py_d/ts_ethdev.py b/test/test_py_d/ts_ethdev.py index 2d9df246..a0d11948 100755 --- a/test/test_py_d/ts_ethdev.py +++ b/test/test_py_d/ts_ethdev.py @@ -146,6 +146,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): color = True cmd_group = ( ('setup', 'OpenEthereum dev mode tests for coin {} (start openethereum)'.format(coin)), + ('daemon_version', 'mmgen-tool daemon_version'), ('wallet_upgrade1', 'upgrading the tracking wallet (v1 -> v2)'), ('wallet_upgrade2', 'upgrading the tracking wallet (v2 -> v3)'), ('addrgen', 'generating addresses'), @@ -349,6 +350,11 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): os.unlink(w_to) return t + def daemon_version(self): + t = self.spawn('mmgen-tool', self.eth_args + ['daemon_version']) + t.expect('version') + return t + def wallet_upgrade1(self): return self.wallet_upgrade('tracking-wallet-v1.json') def wallet_upgrade2(self): return self.wallet_upgrade('tracking-wallet-v2.json') diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index d1a81d3e..2a9261b9 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -143,6 +143,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): color = True cmd_group = ( ('setup', 'regtest (Bob and Alice) mode setup'), + ('daemon_version', 'mmgen-tool daemon_version'), ('current_user', 'current user'), ('halving_calculator_bob', 'halving calculator (Bob)'), ('walletgen_bob', 'wallet generation (Bob)'), @@ -279,6 +280,11 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): t.expect(s) return t + def daemon_version(self): + t = self.spawn('mmgen-tool', ['--bob','daemon_version']) + t.expect('version') + return t + def current_user(self): t = self.spawn('mmgen-regtest', ['user']) t.expect('Bob')