ethswap.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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.cmdtest_d.ethswap: Ethereum swap tests for the cmdtest.py test suite
  12. """
  13. from mmgen.wallet.mmgen import wallet as MMGenWallet
  14. from mmgen.cfg import Config
  15. from mmgen.protocol import init_proto
  16. from .include.runner import CmdTestRunner
  17. from .include.common import dfl_words_file, dfl_seed_id, rt_pw
  18. from .httpd.thornode import ThornodeServer
  19. from .regtest import CmdTestRegtest
  20. from .swap import CmdTestSwapMethods
  21. from .ethdev import CmdTestEthdev
  22. thornode_server = ThornodeServer()
  23. method_template = """
  24. def {name}(self):
  25. self.spawn(log_only=True)
  26. return ethswap_eth.run_test("{eth_name}", sub=True)
  27. """
  28. class CmdTestEthSwap(CmdTestRegtest, CmdTestSwapMethods):
  29. 'Ethereum swap operations'
  30. bdb_wallet = True
  31. tmpdir_nums = [47]
  32. networks = ('btc',)
  33. passthru_opts = ('coin', 'rpc_backend', 'eth_daemon_id')
  34. eth_group = 'ethswap_eth'
  35. cmd_group_in = (
  36. ('setup', 'regtest (Bob and Alice) mode setup'),
  37. ('eth_setup', 'Ethereum devnet setup'),
  38. ('subgroup.init', []),
  39. ('subgroup.fund', ['init']),
  40. ('subgroup.eth_init', []),
  41. ('subgroup.eth_fund', ['eth_init']),
  42. ('subgroup.swap', ['fund', 'eth_fund']),
  43. ('subgroup.eth_swap', ['fund', 'eth_fund']),
  44. ('stop', 'stopping regtest daemon'),
  45. ('eth_stop', 'stopping Ethereum daemon'),
  46. ('thornode_server_stop', 'stopping the Thornode server'),
  47. )
  48. cmd_subgroups = {
  49. 'init': (
  50. 'creating Bob’s MMGen wallet and tracking wallet',
  51. ('walletconv_bob', 'wallet creation (Bob)'),
  52. ('addrgen_bob', 'address generation (Bob)'),
  53. ('addrimport_bob', 'importing Bob’s addresses'),
  54. ),
  55. 'fund': (
  56. 'funding Bob’s wallet',
  57. ('bob_import_miner_addr', 'importing miner’s coinbase addr into Bob’s wallet'),
  58. ('fund_bob', 'funding Bob’s wallet'),
  59. ('generate', 'mining a block'),
  60. ('bob_bal1', 'Bob’s balance'),
  61. ),
  62. 'eth_init': (
  63. 'initializing the ETH tracking wallet',
  64. ('eth_addrgen', ''),
  65. ('eth_addrimport', ''),
  66. ('eth_addrimport_dev_addr', ''),
  67. ('eth_fund_dev_address', ''),
  68. ),
  69. 'eth_fund': (
  70. 'funding the ETH tracking wallet',
  71. ('eth_txcreate1', ''),
  72. ('eth_txsign1', ''),
  73. ('eth_txsend1', ''),
  74. ('eth_bal1', ''),
  75. ),
  76. 'swap': (
  77. 'swap operations (BTC -> ETH)',
  78. ('swaptxcreate1', 'creating a BTC->ETH swap transaction'),
  79. ('swaptxcreate2', 'creating a BTC->ETH swap transaction (used account)'),
  80. ('swaptxsign1', 'signing the swap transaction'),
  81. ('swaptxsend1', 'sending the swap transaction'),
  82. ('swaptxbump1', 'bumping the swap transaction'),
  83. ('swaptxsign2', 'signing the bump transaction'),
  84. ('swaptxsend2', 'sending the bump transaction'),
  85. ('generate', 'generating a block'),
  86. ('bob_bal2', 'Bob’s balance'),
  87. ('swaptxdo1', 'creating, signing and sending a swap transaction'),
  88. ('generate', 'generating a block'),
  89. ('bob_bal3', 'Bob’s balance'),
  90. ),
  91. 'eth_swap': (
  92. 'swap operations (ETH -> BTC)',
  93. ('eth_swaptxcreate1', ''),
  94. ('eth_swaptxcreate2', ''),
  95. ('eth_swaptxsign1', ''),
  96. ('eth_swaptxsend1', ''),
  97. ('eth_swaptxstatus1', ''),
  98. ('eth_bal2', ''),
  99. ),
  100. }
  101. eth_tests = [c[0] for v in tuple(cmd_subgroups.values()) + (cmd_group_in,)
  102. for c in v if isinstance(c, tuple) and c[0].startswith('eth_')]
  103. exec(''.join(method_template.format(name=k, eth_name=k.removeprefix('eth_')) for k in eth_tests))
  104. def __init__(self, cfg, trunner, cfgs, spawn):
  105. super().__init__(cfg, trunner, cfgs, spawn)
  106. if not trunner:
  107. return
  108. global ethswap_eth
  109. cfg = Config({
  110. '_clone': trunner.cfg,
  111. 'proto': init_proto(cfg, network_id='eth'),
  112. 'resume': None,
  113. 'resume_after': None,
  114. 'exit_after': None,
  115. 'eth_daemon_id': trunner.cfg.eth_daemon_id,
  116. 'log': None,
  117. 'coin': 'eth'})
  118. t = trunner
  119. ethswap_eth = CmdTestRunner(cfg, t.repo_root, t.data_dir, t.trash_dir, t.trash_dir2)
  120. ethswap_eth.init_group(self.eth_group)
  121. thornode_server.start()
  122. def walletconv_bob(self):
  123. t = self.spawn(
  124. 'mmgen-walletconv',
  125. ['--bob', '--quiet', '-r0', f'-d{self.cfg.data_dir}/regtest/bob', dfl_words_file],
  126. no_passthru_opts = ['coin', 'eth_daemon_id'])
  127. t.hash_preset(MMGenWallet.desc, '1')
  128. t.passphrase_new('new '+MMGenWallet.desc, rt_pw)
  129. t.label()
  130. return t
  131. def swaptxcreate1(self):
  132. self.get_file_with_ext('rawtx', delete_all=True)
  133. t = self._swaptxcreate(['BTC', '8.765', 'ETH'])
  134. t.expect('OK? (Y/n): ', 'y')
  135. t.expect(':E:2')
  136. t.expect('OK? (Y/n): ', 'y')
  137. return self._swaptxcreate_ui_common(t)
  138. def swaptxcreate2(self):
  139. self.get_file_with_ext('rawtx', delete_all=True)
  140. t = self._swaptxcreate(['BTC', '8.765', 'ETH', f'{dfl_seed_id}:E:1'])
  141. t.expect('OK? (Y/n): ', 'y')
  142. return self._swaptxcreate_ui_common(t)
  143. def swaptxsign1(self):
  144. return self._swaptxsign()
  145. def swaptxsign2(self):
  146. return self._swaptxsign()
  147. def swaptxsend1(self):
  148. return self._swaptxsend()
  149. def swaptxsend2(self):
  150. return self._swaptxsend()
  151. def swaptxbump1(self): # create one-output TX back to self to rescue funds
  152. return self._swaptxbump('40s', output_args=[f'{dfl_seed_id}:B:1'])
  153. def swaptxdo1(self):
  154. return self._swaptxcreate_ui_common(
  155. self._swaptxcreate(
  156. ['BTC', '0.223344', f'{dfl_seed_id}:B:3', 'ETH', f'{dfl_seed_id}:E:2'],
  157. action = 'txdo'),
  158. sign_and_send = True,
  159. file_desc = 'Sent transaction')
  160. def bob_bal2(self):
  161. return self._user_bal_cli('bob', chk='499.9999252')
  162. def bob_bal3(self):
  163. return self._user_bal_cli('bob', chk='499.77656902')
  164. def thornode_server_stop(self):
  165. self.spawn(msg_only=True)
  166. thornode_server.stop()
  167. return 'ok'
  168. class CmdTestEthSwapEth(CmdTestEthdev, CmdTestSwapMethods):
  169. 'Ethereum swap operations - Ethereum wallet'
  170. networks = ('eth',)
  171. tmpdir_nums = [48]
  172. bals = lambda self, k: {
  173. 'swap1': [('98831F3A:E:1', '123.456')],
  174. 'swap2': [('98831F3A:E:1', '114.690978056')],
  175. }[k]
  176. cmd_group_in = CmdTestEthdev.cmd_group_in + (
  177. ('swaptxcreate1', 'creating an ETH->BTC swap transaction'),
  178. ('swaptxcreate2', 'creating an ETH->BTC swap transaction (specific address, trade limit)'),
  179. ('swaptxsign1', 'signing the transaction'),
  180. ('swaptxsend1', 'sending the transaction'),
  181. ('swaptxstatus1', 'getting the transaction status (with --verbose)'),
  182. ('bal1', 'the ETH balance'),
  183. ('bal2', 'the ETH balance'),
  184. )
  185. def swaptxcreate1(self):
  186. self.get_file_with_ext('rawtx', delete_all=True)
  187. t = self._swaptxcreate(['ETH', '8.765', 'BTC'])
  188. t.expect('Continue? (Y/n):', 'y')
  189. t.expect('OK? (Y/n): ', 'y')
  190. return self._swaptxcreate_ui_common(t)
  191. def swaptxcreate2(self):
  192. self.get_file_with_ext('rawtx', delete_all=True)
  193. t = self._swaptxcreate(
  194. ['ETH', '8.765', 'BTC', f'{dfl_seed_id}:B:4'],
  195. add_opts = ['--trade-limit=3%'])
  196. t.expect('Continue? (Y/n):', 'y')
  197. return self._swaptxcreate_ui_common(t, expect=':2019e4/1/0')
  198. def swaptxsign1(self):
  199. return self._swaptxsign()
  200. def swaptxsend1(self):
  201. return self._swaptxsend()
  202. def swaptxstatus1(self):
  203. self.mining_delay()
  204. return self._swaptxsend(add_opts=['--verbose', '--status'], status=True)
  205. def bal1(self):
  206. return self.bal('swap1')
  207. def bal2(self):
  208. return self.bal('swap2')