Browse Source

support Rust Ethereum client (Reth)

Tested on Linux only

Testing:

    $ test/cmdtest.py --coin=eth --daemon-id=reth ethdev
The MMGen Project 2 days ago
parent
commit
1e422b2c2b

+ 1 - 0
MANIFEST.in

@@ -16,6 +16,7 @@ include test/*/*.py
 include test/ref/*
 include test/ref/*
 include test/ref/*/*
 include test/ref/*/*
 include test/ref/*/*/*/*
 include test/ref/*/*/*/*
+include test/ref/*/*/*/*/*
 include test/overlay/fakemods/mmgen/*.py
 include test/overlay/fakemods/mmgen/*.py
 include test/overlay/fakemods/mmgen/*/*.py
 include test/overlay/fakemods/mmgen/*/*.py
 include test/overlay/fakemods/mmgen/*/*/*/*.py
 include test/overlay/fakemods/mmgen/*/*/*/*.py

+ 1 - 1
mmgen/daemon.py

@@ -287,7 +287,7 @@ class CoinDaemon(Daemon):
 		'BCH': _cd(['bitcoin_cash_node']),
 		'BCH': _cd(['bitcoin_cash_node']),
 		'LTC': _cd(['litecoin_core']),
 		'LTC': _cd(['litecoin_core']),
 		'XMR': _cd(['monero']),
 		'XMR': _cd(['monero']),
-		'ETH': _cd(['geth', 'erigon', 'openethereum']),
+		'ETH': _cd(['geth', 'reth', 'erigon', 'openethereum']),
 		'ETC': _cd(['parity']),
 		'ETC': _cd(['parity']),
 	}
 	}
 
 

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-15.1.dev18
+15.1.dev19

+ 12 - 2
mmgen/proto/eth/daemon.py

@@ -113,19 +113,29 @@ class geth_daemon(ethereum_daemon):
 	def init_subclass(self):
 	def init_subclass(self):
 
 
 		self.coind_args = list_gen(
 		self.coind_args = list_gen(
-			['--verbosity=0'],
+			['node', self.id == 'reth'],
+			['--quiet', self.id == 'reth'],
+			['--verbosity=0', self.id == 'geth'],
 			['--ipcdisable'], # IPC-RPC: if path to socket is longer than 108 chars, geth fails to start
 			['--ipcdisable'], # IPC-RPC: if path to socket is longer than 108 chars, geth fails to start
 			['--http'],
 			['--http'],
 			['--http.api=eth,web3,txpool'],
 			['--http.api=eth,web3,txpool'],
 			[f'--http.port={self.rpc_port}'],
 			[f'--http.port={self.rpc_port}'],
 			[f'--authrpc.port={self.authrpc_port}'],
 			[f'--authrpc.port={self.authrpc_port}'],
 			[f'--port={self.p2p_port}', self.p2p_port], # geth binds p2p port even with --maxpeers=0
 			[f'--port={self.p2p_port}', self.p2p_port], # geth binds p2p port even with --maxpeers=0
-			['--maxpeers=0', not self.opt.online],
+			['--maxpeers=0', self.id == 'geth' and not self.opt.online],
 			[f'--datadir={self.datadir}', self.non_dfl_datadir],
 			[f'--datadir={self.datadir}', self.non_dfl_datadir],
 			['--holesky', self.network=='testnet'],
 			['--holesky', self.network=='testnet'],
 			['--dev', self.network=='regtest'],
 			['--dev', self.network=='regtest'],
 		)
 		)
 
 
+class reth_daemon(geth_daemon):
+	daemon_data = _dd('Reth', 1002002, '1.2.2')
+	version_pat = r'reth/v(\d+)\.(\d+)\.(\d+)'
+	exec_fn = 'reth'
+	datadirs = {
+		'linux': [gc.home_dir, '.local', 'share', 'reth'],
+	}
+
 # https://github.com/ledgerwatch/erigon
 # https://github.com/ledgerwatch/erigon
 class erigon_daemon(geth_daemon):
 class erigon_daemon(geth_daemon):
 	daemon_data = _dd('Erigon', 2022099099, '2022.99.99')
 	daemon_data = _dd('Erigon', 2022099099, '2022.99.99')

+ 2 - 0
mmgen/proto/eth/params.py

@@ -38,6 +38,7 @@ class mainnet(CoinProtocol.DummyWIF, CoinProtocol.Secp256k1):
 	avg_bdi       = 15
 	avg_bdi       = 15
 	decimal_prec  = 36
 	decimal_prec  = 36
 
 
+	# https://www.chainid.dev
 	chain_ids = {
 	chain_ids = {
 		1:    'ethereum',         # ethereum mainnet
 		1:    'ethereum',         # ethereum mainnet
 		2:    'morden',           # morden testnet (deprecated)
 		2:    'morden',           # morden testnet (deprecated)
@@ -50,6 +51,7 @@ class mainnet(CoinProtocol.DummyWIF, CoinProtocol.Secp256k1):
 		17:   'developmentchain', # parity dev chain
 		17:   'developmentchain', # parity dev chain
 		1337: 'developmentchain', # geth dev chain
 		1337: 'developmentchain', # geth dev chain
 		711:  'ethereum',         # geth mainnet (empty chain)
 		711:  'ethereum',         # geth mainnet (empty chain)
+		17000: 'holesky',         # proof-of-stake testnet
 	}
 	}
 
 
 	coin_cfg_opts = (
 	coin_cfg_opts = (

+ 5 - 1
mmgen/proto/eth/rpc.py

@@ -25,6 +25,10 @@ class daemon_warning(oneshot_warning_group):
 		color = 'yellow'
 		color = 'yellow'
 		message = 'Geth has not been tested on mainnet. You may experience problems.'
 		message = 'Geth has not been tested on mainnet. You may experience problems.'
 
 
+	class reth:
+		color = 'yellow'
+		message = 'Reth has not been tested on mainnet. You may experience problems.'
+
 	class erigon:
 	class erigon:
 		color = 'red'
 		color = 'red'
 		message = 'Erigon support is EXPERIMENTAL. Use at your own risk!!!'
 		message = 'Erigon support is EXPERIMENTAL. Use at your own risk!!!'
@@ -80,7 +84,7 @@ class EthereumRPCClient(RPCClient, metaclass=AsyncInit):
 				self.caps += ('full_node',)
 				self.caps += ('full_node',)
 			self.chainID = None if ci is None else Int(ci, 16) # parity/oe return chainID only for dev chain
 			self.chainID = None if ci is None else Int(ci, 16) # parity/oe return chainID only for dev chain
 			self.chain = (await self.call('parity_chain')).replace(' ', '_').replace('_testnet', '')
 			self.chain = (await self.call('parity_chain')).replace(' ', '_').replace('_testnet', '')
-		elif self.daemon.id in ('geth', 'erigon'):
+		elif self.daemon.id in ('geth', 'reth', 'erigon'):
 			if self.daemon.network == 'mainnet':
 			if self.daemon.network == 'mainnet':
 				daemon_warning(self.daemon.id)
 				daemon_warning(self.daemon.id)
 			self.caps += ('full_node',)
 			self.caps += ('full_node',)

+ 1 - 1
mmgen/proto/eth/tx/status.py

@@ -33,7 +33,7 @@ class Status(TxBase.Status):
 				return False
 				return False
 			if tx.rpc.daemon.id in ('parity', 'openethereum'):
 			if tx.rpc.daemon.id in ('parity', 'openethereum'):
 				pool = [x['hash'] for x in await tx.rpc.call('parity_pendingTransactions')]
 				pool = [x['hash'] for x in await tx.rpc.call('parity_pendingTransactions')]
-			elif tx.rpc.daemon.id in ('geth', 'erigon'):
+			elif tx.rpc.daemon.id in ('geth', 'reth', 'erigon'):
 				res = await tx.rpc.call('txpool_content')
 				res = await tx.rpc.call('txpool_content')
 				pool = list(res['pending']) + list(res['queued'])
 				pool = list(res['pending']) + list(res['queued'])
 			return '0x'+tx.coin_txid in pool
 			return '0x'+tx.coin_txid in pool

+ 41 - 6
test/cmdtest_d/ct_ethdev.py

@@ -20,7 +20,7 @@
 test.cmdtest_d.ct_ethdev: Ethdev tests for the cmdtest.py test suite
 test.cmdtest_d.ct_ethdev: Ethdev tests for the cmdtest.py test suite
 """
 """
 
 
-import sys, os, re, shutil, asyncio, json
+import sys, time, os, re, shutil, asyncio, json
 from decimal import Decimal
 from decimal import Decimal
 from collections import namedtuple
 from collections import namedtuple
 from subprocess import run, PIPE, DEVNULL
 from subprocess import run, PIPE, DEVNULL
@@ -66,6 +66,15 @@ dfl_sid = '98831F3A'
 dfl_devaddr = '00a329c0648769a73afac7f9381e08fb43dbea72'
 dfl_devaddr = '00a329c0648769a73afac7f9381e08fb43dbea72'
 dfl_devkey = '4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7'
 dfl_devkey = '4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7'
 
 
+def get_reth_dev_keypair():
+	from mmgen.bip39 import bip39
+	from mmgen.bip_hd import MasterNode
+	mn = 'test test test test test test test test test test test junk' # See ‘reth node --help’
+	seed = bip39().generate_seed(mn.split())
+	m = MasterNode(cfg, seed)
+	node = m.to_chain(idx=0, coin='eth').derive_private(0)
+	return (node.key.hex(), node.address)
+
 burn_addr  = 'deadbeef'*5
 burn_addr  = 'deadbeef'*5
 burn_addr2 = 'beadcafe'*5
 burn_addr2 = 'beadcafe'*5
 
 
@@ -85,6 +94,16 @@ def set_vbals(daemon_id):
 		vbal6 = '999904.14880104212345678'
 		vbal6 = '999904.14880104212345678'
 		vbal7 = '999902.91891764212345678'
 		vbal7 = '999902.91891764212345678'
 		vbal9 = '1.2262504'
 		vbal9 = '1.2262504'
+	elif daemon_id == 'reth':
+		vbal1 = '1.2288334'
+		vbal2 = '99.996560752'
+		vbal3 = '1.23142525'
+		vbal3 = '1.2314176'
+		vbal4 = '127.0287834'
+		vbal5 = '999904.14775104212345678'
+		vbal6 = '999904.14880104212345678'
+		vbal7 = '999902.91891764212345678'
+		vbal9 = '1.2262504'
 	else:
 	else:
 		vbal1 = '1.2288396'
 		vbal1 = '1.2288396'
 		vbal2 = '99.997088092'
 		vbal2 = '99.997088092'
@@ -409,6 +428,10 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		from mmgen.daemon import CoinDaemon
 		from mmgen.daemon import CoinDaemon
 		self.daemon = CoinDaemon( cfg, self.proto.coin+'_rt', test_suite=True)
 		self.daemon = CoinDaemon( cfg, self.proto.coin+'_rt', test_suite=True)
 
 
+		if self.daemon.id == 'reth':
+			global dfl_devkey, dfl_devaddr
+			dfl_devkey, dfl_devaddr = get_reth_dev_keypair()
+
 		set_vbals(self.daemon.id)
 		set_vbals(self.daemon.id)
 
 
 		self.using_solc = check_solc_ver()
 		self.using_solc = check_solc_ver()
@@ -432,16 +455,21 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		from mmgen.rpc import rpc_init
 		from mmgen.rpc import rpc_init
 		return await rpc_init(cfg, self.proto)
 		return await rpc_init(cfg, self.proto)
 
 
+	def mining_delay(self): # workaround for mining race condition in dev mode
+		if self.daemon.id == 'reth':
+			time.sleep(0.5)
+
 	async def setup(self):
 	async def setup(self):
 		self.spawn('', msg_only=True)
 		self.spawn('', msg_only=True)
 
 
 		d = self.daemon
 		d = self.daemon
 
 
 		if not self.using_solc:
 		if not self.using_solc:
-			srcdir = os.path.join(self.tr.repo_root, 'test', 'ref', 'ethereum', 'bin')
+			subdir = 'reth' if d.id == 'reth' else 'geth'
+			srcdir = os.path.join(self.tr.repo_root, 'test', 'ref', 'ethereum', 'bin', subdir)
 			from shutil import copytree
 			from shutil import copytree
-			for d in ('mm1', 'mm2'):
-				copytree(os.path.join(srcdir, d), os.path.join(self.tmpdir, d))
+			for _ in ('mm1', 'mm2'):
+				copytree(os.path.join(srcdir, _), os.path.join(self.tmpdir, _))
 
 
 		if d.id in ('geth', 'erigon'):
 		if d.id in ('geth', 'erigon'):
 			self.genesis_setup(d)
 			self.genesis_setup(d)
@@ -575,6 +603,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 			[f'--coin={self.proto.coin}', '--regtest=1', 'eth_getBalance', '0x'+dfl_devaddr, 'latest'])
 			[f'--coin={self.proto.coin}', '--regtest=1', 'eth_getBalance', '0x'+dfl_devaddr, 'latest'])
 		if self.daemon.id == 'geth':
 		if self.daemon.id == 'geth':
 			t.expect('0x33b2e3c91ec0e9113986000')
 			t.expect('0x33b2e3c91ec0e9113986000')
+		elif self.daemon.id == 'reth':
+			t.expect('0xd3c21bcecceda1000000')
 		return t
 		return t
 
 
 	async def _wallet_upgrade(self, src_fn, expect1, expect2=None):
 	async def _wallet_upgrade(self, src_fn, expect1, expect2=None):
@@ -763,6 +793,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		return self.bal(n='3')
 		return self.bal(n='3')
 
 
 	def tx_status(self, ext, expect_str, expect_str2='', add_args=[], exit_val=0):
 	def tx_status(self, ext, expect_str, expect_str2='', add_args=[], exit_val=0):
+		self.mining_delay()
 		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext, no_dot=True)
 		txfile = self.get_file_with_ext(ext, no_dot=True)
 		t = self.spawn(
 		t = self.spawn(
@@ -893,6 +924,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		return self.bal(n='5')
 		return self.bal(n='5')
 
 
 	def bal(self, n):
 	def bal(self, n):
+		self.mining_delay()
 		t = self.spawn('mmgen-tool', self.eth_args + ['twview', 'wide=1'])
 		t = self.spawn('mmgen-tool', self.eth_args + ['twview', 'wide=1'])
 		text = t.read(strip_color=True)
 		text = t.read(strip_color=True)
 		for addr, amt in bals(n):
 		for addr, amt in bals(n):
@@ -903,6 +935,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		return t
 		return t
 
 
 	def token_bal(self, n=None):
 	def token_bal(self, n=None):
+		self.mining_delay()
 		t = self.spawn('mmgen-tool', self.eth_args + ['--token=mm1', 'twview', 'wide=1'])
 		t = self.spawn('mmgen-tool', self.eth_args + ['--token=mm1', 'twview', 'wide=1'])
 		text = t.read(strip_color=True)
 		text = t.read(strip_color=True)
 		for addr, _amt1, _amt2 in token_bals(n):
 		for addr, _amt1, _amt2 in token_bals(n):
@@ -982,8 +1015,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		return self.token_compile(token_data)
 		return self.token_compile(token_data)
 
 
 	async def get_tx_receipt(self, txid):
 	async def get_tx_receipt(self, txid):
-		if self.daemon.id == 'geth': # yet another Geth bug
-			await asyncio.sleep(0.5)
+		if self.daemon.id in ('geth', 'reth'): # workaround for mining race condition in dev mode
+			await asyncio.sleep(1 if self.daemon.id == 'reth' else 0.5)
 		from mmgen.tx import NewTX
 		from mmgen.tx import NewTX
 		tx = await NewTX(cfg=cfg, proto=self.proto, target='tx')
 		tx = await NewTX(cfg=cfg, proto=self.proto, target='tx')
 		tx.rpc = await self.rpc
 		tx.rpc = await self.rpc
@@ -1313,6 +1346,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 
 
 	def _txcreate_refresh_balances(self, bals, args, total, adj_total, total_coin):
 	def _txcreate_refresh_balances(self, bals, args, total, adj_total, total_coin):
 
 
+		self.mining_delay()
+
 		if total_coin is None:
 		if total_coin is None:
 			total_coin = self.proto.coin
 			total_coin = self.proto.coin
 
 

+ 1 - 1
test/daemontest_d/ut_rpc.py

@@ -201,7 +201,7 @@ class unit_tests:
 				'rpc_port':     32323, # ignored
 				'rpc_port':     32323, # ignored
 				'btc_tw_name': 'ignored',
 				'btc_tw_name': 'ignored',
 				'tw_name':     'also-ignored',
 				'tw_name':     'also-ignored',
-				'eth_testnet_chain_names': ['goerli', 'foo', 'bar', 'baz'],
+				'eth_testnet_chain_names': ['goerli', 'holesky', 'foo', 'bar', 'baz'],
 		})
 		})
 
 
 	async def erigon(self, name, ut):
 	async def erigon(self, name, ut):

+ 0 - 0
test/ref/ethereum/bin/mm1/ERC20Interface.bin → test/ref/ethereum/bin/geth/mm1/ERC20Interface.bin


+ 0 - 0
test/ref/ethereum/bin/mm1/Owned.bin → test/ref/ethereum/bin/geth/mm1/Owned.bin


+ 0 - 0
test/ref/ethereum/bin/mm1/SafeMath.bin → test/ref/ethereum/bin/geth/mm1/SafeMath.bin


+ 0 - 0
test/ref/ethereum/bin/mm1/Token.bin → test/ref/ethereum/bin/geth/mm1/Token.bin


+ 0 - 0
test/ref/ethereum/bin/mm2/ERC20Interface.bin → test/ref/ethereum/bin/geth/mm2/ERC20Interface.bin


+ 0 - 0
test/ref/ethereum/bin/mm2/Owned.bin → test/ref/ethereum/bin/geth/mm2/Owned.bin


+ 0 - 0
test/ref/ethereum/bin/mm2/SafeMath.bin → test/ref/ethereum/bin/geth/mm2/SafeMath.bin


+ 0 - 0
test/ref/ethereum/bin/mm2/Token.bin → test/ref/ethereum/bin/geth/mm2/Token.bin


+ 0 - 0
test/ref/ethereum/bin/reth/mm1/ERC20Interface.bin


+ 1 - 0
test/ref/ethereum/bin/reth/mm1/Owned.bin

@@ -0,0 +1 @@
+6080604052348015600f57600080fd5b50600080546001600160a01b031916331790556101ca806100316000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806379ba5097146100515780638da5cb5b1461005b578063d4ee1d901461008a578063f2fde38b1461009d575b600080fd5b6100596100b0565b005b60005461006e906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b60015461006e906001600160a01b031681565b6100596100ab366004610164565b61012b565b6001546001600160a01b031633146100c757600080fd5b600154600080546040516001600160a01b0393841693909116917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6000546001600160a01b0316331461014257600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561017657600080fd5b81356001600160a01b038116811461018d57600080fd5b939250505056fea2646970667358221220bcd3e32a06d3bc97ce1339e72449373355373643c31c80385ca5f89938aca39c64736f6c634300081a0033

+ 1 - 0
test/ref/ethereum/bin/reth/mm1/SafeMath.bin

@@ -0,0 +1 @@
+6080604052348015600f57600080fd5b506102018061001f6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063a293d1e814610051578063b5931f7c14610076578063d05c78da14610089578063e6cb90131461009c575b600080fd5b61006461005f366004610134565b6100af565b60405190815260200160405180910390f35b610064610084366004610134565b6100cf565b610064610097366004610134565b6100e7565b6100646100aa366004610134565b610119565b6000828211156100be57600080fd5b6100c8828461016c565b9392505050565b60008082116100dd57600080fd5b6100c8828461017f565b60006100f382846101a1565b905082158061010a575081610108848361017f565b145b61011357600080fd5b92915050565b600061012582846101b8565b90508281101561011357600080fd5b6000806040838503121561014757600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011357610113610156565b60008261019c57634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761011357610113610156565b808201808211156101135761011361015656fea264697066735822122081e009254874ff32aa4fa09f9d8a315bf6a4e9e870bfb5d8b4e6611f659ed84964736f6c634300081a0033

File diff suppressed because it is too large
+ 0 - 0
test/ref/ethereum/bin/reth/mm1/Token.bin


+ 0 - 0
test/ref/ethereum/bin/reth/mm2/ERC20Interface.bin


+ 1 - 0
test/ref/ethereum/bin/reth/mm2/Owned.bin

@@ -0,0 +1 @@
+6080604052348015600f57600080fd5b50600080546001600160a01b031916331790556101ca806100316000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806379ba5097146100515780638da5cb5b1461005b578063d4ee1d901461008a578063f2fde38b1461009d575b600080fd5b6100596100b0565b005b60005461006e906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b60015461006e906001600160a01b031681565b6100596100ab366004610164565b61012b565b6001546001600160a01b031633146100c757600080fd5b600154600080546040516001600160a01b0393841693909116917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6000546001600160a01b0316331461014257600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561017657600080fd5b81356001600160a01b038116811461018d57600080fd5b939250505056fea2646970667358221220099e0ef07629964c2926acd072d60ab552ff4e436a34052b97f44c2afb36e84664736f6c634300081a0033

+ 1 - 0
test/ref/ethereum/bin/reth/mm2/SafeMath.bin

@@ -0,0 +1 @@
+6080604052348015600f57600080fd5b506102018061001f6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063a293d1e814610051578063b5931f7c14610076578063d05c78da14610089578063e6cb90131461009c575b600080fd5b61006461005f366004610134565b6100af565b60405190815260200160405180910390f35b610064610084366004610134565b6100cf565b610064610097366004610134565b6100e7565b6100646100aa366004610134565b610119565b6000828211156100be57600080fd5b6100c8828461016c565b9392505050565b60008082116100dd57600080fd5b6100c8828461017f565b60006100f382846101a1565b905082158061010a575081610108848361017f565b145b61011357600080fd5b92915050565b600061012582846101b8565b90508281101561011357600080fd5b6000806040838503121561014757600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011357610113610156565b60008261019c57634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761011357610113610156565b808201808211156101135761011361015656fea264697066735822122045f0003b01e5d932310b9838629204723acd115b8e8d8a25145a9d08e21cce6164736f6c634300081a0033

File diff suppressed because it is too large
+ 0 - 0
test/ref/ethereum/bin/reth/mm2/Token.bin


Some files were not shown because too many files changed in this diff