daemon.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. proto.btc.daemon: Bitcoin base protocol daemon classes
  12. """
  13. import os
  14. from ...globalvars import g
  15. from ...util import list_gen
  16. from ...daemon import CoinDaemon,_nw,_dd
  17. class bitcoin_core_daemon(CoinDaemon):
  18. daemon_data = _dd('Bitcoin Core', 230000, '23.0.0')
  19. exec_fn = 'bitcoind'
  20. cli_fn = 'bitcoin-cli'
  21. testnet_dir = 'testnet3'
  22. cfg_file_hdr = '# Bitcoin Core config file\n'
  23. tracking_wallet_name = 'mmgen-tracking-wallet'
  24. rpc_ports = _nw(8332, 18332, 18443)
  25. cfg_file = 'bitcoin.conf'
  26. datadirs = {
  27. 'linux': [g.home_dir,'.bitcoin'],
  28. 'win': [os.getenv('APPDATA'),'Bitcoin']
  29. }
  30. nonstd_datadir = False
  31. def init_datadir(self):
  32. if self.network == 'regtest' and not self.test_suite:
  33. return os.path.join( g.data_dir_root, 'regtest', g.coin.lower() )
  34. else:
  35. return super().init_datadir()
  36. @property
  37. def network_datadir(self):
  38. "location of the network's blockchain data and authentication cookie"
  39. return os.path.join (
  40. self.datadir, {
  41. 'mainnet': '',
  42. 'testnet': self.testnet_dir,
  43. 'regtest': 'regtest',
  44. }[self.network] )
  45. @property
  46. def auth_cookie_fn(self):
  47. return os.path.join(self.network_datadir,'.cookie')
  48. def init_subclass(self):
  49. if self.network == 'regtest':
  50. """
  51. fall back on hard-coded credentials
  52. """
  53. from .regtest import MMGenRegtest
  54. self.rpc_user = MMGenRegtest.rpc_user
  55. self.rpc_password = MMGenRegtest.rpc_password
  56. self.shared_args = list_gen(
  57. [f'--datadir={self.datadir}', self.nonstd_datadir or self.non_dfl_datadir],
  58. [f'--rpcport={self.rpc_port}'],
  59. [f'--rpcuser={self.rpc_user}', self.network == 'regtest'],
  60. [f'--rpcpassword={self.rpc_password}', self.network == 'regtest'],
  61. ['--testnet', self.network == 'testnet'],
  62. ['--regtest', self.network == 'regtest'],
  63. )
  64. self.coind_args = list_gen(
  65. ['--listen=0'],
  66. ['--keypool=1'],
  67. ['--rpcallowip=127.0.0.1'],
  68. [f'--rpcbind=127.0.0.1:{self.rpc_port}'],
  69. ['--pid='+self.pidfile, self.use_pidfile],
  70. ['--daemon', self.platform == 'linux' and not self.opt.no_daemonize],
  71. ['--fallbackfee=0.0002', self.coin == 'BTC' and self.network == 'regtest'],
  72. ['--usecashaddr=0', self.coin == 'BCH'],
  73. ['--mempoolreplacement=1', self.coin == 'LTC'],
  74. ['--txindex=1', self.coin == 'LTC' or self.network == 'regtest'],
  75. ['--addresstype=bech32', self.coin == 'LTC' and self.network == 'regtest'],
  76. )
  77. self.lockfile = os.path.join(self.network_datadir,'.cookie')
  78. @property
  79. def state(self):
  80. cp = self.cli('getblockcount',silent=True)
  81. err = cp.stderr.decode()
  82. if ("error: couldn't connect" in err
  83. or "error: Could not connect" in err
  84. or "does not exist" in err ):
  85. # regtest has no cookie file, so test will always fail
  86. ret = 'busy' if (self.lockfile and os.path.exists(self.lockfile)) else 'stopped'
  87. elif cp.returncode == 0:
  88. ret = 'ready'
  89. else:
  90. ret = 'busy'
  91. if self.debug:
  92. print(f'State: {ret!r}')
  93. return ret
  94. @property
  95. def stop_cmd(self):
  96. return self.cli_cmd('stop')
  97. def set_comment_args(self,rpc,coinaddr,lbl):
  98. if 'label_api' in rpc.caps:
  99. return ('setlabel',coinaddr,lbl)
  100. else:
  101. # NOTE: this works because importaddress() removes the old account before
  102. # associating the new account with the address.
  103. # RPC args: addr,label,rescan[=true],p2sh[=none]
  104. return ('importaddress',coinaddr,lbl,False)
  105. def estimatefee_args(self,rpc):
  106. return (opt.tx_confs,)
  107. def sigfail_errmsg(self,e):
  108. return e.args[0]
  109. class bitcoin_cash_node_daemon(bitcoin_core_daemon):
  110. daemon_data = _dd('Bitcoin Cash Node', 24010000, '24.1.0')
  111. exec_fn = 'bitcoind-bchn'
  112. cli_fn = 'bitcoin-cli-bchn'
  113. rpc_ports = _nw(8432, 18432, 18543) # use non-standard ports (core+100)
  114. datadirs = {
  115. 'linux': [g.home_dir,'.bitcoin-bchn'],
  116. 'win': [os.getenv('APPDATA'),'Bitcoin_ABC']
  117. }
  118. cfg_file_hdr = '# Bitcoin Cash Node config file\n'
  119. nonstd_datadir = True
  120. def set_comment_args(self,rpc,coinaddr,lbl):
  121. # bitcoin-{abc,bchn} 'setlabel' RPC is broken, so use old 'importaddress' method to set label
  122. # Broken behavior: new label is set OK, but old label gets attached to another address
  123. return ('importaddress',coinaddr,lbl,False)
  124. def estimatefee_args(self,rpc):
  125. return () if rpc.daemon_version >= 190100 else (opt.tx_confs,)
  126. def sigfail_errmsg(self,e):
  127. return (
  128. 'This is not the BCH chain.\nRe-run the script without the --coin=bch option.'
  129. if 'Invalid sighash param' in e.args[0] else
  130. e.args[0] )
  131. class litecoin_core_daemon(bitcoin_core_daemon):
  132. # v0.21.2rc5 crashes when mining more than 431 blocks in regtest mode:
  133. # CreateNewBlock: TestBlockValidity failed: bad-txns-vin-empty, Transaction check failed
  134. daemon_data = _dd('Litecoin Core', 210201, '0.21.2.1')
  135. exec_fn = 'litecoind'
  136. cli_fn = 'litecoin-cli'
  137. testnet_dir = 'testnet4'
  138. rpc_ports = _nw(9332, 19332, 19443)
  139. cfg_file = 'litecoin.conf'
  140. datadirs = {
  141. 'linux': [g.home_dir,'.litecoin'],
  142. 'win': [os.getenv('APPDATA'),'Litecoin']
  143. }
  144. cfg_file_hdr = '# Litecoin Core config file\n'