daemon.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2025 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.xmr.daemon: Monero base protocol daemon classes
  12. """
  13. import sys, os
  14. from ...cfg import gc
  15. from ...util import list_gen, die, contains_any
  16. from ...daemon import CoinDaemon, RPCDaemon, _nw, _dd
  17. class monero_daemon(CoinDaemon):
  18. daemon_data = _dd('Monero', 18003004, '0.18.3.4-release')
  19. networks = ('mainnet', 'testnet')
  20. exec_fn = 'monerod'
  21. testnet_dir = 'stagenet'
  22. new_console_mswin = True
  23. rpc_ports = _nw(18081, 38081, None) # testnet is stagenet
  24. cfg_file = 'bitmonero.conf'
  25. datadirs = {
  26. 'linux': [gc.home_dir, '.bitmonero'],
  27. 'darwin': [gc.home_dir, '.bitmonero'],
  28. 'win32': ['/', 'c', 'ProgramData', 'bitmonero']
  29. }
  30. def init_datadir(self):
  31. self.logdir = super().init_datadir()
  32. return os.path.join(
  33. self.logdir,
  34. self.testnet_dir if self.network == 'testnet' else '')
  35. def get_p2p_port(self):
  36. return self.rpc_port - 1
  37. def init_subclass(self):
  38. from .rpc import MoneroRPCClient
  39. self.rpc = MoneroRPCClient(
  40. cfg = self.cfg,
  41. proto = self.proto,
  42. host = 'localhost',
  43. port = self.rpc_port,
  44. user = None,
  45. passwd = None,
  46. test_connection = False,
  47. daemon = self)
  48. self.use_pidfile = sys.platform == 'linux'
  49. self.shared_args = list_gen(
  50. ['--no-zmq'],
  51. [f'--p2p-bind-port={self.p2p_port}', self.p2p_port],
  52. [f'--rpc-bind-port={self.rpc_port}'],
  53. ['--stagenet', self.network == 'testnet'],
  54. )
  55. self.coind_args = list_gen(
  56. ['--hide-my-port'],
  57. ['--no-igd'],
  58. [f'--data-dir={self.datadir}', self.non_dfl_datadir],
  59. [f'--pidfile={self.pidfile}', self.use_pidfile],
  60. ['--detach', not (self.opt.no_daemonize or self.platform=='win32')],
  61. ['--offline', not self.opt.online],
  62. )
  63. @property
  64. def stop_cmd(self):
  65. if self.platform == 'win32':
  66. return ['kill', '-Wf', self.pid]
  67. elif contains_any(self.start_cmd, ['--restricted-rpc', '--public-node']):
  68. return ['kill', self.pid]
  69. else:
  70. return [self.exec_fn] + self.shared_args + ['exit']
  71. class MoneroWalletDaemon(RPCDaemon):
  72. master_daemon = 'monero_daemon'
  73. rpc_type = 'Monero wallet'
  74. exec_fn = 'monero-wallet-rpc'
  75. coin = 'XMR'
  76. new_console_mswin = True
  77. networks = ('mainnet', 'testnet')
  78. rpc_ports = _nw(13131, 13141, None) # testnet is non-standard
  79. _reset_ok = ('debug', 'wait', 'pids', 'force_kill')
  80. test_suite_datadir = os.path.join('test', 'daemons', 'xmrtest', 'wallet_rpc')
  81. def start(self, *args, **kwargs):
  82. try: # NB: required due to bug in v18.3.1: PID file not deleted on shutdown
  83. os.unlink(self.pidfile)
  84. except FileNotFoundError:
  85. pass
  86. super().start(*args, **kwargs)
  87. def __init__(
  88. self,
  89. cfg,
  90. proto,
  91. *,
  92. wallet_dir = None,
  93. test_suite = False,
  94. user = None,
  95. passwd = None,
  96. monerod_addr = None,
  97. proxy = None,
  98. port_shift = None,
  99. datadir = None,
  100. trust_monerod = False,
  101. test_monerod = False,
  102. opts = None,
  103. flags = None):
  104. self.proto = proto
  105. self.test_suite = test_suite
  106. super().__init__(cfg, opts=opts, flags=flags)
  107. self.network = proto.network
  108. self.wallet_dir = wallet_dir or (self.test_suite_datadir if test_suite else None)
  109. self.rpc_port = (
  110. self.cfg.wallet_rpc_port or
  111. getattr(self.rpc_ports, self.network) + (11 if test_suite else 0))
  112. if port_shift:
  113. self.rpc_port += port_shift
  114. id_str = f'{self.exec_fn}-{self.bind_port}'
  115. self.datadir = datadir or (self.test_suite_datadir if test_suite else self.exec_fn + '.d')
  116. self.pidfile = os.path.join(self.datadir, id_str+'.pid')
  117. self.logfile = os.path.join(self.datadir, id_str+'.log')
  118. self.use_pidfile = sys.platform == 'linux'
  119. self.proxy = proxy
  120. self.monerod_addr = monerod_addr
  121. self.monerod_port = (
  122. None if monerod_addr else
  123. CoinDaemon(
  124. cfg = self.cfg,
  125. proto = proto,
  126. test_suite = test_suite).rpc_port
  127. )
  128. if test_monerod and self.monerod_port:
  129. import socket
  130. try:
  131. socket.create_connection(('localhost', self.monerod_port), timeout=1).close()
  132. except:
  133. die('SocketError', f'Unable to connect to Monero daemon at localhost:{self.monerod_port}')
  134. self.user = user or self.cfg.wallet_rpc_user or self.cfg.monero_wallet_rpc_user
  135. self.passwd = passwd or self.cfg.wallet_rpc_password or self.cfg.monero_wallet_rpc_password
  136. assert self.user
  137. if not self.passwd:
  138. die(1,
  139. 'You must set your Monero wallet RPC password.\n' +
  140. 'This can be done on the command line with the --wallet-rpc-password option\n' +
  141. "(insecure, not recommended), or by setting 'monero_wallet_rpc_password' in\n" +
  142. "the MMGen config file.")
  143. self.daemon_args = list_gen(
  144. ['--trusted-daemon', trust_monerod],
  145. ['--untrusted-daemon', not trust_monerod],
  146. [f'--rpc-bind-port={self.rpc_port}'],
  147. [f'--wallet-dir={self.wallet_dir}'],
  148. [f'--log-file={self.logfile}'],
  149. [f'--rpc-login={self.user}:{self.passwd}'],
  150. [f'--daemon-address={self.monerod_addr}', self.monerod_addr],
  151. [f'--daemon-port={self.monerod_port}', not self.monerod_addr],
  152. [f'--proxy={self.proxy}', self.proxy],
  153. [f'--pidfile={self.pidfile}', self.platform == 'linux'],
  154. ['--detach', not (self.opt.no_daemonize or self.platform=='win32')],
  155. ['--stagenet', self.network == 'testnet'],
  156. ['--allow-mismatched-daemon-version', test_suite],
  157. )
  158. from .rpc import MoneroWalletRPCClient
  159. self.rpc = MoneroWalletRPCClient(
  160. cfg = self.cfg,
  161. daemon = self,
  162. test_connection = False)