rpc.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
  5. # Licensed under the GNU General Public License, Version 3:
  6. # https://www.gnu.org/licenses
  7. # Public project repositories:
  8. # https://github.com/mmgen/mmgen-wallet
  9. # https://gitlab.com/mmgen/mmgen-wallet
  10. """
  11. proto.eth.rpc: Ethereum base protocol RPC client class
  12. """
  13. import re
  14. from ...base_obj import AsyncInit
  15. from ...obj import Int
  16. from ...util import die,fmt,oneshot_warning_group
  17. from ...rpc import RPCClient
  18. class daemon_warning(oneshot_warning_group):
  19. class geth:
  20. color = 'yellow'
  21. message = 'Geth has not been tested on mainnet. You may experience problems.'
  22. class erigon:
  23. color = 'red'
  24. message = 'Erigon support is EXPERIMENTAL. Use at your own risk!!!'
  25. class CallSigs:
  26. pass
  27. class EthereumRPCClient(RPCClient,metaclass=AsyncInit):
  28. async def __init__(
  29. self,
  30. cfg,
  31. proto,
  32. daemon,
  33. backend,
  34. ignore_wallet):
  35. self.proto = proto
  36. self.daemon = daemon
  37. self.call_sigs = getattr(CallSigs,daemon.id,None)
  38. super().__init__(
  39. cfg = cfg,
  40. host = 'localhost' if cfg.test_suite else (cfg.rpc_host or 'localhost'),
  41. port = daemon.rpc_port )
  42. await self.set_backend_async(backend)
  43. vi,bh,ci = await self.gathered_call(None, (
  44. ('web3_clientVersion',()),
  45. ('eth_getBlockByNumber',('latest',False)),
  46. ('eth_chainId',()),
  47. ))
  48. vip = re.match(self.daemon.version_pat,vi,re.ASCII)
  49. if not vip:
  50. die(2,fmt(f"""
  51. Aborting on daemon mismatch:
  52. Requested daemon: {self.daemon.id}
  53. Running daemon: {vi}
  54. """,strip_char='\t').rstrip())
  55. self.daemon_version = int('{:d}{:03d}{:03d}'.format(*[int(e) for e in vip.groups()]))
  56. self.daemon_version_str = '{}.{}.{}'.format(*vip.groups())
  57. self.daemon_version_info = vi
  58. self.blockcount = int(bh['number'],16)
  59. self.cur_date = int(bh['timestamp'],16)
  60. self.caps = ()
  61. if self.daemon.id in ('parity','openethereum'):
  62. if (await self.call('parity_nodeKind'))['capability'] == 'full':
  63. self.caps += ('full_node',)
  64. self.chainID = None if ci is None else Int(ci,16) # parity/oe return chainID only for dev chain
  65. self.chain = (await self.call('parity_chain')).replace(' ','_').replace('_testnet','')
  66. elif self.daemon.id in ('geth','erigon'):
  67. if self.daemon.network == 'mainnet':
  68. daemon_warning(self.daemon.id)
  69. self.caps += ('full_node',)
  70. self.chainID = Int(ci,16)
  71. self.chain = self.proto.chain_ids[self.chainID]
  72. def make_host_path(self,wallet):
  73. return ''
  74. rpcmethods = (
  75. 'eth_blockNumber',
  76. 'eth_call',
  77. # Returns the EIP155 chain ID used for transaction signing at the current best block.
  78. # Parity: Null is returned if not available, ID not required in transactions
  79. # Erigon: always returns ID, requires ID in transactions
  80. 'eth_chainId',
  81. 'eth_gasPrice',
  82. 'eth_getBalance',
  83. 'eth_getCode',
  84. 'eth_getTransactionCount',
  85. 'eth_getTransactionReceipt',
  86. 'eth_sendRawTransaction',
  87. 'parity_chain',
  88. 'parity_nodeKind',
  89. 'parity_pendingTransactions',
  90. 'txpool_content',
  91. )