mmgen-wallet/test/test.py
2015-10-25 13:04:30 +03:00

1790 lines
58 KiB
Python
Executable file

#!/usr/bin/env python
# Chdir to repo root.
# Since script is not in repo root, fix sys.path so that modules are
# imported from repo, not system.
import sys,os
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 *
start_mscolor()
from mmgen.test import *
tb_cmd = "scripts/traceback.py"
hincog_fn = "rand_data"
hincog_bytes = 1024*1024
hincog_offset = 98765
hincog_seedlen = 256
incog_id_fn = "incog_id"
non_mmgen_fn = "btckey"
pwfile = "passwd_file"
ref_dir = os.path.join("test","ref")
ref_wallet_brainpass = "abc"
ref_wallet_hash_preset = "1"
ref_wallet_incog_offset = 123
ref_bw_hash_preset = "1"
ref_bw_file = "wallet.mmbrain"
ref_bw_file_spc = "wallet-spaced.mmbrain"
ref_kafile_pass = "kafile password"
ref_kafile_hash_preset = "1"
ref_enc_fn = "sample-text.mmenc"
tool_enc_passwd = "Scrypt it, don't hash it!"
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,
'seed_id': "FE3C6545",
'ref_bw_seed_id': "33F10310",
'addrfile_chk': "B230 7526 638F 38CB 8FDC 8B76",
'keyaddrfile_chk': "CF83 32FB 8A8B 08E2 0F00 D601",
'wpasswd': "reference password",
'ref_wallet': "FE3C6545-D782B529[128,1].mmdat",
'ic_wallet': "FE3C6545-E29303EA-5E229E30[128,1].mmincog",
'ic_wallet_hex': "FE3C6545-BC4BE3F2-32586837[128,1].mmincox",
'hic_wallet': "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
'hic_wallet_old': "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
'tmpdir': os.path.join("test","tmp6"),
'kapasswd': "",
'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
'dep_generators': {
'mmdat': "refwalletgen1",
pwfile: "refwalletgen1",
'addrs': "refaddrgen1",
'akeys.mmenc': "refkeyaddrgen1"
},
},
'7': {
'name': "reference wallet check (192-bit)",
'seed_len': 192,
'seed_id': "1378FC64",
'ref_bw_seed_id': "CE918388",
'addrfile_chk': "8C17 A5FA 0470 6E89 3A87 8182",
'keyaddrfile_chk': "9648 5132 B98E 3AD9 6FC3 C5AD",
'wpasswd': "reference password",
'ref_wallet': "1378FC64-6F0F9BB4[192,1].mmdat",
'ic_wallet': "1378FC64-2907DE97-F980D21F[192,1].mmincog",
'ic_wallet_hex': "1378FC64-4DCB5174-872806A7[192,1].mmincox",
'hic_wallet': "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
'hic_wallet_old': "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
'tmpdir': os.path.join("test","tmp7"),
'kapasswd': "",
'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
'dep_generators': {
'mmdat': "refwalletgen2",
pwfile: "refwalletgen2",
'addrs': "refaddrgen2",
'akeys.mmenc': "refkeyaddrgen2"
},
},
'8': {
'name': "reference wallet check (256-bit)",
'seed_len': 256,
'seed_id': "98831F3A",
'ref_bw_seed_id': "B48CD7FC",
'addrfile_chk': "6FEF 6FB9 7B13 5D91 854A 0BD3",
'keyaddrfile_chk': "9F2D D781 1812 8BAD C396 9DEB",
'wpasswd': "reference password",
'ref_wallet': "98831F3A-27F2BF93[256,1].mmdat",
'ref_addrfile': "98831F3A[1,31-33,500-501,1010-1011].addrs",
'ref_keyaddrfile': "98831F3A[1,31-33,500-501,1010-1011].akeys.mmenc",
'ref_addrfile_chksum': "6FEF 6FB9 7B13 5D91 854A 0BD3",
'ref_keyaddrfile_chksum': "9F2D D781 1812 8BAD C396 9DEB",
# 'ref_fake_unspent_data':"98831F3A_unspent.json",
'ref_tx_file': "tx_FFB367[1.234].raw",
'ic_wallet': "98831F3A-5482381C-18460FB1[256,1].mmincog",
'ic_wallet_hex': "98831F3A-1630A9F2-870376A9[256,1].mmincox",
'hic_wallet': "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
'hic_wallet_old': "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
'tmpdir': os.path.join("test","tmp8"),
'kapasswd': "",
'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
'dep_generators': {
'mmdat': "refwalletgen3",
pwfile: "refwalletgen3",
'addrs': "refaddrgen3",
'akeys.mmenc': "refkeyaddrgen3"
},
},
'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",
# 'tool_encrypt_ref.in': "tool_encrypt_ref",
# 'tool_encrypt_ref.in.mmenc': "tool_encrypt_ref",
},
},
}
from copy import deepcopy
for a,b in ('6','11'),('7','12'),('8','13'):
cfgs[b] = deepcopy(cfgs[a])
cfgs[b]['tmpdir'] = os.path.join("test","tmp"+b)
from collections import OrderedDict
cmd_group = OrderedDict()
cmd_group['help'] = OrderedDict([
# test description depends
['helpscreens', (1,'help screens', [],1)],
])
cmd_group['main'] = OrderedDict([
['walletgen', (1,'wallet generation', [[[],1]],1)],
# ['walletchk', (1,'wallet check', [[["mmdat"],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]])],
['export_mnemonic', (1,'seed export to mmwords format', [[["mmdat"],1]])],
['export_incog', (1,'seed export to mmincog format', [[["mmdat"],1]])],
['export_incog_hex',(1,'seed export to mmincog hex format', [[["mmdat"],1]])],
['export_incog_hidden',(1,'seed export to hidden mmincog format', [[["mmdat"],1]])],
['addrgen_seed', (1,'address generation from mmseed file', [[["mmseed","addrs"],1]])],
['addrgen_mnemonic',(1,'address generation from mmwords file',[[["mmwords","addrs"],1]])],
['addrgen_incog', (1,'address generation from mmincog file',[[["mmincog","addrs"],1]])],
['addrgen_incog_hex',(1,'address generation from mmincog hex file',[[["mmincox","addrs"],1]])],
['addrgen_incog_hidden',(1,'address generation from hidden mmincog file', [[[hincog_fn,"addrs"],1]])],
['keyaddrgen', (1,'key-address file generation', [[["mmdat",pwfile],1]])],
['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
['walletgen2',(2,'wallet generation (2), 128-bit seed', [])],
['addrgen2', (2,'address generation (2)', [[["mmdat"],2]])],
['txcreate2', (2,'transaction creation (2)', [[["addrs"],2]])],
['txsign2', (2,'transaction signing, two transactions',[[["mmdat","raw"],1],[["mmdat","raw"],2]])],
['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
['walletgen3',(3,'wallet generation (3)', [])],
['addrgen3', (3,'address generation (3)', [[["mmdat"],3]])],
['txcreate3', (3,'tx creation with inputs and outputs from two wallets', [[["addrs"],1],[["addrs"],3]])],
['txsign3', (3,'tx signing with inputs and outputs from two wallets',[[["mmdat"],1],[["mmdat","raw"],3]])],
['walletgen4',(4,'wallet generation (4) (brainwallet)', [])],
['addrgen4', (4,'address generation (4)', [[["mmdat"],4]])],
['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]])],
['txsign4', (4,'tx signing with inputs and outputs from incog file, mnemonic file, wallet and brainwallet, plus non-MMGen inputs and outputs', [[["mmincog"],1],[["mmwords"],2],[["mmdat"],3],[["mmbrain","raw"],4]])],
])
cmd_group['tool'] = OrderedDict([
['tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [],1)],
['tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[cfgs['9']['tool_enc_infn'],cfgs['9']['tool_enc_infn']+".mmenc"],9]],1)],
# ['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
cmd_group['ref'] = (
# reading
('ref_wallet_chk', ([],'saved reference wallet')),
('ref_seed_chk', ([],'saved seed file')),
('ref_mn_chk', ([],'saved mnemonic file')),
('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
('ref_brain_chk', ([],'saved brainwallet')),
# generating new reference ('abc' brainwallet) files:
('refwalletgen', ([],'gen new refwallet')),
('refaddrgen', (["mmdat",pwfile],'new refwallet addr chksum')),
('refkeyaddrgen', (["mmdat",pwfile],'new refwallet key-addr chksum'))
)
# misc. saved reference data
cmd_group['ref_other'] = (
('ref_addrfile_chk', 'saved reference address file'),
('ref_keyaddrfile_chk','saved reference key-address file'),
# Create the fake inputs:
# ('txcreate8', 'transaction creation (8)'),
('ref_tx_chk', 'saved reference tx file'),
('ref_brain_chk_spc3', 'saved brainwallet (non-standard spacing)'),
('ref_tool_decrypt', 'decryption of saved MMGen-encrypted file'),
)
# mmgen-walletconv:
cmd_group['conv_in'] = ( # reading
('ref_wallet_conv', 'conversion of saved reference wallet'),
('ref_mn_conv', 'conversion of saved mnemonic'),
('ref_seed_conv', 'conversion of saved seed file'),
('ref_brain_conv', 'conversion of ref brainwallet'),
('ref_incog_conv', 'conversion of saved incog wallet'),
('ref_incox_conv', 'conversion of saved hex incog wallet'),
('ref_hincog_conv', 'conversion of saved hidden incog wallet'),
('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)')
)
cmd_group['conv_out'] = ( # writing
('ref_wallet_conv_out', 'ref seed conversion to wallet'),
('ref_mn_conv_out', 'ref seed conversion to mnemonic'),
('ref_seed_conv_out', 'ref seed conversion to seed'),
('ref_incog_conv_out', 'ref seed conversion to incog data'),
('ref_incox_conv_out', 'ref seed conversion to hex incog data'),
('ref_hincog_conv_out', 'ref seed conversion to hidden incog data')
)
cmd_list = OrderedDict()
for k in cmd_group: cmd_list[k] = []
cmd_data = OrderedDict()
for k,v in (
('help', ("help screens",[])),
('main', ("basic operations",[1,2,3,4,5])),
('tool', ("tools",[9]))
):
cmd_data['info_'+k] = v
for i in cmd_group[k]:
cmd_list[k].append(i)
cmd_data[i] = cmd_group[k][i]
cmd_data['info_ref'] = "reference data",[6,7,8]
for a,b in cmd_group['ref']:
for i,j in (1,128),(2,192),(3,256):
k = a+str(i)
cmd_list['ref'].append(k)
cmd_data[k] = (5+i,"%s (%s-bit)" % (b[1],j),[[b[0],5+i]],1)
cmd_data['info_ref_other'] = "other reference data",[8]
for a,b in cmd_group['ref_other']:
cmd_list['ref_other'].append(a)
cmd_data[a] = (8,b,[[[],8]],1)
cmd_data['info_conv_in'] = "wallet conversion from reference data",[11,12,13]
for a,b in cmd_group['conv_in']:
for i,j in (1,128),(2,192),(3,256):
k = a+str(i)
cmd_list['conv_in'].append(k)
cmd_data[k] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]],1)
cmd_data['info_conv_out'] = "wallet conversion to reference data",[11,12,13]
for a,b in cmd_group['conv_out']:
for i,j in (1,128),(2,192),(3,256):
k = a+str(i)
cmd_list['conv_out'].append(k)
cmd_data[k] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]],1)
utils = {
'check_deps': 'check dependencies for specified command',
'clean': 'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
}
addrs_per_wallet = 8
# total of two outputs must be < 10 BTC
for k in cfgs:
cfgs[k]['amts'] = [0,0]
for idx,mod in (0,6),(1,4):
cfgs[k]['amts'][idx] = "%s.%s" % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
meta_cmds = OrderedDict([
['ref1', ("refwalletgen1","refaddrgen1","refkeyaddrgen1")],
['ref2', ("refwalletgen2","refaddrgen2","refkeyaddrgen2")],
['ref3', ("refwalletgen3","refaddrgen3","refkeyaddrgen3")],
['gen', ("walletgen","addrgen")],
['pass', ("passchg","walletchk_newpass")],
['tx', ("addrimport","txcreate","txsign","txsend")],
['export', [k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1]],
['gen_sp', [k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1]],
['online', ("keyaddrgen","txsign_keyaddr")],
['2', [k for k in cmd_data if cmd_data[k][0] == 2]],
['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]],
['saved_ref1', [c[0]+"1" for c in cmd_group['ref']]],
['saved_ref2', [c[0]+"2" for c in cmd_group['ref']]],
['saved_ref3', [c[0]+"3" for c in cmd_group['ref']]],
['saved_ref_other', [c[0] for c in cmd_group['ref_other']]],
['saved_ref_conv_in1', [c[0]+"1" for c in cmd_group['conv_in']]],
['saved_ref_conv_in2', [c[0]+"2" for c in cmd_group['conv_in']]],
['saved_ref_conv_in3', [c[0]+"3" for c in cmd_group['conv_in']]],
['saved_ref_conv_out1', [c[0]+"1" for c in cmd_group['conv_out']]],
['saved_ref_conv_out2', [c[0]+"2" for c in cmd_group['conv_out']]],
['saved_ref_conv_out3', [c[0]+"3" for c in cmd_group['conv_out']]],
])
del cmd_group
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.
-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 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': """
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)
# Disable MS color in spawned scripts due to bad interactions
os.environ["MMGEN_NOMSCOLOR"] = "1"
os.environ["MMGEN_NOLICENSE"] = "1"
if opt.debug_scripts: os.environ["MMGEN_DEBUG"] = "1"
if opt.buf_keypress:
send_delay = 0.3
else:
send_delay = 0
os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
if opt.exact_output:
def msg(s): pass
vmsg = vmsg_r = msg_r = msg
else:
def msg(s): sys.stderr.write(s+"\n")
def vmsg(s):
if opt.verbose: sys.stderr.write(s+"\n")
def msg_r(s): sys.stderr.write(s)
def vmsg_r(s):
if opt.verbose: sys.stderr.write(s)
stderr_save = sys.stderr
def silence():
if not (opt.verbose or opt.exact_output):
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):
sys.stderr = stderr_save
def errmsg(s): stderr_save.write(s+"\n")
def errmsg_r(s): stderr_save.write(s)
if opt.list_cmds:
fs = " {:<{w}} - {}"
Msg(green("AVAILABLE COMMANDS:"))
w = max([len(i) for i in cmd_data])
for cmd in cmd_data:
if cmd[:5] == "info_":
m = capfirst(cmd_data[cmd][0])
msg(green(" %s:"% m))
continue
Msg(" "+fs.format(cmd,cmd_data[cmd][1],w=w))
w = max([len(i) for i in meta_cmds])
Msg(green("\nAVAILABLE METACOMMANDS:"))
for cmd in meta_cmds:
Msg(fs.format(cmd," ".join(meta_cmds[cmd]),w=w))
w = max([len(i) for i in cmd_list])
Msg(green("\nAVAILABLE COMMAND GROUPS:"))
for g in cmd_list:
Msg(fs.format(g," ".join(cmd_list[g]),w=w))
Msg(green("\nAVAILABLE UTILITIES:"))
w = max([len(i) for i in utils])
for cmd in sorted(utils):
Msg(fs.format(cmd,utils[cmd],w=w))
sys.exit()
import time,re
try:
import pexpect
except: # Windows
m = green("MS Windows detected (or missing pexpect module). Skipping some tests.\n")
n = green("Interactive mode. User prompts will be ")
p = grnbg("HIGHLIGHTED IN GREEN")
q = green(".\nContinue?")
ni = True
from mmgen.util import keypress_confirm
if not keypress_confirm(m+n+p+q,default_yes=True):
errmsg("Exiting at user request")
sys.exit()
from mmgen.util import get_data_from_file,write_data_to_file,get_lines_from_file
def my_send(p,t,delay=send_delay,s=False):
if delay: time.sleep(delay)
ret = p.send(t) # returns num bytes written
if delay: time.sleep(delay)
if opt.verbose:
ls = "" if opt.debug or not s else " "
es = "" if s else " "
msg("%sSEND %s%s" % (ls,es,yellow("'%s'"%t.replace('\n',r'\n'))))
return ret
def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
quo = "'" if type(s) == str else ""
if opt.verbose: msg_r("EXPECT %s" % yellow(quo+str(s)+quo))
else: msg_r("+")
try:
if s == '': ret = 0
else:
f = p.expect if regex else p.expect_exact
ret = f(s,timeout=3)
except pexpect.TIMEOUT:
errmsg(red("\nERROR. Expect %s%s%s timed out. Exiting" % (quo,s,quo)))
sys.exit(1)
if opt.debug or (opt.verbose and type(s) != str): msg_r(" ==> %s " % ret)
if ret == -1:
errmsg("Error. Expect returned %s" % ret)
sys.exit(1)
else:
if t == '':
if not nonl: vmsg("")
else:
my_send(p,t,delay,s)
return ret
def get_file_with_ext(ext,mydir,delete=True,no_dot=False):
dot = ('.','')[int(no_dot)]
flist = [os.path.join(mydir,f) for f in os.listdir(mydir)
if f == ext or f[-len(dot+ext):] == dot+ext]
if not flist: return False
if len(flist) > 1:
if delete:
if not opt.quiet:
msg("Multiple *.%s files in '%s' - deleting" % (ext,mydir))
for f in flist: os.unlink(f)
return False
else:
return flist[0]
def get_addrfile_checksum(display=False):
addrfile = get_file_with_ext("addrs",cfg['tmpdir'])
silence()
from mmgen.addr import AddrInfo
chk = AddrInfo(addrfile).checksum
if opt.verbose and display: msg("Checksum: %s" % cyan(chk))
end_silence()
return chk
def verify_checksum_or_exit(checksum,chk):
if checksum != chk:
errmsg(red("Checksum error: %s" % chk))
sys.exit(1)
vmsg(green("Checksums match: %s") % (cyan(chk)))
class MMGenExpect(object):
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))]
if extra_desc: desc += " " + extra_desc
if opt.verbose or opt.exact_output:
sys.stderr.write(
green("Testing: %s\nExecuting " % desc) +
cyan("'%s %s'\n" % (mmgen_cmd," ".join(cmd_args)))
)
else:
m = "Testing %s: " % desc
msg_r(yellow(m) if ni else m)
if opt.direct_exec or ni:
msg("")
from subprocess import call,check_output
f = (call,check_output)[int(no_output)]
ret = f(["python", mmgen_cmd] + cmd_args)
if f == call and ret != 0:
m = "Warning: process returned a non-zero exit status (%s)"
msg(red(m % ret))
else:
if opt.traceback:
cmd_args = [mmgen_cmd] + cmd_args
mmgen_cmd = tb_cmd
self.p = pexpect.spawn(mmgen_cmd,cmd_args)
if opt.exact_output: self.p.logfile = sys.stdout
def license(self):
if "MMGEN_NOLICENSE" in os.environ: return
p = "'w' for conditions and warranty info, or 'c' to continue: "
my_expect(self.p,p,'c')
def label(self,label="Test Label"):
p = "Enter a wallet label, or hit ENTER for no label: "
my_expect(self.p,p,label+"\n")
def usr_rand_out(self,saved=False):
m = "%suser-supplied entropy" % ("saved " if saved else "")
my_expect(self.p,"Generating encryption key from OS random data plus " + m)
def usr_rand(self,num_chars):
rand_chars = list(getrandstr(num_chars,no_space=True))
my_expect(self.p,'symbols left: ','x')
try:
vmsg_r("SEND ")
while self.p.expect('left: ',0.1) == 0:
ch = rand_chars.pop(0)
msg_r(yellow(ch)+" " if opt.verbose else "+")
self.p.send(ch)
except:
vmsg("EOT")
my_expect(self.p,"ENTER to continue: ",'\n')
def passphrase_new(self,desc,passphrase):
my_expect(self.p,("Enter passphrase for %s: " % desc), passphrase+"\n")
my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
def passphrase(self,desc,passphrase,pwtype=""):
if pwtype: pwtype += " "
my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,desc)),
passphrase+"\n",regex=True)
def hash_preset(self,desc,preset=''):
my_expect(self.p,("Enter hash preset for %s" % desc))
my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
def written_to_file(self,desc,overwrite_unlikely=False,query="Overwrite? ",oo=False):
s1 = "%s written to file " % desc
s2 = query + "Type uppercase 'YES' to confirm: "
ret = my_expect(self.p,s1 if overwrite_unlikely else [s1,s2])
if ret == 1:
my_send(self.p,"YES\n")
# if oo:
outfile = self.expect_getend("Overwriting file '").rstrip("'")
return outfile
# else:
# ret = my_expect(self.p,s1)
outfile = self.p.readline().strip().strip("'")
vmsg("%s file: %s" % (desc,cyan(outfile.replace("'",""))))
return outfile
def no_overwrite(self):
self.expect("Overwrite? Type uppercase 'YES' to confirm: ","\n")
self.expect("Exiting at user request")
def tx_view(self):
my_expect(self.p,r"View .*?transaction.*? \(y\)es, \(N\)o, pager \(v\)iew.*?: ","\n",regex=True)
def expect_getend(self,s,regex=False):
ret = self.expect(s,regex=regex,nonl=True)
end = self.readline().strip()
vmsg(" ==> %s" % cyan(end))
return end
def interactive(self):
return self.p.interact()
def logfile(self,arg):
self.p.logfile = arg
def expect(self,*args,**kwargs):
return my_expect(self.p,*args,**kwargs)
def send(self,*args,**kwargs):
return my_send(self.p,*args,**kwargs)
def readline(self):
return self.p.readline()
def close(self):
return self.p.close()
def readlines(self):
return [l.rstrip()+"\n" for l in self.p.readlines()]
def read(self,n=None):
return self.p.read(n)
from mmgen.rpc.data import TransactionInfo
from decimal import Decimal
from mmgen.bitcoin import verify_addr
def add_fake_unspent_entry(out,address,comment):
out.append(TransactionInfo(
account = unicode(comment),
vout = int(getrandnum(4) % 8),
txid = unicode(hexlify(os.urandom(32))),
amount = Decimal("%s.%s" % (10+(getrandnum(4) % 40), getrandnum(4) % 100000000)),
address = address,
spendable = False,
scriptPubKey = ("76a914"+verify_addr(address,return_hex=True)+"88ac"),
confirmations = getrandnum(4) % 500
))
def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input=''):
out = []
for s in tx_data:
sid = tx_data[s]['sid']
a = adata.addrinfo(sid)
for n,(idx,btcaddr) in enumerate(a.addrpairs(),1):
lbl = (""," addr %02i" % n)[int(bool(n%3))]
add_fake_unspent_entry(out,btcaddr,"%s:%s%s" % (sid,idx,lbl))
if non_mmgen_input:
from mmgen.bitcoin import privnum2addr,hextowif
privnum = getrandnum(32)
btcaddr = privnum2addr(privnum,compressed=True)
of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
write_data_to_file(of, hextowif("{:064x}".format(privnum),
compressed=True)+"\n","compressed bitcoin key",silent=True)
add_fake_unspent_entry(out,btcaddr,"Non-MMGen address")
# msg("\n".join([repr(o) for o in out])); sys.exit()
write_data_to_file(unspent_data_file,repr(out),"Unspent outputs",silent=True)
def add_comments_to_addr_file(addrfile,outfile):
silence()
msg(green("Adding comments to address file '%s'" % addrfile))
from mmgen.addr import AddrInfo
a = AddrInfo(addrfile)
for n,idx in enumerate(a.idxs(),1):
if n % 2: a.set_comment(idx,"Test address %s" % n)
write_data_to_file(outfile,a.fmt_data(enable_comments=True),silent=True)
end_silence()
def make_brainwallet_file(fn):
# Print random words with random whitespace in between
from mmgen.mn_tirosh import words
wl = words.split()
nwords,ws_list,max_spaces = 10," \n",5
def rand_ws_seq():
nchars = getrandnum(1) % max_spaces + 1
return "".join([ws_list[getrandnum(1)%len(ws_list)] for i in range(nchars)])
rand_pairs = [wl[getrandnum(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
d = "".join(rand_pairs).rstrip() + "\n"
if opt.verbose: msg_r("Brainwallet password:\n%s" % cyan(d))
write_data_to_file(fn,d,"brainwallet password",silent=True)
def do_between():
if opt.pause:
from mmgen.util import keypress_confirm
if keypress_confirm(green("Continue?"),default_yes=True):
if opt.verbose or opt.exact_output: sys.stderr.write("\n")
else:
errmsg("Exiting at user request")
sys.exit()
elif opt.verbose or opt.exact_output:
sys.stderr.write("\n")
rebuild_list = OrderedDict()
def check_needs_rerun(
ts,
cmd,
build=False,
root=True,
force_delete=False,
dpy=False
):
rerun = True if root else False # force_delete is not passed to recursive call
fns = []
if force_delete or not root:
# does cmd produce a needed dependency(ies)?
ret = ts.get_num_exts_for_cmd(cmd,dpy)
if ret:
for ext in ret[1]:
fn = get_file_with_ext(ext,cfgs[ret[0]]['tmpdir'],delete=build)
if fn:
if force_delete: os.unlink(fn)
else: fns.append(fn)
else: rerun = True
fdeps = ts.generate_file_deps(cmd)
cdeps = ts.generate_cmd_deps(fdeps)
for fn in fns:
my_age = os.stat(fn).st_mtime
for num,ext in fdeps:
f = get_file_with_ext(ext,cfgs[num]['tmpdir'],delete=build)
if f and os.stat(f).st_mtime > my_age: rerun = True
for cdep in cdeps:
if check_needs_rerun(ts,cdep,build=build,root=False,dpy=cmd): rerun = True
if build:
if rerun:
for fn in fns:
if not root: os.unlink(fn)
ts.do_cmd(cmd)
if not root: do_between()
else:
# If prog produces multiple files:
if cmd not in rebuild_list or rerun == True:
rebuild_list[cmd] = (rerun,fns[0] if fns else "") # FIX
return rerun
def refcheck(desc,chk,refchk):
vmsg("Comparing %s '%s' to stored reference" % (desc,chk))
if chk == refchk:
ok()
else:
if not opt.verbose: errmsg("")
errmsg(red("""
Fatal error - %s '%s' does not match reference value '%s'. Aborting test
""".strip() % (desc,chk,refchk)))
sys.exit(3)
def check_deps(cmds):
if len(cmds) != 1:
msg("Usage: %s check_deps <command>" % g.prog_name)
sys.exit(1)
cmd = cmds[0]
if cmd not in cmd_data:
msg("'%s': unrecognized command" % cmd)
sys.exit(1)
if not opt.quiet:
msg("Checking dependencies for '%s'" % (cmd))
check_needs_rerun(ts,cmd,build=False)
w = max(len(i) for i in rebuild_list) + 1
for cmd in rebuild_list:
c = rebuild_list[cmd]
m = "Rebuild" if (c[0] and c[1]) else "Build" if c[0] else "OK"
msg("cmd {:<{w}} {}".format(cmd+":", m, w=w))
# mmsg(cmd,c)
def clean(usr_dirs=[]):
all_dirs = MMGenTestSuite().list_tmp_dirs()
dirs = (usr_dirs or all_dirs)
for d in sorted(dirs):
if str(d) in all_dirs:
cleandir(all_dirs[str(d)])
else:
die(1,"%s: invalid directory number" % d)
class MMGenTestSuite(object):
def __init__(self):
pass
def list_tmp_dirs(self):
d = {}
for k in cfgs: d[k] = cfgs[k]['tmpdir']
return d
def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
num = str(cmd_data[cmd][0])
dgl = cfgs[num]['dep_generators']
# mmsg(num,cmd,dgl)
if cmd in dgl.values():
exts = [k for k in dgl if dgl[k] == cmd]
return (num,exts)
else:
return None
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]
global cfg
cfg = cfgs[str(cmd_data[cmd][0])]
self.__class__.__dict__[cmd](*([self,cmd] + al))
def generate_file_deps(self,cmd):
return [(str(n),e) for exts,n in cmd_data[cmd][2] for e in exts]
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):
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 + add_args)
if ni: return
t.license()
t.usr_rand(10)
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
t.label()
t.written_to_file("MMGen wallet")
ok()
def brainwalletgen_ref(self,name):
sl_arg = "-l%s" % cfg['seed_len']
hp_arg = "-p%s" % ref_wallet_hash_preset
label = "test.py ref. wallet (pw '%s', seed len %s)" \
% (ref_wallet_brainpass,cfg['seed_len'])
bf = "ref.mmbrain"
args = ["-d",cfg['tmpdir'],hp_arg,sl_arg,"-ib","-L",label]
write_to_tmpfile(cfg,bf,ref_wallet_brainpass)
write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
if ni:
add_args = ["-r0", "-q", "-P%s" % get_tmpfile_fn(cfg,pwfile),
get_tmpfile_fn(cfg,bf)]
else:
add_args = ["-r10"]
t = MMGenExpect(name,"mmgen-walletconv", args + add_args)
if ni: return
t.license()
t.expect("Enter brainwallet: ", ref_wallet_brainpass+"\n")
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
t.usr_rand(10)
sid = t.written_to_file("MMGen wallet").split("-")[0].split("/")[-1]
refcheck("Seed ID",sid,cfg['seed_id'])
def refwalletgen(self,name): self.brainwalletgen_ref(name)
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 ")
t.passphrase("MMGen wallet",cfg['wpasswd'],pwtype="new")
t.expect("Repeat passphrase: ",cfg['wpasswd']+"\n")
t.usr_rand(16)
t.expect_getend("Label changed to ")
# t.expect_getend("Key ID changed: ")
t.written_to_file("MMGen wallet")
ok()
def walletchk(self,name,wf,pf,desc="MMGen wallet",
add_args=[],sid=None,pw=False,extra_desc=""):
args = ["-P",pf,"-q"] if ni and pf else []
hp = cfg['hash_preset'] if 'hash_preset' in cfg else '1'
wf_arg = [wf] if wf else []
t = MMGenExpect(name,"mmgen-walletchk",
add_args+args+["-p",hp]+wf_arg,
extra_desc=extra_desc)
if ni:
if sid:
n = "" if desc == "MMGen wallet" else " should be"
m = grnbg("Seed ID%s:" % n)
msg(grnbg("%s %s" % (m,cyan(sid))))
return
if desc != "hidden incognito data":
t.expect("Getting %s from file '%s'" % (desc,wf))
if pw:
t.passphrase(desc,cfg['wpasswd'])
t.expect(
["Passphrase is OK", "Passphrase.* are correct"],
regex=True
)
chk = t.expect_getend("Valid %s for Seed ID " % desc)[:8]
if sid: cmp_or_die(chk,sid)
else: ok()
def walletchk_newpass (self,name,wf,pf):
return self.walletchk(name,wf,pf,pw=True)
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")
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
if check_ref:
refcheck("address data checksum",chk,cfg['addrfile_chk'])
return
t.written_to_file("Addresses",oo=True)
ok()
def refaddrgen(self,name,wf,pf):
d = " (%s-bit seed)" % cfg['seed_len']
self.addrgen(name,wf,pf=pf,check_ref=True)
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", 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")
vmsg("This is a simulation, so no addresses were actually imported into the tracking\nwallet")
ok()
def txcreate(self,name,addrfile):
self.txcreate_common(name,sources=['1'])
def txcreate_common(self,name,sources=['1'],non_mmgen_input=''):
if opt.verbose or opt.exact_output:
sys.stderr.write(green("Generating fake transaction info\n"))
silence()
from mmgen.addr import AddrInfo,AddrInfoList
tx_data,ail = {},AddrInfoList()
from mmgen.util import parse_addr_idxs
for s in sources:
afile = get_file_with_ext("addrs",cfgs[s]["tmpdir"])
ai = AddrInfo(afile)
ail.add(ai)
aix = parse_addr_idxs(cfgs[s]['addr_idx_list'])
if len(aix) != addrs_per_wallet:
errmsg(red("Address index list length != %s: %s" %
(addrs_per_wallet,repr(aix))))
sys.exit()
tx_data[s] = {
'addrfile': afile,
'chk': ai.checksum,
'sid': ai.seed_id,
'addr_idxs': aix[-2:],
}
unspent_data_file = os.path.join(cfg['tmpdir'],"unspent.json")
create_fake_unspent_data(ail,unspent_data_file,tx_data,non_mmgen_input)
# make the command line
from mmgen.bitcoin import privnum2addr
btcaddr = privnum2addr(getrandnum(32),compressed=True)
cmd_args = ["-d",cfg['tmpdir']]
for num in tx_data:
s = tx_data[num]
cmd_args += [
"%s:%s,%s" % (s['sid'],s['addr_idxs'][0],cfgs[num]['amts'][0]),
]
# + one BTC address
# + one change address and one BTC address
if num is tx_data.keys()[-1]:
cmd_args += ["%s:%s" % (s['sid'],s['addr_idxs'][1])]
cmd_args += ["%s,%s" % (btcaddr,cfgs[num]['amts'][1])]
for num in tx_data: cmd_args += [tx_data[num]['addrfile']]
os.environ["MMGEN_BOGUS_WALLET_DATA"] = unspent_data_file
end_silence()
if opt.verbose or opt.exact_output: sys.stderr.write("\n")
add_args = ["-q"] if ni else []
if ni:
m = "\nAnswer the interactive prompts as follows:\n" + \
" 'y', 'y', 'q', '1-8'<ENTER>, ENTER, ENTER, ENTER, 'y'"
msg(grnbg(m))
t = MMGenExpect(name,"mmgen-txcreate",add_args + cmd_args)
if ni: return
t.license()
for num in tx_data:
t.expect_getend("Getting address data from file ")
chk=t.expect_getend(r"Checksum for address data .*?: ",regex=True)
verify_checksum_or_exit(tx_data[num]['chk'],chk)
# not in tracking wallet warning, (1 + num sources) times
if t.expect(["Continue anyway? (y/N): ",
"Unable to connect to bitcoind"]) == 0:
t.send("y")
else:
errmsg(red("Error: unable to connect to bitcoind. Exiting"))
sys.exit(1)
for num in tx_data:
t.expect("Continue anyway? (y/N): ","y")
t.expect(r"'q' = quit sorting, .*?: ","M", regex=True)
t.expect(r"'q' = quit sorting, .*?: ","q", regex=True)
outputs_list = [addrs_per_wallet*i + 1 for i in range(len(tx_data))]
if non_mmgen_input: outputs_list.append(len(tx_data)*addrs_per_wallet + 1)
t.expect("Enter a range or space-separated list of outputs to spend: ",
" ".join([str(i) for i in outputs_list])+"\n")
if non_mmgen_input: t.expect("Accept? (y/N): ","y")
t.expect("OK? (Y/n): ","y")
t.expect("Add a comment to transaction? (y/N): ","\n")
t.tx_view()
t.expect("Save transaction? (y/N): ","y")
t.written_to_file("Transaction")
ok()
def txsign_end(self,t,tnum=None):
t.expect("Signing transaction")
t.expect("Edit transaction comment? (y/N): ","\n")
t.expect("Save signed transaction? (Y/n): ","y")
add = " #" + tnum if tnum else ""
t.written_to_file("Signed transaction" + add, oo=True)
def txsign(self,name,txfile,wf,pf="",save=True):
add_args = ["-q","-P",pf] if ni else []
if ni:
m = "\nAnswer the interactive prompts as follows:\n ENTER, ENTER, ENTER"
msg(grnbg(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'])
if save:
self.txsign_end(t)
else:
t.expect("Edit transaction comment? (y/N): ","\n")
t.close()
ok()
def txsend(self,name,sigfile):
t = MMGenExpect(name,"mmgen-txsend", ["-d",cfg['tmpdir'],sigfile])
t.license()
t.tx_view()
t.expect("Edit transaction comment? (y/N): ","\n")
t.expect("broadcast this transaction to the network?")
t.expect("'YES, I REALLY WANT TO DO THIS' to confirm: ","\n")
t.expect("Exiting at user request")
vmsg("This is a simulation; no transaction was sent")
ok()
def walletconv_export(self,name,wf,desc,uargs=[],out_fmt="w",pw=False):
opts = ["-d",cfg['tmpdir'],"-o",out_fmt] + uargs + [wf]
t = MMGenExpect(name,"mmgen-walletconv",opts)
t.license()
t.passphrase("MMGen wallet",cfg['wpasswd'])
if pw:
t.passphrase_new("new "+desc,cfg['wpasswd'])
t.usr_rand(10)
if " ".join(desc.split()[-2:]) == "incognito data":
t.expect("Generating encryption key from OS random data ")
t.expect("Generating encryption key from OS random data ")
ic_id = t.expect_getend("New Incog Wallet ID: ")
t.expect("Generating encryption key from OS random data ")
if desc == "hidden incognito data":
write_to_tmpfile(cfg,incog_id_fn,ic_id)
ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "])
if ret == 0:
t.send("\n")
t.expect("Enter file size: ",str(hincog_bytes)+"\n")
else:
t.send("YES\n")
if out_fmt == "w": t.label()
return t.written_to_file(capfirst(desc),oo=True)
def export_seed(self,name,wf,desc="seed data",out_fmt="seed"):
f = self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt)
silence()
msg("%s: %s" % (capfirst(desc),cyan(get_data_from_file(f,desc))))
end_silence()
ok()
def export_mnemonic(self,name,wf):
self.export_seed(name,wf,desc="mnemonic data",out_fmt="words")
def export_incog(self,name,wf,desc="incognito data",out_fmt="i",add_args=[]):
uargs = ["-p1","-r10"] + add_args
self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt,uargs=uargs,pw=True)
ok()
def export_incog_hex(self,name,wf):
self.export_incog(name,wf,desc="hex incognito data",out_fmt="xi")
# TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
def export_incog_hidden(self,name,wf):
rf = os.path.join(cfg['tmpdir'],hincog_fn)
add_args = ["-J","%s,%s"%(rf,hincog_offset)]
self.export_incog(
name,wf,desc="hidden incognito data",out_fmt="hi",add_args=add_args)
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'],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")
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
if stdout: t.read()
verify_checksum_or_exit(get_addrfile_checksum(),chk)
# t.no_overwrite()
ok()
def addrgen_mnemonic(self,name,wf,foo):
self.addrgen_seed(name,wf,foo,desc="mnemonic data",in_fmt="words")
def addrgen_incog(self,name,wf=[],foo="",in_fmt="i",
desc="incognito data",args=[]):
t = MMGenExpect(name,"mmgen-addrgen",
args+["-i"+in_fmt,"-d",cfg['tmpdir']]+
([wf] if wf else [])+
[cfg['addr_idx_list']])
t.license()
t.expect_getend("Incog Wallet ID: ")
t.hash_preset(desc,'1')
t.passphrase("%s \w{8}" % desc, cfg['wpasswd'])
vmsg("Comparing generated checksum with checksum from address file")
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
t.close()
verify_checksum_or_exit(get_addrfile_checksum(),chk)
# t.no_overwrite()
ok()
def addrgen_incog_hex(self,name,wf,foo):
self.addrgen_incog(name,wf,"",in_fmt="xi",desc="hex incognito data")
def addrgen_incog_hidden(self,name,wf,foo):
rf = os.path.join(cfg['tmpdir'],hincog_fn)
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,wf,pf,check_ref=False):
args = ["-d",cfg['tmpdir'],wf,cfg['addr_idx_list']]
if ni:
m = "\nAnswer 'n' at the interactive prompt"
msg(grnbg(m))
args = ["-q","-P",pf] + args
t = MMGenExpect(name,"mmgen-keygen", args)
if ni: return
t.license()
t.passphrase("MMGen wallet",cfg['wpasswd'])
chk = t.expect_getend(r"Checksum for key-address data .*?: ",regex=True)
if check_ref:
refcheck("key-address data checksum",chk,cfg['keyaddrfile_chk'])
return
t.expect("Encrypt key list? (y/N): ","y")
t.hash_preset("new key list",'1')
t.passphrase_new("new key list",cfg['kapasswd'])
t.written_to_file("Secret keys",oo=True)
ok()
def refkeyaddrgen(self,name,wf,pf):
self.keyaddrgen(name,wf,pf,check_ref=True)
def txsign_keyaddr(self,name,keyaddr_file,txfile):
t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],"-M",keyaddr_file,txfile])
t.license()
t.hash_preset("key-address file",'1')
t.passphrase("key-address file",cfg['kapasswd'])
t.expect("Check key-to-address validity? (y/N): ","y")
t.tx_view()
self.txsign_end(t)
ok()
def walletgen2(self,name):
self.walletgen(name,seed_len=128)
def addrgen2(self,name,wf):
self.addrgen(name,wf,pf="")
def txcreate2(self,name,addrfile):
self.txcreate_common(name,sources=['2'])
def txsign2(self,name,txf1,wf1,txf2,wf2):
t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],txf1,wf1,txf2,wf2])
t.license()
for cnum in ('1','2'):
t.tx_view()
t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
self.txsign_end(t,cnum)
ok()
def export_mnemonic2(self,name,wf):
self.export_mnemonic(name,wf)
def walletgen3(self,name):
self.walletgen(name)
def addrgen3(self,name,wf):
self.addrgen(name,wf,pf="")
def txcreate3(self,name,addrfile1,addrfile2):
self.txcreate_common(name,sources=['1','3'])
def txsign3(self,name,wf1,wf2,txf2):
t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],wf1,wf2,txf2])
t.license()
t.tx_view()
for cnum in ('1','3'):
# t.expect_getend("Getting MMGen wallet data from file ")
t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
self.txsign_end(t)
ok()
def walletgen4(self,name):
bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
make_brainwallet_file(bwf)
seed_len = str(cfg['seed_len'])
args = ["-d",cfg['tmpdir'],"-p1","-r10","-l"+seed_len,"-ib"]
t = MMGenExpect(name,"mmgen-walletconv", args + [bwf])
t.license()
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
t.usr_rand(10)
t.label()
t.written_to_file("MMGen wallet")
ok()
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')
def txsign4(self,name,f1,f2,f3,f4,f5):
non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
t = MMGenExpect(name,"mmgen-txsign",
["-d",cfg['tmpdir'],"-i","brain","-b"+cfg['bw_params'],"-p1","-k",non_mm_fn,f1,f2,f3,f4,f5])
t.license()
t.tx_view()
for cnum,desc in ('1',"incognito data"),('3',"MMGen wallet"):
t.passphrase(("%s" % desc),cfgs[cnum]['wpasswd'])
self.txsign_end(t)
ok()
def tool_encrypt(self,name,infile=""):
if infile:
infn = infile
else:
d = os.urandom(1033)
tmp_fn = cfg['tool_enc_infn']
write_to_tmpfile(cfg,tmp_fn,d,binary=True)
infn = get_tmpfile_fn(cfg,tmp_fn)
if ni:
pwfn = 'ni_pw'
write_to_tmpfile(cfg,pwfn,tool_enc_passwd+"\n")
pre = ["-P", get_tmpfile_fn(cfg,pwfn)]
app = ["hash_preset=1"]
else:
pre,app = [],[]
t = MMGenExpect(name,"mmgen-tool",pre+["-d",cfg['tmpdir'],"encrypt",infn]+app)
if ni: return
t.hash_preset("user data",'1')
t.passphrase_new("user data",tool_enc_passwd)
t.written_to_file("Encrypted data")
ok()
# Generate the reference mmenc file
# def tool_encrypt_ref(self,name):
# infn = get_tmpfile_fn(cfg,cfg['tool_enc_ref_infn'])
# write_data_to_file(infn,cfg['tool_enc_reftext'],silent=True)
# self.tool_encrypt(name,infn)
def tool_decrypt(self,name,f1,f2):
of = name + ".out"
if ni:
pwfn = 'ni_pw'
pre = ["-P", get_tmpfile_fn(cfg,pwfn)]
else:
pre = []
t = MMGenExpect(name,"mmgen-tool",
pre+["-d",cfg['tmpdir'],"decrypt",f2,"outfile="+of,"hash_preset=1"])
if not ni:
t.passphrase("user data",tool_enc_passwd)
t.written_to_file("Decrypted data")
d1 = read_from_file(f1,binary=True)
d2 = read_from_file(get_tmpfile_fn(cfg,of),binary=True)
cmp_or_die(d1,d2,skip_ok=ni)
def tool_find_incog_data(self,name,f1,f2):
i_id = read_from_file(f2).rstrip()
vmsg("Incog ID: %s" % cyan(i_id))
t = MMGenExpect(name,"mmgen-tool",
["-d",cfg['tmpdir'],"find_incog_data",f1,i_id])
if ni: return
o = t.expect_getend("Incog data for ID %s found at offset " % i_id)
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()
# Saved reference file tests
def ref_wallet_conv(self,name):
wf = os.path.join(ref_dir,cfg['ref_wallet'])
self.walletconv_in(name,wf,"MMGen wallet",pw=True,oo=True)
def ref_mn_conv(self,name,ext="mmwords",desc="Mnemonic data"):
wf = os.path.join(ref_dir,cfg['seed_id']+"."+ext)
self.walletconv_in(name,wf,desc,oo=True)
def ref_seed_conv(self,name):
self.ref_mn_conv(name,ext="mmseed",desc="Seed data")
def ref_brain_conv(self,name):
uopts = ["-i","b","-p","1","-l",str(cfg['seed_len'])]
self.walletconv_in(name,None,"brainwallet",uopts,oo=True)
def ref_incog_conv(self,name,wfk="ic_wallet",in_fmt="i",desc="incognito data"):
uopts = ["-i",in_fmt,"-p","1","-l",str(cfg['seed_len'])]
wf = os.path.join(ref_dir,cfg[wfk])
self.walletconv_in(name,wf,desc,uopts,oo=True,pw=True)
def ref_incox_conv(self,name):
self.ref_incog_conv(name,in_fmt="xi",wfk="ic_wallet_hex",desc="hex incognito data")
def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
ic_f = os.path.join(ref_dir,cfg[wfk])
uopts = ["-i","hi","-p","1","-l",str(cfg['seed_len'])] + add_uopts
hi_opt = ["-H","%s,%s" % (ic_f,ref_wallet_incog_offset)]
self.walletconv_in(name,None,"hidden incognito data",uopts+hi_opt,oo=True,pw=True)
def ref_hincog_conv_old(self,name):
self.ref_hincog_conv(name,wfk='hic_wallet_old',add_uopts=["-O"])
def ref_wallet_conv_out(self,name):
self.walletconv_out(name,"MMGen wallet","w",pw=True)
def ref_mn_conv_out(self,name):
self.walletconv_out(name,"mnemonic data","mn")
def ref_seed_conv_out(self,name):
self.walletconv_out(name,"seed data","seed")
def ref_incog_conv_out(self,name):
self.walletconv_out(name,"incognito data",out_fmt="i",pw=True)
def ref_incox_conv_out(self,name):
self.walletconv_out(name,"hex incognito data",out_fmt="xi",pw=True)
def ref_hincog_conv_out(self,name,extra_uopts=[]):
ic_f = os.path.join(cfg['tmpdir'],hincog_fn)
hi_parms = "%s,%s" % (ic_f,ref_wallet_incog_offset)
sl_parm = "-l" + str(cfg['seed_len'])
self.walletconv_out(name,
"hidden incognito data", "hi",
uopts=["-J",hi_parms,sl_parm] + extra_uopts,
uopts_chk=["-H",hi_parms,sl_parm],
pw=True
)
def ref_wallet_chk(self,name):
wf = os.path.join(ref_dir,cfg['ref_wallet'])
if ni:
write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
pf = get_tmpfile_fn(cfg,pwfile)
else:
pf = None
self.walletchk(name,wf,pf=pf,pw=True,sid=cfg['seed_id'])
def ref_seed_chk(self,name,ext=g.seed_ext):
wf = os.path.join(ref_dir,"%s.%s" % (cfg['seed_id'],ext))
desc = "seed data" if ext == g.seed_ext else "mnemonic data"
self.walletchk(name,wf,pf=None,desc=desc,sid=cfg['seed_id'])
def ref_mn_chk(self,name): self.ref_seed_chk(name,ext=g.mn_ext)
def ref_brain_chk(self,name,bw_file=ref_bw_file):
wf = os.path.join(ref_dir,bw_file)
add_args = ["-l%s" % cfg['seed_len'], "-p"+ref_bw_hash_preset]
self.walletchk(name,wf,pf=None,add_args=add_args,
desc="brainwallet",sid=cfg['ref_bw_seed_id'])
def ref_brain_chk_spc3(self,name):
self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
def ref_hincog_chk(self,name,desc="hidden incognito data"):
for wtype,edesc,of_arg in ('hic_wallet','',[]), \
('hic_wallet_old','(old format)',["-O"]):
ic_arg = ["-H%s,%s" % (
os.path.join(ref_dir,cfg[wtype]),
ref_wallet_incog_offset
)]
slarg = ["-l%s " % cfg['seed_len']]
hparg = ["-p1"]
if ni:
write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
add_args = ["-q","-P%s" % get_tmpfile_fn(cfg,pwfile)]
else:
add_args = []
if ni and wtype == 'hic_wallet_old':
m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
n = cyan(cfg['seed_id'])
msg("\n%s %s" % (m,n))
t = MMGenExpect(name,"mmgen-walletchk",
add_args + slarg + hparg + of_arg + ic_arg,
extra_desc=edesc)
if ni: continue
t.passphrase(desc,cfg['wpasswd'])
if wtype == 'hic_wallet_old':
t.expect("Is the Seed ID correct? (Y/n): ","\n")
chk = t.expect_getend("Seed ID: ")
t.close()
cmp_or_die(cfg['seed_id'],chk)
def ref_addrfile_chk(self,name,ftype="addr"):
wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
if ni:
m = "\nAnswer the interactive prompts as follows: '1'<ENTER>, ENTER"
msg(grnbg(m))
pfn = "ref_kafile_passwd"
write_to_tmpfile(cfg,pfn,ref_kafile_pass)
aa = ["-P",get_tmpfile_fn(cfg,pfn)]
else:
aa = []
t = MMGenExpect(name,"mmgen-tool",aa+[ftype+"file_chksum",wf])
if ni:
k = 'ref_%saddrfile_chksum' % ('key' if ftype == "keyaddr" else '')
m = grnbg("Checksum should be:")
n = cyan(cfg[k])
msg(grnbg("%s %s" % (m,n)))
return
if ftype == "keyaddr":
w = "key-address file"
t.hash_preset(w,ref_kafile_hash_preset)
t.passphrase(w,ref_kafile_pass)
t.expect("Check key-to-address validity? (y/N): ","y")
o = t.expect_getend("Checksum for .*address data .*: ",regex=True)
cmp_or_die(cfg['ref_'+ftype+'file_chksum'],o)
def ref_keyaddrfile_chk(self,name):
self.ref_addrfile_chk(name,ftype="keyaddr")
# def txcreate8(self,name,addrfile):
# self.txcreate_common(name,sources=['8'])
def ref_tx_chk(self,name):
tf = os.path.join(ref_dir,cfg['ref_tx_file'])
wf = os.path.join(ref_dir,cfg['ref_wallet'])
write_to_tmpfile(cfg,pwfile,cfg['wpasswd'])
pf = get_tmpfile_fn(cfg,pwfile)
self.txsign(name,tf,wf,pf,save=False)
def ref_tool_decrypt(self,name):
f = os.path.join(ref_dir,ref_enc_fn)
aa = []
if ni:
pfn = "tool_enc_passwd"
write_to_tmpfile(cfg,pfn,tool_enc_passwd)
aa = ["-P",get_tmpfile_fn(cfg,pfn)]
t = MMGenExpect(name,"mmgen-tool",
aa + ["-q","decrypt",f,"outfile=-","hash_preset=1"])
if ni: return
t.passphrase("user data",tool_enc_passwd)
t.readline()
import re
o = re.sub('\r\n','\n',t.read())
cmp_or_die(sample_text,o)
# wallet conversion tests
def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
opts = ["-d",cfg['tmpdir'],"-o","words","-r10"]
if_arg = [infile] if infile else []
d = "(convert)"
if ni:
opts += ["-q"]
msg("")
if pw:
pfn = "ni_passwd"
write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
opts += ["-P",get_tmpfile_fn(cfg,pfn)]
if desc == "brainwallet":
m = "\nAnswer the interactive prompt as follows: '%s'<ENTER>"
msg(grnbg(m % ref_wallet_brainpass))
if "-O" in uopts:
m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
n = cyan(cfg['seed_id'])
msg("\n%s %s" % (m,n))
t = MMGenExpect(name,"mmgen-walletconv",opts+uopts+if_arg,extra_desc=d)
if ni:
m = grnbg("Seed ID should be:")
n = cyan(cfg['seed_id'])
msg(grnbg("%s %s" % (m,n)))
return
t.license()
if desc == "brainwallet":
t.expect("Enter brainwallet: ",ref_wallet_brainpass+"\n")
if pw:
t.passphrase(desc,cfg['wpasswd'])
if name[:19] == "ref_hincog_conv_old":
t.expect("Is the Seed ID correct? (Y/n): ","\n")
else:
t.expect(["Passphrase is OK"," are correct"])
# Output
wf = t.written_to_file("Mnemonic data",oo=oo)
t.close()
ok()
# back check of result
self.walletchk(name,wf,pf=None,
desc="mnemonic data",
sid=cfg['seed_id'],
extra_desc="(check)"
)
def walletconv_out(self,name,desc,out_fmt="w",uopts=[],uopts_chk=[],pw=False):
opts = ["-d",cfg['tmpdir'],"-p1","-o",out_fmt] + uopts
if ni:
pfn = "ni_passwd"
write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
l = "Non-Interactive Test Wallet"
aa = ["-q","-L",l,"-r0","-P",get_tmpfile_fn(cfg,pfn)]
if desc == "hidden incognito data":
rd = os.urandom(ref_wallet_incog_offset+128)
write_to_tmpfile(cfg,hincog_fn,rd)
else:
aa = ["-r10"]
infile = os.path.join(ref_dir,cfg['seed_id']+".mmwords")
t = MMGenExpect(name,"mmgen-walletconv",aa+opts+[infile],extra_desc="(convert)")
add_args = ["-l%s" % cfg['seed_len']]
if ni:
pfn = "ni_passwd"
write_to_tmpfile(cfg,pfn,cfg['wpasswd'])
pf = get_tmpfile_fn(cfg,pfn)
if desc != "hidden incognito data":
from mmgen.seed import SeedSource
ext = SeedSource.fmt_code_to_sstype(out_fmt).ext
hps = ("",",1")[int(pw)] # TODO real hp
pre_ext = "[%s%s]." % (cfg['seed_len'],hps)
wf = get_file_with_ext(pre_ext+ext,cfg['tmpdir'],no_dot=True)
else:
t.license()
if pw:
t.passphrase_new("new "+desc,cfg['wpasswd'])
t.usr_rand(10)
if " ".join(desc.split()[-2:]) == "incognito data":
for i in (1,2,3):
t.expect("Generating encryption key from OS random data ")
if desc == "hidden incognito data":
ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "])
if ret == 0:
t.send("\n")
t.expect("Enter file size: ",str(hincog_bytes)+"\n")
else:
t.send("YES\n")
if out_fmt == "w": t.label()
wf = t.written_to_file(capfirst(desc),oo=True)
pf = None
ok()
if desc == "hidden incognito data":
add_args += uopts_chk
wf = None
self.walletchk(name,wf,pf=pf,
desc=desc,sid=cfg['seed_id'],pw=pw,
add_args=add_args,
extra_desc="(check)")
for k in (
"ref_wallet_conv",
"ref_mn_conv",
"ref_seed_conv",
"ref_brain_conv",
"ref_incog_conv",
"ref_incox_conv",
"ref_hincog_conv",
"ref_hincog_conv_old",
"ref_wallet_conv_out",
"ref_mn_conv_out",
"ref_seed_conv_out",
"ref_incog_conv_out",
"ref_incox_conv_out",
"ref_hincog_conv_out",
"ref_wallet_chk",
"refwalletgen",
"refaddrgen",
"ref_seed_chk",
"ref_mn_chk",
"ref_brain_chk",
"ref_hincog_chk",
"refkeyaddrgen",
):
for i in ('1','2','3'):
locals()[k+i] = locals()[k]
# main()
if opt.pause:
import termios,atexit
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
def at_exit():
termios.tcsetattr(fd, termios.TCSADRAIN, old)
atexit.register(at_exit)
start_time = int(time.time())
ts = MMGenTestSuite()
for cfg in sorted(cfgs): mk_tmpdir(cfgs[cfg])
try:
if cmd_args:
for arg in cmd_args:
if arg in utils:
globals()[arg](cmd_args[cmd_args.index(arg)+1:])
sys.exit()
elif "info_"+arg in cmd_data:
dirs = cmd_data["info_"+arg][1]
if dirs: clean(dirs)
for cmd in cmd_list[arg]:
check_needs_rerun(ts,cmd,build=True)
elif arg in meta_cmds:
for cmd in meta_cmds[arg]:
check_needs_rerun(ts,cmd,build=True)
elif arg in cmd_data:
check_needs_rerun(ts,arg,build=True)
else:
die(1,"%s: unrecognized command" % arg)
else:
clean()
for cmd in cmd_data:
if cmd[:5] == "info_":
msg(green("\nTesting " + cmd_data[cmd][0]))
continue
ts.do_cmd(cmd)
if cmd is not cmd_data.keys()[-1]: do_between()
except:
sys.stderr = stderr_save
raise
t = int(time.time()) - start_time
sys.stderr.write(green(
"All requested tests finished OK, elapsed time: %02i:%02i\n"
% (t/60,t%60)))