automount.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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.automount: autosigning with automount tests for the cmdtest.py test suite
  12. """
  13. import time
  14. from .autosign import CmdTestAutosignThreaded
  15. from .regtest import CmdTestRegtest, rt_pw
  16. from ..include.common import gr_uc
  17. class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
  18. 'automounted transacting operations via regtest mode'
  19. networks = ('btc', 'bch', 'ltc')
  20. tmpdir_nums = [49]
  21. bdb_wallet = True
  22. rt_data = {
  23. 'rtFundAmt': {'btc':'500', 'bch':'500', 'ltc':'5500'},
  24. }
  25. cmd_group = (
  26. ('setup', 'regtest mode setup'),
  27. ('walletgen_alice', 'wallet generation (Alice)'),
  28. ('addrgen_alice', 'address generation (Alice)'),
  29. ('addrimport_alice', 'importing Alice’s addresses'),
  30. ('fund_alice', 'funding Alice’s wallet'),
  31. ('generate', 'mining a block'),
  32. ('alice_bal1', 'checking Alice’s balance'),
  33. ('alice_txcreate1', 'creating a transaction'),
  34. ('alice_txcreate_bad_have_unsigned', 'creating the transaction again (error)'),
  35. ('alice_run_autosign_setup', 'running ‘autosign setup’ (with default wallet)'),
  36. ('wait_loop_start', 'starting autosign wait loop'),
  37. ('alice_txstatus1', 'getting transaction status (unsigned)'),
  38. ('alice_txstatus2', 'getting transaction status (unsent)'),
  39. ('alice_txcreate_bad_have_unsent', 'creating the transaction again (error)'),
  40. ('alice_txsend1', 'sending a transaction, editing comment'),
  41. ('alice_txstatus3', 'getting transaction status (in mempool)'),
  42. ('alice_txsend_bad_no_unsent', 'sending the transaction again (error)'),
  43. ('generate', 'mining a block'),
  44. ('alice_txstatus4', 'getting transaction status (one confirmation)'),
  45. ('alice_txcreate2', 'creating a transaction'),
  46. ('alice_txsend_abort1', 'aborting the transaction (raw only)'),
  47. ('alice_txsend_abort2', 'aborting the transaction again (error)'),
  48. ('alice_txcreate3', 'creating a transaction'),
  49. ('alice_txsend_abort3', 'aborting the transaction (user exit)'),
  50. ('alice_txsend_abort4', 'aborting the transaction (raw + signed)'),
  51. ('alice_txsend_abort5', 'aborting the transaction again (error)'),
  52. ('generate', 'mining a block'),
  53. ('alice_txcreate4', 'creating a transaction'),
  54. ('alice_txbump1', 'bumping the unsigned transaction (error)'),
  55. ('alice_txbump2', 'bumping the unsent transaction (error)'),
  56. ('alice_txsend2_dump_hex', 'dumping the transaction to hex'),
  57. ('alice_txsend2_cli', 'sending the transaction via cli'),
  58. ('alice_txsend2_mark_sent', 'marking the transaction sent'),
  59. ('alice_txbump3', 'bumping the transaction'),
  60. ('alice_txsend3', 'sending the bumped transaction'),
  61. ('alice_txbump4', 'bumping the transaction (new outputs, fee too low)'),
  62. ('alice_txbump_abort1', 'aborting the transaction'),
  63. ('alice_txbump5', 'bumping the transaction (new outputs)'),
  64. ('alice_txsend5', 'sending the bumped transaction'),
  65. ('alice_txstatus5', 'getting transaction status (in mempool)'),
  66. ('generate', 'mining a block'),
  67. ('alice_bal2', 'checking Alice’s balance'),
  68. ('wait_loop_kill', 'stopping autosign wait loop'),
  69. ('stop', 'stopping regtest daemon'),
  70. ('txview', 'viewing transactions'),
  71. )
  72. def __init__(self, cfg, trunner, cfgs, spawn):
  73. self.coins = [cfg.coin.lower()]
  74. CmdTestAutosignThreaded.__init__(self, cfg, trunner, cfgs, spawn)
  75. CmdTestRegtest.__init__(self, cfg, trunner, cfgs, spawn)
  76. if trunner is None:
  77. return
  78. self.opts.append('--alice')
  79. def alice_txcreate1(self):
  80. return self._user_txcreate(
  81. 'alice',
  82. chg_addr = 'C:5',
  83. data_arg = 'data:'+gr_uc[:24])
  84. def alice_txcreate2(self):
  85. return self._user_txcreate('alice', chg_addr='L:5')
  86. alice_txcreate3 = alice_txcreate2
  87. def alice_txcreate4(self):
  88. return self._user_txcreate('alice', chg_addr='L:4', need_rbf=True)
  89. def _alice_txsend_abort(self, err=False, send_resp='y', expect=None, shred_expect=[]):
  90. self.insert_device_online()
  91. t = self.spawn(
  92. 'mmgen-txsend',
  93. ['--quiet', '--abort'],
  94. no_passthru_opts = ['coin'],
  95. exit_val = 2 if err else 1 if send_resp == 'n' else None)
  96. if err:
  97. t.expect(expect)
  98. else:
  99. t.expect('(y/N): ', send_resp)
  100. if expect:
  101. t.expect(expect)
  102. for pat in shred_expect:
  103. t.expect(pat, regex=True)
  104. t.read()
  105. self.remove_device_online()
  106. return t
  107. def alice_txsend_abort1(self):
  108. return self._alice_txsend_abort(shred_expect=['Shredding .*arawtx'])
  109. def alice_txsend_abort2(self):
  110. return self._alice_txsend_abort(err=True, expect='No unsent transactions')
  111. def alice_txsend_abort3(self):
  112. return self._alice_txsend_abort(send_resp='n', expect='Exiting at user request')
  113. def alice_txsend_abort4(self):
  114. self._wait_signed('transaction')
  115. return self._alice_txsend_abort(shred_expect=[r'Shredding .*arawtx', r'Shredding .*asigtx'])
  116. alice_txsend_abort5 = alice_txsend_abort2
  117. def alice_txcreate_bad_have_unsigned(self):
  118. return self._user_txcreate('alice', chg_addr='C:5', exit_val=2, expect_str='already present')
  119. def alice_txcreate_bad_have_unsent(self):
  120. return self._user_txcreate('alice', chg_addr='C:5', exit_val=2, expect_str='unsent transaction')
  121. def alice_run_autosign_setup(self):
  122. return self.run_setup(mn_type='default', use_dfl_wallet=True, passwd=rt_pw)
  123. def alice_txsend1(self):
  124. return self._user_txsend('alice', comment='This one’s worth a comment', no_wait=True)
  125. def alice_txsend2_dump_hex(self):
  126. return self._user_txsend('alice', need_rbf=True, dump_hex=True)
  127. def alice_txsend2_cli(self):
  128. if not self.proto.cap('rbf'):
  129. return 'skip'
  130. return self._user_dump_hex_send_cli('alice')
  131. def alice_txsend2_mark_sent(self):
  132. return self._user_txsend('alice', need_rbf=True, mark_sent=True)
  133. def alice_txsend3(self):
  134. return self._user_txsend('alice', need_rbf=True)
  135. def alice_txsend5(self):
  136. return self._user_txsend('alice', need_rbf=True)
  137. def _alice_txstatus(self, expect, exit_val=None, need_rbf=False):
  138. if need_rbf and not self.proto.cap('rbf'):
  139. return 'skip'
  140. self.insert_device_online()
  141. t = self.spawn(
  142. 'mmgen-txsend',
  143. ['--alice', '--autosign', '--status', '--verbose'],
  144. no_passthru_opts = ['coin'],
  145. exit_val = exit_val)
  146. t.expect(expect)
  147. if not exit_val:
  148. t.expect('view: ', 'n')
  149. t.read()
  150. self.remove_device_online()
  151. return t
  152. def alice_txstatus1(self):
  153. return self._alice_txstatus('unsigned', 1)
  154. def alice_txstatus2(self):
  155. self._wait_signed('transaction')
  156. return self._alice_txstatus('unsent', 1)
  157. def alice_txstatus3(self):
  158. return self._alice_txstatus('in mempool', 0)
  159. def alice_txstatus4(self):
  160. return self._alice_txstatus('1 confirmation', 0)
  161. def alice_txstatus5(self):
  162. return self._alice_txstatus('in mempool', need_rbf=True)
  163. def alice_txsend_bad_no_unsent(self):
  164. self.insert_device_online()
  165. t = self.spawn('mmgen-txsend', ['--quiet', '--autosign'], exit_val=2, no_passthru_opts=['coin'])
  166. t.expect('No unsent transactions')
  167. t.read()
  168. self.remove_device_online()
  169. return t
  170. def _alice_txbump(self, fee_opt=None, output_args=[], bad_tx_expect=None, low_fee_fix=None):
  171. if not self.proto.cap('rbf'):
  172. return 'skip'
  173. self.insert_device_online()
  174. t = self.spawn(
  175. 'mmgen-txbump',
  176. ['--alice', '--autosign']
  177. + ([fee_opt] if fee_opt else [])
  178. + output_args,
  179. exit_val = 1 if bad_tx_expect else None)
  180. if bad_tx_expect:
  181. time.sleep(0.5)
  182. t.expect('Only sent transactions')
  183. t.expect(bad_tx_expect)
  184. else:
  185. if not output_args:
  186. t.expect(r'to deduct the fee from .* change output\): ', '\n', regex=True)
  187. t.expect(r'(Y/n): ', 'y') # output OK?
  188. if low_fee_fix or not fee_opt:
  189. if low_fee_fix:
  190. t.expect('Please choose a higher fee')
  191. t.expect('transaction fee: ', (low_fee_fix or '200s') + '\n')
  192. if output_args:
  193. t.expect(r'(Y/n): ', 'y')
  194. t.expect(r'(Y/n): ', 'y') # fee OK?
  195. t.expect(r'(y/N): ', '\n') # add comment?
  196. t.expect(r'(y/N): ', 'y') # save?
  197. t.read()
  198. self.remove_device_online()
  199. return t
  200. def alice_txbump1(self):
  201. return self._alice_txbump(bad_tx_expect='unsigned transaction')
  202. def alice_txbump2(self):
  203. self._wait_signed('transaction')
  204. return self._alice_txbump(bad_tx_expect='unsent transaction')
  205. def alice_txbump3(self):
  206. return self._alice_txbump()
  207. def alice_txbump4(self):
  208. sid = self._user_sid('alice')
  209. return self._alice_txbump(
  210. fee_opt = '--fee=3s',
  211. output_args = [f'{self.burn_addr},7.654321', f'{sid}:C:1'],
  212. low_fee_fix = '300s')
  213. def alice_txbump_abort1(self):
  214. if not self.proto.cap('rbf'):
  215. return 'skip'
  216. return self._alice_txsend_abort(shred_expect=['Shredding .*arawtx'])
  217. def alice_txbump5(self):
  218. sid = self._user_sid('alice')
  219. return self._alice_txbump(
  220. fee_opt = '--fee=400s',
  221. output_args = ['data:message for posterity', f'{self.burn_addr},7.654321', f'{sid}:C:1'])
  222. def alice_bal2(self):
  223. bals = {
  224. 'btc': '491.11002204',
  225. 'ltc': '5491.11002204',
  226. 'bch': '498.7653392',
  227. }
  228. return self.user_bal('alice', bals.get(self.coin, None))