ethswap.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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.cfg import Config
  14. from mmgen.protocol import init_proto
  15. from .include.runner import CmdTestRunner
  16. from .include.common import dfl_sid
  17. from .httpd.thornode import ThornodeServer
  18. from .regtest import CmdTestRegtest
  19. from .swap import CmdTestSwapMethods
  20. from .ethdev import CmdTestEthdev
  21. thornode_server = ThornodeServer()
  22. method_template = """
  23. def {name}(self):
  24. self.spawn(log_only=True)
  25. return ethswap_eth.run_test("{eth_name}", sub=True)
  26. """
  27. class CmdTestEthSwapMethods:
  28. async def token_deploy_a(self):
  29. return await self._token_deploy_math(num=1, get_receipt=False)
  30. async def token_deploy_b(self):
  31. return await self._token_deploy_owned(num=1, get_receipt=False)
  32. async def token_deploy_c(self):
  33. return await self._token_deploy_token(num=1, get_receipt=False)
  34. def token_fund_user(self):
  35. return self._token_transfer_ops(
  36. op = 'fund_user',
  37. mm_idxs = [1],
  38. amt = self.token_fund_amt,
  39. get_receipt = False)
  40. def token_addrgen(self):
  41. return self._token_addrgen(mm_idxs=[1], naddrs=5)
  42. def token_addrimport(self):
  43. return self._token_addrimport('token_addr1', '1-5', expect='5/5')
  44. def token_bal1(self):
  45. return self._token_bal_check(pat=rf'{dfl_sid}:E:1\s+{self.token_fund_amt}\s')
  46. class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest):
  47. 'Ethereum swap operations'
  48. bdb_wallet = True
  49. tmpdir_nums = [47]
  50. networks = ('btc',)
  51. passthru_opts = ('coin', 'rpc_backend', 'eth_daemon_id')
  52. eth_group = 'ethswap_eth'
  53. cmd_group_in = (
  54. ('setup', 'regtest (Bob and Alice) mode setup'),
  55. ('eth_setup', 'Ethereum devnet setup'),
  56. ('subgroup.init', []),
  57. ('subgroup.fund', ['init']),
  58. ('subgroup.eth_init', []),
  59. ('subgroup.eth_fund', ['eth_init']),
  60. ('subgroup.swap', ['fund', 'eth_fund']),
  61. ('subgroup.eth_swap', ['fund', 'eth_fund']),
  62. ('subgroup.token_init', ['eth_fund']),
  63. ('subgroup.token_swap', ['fund', 'token_init']),
  64. ('subgroup.eth_token_swap', ['fund', 'token_init']),
  65. ('stop', 'stopping regtest daemon'),
  66. ('eth_stop', 'stopping Ethereum daemon'),
  67. ('thornode_server_stop', 'stopping the Thornode server'),
  68. )
  69. cmd_subgroups = {
  70. 'init': (
  71. 'creating Bob’s MMGen wallet and tracking wallet',
  72. ('walletconv_bob', 'wallet creation (Bob)'),
  73. ('addrgen_bob', 'address generation (Bob)'),
  74. ('addrimport_bob', 'importing Bob’s addresses'),
  75. ),
  76. 'fund': (
  77. 'funding Bob’s wallet',
  78. ('bob_import_miner_addr', 'importing miner’s coinbase addr into Bob’s wallet'),
  79. ('fund_bob', 'funding Bob’s wallet'),
  80. ('generate', 'mining a block'),
  81. ('bob_bal1', 'Bob’s balance'),
  82. ),
  83. 'eth_init': (
  84. 'initializing the ETH tracking wallet',
  85. ('eth_addrgen', ''),
  86. ('eth_addrimport', ''),
  87. ('eth_addrimport_devaddr', ''),
  88. ('eth_addrimport_reth_devaddr', ''),
  89. ('eth_fund_devaddr', ''),
  90. ('eth_del_reth_devaddr', ''),
  91. ),
  92. 'eth_fund': (
  93. 'funding the ETH tracking wallet',
  94. ('eth_fund_mmgen_addr1', ''),
  95. ('eth_fund_mmgen_addr2', ''),
  96. ('eth_bal1', ''),
  97. ),
  98. 'token_init': (
  99. 'deploying tokens and initializing the ETH token tracking wallet',
  100. ('eth_token_compile1', ''),
  101. ('eth_token_deploy_a', ''),
  102. ('eth_token_deploy_b', ''),
  103. ('eth_token_deploy_c', ''),
  104. ('eth_token_fund_user', ''),
  105. ('eth_token_addrgen', ''),
  106. ('eth_token_addrimport', ''),
  107. ('eth_token_bal1', ''),
  108. ),
  109. 'token_swap': (
  110. 'token swap operations (BTC -> MM1)',
  111. ('swaptxcreate3', 'creating a BTC->MM1 swap transaction'),
  112. ('swaptxsign3', 'signing the swap transaction'),
  113. ('swaptxsend3', 'sending the swap transaction'),
  114. ),
  115. 'swap': (
  116. 'swap operations (BTC -> ETH)',
  117. ('swaptxcreate1', 'creating a BTC->ETH swap transaction'),
  118. ('swaptxcreate2', 'creating a BTC->ETH swap transaction (used account)'),
  119. ('swaptxsign1', 'signing the swap transaction'),
  120. ('swaptxsend1', 'sending the swap transaction'),
  121. ('swaptxbump1', 'bumping the swap transaction'),
  122. ('swaptxsign2', 'signing the bump transaction'),
  123. ('swaptxsend2', 'sending the bump transaction'),
  124. ('generate', 'generating a block'),
  125. ('bob_bal2', 'Bob’s balance'),
  126. ('swaptxdo1', 'creating, signing and sending a swap transaction'),
  127. ('generate', 'generating a block'),
  128. ('bob_bal3', 'Bob’s balance'),
  129. ),
  130. 'eth_swap': (
  131. 'swap operations (ETH -> BTC)',
  132. ('eth_swaptxcreate1', ''),
  133. ('eth_swaptxcreate2', ''),
  134. ('eth_swaptxsign1', ''),
  135. ('eth_swaptxsend1', ''),
  136. ('eth_swaptxstatus1', ''),
  137. ('eth_bal2', ''),
  138. ),
  139. 'eth_token_swap': (
  140. 'swap operations (ETH <-> MM1)',
  141. ('eth_swaptxcreate3', ''),
  142. ('eth_swaptxsign3', ''),
  143. ('eth_swaptxsend3', ''),
  144. ),
  145. }
  146. eth_tests = [c[0] for v in tuple(cmd_subgroups.values()) + (cmd_group_in,)
  147. for c in v if isinstance(c, tuple) and c[0].startswith('eth_')]
  148. exec(''.join(method_template.format(name=k, eth_name=k.removeprefix('eth_')) for k in eth_tests))
  149. def __init__(self, cfg, trunner, cfgs, spawn):
  150. super().__init__(cfg, trunner, cfgs, spawn)
  151. if not trunner:
  152. return
  153. global ethswap_eth
  154. cfg = Config({
  155. '_clone': trunner.cfg,
  156. 'coin': 'eth',
  157. 'eth_daemon_id': trunner.cfg.eth_daemon_id,
  158. 'resume': None,
  159. 'resuming': None,
  160. 'resume_after': None,
  161. 'exit_after': None,
  162. 'log': None})
  163. t = trunner
  164. ethswap_eth = CmdTestRunner(cfg, t.repo_root, t.data_dir, t.trash_dir, t.trash_dir2)
  165. ethswap_eth.init_group(self.eth_group)
  166. thornode_server.start()
  167. def swaptxcreate1(self):
  168. t = self._swaptxcreate(['BTC', '8.765', 'ETH'])
  169. t.expect('OK? (Y/n): ', 'y')
  170. t.expect(':E:2')
  171. t.expect('OK? (Y/n): ', 'y')
  172. return self._swaptxcreate_ui_common(t)
  173. def swaptxcreate2(self):
  174. t = self._swaptxcreate(['BTC', '8.765', 'ETH', f'{dfl_sid}:E:1'])
  175. t.expect('OK? (Y/n): ', 'y')
  176. return self._swaptxcreate_ui_common(t)
  177. def swaptxsign1(self):
  178. return self._swaptxsign()
  179. def swaptxsend1(self):
  180. return self._swaptxsend()
  181. swaptxsign3 = swaptxsign2 = swaptxsign1
  182. swaptxsend3 = swaptxsend2 = swaptxsend1
  183. def swaptxbump1(self): # create one-output TX back to self to rescue funds
  184. return self._swaptxbump('40s', output_args=[f'{dfl_sid}:B:1'])
  185. def swaptxdo1(self):
  186. return self._swaptxcreate_ui_common(
  187. self._swaptxcreate(
  188. ['BTC', '0.223344', f'{dfl_sid}:B:3', 'ETH', f'{dfl_sid}:E:2'],
  189. action = 'txdo'),
  190. sign_and_send = True,
  191. file_desc = 'Sent transaction')
  192. def bob_bal2(self):
  193. return self._user_bal_cli('bob', chk='499.9999252')
  194. def bob_bal3(self):
  195. return self._user_bal_cli('bob', chk='499.77656902')
  196. def swaptxcreate3(self):
  197. t = self._swaptxcreate(['BTC', '0.87654321', 'ETH.MM1', f'{dfl_sid}:E:5'])
  198. t.expect('OK? (Y/n): ', 'y')
  199. return self._swaptxcreate_ui_common(t)
  200. def thornode_server_stop(self):
  201. self.spawn(msg_only=True)
  202. thornode_server.stop()
  203. return 'ok'
  204. class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev):
  205. 'Ethereum swap operations - Ethereum wallet'
  206. networks = ('eth',)
  207. tmpdir_nums = [48]
  208. fund_amt = '123.456'
  209. token_fund_amt = 1000
  210. bals = lambda self, k: {
  211. 'swap1': [('98831F3A:E:1', '123.456')],
  212. 'swap2': [('98831F3A:E:1', '114.690978056')],
  213. }[k]
  214. cmd_group_in = CmdTestEthdev.cmd_group_in + (
  215. ('fund_mmgen_addr1', 'funding user address :1)'),
  216. ('fund_mmgen_addr2', 'funding user address :11)'),
  217. ('swaptxcreate1', 'creating an ETH->BTC swap transaction'),
  218. ('swaptxcreate2', 'creating an ETH->BTC swap transaction (spec address, trade limit)'),
  219. ('swaptxsign1', 'signing the transaction'),
  220. ('swaptxsend1', 'sending the transaction'),
  221. ('swaptxstatus1', 'getting the transaction status (with --verbose)'),
  222. ('swaptxcreate3', 'creating an ETH->MM1 swap transaction'),
  223. ('swaptxsign3', 'signing the transaction'),
  224. ('swaptxsend3', 'sending the transaction'),
  225. ('bal1', 'the ETH balance'),
  226. ('bal2', 'the ETH balance'),
  227. ('token_compile1', 'compiling ERC20 token #1'),
  228. ('token_deploy_a', 'deploying ERC20 token MM1 (SafeMath)'),
  229. ('token_deploy_b', 'deploying ERC20 token MM1 (Owned)'),
  230. ('token_deploy_c', 'deploying ERC20 token MM1 (Token)'),
  231. ('token_fund_user', 'transferring token funds from dev to user'),
  232. ('token_addrgen', 'generating token addresses'),
  233. ('token_addrimport', 'importing token addresses using token address (MM1)'),
  234. ('token_bal1', 'the token balance'),
  235. )
  236. def swaptxcreate1(self):
  237. t = self._swaptxcreate(['ETH', '8.765', 'BTC'])
  238. t.expect('OK? (Y/n): ', 'y')
  239. return self._swaptxcreate_ui_common(t)
  240. def swaptxcreate2(self):
  241. return self._swaptxcreate_ui_common(
  242. self._swaptxcreate(
  243. ['ETH', '8.765', 'BTC', f'{dfl_sid}:B:4'],
  244. add_opts = ['--trade-limit=3%']),
  245. expect = ':2019e4/1/0')
  246. def swaptxcreate3(self):
  247. t = self._swaptxcreate(['ETH', '8.765', 'ETH.MM1', f'{dfl_sid}:E:5'])
  248. return self._swaptxcreate_ui_common(t)
  249. def swaptxsign1(self):
  250. return self._swaptxsign()
  251. def swaptxsend1(self):
  252. return self._swaptxsend()
  253. def swaptxstatus1(self):
  254. self.mining_delay()
  255. return self._swaptxsend(add_opts=['--verbose', '--status'], status=True)
  256. swaptxsign3 = swaptxsign1
  257. swaptxsend3 = swaptxsend1
  258. def bal1(self):
  259. return self.bal('swap1')
  260. def bal2(self):
  261. return self.bal('swap2')