daemon.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, a command-line 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.btc.daemon: Bitcoin base protocol daemon classes
  12. """
  13. import os
  14. from ...cfg import gc
  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', 270100, '27.1.0')
  19. exec_fn = 'bitcoind'
  20. cli_fn = 'bitcoin-cli'
  21. testnet_dir = 'testnet3'
  22. cfg_file_hdr = '# Bitcoin Core config file\n'
  23. rpc_ports = _nw(8332, 18332, 18443)
  24. cfg_file = 'bitcoin.conf'
  25. nonstd_datadir = False
  26. datadirs = {
  27. 'linux': [gc.home_dir,'.bitcoin'],
  28. 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Bitcoin'],
  29. 'win32': [os.getenv('APPDATA'),'Bitcoin']
  30. }
  31. avail_opts = ('no_daemonize', 'online', 'bdb_wallet')
  32. def init_datadir(self):
  33. if self.network == 'regtest' and not self.test_suite:
  34. return os.path.join( self.cfg.data_dir_root, 'regtest', self.cfg.coin.lower() )
  35. else:
  36. return super().init_datadir()
  37. @property
  38. def network_datadir(self):
  39. "location of the network's blockchain data and authentication cookie"
  40. return os.path.join (
  41. self.datadir, {
  42. 'mainnet': '',
  43. 'testnet': self.testnet_dir,
  44. 'regtest': 'regtest',
  45. }[self.network] )
  46. @property
  47. def auth_cookie_fn(self):
  48. return os.path.join(self.network_datadir,'.cookie')
  49. def init_subclass(self):
  50. if self.network == 'regtest':
  51. # fall back on hard-coded credentials:
  52. from .regtest import MMGenRegtest
  53. self.rpc_user = MMGenRegtest.rpc_user
  54. self.rpc_password = MMGenRegtest.rpc_password
  55. self.shared_args = list_gen(
  56. [f'--datadir={self.datadir}', self.nonstd_datadir or self.non_dfl_datadir],
  57. [f'--rpcport={self.rpc_port}'],
  58. [f'--rpcuser={self.rpc_user}', self.network == 'regtest'],
  59. [f'--rpcpassword={self.rpc_password}', self.network == 'regtest'],
  60. ['--testnet', self.network == 'testnet'],
  61. ['--regtest', self.network == 'regtest'],
  62. )
  63. self.coind_args = list_gen(
  64. ['--listen=0'],
  65. ['--keypool=1'],
  66. ['--rpcallowip=127.0.0.1'],
  67. [f'--rpcbind=127.0.0.1:{self.rpc_port}'],
  68. ['--pid='+self.pidfile, self.use_pidfile],
  69. ['--daemon', self.platform in ('linux', 'darwin') and not self.opt.no_daemonize],
  70. ['--fallbackfee=0.0002', self.coin == 'BTC' and self.network == 'regtest'],
  71. ['--deprecatedrpc=create_bdb', self.coin == 'BTC' and self.opt.bdb_wallet],
  72. ['--mempoolreplacement=1', self.coin == 'LTC'],
  73. ['--txindex=1', self.coin == 'LTC' or self.network == 'regtest'],
  74. ['--addresstype=bech32', self.coin == 'LTC' and self.network == 'regtest'],
  75. )
  76. self.lockfile = os.path.join(self.network_datadir,'.cookie')
  77. @property
  78. def state(self):
  79. cp = self.cli('getblockcount',silent=True)
  80. err = cp.stderr.decode()
  81. if ("error: couldn't connect" in err
  82. or "error: Could not connect" in err
  83. or "does not exist" in err ):
  84. # regtest has no cookie file, so test will always fail
  85. ret = 'busy' if (self.lockfile and os.path.exists(self.lockfile)) else 'stopped'
  86. elif cp.returncode == 0:
  87. ret = 'ready'
  88. else:
  89. ret = 'busy'
  90. if self.debug:
  91. print(f'State: {ret!r}')
  92. return ret
  93. @property
  94. def stop_cmd(self):
  95. return self.cli_cmd('stop')
  96. def set_comment_args(self,rpc,coinaddr,lbl):
  97. if 'label_api' in rpc.caps:
  98. return ('setlabel',coinaddr,lbl)
  99. else:
  100. # NOTE: this works because importaddress() removes the old account before
  101. # associating the new account with the address.
  102. # RPC args: addr,label,rescan[=true],p2sh[=none]
  103. return ('importaddress',coinaddr,lbl,False)
  104. def estimatefee_args(self,rpc):
  105. return (self.cfg.fee_estimate_confs,)
  106. def sigfail_errmsg(self,e):
  107. return e.args[0]
  108. class bitcoin_cash_node_daemon(bitcoin_core_daemon):
  109. daemon_data = _dd('Bitcoin Cash Node', 27010000, '27.1.0')
  110. exec_fn = 'bitcoind-bchn'
  111. cli_fn = 'bitcoin-cli-bchn'
  112. rpc_ports = _nw(8432, 18432, 18543) # use non-standard ports (core+100)
  113. cfg_file_hdr = '# Bitcoin Cash Node config file\n'
  114. nonstd_datadir = True
  115. datadirs = {
  116. 'linux': [gc.home_dir,'.bitcoin-bchn'],
  117. 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Bitcoin-Cash-Node'],
  118. 'win32': [os.getenv('APPDATA'),'Bitcoin-Cash-Node']
  119. }
  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 (self.cfg.fee_estimate_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', 210300, '0.21.3')
  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. cfg_file_hdr = '# Litecoin Core config file\n'
  141. datadirs = {
  142. 'linux': [gc.home_dir,'.litecoin'],
  143. 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Litecoin'],
  144. 'win32': [os.getenv('APPDATA'),'Litecoin']
  145. }