coin_daemon_control.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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. test.include.coin_daemon_control: Start and stop daemons for the MMGen test suite
  12. """
  13. import sys
  14. from pathlib import PurePath
  15. sys.path[0] = str(PurePath(*PurePath(__file__).parts[:-3]))
  16. from mmgen.cfg import Config, gc
  17. from mmgen.util import msg, die, oneshot_warning
  18. from mmgen.protocol import init_proto
  19. from mmgen.daemon import CoinDaemon
  20. xmr_wallet_network_ids = {
  21. 'xmrw': 'mainnet',
  22. 'xmrw_tn': 'testnet'
  23. }
  24. action = gc.prog_name.split('-')[0]
  25. opts_data = {
  26. 'sets': [('debug', True, 'verbose', True)],
  27. 'text': {
  28. 'desc': f'{action.capitalize()} coin or wallet daemons for the MMGen test suite',
  29. 'usage':'[opts] <network IDs>',
  30. 'options': """
  31. -h, --help Print this help message
  32. --, --longhelp Print help message for long (global) options
  33. -D, --debug Produce debugging output (implies --verbose)
  34. -d, --datadir= Override the default datadir
  35. -i, --daemon-ids Print all known daemon IDs
  36. -m, --mainnet-only Perform operations for mainnet daemons only
  37. -n, --no-daemonize Don't fork daemon to background
  38. -p, --port-shift= Shift the RPC port by this number
  39. -r, --remove-datadir Remove the datadir(s) after stopping the daemon(s)
  40. -s, --get-state Get the state of the daemon(s) and exit
  41. -t, --testing Testing mode. Print commands but don't execute them
  42. -q, --quiet Produce quieter output
  43. -u, --usermode Run the daemon in user (non test-suite) mode
  44. -v, --verbose Produce more verbose output
  45. -V, --print-version Print version strings from exec’ed daemons (not RPC)
  46. -W, --no-wait Don't wait for daemons to change state before exiting
  47. """,
  48. 'notes': """
  49. Valid network IDs: {nid}, {xmrw_nid}, all, no_xmr
  50. """
  51. },
  52. 'code': {
  53. 'options': lambda s: s.format(
  54. a = action.capitalize(),
  55. pn = gc.prog_name),
  56. 'notes': lambda s, help_notes: s.format(
  57. nid = help_notes('coin_daemon_network_ids'),
  58. xmrw_nid = ', '.join(xmr_wallet_network_ids),
  59. )
  60. }
  61. }
  62. class warn_missing_exec(oneshot_warning):
  63. color = 'yellow'
  64. message = 'missing executable {!r}'
  65. def run(network_id=None, proto=None, daemon_id=None, missing_exec_ok=False):
  66. if network_id in xmr_wallet_network_ids:
  67. from mmgen.proto.xmr.daemon import MoneroWalletDaemon
  68. d = MoneroWalletDaemon(
  69. cfg = cfg,
  70. proto = init_proto(cfg, coin='XMR', network=xmr_wallet_network_ids[network_id]),
  71. user = 'test',
  72. passwd = 'test passwd',
  73. test_suite = True,
  74. monerod_addr = None,
  75. trust_monerod = True,
  76. test_monerod = False,
  77. opts = ['no_daemonize'] if cfg.no_daemonize else None)
  78. else:
  79. d = CoinDaemon(
  80. cfg,
  81. network_id = network_id,
  82. proto = proto,
  83. test_suite = not cfg.usermode,
  84. opts = ['no_daemonize'] if cfg.no_daemonize else None,
  85. port_shift = int(cfg.port_shift or 0),
  86. datadir = cfg.datadir,
  87. daemon_id = daemon_id)
  88. if cfg.mainnet_only and d.network != 'mainnet':
  89. return
  90. d.debug = d.debug or cfg.debug
  91. d.wait = cfg.remove_datadir or not cfg.no_wait
  92. if missing_exec_ok:
  93. try:
  94. d.get_exec_version_str()
  95. except Exception as e:
  96. if not cfg.quiet:
  97. if cfg.verbose:
  98. msg(str(e))
  99. warn_missing_exec(div=d.exec_fn, fmt_args=(d.exec_fn,))
  100. return
  101. if cfg.print_version:
  102. msg('{:16} {}'.format(d.exec_fn+':', d.get_exec_version_str()))
  103. elif cfg.get_state:
  104. print(d.state_msg())
  105. elif cfg.testing:
  106. for cmd in d.start_cmds if action == 'start' else [d.stop_cmd]:
  107. print(' '.join(cmd))
  108. else:
  109. d.cmd(action, quiet=cfg.quiet)
  110. if action == 'stop' and cfg.remove_datadir:
  111. cfg._util.vmsg(f'Removing ‘{d.datadir}’')
  112. d.remove_datadir()
  113. def main():
  114. if cfg.daemon_ids:
  115. print('\n'.join(CoinDaemon.all_daemon_ids()))
  116. elif 'all' in cfg._args or 'no_xmr' in cfg._args:
  117. if len(cfg._args) != 1:
  118. die(1, "'all' or 'no_xmr' must be the sole argument")
  119. for coin in CoinDaemon.coins:
  120. if coin == 'XMR' and cfg._args[0] == 'no_xmr':
  121. continue
  122. for daemon_id in CoinDaemon.get_daemon_ids(cfg, coin):
  123. for network in CoinDaemon.get_daemon(cfg, coin, daemon_id).networks:
  124. run(
  125. proto = init_proto(cfg, coin=coin, network=network),
  126. daemon_id = daemon_id,
  127. missing_exec_ok = True)
  128. else:
  129. ids = cfg._args
  130. network_ids = CoinDaemon.get_network_ids(cfg)
  131. if not ids:
  132. cfg._usage()
  133. for i in ids:
  134. if i not in network_ids + list(xmr_wallet_network_ids):
  135. die(1, f'{i!r}: invalid network ID')
  136. for network_id in ids:
  137. run(network_id=network_id.lower())
  138. cfg = Config(opts_data=opts_data, init_opts={'skip_cfg_file': True})