Browse Source

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
The MMGen Project 4 years ago
parent
commit
a4eee3e
6 changed files with 113 additions and 12 deletions
  1. 68 10
      mmgen/daemon.py
  2. 1 0
      mmgen/opts.py
  3. 26 2
      mmgen/rpc.py
  4. 6 0
      mmgen/tool.py
  5. 6 0
      test/test_py_d/ts_ethdev.py
  6. 6 0
      test/test_py_d/ts_regtest.py

+ 68 - 10
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'])
-
-	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)
+	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 = {
+		'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):

+ 1 - 0
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'

+ 26 - 2
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(

+ 6 - 0
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

+ 6 - 0
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')
 

+ 6 - 0
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')