ts_shared.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. ts_shared.py: Shared methods for the test.py test suite
  20. """
  21. import os
  22. from mmgen.globalvars import g
  23. from mmgen.opts import opt
  24. from mmgen.util import ymsg
  25. from mmgen.seed import SeedSource,SeedSourceEnc,Brainwallet,Wallet,IncogWalletHidden
  26. from test.test_py_d.common import *
  27. from test.common import *
  28. class TestSuiteShared(object):
  29. 'shared methods for the test.py test suite'
  30. def txcreate_ui_common( self,t,
  31. caller = None,
  32. menu = [],
  33. inputs = '1',
  34. file_desc = 'Transaction',
  35. input_sels_prompt = 'to spend',
  36. bad_input_sels = False,
  37. non_mmgen_inputs = 0,
  38. interactive_fee = '',
  39. fee_desc = 'transaction fee',
  40. fee_res = None,
  41. eth_fee_res = None,
  42. add_comment = '',
  43. view = 't',
  44. save = True ):
  45. txdo = (caller or self.test_name)[:4] == 'txdo'
  46. for choice in menu + ['q']:
  47. t.expect(r'\[q\]uit view, .*?:.',choice,regex=True)
  48. if bad_input_sels:
  49. for r in ('x','3-1','9999'):
  50. t.expect(input_sels_prompt+': ',r+'\n')
  51. t.expect(input_sels_prompt+': ',inputs+'\n')
  52. if not txdo:
  53. for i in range(non_mmgen_inputs):
  54. t.expect('Accept? (y/N): ','y')
  55. have_est_fee = t.expect([fee_desc+': ','OK? (Y/n): ']) == 1
  56. if have_est_fee and not interactive_fee:
  57. t.send('y')
  58. else:
  59. if have_est_fee:
  60. t.send('n')
  61. if g.coin == 'BCH' or g.proto.base_coin == 'ETH': # TODO: pexpect race condition?
  62. time.sleep(0.1)
  63. if eth_fee_res:
  64. t.expect('or gas price: ',interactive_fee+'\n')
  65. else:
  66. t.send(interactive_fee+'\n')
  67. if fee_res: t.expect(fee_res)
  68. t.expect('OK? (Y/n): ','y')
  69. t.expect('(Y/n): ','\n') # chg amt OK?
  70. t.do_comment(add_comment)
  71. t.view_tx(view)
  72. if not txdo:
  73. t.expect('(y/N): ',('n','y')[save])
  74. t.written_to_file(file_desc)
  75. return t
  76. def txsign_ui_common( self,t,
  77. caller = None,
  78. view = 't',
  79. add_comment = '',
  80. file_desc = 'Signed transaction',
  81. ni = False,
  82. save = True,
  83. do_passwd = False,
  84. has_label = False ):
  85. txdo = (caller or self.test_name)[:4] == 'txdo'
  86. if do_passwd:
  87. t.passphrase('MMGen wallet',self.wpasswd)
  88. if not ni and not txdo:
  89. t.view_tx(view)
  90. t.do_comment(add_comment,has_label=has_label)
  91. t.expect('(Y/n): ',('n','y')[save])
  92. t.written_to_file(file_desc)
  93. return t
  94. def txsend_ui_common( self,t,
  95. caller = None,
  96. view = 'n',
  97. add_comment = '',
  98. file_desc = 'Sent transaction',
  99. confirm_send = True,
  100. bogus_send = True,
  101. quiet = False,
  102. has_label = False ):
  103. txdo = (caller or self.test_name)[:4] == 'txdo'
  104. if not txdo:
  105. t.license() # MMGEN_NO_LICENSE is set, so does nothing
  106. t.view_tx(view)
  107. t.do_comment(add_comment,has_label=has_label)
  108. self._do_confirm_send(t,quiet=quiet,confirm_send=confirm_send)
  109. if bogus_send:
  110. txid = ''
  111. t.expect('BOGUS transaction NOT sent')
  112. else:
  113. txid = t.expect_getend('Transaction sent: ')
  114. assert len(txid) == 64,"'{}': Incorrect txid length!".format(txid)
  115. t.written_to_file(file_desc)
  116. return txid
  117. def txsign_end(self,t,tnum=None,has_label=False):
  118. t.expect('Signing transaction')
  119. t.do_comment(False,has_label=has_label)
  120. t.expect('Save signed transaction.*?\? \(Y/n\): ','y',regex=True)
  121. t.written_to_file('Signed transaction' + (' #' + tnum if tnum else ''), oo=True)
  122. return t
  123. def txsign( self, wf, txfile,
  124. pf = '',
  125. bumpf = '',
  126. save = True,
  127. has_label = False,
  128. extra_opts = [],
  129. extra_desc = '',
  130. view = 'n',
  131. dfl_wallet = False ):
  132. opts = extra_opts + ['-d',self.tmpdir,txfile] + ([wf] if wf else [])
  133. t = self.spawn('mmgen-txsign', opts, extra_desc)
  134. t.license()
  135. t.view_tx(view)
  136. wcls = Wallet if dfl_wallet else SeedSource.ext_to_type(get_extension(wf))
  137. pw = issubclass(wcls,SeedSourceEnc) and wcls != Brainwallet
  138. if pw:
  139. t.passphrase(wcls.desc,self.wpasswd)
  140. if save:
  141. self.txsign_end(t,has_label=has_label)
  142. else:
  143. t.do_comment(False,has_label=has_label)
  144. t.expect('Save signed transaction? (Y/n): ','n')
  145. t.req_exit_val = 1
  146. return t
  147. def ref_brain_chk(self,bw_file=ref_bw_file):
  148. wf = joinpath(ref_dir,bw_file)
  149. add_args = ['-l{}'.format(self.seed_len), '-p'+ref_bw_hash_preset]
  150. return self.walletchk(wf,pf=None,add_args=add_args,sid=self.ref_bw_seed_id)
  151. def walletchk(self,wf,pf,wcls=None,add_args=[],sid=None,extra_desc='',dfl_wallet=False):
  152. hp = self.hash_preset if hasattr(self,'hash_preset') else '1'
  153. wcls = wcls or SeedSource.ext_to_type(get_extension(wf))
  154. t = self.spawn('mmgen-walletchk',
  155. ([] if dfl_wallet else ['-i',wcls.fmt_codes[0]])
  156. + add_args + ['-p',hp]
  157. + ([wf] if wf else []),
  158. extra_desc=extra_desc)
  159. if wcls != IncogWalletHidden:
  160. t.expect("Getting {} from file '".format(wcls.desc))
  161. pw = issubclass(wcls,SeedSourceEnc) and wcls != Brainwallet
  162. if pw:
  163. t.passphrase(wcls.desc,self.wpasswd)
  164. t.expect(['Passphrase is OK', 'Passphrase.* are correct'],regex=True)
  165. chk = t.expect_getend('Valid {} for Seed ID '.format(wcls.desc))[:8]
  166. if sid: cmp_or_die(chk,sid)
  167. return t
  168. def addrgen(self,wf,
  169. pf = None,
  170. check_ref = False,
  171. ftype = 'addr',
  172. id_str = None,
  173. extra_args = [],
  174. mmtype = None,
  175. stdout = False,
  176. dfl_wallet = False ):
  177. passgen = ftype[:4] == 'pass'
  178. if not mmtype and not passgen:
  179. mmtype = self.segwit_mmtype
  180. cmd_pfx = (ftype,'pass')[passgen]
  181. t = self.spawn('mmgen-{}gen'.format(cmd_pfx),
  182. ['-d',self.tmpdir] + extra_args +
  183. ([],['--type='+str(mmtype)])[bool(mmtype)] +
  184. ([],['--stdout'])[stdout] +
  185. ([],[wf])[bool(wf)] +
  186. ([],[id_str])[bool(id_str)] +
  187. [getattr(self,'{}_idx_list'.format(cmd_pfx))],
  188. extra_desc='({})'.format(mmtype) if mmtype in ('segwit','bech32') else '')
  189. t.license()
  190. wcls = Wallet if dfl_wallet else SeedSource.ext_to_type(get_extension(wf))
  191. t.passphrase(wcls.desc,self.wpasswd)
  192. t.expect('Passphrase is OK')
  193. desc = ('address','password')[passgen]
  194. chk = t.expect_getend(r'Checksum for {} data .*?: '.format(desc),regex=True)
  195. if passgen:
  196. t.expect('Encrypt password list? (y/N): ','N')
  197. t.read() if stdout else t.written_to_file(('Addresses','Password list')[passgen])
  198. if check_ref:
  199. chk_ref = (self.chk_data[self.test_name] if passgen else
  200. self.chk_data[self.test_name][self.fork][g.testnet])
  201. cmp_or_die(chk,chk_ref,desc='{}list data checksum'.format(ftype))
  202. return t
  203. def keyaddrgen(self,wf,pf=None,check_ref=False,mmtype=None):
  204. if not mmtype:
  205. mmtype = self.segwit_mmtype
  206. args = ['-d',self.tmpdir,self.usr_rand_arg,wf,self.addr_idx_list]
  207. t = self.spawn('mmgen-keygen',
  208. ([],['--type='+str(mmtype)])[bool(mmtype)] + args,
  209. extra_desc='({})'.format(mmtype) if mmtype in ('segwit','bech32') else '')
  210. t.license()
  211. wcls = SeedSource.ext_to_type(get_extension(wf))
  212. t.passphrase(wcls.desc,self.wpasswd)
  213. chk = t.expect_getend(r'Checksum for key-address data .*?: ',regex=True)
  214. if check_ref:
  215. chk_ref = self.chk_data[self.test_name][self.fork][g.testnet]
  216. cmp_or_die(chk,chk_ref,desc='key-address list data checksum')
  217. t.expect('Encrypt key list? (y/N): ','y')
  218. t.usr_rand(self.usr_rand_chars)
  219. t.hash_preset('new key list','1')
  220. t.passphrase_new('new key list',self.kapasswd)
  221. t.written_to_file('Encrypted secret keys',oo=True)
  222. return t
  223. def _do_confirm_send(self,t,quiet=False,confirm_send=True,sure=True):
  224. if sure:
  225. t.expect('Are you sure you want to broadcast this')
  226. m = ('YES, I REALLY WANT TO DO THIS','YES')[quiet]
  227. t.expect("'{}' to confirm: ".format(m),('',m)[confirm_send]+'\n')