test.py 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795
  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=3)
  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')
  977. t.expect('Add a comment to transaction? (y/N): ','\n')
  978. t.tx_view()
  979. t.expect('Save transaction? (y/N): ','y')
  980. t.written_to_file('Transaction')
  981. ok()
  982. def txsign_end(self,t,tnum=None):
  983. t.expect('Signing transaction')
  984. t.expect('Edit transaction comment? (y/N): ','\n')
  985. t.expect('Save signed transaction? (Y/n): ','y')
  986. add = ' #' + tnum if tnum else ''
  987. t.written_to_file('Signed transaction' + add, oo=True)
  988. def txsign(self,name,txfile,wf,pf='',save=True):
  989. add_args = ([],['-q','-P',pf])[ni]
  990. if ni:
  991. m = '\nAnswer the interactive prompts as follows:\n ENTER, ENTER, ENTER'
  992. msg(grnbg(m))
  993. t = MMGenExpect(name,'mmgen-txsign', add_args+['-d',cfg['tmpdir'],txfile,wf])
  994. if ni: return
  995. t.license()
  996. t.tx_view()
  997. t.passphrase('MMGen wallet',cfg['wpasswd'])
  998. if save:
  999. self.txsign_end(t)
  1000. else:
  1001. t.expect('Edit transaction comment? (y/N): ','\n')
  1002. t.close()
  1003. ok()
  1004. def txsend(self,name,sigfile):
  1005. t = MMGenExpect(name,'mmgen-txsend', ['-d',cfg['tmpdir'],sigfile])
  1006. t.license()
  1007. t.tx_view()
  1008. t.expect('Edit transaction comment? (y/N): ','\n')
  1009. t.expect('broadcast this transaction to the network?')
  1010. t.expect("'YES, I REALLY WANT TO DO THIS' to confirm: ",'\n')
  1011. t.expect('Exiting at user request')
  1012. vmsg('This is a simulation; no transaction was sent')
  1013. ok()
  1014. def walletconv_export(self,name,wf,desc,uargs=[],out_fmt='w',pw=False):
  1015. opts = ['-d',cfg['tmpdir'],'-o',out_fmt] + uargs + [wf]
  1016. t = MMGenExpect(name,'mmgen-walletconv',opts)
  1017. t.license()
  1018. t.passphrase('MMGen wallet',cfg['wpasswd'])
  1019. if pw:
  1020. t.passphrase_new('new '+desc,cfg['wpasswd'])
  1021. t.usr_rand(10)
  1022. if ' '.join(desc.split()[-2:]) == 'incognito data':
  1023. t.expect('Generating encryption key from OS random data ')
  1024. t.expect('Generating encryption key from OS random data ')
  1025. ic_id = t.expect_getend('New Incog Wallet ID: ')
  1026. t.expect('Generating encryption key from OS random data ')
  1027. if desc == 'hidden incognito data':
  1028. write_to_tmpfile(cfg,incog_id_fn,ic_id)
  1029. ret = t.expect(['Create? (Y/n): ',"'YES' to confirm: "])
  1030. if ret == 0:
  1031. t.send('\n')
  1032. t.expect('Enter file size: ',str(hincog_bytes)+'\n')
  1033. else:
  1034. t.send('YES\n')
  1035. if out_fmt == 'w': t.label()
  1036. return t.written_to_file(capfirst(desc),oo=True)
  1037. def export_seed(self,name,wf,desc='seed data',out_fmt='seed'):
  1038. f = self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt)
  1039. silence()
  1040. msg('%s: %s' % (capfirst(desc),cyan(get_data_from_file(f,desc))))
  1041. end_silence()
  1042. ok()
  1043. def export_mnemonic(self,name,wf):
  1044. self.export_seed(name,wf,desc='mnemonic data',out_fmt='words')
  1045. def export_incog(self,name,wf,desc='incognito data',out_fmt='i',add_args=[]):
  1046. uargs = ['-p1','-r10'] + add_args
  1047. self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt,uargs=uargs,pw=True)
  1048. ok()
  1049. def export_incog_hex(self,name,wf):
  1050. self.export_incog(name,wf,desc='hex incognito data',out_fmt='xi')
  1051. # TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
  1052. def export_incog_hidden(self,name,wf):
  1053. rf = os.path.join(cfg['tmpdir'],hincog_fn)
  1054. add_args = ['-J','%s,%s'%(rf,hincog_offset)]
  1055. self.export_incog(
  1056. name,wf,desc='hidden incognito data',out_fmt='hi',add_args=add_args)
  1057. def addrgen_seed(self,name,wf,foo,desc='seed data',in_fmt='seed'):
  1058. stdout = (False,True)[desc=='seed data'] #capture output to screen once
  1059. add_arg = ([],['-S'])[bool(stdout)]
  1060. t = MMGenExpect(name,'mmgen-addrgen', add_arg +
  1061. ['-i'+in_fmt,'-d',cfg['tmpdir'],wf,cfg['addr_idx_list']])
  1062. t.license()
  1063. t.expect_getend('Valid %s for Seed ID ' % desc)
  1064. vmsg('Comparing generated checksum with checksum from previous address file')
  1065. chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
  1066. if stdout: t.read()
  1067. verify_checksum_or_exit(get_addrfile_checksum(),chk)
  1068. # t.no_overwrite()
  1069. ok()
  1070. def addrgen_mnemonic(self,name,wf,foo):
  1071. self.addrgen_seed(name,wf,foo,desc='mnemonic data',in_fmt='words')
  1072. def addrgen_incog(self,name,wf=[],foo='',in_fmt='i',desc='incognito data',args=[]):
  1073. t = MMGenExpect(name,'mmgen-addrgen', args+['-i'+in_fmt,'-d',cfg['tmpdir']]+
  1074. ([],[wf])[bool(wf)] + [cfg['addr_idx_list']])
  1075. t.license()
  1076. t.expect_getend('Incog Wallet ID: ')
  1077. t.hash_preset(desc,'1')
  1078. t.passphrase('%s \w{8}' % desc, cfg['wpasswd'])
  1079. vmsg('Comparing generated checksum with checksum from address file')
  1080. chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
  1081. t.close()
  1082. verify_checksum_or_exit(get_addrfile_checksum(),chk)
  1083. # t.no_overwrite()
  1084. ok()
  1085. def addrgen_incog_hex(self,name,wf,foo):
  1086. self.addrgen_incog(name,wf,'',in_fmt='xi',desc='hex incognito data')
  1087. def addrgen_incog_hidden(self,name,wf,foo):
  1088. rf = os.path.join(cfg['tmpdir'],hincog_fn)
  1089. self.addrgen_incog(name,[],'',in_fmt='hi',desc='hidden incognito data',
  1090. args=['-H','%s,%s'%(rf,hincog_offset),'-l',str(hincog_seedlen)])
  1091. def keyaddrgen(self,name,wf,pf,check_ref=False):
  1092. args = ['-d',cfg['tmpdir'],wf,cfg['addr_idx_list']]
  1093. if ni:
  1094. m = "\nAnswer 'n' at the interactive prompt"
  1095. msg(grnbg(m))
  1096. args = ['-q','-P',pf] + args
  1097. t = MMGenExpect(name,'mmgen-keygen', args)
  1098. if ni: return
  1099. t.license()
  1100. t.passphrase('MMGen wallet',cfg['wpasswd'])
  1101. chk = t.expect_getend(r'Checksum for key-address data .*?: ',regex=True)
  1102. if check_ref:
  1103. refcheck('key-address data checksum',chk,cfg['keyaddrfile_chk'])
  1104. return
  1105. t.expect('Encrypt key list? (y/N): ','y')
  1106. t.hash_preset('new key list','1')
  1107. t.passphrase_new('new key list',cfg['kapasswd'])
  1108. t.written_to_file('Secret keys',oo=True)
  1109. ok()
  1110. def refkeyaddrgen(self,name,wf,pf):
  1111. self.keyaddrgen(name,wf,pf,check_ref=True)
  1112. def txsign_keyaddr(self,name,keyaddr_file,txfile):
  1113. t = MMGenExpect(name,'mmgen-txsign', ['-d',cfg['tmpdir'],'-M',keyaddr_file,txfile])
  1114. t.license()
  1115. t.hash_preset('key-address file','1')
  1116. t.passphrase('key-address file',cfg['kapasswd'])
  1117. t.expect('Check key-to-address validity? (y/N): ','y')
  1118. t.tx_view()
  1119. self.txsign_end(t)
  1120. ok()
  1121. def walletgen2(self,name):
  1122. self.walletgen(name,seed_len=128)
  1123. def addrgen2(self,name,wf):
  1124. self.addrgen(name,wf,pf='')
  1125. def txcreate2(self,name,addrfile):
  1126. self.txcreate_common(name,sources=['2'])
  1127. def txsign2(self,name,txf1,wf1,txf2,wf2):
  1128. t = MMGenExpect(name,'mmgen-txsign', ['-d',cfg['tmpdir'],txf1,wf1,txf2,wf2])
  1129. t.license()
  1130. for cnum in ('1','2'):
  1131. t.tx_view()
  1132. t.passphrase('MMGen wallet',cfgs[cnum]['wpasswd'])
  1133. self.txsign_end(t,cnum)
  1134. ok()
  1135. def export_mnemonic2(self,name,wf):
  1136. self.export_mnemonic(name,wf)
  1137. def walletgen3(self,name):
  1138. self.walletgen(name)
  1139. def addrgen3(self,name,wf):
  1140. self.addrgen(name,wf,pf='')
  1141. def txcreate3(self,name,addrfile1,addrfile2):
  1142. self.txcreate_common(name,sources=['1','3'])
  1143. def txsign3(self,name,wf1,wf2,txf2):
  1144. t = MMGenExpect(name,'mmgen-txsign', ['-d',cfg['tmpdir'],wf1,wf2,txf2])
  1145. t.license()
  1146. t.tx_view()
  1147. for cnum in ('1','3'):
  1148. # t.expect_getend('Getting MMGen wallet data from file ')
  1149. t.passphrase('MMGen wallet',cfgs[cnum]['wpasswd'])
  1150. self.txsign_end(t)
  1151. ok()
  1152. def walletgen4(self,name):
  1153. bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
  1154. make_brainwallet_file(bwf)
  1155. seed_len = str(cfg['seed_len'])
  1156. args = ['-d',cfg['tmpdir'],'-p1','-r10','-l'+seed_len,'-ib']
  1157. t = MMGenExpect(name,'mmgen-walletconv', args + [bwf])
  1158. t.license()
  1159. t.passphrase_new('new MMGen wallet',cfg['wpasswd'])
  1160. t.usr_rand(10)
  1161. t.label()
  1162. t.written_to_file('MMGen wallet')
  1163. ok()
  1164. def addrgen4(self,name,wf):
  1165. self.addrgen(name,wf,pf='')
  1166. def txcreate4(self,name,f1,f2,f3,f4):
  1167. self.txcreate_common(name,sources=['1','2','3','4'],non_mmgen_input='4')
  1168. def txsign4(self,name,f1,f2,f3,f4,f5):
  1169. non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
  1170. t = MMGenExpect(name,'mmgen-txsign',
  1171. ['-d',cfg['tmpdir'],'-i','brain','-b'+cfg['bw_params'],'-p1','-k',non_mm_fn,f1,f2,f3,f4,f5])
  1172. t.license()
  1173. t.tx_view()
  1174. for cnum,desc in ('1','incognito data'),('3','MMGen wallet'):
  1175. t.passphrase(('%s' % desc),cfgs[cnum]['wpasswd'])
  1176. self.txsign_end(t)
  1177. ok()
  1178. def tool_encrypt(self,name,infile=''):
  1179. if infile:
  1180. infn = infile
  1181. else:
  1182. d = os.urandom(1033)
  1183. tmp_fn = cfg['tool_enc_infn']
  1184. write_to_tmpfile(cfg,tmp_fn,d,binary=True)
  1185. infn = get_tmpfile_fn(cfg,tmp_fn)
  1186. if ni:
  1187. pwfn = 'ni_pw'
  1188. write_to_tmpfile(cfg,pwfn,tool_enc_passwd+'\n')
  1189. pre = ['-P', get_tmpfile_fn(cfg,pwfn)]
  1190. app = ['hash_preset=1']
  1191. else:
  1192. pre,app = [],[]
  1193. t = MMGenExpect(name,'mmgen-tool',pre+['-d',cfg['tmpdir'],'encrypt',infn]+app)
  1194. if ni: return
  1195. t.hash_preset('user data','1')
  1196. t.passphrase_new('user data',tool_enc_passwd)
  1197. t.written_to_file('Encrypted data')
  1198. ok()
  1199. # Generate the reference mmenc file
  1200. # def tool_encrypt_ref(self,name):
  1201. # infn = get_tmpfile_fn(cfg,cfg['tool_enc_ref_infn'])
  1202. # write_data_to_file(infn,cfg['tool_enc_reftext'],silent=True)
  1203. # self.tool_encrypt(name,infn)
  1204. def tool_decrypt(self,name,f1,f2):
  1205. of = name + '.out'
  1206. if ni:
  1207. pwfn = 'ni_pw'
  1208. pre = ['-P', get_tmpfile_fn(cfg,pwfn)]
  1209. else:
  1210. pre = []
  1211. t = MMGenExpect(name,'mmgen-tool',
  1212. pre+['-d',cfg['tmpdir'],'decrypt',f2,'outfile='+of,'hash_preset=1'])
  1213. if not ni:
  1214. t.passphrase('user data',tool_enc_passwd)
  1215. t.written_to_file('Decrypted data')
  1216. d1 = read_from_file(f1,binary=True)
  1217. d2 = read_from_file(get_tmpfile_fn(cfg,of),binary=True)
  1218. cmp_or_die(d1,d2,skip_ok=ni)
  1219. def tool_find_incog_data(self,name,f1,f2):
  1220. i_id = read_from_file(f2).rstrip()
  1221. vmsg('Incog ID: %s' % cyan(i_id))
  1222. t = MMGenExpect(name,'mmgen-tool',
  1223. ['-d',cfg['tmpdir'],'find_incog_data',f1,i_id])
  1224. if ni: return
  1225. o = t.expect_getend('Incog data for ID %s found at offset ' % i_id)
  1226. os.unlink(f1)
  1227. cmp_or_die(hincog_offset,int(o))
  1228. # def pywallet(self,name): # TODO - check output
  1229. # pf = get_tmpfile_fn(cfg,pwfile)
  1230. # write_data_to_file(pf,cfg['wpasswd']+'\n',silent=True)
  1231. # args = ([],['-q','-P',pf])[ni]
  1232. # unenc_wf = os.path.join(ref_dir,'wallet-unenc.dat')
  1233. # enc_wf = os.path.join(ref_dir,'wallet-enc.dat')
  1234. # for wf,enc in (unenc_wf,False),(enc_wf,True):
  1235. # for w,o,pk in (
  1236. # ('addresses','a',False),
  1237. # ('private keys','k',True),
  1238. # ('json dump','j',True)
  1239. # ):
  1240. # ed = '(%sencrypted wallet, %s)' % (('un','')[bool(enc)],w)
  1241. # t = MMGenExpect(name,'mmgen-pywallet', args +
  1242. # ['-'+o,'-d',cfg['tmpdir']] + [wf], extra_desc=ed)
  1243. # if ni: continue
  1244. # if pk and enc and not ni:
  1245. # t.expect('Enter password: ',cfg['wpasswd']+'\n')
  1246. # t.written_to_file(capfirst(w),oo=True)
  1247. # if not ni: ok()
  1248. # Saved reference file tests
  1249. def ref_wallet_conv(self,name):
  1250. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1251. self.walletconv_in(name,wf,'MMGen wallet',pw=True,oo=True)
  1252. def ref_mn_conv(self,name,ext='mmwords',desc='Mnemonic data'):
  1253. wf = os.path.join(ref_dir,cfg['seed_id']+'.'+ext)
  1254. self.walletconv_in(name,wf,desc,oo=True)
  1255. def ref_seed_conv(self,name):
  1256. self.ref_mn_conv(name,ext='mmseed',desc='Seed data')
  1257. def ref_brain_conv(self,name):
  1258. uopts = ['-i','b','-p','1','-l',str(cfg['seed_len'])]
  1259. self.walletconv_in(name,None,'brainwallet',uopts,oo=True)
  1260. def ref_incog_conv(self,name,wfk='ic_wallet',in_fmt='i',desc='incognito data'):
  1261. uopts = ['-i',in_fmt,'-p','1','-l',str(cfg['seed_len'])]
  1262. wf = os.path.join(ref_dir,cfg[wfk])
  1263. self.walletconv_in(name,wf,desc,uopts,oo=True,pw=True)
  1264. def ref_incox_conv(self,name):
  1265. self.ref_incog_conv(name,in_fmt='xi',wfk='ic_wallet_hex',desc='hex incognito data')
  1266. def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
  1267. ic_f = os.path.join(ref_dir,cfg[wfk])
  1268. uopts = ['-i','hi','-p','1','-l',str(cfg['seed_len'])] + add_uopts
  1269. hi_opt = ['-H','%s,%s' % (ic_f,ref_wallet_incog_offset)]
  1270. self.walletconv_in(name,None,'hidden incognito data',uopts+hi_opt,oo=True,pw=True)
  1271. def ref_hincog_conv_old(self,name):
  1272. self.ref_hincog_conv(name,wfk='hic_wallet_old',add_uopts=['-O'])
  1273. def ref_wallet_conv_out(self,name):
  1274. self.walletconv_out(name,'MMGen wallet','w',pw=True)
  1275. def ref_mn_conv_out(self,name):
  1276. self.walletconv_out(name,'mnemonic data','mn')
  1277. def ref_seed_conv_out(self,name):
  1278. self.walletconv_out(name,'seed data','seed')
  1279. def ref_incog_conv_out(self,name):
  1280. self.walletconv_out(name,'incognito data',out_fmt='i',pw=True)
  1281. def ref_incox_conv_out(self,name):
  1282. self.walletconv_out(name,'hex incognito data',out_fmt='xi',pw=True)
  1283. def ref_hincog_conv_out(self,name,extra_uopts=[]):
  1284. ic_f = os.path.join(cfg['tmpdir'],hincog_fn)
  1285. hi_parms = '%s,%s' % (ic_f,ref_wallet_incog_offset)
  1286. sl_parm = '-l' + str(cfg['seed_len'])
  1287. self.walletconv_out(name,
  1288. 'hidden incognito data', 'hi',
  1289. uopts=['-J',hi_parms,sl_parm] + extra_uopts,
  1290. uopts_chk=['-H',hi_parms,sl_parm],
  1291. pw=True
  1292. )
  1293. def ref_wallet_chk(self,name):
  1294. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1295. if ni:
  1296. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  1297. pf = get_tmpfile_fn(cfg,pwfile)
  1298. else:
  1299. pf = None
  1300. self.walletchk(name,wf,pf=pf,pw=True,sid=cfg['seed_id'])
  1301. def ref_seed_chk(self,name,ext=g.seed_ext):
  1302. wf = os.path.join(ref_dir,'%s.%s' % (cfg['seed_id'],ext))
  1303. desc = ('mnemonic data','seed data')[ext==g.seed_ext]
  1304. self.walletchk(name,wf,pf=None,desc=desc,sid=cfg['seed_id'])
  1305. def ref_mn_chk(self,name): self.ref_seed_chk(name,ext=g.mn_ext)
  1306. def ref_brain_chk(self,name,bw_file=ref_bw_file):
  1307. wf = os.path.join(ref_dir,bw_file)
  1308. add_args = ['-l%s' % cfg['seed_len'], '-p'+ref_bw_hash_preset]
  1309. self.walletchk(name,wf,pf=None,add_args=add_args,
  1310. desc='brainwallet',sid=cfg['ref_bw_seed_id'])
  1311. def ref_brain_chk_spc3(self,name):
  1312. self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
  1313. def ref_hincog_chk(self,name,desc='hidden incognito data'):
  1314. for wtype,edesc,of_arg in ('hic_wallet','',[]), \
  1315. ('hic_wallet_old','(old format)',['-O']):
  1316. ic_arg = ['-H%s,%s' % (
  1317. os.path.join(ref_dir,cfg[wtype]),
  1318. ref_wallet_incog_offset
  1319. )]
  1320. slarg = ['-l%s ' % cfg['seed_len']]
  1321. hparg = ['-p1']
  1322. if ni:
  1323. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  1324. add_args = ['-q','-P%s' % get_tmpfile_fn(cfg,pwfile)]
  1325. else:
  1326. add_args = []
  1327. if ni and wtype == 'hic_wallet_old':
  1328. m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
  1329. n = cyan(cfg['seed_id'])
  1330. msg('\n%s %s' % (m,n))
  1331. t = MMGenExpect(name,'mmgen-walletchk',
  1332. add_args + slarg + hparg + of_arg + ic_arg,
  1333. extra_desc=edesc)
  1334. if ni: continue
  1335. t.passphrase(desc,cfg['wpasswd'])
  1336. if wtype == 'hic_wallet_old':
  1337. t.expect('Is the Seed ID correct? (Y/n): ','\n')
  1338. chk = t.expect_getend('Seed ID: ')
  1339. t.close()
  1340. cmp_or_die(cfg['seed_id'],chk)
  1341. def ref_addrfile_chk(self,name,ftype='addr'):
  1342. wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
  1343. if ni:
  1344. m = "\nAnswer the interactive prompts as follows: '1'<ENTER>, ENTER"
  1345. msg(grnbg(m))
  1346. pfn = 'ref_kafile_passwd'
  1347. write_to_tmpfile(cfg,pfn,ref_kafile_pass)
  1348. aa = ['-P',get_tmpfile_fn(cfg,pfn)]
  1349. else:
  1350. aa = []
  1351. t = MMGenExpect(name,'mmgen-tool',aa+[ftype+'file_chksum',wf])
  1352. if ni:
  1353. k = 'ref_%saddrfile_chksum' % ('','key')[ftype == 'keyaddr']
  1354. m = grnbg('Checksum should be:')
  1355. n = cyan(cfg[k])
  1356. msg(grnbg('%s %s' % (m,n)))
  1357. return
  1358. if ftype == 'keyaddr':
  1359. w = 'key-address file'
  1360. t.hash_preset(w,ref_kafile_hash_preset)
  1361. t.passphrase(w,ref_kafile_pass)
  1362. t.expect('Check key-to-address validity? (y/N): ','y')
  1363. o = t.read().strip().split('\n')[-1]
  1364. cmp_or_die(cfg['ref_'+ftype+'file_chksum'],o)
  1365. def ref_keyaddrfile_chk(self,name):
  1366. self.ref_addrfile_chk(name,ftype='keyaddr')
  1367. # def txcreate8(self,name,addrfile):
  1368. # self.txcreate_common(name,sources=['8'])
  1369. def ref_tx_chk(self,name):
  1370. tf = os.path.join(ref_dir,cfg['ref_tx_file'])
  1371. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1372. write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
  1373. pf = get_tmpfile_fn(cfg,pwfile)
  1374. self.txsign(name,tf,wf,pf,save=False)
  1375. def ref_tool_decrypt(self,name):
  1376. f = os.path.join(ref_dir,ref_enc_fn)
  1377. aa = []
  1378. if ni:
  1379. pfn = 'tool_enc_passwd'
  1380. write_to_tmpfile(cfg,pfn,tool_enc_passwd)
  1381. aa = ['-P',get_tmpfile_fn(cfg,pfn)]
  1382. t = MMGenExpect(name,'mmgen-tool',
  1383. aa + ['-q','decrypt',f,'outfile=-','hash_preset=1'])
  1384. if ni: return
  1385. t.passphrase('user data',tool_enc_passwd)
  1386. t.readline()
  1387. import re
  1388. o = re.sub('\r\n','\n',t.read())
  1389. cmp_or_die(sample_text,o)
  1390. # wallet conversion tests
  1391. def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
  1392. opts = ['-d',cfg['tmpdir'],'-o','words','-r10']
  1393. if_arg = [infile] if infile else []
  1394. d = '(convert)'
  1395. if ni:
  1396. opts += ['-q']
  1397. msg('')
  1398. if pw:
  1399. pfn = 'ni_passwd'
  1400. write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
  1401. opts += ['-P',get_tmpfile_fn(cfg,pfn)]
  1402. if desc == 'brainwallet':
  1403. m = "\nAnswer the interactive prompt as follows: '%s'<ENTER>"
  1404. msg(grnbg(m % ref_wallet_brainpass))
  1405. if '-O' in uopts:
  1406. m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
  1407. n = cyan(cfg['seed_id'])
  1408. msg('\n%s %s' % (m,n))
  1409. t = MMGenExpect(name,'mmgen-walletconv',opts+uopts+if_arg,extra_desc=d)
  1410. if ni:
  1411. m = grnbg('Seed ID should be:')
  1412. n = cyan(cfg['seed_id'])
  1413. msg(grnbg('%s %s' % (m,n)))
  1414. return
  1415. t.license()
  1416. if desc == 'brainwallet':
  1417. t.expect('Enter brainwallet: ',ref_wallet_brainpass+'\n')
  1418. if pw:
  1419. t.passphrase(desc,cfg['wpasswd'])
  1420. if name[:19] == 'ref_hincog_conv_old':
  1421. t.expect('Is the Seed ID correct? (Y/n): ','\n')
  1422. else:
  1423. t.expect(['Passphrase is OK',' are correct'])
  1424. # Output
  1425. wf = t.written_to_file('Mnemonic data',oo=oo)
  1426. t.close()
  1427. ok()
  1428. # back check of result
  1429. self.walletchk(name,wf,pf=None,
  1430. desc='mnemonic data',
  1431. sid=cfg['seed_id'],
  1432. extra_desc='(check)'
  1433. )
  1434. def walletconv_out(self,name,desc,out_fmt='w',uopts=[],uopts_chk=[],pw=False):
  1435. opts = ['-d',cfg['tmpdir'],'-p1','-o',out_fmt] + uopts
  1436. if ni:
  1437. pfn = 'ni_passwd'
  1438. write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
  1439. l = 'Non-Interactive Test Wallet'
  1440. aa = ['-q','-L',l,'-r0','-P',get_tmpfile_fn(cfg,pfn)]
  1441. if desc == 'hidden incognito data':
  1442. rd = os.urandom(ref_wallet_incog_offset+128)
  1443. write_to_tmpfile(cfg,hincog_fn,rd)
  1444. else:
  1445. aa = ['-r10']
  1446. infile = os.path.join(ref_dir,cfg['seed_id']+'.mmwords')
  1447. t = MMGenExpect(name,'mmgen-walletconv',aa+opts+[infile],extra_desc='(convert)')
  1448. add_args = ['-l%s' % cfg['seed_len']]
  1449. if ni:
  1450. pfn = 'ni_passwd'
  1451. write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
  1452. pf = get_tmpfile_fn(cfg,pfn)
  1453. if desc != 'hidden incognito data':
  1454. from mmgen.seed import SeedSource
  1455. ext = SeedSource.fmt_code_to_sstype(out_fmt).ext
  1456. hps = ('',',1')[bool(pw)] # TODO real hp
  1457. pre_ext = '[%s%s].' % (cfg['seed_len'],hps)
  1458. wf = get_file_with_ext(pre_ext+ext,cfg['tmpdir'],no_dot=True)
  1459. else:
  1460. t.license()
  1461. if pw:
  1462. t.passphrase_new('new '+desc,cfg['wpasswd'])
  1463. t.usr_rand(10)
  1464. if ' '.join(desc.split()[-2:]) == 'incognito data':
  1465. for i in (1,2,3):
  1466. t.expect('Generating encryption key from OS random data ')
  1467. if desc == 'hidden incognito data':
  1468. ret = t.expect(['Create? (Y/n): ',"'YES' to confirm: "])
  1469. if ret == 0:
  1470. t.send('\n')
  1471. t.expect('Enter file size: ',str(hincog_bytes)+'\n')
  1472. else:
  1473. t.send('YES\n')
  1474. if out_fmt == 'w': t.label()
  1475. wf = t.written_to_file(capfirst(desc),oo=True)
  1476. pf = None
  1477. ok()
  1478. if desc == 'hidden incognito data':
  1479. add_args += uopts_chk
  1480. wf = None
  1481. self.walletchk(name,wf,pf=pf,
  1482. desc=desc,sid=cfg['seed_id'],pw=pw,
  1483. add_args=add_args,
  1484. extra_desc='(check)')
  1485. for k in (
  1486. 'ref_wallet_conv',
  1487. 'ref_mn_conv',
  1488. 'ref_seed_conv',
  1489. 'ref_brain_conv',
  1490. 'ref_incog_conv',
  1491. 'ref_incox_conv',
  1492. 'ref_hincog_conv',
  1493. 'ref_hincog_conv_old',
  1494. 'ref_wallet_conv_out',
  1495. 'ref_mn_conv_out',
  1496. 'ref_seed_conv_out',
  1497. 'ref_incog_conv_out',
  1498. 'ref_incox_conv_out',
  1499. 'ref_hincog_conv_out',
  1500. 'ref_wallet_chk',
  1501. 'refwalletgen',
  1502. 'refaddrgen',
  1503. 'ref_seed_chk',
  1504. 'ref_mn_chk',
  1505. 'ref_brain_chk',
  1506. 'ref_hincog_chk',
  1507. 'refkeyaddrgen',
  1508. ):
  1509. for i in ('1','2','3'):
  1510. locals()[k+i] = locals()[k]
  1511. # main()
  1512. if opt.pause:
  1513. import termios,atexit
  1514. fd = sys.stdin.fileno()
  1515. old = termios.tcgetattr(fd)
  1516. def at_exit():
  1517. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  1518. atexit.register(at_exit)
  1519. start_time = int(time.time())
  1520. ts = MMGenTestSuite()
  1521. for cfg in sorted(cfgs): mk_tmpdir(cfgs[cfg])
  1522. try:
  1523. if cmd_args:
  1524. for arg in cmd_args:
  1525. if arg in utils:
  1526. globals()[arg](cmd_args[cmd_args.index(arg)+1:])
  1527. sys.exit()
  1528. elif 'info_'+arg in cmd_data:
  1529. dirs = cmd_data['info_'+arg][1]
  1530. if dirs: clean(dirs)
  1531. for cmd in cmd_list[arg]:
  1532. check_needs_rerun(ts,cmd,build=True)
  1533. elif arg in meta_cmds:
  1534. for cmd in meta_cmds[arg]:
  1535. check_needs_rerun(ts,cmd,build=True)
  1536. elif arg in cmd_data:
  1537. check_needs_rerun(ts,arg,build=True)
  1538. else:
  1539. die(1,'%s: unrecognized command' % arg)
  1540. else:
  1541. clean()
  1542. for cmd in cmd_data:
  1543. if cmd[:5] == 'info_':
  1544. msg(green('\nTesting ' + cmd_data[cmd][0]))
  1545. continue
  1546. ts.do_cmd(cmd)
  1547. if cmd is not cmd_data.keys()[-1]: do_between()
  1548. except:
  1549. sys.stderr = stderr_save
  1550. raise
  1551. t = int(time.time()) - start_time
  1552. sys.stderr.write(green(
  1553. 'All requested tests finished OK, elapsed time: %02i:%02i\n'
  1554. % (t/60,t%60)))