Browse Source

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
The MMGen Project 3 years ago
parent
commit
b88c4bb
5 changed files with 114 additions and 2 deletions
  1. 4 0
      mmgen/altcoins/eth/tx.py
  2. 82 1
      mmgen/daemon.py
  3. 2 0
      mmgen/globalvars.py
  4. 14 1
      mmgen/protocol.py
  5. 12 0
      mmgen/rpc.py

+ 4 - 0
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():

+ 82 - 1
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()

+ 2 - 0
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',

+ 14 - 1
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']

+ 12 - 0
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: