test.py 59 KB

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