|
@@ -0,0 +1,969 @@
|
|
|
|
+#!/usr/bin/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))
|
|
|
|
+
|
|
|
|
+hincog_fn = "rand_data"
|
|
|
|
+non_mmgen_fn = "btckey"
|
|
|
|
+
|
|
|
|
+from collections import OrderedDict
|
|
|
|
+cmd_data = OrderedDict([
|
|
|
|
+# test description depends
|
|
|
|
+ ['walletgen', (1,'wallet generation', [[[],1]])],
|
|
|
|
+ ['walletchk', (1,'wallet check', [[["mmdat"],1]])],
|
|
|
|
+ ['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]])],
|
|
|
|
+ ['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"],1]])],
|
|
|
|
+ ['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
|
|
|
|
+
|
|
|
|
+ ['walletgen2',(2,'wallet generation (2)', [])],
|
|
|
|
+ ['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",non_mmgen_fn],4]])],
|
|
|
|
+])
|
|
|
|
+
|
|
|
|
+utils = {
|
|
|
|
+ 'check_deps': 'check dependencies for specified command, deleting out-of-date files',
|
|
|
|
+ 'clean': 'clean specified tmp dir(s) (1,2,3,4; no arg = all tmpdirs)',
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+addrs_per_wallet = 8
|
|
|
|
+cfgs = {
|
|
|
|
+ '1': {
|
|
|
|
+ 'tmpdir': "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",
|
|
|
|
+ 'akeys.mmenc': "keyaddrgen"
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ '2': {
|
|
|
|
+ 'tmpdir': "test/tmp2",
|
|
|
|
+ 'wpasswd': "Hodling away",
|
|
|
|
+ 'addr_idx_list': "37,45,3-6,22-23", # 8 addresses
|
|
|
|
+ 'dep_generators': {
|
|
|
|
+ 'mmdat': "walletgen2",
|
|
|
|
+ 'addrs': "addrgen2",
|
|
|
|
+ 'raw': "txcreate2",
|
|
|
|
+ 'sig': "txsign2",
|
|
|
|
+ 'mmwords': "export_mnemonic2",
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ '3': {
|
|
|
|
+ 'tmpdir': "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': "test/tmp4",
|
|
|
|
+ 'wpasswd': "Hashrate rising",
|
|
|
|
+ 'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses
|
|
|
|
+ 'dep_generators': {
|
|
|
|
+ 'mmdat': "walletgen4",
|
|
|
|
+ 'mmbrain': "walletgen4",
|
|
|
|
+ 'addrs': "addrgen4",
|
|
|
|
+ 'raw': "txcreate4",
|
|
|
|
+ 'sig': "txsign4",
|
|
|
|
+ non_mmgen_fn: "txcreate4"
|
|
|
|
+ },
|
|
|
|
+ 'bw_filename': "brainwallet.mmbrain",
|
|
|
|
+ 'bw_params': "256,1",
|
|
|
|
+ },
|
|
|
|
+}
|
|
|
|
+cfg = cfgs['1']
|
|
|
|
+
|
|
|
|
+from binascii import hexlify
|
|
|
|
+def getrand(n): return int(hexlify(os.urandom(n)),16)
|
|
|
|
+def msgrepr(d): sys.stderr.write(repr(d)+"\n")
|
|
|
|
+def msgrepr_exit(d):
|
|
|
|
+ sys.stderr.write(repr(d)+"\n")
|
|
|
|
+ sys.exit()
|
|
|
|
+
|
|
|
|
+# total of two outputs must be < 10 BTC
|
|
|
|
+for k in cfgs.keys():
|
|
|
|
+ cfgs[k]['amts'] = [0,0]
|
|
|
|
+ for idx,mod in (0,6),(1,4):
|
|
|
|
+ cfgs[k]['amts'][idx] = "%s.%s" % ((getrand(2) % mod), str(getrand(4))[:5])
|
|
|
|
+
|
|
|
|
+meta_cmds = OrderedDict([
|
|
|
|
+ ['gen', (1,("walletgen","walletchk","addrgen"))],
|
|
|
|
+ ['tx', (1,("txcreate","txsign","txsend"))],
|
|
|
|
+ ['export', (1,[k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1])],
|
|
|
|
+ ['gen_sp', (1,[k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1])],
|
|
|
|
+ ['online', (1,("keyaddrgen","txsign_keyaddr"))],
|
|
|
|
+ ['2', (2,[k for k in cmd_data if cmd_data[k][0] == 2])],
|
|
|
|
+ ['3', (3,[k for k in cmd_data if cmd_data[k][0] == 3])],
|
|
|
|
+ ['4', (4,[k for k in cmd_data if cmd_data[k][0] == 4])],
|
|
|
|
+])
|
|
|
|
+
|
|
|
|
+from mmgen.Opts import *
|
|
|
|
+help_data = {
|
|
|
|
+ 'prog_name': "test.py",
|
|
|
|
+ 'desc': "Test suite for the MMGen suite",
|
|
|
|
+ 'usage':"[options] [command or metacommand]",
|
|
|
|
+ 'options': """
|
|
|
|
+-h, --help Print this help message
|
|
|
|
+-b, --buf-keypress Use buffered keypresses as with real human input
|
|
|
|
+-d, --debug Produce debugging output
|
|
|
|
+-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
|
|
|
|
+-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
|
|
|
|
+-v, --verbose Produce more verbose output
|
|
|
|
+""",
|
|
|
|
+ 'notes': """
|
|
|
|
+
|
|
|
|
+If no command is given, the whole suite of tests is run.
|
|
|
|
+"""
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+opts,cmd_args = parse_opts(sys.argv,help_data)
|
|
|
|
+
|
|
|
|
+if 'system' in opts: sys.path.pop(0)
|
|
|
|
+
|
|
|
|
+env = os.environ
|
|
|
|
+if 'buf_keypress' in opts:
|
|
|
|
+ send_delay = 0.3
|
|
|
|
+else:
|
|
|
|
+ send_delay = 0
|
|
|
|
+ env["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
|
|
|
|
+
|
|
|
|
+for k in 'debug','verbose','exact_output','pause','quiet':
|
|
|
|
+ globals()[k] = True if k in opts else False
|
|
|
|
+
|
|
|
|
+if debug: verbose = True
|
|
|
|
+
|
|
|
|
+if 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 verbose: sys.stderr.write(s+"\n")
|
|
|
|
+ def msg_r(s): sys.stderr.write(s)
|
|
|
|
+ def vmsg_r(s):
|
|
|
|
+ if verbose: sys.stderr.write(s)
|
|
|
|
+
|
|
|
|
+stderr_save = sys.stderr
|
|
|
|
+
|
|
|
|
+def silence():
|
|
|
|
+ if not (verbose or exact_output):
|
|
|
|
+ sys.stderr = open("/dev/null","a")
|
|
|
|
+
|
|
|
|
+def end_silence():
|
|
|
|
+ if not (verbose or exact_output):
|
|
|
|
+ sys.stderr = stderr_save
|
|
|
|
+
|
|
|
|
+def errmsg(s): stderr_save.write(s+"\n")
|
|
|
|
+
|
|
|
|
+def Msg(s): sys.stdout.write(s+"\n")
|
|
|
|
+
|
|
|
|
+if "list_cmds" in opts:
|
|
|
|
+ Msg("Available commands:")
|
|
|
|
+ w = max([len(i) for i in cmd_data])
|
|
|
|
+ for cmd in cmd_data:
|
|
|
|
+ Msg(" {:<{w}} - {}".format(cmd,cmd_data[cmd][1],w=w))
|
|
|
|
+ Msg("\nAvailable metacommands:")
|
|
|
|
+ w = max([len(i) for i in meta_cmds])
|
|
|
|
+ for cmd in meta_cmds:
|
|
|
|
+ Msg(" {:<{w}} - {}".format(cmd," + ".join(meta_cmds[cmd][1]),w=w))
|
|
|
|
+ Msg("\nAvailable utilities:")
|
|
|
|
+ w = max([len(i) for i in utils])
|
|
|
|
+ for cmd in sorted(utils):
|
|
|
|
+ Msg(" {:<{w}} - {}".format(cmd,utils[cmd],w=w))
|
|
|
|
+ sys.exit()
|
|
|
|
+
|
|
|
|
+import pexpect,time,re
|
|
|
|
+import mmgen.config as g
|
|
|
|
+from mmgen.util import get_data_from_file, write_to_file, get_lines_from_file
|
|
|
|
+
|
|
|
|
+redc,grnc,yelc,cyac,reset = (
|
|
|
|
+ ["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0"]
|
|
|
|
+)
|
|
|
|
+def red(s): return redc+s+reset
|
|
|
|
+def green(s): return grnc+s+reset
|
|
|
|
+def yellow(s): return yelc+s+reset
|
|
|
|
+def cyan(s): return cyac+s+reset
|
|
|
|
+
|
|
|
|
+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 verbose:
|
|
|
|
+ ls = "" if 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 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 debug or (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: ret = my_send(p,t,delay,s)
|
|
|
|
+ return ret
|
|
|
|
+
|
|
|
|
+def cleandir(d):
|
|
|
|
+ try: files = os.listdir(d)
|
|
|
|
+ except: return
|
|
|
|
+
|
|
|
|
+ msg(green("Cleaning directory '%s'" % d))
|
|
|
|
+ for f in files:
|
|
|
|
+ os.unlink(os.path.join(d,f))
|
|
|
|
+
|
|
|
|
+def get_file_with_ext(ext,mydir,delete=False):
|
|
|
|
+ flist = [os.path.join(mydir,f)
|
|
|
|
+ for f in os.listdir(mydir) if f.split(".")[-1] == ext]
|
|
|
|
+ if not flist:
|
|
|
|
+ flist = [os.path.join(mydir,f)
|
|
|
|
+ for f in os.listdir(mydir) if ".".join(f.split(".")[-2:]) == ext]
|
|
|
|
+ if not flist:
|
|
|
|
+ return False
|
|
|
|
+
|
|
|
|
+ if len(flist) > 1 or delete:
|
|
|
|
+ if not 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.tx import parse_addrfile
|
|
|
|
+ chk = parse_addrfile(addrfile,{},return_chk_and_sid=True)[0]
|
|
|
|
+ if 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=[],env=env):
|
|
|
|
+ if not 'system' in opts:
|
|
|
|
+ mmgen_cmd = os.path.join(os.curdir,mmgen_cmd)
|
|
|
|
+ desc = cmd_data[name][1]
|
|
|
|
+ if verbose or exact_output:
|
|
|
|
+ sys.stderr.write(
|
|
|
|
+ green("Testing %s\nExecuting " % desc) +
|
|
|
|
+ cyan("'%s %s'\n" % (mmgen_cmd," ".join(cmd_args)))
|
|
|
|
+ )
|
|
|
|
+ else:
|
|
|
|
+ msg_r("Testing %s " % (desc+":"))
|
|
|
|
+ if env: self.p = pexpect.spawn(mmgen_cmd,cmd_args,env=env)
|
|
|
|
+ else: self.p = pexpect.spawn(mmgen_cmd,cmd_args)
|
|
|
|
+ if exact_output: self.p.logfile = sys.stdout
|
|
|
|
+
|
|
|
|
+ def license(self):
|
|
|
|
+ p = "'w' for conditions and warranty info, or 'c' to continue: "
|
|
|
|
+ my_expect(self.p,p,'c')
|
|
|
|
+
|
|
|
|
+ def usr_rand(self,num_chars):
|
|
|
|
+ rand_chars = [chr(ord(i)%94+33) for i in list(os.urandom(num_chars))]
|
|
|
|
+ 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 verbose else "+")
|
|
|
|
+ self.p.send(ch)
|
|
|
|
+ except:
|
|
|
|
+ vmsg("EOT")
|
|
|
|
+ my_expect(self.p,"ENTER to continue: ",'\n')
|
|
|
|
+
|
|
|
|
+ def passphrase_new(self,what,passphrase):
|
|
|
|
+ my_expect(self.p,("Enter passphrase for new %s: " % what), passphrase+"\n")
|
|
|
|
+ my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
|
|
|
|
+
|
|
|
|
+ def passphrase(self,what,passphrase):
|
|
|
|
+ my_expect(self.p,("Enter passphrase for %s.*?: " % what),
|
|
|
|
+ passphrase+"\n",regex=True)
|
|
|
|
+
|
|
|
|
+ def hash_preset(self,what,preset=''):
|
|
|
|
+ my_expect(self.p,("Enter hash preset for %s, or ENTER .*?:" % what),
|
|
|
|
+ str(preset)+"\n",regex=True)
|
|
|
|
+
|
|
|
|
+ def ok(self):
|
|
|
|
+ if verbose or exact_output:
|
|
|
|
+ sys.stderr.write(green("OK\n"))
|
|
|
|
+ else: msg(" OK")
|
|
|
|
+
|
|
|
|
+ def written_to_file(self,what,overwrite_unlikely=False,query="Overwrite? "):
|
|
|
|
+ s1 = "%s written to file " % what
|
|
|
|
+ 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")
|
|
|
|
+ ret = my_expect(self.p,s1)
|
|
|
|
+ outfile = self.p.readline().strip().strip("'")
|
|
|
|
+ vmsg("%s file: %s" % (what,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, \(v\)iew in pager: ","\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 read(self,n):
|
|
|
|
+ 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 = (getrand(4) % 8),
|
|
|
|
+ txid = unicode(hexlify(os.urandom(32))),
|
|
|
|
+ amount = Decimal("%s.%s" % (10+(getrand(4) % 40), getrand(4) % 100000000)),
|
|
|
|
+ address = address,
|
|
|
|
+ spendable = False,
|
|
|
|
+ scriptPubKey = ("76a914"+verify_addr(address,return_hex=True)+"88ac"),
|
|
|
|
+ confirmations = getrand(4) % 500
|
|
|
|
+ ))
|
|
|
|
+
|
|
|
|
+def create_fake_unspent_data(addr_data,unspent_data_file,tx_data,non_mmgen_input=''):
|
|
|
|
+
|
|
|
|
+ out = []
|
|
|
|
+ for s in tx_data.keys():
|
|
|
|
+ sid = tx_data[s]['sid']
|
|
|
|
+ for idx in addr_data[sid].keys():
|
|
|
|
+ address = unicode(addr_data[sid][idx][0])
|
|
|
|
+ add_fake_unspent_entry(out,address, "%s:%s Test Wallet" % (sid,idx))
|
|
|
|
+
|
|
|
|
+ if non_mmgen_input:
|
|
|
|
+ from mmgen.bitcoin import privnum2addr,hextowif
|
|
|
|
+ privnum = getrand(32)
|
|
|
|
+ btcaddr = privnum2addr(privnum,compressed=True)
|
|
|
|
+ of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
|
|
|
|
+ write_to_file(of, hextowif("{:064x}".format(privnum),
|
|
|
|
+ compressed=True)+"\n",{},"compressed bitcoin key")
|
|
|
|
+
|
|
|
|
+ add_fake_unspent_entry(out,btcaddr,"Non-MMGen address")
|
|
|
|
+
|
|
|
|
+ write_to_file(unspent_data_file,repr(out),{},"Unspent outputs",verbose=True)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def add_comments_to_addr_file(addrfile,tfile):
|
|
|
|
+ silence()
|
|
|
|
+ msg(green("Adding comments to address file '%s'" % addrfile))
|
|
|
|
+ d = get_lines_from_file(addrfile)
|
|
|
|
+ addr_data = {}
|
|
|
|
+ from mmgen.tx import parse_addrfile
|
|
|
|
+ parse_addrfile(addrfile,addr_data)
|
|
|
|
+ sid = addr_data.keys()[0]
|
|
|
|
+ def s(k): return int(k)
|
|
|
|
+ keys = sorted(addr_data[sid].keys(),key=s)
|
|
|
|
+ for n,k in enumerate(keys,1):
|
|
|
|
+ addr_data[sid][k][1] = ("Test address " + str(n))
|
|
|
|
+ d = "#\n# Test address file with comments\n#\n%s {\n%s\n}\n" % (sid,
|
|
|
|
+ "\n".join([" {:<3} {:<36} {}".format(k,*addr_data[sid][k]) for k in keys]))
|
|
|
|
+ msg_r(d)
|
|
|
|
+ write_to_file(tfile,d,{})
|
|
|
|
+ end_silence()
|
|
|
|
+
|
|
|
|
+def make_brainwallet_file(fn):
|
|
|
|
+ # Print random words with random whitespace in between
|
|
|
|
+ from mmgen.mn_tirosh import tirosh_words
|
|
|
|
+ wl = tirosh_words.split("\n")
|
|
|
|
+ nwords,ws_list,max_spaces = 10," \n",5
|
|
|
|
+ def rand_ws_seq():
|
|
|
|
+ nchars = getrand(1) % max_spaces + 1
|
|
|
|
+ return "".join([ws_list[getrand(1)%len(ws_list)] for i in range(nchars)])
|
|
|
|
+ rand_pairs = [wl[getrand(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
|
|
|
|
+ d = "".join(rand_pairs).rstrip() + "\n"
|
|
|
|
+ if verbose: msg_r("Brainwallet password:\n%s" % cyan(d))
|
|
|
|
+ write_to_file(fn,d,{},"brainwallet password")
|
|
|
|
+
|
|
|
|
+def do_between():
|
|
|
|
+ if pause:
|
|
|
|
+ from mmgen.util import keypress_confirm
|
|
|
|
+ if keypress_confirm(green("Continue?"),default_yes=True):
|
|
|
|
+ if verbose or exact_output: sys.stderr.write("\n")
|
|
|
|
+ else:
|
|
|
|
+ errmsg("Exiting at user request")
|
|
|
|
+ sys.exit()
|
|
|
|
+ elif verbose or exact_output:
|
|
|
|
+ sys.stderr.write("\n")
|
|
|
|
+
|
|
|
|
+def do_cmd(ts,cmd):
|
|
|
|
+
|
|
|
|
+ al = []
|
|
|
|
+ for exts,idx in cmd_data[cmd][2]:
|
|
|
|
+ global cfg
|
|
|
|
+ cfg = cfgs[str(idx)]
|
|
|
|
+ for ext in exts:
|
|
|
|
+ while True:
|
|
|
|
+ infile = get_file_with_ext(ext,cfg['tmpdir'])
|
|
|
|
+ if infile:
|
|
|
|
+ al.append(infile); break
|
|
|
|
+ else:
|
|
|
|
+ dg = cfg['dep_generators'][ext]
|
|
|
|
+ if not quiet: msg("Need *.%s from '%s'" % (ext,dg))
|
|
|
|
+ do_cmd(ts,dg)
|
|
|
|
+ do_between()
|
|
|
|
+
|
|
|
|
+ MMGenTestSuite.__dict__[cmd](*([ts,cmd] + al))
|
|
|
|
+
|
|
|
|
+hincog_bytes = 1024*1024
|
|
|
|
+hincog_offset = 98765
|
|
|
|
+hincog_seedlen = 256
|
|
|
|
+
|
|
|
|
+rebuild_list = OrderedDict()
|
|
|
|
+
|
|
|
|
+def check_if_needs_rebuild(num,ext):
|
|
|
|
+ ret = False
|
|
|
|
+
|
|
|
|
+ fn = get_file_with_ext(ext,cfgs[num]['tmpdir'])
|
|
|
|
+ if not fn: ret = True
|
|
|
|
+
|
|
|
|
+ cmd = cfgs[num]['dep_generators'][ext]
|
|
|
|
+ deps = [(str(n),e) for exts,n in cmd_data[cmd][2] for e in exts]
|
|
|
|
+
|
|
|
|
+ if fn:
|
|
|
|
+ my_age = os.stat(fn).st_mtime
|
|
|
|
+ for num,ext in deps:
|
|
|
|
+ f = get_file_with_ext(ext,cfgs[num]['tmpdir'])
|
|
|
|
+ if f and os.stat(f).st_mtime > my_age: ret = True
|
|
|
|
+
|
|
|
|
+ for num,ext in deps:
|
|
|
|
+ if check_if_needs_rebuild(num,ext): ret = True
|
|
|
|
+
|
|
|
|
+ if ret and fn:
|
|
|
|
+ if not quiet: msg("File '%s' out of date - deleting" % fn)
|
|
|
|
+ os.unlink(fn)
|
|
|
|
+
|
|
|
|
+ rebuild_list[cmd] = ret
|
|
|
|
+ return ret
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class MMGenTestSuite(object):
|
|
|
|
+
|
|
|
|
+ def __init__(self):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ def check_deps(self,name,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)
|
|
|
|
+
|
|
|
|
+ d = [(str(num),ext) for exts,num in cmd_data[cmd][2] for ext in exts]
|
|
|
|
+
|
|
|
|
+ if not quiet:
|
|
|
|
+ w = "Checking" if d else "No"
|
|
|
|
+ msg("%s dependencies for '%s'" % (w,cmd))
|
|
|
|
+
|
|
|
|
+ for num,ext in d:
|
|
|
|
+ check_if_needs_rebuild(num,ext)
|
|
|
|
+
|
|
|
|
+ if debug:
|
|
|
|
+ for cmd in rebuild_list:
|
|
|
|
+ msg("cmd: %-15s rebuild: %s" %
|
|
|
|
+ (cmd, cyan("Yes") if rebuild_list[cmd] else "No"))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def clean(self,name,dirs=[]):
|
|
|
|
+ dirlist = dirs if dirs else cfgs.keys()
|
|
|
|
+ for k in dirlist:
|
|
|
|
+ if k in cfgs:
|
|
|
|
+ cleandir(cfgs[k]['tmpdir'])
|
|
|
|
+ else:
|
|
|
|
+ msg("%s: invalid directory index" % k)
|
|
|
|
+ sys.exit(1)
|
|
|
|
+
|
|
|
|
+ def walletgen(self,name,brain=False):
|
|
|
|
+ try: os.mkdir(cfg['tmpdir'],0755)
|
|
|
|
+ except OSError as e:
|
|
|
|
+ if e.errno != 17: raise
|
|
|
|
+ else: msg("Created directory '%s'" % cfg['tmpdir'])
|
|
|
|
+ # cleandir(cfg['tmpdir'])
|
|
|
|
+
|
|
|
|
+ args = ["-d",cfg['tmpdir'],"-p1","-r10"]
|
|
|
|
+ if brain:
|
|
|
|
+ bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
|
|
|
|
+ args += ["-b",cfg['bw_params'],bwf]
|
|
|
|
+ make_brainwallet_file(bwf)
|
|
|
|
+
|
|
|
|
+ t = MMGenExpect(name,"mmgen-walletgen", args)
|
|
|
|
+ t.license()
|
|
|
|
+
|
|
|
|
+ if brain:
|
|
|
|
+ t.expect(
|
|
|
|
+ "A brainwallet will be secure only if you really know what you're doing")
|
|
|
|
+ t.expect("Type uppercase 'YES' to confirm: ","YES\n")
|
|
|
|
+
|
|
|
|
+ t.usr_rand(10)
|
|
|
|
+ t.expect("Generating a key from OS random data plus user entropy")
|
|
|
|
+
|
|
|
|
+ if not brain:
|
|
|
|
+ t.expect("Generating a key from OS random data plus saved user entropy")
|
|
|
|
+
|
|
|
|
+ t.passphrase_new("MMGen wallet",cfg['wpasswd'])
|
|
|
|
+ t.written_to_file("Wallet")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def walletchk_beg(self,name,args):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-walletchk", args)
|
|
|
|
+ t.expect("Getting MMGen wallet data from file '%s'" % args[-1])
|
|
|
|
+ t.passphrase("MMGen wallet",cfg['wpasswd'])
|
|
|
|
+ t.expect("Passphrase is OK")
|
|
|
|
+ t.expect("Wallet is OK")
|
|
|
|
+ return t
|
|
|
|
+
|
|
|
|
+ def walletchk(self,name,walletfile):
|
|
|
|
+ t = self.walletchk_beg(name,[walletfile])
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def addrgen(self,name,walletfile):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-addrgen",["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
|
|
|
+ t.license()
|
|
|
|
+ t.passphrase("MMGen wallet",cfg['wpasswd'])
|
|
|
|
+ t.expect("Passphrase is OK")
|
|
|
|
+ t.expect("Generated [0-9]+ addresses",regex=True)
|
|
|
|
+ t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
|
|
|
+ t.written_to_file("Addresses")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def addrimport(self,name,addrfile):
|
|
|
|
+ outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments")
|
|
|
|
+ add_comments_to_addr_file(addrfile,outfile)
|
|
|
|
+ t = MMGenExpect(name,"mmgen-addrimport",[outfile])
|
|
|
|
+ t.expect_getend(r"checksum for addr 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")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def txcreate(self,name,addrfile):
|
|
|
|
+ self.txcreate_common(name,sources=['1'])
|
|
|
|
+
|
|
|
|
+ def txcreate_common(self,name,sources=['1'],non_mmgen_input=''):
|
|
|
|
+ if verbose or exact_output:
|
|
|
|
+ sys.stderr.write(green("Generating fake transaction info\n"))
|
|
|
|
+ silence()
|
|
|
|
+ tx_data,addr_data = {},{}
|
|
|
|
+ from mmgen.tx import parse_addrfile
|
|
|
|
+ from mmgen.util import parse_addr_idxs
|
|
|
|
+ for s in sources:
|
|
|
|
+ afile = get_file_with_ext("addrs",cfgs[s]["tmpdir"])
|
|
|
|
+ chk,sid = parse_addrfile(afile,addr_data,return_chk_and_sid=True)
|
|
|
|
+ aix = parse_addr_idxs(cfgs[s]['addr_idx_list'])
|
|
|
|
+ if len(aix) != addrs_per_wallet:
|
|
|
|
+ errmsg(red("Addr index list length != %s: %s" %
|
|
|
|
+ (addrs_per_wallet,repr(aix))))
|
|
|
|
+ sys.exit()
|
|
|
|
+ tx_data[s] = {
|
|
|
|
+ 'addrfile': get_file_with_ext("addrs",cfgs[s]['tmpdir']),
|
|
|
|
+ 'chk': chk,
|
|
|
|
+ 'sid': sid,
|
|
|
|
+ 'addr_idxs': aix[-2:],
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ unspent_data_file = os.path.join(cfg['tmpdir'],"unspent.json")
|
|
|
|
+ create_fake_unspent_data(addr_data,unspent_data_file,tx_data,non_mmgen_input)
|
|
|
|
+
|
|
|
|
+ # make the command line
|
|
|
|
+ from mmgen.bitcoin import privnum2addr
|
|
|
|
+ btcaddr = privnum2addr(getrand(32),compressed=True)
|
|
|
|
+
|
|
|
|
+ cmd_args = ["-d",cfg['tmpdir']]
|
|
|
|
+ for num in tx_data.keys():
|
|
|
|
+ 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']]
|
|
|
|
+
|
|
|
|
+ env["MMGEN_BOGUS_WALLET_DATA"] = unspent_data_file
|
|
|
|
+ end_silence()
|
|
|
|
+ if verbose or exact_output: sys.stderr.write("\n")
|
|
|
|
+
|
|
|
|
+ t = MMGenExpect(name,"mmgen-txcreate",cmd_args,env)
|
|
|
|
+ t.license()
|
|
|
|
+ for num in tx_data.keys():
|
|
|
|
+ t.expect_getend("Getting address data from file ")
|
|
|
|
+ from mmgen.addr import fmt_addr_idxs
|
|
|
|
+ chk=t.expect_getend(r"Computed checksum for addr 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.keys():
|
|
|
|
+ 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): ","\n")
|
|
|
|
+ t.written_to_file("Transaction")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def txsign(self,name,txfile,walletfile):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],txfile,walletfile])
|
|
|
|
+ t.license()
|
|
|
|
+ t.tx_view()
|
|
|
|
+ t.passphrase("MMGen wallet",cfg['wpasswd'])
|
|
|
|
+ t.expect("Edit transaction comment? (y/N): ","\n")
|
|
|
|
+ t.written_to_file("Signed transaction")
|
|
|
|
+ t.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("Are you sure you want to broadcast this transaction to the network?")
|
|
|
|
+ t.expect("Type uppercase 'YES, I REALLY WANT TO DO THIS' to confirm: ","\n")
|
|
|
|
+ t.expect("Exiting at user request")
|
|
|
|
+ vmsg("This is a simulation, so no transaction was sent")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def export_seed(self,name,walletfile):
|
|
|
|
+ t = self.walletchk_beg(name,["-s","-d",cfg['tmpdir'],walletfile])
|
|
|
|
+ f = t.written_to_file("Seed data")
|
|
|
|
+ silence()
|
|
|
|
+ msg("Seed data: %s" % cyan(get_data_from_file(f,"seed data")))
|
|
|
|
+ end_silence()
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def export_mnemonic(self,name,walletfile):
|
|
|
|
+ t = self.walletchk_beg(name,["-m","-d",cfg['tmpdir'],walletfile])
|
|
|
|
+ f = t.written_to_file("Mnemonic data")
|
|
|
|
+ silence()
|
|
|
|
+ msg_r("Mnemonic data: %s" % cyan(get_data_from_file(f,"mnemonic data")))
|
|
|
|
+ end_silence()
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def export_incog(self,name,walletfile,args=["-g"]):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-walletchk",args+["-d",cfg['tmpdir'],"-r","10",walletfile])
|
|
|
|
+ t.passphrase("MMGen wallet",cfg['wpasswd'])
|
|
|
|
+ t.usr_rand(10)
|
|
|
|
+ t.expect_getend("Incog ID: ")
|
|
|
|
+ if args[0] == "-G": return t
|
|
|
|
+ t.written_to_file("Incognito wallet data",overwrite_unlikely=True)
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def export_incog_hex(self,name,walletfile):
|
|
|
|
+ self.export_incog(name,walletfile,args=["-X"])
|
|
|
|
+
|
|
|
|
+ # TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
|
|
|
|
+ def export_incog_hidden(self,name,walletfile):
|
|
|
|
+ rf,rd = os.path.join(cfg['tmpdir'],hincog_fn),os.urandom(hincog_bytes)
|
|
|
|
+ vmsg(green("Writing %s bytes of data to file '%s'" % (hincog_bytes,rf)))
|
|
|
|
+ write_to_file(rf,rd,{},verbose=verbose)
|
|
|
|
+ t = self.export_incog(name,walletfile,args=["-G","%s,%s"%(rf,hincog_offset)])
|
|
|
|
+ t.written_to_file("Data",query="")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def addrgen_seed(self,name,walletfile,foo,what="seed data",arg="-s"):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-addrgen",
|
|
|
|
+ [arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
|
|
|
+ t.license()
|
|
|
|
+ t.expect_getend("Valid %s for seed ID " % what)
|
|
|
|
+ vmsg("Comparing generated checksum with checksum from previous address file")
|
|
|
|
+ chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
|
|
|
+ verify_checksum_or_exit(get_addrfile_checksum(),chk)
|
|
|
|
+ t.no_overwrite()
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def addrgen_mnemonic(self,name,walletfile,foo):
|
|
|
|
+ self.addrgen_seed(name,walletfile,foo,what="mnemonic",arg="-m")
|
|
|
|
+
|
|
|
|
+ def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
|
|
|
|
+ cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
|
|
|
+ t.license()
|
|
|
|
+ t.expect_getend("Incog ID: ")
|
|
|
|
+ t.passphrase("MMGen incognito wallet \w{8}", cfg['wpasswd'])
|
|
|
|
+ t.hash_preset("incog wallet",'1')
|
|
|
|
+ vmsg("Comparing generated checksum with checksum from address file")
|
|
|
|
+ chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
|
|
|
+ verify_checksum_or_exit(get_addrfile_checksum(),chk)
|
|
|
|
+ t.no_overwrite()
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def addrgen_incog_hex(self,name,walletfile,foo):
|
|
|
|
+ self.addrgen_incog(name,walletfile,foo,args=["-X"])
|
|
|
|
+
|
|
|
|
+ def addrgen_incog_hidden(self,name,walletfile,foo):
|
|
|
|
+ rf = os.path.join(cfg['tmpdir'],hincog_fn)
|
|
|
|
+ self.addrgen_incog(name,walletfile,foo,
|
|
|
|
+ args=["-G","%s,%s,%s"%(rf,hincog_offset,hincog_seedlen)])
|
|
|
|
+
|
|
|
|
+ def keyaddrgen(self,name,walletfile):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-keygen",
|
|
|
|
+ ["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
|
|
|
+ t.license()
|
|
|
|
+ t.expect("Type uppercase 'YES' to confirm: ","YES\n")
|
|
|
|
+ t.passphrase("MMGen wallet",cfg['wpasswd'])
|
|
|
|
+ t.expect_getend(r"Checksum for key-address data .*?: ",regex=True)
|
|
|
|
+ t.expect("Encrypt key list? (y/N): ","y")
|
|
|
|
+ t.hash_preset("new key list",'1')
|
|
|
|
+ t.passphrase_new("key list",cfg['kapasswd'])
|
|
|
|
+ t.written_to_file("Keys")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ 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.expect("View data for transaction? (y)es, (N)o, (v)iew in pager: ","\n")
|
|
|
|
+ t.expect("Signing transaction...OK")
|
|
|
|
+ t.expect("Edit transaction comment? (y/N): ","\n")
|
|
|
|
+ t.written_to_file("Signed transaction")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def walletgen2(self,name):
|
|
|
|
+ global cfg
|
|
|
|
+ cfg = cfgs['2']
|
|
|
|
+ self.walletgen(name)
|
|
|
|
+
|
|
|
|
+ def addrgen2(self,name,walletfile):
|
|
|
|
+ self.addrgen(name,walletfile)
|
|
|
|
+
|
|
|
|
+ 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'])
|
|
|
|
+ t.expect_getend("Signing transaction ")
|
|
|
|
+ t.expect("Edit transaction comment? (y/N): ","\n")
|
|
|
|
+ t.written_to_file("Signed transaction #%s" % cnum)
|
|
|
|
+
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def export_mnemonic2(self,name,walletfile):
|
|
|
|
+ self.export_mnemonic(name,walletfile)
|
|
|
|
+
|
|
|
|
+ def walletgen3(self,name):
|
|
|
|
+ global cfg
|
|
|
|
+ cfg = cfgs['3']
|
|
|
|
+ self.walletgen(name)
|
|
|
|
+
|
|
|
|
+ def addrgen3(self,name,walletfile):
|
|
|
|
+ self.addrgen(name,walletfile)
|
|
|
|
+
|
|
|
|
+ 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 s in ['1','3']:
|
|
|
|
+ t.expect_getend("Getting MMGen wallet data from file ")
|
|
|
|
+ t.passphrase("MMGen wallet",cfgs[s]['wpasswd'])
|
|
|
|
+
|
|
|
|
+ t.expect_getend("Signing transaction")
|
|
|
|
+ t.expect("Edit transaction comment? (y/N): ","\n")
|
|
|
|
+ t.written_to_file("Signed transaction")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+ def walletgen4(self,name):
|
|
|
|
+ global cfg
|
|
|
|
+ cfg = cfgs['4']
|
|
|
|
+ self.walletgen(name,brain=True)
|
|
|
|
+
|
|
|
|
+ def addrgen4(self,name,walletfile):
|
|
|
|
+ self.addrgen(name,walletfile)
|
|
|
|
+
|
|
|
|
+ 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):
|
|
|
|
+ t = MMGenExpect(name,"mmgen-txsign",
|
|
|
|
+ ["-d",cfg['tmpdir'],"-b",cfg['bw_params'],"-k",non_mm_fn,f1,f2,f3,f4,f5])
|
|
|
|
+ t.license()
|
|
|
|
+ t.tx_view()
|
|
|
|
+
|
|
|
|
+ for cfgnum,what,app in ('1',"incognito"," incognito"),('3',"MMGen",""):
|
|
|
|
+ t.expect_getend("Getting %s wallet data from file " % what)
|
|
|
|
+ t.passphrase("MMGen%s wallet"%app,cfgs[cfgnum]['wpasswd'])
|
|
|
|
+ if cfgnum == '1':
|
|
|
|
+ t.hash_preset("incog wallet",'1')
|
|
|
|
+
|
|
|
|
+ t.expect_getend("Signing transaction")
|
|
|
|
+ t.expect("Edit transaction comment? (y/N): ","\n")
|
|
|
|
+ t.written_to_file("Signed transaction")
|
|
|
|
+ t.ok()
|
|
|
|
+
|
|
|
|
+# main()
|
|
|
|
+ts = MMGenTestSuite()
|
|
|
|
+start_time = int(time.time())
|
|
|
|
+
|
|
|
|
+if 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)
|
|
|
|
+
|
|
|
|
+try:
|
|
|
|
+ if cmd_args:
|
|
|
|
+ arg1 = cmd_args[0]
|
|
|
|
+ if arg1 in utils:
|
|
|
|
+ if arg1 == "check_deps": debug = True
|
|
|
|
+ MMGenTestSuite.__dict__[arg1](ts,arg1,cmd_args[1:])
|
|
|
|
+ sys.exit()
|
|
|
|
+ elif arg1 in meta_cmds:
|
|
|
|
+ if len(cmd_args) == 1:
|
|
|
|
+ ts.clean("clean",str(meta_cmds[arg1][0]))
|
|
|
|
+ for cmd in meta_cmds[arg1][1]:
|
|
|
|
+ do_cmd(ts,cmd)
|
|
|
|
+ if cmd is not cmd_data.keys()[-1]: do_between()
|
|
|
|
+ else:
|
|
|
|
+ msg("Only one meta command may be specified")
|
|
|
|
+ sys.exit(1)
|
|
|
|
+ elif arg1 in cmd_data:
|
|
|
|
+ if len(cmd_args) == 1:
|
|
|
|
+ ts.check_deps("check_deps",[arg1])
|
|
|
|
+ do_cmd(ts,arg1)
|
|
|
|
+ else:
|
|
|
|
+ msg("Only one command may be specified")
|
|
|
|
+ sys.exit(1)
|
|
|
|
+ else:
|
|
|
|
+ errmsg("%s: unrecognized command" % arg1)
|
|
|
|
+ sys.exit(1)
|
|
|
|
+ else:
|
|
|
|
+ ts.clean("clean")
|
|
|
|
+ for cmd in cmd_data:
|
|
|
|
+ do_cmd(ts,cmd)
|
|
|
|
+ if cmd is not cmd_data.keys()[-1]: do_between()
|
|
|
|
+except:
|
|
|
|
+ sys.stderr = stderr_save
|
|
|
|
+ raise
|
|
|
|
+
|
|
|
|
+t = int(time.time()) - start_time
|
|
|
|
+msg(green(
|
|
|
|
+ "All requested tests finished OK, elapsed time: %02i:%02i" % (t/60,t%60)))
|