From 85d94eaec1758a602d51427a5b5e28c2770d5aea Mon Sep 17 00:00:00 2001 From: philemon Date: Fri, 1 May 2015 22:04:07 +0300 Subject: [PATCH] Version 0.8.1 --- ...d-Its-Dependencies-on-Microsoft-Windows.md | 10 +- mmgen/crypto.py | 4 +- mmgen/globalvars.py | 2 +- mmgen/main_addrimport.py | 3 +- mmgen/main_pywallet.py | 49 ++- mmgen/seed.py | 2 + mmgen/util.py | 14 +- setup.py | 2 +- test/gentest.py | 2 +- test/ref/wallet-enc.dat | Bin 0 -> 61440 bytes test/ref/wallet-unenc.dat | Bin 0 -> 86016 bytes test/test.py | 350 +++++++++++------- test/tooltest.py | 2 +- 13 files changed, 275 insertions(+), 165 deletions(-) create mode 100644 test/ref/wallet-enc.dat create mode 100644 test/ref/wallet-unenc.dat diff --git a/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md b/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md index aaf2ef3f..edfce233 100644 --- a/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md +++ b/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md @@ -74,7 +74,14 @@ This is a workaround for a dependency issue with the package's setup script. If your Python is installed in a different location, you'll have to adjust the destination path accordingly. -#### 7. Install MMGen: +#### 7. Install the colorama Python module (optional but recommended): + +Grab the [tarball][14] and unpack it. At the MSYS prompt, run: + + $ cd /c/colorama-0.3.3 + $ python setup.py install + +#### 8. Install MMGen: Get the [zip archive][10] from GitHub and unpack it. At the MSYS prompt, run: @@ -103,3 +110,4 @@ a new MSYS window to update your path. [11]: http://slproweb.com/download/Win32OpenSSL-1_0_1f.exe [12]: http://www.openssl.org/related/binaries.html [13]: Getting-Started-with-MMGen +[14]: https://pypi.python.org/pypi/colorama diff --git a/mmgen/crypto.py b/mmgen/crypto.py index b3dd1b3b..53ce6e18 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -67,7 +67,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id): chk1 = make_chksum_8(key) if key_id: if not compare_chksums(key_id,"key ID",chk1,"computed"): - msg("Incorrect passphrase") + msg("Incorrect passphrase or hash preset") return False dec_seed = decrypt_data(enc_seed, key, iv=1, desc="seed") @@ -83,7 +83,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id): if compare_chksums(key_id,"key ID",chk1,"computed"): msg("Key ID is correct but decryption of seed failed") else: - msg("Incorrect passphrase") + msg("Incorrect passphrase or hash preset") vmsg("") return False diff --git a/mmgen/globalvars.py b/mmgen/globalvars.py index 6adbf8d6..a2fe91f1 100755 --- a/mmgen/globalvars.py +++ b/mmgen/globalvars.py @@ -49,7 +49,7 @@ prog_name = os.path.basename(sys.argv[0]) author = "Philemon" email = "" Cdates = '2013-2015' -version = '0.8.1rc1' +version = '0.8.1' required_opts = [ "quiet","verbose","debug","outdir","echo_passphrase","passwd_file", diff --git a/mmgen/main_addrimport.py b/mmgen/main_addrimport.py index 739dc247..bdd69b04 100755 --- a/mmgen/main_addrimport.py +++ b/mmgen/main_addrimport.py @@ -97,8 +97,7 @@ program now and rerun it using the '--rescan' option. Otherwise you may ignore this message and continue. """.strip() -if opt.quiet: m = "" -confirm_or_exit(m, "continue", expect="YES") +if not opt.quiet: confirm_or_exit(m, "continue", expect="YES") err_flag = False diff --git a/mmgen/main_pywallet.py b/mmgen/main_pywallet.py index 6c816f2c..e7ce9f59 100755 --- a/mmgen/main_pywallet.py +++ b/mmgen/main_pywallet.py @@ -61,7 +61,7 @@ import math import mmgen.globalvars as g import mmgen.opt as opt -from mmgen.util import msg,mdie,mmsg +from mmgen.util import msg,mdie,mmsg,write_data_to_file max_version = 60000 addrtype = 0 @@ -73,15 +73,16 @@ opts_data = { 'desc': "Dump contents of a bitcoind wallet to file", 'usage': "[opts] ", 'options': """ --h, --help Print this help message --d, --outdir= d Specify an alternate directory 'd' for output --e, --echo-passphrase Display passphrase on screen upon entry --j, --json Dump wallet in json format --k, --keys Dump all private keys (flat list) --a, --addrs Dump all addresses (flat list) --K, --keysforaddrs= f Dump private keys for addresses listed in file 'f' --P, --passwd-file= f Get passphrase from file 'f' --S, --stdout Dump to stdout rather than file +-h, --help Print this help message. +-d, --outdir= d Specify an alternate directory 'd' for output. +-e, --echo-passphrase Display passphrase on screen upon entry. +-j, --json Dump wallet in json format. +-k, --keys Dump all private keys (flat list). +-a, --addrs Dump all addresses (flat list). +-K, --keysforaddrs= f Dump private keys for addresses listed in file 'f'. +-P, --passwd-file= f Get passphrase from file 'f'. +-q, --quiet Produce quieter output; suppress some warnings. +-S, --stdout Dump to stdout rather than file. """ } @@ -1540,9 +1541,13 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction mkey['vchOtherDerivationParameters'] = d['vchOtherDerivationParameters'].encode('hex') json_db['mkey'] = mkey - if password == None and (opt.json or opt.keysforaddr or opt.keys): - from mmgen.util import get_bitcoind_passphrase - password = get_bitcoind_passphrase("Enter password: ") + if password == None and (opt.json or opt.keysforaddrs or opt.keys): + if opt.passwd_file: + from mmgen.util import get_data_from_file + password = get_data_from_file(opt.passwd_file).rstrip() + else: + from mmgen.util import get_bitcoind_passphrase + password = get_bitcoind_passphrase("Enter password: ") if password != None: global crypter @@ -1665,9 +1670,15 @@ wallet_id = make_chksum_8(str(sorted(wallet_addrs))) data = "\n".join(data) + "\n" # Output data -if opt.stdout or not sys.stdout.isatty(): - conf = not (opt.addrs or not sys.stdout.isatty()) - write_to_stdout(data,"secret keys",conf) -else: - of = "wd_%s[%s].%s" % (wallet_id,len_arg,ext) - write_to_file(of, data, what, confirm_overwrite=True,verbose=True) +of = "wd_%s[%s].%s" % (wallet_id,len_arg,ext) +write_data_to_file(of, data, what, ask_overwrite=not opt.quiet) +# outfile, +# data, +# desc="data", +# ask_write=False, +# ask_write_prompt="", +# ask_write_default_yes=False, +# ask_overwrite=True, +# ask_tty=True, +# no_tty=False, +# silent=False diff --git a/mmgen/seed.py b/mmgen/seed.py index 101019b0..f15d500d 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -155,6 +155,8 @@ class SeedSource(MMGenObject): def _decrypt_retry(self): while True: if self._decrypt(): break + if opt.passwd_file: + die(2,"Passphrase from password file, so exiting") msg("Trying again...") subclasses = [] diff --git a/mmgen/util.py b/mmgen/util.py index 88ebd9d1..2dd78a8f 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -30,8 +30,10 @@ import mmgen.globalvars as g pnm = g.proj_name _red,_grn,_yel,_cya,_reset = ( - ["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0"] -) + ["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0"], + ["","","","",""] +)[int(sys.platform[:3] == "win")] + def red(s): return _red+s+_reset def green(s): return _grn+s+_reset def yellow(s): return _yel+s+_reset @@ -456,10 +458,10 @@ def write_data_to_file( hush = False if file_exists(outfile): - if ask_overwrite: - q = "File '%s' already exists\nOverwrite?" % outfile - confirm_or_exit("",q) - if not silent: msg("Overwriting file '%s'" % outfile) + if ask_overwrite and not silent: + q = "File '%s' already exists\nOverwrite?" % outfile + confirm_or_exit("",q) + msg("Overwriting file '%s'" % outfile) hush = True f = open_file_or_exit(outfile,'wb') diff --git a/setup.py b/setup.py index 787b6345..6787b401 100755 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ from distutils.core import setup setup( name = 'mmgen', description = 'A complete Bitcoin cold-storage solution for the command line', - version = '0.8.1rc1', + version = '0.8.1', author = 'Philemon', author_email = 'mmgen-py@yandex.com', url = 'https://github.com/mmgen/mmgen', diff --git a/test/gentest.py b/test/gentest.py index 5a9c4552..182547fb 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Chdir to repo root. # Since script is not in repo root, fix sys.path so that modules are diff --git a/test/ref/wallet-enc.dat b/test/ref/wallet-enc.dat new file mode 100644 index 0000000000000000000000000000000000000000..15bc741d7c35ec6dcdd7d6be932375ac953b3dac GIT binary patch literal 61440 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 literal 0 HcmV?d00001 diff --git a/test/ref/wallet-unenc.dat b/test/ref/wallet-unenc.dat new file mode 100644 index 0000000000000000000000000000000000000000..54eb57d5be298a3ddf71c7bf4488cca05da56409 GIT binary patch literal 86016 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 Sy*+2{{X(4MO3)}^`~d(@$uf@s literal 0 HcmV?d00001 diff --git a/test/test.py b/test/test.py index 87a9a262..6fe23116 100755 --- a/test/test.py +++ b/test/test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Chdir to repo root. # Since script is not in repo root, fix sys.path so that modules are @@ -8,11 +8,26 @@ pn = os.path.dirname(sys.argv[0]) os.chdir(os.path.join(pn,os.pardir)) sys.path.__setitem__(0,os.path.abspath(os.curdir)) +scripts = ( + "addrgen", "addrimport", "keygen", + "passchg", "pywallet", "tool", + "txcreate", "txsend", "txsign", + "walletchk", "walletconv", "walletgen" +) + import mmgen.globalvars as g import mmgen.opt as opt -from mmgen.util import mmsg,mdie,Msg,die,capfirst +from mmgen.util import mmsg,mdie,Msg,die,capfirst,write_data_to_file from mmgen.test import * +if sys.platform[:3] == "win": + try: + import colorama + colorama.init(strip=True,convert=True) + except: + def nocolor(s): return s + red = green = yellow = cyan = nocolor + tb_cmd = "scripts/traceback.py" hincog_fn = "rand_data" hincog_bytes = 1024*1024 @@ -21,6 +36,7 @@ hincog_seedlen = 256 incog_id_fn = "incog_id" non_mmgen_fn = "btckey" +pwfile = "passwd_file" ref_dir = os.path.join("test","ref") @@ -41,6 +57,74 @@ sample_text = \ "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n" cfgs = { + '1': { + 'tmpdir': os.path.join("test","tmp1"), + 'wpasswd': "Dorian", + 'kapasswd': "Grok the blockchain", + 'addr_idx_list': "12,99,5-10,5,12", # 8 addresses + 'dep_generators': { + pwfile: "walletgen", + 'mmdat': "walletgen", + 'addrs': "addrgen", + 'raw': "txcreate", + 'sig': "txsign", + 'mmwords': "export_mnemonic", + 'mmseed': "export_seed", + 'mmincog': "export_incog", + 'mmincox': "export_incog_hex", + hincog_fn: "export_incog_hidden", + incog_id_fn: "export_incog_hidden", + 'akeys.mmenc': "keyaddrgen" + }, + }, + '2': { + 'tmpdir': os.path.join("test","tmp2"), + 'wpasswd': "Hodling away", + 'addr_idx_list': "37,45,3-6,22-23", # 8 addresses + 'seed_len': 128, + 'dep_generators': { + 'mmdat': "walletgen2", + 'addrs': "addrgen2", + 'raw': "txcreate2", + 'sig': "txsign2", + 'mmwords': "export_mnemonic2", + }, + }, + '3': { + 'tmpdir': os.path.join("test","tmp3"), + 'wpasswd': "Major miner", + 'addr_idx_list': "73,54,1022-1023,2-5", # 8 addresses + 'dep_generators': { + 'mmdat': "walletgen3", + 'addrs': "addrgen3", + 'raw': "txcreate3", + 'sig': "txsign3" + }, + }, + '4': { + 'tmpdir': os.path.join("test","tmp4"), + 'wpasswd': "Hashrate rising", + 'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses + 'seed_len': 192, + 'dep_generators': { + 'mmdat': "walletgen4", + 'mmbrain': "walletgen4", + 'addrs': "addrgen4", + 'raw': "txcreate4", + 'sig': "txsign4", + }, + 'bw_filename': "brainwallet.mmbrain", + 'bw_params': "192,1", + }, + '5': { + 'tmpdir': os.path.join("test","tmp5"), + 'wpasswd': "My changed password", + 'hash_preset': '2', + 'dep_generators': { + 'mmdat': "passchg", + pwfile: "passchg", + }, + }, '6': { 'name': "reference wallet check (128-bit)", 'seed_len': 128, @@ -122,75 +206,11 @@ cfgs = { 'akeys.mmenc': "refkeyaddrgen3" }, }, - '1': { - 'tmpdir': os.path.join("test","tmp1"), - 'wpasswd': "Dorian", - 'kapasswd': "Grok the blockchain", - 'addr_idx_list': "12,99,5-10,5,12", # 8 addresses - 'dep_generators': { - 'mmdat': "walletgen", - 'addrs': "addrgen", - 'raw': "txcreate", - 'sig': "txsign", - 'mmwords': "export_mnemonic", - 'mmseed': "export_seed", - 'mmincog': "export_incog", - 'mmincox': "export_incog_hex", - hincog_fn: "export_incog_hidden", - incog_id_fn: "export_incog_hidden", - 'akeys.mmenc': "keyaddrgen" - }, - }, - '2': { - 'tmpdir': os.path.join("test","tmp2"), - 'wpasswd': "Hodling away", - 'addr_idx_list': "37,45,3-6,22-23", # 8 addresses - 'seed_len': 128, - 'dep_generators': { - 'mmdat': "walletgen2", - 'addrs': "addrgen2", - 'raw': "txcreate2", - 'sig': "txsign2", - 'mmwords': "export_mnemonic2", - }, - }, - '3': { - 'tmpdir': os.path.join("test","tmp3"), - 'wpasswd': "Major miner", - 'addr_idx_list': "73,54,1022-1023,2-5", # 8 addresses - 'dep_generators': { - 'mmdat': "walletgen3", - 'addrs': "addrgen3", - 'raw': "txcreate3", - 'sig': "txsign3" - }, - }, - '4': { - 'tmpdir': os.path.join("test","tmp4"), - 'wpasswd': "Hashrate rising", - 'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses - 'seed_len': 192, - 'dep_generators': { - 'mmdat': "walletgen4", - 'mmbrain': "walletgen4", - 'addrs': "addrgen4", - 'raw': "txcreate4", - 'sig': "txsign4", - }, - 'bw_filename': "brainwallet.mmbrain", - 'bw_params': "192,1", - }, - '5': { - 'tmpdir': os.path.join("test","tmp5"), - 'wpasswd': "My changed password", - 'dep_generators': { - 'mmdat': "passchg", - }, - }, '9': { 'tmpdir': os.path.join("test","tmp9"), 'tool_enc_infn': "tool_encrypt.in", # 'tool_enc_ref_infn': "tool_encrypt_ref.in", + 'wpasswd': "reference password", 'dep_generators': { 'tool_encrypt.in': "tool_encrypt", 'tool_encrypt.in.mmenc': "tool_encrypt", @@ -208,14 +228,15 @@ for a,b in ('6','11'),('7','12'),('8','13'): from collections import OrderedDict cmd_data = OrderedDict([ # test description depends - ['walletgen', (1,'wallet generation', [[[],1]])], + ['helpscreens', (1,'help screens', [],1)], + ['walletgen', (1,'wallet generation', [[[],1]],1)], # ['walletchk', (1,'wallet check', [[["mmdat"],1]])], - ['passchg', (5,'password, label and hash preset change',[[["mmdat"],1]])], - ['walletchk_newpass',(5,'wallet check with new pw, label and hash preset',[[["mmdat"],5]])], - ['addrgen', (1,'address generation', [[["mmdat"],1]])], - ['addrimport', (1,'address import', [[["addrs"],1]])], - ['txcreate', (1,'transaction creation', [[["addrs"],1]])], - ['txsign', (1,'transaction signing', [[["mmdat","raw"],1]])], + ['passchg', (5,'password, label and hash preset change',[[["mmdat",pwfile],1]],1)], + ['walletchk_newpass',(5,'wallet check with new pw, label and hash preset',[[["mmdat",pwfile],5]],1)], + ['addrgen', (1,'address generation', [[["mmdat",pwfile],1]],1)], + ['addrimport', (1,'address import', [[["addrs"],1]],1)], + ['txcreate', (1,'transaction creation', [[["addrs"],1]],1)], + ['txsign', (1,'transaction signing', [[["mmdat","raw",pwfile],1]],1)], ['txsend', (1,'transaction sending', [[["sig"],1]])], ['export_seed', (1,'seed export to mmseed format', [[["mmdat"],1]])], @@ -252,6 +273,7 @@ cmd_data = OrderedDict([ ['tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[cfgs['9']['tool_enc_infn'],cfgs['9']['tool_enc_infn']+".mmenc"],9]])], # ['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])], ['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])], + ['pywallet', (9,"'mmgen-pywallet'", [],1)], ]) # saved reference data @@ -349,7 +371,7 @@ meta_cmds = OrderedDict([ ['3', [k for k in cmd_data if cmd_data[k][0] == 3]], ['4', [k for k in cmd_data if cmd_data[k][0] == 4]], - ['tool', ("tool_encrypt","tool_decrypt","tool_find_incog_data")], + ['tool', ("tool_encrypt","tool_decrypt","tool_find_incog_data","pywallet")], ['saved_ref1', [c[0]+"1" for c in cmd_data_ref]], ['saved_ref2', [c[0]+"2" for c in cmd_data_ref]], @@ -367,23 +389,25 @@ meta_cmds = OrderedDict([ ]) opts_data = { +# 'sets': [('non_interactive',bool,'verbose',None)], 'desc': "Test suite for the MMGen suite", 'usage':"[options] [command(s) or metacommand(s)]", 'options': """ --h, --help Print this help message --b, --buf-keypress Use buffered keypresses as with real human input --d, --debug-scripts Turn on debugging output in executed scripts +-h, --help Print this help message. +-b, --buf-keypress Use buffered keypresses as with real human input. +-d, --debug-scripts Turn on debugging output in executed scripts. -D, --direct-exec Bypass pexpect and execute a command directly (for - debugging only) --e, --exact-output Show the exact output of the MMGen script(s) being run --l, --list-cmds List and describe the tests and commands in the test suite --n, --names Display command names instead of descriptions --p, --pause Pause between tests, resuming on keypress --q, --quiet Produce minimal output. Suppress dependency info --s, --system Test scripts and modules installed on system rather than - those in the repo root --t, --traceback Run the command inside the '{tb_cmd}' script --v, --verbose Produce more verbose output + debugging only). +-e, --exact-output Show the exact output of the MMGen script(s) being run. +-l, --list-cmds List and describe the commands in the test suite. +-n, --names Display command names instead of descriptions. +-I, --non-interactive Non-interactive operation (MS Windows mode) +-p, --pause Pause between tests, resuming on keypress. +-q, --quiet Produce minimal output. Suppress dependency info. +-s, --system Test scripts and modules installed on system rather + than those in the repo root. +-t, --traceback Run the command inside the '{tb_cmd}' script. +-v, --verbose Produce more verbose output. """.format(tb_cmd=tb_cmd), 'notes': """ @@ -394,6 +418,7 @@ If no command is given, the whole suite of tests is run. cmd_args = opt.opts.init(opts_data) if opt.system: sys.path.pop(0) +ni = bool(opt.non_interactive) # temporary #os.environ["MMGEN_USE_OLD_SCRIPTS"] = "1" @@ -421,7 +446,8 @@ stderr_save = sys.stderr def silence(): if not (opt.verbose or opt.exact_output): - sys.stderr = open("/dev/null","a") + f = ("/dev/null","stderr.out")[int(sys.platform[:3] == "win")] + sys.stderr = open(f,"a") def end_silence(): if not (opt.verbose or opt.exact_output): @@ -453,7 +479,13 @@ if opt.list_cmds: Msg(fs.format(cmd,utils[cmd],w=w)) sys.exit() -import pexpect,time,re +import time,re +try: + import pexpect +except: # Windows + msg(red("MS Windows detected (or missing pexpect module). Running in non-interactive mode")) + ni = True + from mmgen.util import get_data_from_file,write_to_file,get_lines_from_file def my_send(p,t,delay=send_delay,s=False): @@ -527,7 +559,7 @@ def verify_checksum_or_exit(checksum,chk): class MMGenExpect(object): - def __init__(self,name,mmgen_cmd,cmd_args=[],extra_desc=""): + def __init__(self,name,mmgen_cmd,cmd_args=[],extra_desc="",no_output=False): if not opt.system: mmgen_cmd = os.path.join(os.curdir,mmgen_cmd) desc = (cmd_data[name][1],name)[int(bool(opt.names))] @@ -540,9 +572,11 @@ class MMGenExpect(object): else: msg_r("Testing %s: " % desc) - if opt.direct_exec: - os.system(" ".join([mmgen_cmd] + cmd_args)) - sys.exit() + if opt.direct_exec or ni: + msg("") + from subprocess import check_call,check_output + f = (check_call,check_output)[int(no_output)] + f(["python", mmgen_cmd] + cmd_args) else: if opt.traceback: cmd_args = [mmgen_cmd] + cmd_args @@ -825,6 +859,8 @@ class MMGenTestSuite(object): def do_cmd(self,cmd): + if ni and (len(cmd_data[cmd]) < 4 or cmd_data[cmd][3] != 1): return + d = [(str(num),ext) for exts,num in cmd_data[cmd][2] for ext in exts] al = [get_file_with_ext(ext,cfgs[num]['tmpdir']) for num,ext in d] @@ -839,10 +875,21 @@ class MMGenTestSuite(object): def generate_cmd_deps(self,fdeps): return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps] + def helpscreens(self,name): + for s in scripts: + t = MMGenExpect(name,("mmgen-"+s),["--help"], + extra_desc="(mmgen-%s)"%s,no_output=True) + if not ni: + t.read(); ok() + def walletgen(self,name,seed_len=None): - args = ["-d",cfg['tmpdir'],"-p1","-r10"] + write_to_tmpfile(cfg,pwfile,cfg['wpasswd']+"\n") + add_args = (["-r10"], + ["-q","-r0","-L","NI Wallet","-P",get_tmpfile_fn(cfg,pwfile)])[int(ni)] + args = ["-d",cfg['tmpdir'],"-p1"] if seed_len: args += ["-l",str(seed_len)] - t = MMGenExpect(name,"mmgen-walletgen", args) + t = MMGenExpect(name,"mmgen-walletgen", args + add_args) + if ni: return t.license() t.usr_rand(10) t.passphrase_new("new MMGen wallet",cfg['wpasswd']) @@ -868,10 +915,15 @@ class MMGenTestSuite(object): refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen - def passchg(self,name,walletfile): - - t = MMGenExpect(name,"mmgen-passchg", - ["-d",cfg['tmpdir'],"-p","2","-L","New Label","-r","16",walletfile]) + def passchg(self,name,wf,pf): + # ni: reuse password, since there's no way to change it non-interactively + silence() + write_to_tmpfile(cfg,pwfile,get_data_from_file(pf)) + end_silence() + add_args = (["-r16"],["-q","-r0","-P",pf])[int(ni)] + t = MMGenExpect(name,"mmgen-passchg", add_args + + ["-d",cfg['tmpdir'],"-p","2","-L","New Label",wf]) + if ni: return t.license() t.passphrase("MMGen wallet",cfgs['1']['wpasswd'],pwtype="old") t.expect_getend("Hash preset changed to ") @@ -883,22 +935,24 @@ class MMGenTestSuite(object): t.written_to_file("MMGen wallet") ok() - def walletchk_beg(self,name,args): - t = MMGenExpect(name,"mmgen-walletchk", args) - t.expect("Getting MMGen wallet from file '%s'" % args[-1]) + def walletchk(self,name,wf,pf): + args = ["-P",pf,"-q"] if ni and pf else [] + hp = cfg['hash_preset'] if 'hash_preset' in cfg else '1' + t = MMGenExpect(name,"mmgen-walletchk", args + ["-p",hp] + [wf]) + if ni: return + t.expect("Getting MMGen wallet from file '%s'" % wf) t.passphrase("MMGen wallet",cfg['wpasswd']) t.expect("Passphrase is OK") t.expect_getend("Valid MMGen wallet for seed ID ") - return t - - def walletchk(self,name,walletfile): - self.walletchk_beg(name,[walletfile]) ok() walletchk_newpass = walletchk - def addrgen(self,name,walletfile,check_ref=False): - t = MMGenExpect(name,"mmgen-addrgen",["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']]) + def addrgen(self,name,wf,pf,check_ref=False): + add_args = ["-P",pf,"-q"] if ni else [] + t = MMGenExpect(name,"mmgen-addrgen", add_args + + ["-d",cfg['tmpdir'],wf,cfg['addr_idx_list']]) + if ni: return t.license() t.passphrase("MMGen wallet",cfg['wpasswd']) t.expect("Passphrase is OK") @@ -909,16 +963,18 @@ class MMGenTestSuite(object): t.written_to_file("Addresses",oo=True) ok() - def refaddrgen(self,name,walletfile): + def refaddrgen(self,name,wf): d = " (%s-bit seed)" % cfg['seed_len'] - self.addrgen(name,walletfile,check_ref=True) + self.addrgen(name,wf,pf="",check_ref=True) refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen def addrimport(self,name,addrfile): + add_args = ["-q","-t"] if ni else [] outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments") add_comments_to_addr_file(addrfile,outfile) - t = MMGenExpect(name,"mmgen-addrimport",[outfile]) + t = MMGenExpect(name,"mmgen-addrimport", add_args + [outfile]) + if ni: return t.expect_getend(r"Checksum for address data .*\[.*\]: ",regex=True) t.expect_getend("Validating addresses...OK. ") t.expect("Type uppercase 'YES' to confirm: ","\n") @@ -976,7 +1032,13 @@ class MMGenTestSuite(object): end_silence() if opt.verbose or opt.exact_output: sys.stderr.write("\n") - t = MMGenExpect(name,"mmgen-txcreate",cmd_args) + add_args = ["-q"] if ni else [] + if ni: + m = "Answer the interactive prompts as follows:\n" + \ + " 'y', 'y', 'q', '1-8', ENTER, ENTER, ENTER, 'y'" + msg(green(m)) + t = MMGenExpect(name,"mmgen-txcreate",add_args + cmd_args) + if ni: return t.license() for num in tx_data.keys(): t.expect_getend("Getting address data from file ") @@ -1014,9 +1076,14 @@ class MMGenTestSuite(object): add = " #" + tnum if tnum else "" t.written_to_file("Signed transaction" + add) - def txsign(self,name,txfile,walletfile,save=True): - t = MMGenExpect(name,"mmgen-txsign", - ["-d",cfg['tmpdir'],txfile,walletfile]) + def txsign(self,name,txfile,wf,pf="",save=True): + add_args = ["-q","-P",pf] if ni else [] + if ni: + m = "Answer the interactive prompts as follows:\n ENTER, ENTER, 'y'" + msg(green(m)) + t = MMGenExpect(name,"mmgen-txsign", add_args + + ["-d",cfg['tmpdir'],txfile,wf]) + if ni: return t.license() t.tx_view() t.passphrase("MMGen wallet",cfg['wpasswd']) @@ -1088,11 +1155,11 @@ class MMGenTestSuite(object): self.export_incog( name,wf,desc="hidden incognito data",out_fmt="hi",add_args=add_args) - def addrgen_seed(self,name,walletfile,foo,desc="seed data",in_fmt="seed"): + def addrgen_seed(self,name,wf,foo,desc="seed data",in_fmt="seed"): stdout = (False,True)[int(desc=="seed data")] #capture output to screen once add_arg = ([],["-S"])[int(stdout)] t = MMGenExpect(name,"mmgen-addrgen", add_arg + - ["-i"+in_fmt,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']]) + ["-i"+in_fmt,"-d",cfg['tmpdir'],wf,cfg['addr_idx_list']]) t.license() t.expect_getend("Valid %s for seed ID " % desc) vmsg("Comparing generated checksum with checksum from previous address file") @@ -1130,9 +1197,9 @@ class MMGenTestSuite(object): self.addrgen_incog(name,[],"",in_fmt="hi",desc="hidden incognito data", args=["-H","%s,%s"%(rf,hincog_offset),"-l",str(hincog_seedlen)]) - def keyaddrgen(self,name,walletfile,check_ref=False): + def keyaddrgen(self,name,wf,check_ref=False): t = MMGenExpect(name,"mmgen-keygen", - ["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']]) + ["-d",cfg['tmpdir'],wf,cfg['addr_idx_list']]) t.license() t.passphrase("MMGen wallet",cfg['wpasswd']) chk = t.expect_getend(r"Checksum for key-address data .*?: ",regex=True) @@ -1145,8 +1212,8 @@ class MMGenTestSuite(object): t.written_to_file("Secret keys",oo=True) ok() - def refkeyaddrgen(self,name,walletfile): - self.keyaddrgen(name,walletfile,check_ref=True) + def refkeyaddrgen(self,name,wf): + self.keyaddrgen(name,wf,check_ref=True) refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen @@ -1163,8 +1230,8 @@ class MMGenTestSuite(object): def walletgen2(self,name): self.walletgen(name,seed_len=128) - def addrgen2(self,name,walletfile): - self.addrgen(name,walletfile) + def addrgen2(self,name,wf): + self.addrgen(name,wf,pf="") def txcreate2(self,name,addrfile): self.txcreate_common(name,sources=['2']) @@ -1178,14 +1245,14 @@ class MMGenTestSuite(object): self.txsign_end(t,cnum) ok() - def export_mnemonic2(self,name,walletfile): - self.export_mnemonic(name,walletfile) + def export_mnemonic2(self,name,wf): + self.export_mnemonic(name,wf) def walletgen3(self,name): self.walletgen(name) - def addrgen3(self,name,walletfile): - self.addrgen(name,walletfile) + def addrgen3(self,name,wf): + self.addrgen(name,wf,pf="") def txcreate3(self,name,addrfile1,addrfile2): self.txcreate_common(name,sources=['1','3']) @@ -1215,8 +1282,8 @@ class MMGenTestSuite(object): def walletgen4(self,name): self.brainwalletgen_pwfile(name) - def addrgen4(self,name,walletfile): - self.addrgen(name,walletfile) + def addrgen4(self,name,wf): + self.addrgen(name,wf,pf="") def txcreate4(self,name,f1,f2,f3,f4): self.txcreate_common(name,sources=['1','2','3','4'],non_mmgen_input='4') @@ -1272,6 +1339,27 @@ class MMGenTestSuite(object): os.unlink(f1) cmp_or_die(hincog_offset,int(o)) + def pywallet(self,name): # TODO - check output + pf = get_tmpfile_fn(cfg,pwfile) + write_data_to_file(pf,cfg['wpasswd']+"\n",silent=True) + args = ["-q","-P",pf] if ni else [] + unenc_wf = os.path.join(ref_dir,"wallet-unenc.dat") + enc_wf = os.path.join(ref_dir,"wallet-enc.dat") + for wf,enc in (unenc_wf,False),(enc_wf,True): + for w,o,pk in ( + ("addresses","a",False), + ("private keys","k",True), + ("json dump","j",True) + ): + ed = "(%sencrypted wallet, %s)" % (("un","")[int(enc)],w) + t = MMGenExpect(name,"mmgen-pywallet", args + + ["-"+o,"-d",cfg['tmpdir']] + [wf], extra_desc=ed) + if ni: continue + if pk and enc and not ni: + t.expect("Enter password: ",cfg['wpasswd']+"\n") + t.written_to_file(capfirst(w),oo=True) + if not ni: ok() + def walletconv_out(self,name,desc,out_fmt="w",uopts=[],uopts_chk=[],pw=False): opts = ["-d",cfg['tmpdir'],"-r10","-p1","-o",out_fmt] + uopts infile = os.path.join(ref_dir,cfg['seed_id']+".mmwords") @@ -1397,7 +1485,7 @@ class MMGenTestSuite(object): def ref_wallet_chk(self,name): wf = os.path.join(ref_dir,cfg['ref_wallet']) - self.walletchk(name,wf) + self.walletchk(name,wf,pf="") ref_wallet_chk1 = ref_wallet_chk2 = ref_wallet_chk3 = ref_wallet_chk diff --git a/test/tooltest.py b/test/tooltest.py index ff1dbae2..f3831104 100755 --- a/test/tooltest.py +++ b/test/tooltest.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Chdir to repo root. # Since script is not in repo root, fix sys.path so that modules are