daemon.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
  4. # Copyright (C)2013-2022 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
  9. # https://gitlab.com/mmgen/mmgen
  10. """
  11. base_proto.ethereum.daemon: Ethereum base protocol daemon classes
  12. """
  13. import os
  14. from ...globalvars import g
  15. from ...util import list_gen,get_subclasses
  16. from ...daemon import CoinDaemon,RPCDaemon,_nw,_dd
  17. class ethereum_daemon(CoinDaemon):
  18. chain_subdirs = _nw('ethereum','goerli','DevelopmentChain')
  19. base_rpc_port = 8545 # same for all networks!
  20. base_p2p_port = 30303 # same for all networks!
  21. daemon_port_offset = 100
  22. network_port_offsets = _nw(0,10,20)
  23. def __init__(self,*args,**kwargs):
  24. if not hasattr(self,'all_daemons'):
  25. ethereum_daemon.all_daemons = get_subclasses(ethereum_daemon,names=True)
  26. self.port_offset = (
  27. self.all_daemons.index(self.id+'_daemon') * self.daemon_port_offset
  28. + getattr(self.network_port_offsets,self.network) )
  29. return super().__init__(*args,**kwargs)
  30. def get_rpc_port(self):
  31. return self.base_rpc_port + self.port_offset
  32. def get_p2p_port(self):
  33. return self.base_p2p_port + self.port_offset
  34. def init_datadir(self):
  35. self.logdir = super().init_datadir()
  36. return os.path.join(
  37. self.logdir,
  38. self.id,
  39. getattr(self.chain_subdirs,self.network) )
  40. class openethereum_daemon(ethereum_daemon):
  41. daemon_data = _dd('OpenEthereum', 3003005, '3.3.5')
  42. version_pat = r'OpenEthereum//v(\d+)\.(\d+)\.(\d+)'
  43. exec_fn = 'openethereum'
  44. cfg_file = 'parity.conf'
  45. datadirs = {
  46. 'linux': [g.home_dir,'.local','share','io.parity.ethereum'],
  47. 'win': [os.getenv('LOCALAPPDATA'),'Parity','Ethereum']
  48. }
  49. def init_subclass(self):
  50. ld = self.platform == 'linux' and not self.opt.no_daemonize
  51. self.coind_args = list_gen(
  52. ['--no-ws'],
  53. ['--no-ipc'],
  54. ['--no-secretstore'],
  55. [f'--jsonrpc-port={self.rpc_port}'],
  56. [f'--port={self.p2p_port}', self.p2p_port],
  57. [f'--base-path={self.datadir}', self.non_dfl_datadir],
  58. [f'--chain={self.proto.chain_name}', self.network!='regtest'],
  59. [f'--config=dev', self.network=='regtest'], # no presets for mainnet or testnet
  60. ['--mode=offline', self.test_suite or self.network=='regtest'],
  61. [f'--log-file={self.logfile}', self.non_dfl_datadir],
  62. ['daemon', ld],
  63. [self.pidfile, ld],
  64. )
  65. class parity_daemon(openethereum_daemon):
  66. daemon_data = _dd('Parity', 2007002, '2.7.2')
  67. version_pat = r'Parity-Ethereum//v(\d+)\.(\d+)\.(\d+)'
  68. exec_fn = 'parity'
  69. class geth_daemon(ethereum_daemon):
  70. # bug in v1.10.17 requires --dev to be omitted to initialize blockchain (devnet_init_bug)
  71. # daemon_data = _dd('Geth', 1010017, '1.10.17')
  72. daemon_data = _dd('Geth', 1010014, '1.10.14')
  73. version_pat = r'Geth/v(\d+)\.(\d+)\.(\d+)'
  74. exec_fn = 'geth'
  75. use_pidfile = False
  76. use_threads = True
  77. datadirs = {
  78. 'linux': [g.home_dir,'.ethereum','geth'],
  79. 'win': [os.getenv('LOCALAPPDATA'),'Geth'] # FIXME
  80. }
  81. avail_opts = ('no_daemonize','online','devnet_init_bug')
  82. def init_subclass(self):
  83. self.coind_args = list_gen(
  84. ['--verbosity=0'],
  85. ['--ipcdisable'], # IPC-RPC: if path to socket is longer than 108 chars, geth fails to start
  86. ['--http'],
  87. ['--http.api=eth,web3,txpool'],
  88. [f'--http.port={self.rpc_port}'],
  89. [f'--port={self.p2p_port}', self.p2p_port], # geth binds p2p port even with --maxpeers=0
  90. ['--maxpeers=0', not self.opt.online],
  91. [f'--datadir={self.datadir}', self.non_dfl_datadir],
  92. ['--goerli', self.network=='testnet'],
  93. ['--dev', self.network=='regtest' and not self.opt.devnet_init_bug],
  94. )
  95. # https://github.com/ledgerwatch/erigon
  96. class erigon_daemon(geth_daemon):
  97. daemon_data = _dd('Erigon', 2022099099, '2022.99.99')
  98. version_pat = r'erigon/(\d+)\.(\d+)\.(\d+)'
  99. exec_fn = 'erigon'
  100. private_ports = _nw(9090,9091,9092) # testnet and regtest are non-standard
  101. datadirs = {
  102. 'linux': [g.home_dir,'.local','share','erigon'],
  103. 'win': [os.getenv('LOCALAPPDATA'),'Erigon'] # FIXME
  104. }
  105. def init_subclass(self):
  106. self.coind_args = list_gen(
  107. ['--verbosity=0'],
  108. [f'--port={self.p2p_port}', self.p2p_port],
  109. ['--maxpeers=0', not self.opt.online],
  110. [f'--private.api.addr=127.0.0.1:{self.private_port}'],
  111. [f'--datadir={self.datadir}', self.non_dfl_datadir],
  112. ['--chain=goerli', self.network=='testnet'],
  113. ['--chain=dev', self.network=='regtest'],
  114. ['--mine', self.network=='regtest'],
  115. )
  116. self.rpc_d = erigon_rpcdaemon(
  117. proto = self.proto,
  118. rpc_port = self.rpc_port,
  119. private_port = self.private_port,
  120. test_suite = self.test_suite,
  121. datadir = self.datadir )
  122. def start(self,quiet=False,silent=False):
  123. super().start(quiet=quiet,silent=silent)
  124. self.rpc_d.debug = self.debug
  125. return self.rpc_d.start(quiet=quiet,silent=silent)
  126. def stop(self,quiet=False,silent=False):
  127. self.rpc_d.debug = self.debug
  128. self.rpc_d.stop(quiet=quiet,silent=silent)
  129. return super().stop(quiet=quiet,silent=silent)
  130. @property
  131. def start_cmds(self):
  132. return [self.start_cmd,self.rpc_d.start_cmd]
  133. class erigon_rpcdaemon(RPCDaemon):
  134. master_daemon = 'erigon_daemon'
  135. rpc_type = 'Erigon'
  136. exec_fn = 'rpcdaemon'
  137. use_pidfile = False
  138. use_threads = True
  139. def __init__(self,proto,rpc_port,private_port,test_suite,datadir):
  140. self.proto = proto
  141. self.test_suite = test_suite
  142. super().__init__()
  143. self.network = proto.network
  144. self.rpc_port = rpc_port
  145. self.datadir = datadir
  146. self.daemon_args = list_gen(
  147. ['--verbosity=0'],
  148. [f'--private.api.addr=127.0.0.1:{private_port}'],
  149. [f'--http.port={self.rpc_port}'],
  150. [f'--datadir={self.datadir}'],
  151. ['--http.api=eth,erigon,web3,net,debug,trace,txpool,parity'],
  152. )