test.py 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
  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. test/test.py: Test suite for the MMGen suite
  20. """
  21. import sys,os
  22. pn = os.path.dirname(sys.argv[0])
  23. os.chdir(os.path.join(pn,os.pardir))
  24. sys.path.__setitem__(0,os.path.abspath(os.curdir))
  25. # Import these _after_ local path's been added to sys.path
  26. from mmgen.common import *
  27. from mmgen.test import *
  28. start_mscolor()
  29. scripts = (
  30. 'addrgen', 'addrimport', 'keygen',
  31. 'passchg', 'tool',
  32. 'txcreate', 'txsend', 'txsign',
  33. 'walletchk', 'walletconv', 'walletgen'
  34. )
  35. tb_cmd = 'scripts/traceback.py'
  36. hincog_fn = 'rand_data'
  37. hincog_bytes = 1024*1024
  38. hincog_offset = 98765
  39. hincog_seedlen = 256
  40. incog_id_fn = 'incog_id'
  41. non_mmgen_fn = 'btckey'
  42. pwfile = 'passwd_file'
  43. ref_dir = os.path.join('test','ref')
  44. ref_wallet_brainpass = 'abc'
  45. ref_wallet_hash_preset = '1'
  46. ref_wallet_incog_offset = 123
  47. ref_bw_hash_preset = '1'
  48. ref_bw_file = 'wallet.mmbrain'
  49. ref_bw_file_spc = 'wallet-spaced.mmbrain'
  50. ref_kafile_pass = 'kafile password'
  51. ref_kafile_hash_preset = '1'
  52. ref_enc_fn = 'sample-text.mmenc'
  53. tool_enc_passwd = "Scrypt it, don't hash it!"
  54. sample_text = \
  55. 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n'
  56. cfgs = {
  57. '1': {
  58. 'tmpdir': os.path.join('test','tmp1'),
  59. 'wpasswd': 'Dorian',
  60. 'kapasswd': 'Grok the blockchain',
  61. 'addr_idx_list': '12,99,5-10,5,12', # 8 addresses
  62. 'dep_generators': {
  63. pwfile: 'walletgen',
  64. 'mmdat': 'walletgen',
  65. 'addrs': 'addrgen',
  66. 'raw': 'txcreate',
  67. 'sig': 'txsign',
  68. 'mmwords': 'export_mnemonic',
  69. 'mmseed': 'export_seed',
  70. 'mmincog': 'export_incog',
  71. 'mmincox': 'export_incog_hex',
  72. hincog_fn: 'export_incog_hidden',
  73. incog_id_fn: 'export_incog_hidden',
  74. 'akeys.mmenc': 'keyaddrgen'
  75. },
  76. },
  77. '2': {
  78. 'tmpdir': os.path.join('test','tmp2'),
  79. 'wpasswd': 'Hodling away',
  80. 'addr_idx_list': '37,45,3-6,22-23', # 8 addresses
  81. 'seed_len': 128,
  82. 'dep_generators': {
  83. 'mmdat': 'walletgen2',
  84. 'addrs': 'addrgen2',
  85. 'raw': 'txcreate2',
  86. 'sig': 'txsign2',
  87. 'mmwords': 'export_mnemonic2',
  88. },
  89. },
  90. '3': {
  91. 'tmpdir': os.path.join('test','tmp3'),
  92. 'wpasswd': 'Major miner',
  93. 'addr_idx_list': '73,54,1022-1023,2-5', # 8 addresses
  94. 'dep_generators': {
  95. 'mmdat': 'walletgen3',
  96. 'addrs': 'addrgen3',
  97. 'raw': 'txcreate3',
  98. 'sig': 'txsign3'
  99. },
  100. },
  101. '4': {
  102. 'tmpdir': os.path.join('test','tmp4'),
  103. 'wpasswd': 'Hashrate rising',
  104. 'addr_idx_list': '63,1004,542-544,7-9', # 8 addresses
  105. 'seed_len': 192,
  106. 'dep_generators': {
  107. 'mmdat': 'walletgen4',
  108. 'mmbrain': 'walletgen4',
  109. 'addrs': 'addrgen4',
  110. 'raw': 'txcreate4',
  111. 'sig': 'txsign4',
  112. },
  113. 'bw_filename': 'brainwallet.mmbrain',
  114. 'bw_params': '192,1',
  115. },
  116. '5': {
  117. 'tmpdir': os.path.join('test','tmp5'),
  118. 'wpasswd': 'My changed password',
  119. 'hash_preset': '2',
  120. 'dep_generators': {
  121. 'mmdat': 'passchg',
  122. pwfile: 'passchg',
  123. },
  124. },
  125. '6': {
  126. 'name': 'reference wallet check (128-bit)',
  127. 'seed_len': 128,
  128. 'seed_id': 'FE3C6545',
  129. 'ref_bw_seed_id': '33F10310',
  130. 'addrfile_chk': 'B230 7526 638F 38CB 8FDC 8B76',
  131. 'keyaddrfile_chk': 'CF83 32FB 8A8B 08E2 0F00 D601',
  132. 'wpasswd': 'reference password',
  133. 'ref_wallet': 'FE3C6545-D782B529[128,1].mmdat',
  134. 'ic_wallet': 'FE3C6545-E29303EA-5E229E30[128,1].mmincog',
  135. 'ic_wallet_hex': 'FE3C6545-BC4BE3F2-32586837[128,1].mmincox',
  136. 'hic_wallet': 'FE3C6545-161E495F-BEB7548E[128:1].incog-offset123',
  137. 'hic_wallet_old': 'FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123',
  138. 'tmpdir': os.path.join('test','tmp6'),
  139. 'kapasswd': '',
  140. 'addr_idx_list': '1010,500-501,31-33,1,33,500,1011', # 8 addresses
  141. 'dep_generators': {
  142. 'mmdat': 'refwalletgen1',
  143. pwfile: 'refwalletgen1',
  144. 'addrs': 'refaddrgen1',
  145. 'akeys.mmenc': 'refkeyaddrgen1'
  146. },
  147. },
  148. '7': {
  149. 'name': 'reference wallet check (192-bit)',
  150. 'seed_len': 192,
  151. 'seed_id': '1378FC64',
  152. 'ref_bw_seed_id': 'CE918388',
  153. 'addrfile_chk': '8C17 A5FA 0470 6E89 3A87 8182',
  154. 'keyaddrfile_chk': '9648 5132 B98E 3AD9 6FC3 C5AD',
  155. 'wpasswd': 'reference password',
  156. 'ref_wallet': '1378FC64-6F0F9BB4[192,1].mmdat',
  157. 'ic_wallet': '1378FC64-2907DE97-F980D21F[192,1].mmincog',
  158. 'ic_wallet_hex': '1378FC64-4DCB5174-872806A7[192,1].mmincox',
  159. 'hic_wallet': '1378FC64-B55E9958-77256FC1[192:1].incog.offset123',
  160. 'hic_wallet_old': '1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123',
  161. 'tmpdir': os.path.join('test','tmp7'),
  162. 'kapasswd': '',
  163. 'addr_idx_list': '1010,500-501,31-33,1,33,500,1011', # 8 addresses
  164. 'dep_generators': {
  165. 'mmdat': 'refwalletgen2',
  166. pwfile: 'refwalletgen2',
  167. 'addrs': 'refaddrgen2',
  168. 'akeys.mmenc': 'refkeyaddrgen2'
  169. },
  170. },
  171. '8': {
  172. 'name': 'reference wallet check (256-bit)',
  173. 'seed_len': 256,
  174. 'seed_id': '98831F3A',
  175. 'ref_bw_seed_id': 'B48CD7FC',
  176. 'addrfile_chk': '6FEF 6FB9 7B13 5D91 854A 0BD3',
  177. 'keyaddrfile_chk': '9F2D D781 1812 8BAD C396 9DEB',
  178. 'wpasswd': 'reference password',
  179. 'ref_wallet': '98831F3A-27F2BF93[256,1].mmdat',
  180. 'ref_addrfile': '98831F3A[1,31-33,500-501,1010-1011].addrs',
  181. 'ref_keyaddrfile': '98831F3A[1,31-33,500-501,1010-1011].akeys.mmenc',
  182. 'ref_addrfile_chksum': '6FEF 6FB9 7B13 5D91 854A 0BD3',
  183. 'ref_keyaddrfile_chksum': '9F2D D781 1812 8BAD C396 9DEB',
  184. # 'ref_fake_unspent_data':'98831F3A_unspent.json',
  185. 'ref_tx_file': 'tx_FFB367[1.234].raw',
  186. 'ic_wallet': '98831F3A-5482381C-18460FB1[256,1].mmincog',
  187. 'ic_wallet_hex': '98831F3A-1630A9F2-870376A9[256,1].mmincox',
  188. 'hic_wallet': '98831F3A-F59B07A0-559CEF19[256:1].incog.offset123',
  189. 'hic_wallet_old': '98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123',
  190. 'tmpdir': os.path.join('test','tmp8'),
  191. 'kapasswd': '',
  192. 'addr_idx_list': '1010,500-501,31-33,1,33,500,1011', # 8 addresses
  193. 'dep_generators': {
  194. 'mmdat': 'refwalletgen3',
  195. pwfile: 'refwalletgen3',
  196. 'addrs': 'refaddrgen3',
  197. 'akeys.mmenc': 'refkeyaddrgen3'
  198. },
  199. },
  200. '9': {
  201. 'tmpdir': os.path.join('test','tmp9'),
  202. 'tool_enc_infn': 'tool_encrypt.in',
  203. # 'tool_enc_ref_infn': 'tool_encrypt_ref.in',
  204. 'wpasswd': 'reference password',
  205. 'dep_generators': {
  206. 'tool_encrypt.in': 'tool_encrypt',
  207. 'tool_encrypt.in.mmenc': 'tool_encrypt',
  208. # 'tool_encrypt_ref.in': 'tool_encrypt_ref',
  209. # 'tool_encrypt_ref.in.mmenc': 'tool_encrypt_ref',
  210. },
  211. },
  212. }
  213. from copy import deepcopy
  214. for a,b in ('6','11'),('7','12'),('8','13'):
  215. cfgs[b] = deepcopy(cfgs[a])
  216. cfgs[b]['tmpdir'] = os.path.join('test','tmp'+b)
  217. from collections import OrderedDict
  218. cmd_group = OrderedDict()
  219. cmd_group['help'] = OrderedDict([
  220. # test description depends
  221. ['helpscreens', (1,'help screens', [],1)],
  222. ])
  223. cmd_group['main'] = OrderedDict([
  224. ['walletgen', (1,'wallet generation', [[[],1]],1)],
  225. # ['walletchk', (1,'wallet check', [[['mmdat'],1]])],
  226. ['passchg', (5,'password, label and hash preset change',[[['mmdat',pwfile],1]],1)],
  227. ['walletchk_newpass',(5,'wallet check with new pw, label and hash preset',[[['mmdat',pwfile],5]],1)],
  228. ['addrgen', (1,'address generation', [[['mmdat',pwfile],1]],1)],
  229. ['addrimport', (1,'address import', [[['addrs'],1]],1)],
  230. ['txcreate', (1,'transaction creation', [[['addrs'],1]],1)],
  231. ['txsign', (1,'transaction signing', [[['mmdat','raw',pwfile],1]],1)],
  232. ['txsend', (1,'transaction sending', [[['sig'],1]])],
  233. ['export_seed', (1,'seed export to mmseed format', [[['mmdat'],1]])],
  234. ['export_mnemonic', (1,'seed export to mmwords format', [[['mmdat'],1]])],
  235. ['export_incog', (1,'seed export to mmincog format', [[['mmdat'],1]])],
  236. ['export_incog_hex',(1,'seed export to mmincog hex format', [[['mmdat'],1]])],
  237. ['export_incog_hidden',(1,'seed export to hidden mmincog format', [[['mmdat'],1]])],
  238. ['addrgen_seed', (1,'address generation from mmseed file', [[['mmseed','addrs'],1]])],
  239. ['addrgen_mnemonic',(1,'address generation from mmwords file',[[['mmwords','addrs'],1]])],
  240. ['addrgen_incog', (1,'address generation from mmincog file',[[['mmincog','addrs'],1]])],
  241. ['addrgen_incog_hex',(1,'address generation from mmincog hex file',[[['mmincox','addrs'],1]])],
  242. ['addrgen_incog_hidden',(1,'address generation from hidden mmincog file', [[[hincog_fn,'addrs'],1]])],
  243. ['keyaddrgen', (1,'key-address file generation', [[['mmdat',pwfile],1]])],
  244. ['txsign_keyaddr',(1,'transaction signing with key-address file', [[['akeys.mmenc','raw'],1]])],
  245. ['walletgen2',(2,'wallet generation (2), 128-bit seed', [])],
  246. ['addrgen2', (2,'address generation (2)', [[['mmdat'],2]])],
  247. ['txcreate2', (2,'transaction creation (2)', [[['addrs'],2]])],
  248. ['txsign2', (2,'transaction signing, two transactions',[[['mmdat','raw'],1],[['mmdat','raw'],2]])],
  249. ['export_mnemonic2', (2,'seed export to mmwords format (2)',[[['mmdat'],2]])],
  250. ['walletgen3',(3,'wallet generation (3)', [])],
  251. ['addrgen3', (3,'address generation (3)', [[['mmdat'],3]])],
  252. ['txcreate3', (3,'tx creation with inputs and outputs from two wallets', [[['addrs'],1],[['addrs'],3]])],
  253. ['txsign3', (3,'tx signing with inputs and outputs from two wallets',[[['mmdat'],1],[['mmdat','raw'],3]])],
  254. ['walletgen4',(4,'wallet generation (4) (brainwallet)', [])],
  255. ['addrgen4', (4,'address generation (4)', [[['mmdat'],4]])],
  256. ['txcreate4', (4,'tx creation with inputs and outputs from four seed sources, plus non-MMGen inputs and outputs', [[['addrs'],1],[['addrs'],2],[['addrs'],3],[['addrs'],4]])],
  257. ['txsign4', (4,'tx signing with inputs and outputs from incog file, mnemonic file, wallet and brainwallet, plus non-MMGen inputs and outputs', [[['mmincog'],1],[['mmwords'],2],[['mmdat'],3],[['mmbrain','raw'],4]])],
  258. ])
  259. cmd_group['tool'] = OrderedDict([
  260. ['tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [],1)],
  261. ['tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[cfgs['9']['tool_enc_infn'],cfgs['9']['tool_enc_infn']+'.mmenc'],9]],1)],
  262. # ['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])],
  263. ['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])],
  264. # ['pywallet', (9,"'mmgen-pywallet'", [],1)],
  265. ])
  266. # saved reference data
  267. cmd_group['ref'] = (
  268. # reading
  269. ('ref_wallet_chk', ([],'saved reference wallet')),
  270. ('ref_seed_chk', ([],'saved seed file')),
  271. ('ref_mn_chk', ([],'saved mnemonic file')),
  272. ('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
  273. ('ref_brain_chk', ([],'saved brainwallet')),
  274. # generating new reference ('abc' brainwallet) files:
  275. ('refwalletgen', ([],'gen new refwallet')),
  276. ('refaddrgen', (['mmdat',pwfile],'new refwallet addr chksum')),
  277. ('refkeyaddrgen', (['mmdat',pwfile],'new refwallet key-addr chksum'))
  278. )
  279. # misc. saved reference data
  280. cmd_group['ref_other'] = (
  281. ('ref_addrfile_chk', 'saved reference address file'),
  282. ('ref_keyaddrfile_chk','saved reference key-address file'),
  283. # Create the fake inputs:
  284. # ('txcreate8', 'transaction creation (8)'),
  285. ('ref_tx_chk', 'saved reference tx file'),
  286. ('ref_brain_chk_spc3', 'saved brainwallet (non-standard spacing)'),
  287. ('ref_tool_decrypt', 'decryption of saved MMGen-encrypted file'),
  288. )
  289. # mmgen-walletconv:
  290. cmd_group['conv_in'] = ( # reading
  291. ('ref_wallet_conv', 'conversion of saved reference wallet'),
  292. ('ref_mn_conv', 'conversion of saved mnemonic'),
  293. ('ref_seed_conv', 'conversion of saved seed file'),
  294. ('ref_brain_conv', 'conversion of ref brainwallet'),
  295. ('ref_incog_conv', 'conversion of saved incog wallet'),
  296. ('ref_incox_conv', 'conversion of saved hex incog wallet'),
  297. ('ref_hincog_conv', 'conversion of saved hidden incog wallet'),
  298. ('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)')
  299. )
  300. cmd_group['conv_out'] = ( # writing
  301. ('ref_wallet_conv_out', 'ref seed conversion to wallet'),
  302. ('ref_mn_conv_out', 'ref seed conversion to mnemonic'),
  303. ('ref_seed_conv_out', 'ref seed conversion to seed'),
  304. ('ref_incog_conv_out', 'ref seed conversion to incog data'),
  305. ('ref_incox_conv_out', 'ref seed conversion to hex incog data'),
  306. ('ref_hincog_conv_out', 'ref seed conversion to hidden incog data')
  307. )
  308. cmd_list = OrderedDict()
  309. for k in cmd_group: cmd_list[k] = []
  310. cmd_data = OrderedDict()
  311. for k,v in (
  312. ('help', ('help screens',[])),
  313. ('main', ('basic operations',[1,2,3,4,5])),
  314. ('tool', ('tools',[9]))
  315. ):
  316. cmd_data['info_'+k] = v
  317. for i in cmd_group[k]:
  318. cmd_list[k].append(i)
  319. cmd_data[i] = cmd_group[k][i]
  320. cmd_data['info_ref'] = 'reference data',[6,7,8]
  321. for a,b in cmd_group['ref']:
  322. for i,j in (1,128),(2,192),(3,256):
  323. k = a+str(i)
  324. cmd_list['ref'].append(k)
  325. cmd_data[k] = (5+i,'%s (%s-bit)' % (b[1],j),[[b[0],5+i]],1)
  326. cmd_data['info_ref_other'] = 'other reference data',[8]
  327. for a,b in cmd_group['ref_other']:
  328. cmd_list['ref_other'].append(a)
  329. cmd_data[a] = (8,b,[[[],8]],1)
  330. cmd_data['info_conv_in'] = 'wallet conversion from reference data',[11,12,13]
  331. for a,b in cmd_group['conv_in']:
  332. for i,j in (1,128),(2,192),(3,256):
  333. k = a+str(i)
  334. cmd_list['conv_in'].append(k)
  335. cmd_data[k] = (10+i,'%s (%s-bit)' % (b,j),[[[],10+i]],1)
  336. cmd_data['info_conv_out'] = 'wallet conversion to reference data',[11,12,13]
  337. for a,b in cmd_group['conv_out']:
  338. for i,j in (1,128),(2,192),(3,256):
  339. k = a+str(i)
  340. cmd_list['conv_out'].append(k)
  341. cmd_data[k] = (10+i,'%s (%s-bit)' % (b,j),[[[],10+i]],1)
  342. utils = {
  343. 'check_deps': 'check dependencies for specified command',
  344. 'clean': 'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
  345. }
  346. addrs_per_wallet = 8
  347. # total of two outputs must be < 10 BTC
  348. for k in cfgs:
  349. cfgs[k]['amts'] = [0,0]
  350. for idx,mod in (0,6),(1,4):
  351. cfgs[k]['amts'][idx] = '%s.%s' % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
  352. meta_cmds = OrderedDict([
  353. ['ref1', ('refwalletgen1','refaddrgen1','refkeyaddrgen1')],
  354. ['ref2', ('refwalletgen2','refaddrgen2','refkeyaddrgen2')],
  355. ['ref3', ('refwalletgen3','refaddrgen3','refkeyaddrgen3')],
  356. ['gen', ('walletgen','addrgen')],
  357. ['pass', ('passchg','walletchk_newpass')],
  358. ['tx', ('addrimport','txcreate','txsign','txsend')],
  359. ['export', [k for k in cmd_data if k[:7] == 'export_' and cmd_data[k][0] == 1]],
  360. ['gen_sp', [k for k in cmd_data if k[:8] == 'addrgen_' and cmd_data[k][0] == 1]],
  361. ['online', ('keyaddrgen','txsign_keyaddr')],
  362. ['2', [k for k in cmd_data if cmd_data[k][0] == 2]],
  363. ['3', [k for k in cmd_data if cmd_data[k][0] == 3]],
  364. ['4', [k for k in cmd_data if cmd_data[k][0] == 4]],
  365. ['saved_ref1', [c[0]+'1' for c in cmd_group['ref']]],
  366. ['saved_ref2', [c[0]+'2' for c in cmd_group['ref']]],
  367. ['saved_ref3', [c[0]+'3' for c in cmd_group['ref']]],
  368. ['saved_ref_other', [c[0] for c in cmd_group['ref_other']]],
  369. ['saved_ref_conv_in1', [c[0]+'1' for c in cmd_group['conv_in']]],
  370. ['saved_ref_conv_in2', [c[0]+'2' for c in cmd_group['conv_in']]],
  371. ['saved_ref_conv_in3', [c[0]+'3' for c in cmd_group['conv_in']]],
  372. ['saved_ref_conv_out1', [c[0]+'1' for c in cmd_group['conv_out']]],
  373. ['saved_ref_conv_out2', [c[0]+'2' for c in cmd_group['conv_out']]],
  374. ['saved_ref_conv_out3', [c[0]+'3' for c in cmd_group['conv_out']]],
  375. ])
  376. del cmd_group
  377. opts_data = {
  378. # 'sets': [('non_interactive',bool,'verbose',None)],
  379. 'desc': 'Test suite for the MMGen suite',
  380. 'usage':'[options] [command(s) or metacommand(s)]',
  381. 'options': """
  382. -h, --help Print this help message.
  383. -b, --buf-keypress Use buffered keypresses as with real human input.
  384. -d, --debug-scripts Turn on debugging output in executed scripts.
  385. -D, --direct-exec Bypass pexpect and execute a command directly (for
  386. debugging only).
  387. -e, --exact-output Show the exact output of the MMGen script(s) being run.
  388. -l, --list-cmds List and describe the commands in the test suite.
  389. -n, --names Display command names instead of descriptions.
  390. -I, --non-interactive Non-interactive operation (MS Windows mode)
  391. -p, --pause Pause between tests, resuming on keypress.
  392. -q, --quiet Produce minimal output. Suppress dependency info.
  393. -s, --system Test scripts and modules installed on system rather
  394. than those in the repo root.
  395. -t, --traceback Run the command inside the '{tb_cmd}' script.
  396. -v, --verbose Produce more verbose output.
  397. """.format(tb_cmd=tb_cmd),
  398. 'notes': """
  399. If no command is given, the whole suite of tests is run.
  400. """
  401. }
  402. cmd_args = opts.init(opts_data)
  403. if opt.system: sys.path.pop(0)
  404. ni = bool(opt.non_interactive)
  405. # Disable MS color in spawned scripts due to bad interactions
  406. os.environ['MMGEN_NOMSCOLOR'] = '1'
  407. os.environ['MMGEN_NOLICENSE'] = '1'
  408. if opt.debug_scripts: os.environ['MMGEN_DEBUG'] = '1'
  409. if opt.buf_keypress:
  410. send_delay = 0.3
  411. else:
  412. send_delay = 0
  413. os.environ['MMGEN_DISABLE_HOLD_PROTECT'] = '1'
  414. if opt.exact_output:
  415. def msg(s): pass
  416. vmsg = vmsg_r = msg_r = msg
  417. else:
  418. def msg(s): sys.stderr.write(s+'\n')
  419. def vmsg(s):
  420. if opt.verbose: sys.stderr.write(s+'\n')
  421. def msg_r(s): sys.stderr.write(s)
  422. def vmsg_r(s):
  423. if opt.verbose: sys.stderr.write(s)
  424. stderr_save = sys.stderr
  425. def silence():
  426. if not (opt.verbose or opt.exact_output):
  427. f = ('/dev/null','stderr.out')[sys.platform[:3]=='win']
  428. sys.stderr = open(f,'a')
  429. def end_silence():
  430. if not (opt.verbose or opt.exact_output):
  431. sys.stderr = stderr_save
  432. def errmsg(s): stderr_save.write(s+'\n')
  433. def errmsg_r(s): stderr_save.write(s)
  434. if opt.list_cmds:
  435. fs = ' {:<{w}} - {}'
  436. Msg(green('AVAILABLE COMMANDS:'))
  437. w = max([len(i) for i in cmd_data])
  438. for cmd in cmd_data:
  439. if cmd[:5] == 'info_':
  440. m = capfirst(cmd_data[cmd][0])
  441. msg(green(' %s:' % m))
  442. continue
  443. Msg(' '+fs.format(cmd,cmd_data[cmd][1],w=w))
  444. w = max([len(i) for i in meta_cmds])
  445. Msg(green('\nAVAILABLE METACOMMANDS:'))
  446. for cmd in meta_cmds:
  447. Msg(fs.format(cmd,' '.join(meta_cmds[cmd]),w=w))
  448. w = max([len(i) for i in cmd_list])
  449. Msg(green('\nAVAILABLE COMMAND GROUPS:'))
  450. for g in cmd_list:
  451. Msg(fs.format(g,' '.join(cmd_list[g]),w=w))
  452. Msg(green('\nAVAILABLE UTILITIES:'))
  453. w = max([len(i) for i in utils])
  454. for cmd in sorted(utils):
  455. Msg(fs.format(cmd,utils[cmd],w=w))
  456. sys.exit()
  457. import time,re
  458. try:
  459. import pexpect
  460. except: # Windows
  461. m1 = green('MS Windows detected (or missing pexpect module). Skipping some tests.\n')
  462. m2 = green('Interactive mode. User prompts will be ')
  463. m3 = grnbg('HIGHLIGHTED IN GREEN')
  464. m4 = green('.\nContinue?')
  465. ni = True
  466. if not keypress_confirm(m1+m2+m3+m4,default_yes=True):
  467. errmsg('Exiting at user request')
  468. sys.exit()
  469. def my_send(p,t,delay=send_delay,s=False):
  470. if delay: time.sleep(delay)
  471. ret = p.send(t) # returns num bytes written
  472. if delay: time.sleep(delay)
  473. if opt.verbose:
  474. ls = (' ','')[bool(opt.debug or not s)]
  475. es = (' ','')[bool(s)]
  476. msg('%sSEND %s%s' % (ls,es,yellow("'%s'"%t.replace('\n',r'\n'))))
  477. return ret
  478. def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
  479. quo = ('',"'")[type(s) == str]
  480. if opt.verbose: msg_r('EXPECT %s' % yellow(quo+str(s)+quo))
  481. else: msg_r('+')
  482. try:
  483. if s == '': ret = 0
  484. else:
  485. f = (p.expect_exact,p.expect)[bool(regex)]
  486. ret = f(s,timeout=60)
  487. except pexpect.TIMEOUT:
  488. errmsg(red('\nERROR. Expect %s%s%s timed out. Exiting' % (quo,s,quo)))
  489. sys.exit(1)
  490. if opt.debug or (opt.verbose and type(s) != str): msg_r(' ==> %s ' % ret)
  491. if ret == -1:
  492. errmsg('Error. Expect returned %s' % ret)
  493. sys.exit(1)
  494. else:
  495. if t == '':
  496. if not nonl: vmsg('')
  497. else:
  498. my_send(p,t,delay,s)
  499. return ret
  500. def get_file_with_ext(ext,mydir,delete=True,no_dot=False):
  501. dot = ('.','')[bool(no_dot)]
  502. flist = [os.path.join(mydir,f) for f in os.listdir(mydir)
  503. if f == ext or f[-len(dot+ext):] == dot+ext]
  504. if not flist: return False
  505. if len(flist) > 1:
  506. if delete:
  507. if not opt.quiet:
  508. msg("Multiple *.%s files in '%s' - deleting" % (ext,mydir))
  509. for f in flist: os.unlink(f)
  510. return False
  511. else:
  512. return flist[0]
  513. def get_addrfile_checksum(display=False):
  514. addrfile = get_file_with_ext('addrs',cfg['tmpdir'])
  515. silence()
  516. from mmgen.addr import AddrInfo
  517. chk = AddrInfo(addrfile).checksum
  518. if opt.verbose and display: msg('Checksum: %s' % cyan(chk))
  519. end_silence()
  520. return chk
  521. def verify_checksum_or_exit(checksum,chk):
  522. if checksum != chk:
  523. errmsg(red('Checksum error: %s' % chk))
  524. sys.exit(1)
  525. vmsg(green('Checksums match: %s') % (cyan(chk)))
  526. class MMGenExpect(object):
  527. def __init__(self,name,mmgen_cmd,cmd_args=[],extra_desc='',no_output=False):
  528. if not opt.system:
  529. mmgen_cmd = os.path.join(os.curdir,mmgen_cmd)
  530. desc = (cmd_data[name][1],name)[bool(opt.names)]
  531. if extra_desc: desc += ' ' + extra_desc
  532. if opt.verbose or opt.exact_output:
  533. sys.stderr.write(
  534. green('Testing: %s\nExecuting ' % desc) +
  535. cyan("'%s %s'\n" % (mmgen_cmd,' '.join(cmd_args)))
  536. )
  537. else:
  538. m = 'Testing %s: ' % desc
  539. msg_r((m,yellow(m))[ni])
  540. if opt.direct_exec or ni:
  541. msg('')
  542. from subprocess import call,check_output
  543. f = (call,check_output)[bool(no_output)]
  544. ret = f(['python', mmgen_cmd] + cmd_args)
  545. if f == call and ret != 0:
  546. m = 'Warning: process returned a non-zero exit status (%s)'
  547. msg(red(m % ret))
  548. else:
  549. if opt.traceback:
  550. cmd_args = [mmgen_cmd] + cmd_args
  551. mmgen_cmd = tb_cmd
  552. self.p = pexpect.spawn(mmgen_cmd,cmd_args)
  553. if opt.exact_output: self.p.logfile = sys.stdout
  554. def license(self):
  555. if 'MMGEN_NOLICENSE' in os.environ: return
  556. p = "'w' for conditions and warranty info, or 'c' to continue: "
  557. my_expect(self.p,p,'c')
  558. def label(self,label='Test Label'):
  559. p = 'Enter a wallet label, or hit ENTER for no label: '
  560. my_expect(self.p,p,label+'\n')
  561. def usr_rand_out(self,saved=False):
  562. m = '%suser-supplied entropy' % (('','saved ')[saved])
  563. my_expect(self.p,'Generating encryption key from OS random data plus ' + m)
  564. def usr_rand(self,num_chars):
  565. rand_chars = list(getrandstr(num_chars,no_space=True))
  566. my_expect(self.p,'symbols left: ','x')
  567. try:
  568. vmsg_r('SEND ')
  569. while self.p.expect('left: ',0.1) == 0:
  570. ch = rand_chars.pop(0)
  571. msg_r(yellow(ch)+' ' if opt.verbose else '+')
  572. self.p.send(ch)
  573. except:
  574. vmsg('EOT')
  575. my_expect(self.p,'ENTER to continue: ','\n')
  576. def passphrase_new(self,desc,passphrase):
  577. my_expect(self.p,('Enter passphrase for %s: ' % desc), passphrase+'\n')
  578. my_expect(self.p,'Repeat passphrase: ', passphrase+'\n')
  579. def passphrase(self,desc,passphrase,pwtype=''):
  580. if pwtype: pwtype += ' '
  581. my_expect(self.p,('Enter %spassphrase for %s.*?: ' % (pwtype,desc)),
  582. passphrase+'\n',regex=True)
  583. def hash_preset(self,desc,preset=''):
  584. my_expect(self.p,('Enter hash preset for %s' % desc))
  585. my_expect(self.p,('or hit ENTER .*?:'), str(preset)+'\n',regex=True)
  586. def written_to_file(self,desc,overwrite_unlikely=False,query='Overwrite? ',oo=False):
  587. s1 = '%s written to file ' % desc
  588. s2 = query + "Type uppercase 'YES' to confirm: "
  589. ret = my_expect(self.p,([s1,s2],s1)[overwrite_unlikely])
  590. if ret == 1:
  591. my_send(self.p,'YES\n')
  592. # if oo:
  593. outfile = self.expect_getend("Overwriting file '").rstrip("'")
  594. return outfile
  595. # else:
  596. # ret = my_expect(self.p,s1)
  597. outfile = self.p.readline().strip().strip("'")
  598. vmsg('%s file: %s' % (desc,cyan(outfile.replace("'",''))))
  599. return outfile
  600. def no_overwrite(self):
  601. self.expect("Overwrite? Type uppercase 'YES' to confirm: ",'\n')
  602. self.expect('Exiting at user request')
  603. def tx_view(self):
  604. my_expect(self.p,r'View .*?transaction.*? \(y\)es, \(N\)o, pager \(v\)iew.*?: ','\n',regex=True)
  605. def expect_getend(self,s,regex=False):
  606. ret = self.expect(s,regex=regex,nonl=True)
  607. end = self.readline().strip()
  608. vmsg(' ==> %s' % cyan(end))
  609. return end
  610. def interactive(self):
  611. return self.p.interact()
  612. def logfile(self,arg):
  613. self.p.logfile = arg
  614. def expect(self,*args,**kwargs):
  615. return my_expect(self.p,*args,**kwargs)
  616. def send(self,*args,**kwargs):
  617. return my_send(self.p,*args,**kwargs)
  618. def readline(self):
  619. return self.p.readline()
  620. def close(self):
  621. return self.p.close()
  622. def readlines(self):
  623. return [l.rstrip()+'\n' for l in self.p.readlines()]
  624. def read(self,n=None):
  625. return self.p.read(n)
  626. from decimal import Decimal
  627. from mmgen.bitcoin import verify_addr
  628. def add_fake_unspent_entry(out,address,comment):
  629. out.append({
  630. 'account': unicode(comment),
  631. 'vout': int(getrandnum(4) % 8),
  632. 'txid': unicode(hexlify(os.urandom(32))),
  633. 'amount': Decimal('%s.%s' % (10+(getrandnum(4) % 40), getrandnum(4) % 100000000)),
  634. 'address': address,
  635. 'spendable': False,
  636. 'scriptPubKey': ('76a914'+verify_addr(address,return_hex=True)+'88ac'),
  637. 'confirmations': getrandnum(4) % 500
  638. })
  639. def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input=''):
  640. out = []
  641. for s in tx_data:
  642. sid = tx_data[s]['sid']
  643. a = adata.addrinfo(sid)
  644. for n,(idx,btcaddr) in enumerate(a.addrpairs(),1):
  645. lbl = ('',' addr %02i' % n)[bool(n%3)]
  646. add_fake_unspent_entry(out,btcaddr,'%s:%s%s' % (sid,idx,lbl))
  647. if non_mmgen_input:
  648. from mmgen.bitcoin import privnum2addr,hextowif
  649. privnum = getrandnum(32)
  650. btcaddr = privnum2addr(privnum,compressed=True)
  651. of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
  652. write_data_to_file(of, hextowif('{:064x}'.format(privnum),
  653. compressed=True)+'\n','compressed bitcoin key',silent=True)
  654. add_fake_unspent_entry(out,btcaddr,'Non-MMGen address')
  655. # msg('\n'.join([repr(o) for o in out])); sys.exit()
  656. write_data_to_file(unspent_data_file,repr(out),'Unspent outputs',silent=True)
  657. def add_comments_to_addr_file(addrfile,outfile):
  658. silence()
  659. msg(green("Adding comments to address file '%s'" % addrfile))
  660. from mmgen.addr import AddrInfo
  661. a = AddrInfo(addrfile)
  662. for n,idx in enumerate(a.idxs(),1):
  663. if n % 2: a.set_comment(idx,'Test address %s' % n)
  664. write_data_to_file(outfile,a.fmt_data(enable_comments=True),silent=True)
  665. end_silence()
  666. def make_brainwallet_file(fn):
  667. # Print random words with random whitespace in between
  668. from mmgen.mn_tirosh import words
  669. wl = words.split()
  670. nwords,ws_list,max_spaces = 10,' \n',5
  671. def rand_ws_seq():
  672. nchars = getrandnum(1) % max_spaces + 1
  673. return ''.join([ws_list[getrandnum(1)%len(ws_list)] for i in range(nchars)])
  674. rand_pairs = [wl[getrandnum(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
  675. d = ''.join(rand_pairs).rstrip() + '\n'
  676. if opt.verbose: msg_r('Brainwallet password:\n%s' % cyan(d))
  677. write_data_to_file(fn,d,'brainwallet password',silent=True)
  678. def do_between():
  679. if opt.pause:
  680. if keypress_confirm(green('Continue?'),default_yes=True):
  681. if opt.verbose or opt.exact_output: sys.stderr.write('\n')
  682. else:
  683. errmsg('Exiting at user request')
  684. sys.exit()
  685. elif opt.verbose or opt.exact_output:
  686. sys.stderr.write('\n')
  687. rebuild_list = OrderedDict()
  688. def check_needs_rerun(
  689. ts,
  690. cmd,
  691. build=False,
  692. root=True,
  693. force_delete=False,
  694. dpy=False
  695. ):
  696. rerun = (False,True)[root] # force_delete is not passed to recursive call
  697. fns = []
  698. if force_delete or not root:
  699. # does cmd produce a needed dependency(ies)?
  700. ret = ts.get_num_exts_for_cmd(cmd,dpy)
  701. if ret:
  702. for ext in ret[1]:
  703. fn = get_file_with_ext(ext,cfgs[ret[0]]['tmpdir'],delete=build)
  704. if fn:
  705. if force_delete: os.unlink(fn)
  706. else: fns.append(fn)
  707. else: rerun = True
  708. fdeps = ts.generate_file_deps(cmd)
  709. cdeps = ts.generate_cmd_deps(fdeps)
  710. for fn in fns:
  711. my_age = os.stat(fn).st_mtime
  712. for num,ext in fdeps:
  713. f = get_file_with_ext(ext,cfgs[num]['tmpdir'],delete=build)
  714. if f and os.stat(f).st_mtime > my_age: rerun = True
  715. for cdep in cdeps:
  716. if check_needs_rerun(ts,cdep,build=build,root=False,dpy=cmd): rerun = True
  717. if build:
  718. if rerun:
  719. for fn in fns:
  720. if not root: os.unlink(fn)
  721. ts.do_cmd(cmd)
  722. if not root: do_between()
  723. else:
  724. # If prog produces multiple files:
  725. if cmd not in rebuild_list or rerun == True:
  726. rebuild_list[cmd] = (rerun,fns[0] if fns else '') # FIX
  727. return rerun
  728. def refcheck(desc,chk,refchk):
  729. vmsg("Comparing %s '%s' to stored reference" % (desc,chk))
  730. if chk == refchk:
  731. ok()
  732. else:
  733. if not opt.verbose: errmsg('')
  734. errmsg(red("""
  735. Fatal error - %s '%s' does not match reference value '%s'. Aborting test
  736. """.strip() % (desc,chk,refchk)))
  737. sys.exit(3)
  738. def check_deps(cmds):
  739. if len(cmds) != 1:
  740. die(1,'Usage: %s check_deps <command>' % g.prog_name)
  741. cmd = cmds[0]
  742. if cmd not in cmd_data:
  743. die(1,"'%s': unrecognized command" % cmd)
  744. if not opt.quiet:
  745. msg("Checking dependencies for '%s'" % (cmd))
  746. check_needs_rerun(ts,cmd,build=False)
  747. w = max(len(i) for i in rebuild_list) + 1
  748. for cmd in rebuild_list:
  749. c = rebuild_list[cmd]
  750. m = 'Rebuild' if (c[0] and c[1]) else 'Build' if c[0] else 'OK'
  751. msg('cmd {:<{w}} {}'.format(cmd+':', m, w=w))
  752. # mmsg(cmd,c)
  753. def clean(usr_dirs=[]):
  754. all_dirs = MMGenTestSuite().list_tmp_dirs()
  755. dirs = (usr_dirs or all_dirs)
  756. for d in sorted(dirs):
  757. if str(d) in all_dirs:
  758. cleandir(all_dirs[str(d)])
  759. else:
  760. die(1,'%s: invalid directory number' % d)
  761. class MMGenTestSuite(object):
  762. def __init__(self):
  763. pass
  764. def list_tmp_dirs(self):
  765. d = {}
  766. for k in cfgs: d[k] = cfgs[k]['tmpdir']
  767. return d
  768. def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
  769. num = str(cmd_data[cmd][0])
  770. dgl = cfgs[num]['dep_generators']
  771. # mmsg(num,cmd,dgl)
  772. if cmd in dgl.values():
  773. exts = [k for k in dgl if dgl[k] == cmd]
  774. return (num,exts)
  775. else:
  776. return None
  777. def do_cmd(self,cmd):
  778. if ni and (len(cmd_data[cmd]) < 4 or cmd_data[cmd][3] != 1): return
  779. d = [(str(num),ext) for exts,num in cmd_data[cmd][2] for ext in exts]
  780. al = [get_file_with_ext(ext,cfgs[num]['tmpdir']) for num,ext in d]
  781. global cfg
  782. cfg = cfgs[str(cmd_data[cmd][0])]
  783. self.__class__.__dict__[cmd](*([self,cmd] + al))
  784. def generate_file_deps(self,cmd):
  785. return [(str(n),e) for exts,n in cmd_data[cmd][2] for e in exts]
  786. def generate_cmd_deps(self,fdeps):
  787. return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps]
  788. def helpscreens(self,name):
  789. for s in scripts:
  790. t = MMGenExpect(name,('mmgen-'+s),['--help'],
  791. extra_desc='(mmgen-%s)'%s,no_output=True)
  792. if not ni:
  793. t.read(); ok()
  794. def walletgen(self,name,seed_len=None):
  795. write_to_tmpfile(cfg,pwfile,cfg['wpasswd']+'\n')
  796. add_args = (['-r10'],
  797. ['-q','-r0','-L','NI Wallet','-P',get_tmpfile_fn(cfg,pwfile)])[bool(ni)]
  798. args = ['-d',cfg['tmpdir'],'-p1']
  799. if seed_len: args += ['-l',str(seed_len)]
  800. t = MMGenExpect(name,'mmgen-walletgen', args + add_args)
  801. if ni: return
  802. t.license()
  803. t.usr_rand(10)
  804. t.passphrase_new('new MMGen wallet',cfg['wpasswd'])
  805. t.label()
  806. t.written_to_file('MMGen wallet')
  807. ok()
  808. def brainwalletgen_ref(self,name):
  809. sl_arg = '-l%s' % cfg['seed_len']
  810. hp_arg = '-p%s' % ref_wallet_hash_preset
  811. label = "test.py ref. wallet (pw '%s', seed len %s)" \
  812. % (ref_wallet_brainpass,cfg['seed_len'])
  813. bf = 'ref.mmbrain'
  814. args = ['-d',cfg['tmpdir'],hp_arg,sl_arg,'-ib','-L',label]
  815. write_to_tmpfile(cfg,bf,ref_wallet_brainpass)
  816. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  817. if ni:
  818. add_args = ['-r0', '-q', '-P%s' % get_tmpfile_fn(cfg,pwfile),
  819. get_tmpfile_fn(cfg,bf)]
  820. else:
  821. add_args = ['-r10']
  822. t = MMGenExpect(name,'mmgen-walletconv', args + add_args)
  823. if ni: return
  824. t.license()
  825. t.expect('Enter brainwallet: ', ref_wallet_brainpass+'\n')
  826. t.passphrase_new('new MMGen wallet',cfg['wpasswd'])
  827. t.usr_rand(10)
  828. sid = t.written_to_file('MMGen wallet').split('-')[0].split('/')[-1]
  829. refcheck('Seed ID',sid,cfg['seed_id'])
  830. def refwalletgen(self,name): self.brainwalletgen_ref(name)
  831. def passchg(self,name,wf,pf):
  832. # ni: reuse password, since there's no way to change it non-interactively
  833. silence()
  834. write_to_tmpfile(cfg,pwfile,get_data_from_file(pf))
  835. end_silence()
  836. add_args = (['-r16'],['-q','-r0','-P',pf])[bool(ni)]
  837. t = MMGenExpect(name,'mmgen-passchg', add_args +
  838. ['-d',cfg['tmpdir'],'-p','2','-L','New Label',wf])
  839. if ni: return
  840. t.license()
  841. t.passphrase('MMGen wallet',cfgs['1']['wpasswd'],pwtype='old')
  842. t.expect_getend('Hash preset changed to ')
  843. t.passphrase('MMGen wallet',cfg['wpasswd'],pwtype='new')
  844. t.expect('Repeat passphrase: ',cfg['wpasswd']+'\n')
  845. t.usr_rand(16)
  846. t.expect_getend('Label changed to ')
  847. # t.expect_getend('Key ID changed: ')
  848. t.written_to_file('MMGen wallet')
  849. ok()
  850. def walletchk(self,name,wf,pf,desc='MMGen wallet',
  851. add_args=[],sid=None,pw=False,extra_desc=''):
  852. args = ([],['-P',pf,'-q'])[bool(ni and pf)]
  853. hp = cfg['hash_preset'] if 'hash_preset' in cfg else '1'
  854. wf_arg = ([],[wf])[bool(wf)]
  855. t = MMGenExpect(name,'mmgen-walletchk',
  856. add_args+args+['-p',hp]+wf_arg,
  857. extra_desc=extra_desc)
  858. if ni:
  859. if sid:
  860. n = (' should be','')[desc=='MMGen wallet']
  861. m = grnbg('Seed ID%s:' % n)
  862. msg(grnbg('%s %s' % (m,cyan(sid))))
  863. return
  864. if desc != 'hidden incognito data':
  865. t.expect("Getting %s from file '%s'" % (desc,wf))
  866. if pw:
  867. t.passphrase(desc,cfg['wpasswd'])
  868. t.expect(
  869. ['Passphrase is OK', 'Passphrase.* are correct'],
  870. regex=True
  871. )
  872. chk = t.expect_getend('Valid %s for Seed ID ' % desc)[:8]
  873. if sid: cmp_or_die(chk,sid)
  874. else: ok()
  875. def walletchk_newpass (self,name,wf,pf):
  876. return self.walletchk(name,wf,pf,pw=True)
  877. def addrgen(self,name,wf,pf,check_ref=False):
  878. add_args = ([],['-P',pf,'-q'])[ni]
  879. t = MMGenExpect(name,'mmgen-addrgen', add_args +
  880. ['-d',cfg['tmpdir'],wf,cfg['addr_idx_list']])
  881. if ni: return
  882. t.license()
  883. t.passphrase('MMGen wallet',cfg['wpasswd'])
  884. t.expect('Passphrase is OK')
  885. chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
  886. if check_ref:
  887. refcheck('address data checksum',chk,cfg['addrfile_chk'])
  888. return
  889. t.written_to_file('Addresses',oo=True)
  890. ok()
  891. def refaddrgen(self,name,wf,pf):
  892. d = ' (%s-bit seed)' % cfg['seed_len']
  893. self.addrgen(name,wf,pf=pf,check_ref=True)
  894. def addrimport(self,name,addrfile):
  895. add_args = ([],['-q','-t'])[ni]
  896. outfile = os.path.join(cfg['tmpdir'],'addrfile_w_comments')
  897. add_comments_to_addr_file(addrfile,outfile)
  898. t = MMGenExpect(name,'mmgen-addrimport', add_args + [outfile])
  899. if ni: return
  900. t.expect_getend(r'Checksum for address data .*\[.*\]: ',regex=True)
  901. t.expect_getend('Validating addresses...OK. ')
  902. t.expect("Type uppercase 'YES' to confirm: ",'\n')
  903. vmsg('This is a simulation, so no addresses were actually imported into the tracking\nwallet')
  904. ok()
  905. def txcreate(self,name,addrfile):
  906. self.txcreate_common(name,sources=['1'])
  907. def txcreate_common(self,name,sources=['1'],non_mmgen_input=''):
  908. if opt.verbose or opt.exact_output:
  909. sys.stderr.write(green('Generating fake transaction info\n'))
  910. silence()
  911. from mmgen.addr import AddrInfo,AddrInfoList
  912. tx_data,ail = {},AddrInfoList()
  913. for s in sources:
  914. afile = get_file_with_ext('addrs',cfgs[s]['tmpdir'])
  915. ai = AddrInfo(afile)
  916. ail.add(ai)
  917. aix = parse_addr_idxs(cfgs[s]['addr_idx_list'])
  918. if len(aix) != addrs_per_wallet:
  919. errmsg(red('Address index list length != %s: %s' %
  920. (addrs_per_wallet,repr(aix))))
  921. sys.exit()
  922. tx_data[s] = {
  923. 'addrfile': afile,
  924. 'chk': ai.checksum,
  925. 'sid': ai.seed_id,
  926. 'addr_idxs': aix[-2:],
  927. }
  928. unspent_data_file = os.path.join(cfg['tmpdir'],'unspent.json')
  929. create_fake_unspent_data(ail,unspent_data_file,tx_data,non_mmgen_input)
  930. # make the command line
  931. from mmgen.bitcoin import privnum2addr
  932. btcaddr = privnum2addr(getrandnum(32),compressed=True)
  933. cmd_args = ['-d',cfg['tmpdir']]
  934. for num in tx_data:
  935. s = tx_data[num]
  936. cmd_args += [
  937. '%s:%s,%s' % (s['sid'],s['addr_idxs'][0],cfgs[num]['amts'][0]),
  938. ]
  939. # + one BTC address
  940. # + one change address and one BTC address
  941. if num is tx_data.keys()[-1]:
  942. cmd_args += ['%s:%s' % (s['sid'],s['addr_idxs'][1])]
  943. cmd_args += ['%s,%s' % (btcaddr,cfgs[num]['amts'][1])]
  944. for num in tx_data: cmd_args += [tx_data[num]['addrfile']]
  945. os.environ['MMGEN_BOGUS_WALLET_DATA'] = unspent_data_file
  946. end_silence()
  947. if opt.verbose or opt.exact_output: sys.stderr.write('\n')
  948. add_args = ([],['-q'])[ni]
  949. if ni:
  950. m = '\nAnswer the interactive prompts as follows:\n' + \
  951. " 'y', 'y', 'q', '1-8'<ENTER>, ENTER, ENTER, ENTER, 'y'"
  952. msg(grnbg(m))
  953. t = MMGenExpect(name,'mmgen-txcreate',add_args + cmd_args)
  954. if ni: return
  955. t.license()
  956. for num in tx_data:
  957. t.expect_getend('Getting address data from file ')
  958. chk=t.expect_getend(r'Checksum for address data .*?: ',regex=True)
  959. verify_checksum_or_exit(tx_data[num]['chk'],chk)
  960. # not in tracking wallet warning, (1 + num sources) times
  961. if t.expect(['Continue anyway? (y/N): ',
  962. 'Unable to connect to bitcoind']) == 0:
  963. t.send('y')
  964. else:
  965. errmsg(red('Error: unable to connect to bitcoind. Exiting'))
  966. sys.exit(1)
  967. for num in tx_data:
  968. t.expect('Continue anyway? (y/N): ','y')
  969. t.expect(r"'q' = quit sorting, .*?: ",'M', regex=True)
  970. t.expect(r"'q' = quit sorting, .*?: ",'q', regex=True)
  971. outputs_list = [addrs_per_wallet*i + 1 for i in range(len(tx_data))]
  972. if non_mmgen_input: outputs_list.append(len(tx_data)*addrs_per_wallet + 1)
  973. t.expect('Enter a range or space-separated list of outputs to spend: ',
  974. ' '.join([str(i) for i in outputs_list])+'\n')
  975. if non_mmgen_input: t.expect('Accept? (y/N): ','y')
  976. t.expect('OK? (Y/n): ','y') # fee OK?
  977. t.expect('OK? (Y/n): ','y') # change OK?
  978. t.expect('Add a comment to transaction? (y/N): ','\n')
  979. t.tx_view()
  980. t.expect('Save transaction? (y/N): ','y')
  981. t.written_to_file('Transaction')
  982. ok()
  983. def txsign_end(self,t,tnum=None):
  984. t.expect('Signing transaction')
  985. t.expect('Edit transaction comment? (y/N): ','\n')
  986. t.expect('Save signed transaction? (Y/n): ','y')
  987. add = ' #' + tnum if tnum else ''
  988. t.written_to_file('Signed transaction' + add, oo=True)
  989. def txsign(self,name,txfile,wf,pf='',save=True):
  990. add_args = ([],['-q','-P',pf])[ni]
  991. if ni:
  992. m = '\nAnswer the interactive prompts as follows:\n ENTER, ENTER, ENTER'
  993. msg(grnbg(m))
  994. t = MMGenExpect(name,'mmgen-txsign', add_args+['-d',cfg['tmpdir'],txfile,wf])
  995. if ni: return
  996. t.license()
  997. t.tx_view()
  998. t.passphrase('MMGen wallet',cfg['wpasswd'])
  999. if save:
  1000. self.txsign_end(t)
  1001. else:
  1002. t.expect('Edit transaction comment? (y/N): ','\n')
  1003. t.close()
  1004. ok()
  1005. def txsend(self,name,sigfile):
  1006. t = MMGenExpect(name,'mmgen-txsend', ['-d',cfg['tmpdir'],sigfile])
  1007. t.license()
  1008. t.tx_view()
  1009. t.expect('Edit transaction comment? (y/N): ','\n')
  1010. t.expect('broadcast this transaction to the network?')
  1011. t.expect("'YES, I REALLY WANT TO DO THIS' to confirm: ",'\n')
  1012. t.expect('Exiting at user request')
  1013. vmsg('This is a simulation; no transaction was sent')
  1014. ok()
  1015. def walletconv_export(self,name,wf,desc,uargs=[],out_fmt='w',pw=False):
  1016. opts = ['-d',cfg['tmpdir'],'-o',out_fmt] + uargs + [wf]
  1017. t = MMGenExpect(name,'mmgen-walletconv',opts)
  1018. t.license()
  1019. t.passphrase('MMGen wallet',cfg['wpasswd'])
  1020. if pw:
  1021. t.passphrase_new('new '+desc,cfg['wpasswd'])
  1022. t.usr_rand(10)
  1023. if ' '.join(desc.split()[-2:]) == 'incognito data':
  1024. t.expect('Generating encryption key from OS random data ')
  1025. t.expect('Generating encryption key from OS random data ')
  1026. ic_id = t.expect_getend('New Incog Wallet ID: ')
  1027. t.expect('Generating encryption key from OS random data ')
  1028. if desc == 'hidden incognito data':
  1029. write_to_tmpfile(cfg,incog_id_fn,ic_id)
  1030. ret = t.expect(['Create? (Y/n): ',"'YES' to confirm: "])
  1031. if ret == 0:
  1032. t.send('\n')
  1033. t.expect('Enter file size: ',str(hincog_bytes)+'\n')
  1034. else:
  1035. t.send('YES\n')
  1036. if out_fmt == 'w': t.label()
  1037. return t.written_to_file(capfirst(desc),oo=True)
  1038. def export_seed(self,name,wf,desc='seed data',out_fmt='seed'):
  1039. f = self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt)
  1040. silence()
  1041. msg('%s: %s' % (capfirst(desc),cyan(get_data_from_file(f,desc))))
  1042. end_silence()
  1043. ok()
  1044. def export_mnemonic(self,name,wf):
  1045. self.export_seed(name,wf,desc='mnemonic data',out_fmt='words')
  1046. def export_incog(self,name,wf,desc='incognito data',out_fmt='i',add_args=[]):
  1047. uargs = ['-p1','-r10'] + add_args
  1048. self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt,uargs=uargs,pw=True)
  1049. ok()
  1050. def export_incog_hex(self,name,wf):
  1051. self.export_incog(name,wf,desc='hex incognito data',out_fmt='xi')
  1052. # TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
  1053. def export_incog_hidden(self,name,wf):
  1054. rf = os.path.join(cfg['tmpdir'],hincog_fn)
  1055. add_args = ['-J','%s,%s'%(rf,hincog_offset)]
  1056. self.export_incog(
  1057. name,wf,desc='hidden incognito data',out_fmt='hi',add_args=add_args)
  1058. def addrgen_seed(self,name,wf,foo,desc='seed data',in_fmt='seed'):
  1059. stdout = (False,True)[desc=='seed data'] #capture output to screen once
  1060. add_arg = ([],['-S'])[bool(stdout)]
  1061. t = MMGenExpect(name,'mmgen-addrgen', add_arg +
  1062. ['-i'+in_fmt,'-d',cfg['tmpdir'],wf,cfg['addr_idx_list']])
  1063. t.license()
  1064. t.expect_getend('Valid %s for Seed ID ' % desc)
  1065. vmsg('Comparing generated checksum with checksum from previous address file')
  1066. chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
  1067. if stdout: t.read()
  1068. verify_checksum_or_exit(get_addrfile_checksum(),chk)
  1069. # t.no_overwrite()
  1070. ok()
  1071. def addrgen_mnemonic(self,name,wf,foo):
  1072. self.addrgen_seed(name,wf,foo,desc='mnemonic data',in_fmt='words')
  1073. def addrgen_incog(self,name,wf=[],foo='',in_fmt='i',desc='incognito data',args=[]):
  1074. t = MMGenExpect(name,'mmgen-addrgen', args+['-i'+in_fmt,'-d',cfg['tmpdir']]+
  1075. ([],[wf])[bool(wf)] + [cfg['addr_idx_list']])
  1076. t.license()
  1077. t.expect_getend('Incog Wallet ID: ')
  1078. t.hash_preset(desc,'1')
  1079. t.passphrase('%s \w{8}' % desc, cfg['wpasswd'])
  1080. vmsg('Comparing generated checksum with checksum from address file')
  1081. chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
  1082. t.close()
  1083. verify_checksum_or_exit(get_addrfile_checksum(),chk)
  1084. # t.no_overwrite()
  1085. ok()
  1086. def addrgen_incog_hex(self,name,wf,foo):
  1087. self.addrgen_incog(name,wf,'',in_fmt='xi',desc='hex incognito data')
  1088. def addrgen_incog_hidden(self,name,wf,foo):
  1089. rf = os.path.join(cfg['tmpdir'],hincog_fn)
  1090. self.addrgen_incog(name,[],'',in_fmt='hi',desc='hidden incognito data',
  1091. args=['-H','%s,%s'%(rf,hincog_offset),'-l',str(hincog_seedlen)])
  1092. def keyaddrgen(self,name,wf,pf,check_ref=False):
  1093. args = ['-d',cfg['tmpdir'],wf,cfg['addr_idx_list']]
  1094. if ni:
  1095. m = "\nAnswer 'n' at the interactive prompt"
  1096. msg(grnbg(m))
  1097. args = ['-q','-P',pf] + args
  1098. t = MMGenExpect(name,'mmgen-keygen', args)
  1099. if ni: return
  1100. t.license()
  1101. t.passphrase('MMGen wallet',cfg['wpasswd'])
  1102. chk = t.expect_getend(r'Checksum for key-address data .*?: ',regex=True)
  1103. if check_ref:
  1104. refcheck('key-address data checksum',chk,cfg['keyaddrfile_chk'])
  1105. return
  1106. t.expect('Encrypt key list? (y/N): ','y')
  1107. t.hash_preset('new key list','1')
  1108. t.passphrase_new('new key list',cfg['kapasswd'])
  1109. t.written_to_file('Secret keys',oo=True)
  1110. ok()
  1111. def refkeyaddrgen(self,name,wf,pf):
  1112. self.keyaddrgen(name,wf,pf,check_ref=True)
  1113. def txsign_keyaddr(self,name,keyaddr_file,txfile):
  1114. t = MMGenExpect(name,'mmgen-txsign', ['-d',cfg['tmpdir'],'-M',keyaddr_file,txfile])
  1115. t.license()
  1116. t.hash_preset('key-address file','1')
  1117. t.passphrase('key-address file',cfg['kapasswd'])
  1118. t.expect('Check key-to-address validity? (y/N): ','y')
  1119. t.tx_view()
  1120. self.txsign_end(t)
  1121. ok()
  1122. def walletgen2(self,name):
  1123. self.walletgen(name,seed_len=128)
  1124. def addrgen2(self,name,wf):
  1125. self.addrgen(name,wf,pf='')
  1126. def txcreate2(self,name,addrfile):
  1127. self.txcreate_common(name,sources=['2'])
  1128. def txsign2(self,name,txf1,wf1,txf2,wf2):
  1129. t = MMGenExpect(name,'mmgen-txsign', ['-d',cfg['tmpdir'],txf1,wf1,txf2,wf2])
  1130. t.license()
  1131. for cnum in ('1','2'):
  1132. t.tx_view()
  1133. t.passphrase('MMGen wallet',cfgs[cnum]['wpasswd'])
  1134. self.txsign_end(t,cnum)
  1135. ok()
  1136. def export_mnemonic2(self,name,wf):
  1137. self.export_mnemonic(name,wf)
  1138. def walletgen3(self,name):
  1139. self.walletgen(name)
  1140. def addrgen3(self,name,wf):
  1141. self.addrgen(name,wf,pf='')
  1142. def txcreate3(self,name,addrfile1,addrfile2):
  1143. self.txcreate_common(name,sources=['1','3'])
  1144. def txsign3(self,name,wf1,wf2,txf2):
  1145. t = MMGenExpect(name,'mmgen-txsign', ['-d',cfg['tmpdir'],wf1,wf2,txf2])
  1146. t.license()
  1147. t.tx_view()
  1148. for cnum in ('1','3'):
  1149. # t.expect_getend('Getting MMGen wallet data from file ')
  1150. t.passphrase('MMGen wallet',cfgs[cnum]['wpasswd'])
  1151. self.txsign_end(t)
  1152. ok()
  1153. def walletgen4(self,name):
  1154. bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
  1155. make_brainwallet_file(bwf)
  1156. seed_len = str(cfg['seed_len'])
  1157. args = ['-d',cfg['tmpdir'],'-p1','-r10','-l'+seed_len,'-ib']
  1158. t = MMGenExpect(name,'mmgen-walletconv', args + [bwf])
  1159. t.license()
  1160. t.passphrase_new('new MMGen wallet',cfg['wpasswd'])
  1161. t.usr_rand(10)
  1162. t.label()
  1163. t.written_to_file('MMGen wallet')
  1164. ok()
  1165. def addrgen4(self,name,wf):
  1166. self.addrgen(name,wf,pf='')
  1167. def txcreate4(self,name,f1,f2,f3,f4):
  1168. self.txcreate_common(name,sources=['1','2','3','4'],non_mmgen_input='4')
  1169. def txsign4(self,name,f1,f2,f3,f4,f5):
  1170. non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
  1171. t = MMGenExpect(name,'mmgen-txsign',
  1172. ['-d',cfg['tmpdir'],'-i','brain','-b'+cfg['bw_params'],'-p1','-k',non_mm_fn,f1,f2,f3,f4,f5])
  1173. t.license()
  1174. t.tx_view()
  1175. for cnum,desc in ('1','incognito data'),('3','MMGen wallet'):
  1176. t.passphrase(('%s' % desc),cfgs[cnum]['wpasswd'])
  1177. self.txsign_end(t)
  1178. ok()
  1179. def tool_encrypt(self,name,infile=''):
  1180. if infile:
  1181. infn = infile
  1182. else:
  1183. d = os.urandom(1033)
  1184. tmp_fn = cfg['tool_enc_infn']
  1185. write_to_tmpfile(cfg,tmp_fn,d,binary=True)
  1186. infn = get_tmpfile_fn(cfg,tmp_fn)
  1187. if ni:
  1188. pwfn = 'ni_pw'
  1189. write_to_tmpfile(cfg,pwfn,tool_enc_passwd+'\n')
  1190. pre = ['-P', get_tmpfile_fn(cfg,pwfn)]
  1191. app = ['hash_preset=1']
  1192. else:
  1193. pre,app = [],[]
  1194. t = MMGenExpect(name,'mmgen-tool',pre+['-d',cfg['tmpdir'],'encrypt',infn]+app)
  1195. if ni: return
  1196. t.hash_preset('user data','1')
  1197. t.passphrase_new('user data',tool_enc_passwd)
  1198. t.written_to_file('Encrypted data')
  1199. ok()
  1200. # Generate the reference mmenc file
  1201. # def tool_encrypt_ref(self,name):
  1202. # infn = get_tmpfile_fn(cfg,cfg['tool_enc_ref_infn'])
  1203. # write_data_to_file(infn,cfg['tool_enc_reftext'],silent=True)
  1204. # self.tool_encrypt(name,infn)
  1205. def tool_decrypt(self,name,f1,f2):
  1206. of = name + '.out'
  1207. if ni:
  1208. pwfn = 'ni_pw'
  1209. pre = ['-P', get_tmpfile_fn(cfg,pwfn)]
  1210. else:
  1211. pre = []
  1212. t = MMGenExpect(name,'mmgen-tool',
  1213. pre+['-d',cfg['tmpdir'],'decrypt',f2,'outfile='+of,'hash_preset=1'])
  1214. if not ni:
  1215. t.passphrase('user data',tool_enc_passwd)
  1216. t.written_to_file('Decrypted data')
  1217. d1 = read_from_file(f1,binary=True)
  1218. d2 = read_from_file(get_tmpfile_fn(cfg,of),binary=True)
  1219. cmp_or_die(d1,d2,skip_ok=ni)
  1220. def tool_find_incog_data(self,name,f1,f2):
  1221. i_id = read_from_file(f2).rstrip()
  1222. vmsg('Incog ID: %s' % cyan(i_id))
  1223. t = MMGenExpect(name,'mmgen-tool',
  1224. ['-d',cfg['tmpdir'],'find_incog_data',f1,i_id])
  1225. if ni: return
  1226. o = t.expect_getend('Incog data for ID %s found at offset ' % i_id)
  1227. os.unlink(f1)
  1228. cmp_or_die(hincog_offset,int(o))
  1229. # def pywallet(self,name): # TODO - check output
  1230. # pf = get_tmpfile_fn(cfg,pwfile)
  1231. # write_data_to_file(pf,cfg['wpasswd']+'\n',silent=True)
  1232. # args = ([],['-q','-P',pf])[ni]
  1233. # unenc_wf = os.path.join(ref_dir,'wallet-unenc.dat')
  1234. # enc_wf = os.path.join(ref_dir,'wallet-enc.dat')
  1235. # for wf,enc in (unenc_wf,False),(enc_wf,True):
  1236. # for w,o,pk in (
  1237. # ('addresses','a',False),
  1238. # ('private keys','k',True),
  1239. # ('json dump','j',True)
  1240. # ):
  1241. # ed = '(%sencrypted wallet, %s)' % (('un','')[bool(enc)],w)
  1242. # t = MMGenExpect(name,'mmgen-pywallet', args +
  1243. # ['-'+o,'-d',cfg['tmpdir']] + [wf], extra_desc=ed)
  1244. # if ni: continue
  1245. # if pk and enc and not ni:
  1246. # t.expect('Enter password: ',cfg['wpasswd']+'\n')
  1247. # t.written_to_file(capfirst(w),oo=True)
  1248. # if not ni: ok()
  1249. # Saved reference file tests
  1250. def ref_wallet_conv(self,name):
  1251. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1252. self.walletconv_in(name,wf,'MMGen wallet',pw=True,oo=True)
  1253. def ref_mn_conv(self,name,ext='mmwords',desc='Mnemonic data'):
  1254. wf = os.path.join(ref_dir,cfg['seed_id']+'.'+ext)
  1255. self.walletconv_in(name,wf,desc,oo=True)
  1256. def ref_seed_conv(self,name):
  1257. self.ref_mn_conv(name,ext='mmseed',desc='Seed data')
  1258. def ref_brain_conv(self,name):
  1259. uopts = ['-i','b','-p','1','-l',str(cfg['seed_len'])]
  1260. self.walletconv_in(name,None,'brainwallet',uopts,oo=True)
  1261. def ref_incog_conv(self,name,wfk='ic_wallet',in_fmt='i',desc='incognito data'):
  1262. uopts = ['-i',in_fmt,'-p','1','-l',str(cfg['seed_len'])]
  1263. wf = os.path.join(ref_dir,cfg[wfk])
  1264. self.walletconv_in(name,wf,desc,uopts,oo=True,pw=True)
  1265. def ref_incox_conv(self,name):
  1266. self.ref_incog_conv(name,in_fmt='xi',wfk='ic_wallet_hex',desc='hex incognito data')
  1267. def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
  1268. ic_f = os.path.join(ref_dir,cfg[wfk])
  1269. uopts = ['-i','hi','-p','1','-l',str(cfg['seed_len'])] + add_uopts
  1270. hi_opt = ['-H','%s,%s' % (ic_f,ref_wallet_incog_offset)]
  1271. self.walletconv_in(name,None,'hidden incognito data',uopts+hi_opt,oo=True,pw=True)
  1272. def ref_hincog_conv_old(self,name):
  1273. self.ref_hincog_conv(name,wfk='hic_wallet_old',add_uopts=['-O'])
  1274. def ref_wallet_conv_out(self,name):
  1275. self.walletconv_out(name,'MMGen wallet','w',pw=True)
  1276. def ref_mn_conv_out(self,name):
  1277. self.walletconv_out(name,'mnemonic data','mn')
  1278. def ref_seed_conv_out(self,name):
  1279. self.walletconv_out(name,'seed data','seed')
  1280. def ref_incog_conv_out(self,name):
  1281. self.walletconv_out(name,'incognito data',out_fmt='i',pw=True)
  1282. def ref_incox_conv_out(self,name):
  1283. self.walletconv_out(name,'hex incognito data',out_fmt='xi',pw=True)
  1284. def ref_hincog_conv_out(self,name,extra_uopts=[]):
  1285. ic_f = os.path.join(cfg['tmpdir'],hincog_fn)
  1286. hi_parms = '%s,%s' % (ic_f,ref_wallet_incog_offset)
  1287. sl_parm = '-l' + str(cfg['seed_len'])
  1288. self.walletconv_out(name,
  1289. 'hidden incognito data', 'hi',
  1290. uopts=['-J',hi_parms,sl_parm] + extra_uopts,
  1291. uopts_chk=['-H',hi_parms,sl_parm],
  1292. pw=True
  1293. )
  1294. def ref_wallet_chk(self,name):
  1295. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1296. if ni:
  1297. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  1298. pf = get_tmpfile_fn(cfg,pwfile)
  1299. else:
  1300. pf = None
  1301. self.walletchk(name,wf,pf=pf,pw=True,sid=cfg['seed_id'])
  1302. def ref_seed_chk(self,name,ext=g.seed_ext):
  1303. wf = os.path.join(ref_dir,'%s.%s' % (cfg['seed_id'],ext))
  1304. desc = ('mnemonic data','seed data')[ext==g.seed_ext]
  1305. self.walletchk(name,wf,pf=None,desc=desc,sid=cfg['seed_id'])
  1306. def ref_mn_chk(self,name): self.ref_seed_chk(name,ext=g.mn_ext)
  1307. def ref_brain_chk(self,name,bw_file=ref_bw_file):
  1308. wf = os.path.join(ref_dir,bw_file)
  1309. add_args = ['-l%s' % cfg['seed_len'], '-p'+ref_bw_hash_preset]
  1310. self.walletchk(name,wf,pf=None,add_args=add_args,
  1311. desc='brainwallet',sid=cfg['ref_bw_seed_id'])
  1312. def ref_brain_chk_spc3(self,name):
  1313. self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
  1314. def ref_hincog_chk(self,name,desc='hidden incognito data'):
  1315. for wtype,edesc,of_arg in ('hic_wallet','',[]), \
  1316. ('hic_wallet_old','(old format)',['-O']):
  1317. ic_arg = ['-H%s,%s' % (
  1318. os.path.join(ref_dir,cfg[wtype]),
  1319. ref_wallet_incog_offset
  1320. )]
  1321. slarg = ['-l%s ' % cfg['seed_len']]
  1322. hparg = ['-p1']
  1323. if ni:
  1324. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  1325. add_args = ['-q','-P%s' % get_tmpfile_fn(cfg,pwfile)]
  1326. else:
  1327. add_args = []
  1328. if ni and wtype == 'hic_wallet_old':
  1329. m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
  1330. n = cyan(cfg['seed_id'])
  1331. msg('\n%s %s' % (m,n))
  1332. t = MMGenExpect(name,'mmgen-walletchk',
  1333. add_args + slarg + hparg + of_arg + ic_arg,
  1334. extra_desc=edesc)
  1335. if ni: continue
  1336. t.passphrase(desc,cfg['wpasswd'])
  1337. if wtype == 'hic_wallet_old':
  1338. t.expect('Is the Seed ID correct? (Y/n): ','\n')
  1339. chk = t.expect_getend('Seed ID: ')
  1340. t.close()
  1341. cmp_or_die(cfg['seed_id'],chk)
  1342. def ref_addrfile_chk(self,name,ftype='addr'):
  1343. wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
  1344. if ni:
  1345. m = "\nAnswer the interactive prompts as follows: '1'<ENTER>, ENTER"
  1346. msg(grnbg(m))
  1347. pfn = 'ref_kafile_passwd'
  1348. write_to_tmpfile(cfg,pfn,ref_kafile_pass)
  1349. aa = ['-P',get_tmpfile_fn(cfg,pfn)]
  1350. else:
  1351. aa = []
  1352. t = MMGenExpect(name,'mmgen-tool',aa+[ftype+'file_chksum',wf])
  1353. if ni:
  1354. k = 'ref_%saddrfile_chksum' % ('','key')[ftype == 'keyaddr']
  1355. m = grnbg('Checksum should be:')
  1356. n = cyan(cfg[k])
  1357. msg(grnbg('%s %s' % (m,n)))
  1358. return
  1359. if ftype == 'keyaddr':
  1360. w = 'key-address file'
  1361. t.hash_preset(w,ref_kafile_hash_preset)
  1362. t.passphrase(w,ref_kafile_pass)
  1363. t.expect('Check key-to-address validity? (y/N): ','y')
  1364. o = t.read().strip().split('\n')[-1]
  1365. cmp_or_die(cfg['ref_'+ftype+'file_chksum'],o)
  1366. def ref_keyaddrfile_chk(self,name):
  1367. self.ref_addrfile_chk(name,ftype='keyaddr')
  1368. # def txcreate8(self,name,addrfile):
  1369. # self.txcreate_common(name,sources=['8'])
  1370. def ref_tx_chk(self,name):
  1371. tf = os.path.join(ref_dir,cfg['ref_tx_file'])
  1372. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1373. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  1374. pf = get_tmpfile_fn(cfg,pwfile)
  1375. self.txsign(name,tf,wf,pf,save=False)
  1376. def ref_tool_decrypt(self,name):
  1377. f = os.path.join(ref_dir,ref_enc_fn)
  1378. aa = []
  1379. if ni:
  1380. pfn = 'tool_enc_passwd'
  1381. write_to_tmpfile(cfg,pfn,tool_enc_passwd)
  1382. aa = ['-P',get_tmpfile_fn(cfg,pfn)]
  1383. t = MMGenExpect(name,'mmgen-tool',
  1384. aa + ['-q','decrypt',f,'outfile=-','hash_preset=1'])
  1385. if ni: return
  1386. t.passphrase('user data',tool_enc_passwd)
  1387. t.readline()
  1388. import re
  1389. o = re.sub('\r\n','\n',t.read())
  1390. cmp_or_die(sample_text,o)
  1391. # wallet conversion tests
  1392. def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
  1393. opts = ['-d',cfg['tmpdir'],'-o','words','-r10']
  1394. if_arg = [infile] if infile else []
  1395. d = '(convert)'
  1396. if ni:
  1397. opts += ['-q']
  1398. msg('')
  1399. if pw:
  1400. pfn = 'ni_passwd'
  1401. write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
  1402. opts += ['-P',get_tmpfile_fn(cfg,pfn)]
  1403. if desc == 'brainwallet':
  1404. m = "\nAnswer the interactive prompt as follows: '%s'<ENTER>"
  1405. msg(grnbg(m % ref_wallet_brainpass))
  1406. if '-O' in uopts:
  1407. m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
  1408. n = cyan(cfg['seed_id'])
  1409. msg('\n%s %s' % (m,n))
  1410. t = MMGenExpect(name,'mmgen-walletconv',opts+uopts+if_arg,extra_desc=d)
  1411. if ni:
  1412. m = grnbg('Seed ID should be:')
  1413. n = cyan(cfg['seed_id'])
  1414. msg(grnbg('%s %s' % (m,n)))
  1415. return
  1416. t.license()
  1417. if desc == 'brainwallet':
  1418. t.expect('Enter brainwallet: ',ref_wallet_brainpass+'\n')
  1419. if pw:
  1420. t.passphrase(desc,cfg['wpasswd'])
  1421. if name[:19] == 'ref_hincog_conv_old':
  1422. t.expect('Is the Seed ID correct? (Y/n): ','\n')
  1423. else:
  1424. t.expect(['Passphrase is OK',' are correct'])
  1425. # Output
  1426. wf = t.written_to_file('Mnemonic data',oo=oo)
  1427. t.close()
  1428. ok()
  1429. # back check of result
  1430. self.walletchk(name,wf,pf=None,
  1431. desc='mnemonic data',
  1432. sid=cfg['seed_id'],
  1433. extra_desc='(check)'
  1434. )
  1435. def walletconv_out(self,name,desc,out_fmt='w',uopts=[],uopts_chk=[],pw=False):
  1436. opts = ['-d',cfg['tmpdir'],'-p1','-o',out_fmt] + uopts
  1437. if ni:
  1438. pfn = 'ni_passwd'
  1439. write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
  1440. l = 'Non-Interactive Test Wallet'
  1441. aa = ['-q','-L',l,'-r0','-P',get_tmpfile_fn(cfg,pfn)]
  1442. if desc == 'hidden incognito data':
  1443. rd = os.urandom(ref_wallet_incog_offset+128)
  1444. write_to_tmpfile(cfg,hincog_fn,rd)
  1445. else:
  1446. aa = ['-r10']
  1447. infile = os.path.join(ref_dir,cfg['seed_id']+'.mmwords')
  1448. t = MMGenExpect(name,'mmgen-walletconv',aa+opts+[infile],extra_desc='(convert)')
  1449. add_args = ['-l%s' % cfg['seed_len']]
  1450. if ni:
  1451. pfn = 'ni_passwd'
  1452. write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
  1453. pf = get_tmpfile_fn(cfg,pfn)
  1454. if desc != 'hidden incognito data':
  1455. from mmgen.seed import SeedSource
  1456. ext = SeedSource.fmt_code_to_sstype(out_fmt).ext
  1457. hps = ('',',1')[bool(pw)] # TODO real hp
  1458. pre_ext = '[%s%s].' % (cfg['seed_len'],hps)
  1459. wf = get_file_with_ext(pre_ext+ext,cfg['tmpdir'],no_dot=True)
  1460. else:
  1461. t.license()
  1462. if pw:
  1463. t.passphrase_new('new '+desc,cfg['wpasswd'])
  1464. t.usr_rand(10)
  1465. if ' '.join(desc.split()[-2:]) == 'incognito data':
  1466. for i in (1,2,3):
  1467. t.expect('Generating encryption key from OS random data ')
  1468. if desc == 'hidden incognito data':
  1469. ret = t.expect(['Create? (Y/n): ',"'YES' to confirm: "])
  1470. if ret == 0:
  1471. t.send('\n')
  1472. t.expect('Enter file size: ',str(hincog_bytes)+'\n')
  1473. else:
  1474. t.send('YES\n')
  1475. if out_fmt == 'w': t.label()
  1476. wf = t.written_to_file(capfirst(desc),oo=True)
  1477. pf = None
  1478. ok()
  1479. if desc == 'hidden incognito data':
  1480. add_args += uopts_chk
  1481. wf = None
  1482. self.walletchk(name,wf,pf=pf,
  1483. desc=desc,sid=cfg['seed_id'],pw=pw,
  1484. add_args=add_args,
  1485. extra_desc='(check)')
  1486. for k in (
  1487. 'ref_wallet_conv',
  1488. 'ref_mn_conv',
  1489. 'ref_seed_conv',
  1490. 'ref_brain_conv',
  1491. 'ref_incog_conv',
  1492. 'ref_incox_conv',
  1493. 'ref_hincog_conv',
  1494. 'ref_hincog_conv_old',
  1495. 'ref_wallet_conv_out',
  1496. 'ref_mn_conv_out',
  1497. 'ref_seed_conv_out',
  1498. 'ref_incog_conv_out',
  1499. 'ref_incox_conv_out',
  1500. 'ref_hincog_conv_out',
  1501. 'ref_wallet_chk',
  1502. 'refwalletgen',
  1503. 'refaddrgen',
  1504. 'ref_seed_chk',
  1505. 'ref_mn_chk',
  1506. 'ref_brain_chk',
  1507. 'ref_hincog_chk',
  1508. 'refkeyaddrgen',
  1509. ):
  1510. for i in ('1','2','3'):
  1511. locals()[k+i] = locals()[k]
  1512. # main()
  1513. if opt.pause:
  1514. import termios,atexit
  1515. fd = sys.stdin.fileno()
  1516. old = termios.tcgetattr(fd)
  1517. def at_exit():
  1518. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  1519. atexit.register(at_exit)
  1520. start_time = int(time.time())
  1521. ts = MMGenTestSuite()
  1522. # Laggy flash media cause pexpect to crash, so read and write all temporary
  1523. # files to volatile memory in '/dev/shm'
  1524. if sys.platform[:3] == 'win':
  1525. for cfg in sorted(cfgs): mk_tmpdir(cfgs[cfg])
  1526. else:
  1527. d,pfx = '/dev/shm','mmgen-test-'
  1528. try:
  1529. import subprocess
  1530. subprocess.call('rm -rf %s/%s*'%(d,pfx),shell=True)
  1531. except Exception as e:
  1532. die(2,'Unable to delete directory tree %s/%s* (%s)'%(d,pfx,e))
  1533. try:
  1534. import tempfile
  1535. shm_dir = tempfile.mkdtemp('',pfx,d)
  1536. except Exception as e:
  1537. die(2,'Unable to create temporary directory in %s (%s)'%(d,e))
  1538. for cfg in sorted(cfgs): mk_tmpdir_path(shm_dir,cfgs[cfg])
  1539. try:
  1540. if cmd_args:
  1541. for arg in cmd_args:
  1542. if arg in utils:
  1543. globals()[arg](cmd_args[cmd_args.index(arg)+1:])
  1544. sys.exit()
  1545. elif 'info_'+arg in cmd_data:
  1546. dirs = cmd_data['info_'+arg][1]
  1547. if dirs: clean(dirs)
  1548. for cmd in cmd_list[arg]:
  1549. check_needs_rerun(ts,cmd,build=True)
  1550. elif arg in meta_cmds:
  1551. for cmd in meta_cmds[arg]:
  1552. check_needs_rerun(ts,cmd,build=True)
  1553. elif arg in cmd_data:
  1554. check_needs_rerun(ts,arg,build=True)
  1555. else:
  1556. die(1,'%s: unrecognized command' % arg)
  1557. else:
  1558. clean()
  1559. for cmd in cmd_data:
  1560. if cmd[:5] == 'info_':
  1561. msg(green('\nTesting ' + cmd_data[cmd][0]))
  1562. continue
  1563. ts.do_cmd(cmd)
  1564. if cmd is not cmd_data.keys()[-1]: do_between()
  1565. except:
  1566. sys.stderr = stderr_save
  1567. raise
  1568. t = int(time.time()) - start_time
  1569. sys.stderr.write(green(
  1570. 'All requested tests finished OK, elapsed time: %02i:%02i\n'
  1571. % (t/60,t%60)))