Improved command-line processing in 'mmgen-tool'
Added 'tool' argument to test/test.py (WIP)
This commit is contained in:
parent
aa6ae750b9
commit
7b44b67e0a
5 changed files with 187 additions and 65 deletions
|
|
@ -20,10 +20,6 @@
|
|||
mmgen-tool: Perform various Bitcoin-related operations.
|
||||
Part of the MMGen suite
|
||||
"""
|
||||
import sys
|
||||
print "MMGEN-TOOL was determined to be buggy and is in the process of maintenance."
|
||||
print "Please check back soon!"
|
||||
sys.exit(3)
|
||||
|
||||
from mmgen.main import launch
|
||||
launch("tool")
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ if cmd_args and cmd_args[0] == '--help':
|
|||
tool.tool_usage(g.prog_name, command)
|
||||
sys.exit(0)
|
||||
|
||||
args = tool.process_args(g.prog_name, command, cmd_args)
|
||||
args,kwargs = tool.process_args(g.prog_name, command, cmd_args)
|
||||
|
||||
#msgrepr(args,kwargs)
|
||||
tool.opts = opts
|
||||
|
||||
#print command + "(" + ", ".join(args) + ")"
|
||||
eval("tool." + command + "(" + ", ".join(args) + ")")
|
||||
tool.__dict__[command](*args,**kwargs)
|
||||
|
|
|
|||
|
|
@ -155,65 +155,53 @@ def tool_usage(prog_name, command):
|
|||
print "USAGE: '%s %s%s'" % (prog_name, command,
|
||||
(" "+" ".join(commands[command]) if commands[command] else ""))
|
||||
|
||||
def process_args(prog_name, command, uargs):
|
||||
cargs = commands[command]
|
||||
cargs_req = [[i.split(" [")[0],i.split(" [")[1][:-1]]
|
||||
for i in cargs if "=" not in i]
|
||||
cargs_nam = dict([[
|
||||
def process_args(prog_name, command, cmd_args):
|
||||
c_args = [[i.split(" [")[0],i.split(" [")[1][:-1]]
|
||||
for i in commands[command] if "=" not in i]
|
||||
c_kwargs = dict([[
|
||||
i.split(" [")[0],
|
||||
[i.split(" [")[1].split("=")[0], i.split(" [")[1].split("=")[1][:-1]]
|
||||
] for i in cargs if "=" in i])
|
||||
uargs_req = [i for i in uargs if "=" not in i]
|
||||
uargs_nam = dict([i.split("=") for i in uargs if "=" in i])
|
||||
] for i in commands[command] if "=" in i])
|
||||
u_args = cmd_args[:len(c_args)]
|
||||
u_kwargs = dict([i.split("=") for i in cmd_args[len(c_args):]])
|
||||
|
||||
# print cargs_req; print cargs_nam; print uargs_req; print uargs_nam; sys.exit()
|
||||
# print c_args; print c_kwargs; print u_args; print u_kwargs; sys.exit()
|
||||
|
||||
n = len(cargs_req)
|
||||
if len(uargs_req) != n:
|
||||
if len(u_args) != len(c_args):
|
||||
tool_usage(prog_name, command)
|
||||
sys.exit(1)
|
||||
|
||||
for a in uargs_nam.keys():
|
||||
if a not in cargs_nam.keys():
|
||||
print "'%s' invalid named argument" % a
|
||||
sys.exit(1)
|
||||
if set(u_kwargs) > set(c_kwargs):
|
||||
print "Invalid named argument"
|
||||
sys.exit(1)
|
||||
|
||||
def test_type(arg_type,arg,name=""):
|
||||
def convert_type(arg,arg_name,arg_type):
|
||||
try:
|
||||
t = type(eval(arg))
|
||||
assert(t == eval(arg_type))
|
||||
return __builtins__[arg_type](arg)
|
||||
except:
|
||||
print "'%s': Invalid argument for argument %s ('%s' required)" % \
|
||||
(arg, name, arg_type)
|
||||
(arg, arg_name, arg_type)
|
||||
sys.exit(1)
|
||||
return True
|
||||
|
||||
ret = []
|
||||
|
||||
def normalize_arg(arg, arg_type):
|
||||
def convert_to_bool_maybe(arg, arg_type):
|
||||
if arg_type == "bool":
|
||||
if arg.lower() in ("true","yes","1","on"): return "True"
|
||||
if arg.lower() in ("false","no","0","off"): return "False"
|
||||
if arg.lower() in ("true","yes","1","on"): return True
|
||||
if arg.lower() in ("false","no","0","off"): return False
|
||||
return arg
|
||||
|
||||
for i in range(len(cargs_req)):
|
||||
arg_type = cargs_req[i][1]
|
||||
arg = normalize_arg(uargs_req[i], arg_type)
|
||||
if arg_type == "str":
|
||||
ret.append('"%s"' % (arg))
|
||||
elif test_type(arg_type, arg, "#"+str(i+1)):
|
||||
ret.append('%s' % (arg))
|
||||
args = []
|
||||
for i in range(len(c_args)):
|
||||
arg_type = c_args[i][1]
|
||||
arg = convert_to_bool_maybe(u_args[i], arg_type)
|
||||
args.append(convert_type(arg,c_args[i][0],arg_type))
|
||||
|
||||
for k in uargs_nam.keys():
|
||||
arg_type = cargs_nam[k][0]
|
||||
arg = normalize_arg(uargs_nam[k], arg_type)
|
||||
if arg_type == "str":
|
||||
ret.append('%s="%s"' % (k, arg))
|
||||
elif test_type(arg_type, arg, "'"+k+"'"):
|
||||
ret.append('%s=%s' % (k, arg))
|
||||
|
||||
return ret
|
||||
kwargs = {}
|
||||
for k in u_kwargs:
|
||||
arg_type = c_kwargs[k][0]
|
||||
arg = convert_to_bool_maybe(u_kwargs[k], arg_type)
|
||||
kwargs[k] = convert_type(arg,k,arg_type)
|
||||
|
||||
return args,kwargs
|
||||
|
||||
# Individual commands
|
||||
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ def write_to_stdout(data, what, confirm=True):
|
|||
sys.stdout.write(data)
|
||||
|
||||
|
||||
def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True):
|
||||
def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True,silent=False):
|
||||
|
||||
if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile)
|
||||
|
||||
|
|
@ -333,13 +333,13 @@ def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=
|
|||
msg("Not overwriting file at user request")
|
||||
return False
|
||||
else:
|
||||
msg("Overwriting file '%s'" % outfile)
|
||||
if not silent: msg("Overwriting file '%s'" % outfile)
|
||||
|
||||
f = open_file_or_exit(outfile,'wb')
|
||||
try:
|
||||
f.write(data)
|
||||
except:
|
||||
msg("Failed to write %s to file '%s'" % (what,outfile))
|
||||
if not silent: msg("Failed to write %s to file '%s'" % (what,outfile))
|
||||
sys.exit(2)
|
||||
f.close
|
||||
|
||||
|
|
@ -549,9 +549,10 @@ def get_lines_from_file(infile,what="",trim_comments=False):
|
|||
return remove_comments(lines) if trim_comments else lines
|
||||
|
||||
|
||||
def get_data_from_file(infile,what="data",dash=False):
|
||||
def get_data_from_file(infile,what="data",dash=False,silent=False):
|
||||
if dash and infile == "-": return sys.stdin.read()
|
||||
qmsg("Getting %s from file '%s'" % (what,infile))
|
||||
if not silent:
|
||||
qmsg("Getting %s from file '%s'" % (what,infile))
|
||||
f = open_file_or_exit(infile,'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
|
|
|||
161
test/test.py
161
test/test.py
|
|
@ -45,23 +45,55 @@ cmd_data = OrderedDict([
|
|||
['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), 128-bit seed (WIP)', [])],
|
||||
['walletgen2',(2,'wallet generation (2)', [])],
|
||||
# ['walletgen2',(2,'wallet generation (2), 128-bit seed (WIP)', [])],
|
||||
['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), 128-bit seed (WIP)',[[["mmdat"],2]])],
|
||||
['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
|
||||
# ['export_mnemonic2', (2,'seed export to mmwords format (2), 128-bit seed (WIP)',[[["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, 192-bit seed (WIP))', [])],
|
||||
['walletgen4',(4,'wallet generation (4) (brainwallet)', [])],
|
||||
# ['walletgen4',(4,'wallet generation (4) (brainwallet, 192-bit seed (WIP))', [])],
|
||||
['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]])],
|
||||
])
|
||||
|
||||
tool_cmd_data = OrderedDict([
|
||||
['strtob58', (10, '', [])],
|
||||
['b58tostr', (10, '', [[["strtob58.in","strtob58.out"],10]])],
|
||||
['hextob58', (10, '', [])],
|
||||
['b58tohex', (10, '', [[["hextob58.in","hextob58.out"],10]])],
|
||||
# "b58randenc": [],
|
||||
# "randhex": ['nbytes [int=32]'],
|
||||
# "randwif": ['compressed [bool=False]'],
|
||||
# "randpair": ['compressed [bool=False]'],
|
||||
# "wif2hex": ['<wif> [str]', 'compressed [bool=False]'],
|
||||
# "wif2addr": ['<wif> [str]', 'compressed [bool=False]'],
|
||||
# "hex2wif": ['<private key in hex format> [str]', 'compressed [bool=False]'],
|
||||
# "hexdump": ['<infile> [str]', 'cols [int=8]', 'line_nums [bool=True]'],
|
||||
# "unhexdump": ['<infile> [str]'],
|
||||
# "hex2mn": ['<hexadecimal string> [str]','wordlist [str="electrum"]'],
|
||||
# "mn2hex": ['<mnemonic> [str]', 'wordlist [str="electrum"]'],
|
||||
# "b32tohex": ['<b32 num> [str]'],
|
||||
# "hextob32": ['<hex num> [str]'],
|
||||
# "mn_rand128": ['wordlist [str="electrum"]'],
|
||||
# "mn_rand192": ['wordlist [str="electrum"]'],
|
||||
# "mn_rand256": ['wordlist [str="electrum"]'],
|
||||
# "mn_stats": ['wordlist [str="electrum"]'],
|
||||
# "mn_printlist": ['wordlist [str="electrum"]'],
|
||||
# "id8": ['<infile> [str]'],
|
||||
# "id6": ['<infile> [str]'],
|
||||
# "str2id6": ['<string (spaces are ignored)> [str]'],
|
||||
])
|
||||
|
||||
|
||||
utils = {
|
||||
'check_deps': 'check dependencies for specified command',
|
||||
'clean': 'clean specified tmp dir(s) 1, 2, 3 or 4 (no arg = all dirs)',
|
||||
|
|
@ -69,6 +101,16 @@ utils = {
|
|||
|
||||
addrs_per_wallet = 8
|
||||
cfgs = {
|
||||
'10': {
|
||||
'name': "test the tool utility",
|
||||
'enc_passwd': "Ten Satoshis",
|
||||
'tmpdir': "test/tmp10",
|
||||
'dep_generators': {
|
||||
'strtob58.out': "strtob58",
|
||||
'strtob58.in': "strtob58",
|
||||
},
|
||||
|
||||
},
|
||||
'6': {
|
||||
'name': "reference wallet check",
|
||||
'bw_passwd': "abc",
|
||||
|
|
@ -156,6 +198,7 @@ cfgs = {
|
|||
|
||||
from binascii import hexlify
|
||||
def getrand(n): return int(hexlify(os.urandom(n)),16)
|
||||
def getrandhex(n): return hexlify(os.urandom(n))
|
||||
|
||||
# total of two outputs must be < 10 BTC
|
||||
for k in cfgs.keys():
|
||||
|
|
@ -342,12 +385,17 @@ def verify_checksum_or_exit(checksum,chk):
|
|||
sys.exit(1)
|
||||
vmsg(green("Checksums match: %s") % (cyan(chk)))
|
||||
|
||||
def get_rand_printable_chars(num_chars,no_punc=False):
|
||||
return [chr(ord(i)%94+33) for i in list(os.urandom(num_chars))]
|
||||
|
||||
|
||||
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 not desc: desc = name
|
||||
if verbose or exact_output:
|
||||
sys.stderr.write(
|
||||
green("Testing %s\nExecuting " % desc) +
|
||||
|
|
@ -365,7 +413,7 @@ class MMGenExpect(object):
|
|||
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))]
|
||||
rand_chars = get_rand_printable_chars(num_chars)
|
||||
my_expect(self.p,'symbols left: ','x')
|
||||
try:
|
||||
vmsg_r("SEND ")
|
||||
|
|
@ -429,7 +477,10 @@ class MMGenExpect(object):
|
|||
def readline(self):
|
||||
return self.p.readline()
|
||||
|
||||
def read(self,n):
|
||||
def readlines(self):
|
||||
return [l.rstrip()+"\n" for l in self.p.readlines()]
|
||||
|
||||
def read(self,n=None):
|
||||
return self.p.read(n)
|
||||
|
||||
|
||||
|
|
@ -513,7 +564,7 @@ def do_cmd(ts,cmd):
|
|||
global cfg
|
||||
cfg = cfgs[str(cmd_data[cmd][0])]
|
||||
|
||||
MMGenTestSuite.__dict__[cmd](*([ts,cmd] + al))
|
||||
ts.__class__.__dict__[cmd](*([ts,cmd] + al))
|
||||
|
||||
|
||||
hincog_bytes = 1024*1024
|
||||
|
|
@ -591,7 +642,6 @@ def ok():
|
|||
sys.stderr.write(green("OK\n"))
|
||||
else: msg(" OK")
|
||||
|
||||
|
||||
class MMGenTestSuite(object):
|
||||
|
||||
def __init__(self):
|
||||
|
|
@ -1004,10 +1054,85 @@ class MMGenTestSuite(object):
|
|||
t.written_to_file("Signed transaction")
|
||||
ok()
|
||||
|
||||
# main()
|
||||
ts = MMGenTestSuite()
|
||||
start_time = int(time.time())
|
||||
|
||||
def write_to_tmpfile(fn,data):
|
||||
write_to_file(os.path.join(cfg['tmpdir'],fn),data,{},silent=True)
|
||||
|
||||
def read_from_tmpfile(fn):
|
||||
from mmgen.util import get_data_from_file
|
||||
return get_data_from_file(os.path.join(cfg['tmpdir'],fn),silent=True)
|
||||
|
||||
def read_from_file(fn):
|
||||
from mmgen.util import get_data_from_file
|
||||
return get_data_from_file(fn,silent=True)
|
||||
|
||||
class MMGenToolTestSuite(object):
|
||||
|
||||
def __init__(self):
|
||||
global cmd_data,tool_cmd_data
|
||||
cmd_data = tool_cmd_data
|
||||
pass
|
||||
|
||||
def clean(self,name):
|
||||
cleandir(cfgs['10']['tmpdir'])
|
||||
|
||||
def cmd(self,name,tool_args):
|
||||
mk_tmpdir(cfg)
|
||||
t = MMGenExpect(name,"mmgen-tool", ["-d",cfg['tmpdir']] + tool_args)
|
||||
return t.read()
|
||||
|
||||
def cmd_to_tmpfile(self,name,tool_args,tmpfile):
|
||||
ret = self.cmd(name,tool_args)
|
||||
if ret:
|
||||
write_to_tmpfile(tmpfile,ret)
|
||||
ok()
|
||||
|
||||
def strtob58(self,name):
|
||||
s = "".join(get_rand_printable_chars(15))
|
||||
write_to_tmpfile('strtob58.in',s)
|
||||
self.cmd_to_tmpfile(name,["strtob58",s],'strtob58.out')
|
||||
|
||||
def b58tostr(self,name,f1,f2):
|
||||
idata = read_from_file(f1)
|
||||
odata = read_from_file(f2)[:-2]
|
||||
res = self.cmd(name,["b58tostr",odata])[:-2]
|
||||
if res == idata: ok()
|
||||
else: errmsg(red("Error"))
|
||||
|
||||
def hextob58(self,name):
|
||||
hexnum = getrandhex(32)
|
||||
write_to_tmpfile('hextob58.in',hexnum)
|
||||
self.cmd_to_tmpfile(name,["hextob58",hexnum],'hextob58.out')
|
||||
|
||||
def b58tohex(self,name,f1,f2):
|
||||
idata = read_from_file(f1)
|
||||
odata = read_from_file(f2)[:-2]
|
||||
res = self.cmd(name,["b58tohex",odata])[:-2]
|
||||
if res == idata: ok()
|
||||
else: errmsg(red("Error"))
|
||||
# "b58randenc": [],
|
||||
# "randhex": ['nbytes [int=32]'],
|
||||
# "randwif": ['compressed [bool=False]'],
|
||||
# "randpair": ['compressed [bool=False]'],
|
||||
# "wif2hex": ['<wif> [str]', 'compressed [bool=False]'],
|
||||
# "wif2addr": ['<wif> [str]', 'compressed [bool=False]'],
|
||||
# "hex2wif": ['<private key in hex format> [str]', 'compressed [bool=False]'],
|
||||
# "hexdump": ['<infile> [str]', 'cols [int=8]', 'line_nums [bool=True]'],
|
||||
# "unhexdump": ['<infile> [str]'],
|
||||
# "hex2mn": ['<hexadecimal string> [str]','wordlist [str="electrum"]'],
|
||||
# "mn2hex": ['<mnemonic> [str]', 'wordlist [str="electrum"]'],
|
||||
# "b32tohex": ['<b32 num> [str]'],
|
||||
# "hextob32": ['<hex num> [str]'],
|
||||
# "mn_rand128": ['wordlist [str="electrum"]'],
|
||||
# "mn_rand192": ['wordlist [str="electrum"]'],
|
||||
# "mn_rand256": ['wordlist [str="electrum"]'],
|
||||
# "mn_stats": ['wordlist [str="electrum"]'],
|
||||
# "mn_printlist": ['wordlist [str="electrum"]'],
|
||||
# "id8": ['<infile> [str]'],
|
||||
# "id6": ['<infile> [str]'],
|
||||
# "str2id6": ['<string (spaces are ignored)> [str]'],
|
||||
|
||||
# main()
|
||||
if pause:
|
||||
import termios,atexit
|
||||
fd = sys.stdin.fileno()
|
||||
|
|
@ -1016,20 +1141,24 @@ if pause:
|
|||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
atexit.register(at_exit)
|
||||
|
||||
start_time = int(time.time())
|
||||
|
||||
try:
|
||||
if cmd_args:
|
||||
if cmd_args and cmd_args[0] != "tool":
|
||||
arg1 = cmd_args[0]
|
||||
if arg1 in utils:
|
||||
MMGenTestSuite.__dict__[arg1](ts,arg1,cmd_args[1:])
|
||||
sys.exit()
|
||||
elif arg1 in meta_cmds:
|
||||
ts = MMGenTestSuite()
|
||||
if len(cmd_args) == 1:
|
||||
for cmd in meta_cmds[arg1][1]:
|
||||
check_needs_rerun(cmd,build=True,force_delete=True)
|
||||
else:
|
||||
msg("Only one meta command may be specified")
|
||||
sys.exit(1)
|
||||
elif arg1 in cmd_data:
|
||||
elif arg1 in cmd_data.keys() + tool_cmd_data.keys():
|
||||
ts = MMGenTestSuite() if arg1 in cmd_data else MMGenToolTestSuite()
|
||||
if len(cmd_args) == 1:
|
||||
check_needs_rerun(arg1,build=True)
|
||||
else:
|
||||
|
|
@ -1039,6 +1168,14 @@ try:
|
|||
errmsg("%s: unrecognized command" % arg1)
|
||||
sys.exit(1)
|
||||
else:
|
||||
if cmd_args: # tool
|
||||
if len(cmd_args) != 1:
|
||||
msg("Only one command may be specified")
|
||||
sys.exit(1)
|
||||
ts = MMGenToolTestSuite()
|
||||
else:
|
||||
ts = MMGenTestSuite()
|
||||
|
||||
ts.clean("clean")
|
||||
for cmd in cmd_data:
|
||||
do_cmd(ts,cmd)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue