test.py 58 KB

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