From b88c4bbd48fa661a6d33a6e7ace4c4d8b5383296 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 4 Aug 2021 10:47:20 +0000 Subject: [PATCH] ETH: add experimental support for Erigon https://github.com/ledgerwatch/erigon - Support is for testing/development only - Enable with MMGEN_ENABLE_ERIGON environmental var - Specify with --daemon-id=erigon --- mmgen/altcoins/eth/tx.py | 4 ++ mmgen/daemon.py | 83 +++++++++++++++++++++++++++++++++++++++- mmgen/globalvars.py | 2 + mmgen/protocol.py | 15 +++++++- mmgen/rpc.py | 12 ++++++ 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/mmgen/altcoins/eth/tx.py b/mmgen/altcoins/eth/tx.py index c19e9007..10401835 100755 --- a/mmgen/altcoins/eth/tx.py +++ b/mmgen/altcoins/eth/tx.py @@ -396,6 +396,10 @@ class EthereumMMGenTX: return False if self.rpc.daemon.id in ('parity','openethereum'): pool = [x['hash'] for x in await self.rpc.call('parity_pendingTransactions')] + elif self.rpc.daemon.id == 'erigon': + res = await self.rpc.call('txpool_content') + pmsg('txpool_content:',res) # DEBUG + pool = list(res['pending']) + list(res['queued']) return '0x'+self.coin_txid in pool async def is_in_wallet(): diff --git a/mmgen/daemon.py b/mmgen/daemon.py index b093afab..b54b3799 100755 --- a/mmgen/daemon.py +++ b/mmgen/daemon.py @@ -342,7 +342,7 @@ class CoinDaemon(Daemon): 'BCH': _cd('Bitcoin Cash Node', ['bitcoin_cash_node']), 'LTC': _cd('Litecoin', ['litecoin_core']), 'XMR': _cd('Monero', ['monero']), - 'ETH': _cd('Ethereum', ['openethereum']), + 'ETH': _cd('Ethereum', ['openethereum'] + (['erigon'] if g.enable_erigon else []) ), 'ETC': _cd('Ethereum Classic', ['parity']), } @@ -656,3 +656,84 @@ class parity_daemon(openethereum_daemon): exec_fn = 'parity' ports_shift = _nw(100,110,120) rpc_ports = _nw(*[8545 + n for n in ports_shift]) # non-standard + +# https://github.com/ledgerwatch/erigon +class erigon_daemon(CoinDaemon): + avail_opts = ('online',) + daemon_data = _dd('Erigon', 2021007005, '2021.07.5') + version_pat = r'erigon/(\d+)\.(\d+)\.(\d+)' + exec_fn = 'erigon' + cfg_file = 'erigon.conf' + private_ports = _nw(9090,9091,9092) # testnet and regtest are non-standard + ports_shift = _nw(200,210,220) + rpc_ports = _nw(*[8545 + n for n in ports_shift]) # non-standard + use_pidfile = False + use_threads = True + datadirs = { + 'linux': [g.home_dir,'.local','share','erigon'], + 'win': [os.getenv('LOCALAPPDATA'),'Erigon'] # FIXME + } + datadir_is_subdir = True + testnet_dir = 'erigon_testnet' + + def subclass_init(self): + self.private_port = getattr(self.private_ports,self.network) + if self.network == 'regtest': + self.datadir = None + self.coind_args = list_gen( + ['--verbosity=0'], + [f'--private.api.addr=127.0.0.1:{self.private_port}'], + [f'--datadir={self.datadir}', self.network!='regtest'], + ['--chain=dev', self.network=='regtest'], + ['--chain=goerli', self.network=='testnet'], + ['--miner.etherbase=00a329c0648769a73afac7f9381e08fb43dbea72', self.network=='regtest'], + ) + self.rpc_d = erigon_rpcdaemon( + proto = self.proto, + rpc_port = self.rpc_port, + private_port = self.private_port, + test_suite = self.test_suite, + datadir = self.datadir ) + + def start(self,quiet=False,silent=False): + super().start(quiet=quiet,silent=silent) + self.rpc_d.debug = self.debug + return self.rpc_d.start(quiet=quiet,silent=silent) + + def stop(self,quiet=False,silent=False): + self.rpc_d.debug = self.debug + self.rpc_d.stop(quiet=quiet,silent=silent) + return super().stop(quiet=quiet,silent=silent) + + @property + def start_cmds(self): + return [self.start_cmd,self.rpc_d.start_cmd] + +class erigon_rpcdaemon(RPCDaemon): + + master_daemon = 'erigon_daemon' + rpc_type = 'Erigon' + exec_fn = 'rpcdaemon' + use_pidfile = False + use_threads = True + + def __init__(self,proto,rpc_port,private_port,test_suite,datadir): + + self.proto = proto + self.test_suite = test_suite + + super().__init__() + + self.network = proto.network + self.rpc_port = rpc_port + self.datadir = datadir + + self.daemon_args = list_gen( + ['--verbosity=0'], + [f'--private.api.addr=127.0.0.1:{private_port}'], + [f'--datadir={self.datadir}', self.network != 'regtest'], + ['--http.api=eth,erigon,web3,net,txpool'], + [f'--http.port={self.rpc_port}'], + ) + + self.lock() diff --git a/mmgen/globalvars.py b/mmgen/globalvars.py index c6e3a791..682474ac 100755 --- a/mmgen/globalvars.py +++ b/mmgen/globalvars.py @@ -112,6 +112,7 @@ class GlobalContext(Lockable): # miscellaneous features: use_internal_keccak_module = False + enable_erigon = False # test suite: bogus_wallet_data = '' @@ -227,6 +228,7 @@ class GlobalContext(Lockable): 'MMGEN_RPC_BACKEND', 'MMGEN_IGNORE_DAEMON_VERSION', 'MMGEN_USE_STANDALONE_SCRYPT_MODULE', + 'MMGEN_ENABLE_ERIGON', 'MMGEN_DISABLE_COLOR', 'MMGEN_DISABLE_MSWIN_PW_WARNING', diff --git a/mmgen/protocol.py b/mmgen/protocol.py index 3d168b1f..7b3ab972 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -397,6 +397,19 @@ class CoinProtocol(MMGenObject): avg_bdi = 15 ignore_daemon_version = False + chain_ids = { + 1: 'ethereum', # ethereum mainnet + 2: 'morden', # morden testnet (deprecated) + 3: 'ropsten', # ropsten testnet + 4: 'rinkeby', # rinkeby testnet + 5: 'goerli', # goerli testnet + 42: 'kovan', # kovan testnet + 61: 'classic', # ethereum classic mainnet + 62: 'morden', # ethereum classic testnet + 17: 'developmentchain', # parity dev chain + 1337: 'developmentchain', # erigon dev chain + } + @property def dcoin(self): return self.tokensym or self.coin @@ -415,7 +428,7 @@ class CoinProtocol(MMGenObject): return pubkey_hash class EthereumTestnet(Ethereum): - chain_names = ['kovan'] + chain_names = ['kovan','goerli','rinkeby'] class EthereumRegtest(EthereumTestnet): chain_names = ['developmentchain'] diff --git a/mmgen/rpc.py b/mmgen/rpc.py index 3bada2d9..92c9b34e 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -624,12 +624,18 @@ class EthereumRPCClient(RPCClient,metaclass=aInitMeta): self.caps += ('full_node',) self.chainID = None if ci == None else Int(ci,16) # parity/oe return chainID only for dev chain self.chain = (await self.call('parity_chain')).replace(' ','_').replace('_testnet','') + elif self.daemon.id == 'erigon': + do_erigon_warning() + self.caps += ('full_node',) + self.chainID = Int(ci,16) + self.chain = self.proto.chain_ids[self.chainID] rpcmethods = ( 'eth_blockNumber', 'eth_call', # Returns the EIP155 chain ID used for transaction signing at the current best block. # Parity: Null is returned if not available, ID not required in transactions + # Erigon: always returns ID, requires ID in transactions 'eth_chainId', 'eth_gasPrice', 'eth_getBalance', @@ -640,6 +646,7 @@ class EthereumRPCClient(RPCClient,metaclass=aInitMeta): 'parity_chain', 'parity_nodeKind', 'parity_pendingTransactions', + 'txpool_content', ) class MoneroRPCClient(RPCClient): @@ -709,6 +716,11 @@ class MoneroWalletRPCClient(MoneroRPCClient): 'refresh', # start_height ) +def do_erigon_warning(erigon_warning_shown=[]): + if not erigon_warning_shown: + rmsg(f'WARNING: Erigon support is EXPERIMENTAL. Use at your own risk!!!') + erigon_warning_shown.append(1) + def handle_unsupported_daemon_version(rpc,proto,ignore_daemon_version,unsupported_daemon_warning_shown=[]): if ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version: if not type(proto) in unsupported_daemon_warning_shown: