test.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309
  1. #!/usr/bin/python
  2. # Chdir to repo root.
  3. # Since script is not in repo root, fix sys.path so that modules are
  4. # imported from repo, not system.
  5. import sys,os
  6. pn = os.path.dirname(sys.argv[0])
  7. os.chdir(os.path.join(pn,os.pardir))
  8. sys.path.__setitem__(0,os.path.abspath(os.curdir))
  9. import mmgen.globalvars as g
  10. import mmgen.opt as opt
  11. from mmgen.util import msgrepr,msgrepr_exit,Msg,die
  12. from mmgen.test import *
  13. hincog_fn = "rand_data"
  14. hincog_bytes = 1024*1024
  15. hincog_offset = 98765
  16. hincog_seedlen = 256
  17. incog_id_fn = "incog_id"
  18. non_mmgen_fn = "btckey"
  19. ref_dir = os.path.join("test","ref")
  20. ref_wallet_brainpass = "abc"
  21. ref_wallet_hash_preset = "1"
  22. ref_wallet_incog_offset = 123
  23. ref_bw_hash_preset = "1"
  24. ref_bw_file = "brainwallet"
  25. ref_bw_file_spc = "brainwallet-spaced"
  26. ref_kafile_pass = "kafile password"
  27. ref_kafile_hash_preset = "1"
  28. ref_enc_fn = "sample-text.mmenc"
  29. cfgs = {
  30. '6': {
  31. 'name': "reference wallet check (128-bit)",
  32. 'seed_len': 128,
  33. 'seed_id': "FE3C6545",
  34. 'ref_bw_seed_id': "33F10310",
  35. 'addrfile_chk': "B230 7526 638F 38CB 8FDC 8B76",
  36. 'keyaddrfile_chk': "CF83 32FB 8A8B 08E2 0F00 D601",
  37. 'wpasswd': "reference password",
  38. 'ref_wallet': "FE3C6545-D782B529[128,1].mmdat",
  39. 'ic_wallet': "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
  40. 'ic_wallet_old': "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
  41. 'tmpdir': os.path.join("test","tmp6"),
  42. 'kapasswd': "",
  43. 'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
  44. 'dep_generators': {
  45. 'mmdat': "refwalletgen1",
  46. 'addrs': "refaddrgen1",
  47. 'akeys.mmenc': "refkeyaddrgen1"
  48. },
  49. },
  50. '7': {
  51. 'name': "reference wallet check (192-bit)",
  52. 'seed_len': 192,
  53. 'seed_id': "1378FC64",
  54. 'ref_bw_seed_id': "CE918388",
  55. 'addrfile_chk': "8C17 A5FA 0470 6E89 3A87 8182",
  56. 'keyaddrfile_chk': "9648 5132 B98E 3AD9 6FC3 C5AD",
  57. 'wpasswd': "reference password",
  58. 'ref_wallet': "1378FC64-6F0F9BB4[192,1].mmdat",
  59. 'ic_wallet': "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
  60. 'ic_wallet_old': "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
  61. 'tmpdir': os.path.join("test","tmp7"),
  62. 'kapasswd': "",
  63. 'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
  64. 'dep_generators': {
  65. 'mmdat': "refwalletgen2",
  66. 'addrs': "refaddrgen2",
  67. 'akeys.mmenc': "refkeyaddrgen2"
  68. },
  69. },
  70. '8': {
  71. 'name': "reference wallet check (256-bit)",
  72. 'seed_len': 256,
  73. 'seed_id': "98831F3A",
  74. 'ref_bw_seed_id': "B48CD7FC",
  75. 'addrfile_chk': "6FEF 6FB9 7B13 5D91 854A 0BD3",
  76. 'keyaddrfile_chk': "9F2D D781 1812 8BAD C396 9DEB",
  77. 'wpasswd': "reference password",
  78. 'ref_wallet': "98831F3A-27F2BF93[256,1].mmdat",
  79. 'ref_addrfile': "98831F3A[1,31-33,500-501,1010-1011].addrs",
  80. 'ref_keyaddrfile': "98831F3A[1,31-33,500-501,1010-1011].akeys.mmenc",
  81. 'ref_addrfile_chksum': "6FEF 6FB9 7B13 5D91 854A 0BD3",
  82. 'ref_keyaddrfile_chksum': "9F2D D781 1812 8BAD C396 9DEB",
  83. # 'ref_fake_unspent_data':"98831F3A_unspent.json",
  84. 'ref_tx_file': "tx_FFB367[1.234].raw",
  85. 'ic_wallet': "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
  86. 'ic_wallet_old': "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
  87. 'tmpdir': os.path.join("test","tmp8"),
  88. 'kapasswd': "",
  89. 'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
  90. 'dep_generators': {
  91. 'mmdat': "refwalletgen3",
  92. 'addrs': "refaddrgen3",
  93. 'akeys.mmenc': "refkeyaddrgen3"
  94. },
  95. },
  96. '1': {
  97. 'tmpdir': os.path.join("test","tmp1"),
  98. 'wpasswd': "Dorian",
  99. 'kapasswd': "Grok the blockchain",
  100. 'addr_idx_list': "12,99,5-10,5,12", # 8 addresses
  101. 'dep_generators': {
  102. 'mmdat': "walletgen",
  103. 'addrs': "addrgen",
  104. 'raw': "txcreate",
  105. 'sig': "txsign",
  106. 'mmwords': "export_mnemonic",
  107. 'mmseed': "export_seed",
  108. 'mmincog': "export_incog",
  109. 'mmincox': "export_incog_hex",
  110. hincog_fn: "export_incog_hidden",
  111. incog_id_fn: "export_incog_hidden",
  112. 'akeys.mmenc': "keyaddrgen"
  113. },
  114. },
  115. '2': {
  116. 'tmpdir': os.path.join("test","tmp2"),
  117. 'wpasswd': "Hodling away",
  118. 'addr_idx_list': "37,45,3-6,22-23", # 8 addresses
  119. 'seed_len': 128,
  120. 'dep_generators': {
  121. 'mmdat': "walletgen2",
  122. 'addrs': "addrgen2",
  123. 'raw': "txcreate2",
  124. 'sig': "txsign2",
  125. 'mmwords': "export_mnemonic2",
  126. },
  127. },
  128. '3': {
  129. 'tmpdir': os.path.join("test","tmp3"),
  130. 'wpasswd': "Major miner",
  131. 'addr_idx_list': "73,54,1022-1023,2-5", # 8 addresses
  132. 'dep_generators': {
  133. 'mmdat': "walletgen3",
  134. 'addrs': "addrgen3",
  135. 'raw': "txcreate3",
  136. 'sig': "txsign3"
  137. },
  138. },
  139. '4': {
  140. 'tmpdir': os.path.join("test","tmp4"),
  141. 'wpasswd': "Hashrate rising",
  142. 'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses
  143. 'seed_len': 192,
  144. 'dep_generators': {
  145. 'mmdat': "walletgen4",
  146. 'mmbrain': "walletgen4",
  147. 'addrs': "addrgen4",
  148. 'raw': "txcreate4",
  149. 'sig': "txsign4",
  150. },
  151. 'bw_filename': "brainwallet.mmbrain",
  152. 'bw_params': "192,1",
  153. },
  154. '5': {
  155. 'tmpdir': os.path.join("test","tmp5"),
  156. 'wpasswd': "My changed password",
  157. 'dep_generators': {
  158. 'mmdat': "passchg",
  159. },
  160. },
  161. '9': {
  162. 'tmpdir': os.path.join("test","tmp9"),
  163. 'tool_enc_passwd': "Scrypt it, don't hash it!",
  164. 'sample_text':
  165. "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n",
  166. 'tool_enc_infn': "tool_encrypt.in",
  167. # 'tool_enc_ref_infn': "tool_encrypt_ref.in",
  168. 'dep_generators': {
  169. 'tool_encrypt.in': "tool_encrypt",
  170. 'tool_encrypt.in.mmenc': "tool_encrypt",
  171. # 'tool_encrypt_ref.in': "tool_encrypt_ref",
  172. # 'tool_encrypt_ref.in.mmenc': "tool_encrypt_ref",
  173. },
  174. },
  175. }
  176. from collections import OrderedDict
  177. cmd_data = OrderedDict([
  178. # test description depends
  179. # Check saved reference files:
  180. ['ref_wallet_chk1', (6,'saved reference wallet (128-bit)', [[[],6]])],
  181. ['ref_wallet_chk2', (7,'saved reference wallet (192-bit)', [[[],7]])],
  182. ['ref_wallet_chk3', (8,'saved reference wallet (256-bit)', [[[],8]])],
  183. ['ref_seed_chk1', (6,'saved seed file (128-bit)', [[[],6]])],
  184. ['ref_seed_chk2', (7,'saved seed file (192-bit)', [[[],7]])],
  185. ['ref_seed_chk3', (8,'saved seed file (256-bit)', [[[],8]])],
  186. ['ref_mn_chk1', (6,'saved mnemonic file (128-bit)', [[[],6]])],
  187. ['ref_mn_chk2', (7,'saved mnemonic file (192-bit)', [[[],7]])],
  188. ['ref_mn_chk3', (8,'saved mnemonic file (256-bit)', [[[],8]])],
  189. ['ref_incog_chk1', (6,'saved incog reference wallet (128-bit)', [[[],6]])],
  190. ['ref_incog_chk2', (7,'saved incog reference wallet (192-bit)', [[[],7]])],
  191. ['ref_incog_chk3', (8,'saved incog reference wallet (256-bit)', [[[],8]])],
  192. ['ref_brain_chk1', (6,'saved brainwallet (128-bit)', [[[],6]])],
  193. ['ref_brain_chk2', (7,'saved brainwallet (192-bit)', [[[],7]])],
  194. ['ref_brain_chk3', (8,'saved brainwallet (256-bit)', [[[],8]])],
  195. ['ref_brain_chk3_spc', (8,'saved brainwallet (256-bit, non-standard spacing)', [[[],8]])],
  196. ['ref_addrfile_chk', (8,'saved reference address file', [[[],8]])],
  197. ['ref_keyaddrfile_chk', (8,'saved reference key-address file', [[[],8]])],
  198. # Create the fake inputs:
  199. # ['txcreate8', (8,'transaction creation (8)', [[["addrs"],8]])],
  200. ['ref_tx_chk', (8,'saved reference tx file', [[[],8]])],
  201. ['ref_tool_decrypt', (9,'decryption of saved MMGen-encrypted file', [[[],9]])],
  202. # Generate new reference ('abc' brainwallet) files:
  203. ['refwalletgen1', (6,'gen new refwallet (128-bit)', [[[],6]])],
  204. ['refwalletgen2', (7,'gen new refwallet (192-bit)', [[[],7]])],
  205. ['refwalletgen3', (8,'gen new refwallet (256-bit)', [[[],8]])],
  206. ['refaddrgen1', (6,'new refwallet addr chksum (128-bit)', [[["mmdat"],6]])],
  207. ['refaddrgen2', (7,'new refwallet addr chksum (192-bit)', [[["mmdat"],7]])],
  208. ['refaddrgen3', (8,'new refwallet addr chksum (256-bit)', [[["mmdat"],8]])],
  209. ['refkeyaddrgen1', (6,'new refwallet key-addr chksum (128-bit)', [[["mmdat"],6]])],
  210. ['refkeyaddrgen2', (7,'new refwallet key-addr chksum (192-bit)', [[["mmdat"],7]])],
  211. ['refkeyaddrgen3', (8,'new refwallet key-addr chksum (256-bit)', [[["mmdat"],8]])],
  212. ['walletgen', (1,'wallet generation', [[[],1]])],
  213. # ['walletchk', (1,'wallet check', [[["mmdat"],1]])],
  214. ['passchg', (5,'password, label and hash preset change',[[["mmdat"],1]])],
  215. ['walletchk_newpass',(5,'wallet check with new pw, label and hash preset',[[["mmdat"],5]])],
  216. ['addrgen', (1,'address generation', [[["mmdat"],1]])],
  217. ['addrimport', (1,'address import', [[["addrs"],1]])],
  218. ['txcreate', (1,'transaction creation', [[["addrs"],1]])],
  219. ['txsign', (1,'transaction signing', [[["mmdat","raw"],1]])],
  220. ['txsend', (1,'transaction sending', [[["sig"],1]])],
  221. ['export_seed', (1,'seed export to mmseed format', [[["mmdat"],1]])],
  222. ['export_mnemonic', (1,'seed export to mmwords format', [[["mmdat"],1]])],
  223. ['export_incog', (1,'seed export to mmincog format', [[["mmdat"],1]])],
  224. ['export_incog_hex',(1,'seed export to mmincog hex format', [[["mmdat"],1]])],
  225. ['export_incog_hidden',(1,'seed export to hidden mmincog format', [[["mmdat"],1]])],
  226. ['addrgen_seed', (1,'address generation from mmseed file', [[["mmseed","addrs"],1]])],
  227. ['addrgen_mnemonic',(1,'address generation from mmwords file',[[["mmwords","addrs"],1]])],
  228. ['addrgen_incog', (1,'address generation from mmincog file',[[["mmincog","addrs"],1]])],
  229. ['addrgen_incog_hex',(1,'address generation from mmincog hex file',[[["mmincox","addrs"],1]])],
  230. ['addrgen_incog_hidden',(1,'address generation from hidden mmincog file', [[[hincog_fn,"addrs"],1]])],
  231. ['keyaddrgen', (1,'key-address file generation', [[["mmdat"],1]])],
  232. ['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
  233. # ['walletgen2',(2,'wallet generation (2)', [])],
  234. ['walletgen2',(2,'wallet generation (2), 128-bit seed', [])],
  235. ['addrgen2', (2,'address generation (2)', [[["mmdat"],2]])],
  236. ['txcreate2', (2,'transaction creation (2)', [[["addrs"],2]])],
  237. ['txsign2', (2,'transaction signing, two transactions',[[["mmdat","raw"],1],[["mmdat","raw"],2]])],
  238. ['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
  239. # ['export_mnemonic2', (2,'seed export to mmwords format (2), 128-bit seed (WIP)',[[["mmdat"],2]])],
  240. ['walletgen3',(3,'wallet generation (3)', [])],
  241. ['addrgen3', (3,'address generation (3)', [[["mmdat"],3]])],
  242. ['txcreate3', (3,'tx creation with inputs and outputs from two wallets', [[["addrs"],1],[["addrs"],3]])],
  243. ['txsign3', (3,'tx signing with inputs and outputs from two wallets',[[["mmdat"],1],[["mmdat","raw"],3]])],
  244. ['walletgen4',(4,'wallet generation (4) (brainwallet)', [])],
  245. # ['walletgen4',(4,'wallet generation (4) (brainwallet, 192-bit seed (WIP))', [])],
  246. ['addrgen4', (4,'address generation (4)', [[["mmdat"],4]])],
  247. ['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]])],
  248. ['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]])],
  249. ['tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [])],
  250. ['tool_decrypt', (9,"'mmgen-tool decrypt' (random data)",
  251. [[[cfgs['9']['tool_enc_infn'],
  252. cfgs['9']['tool_enc_infn']+".mmenc"],9]])],
  253. # ['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])],
  254. ['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])],
  255. ])
  256. utils = {
  257. 'check_deps': 'check dependencies for specified command',
  258. 'clean': 'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
  259. }
  260. addrs_per_wallet = 8
  261. # total of two outputs must be < 10 BTC
  262. for k in cfgs.keys():
  263. cfgs[k]['amts'] = [0,0]
  264. for idx,mod in (0,6),(1,4):
  265. cfgs[k]['amts'][idx] = "%s.%s" % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
  266. meta_cmds = OrderedDict([
  267. ['saved_ref1', (6,("ref_wallet_chk1","ref_seed_chk1","ref_mn_chk1","ref_brain_chk1","ref_incog_chk1"))],
  268. ['saved_ref2', (7,("ref_wallet_chk2","ref_seed_chk2","ref_mn_chk2","ref_brain_chk2","ref_incog_chk2"))],
  269. ['saved_ref3', (8,("ref_wallet_chk3","ref_seed_chk3","ref_mn_chk3","ref_brain_chk3","ref_incog_chk3","ref_brain_chk3_spc"))],
  270. ['saved_ref_other', (8,("ref_addrfile_chk","ref_tx_chk","ref_tool_decrypt"))],
  271. ['ref1', (6,("refwalletgen1","refaddrgen1","refkeyaddrgen1"))],
  272. ['ref2', (7,("refwalletgen2","refaddrgen2","refkeyaddrgen2"))],
  273. ['ref3', (8,("refwalletgen3","refaddrgen3","refkeyaddrgen3"))],
  274. ['gen', (1,("walletgen","addrgen"))],
  275. ['pass', (5,("passchg","walletchk_newpass"))],
  276. ['tx', (1,("txcreate","txsign","txsend"))],
  277. ['export', (1,[k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1])],
  278. ['gen_sp', (1,[k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1])],
  279. ['online', (1,("keyaddrgen","txsign_keyaddr"))],
  280. ['2', (2,[k for k in cmd_data if cmd_data[k][0] == 2])],
  281. ['3', (3,[k for k in cmd_data if cmd_data[k][0] == 3])],
  282. ['4', (4,[k for k in cmd_data if cmd_data[k][0] == 4])],
  283. ['tool', (9,("tool_encrypt","tool_decrypt","tool_find_incog_data"))],
  284. ])
  285. opts_data = {
  286. 'desc': "Test suite for the MMGen suite",
  287. 'usage':"[options] [command(s) or metacommand(s)]",
  288. 'options': """
  289. -h, --help Print this help message
  290. -b, --buf-keypress Use buffered keypresses as with real human input
  291. -d, --debug-scripts Turn on debugging output in executed scripts
  292. -D, --direct-exec Bypass pexpect and execute a command directly (for
  293. debugging only)
  294. -e, --exact-output Show the exact output of the MMGen script(s) being run
  295. -l, --list-cmds List and describe the tests and commands in the test suite
  296. -p, --pause Pause between tests, resuming on keypress
  297. -q, --quiet Produce minimal output. Suppress dependency info
  298. -s, --system Test scripts and modules installed on system rather than
  299. those in the repo root
  300. -v, --verbose Produce more verbose output
  301. """,
  302. 'notes': """
  303. If no command is given, the whole suite of tests is run.
  304. """
  305. }
  306. cmd_args = opt.opts.init(opts_data)
  307. if opt.system: sys.path.pop(0)
  308. if opt.debug_scripts: os.environ["MMGEN_DEBUG"] = "1"
  309. if opt.buf_keypress:
  310. send_delay = 0.3
  311. else:
  312. send_delay = 0
  313. os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
  314. if opt.debug: opt.verbose = True
  315. if opt.exact_output:
  316. def msg(s): pass
  317. vmsg = vmsg_r = msg_r = msg
  318. else:
  319. def msg(s): sys.stderr.write(s+"\n")
  320. def vmsg(s):
  321. if opt.verbose: sys.stderr.write(s+"\n")
  322. def msg_r(s): sys.stderr.write(s)
  323. def vmsg_r(s):
  324. if opt.verbose: sys.stderr.write(s)
  325. stderr_save = sys.stderr
  326. def silence():
  327. if not (opt.verbose or opt.exact_output):
  328. sys.stderr = open("/dev/null","a")
  329. def end_silence():
  330. if not (opt.verbose or opt.exact_output):
  331. sys.stderr = stderr_save
  332. def errmsg(s): stderr_save.write(s+"\n")
  333. def errmsg_r(s): stderr_save.write(s)
  334. if opt.list_cmds:
  335. fs = " {:<{w}} - {}"
  336. Msg("Available commands:")
  337. w = max([len(i) for i in cmd_data])
  338. for cmd in cmd_data:
  339. Msg(fs.format(cmd,cmd_data[cmd][1],w=w))
  340. Msg("\nAvailable metacommands:")
  341. w = max([len(i) for i in meta_cmds])
  342. for cmd in meta_cmds:
  343. Msg(fs.format(cmd," + ".join(meta_cmds[cmd][1]),w=w))
  344. Msg("\nAvailable utilities:")
  345. w = max([len(i) for i in utils])
  346. for cmd in sorted(utils):
  347. Msg(fs.format(cmd,utils[cmd],w=w))
  348. sys.exit()
  349. import pexpect,time,re
  350. from mmgen.util import get_data_from_file,write_to_file,get_lines_from_file
  351. def my_send(p,t,delay=send_delay,s=False):
  352. if delay: time.sleep(delay)
  353. ret = p.send(t) # returns num bytes written
  354. if delay: time.sleep(delay)
  355. if opt.verbose:
  356. ls = "" if opt.debug or not s else " "
  357. es = "" if s else " "
  358. msg("%sSEND %s%s" % (ls,es,yellow("'%s'"%t.replace('\n',r'\n'))))
  359. return ret
  360. def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
  361. quo = "'" if type(s) == str else ""
  362. if opt.verbose: msg_r("EXPECT %s" % yellow(quo+str(s)+quo))
  363. else: msg_r("+")
  364. try:
  365. if s == '': ret = 0
  366. else:
  367. f = p.expect if regex else p.expect_exact
  368. ret = f(s,timeout=3)
  369. except pexpect.TIMEOUT:
  370. errmsg(red("\nERROR. Expect %s%s%s timed out. Exiting" % (quo,s,quo)))
  371. sys.exit(1)
  372. if opt.debug or (opt.verbose and type(s) != str): msg_r(" ==> %s " % ret)
  373. if ret == -1:
  374. errmsg("Error. Expect returned %s" % ret)
  375. sys.exit(1)
  376. else:
  377. if t == '':
  378. if not nonl: vmsg("")
  379. else: ret = my_send(p,t,delay,s)
  380. return ret
  381. def get_file_with_ext(ext,mydir,delete=True):
  382. flist = [os.path.join(mydir,f) for f in os.listdir(mydir)
  383. if f == ext or f[-(len(ext)+1):] == "."+ext]
  384. if not flist: return False
  385. if len(flist) > 1:
  386. if delete:
  387. if not opt.quiet:
  388. msg("Multiple *.%s files in '%s' - deleting" % (ext,mydir))
  389. for f in flist: os.unlink(f)
  390. return False
  391. else:
  392. return flist[0]
  393. def get_addrfile_checksum(display=False):
  394. addrfile = get_file_with_ext("addrs",cfg['tmpdir'])
  395. silence()
  396. from mmgen.addr import AddrInfo
  397. chk = AddrInfo(addrfile).checksum
  398. if opt.verbose and display: msg("Checksum: %s" % cyan(chk))
  399. end_silence()
  400. return chk
  401. def verify_checksum_or_exit(checksum,chk):
  402. if checksum != chk:
  403. errmsg(red("Checksum error: %s" % chk))
  404. sys.exit(1)
  405. vmsg(green("Checksums match: %s") % (cyan(chk)))
  406. class MMGenExpect(object):
  407. def __init__(self,name,mmgen_cmd,cmd_args=[],extra_desc=""):
  408. if not opt.system:
  409. mmgen_cmd = os.path.join(os.curdir,mmgen_cmd)
  410. desc = cmd_data[name][1]
  411. if extra_desc: desc += " " + extra_desc
  412. if opt.verbose or opt.exact_output:
  413. sys.stderr.write(
  414. green("Testing %s\nExecuting " % desc) +
  415. cyan("'%s %s'\n" % (mmgen_cmd," ".join(cmd_args)))
  416. )
  417. else:
  418. msg_r("Testing %s " % (desc+":"))
  419. if opt.direct_exec:
  420. os.system(" ".join([mmgen_cmd] + cmd_args))
  421. sys.exit()
  422. else:
  423. self.p = pexpect.spawn(mmgen_cmd,cmd_args)
  424. if opt.exact_output: self.p.logfile = sys.stdout
  425. def license(self):
  426. p = "'w' for conditions and warranty info, or 'c' to continue: "
  427. my_expect(self.p,p,'c')
  428. def usr_rand(self,num_chars):
  429. rand_chars = list(getrandstr(num_chars,no_space=True))
  430. my_expect(self.p,'symbols left: ','x')
  431. try:
  432. vmsg_r("SEND ")
  433. while self.p.expect('left: ',0.1) == 0:
  434. ch = rand_chars.pop(0)
  435. msg_r(yellow(ch)+" " if opt.verbose else "+")
  436. self.p.send(ch)
  437. except:
  438. vmsg("EOT")
  439. my_expect(self.p,"ENTER to continue: ",'\n')
  440. def passphrase_new(self,what,passphrase):
  441. my_expect(self.p,("Enter passphrase for %s: " % what), passphrase+"\n")
  442. my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
  443. def passphrase(self,what,passphrase,pwtype=""):
  444. if pwtype: pwtype += " "
  445. my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,what)),
  446. passphrase+"\n",regex=True)
  447. def hash_preset(self,what,preset=''):
  448. my_expect(self.p,("Enter hash preset for %s," % what))
  449. my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
  450. def written_to_file(self,what,overwrite_unlikely=False,query="Overwrite? "):
  451. s1 = "%s written to file " % what
  452. s2 = query + "Type uppercase 'YES' to confirm: "
  453. ret = my_expect(self.p,s1 if overwrite_unlikely else [s1,s2])
  454. if ret == 1:
  455. my_send(self.p,"YES\n")
  456. ret = my_expect(self.p,s1)
  457. outfile = self.p.readline().strip().strip("'")
  458. vmsg("%s file: %s" % (what,cyan(outfile.replace("'",""))))
  459. return outfile
  460. def no_overwrite(self):
  461. self.expect("Overwrite? Type uppercase 'YES' to confirm: ","\n")
  462. self.expect("Exiting at user request")
  463. def tx_view(self):
  464. my_expect(self.p,r"View .*?transaction.*? \(y\)es, \(N\)o, pager \(v\)iew.*?: ","\n",regex=True)
  465. def expect_getend(self,s,regex=False):
  466. ret = self.expect(s,regex=regex,nonl=True)
  467. end = self.readline().strip()
  468. vmsg(" ==> %s" % cyan(end))
  469. return end
  470. def interactive(self):
  471. return self.p.interact()
  472. def logfile(self,arg):
  473. self.p.logfile = arg
  474. def expect(self,*args,**kwargs):
  475. return my_expect(self.p,*args,**kwargs)
  476. def send(self,*args,**kwargs):
  477. return my_send(self.p,*args,**kwargs)
  478. def readline(self):
  479. return self.p.readline()
  480. def close(self):
  481. return self.p.close()
  482. def readlines(self):
  483. return [l.rstrip()+"\n" for l in self.p.readlines()]
  484. def read(self,n=None):
  485. return self.p.read(n)
  486. from mmgen.rpc.data import TransactionInfo
  487. from decimal import Decimal
  488. from mmgen.bitcoin import verify_addr
  489. def add_fake_unspent_entry(out,address,comment):
  490. out.append(TransactionInfo(
  491. account = unicode(comment),
  492. vout = int(getrandnum(4) % 8),
  493. txid = unicode(hexlify(os.urandom(32))),
  494. amount = Decimal("%s.%s" % (10+(getrandnum(4) % 40), getrandnum(4) % 100000000)),
  495. address = address,
  496. spendable = False,
  497. scriptPubKey = ("76a914"+verify_addr(address,return_hex=True)+"88ac"),
  498. confirmations = getrandnum(4) % 500
  499. ))
  500. def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input=''):
  501. out = []
  502. for s in tx_data.keys():
  503. sid = tx_data[s]['sid']
  504. a = adata.addrinfo(sid)
  505. for idx,btcaddr in a.addrpairs():
  506. add_fake_unspent_entry(out,btcaddr,"%s:%s Test Wallet" % (sid,idx))
  507. if non_mmgen_input:
  508. from mmgen.bitcoin import privnum2addr,hextowif
  509. privnum = getrandnum(32)
  510. btcaddr = privnum2addr(privnum,compressed=True)
  511. of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
  512. write_to_file(of, hextowif("{:064x}".format(privnum),
  513. compressed=True)+"\n","compressed bitcoin key")
  514. add_fake_unspent_entry(out,btcaddr,"Non-MMGen address")
  515. # msg("\n".join([repr(o) for o in out])); sys.exit()
  516. write_to_file(unspent_data_file,repr(out),"Unspent outputs",verbose=True)
  517. def add_comments_to_addr_file(addrfile,tfile):
  518. silence()
  519. msg(green("Adding comments to address file '%s'" % addrfile))
  520. from mmgen.addr import AddrInfo
  521. a = AddrInfo(addrfile)
  522. for i in a.idxs(): a.set_comment(idx,"Test address %s" % idx)
  523. write_to_file(tfile,a.fmt_data(),{})
  524. end_silence()
  525. def make_brainwallet_file(fn):
  526. # Print random words with random whitespace in between
  527. from mmgen.mn_tirosh import words
  528. wl = words.split("\n")
  529. nwords,ws_list,max_spaces = 10," \n",5
  530. def rand_ws_seq():
  531. nchars = getrandnum(1) % max_spaces + 1
  532. return "".join([ws_list[getrandnum(1)%len(ws_list)] for i in range(nchars)])
  533. rand_pairs = [wl[getrandnum(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
  534. d = "".join(rand_pairs).rstrip() + "\n"
  535. if opt.verbose: msg_r("Brainwallet password:\n%s" % cyan(d))
  536. write_to_file(fn,d,"brainwallet password")
  537. def do_between():
  538. if opt.pause:
  539. from mmgen.util import keypress_confirm
  540. if keypress_confirm(green("Continue?"),default_yes=True):
  541. if opt.verbose or opt.exact_output: sys.stderr.write("\n")
  542. else:
  543. errmsg("Exiting at user request")
  544. sys.exit()
  545. elif opt.verbose or opt.exact_output:
  546. sys.stderr.write("\n")
  547. rebuild_list = OrderedDict()
  548. def check_needs_rerun(ts,cmd,build=False,root=True,force_delete=False,dpy=False):
  549. rerun = True if root else False # force_delete is not passed to recursive call
  550. fns = []
  551. if force_delete or not root:
  552. # does cmd produce a needed dependency(ies)?
  553. ret = ts.get_num_exts_for_cmd(cmd,dpy)
  554. if ret:
  555. for ext in ret[1]:
  556. fn = get_file_with_ext(ext,cfgs[ret[0]]['tmpdir'],delete=build)
  557. if fn:
  558. if force_delete: os.unlink(fn)
  559. else: fns.append(fn)
  560. else: rerun = True
  561. fdeps = ts.generate_file_deps(cmd)
  562. cdeps = ts.generate_cmd_deps(fdeps)
  563. for fn in fns:
  564. my_age = os.stat(fn).st_mtime
  565. for num,ext in fdeps:
  566. f = get_file_with_ext(ext,cfgs[num]['tmpdir'],delete=build)
  567. if f and os.stat(f).st_mtime > my_age: rerun = True
  568. for cdep in cdeps:
  569. if check_needs_rerun(ts,cdep,build=build,root=False,dpy=cmd): rerun = True
  570. if build:
  571. if rerun:
  572. for fn in fns:
  573. if not root: os.unlink(fn)
  574. ts.do_cmd(cmd)
  575. if not root: do_between()
  576. else:
  577. # If prog produces multiple files:
  578. if cmd not in rebuild_list or rerun == True:
  579. rebuild_list[cmd] = (rerun,fns[0] if fns else "") # FIX
  580. return rerun
  581. def refcheck(what,chk,refchk):
  582. vmsg("Comparing %s '%s' to stored reference" % (what,chk))
  583. if chk == refchk:
  584. ok()
  585. else:
  586. if not opt.verbose: errmsg("")
  587. errmsg(red("""
  588. Fatal error - %s '%s' does not match reference value '%s'. Aborting test
  589. """.strip() % (what,chk,refchk)))
  590. sys.exit(3)
  591. def check_deps(cmds):
  592. if len(cmds) != 1:
  593. msg("Usage: %s check_deps <command>" % g.prog_name)
  594. sys.exit(1)
  595. cmd = cmds[0]
  596. if cmd not in cmd_data:
  597. msg("'%s': unrecognized command" % cmd)
  598. sys.exit(1)
  599. if not opt.quiet:
  600. msg("Checking dependencies for '%s'" % (cmd))
  601. check_needs_rerun(ts,cmd,build=False)
  602. w = max(len(i) for i in rebuild_list) + 1
  603. for cmd in rebuild_list:
  604. c = rebuild_list[cmd]
  605. m = "Rebuild" if (c[0] and c[1]) else "Build" if c[0] else "OK"
  606. msg("cmd {:<{w}} {}".format(cmd+":", m, w=w))
  607. # msgrepr(cmd,c)
  608. def clean(dirs=[]):
  609. ts = MMGenTestSuite()
  610. dirlist = ts.list_tmp_dirs()
  611. if not dirs: dirs = dirlist.keys()
  612. for d in sorted(dirs):
  613. if d in dirlist:
  614. cleandir(dirlist[d])
  615. else:
  616. msg("%s: invalid directory number" % d)
  617. sys.exit(1)
  618. class MMGenTestSuite(object):
  619. def __init__(self):
  620. pass
  621. def list_tmp_dirs(self):
  622. d = {}
  623. for k in cfgs: d[k] = cfgs[k]['tmpdir']
  624. return d
  625. def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
  626. num = str(cmd_data[cmd][0])
  627. dgl = cfgs[num]['dep_generators']
  628. # msgrepr(num,cmd,dgl)
  629. if cmd in dgl.values():
  630. exts = [k for k in dgl if dgl[k] == cmd]
  631. return (num,exts)
  632. else:
  633. return None
  634. def do_cmd(self,cmd):
  635. d = [(str(num),ext) for exts,num in cmd_data[cmd][2] for ext in exts]
  636. al = [get_file_with_ext(ext,cfgs[num]['tmpdir']) for num,ext in d]
  637. global cfg
  638. cfg = cfgs[str(cmd_data[cmd][0])]
  639. self.__class__.__dict__[cmd](*([self,cmd] + al))
  640. def generate_file_deps(self,cmd):
  641. return [(str(n),e) for exts,n in cmd_data[cmd][2] for e in exts]
  642. def generate_cmd_deps(self,fdeps):
  643. return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps]
  644. def walletgen(self,name,brain=False,seed_len=None):
  645. args = ["-d",cfg['tmpdir'],"-p1","-r10"]
  646. if seed_len: args += ["-l",str(seed_len)]
  647. # if 'seed_len' in cfg: args += ["-l",cfg['seed_len']]
  648. if brain:
  649. bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
  650. args += ["-b",cfg['bw_params'],bwf]
  651. make_brainwallet_file(bwf)
  652. t = MMGenExpect(name,"mmgen-walletgen", args)
  653. t.license()
  654. if brain:
  655. t.expect(
  656. "A brainwallet will be secure only if you really know what you're doing")
  657. t.expect("Type uppercase 'YES' to confirm: ","YES\n")
  658. t.usr_rand(10)
  659. for s in "user-supplied entropy","saved user-supplied entropy":
  660. t.expect("Generating encryption key from OS random data plus %s" % s)
  661. if brain: break
  662. t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
  663. t.written_to_file("Wallet")
  664. ok()
  665. def refwalletgen(self,name):
  666. label = "test.py ref. wallet (pw '%s', seed len %s)" \
  667. % (ref_wallet_brainpass,cfg['seed_len'])
  668. bw_arg = "-b%s,%s" % (cfg['seed_len'], ref_wallet_hash_preset)
  669. args = ["-d",cfg['tmpdir'],"-p1","-r10",bw_arg,"-L",label]
  670. d = " (%s-bit seed)" % cfg['seed_len']
  671. t = MMGenExpect(name,"mmgen-walletgen", args)
  672. t.license()
  673. t.expect("Type uppercase 'YES' to confirm: ","YES\n")
  674. t.expect("passphrase: ",ref_wallet_brainpass+"\n")
  675. t.usr_rand(10)
  676. t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
  677. seed_id = t.written_to_file("Wallet").split("-")[0].split("/")[-1]
  678. refcheck("seed id",seed_id,cfg['seed_id'])
  679. refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
  680. def passchg(self,name,walletfile):
  681. t = MMGenExpect(name,"mmgen-passchg",
  682. ["-d",cfg['tmpdir'],"-p","2","-L","New Label","-r","16",walletfile])
  683. t.passphrase("MMGen wallet",cfgs['1']['wpasswd'],pwtype="old")
  684. t.expect_getend("Label changed: ")
  685. t.expect_getend("Hash preset changed: ")
  686. t.passphrase("MMGen wallet",cfg['wpasswd'],pwtype="new")
  687. t.expect("Repeat passphrase: ",cfg['wpasswd']+"\n")
  688. t.usr_rand(16)
  689. t.expect_getend("Key ID changed: ")
  690. t.written_to_file("Wallet")
  691. ok()
  692. def walletchk_beg(self,name,args):
  693. t = MMGenExpect(name,"mmgen-walletchk", args)
  694. t.expect("Getting MMGen wallet data from file '%s'" % args[-1])
  695. t.passphrase("MMGen wallet",cfg['wpasswd'])
  696. t.expect("Passphrase is OK")
  697. t.expect("Wallet is OK")
  698. return t
  699. def walletchk(self,name,walletfile):
  700. self.walletchk_beg(name,[walletfile])
  701. ok()
  702. walletchk_newpass = walletchk
  703. def addrgen(self,name,walletfile,check_ref=False):
  704. t = MMGenExpect(name,"mmgen-addrgen",["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
  705. t.license()
  706. t.passphrase("MMGen wallet",cfg['wpasswd'])
  707. t.expect("Passphrase is OK")
  708. t.expect("[0-9]+ addresses generated",regex=True)
  709. chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
  710. if check_ref:
  711. refcheck("address data checksum",chk,cfg['addrfile_chk'])
  712. return
  713. t.written_to_file("Addresses")
  714. ok()
  715. def refaddrgen(self,name,walletfile):
  716. d = " (%s-bit seed)" % cfg['seed_len']
  717. self.addrgen(name,walletfile,check_ref=True)
  718. refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen
  719. def addrimport(self,name,addrfile):
  720. outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments")
  721. add_comments_to_addr_file(addrfile,outfile)
  722. t = MMGenExpect(name,"mmgen-addrimport",[outfile])
  723. t.expect_getend(r"Checksum for address data .*\[.*\]: ",regex=True)
  724. t.expect_getend("Validating addresses...OK. ")
  725. t.expect("Type uppercase 'YES' to confirm: ","\n")
  726. vmsg("This is a simulation, so no addresses were actually imported into the tracking\nwallet")
  727. ok()
  728. def txcreate(self,name,addrfile):
  729. self.txcreate_common(name,sources=['1'])
  730. def txcreate_common(self,name,sources=['1'],non_mmgen_input=''):
  731. if opt.verbose or opt.exact_output:
  732. sys.stderr.write(green("Generating fake transaction info\n"))
  733. silence()
  734. from mmgen.addr import AddrInfo,AddrInfoList
  735. tx_data,ail = {},AddrInfoList()
  736. from mmgen.util import parse_addr_idxs
  737. for s in sources:
  738. afile = get_file_with_ext("addrs",cfgs[s]["tmpdir"])
  739. ai = AddrInfo(afile)
  740. ail.add(ai)
  741. aix = parse_addr_idxs(cfgs[s]['addr_idx_list'])
  742. if len(aix) != addrs_per_wallet:
  743. errmsg(red("Address index list length != %s: %s" %
  744. (addrs_per_wallet,repr(aix))))
  745. sys.exit()
  746. tx_data[s] = {
  747. 'addrfile': afile,
  748. 'chk': ai.checksum,
  749. 'sid': ai.seed_id,
  750. 'addr_idxs': aix[-2:],
  751. }
  752. unspent_data_file = os.path.join(cfg['tmpdir'],"unspent.json")
  753. create_fake_unspent_data(ail,unspent_data_file,tx_data,non_mmgen_input)
  754. # make the command line
  755. from mmgen.bitcoin import privnum2addr
  756. btcaddr = privnum2addr(getrandnum(32),compressed=True)
  757. cmd_args = ["-d",cfg['tmpdir']]
  758. for num in tx_data.keys():
  759. s = tx_data[num]
  760. cmd_args += [
  761. "%s:%s,%s" % (s['sid'],s['addr_idxs'][0],cfgs[num]['amts'][0]),
  762. ]
  763. # + one BTC address
  764. # + one change address and one BTC address
  765. if num is tx_data.keys()[-1]:
  766. cmd_args += ["%s:%s" % (s['sid'],s['addr_idxs'][1])]
  767. cmd_args += ["%s,%s" % (btcaddr,cfgs[num]['amts'][1])]
  768. for num in tx_data: cmd_args += [tx_data[num]['addrfile']]
  769. os.environ["MMGEN_BOGUS_WALLET_DATA"] = unspent_data_file
  770. end_silence()
  771. if opt.verbose or opt.exact_output: sys.stderr.write("\n")
  772. t = MMGenExpect(name,"mmgen-txcreate",cmd_args)
  773. t.license()
  774. for num in tx_data.keys():
  775. t.expect_getend("Getting address data from file ")
  776. chk=t.expect_getend(r"Checksum for address data .*?: ",regex=True)
  777. verify_checksum_or_exit(tx_data[num]['chk'],chk)
  778. # not in tracking wallet warning, (1 + num sources) times
  779. if t.expect(["Continue anyway? (y/N): ",
  780. "Unable to connect to bitcoind"]) == 0:
  781. t.send("y")
  782. else:
  783. errmsg(red("Error: unable to connect to bitcoind. Exiting"))
  784. sys.exit(1)
  785. for num in tx_data.keys():
  786. t.expect("Continue anyway? (y/N): ","y")
  787. t.expect(r"'q' = quit sorting, .*?: ","M", regex=True)
  788. t.expect(r"'q' = quit sorting, .*?: ","q", regex=True)
  789. outputs_list = [addrs_per_wallet*i + 1 for i in range(len(tx_data))]
  790. if non_mmgen_input: outputs_list.append(len(tx_data)*addrs_per_wallet + 1)
  791. t.expect("Enter a range or space-separated list of outputs to spend: ",
  792. " ".join([str(i) for i in outputs_list])+"\n")
  793. if non_mmgen_input: t.expect("Accept? (y/N): ","y")
  794. t.expect("OK? (Y/n): ","y")
  795. t.expect("Add a comment to transaction? (y/N): ","\n")
  796. t.tx_view()
  797. t.expect("Save transaction? (y/N): ","y")
  798. t.written_to_file("Transaction")
  799. ok()
  800. def txsign_end(self,t,tnum=None):
  801. t.expect("Signing transaction")
  802. t.expect("Edit transaction comment? (y/N): ","\n")
  803. t.expect("Save signed transaction? (y/N): ","y")
  804. add = " #" + tnum if tnum else ""
  805. t.written_to_file("Signed transaction" + add)
  806. def txsign(self,name,txfile,walletfile,save=True):
  807. t = MMGenExpect(name,"mmgen-txsign",
  808. ["-d",cfg['tmpdir'],txfile,walletfile])
  809. t.license()
  810. t.tx_view()
  811. t.passphrase("MMGen wallet",cfg['wpasswd'])
  812. if save:
  813. self.txsign_end(t)
  814. else:
  815. t.expect("Edit transaction comment? (y/N): ","\n")
  816. t.expect("Save signed transaction? (y/N): ","\n")
  817. t.expect("Signed transaction not saved")
  818. ok()
  819. def txsend(self,name,sigfile):
  820. t = MMGenExpect(name,"mmgen-txsend", ["-d",cfg['tmpdir'],sigfile])
  821. t.license()
  822. t.tx_view()
  823. t.expect("Edit transaction comment? (y/N): ","\n")
  824. t.expect("broadcast this transaction to the network?")
  825. t.expect("'YES, I REALLY WANT TO DO THIS' to confirm: ","\n")
  826. t.expect("Exiting at user request")
  827. vmsg("This is a simulation; no transaction was sent")
  828. ok()
  829. def export_seed(self,name,walletfile):
  830. t = self.walletchk_beg(name,["-s","-d",cfg['tmpdir'],walletfile])
  831. f = t.written_to_file("Seed data")
  832. silence()
  833. msg("Seed data: %s" % cyan(get_data_from_file(f,"seed data")))
  834. end_silence()
  835. ok()
  836. def export_mnemonic(self,name,walletfile):
  837. t = self.walletchk_beg(name,["-m","-d",cfg['tmpdir'],walletfile])
  838. f = t.written_to_file("Mnemonic data")
  839. silence()
  840. msg_r("Mnemonic data: %s" % cyan(get_data_from_file(f,"mnemonic data")))
  841. end_silence()
  842. ok()
  843. def export_incog(self,name,walletfile,args=["-g"]):
  844. t = MMGenExpect(name,"mmgen-walletchk",args+["-d",cfg['tmpdir'],"-r","10",walletfile])
  845. t.passphrase("MMGen wallet",cfg['wpasswd'])
  846. t.usr_rand(10)
  847. incog_id = t.expect_getend("Incog ID: ")
  848. write_to_tmpfile(cfg,incog_id_fn,incog_id+"\n")
  849. if args[0] == "-G": return t
  850. t.written_to_file("Incognito wallet data",overwrite_unlikely=True)
  851. ok()
  852. def export_incog_hex(self,name,walletfile):
  853. self.export_incog(name,walletfile,args=["-X"])
  854. # TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
  855. def export_incog_hidden(self,name,walletfile):
  856. rf,rd = os.path.join(cfg['tmpdir'],hincog_fn),os.urandom(hincog_bytes)
  857. vmsg(green("Writing %s bytes of data to file '%s'" % (hincog_bytes,rf)))
  858. write_to_file(rf,rd,verbose=opt.verbose)
  859. t = self.export_incog(name,walletfile,args=["-G","%s,%s"%(rf,hincog_offset)])
  860. t.written_to_file("Data",query="")
  861. ok()
  862. def addrgen_seed(self,name,walletfile,foo,what="seed data",arg="-s"):
  863. t = MMGenExpect(name,"mmgen-addrgen",
  864. [arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
  865. t.license()
  866. t.expect_getend("Valid %s for seed ID " % what)
  867. vmsg("Comparing generated checksum with checksum from previous address file")
  868. chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
  869. verify_checksum_or_exit(get_addrfile_checksum(),chk)
  870. t.no_overwrite()
  871. ok()
  872. def addrgen_mnemonic(self,name,walletfile,foo):
  873. self.addrgen_seed(name,walletfile,foo,what="mnemonic",arg="-m")
  874. def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
  875. t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
  876. cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
  877. t.license()
  878. t.expect_getend("Incog ID: ")
  879. t.passphrase("MMGen incognito wallet \w{8}", cfg['wpasswd'])
  880. t.hash_preset("incog wallet",'1')
  881. vmsg("Comparing generated checksum with checksum from address file")
  882. chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
  883. verify_checksum_or_exit(get_addrfile_checksum(),chk)
  884. t.no_overwrite()
  885. ok()
  886. def addrgen_incog_hex(self,name,walletfile,foo):
  887. self.addrgen_incog(name,walletfile,foo,args=["-X"])
  888. def addrgen_incog_hidden(self,name,walletfile,foo):
  889. rf = os.path.join(cfg['tmpdir'],hincog_fn)
  890. self.addrgen_incog(name,walletfile,foo,
  891. args=["-G","%s,%s,%s"%(rf,hincog_offset,hincog_seedlen)])
  892. def keyaddrgen(self,name,walletfile,check_ref=False):
  893. t = MMGenExpect(name,"mmgen-keygen",
  894. ["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
  895. t.license()
  896. t.expect("Type uppercase 'YES' to confirm: ","YES\n")
  897. t.passphrase("MMGen wallet",cfg['wpasswd'])
  898. chk = t.expect_getend(r"Checksum for key-address data .*?: ",regex=True)
  899. if check_ref:
  900. refcheck("key-address data checksum",chk,cfg['keyaddrfile_chk'])
  901. return
  902. t.expect("Encrypt key list? (y/N): ","y")
  903. t.hash_preset("new key list",'1')
  904. t.passphrase_new("new key list",cfg['kapasswd'])
  905. t.written_to_file("Keys")
  906. ok()
  907. def refkeyaddrgen(self,name,walletfile):
  908. self.keyaddrgen(name,walletfile,check_ref=True)
  909. refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen
  910. def txsign_keyaddr(self,name,keyaddr_file,txfile):
  911. t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],"-M",keyaddr_file,txfile])
  912. t.license()
  913. t.hash_preset("key-address file",'1')
  914. t.passphrase("key-address file",cfg['kapasswd'])
  915. t.expect("Check key-to-address validity? (y/N): ","y")
  916. t.tx_view()
  917. self.txsign_end(t)
  918. ok()
  919. def walletgen2(self,name):
  920. self.walletgen(name,seed_len=128)
  921. def addrgen2(self,name,walletfile):
  922. self.addrgen(name,walletfile)
  923. def txcreate2(self,name,addrfile):
  924. self.txcreate_common(name,sources=['2'])
  925. def txsign2(self,name,txf1,wf1,txf2,wf2):
  926. t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],txf1,wf1,txf2,wf2])
  927. t.license()
  928. for cnum in ('1','2'):
  929. t.tx_view()
  930. t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
  931. self.txsign_end(t,cnum)
  932. ok()
  933. def export_mnemonic2(self,name,walletfile):
  934. self.export_mnemonic(name,walletfile)
  935. def walletgen3(self,name):
  936. self.walletgen(name)
  937. def addrgen3(self,name,walletfile):
  938. self.addrgen(name,walletfile)
  939. def txcreate3(self,name,addrfile1,addrfile2):
  940. self.txcreate_common(name,sources=['1','3'])
  941. def txsign3(self,name,wf1,wf2,txf2):
  942. t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],wf1,wf2,txf2])
  943. t.license()
  944. t.tx_view()
  945. for cnum in ('1','3'):
  946. t.expect_getend("Getting MMGen wallet data from file ")
  947. t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
  948. self.txsign_end(t)
  949. ok()
  950. def walletgen4(self,name):
  951. self.walletgen(name,brain=True)
  952. def addrgen4(self,name,walletfile):
  953. self.addrgen(name,walletfile)
  954. def txcreate4(self,name,f1,f2,f3,f4):
  955. self.txcreate_common(name,sources=['1','2','3','4'],non_mmgen_input='4')
  956. def txsign4(self,name,f1,f2,f3,f4,f5):
  957. non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
  958. t = MMGenExpect(name,"mmgen-txsign",
  959. ["-d",cfg['tmpdir'],"-b",cfg['bw_params'],"-k",non_mm_fn,f1,f2,f3,f4,f5])
  960. t.license()
  961. t.tx_view()
  962. for cnum,what,app in ('1',"incognito"," incognito"),('3',"MMGen",""):
  963. t.expect_getend("Getting %s wallet data from file " % what)
  964. t.passphrase("MMGen%s wallet"%app,cfgs[cnum]['wpasswd'])
  965. if cnum == '1':
  966. t.hash_preset("incog wallet",'1')
  967. self.txsign_end(t)
  968. ok()
  969. def tool_encrypt(self,name,infile=""):
  970. if infile:
  971. infn = infile
  972. else:
  973. d = os.urandom(1033)
  974. tmp_fn = cfg['tool_enc_infn']
  975. write_to_tmpfile(cfg,tmp_fn,d)
  976. infn = get_tmpfile_fn(cfg,tmp_fn)
  977. t = MMGenExpect(name,"mmgen-tool",["-d",cfg['tmpdir'],"encrypt",infn])
  978. t.hash_preset("user data",'1')
  979. t.passphrase_new("user data",cfg['tool_enc_passwd'])
  980. t.written_to_file("Encrypted data")
  981. ok()
  982. # Generate the reference mmenc file
  983. # def tool_encrypt_ref(self,name):
  984. # infn = get_tmpfile_fn(cfg,cfg['tool_enc_ref_infn'])
  985. # write_to_file(infn,cfg['tool_enc_reftext'],silent=True)
  986. # self.tool_encrypt(name,infn)
  987. def tool_decrypt(self,name,f1,f2):
  988. of = name + ".out"
  989. t = MMGenExpect(name,"mmgen-tool",
  990. ["-d",cfg['tmpdir'],"decrypt",f2,"outfile="+of,"hash_preset=1"])
  991. t.passphrase("user data",cfg['tool_enc_passwd'])
  992. t.written_to_file("Decrypted data")
  993. d1 = read_from_file(f1)
  994. d2 = read_from_file(get_tmpfile_fn(cfg,of))
  995. cmp_or_die(d1,d2)
  996. def tool_find_incog_data(self,name,f1,f2):
  997. i_id = read_from_file(f2).rstrip()
  998. vmsg("Incog ID: %s" % cyan(i_id))
  999. t = MMGenExpect(name,"mmgen-tool",
  1000. ["-d",cfg['tmpdir'],"find_incog_data",f1,i_id])
  1001. o = t.expect_getend("Incog data for ID \w{8} found at offset ",regex=True)
  1002. cmp_or_die(hincog_offset,int(o))
  1003. # Saved reference file tests
  1004. def ref_wallet_chk(self,name):
  1005. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1006. self.walletchk(name,wf)
  1007. ref_wallet_chk1 = ref_wallet_chk2 = ref_wallet_chk3 = ref_wallet_chk
  1008. def ref_seed_chk(self,name,ext=g.seed_ext):
  1009. wf = os.path.join(ref_dir,"%s.%s" % (cfg['seed_id'],ext))
  1010. what = "seed data" if ext == g.seed_ext else "mnemonic"
  1011. self.keygen_chksum_chk(name,wf,cfg['seed_id'],what)
  1012. ref_seed_chk1 = ref_seed_chk2 = ref_seed_chk3 = ref_seed_chk
  1013. def ref_mn_chk(self,name): self.ref_seed_chk(name,ext=g.mn_ext)
  1014. ref_mn_chk1 = ref_mn_chk2 = ref_mn_chk3 = ref_mn_chk
  1015. def ref_brain_chk(self,name,bw_file=ref_bw_file):
  1016. wf = os.path.join(ref_dir,bw_file)
  1017. arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
  1018. self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],"brainwallet",[arg])
  1019. def keygen_chksum_chk(self,name,wf,seed_id,what,args=[]):
  1020. t = MMGenExpect(name,"mmgen-keygen", ["-q","-A"]+args+[wf,"1"])
  1021. chk = t.expect_getend("Valid %s for seed ID " % what)
  1022. t.close()
  1023. cmp_or_die(seed_id,chk)
  1024. ref_brain_chk1 = ref_brain_chk2 = ref_brain_chk3 = ref_brain_chk
  1025. def ref_brain_chk3_spc(self,name):
  1026. self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
  1027. def ref_incog_chk(self,name):
  1028. for wtype,desc,earg in ('ic_wallet','',[]), \
  1029. ('ic_wallet_old','(old format)',["-o"]):
  1030. ic_arg = "%s,%s,%s" % (
  1031. os.path.join(ref_dir,cfg[wtype]),
  1032. ref_wallet_incog_offset,cfg['seed_len']
  1033. )
  1034. t = MMGenExpect(name,"mmgen-keygen",
  1035. ["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
  1036. t.passphrase("MMGen incognito wallet",cfg['wpasswd'])
  1037. t.hash_preset("incog wallet","1")
  1038. if wtype == 'ic_wallet_old':
  1039. t.expect("Is the seed ID correct? (Y/n): ","\n")
  1040. chk = t.expect_getend("Valid incog data for seed ID ")
  1041. t.close()
  1042. cmp_or_die(cfg['seed_id'],chk)
  1043. ref_incog_chk1 = ref_incog_chk2 = ref_incog_chk3 = ref_incog_chk
  1044. def ref_addrfile_chk(self,name,ftype="addr"):
  1045. wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
  1046. t = MMGenExpect(name,"mmgen-tool",[ftype+"file_chksum",wf])
  1047. if ftype == "keyaddr":
  1048. w = "key-address file"
  1049. t.hash_preset(w,ref_kafile_hash_preset)
  1050. t.passphrase(w,ref_kafile_pass)
  1051. t.expect("Check key-to-address validity? (y/N): ","y")
  1052. o = t.expect_getend("Checksum for .*address data .*: ",regex=True)
  1053. cmp_or_die(cfg['ref_'+ftype+'file_chksum'],o)
  1054. def ref_keyaddrfile_chk(self,name):
  1055. self.ref_addrfile_chk(name,ftype="keyaddr")
  1056. # def txcreate8(self,name,addrfile):
  1057. # self.txcreate_common(name,sources=['8'])
  1058. def ref_tx_chk(self,name):
  1059. tf = os.path.join(ref_dir,cfg['ref_tx_file'])
  1060. wf = os.path.join(ref_dir,cfg['ref_wallet'])
  1061. self.txsign(name,tf,wf,save=False)
  1062. def ref_tool_decrypt(self,name):
  1063. f = os.path.join(ref_dir,ref_enc_fn)
  1064. t = MMGenExpect(name,"mmgen-tool",
  1065. ["-q","decrypt",f,"outfile=-","hash_preset=1"])
  1066. t.passphrase("user data",cfg['tool_enc_passwd'])
  1067. t.readline()
  1068. import re
  1069. o = re.sub('\r\n','\n',t.read())
  1070. cmp_or_die(cfg['sample_text'],o)
  1071. # main()
  1072. if opt.pause:
  1073. import termios,atexit
  1074. fd = sys.stdin.fileno()
  1075. old = termios.tcgetattr(fd)
  1076. def at_exit():
  1077. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  1078. atexit.register(at_exit)
  1079. start_time = int(time.time())
  1080. ts = MMGenTestSuite()
  1081. for cfg in sorted(cfgs): mk_tmpdir(cfgs[cfg])
  1082. try:
  1083. if cmd_args:
  1084. for arg in cmd_args:
  1085. if arg in utils:
  1086. globals()[arg](cmd_args[cmd_args.index(arg)+1:])
  1087. sys.exit()
  1088. elif arg in meta_cmds:
  1089. for cmd in meta_cmds[arg][1]:
  1090. check_needs_rerun(ts,cmd,build=True,force_delete=True)
  1091. elif arg in cmd_data:
  1092. check_needs_rerun(ts,arg,build=True)
  1093. else:
  1094. die(1,"%s: unrecognized command" % arg)
  1095. else:
  1096. clean()
  1097. for cmd in cmd_data:
  1098. ts.do_cmd(cmd)
  1099. if cmd is not cmd_data.keys()[-1]: do_between()
  1100. except:
  1101. sys.stderr = stderr_save
  1102. raise
  1103. t = int(time.time()) - start_time
  1104. sys.stderr.write(green(
  1105. "All requested tests finished OK, elapsed time: %02i:%02i\n"
  1106. % (t/60,t%60)))