From 7e079f1b7afbfde4124faff64eafc1bb8df56dbc Mon Sep 17 00:00:00 2001 From: philemon Date: Sun, 3 Aug 2014 16:16:09 +0400 Subject: [PATCH] Code cleanups, streamlined code in 'mmgen/addr.py' --- mmgen-addrgen | 49 +++---- mmgen-passchg | 1 + mmgen-pywallet | 2 + mmgen-tool | 5 +- mmgen-txsign | 5 + mmgen-walletgen | 1 - mmgen/Opts.py | 283 +++++++++++++------------------------ mmgen/addr.py | 118 +++++++--------- mmgen/tests/addr.py | 27 ---- mmgen/tests/mn_electrum.py | 24 ---- mmgen/tests/mn_tirosh.py | 24 ---- mmgen/tests/util.py | 27 ---- mmgen/tests/walletgen.py | 27 ---- mmgen/tool.py | 19 ++- mmgen/tx.py | 10 +- mmgen/util.py | 25 +++- mmgen/walletgen.py | 27 ---- 17 files changed, 222 insertions(+), 452 deletions(-) delete mode 100755 mmgen/tests/addr.py delete mode 100755 mmgen/tests/mn_electrum.py delete mode 100755 mmgen/tests/mn_tirosh.py delete mode 100755 mmgen/tests/util.py delete mode 100755 mmgen/tests/walletgen.py delete mode 100755 mmgen/walletgen.py diff --git a/mmgen-addrgen b/mmgen-addrgen index 8d4176b0..eeaf315c 100755 --- a/mmgen-addrgen +++ b/mmgen-addrgen @@ -31,12 +31,12 @@ from mmgen.util import * from mmgen.addr import * from mmgen.tx import make_addr_data_chksum -gen_what = "addresses" if sys.argv[0].split("-")[-1] == "addrgen" else "keys" +what = "keys" if sys.argv[0].split("-")[-1] == "keygen" else "addresses" help_data = { 'prog_name': g.prog_name, 'desc': """Generate a list or range of {} from an {g.proj_name} wallet, - mnemonic, seed or password""".format(gen_what,g=g), + mnemonic, seed or password""".format(what,g=g), 'usage':"[opts] [infile]
", 'options': """ -h, --help Print this help message{} @@ -73,9 +73,9 @@ help_data = { "\n '{}-txsign'".format(g.proj_name.lower()), "\n-x, --b16 Print secret keys in hexadecimal too" ) - if gen_what == "keys" else ("","","")), + if what == "keys" else ("","","")), seed_lens=", ".join([str(i) for i in g.seed_lens]), - what=gen_what, g=g + what=what, g=g ), 'notes': """ @@ -106,7 +106,7 @@ For a brainwallet passphrase to always generate the same keys and addresses, the same 'l' and 'p' parameters to '--from-brain' must be used in all future invocations with that passphrase """.format("\n\nBy default, both addresses and secret keys are generated." - if gen_what == "keys" else "") + if what == "keys" else "") } opts,cmd_args = parse_opts(sys.argv,help_data) @@ -117,8 +117,6 @@ if 'quiet' in opts: g.quiet = True if 'from_incog_hex' in opts or 'from_incog_hidden' in opts: opts['from_incog'] = True -opts['gen_what'] = gen_what - if g.debug: show_opts_and_cmd_args(opts,cmd_args) if len(cmd_args) == 1 and ( @@ -140,22 +138,26 @@ if not addr_idxs: sys.exit(2) do_license_msg() # Interact with user: -if gen_what == "keys" and not g.quiet: +if what == "keys" and not g.quiet: confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue') # Generate data: -if gen_what == "addresses": - opts['print_addresses_only'] = True -else: - if not 'no_addresses' in opts: opts['print_secret'] = True - seed = get_seed_retry(infile,opts) seed_id = make_chksum_8(seed) +for l in ( + ('flat_list', 'no_addresses'), + ('flat_list', 'b16'), +): check_incompatible_opts(opts,l) + +opts['gen_what'] = \ + ("addrs") if what == "addresses" else ( + ("keys") if 'no_addresses' in opts else ("addrs","keys")) + addr_data = generate_addrs(seed, addr_idxs, opts) -addr_data_chksum = make_addr_data_chksum([(a['num'],a['addr']) - for a in addr_data]) if not 'no_addresses' in opts else "" +addr_data_chksum = make_addr_data_chksum([(a.num,a.addr) + for a in addr_data]) if 'addrs' in opts['gen_what'] else "" addr_data_str = format_addr_data( addr_data, addr_data_chksum, seed_id, addr_idxs, opts) @@ -163,20 +165,19 @@ outfile_base = "{}[{}]".format(seed_id, fmt_addr_idxs(addr_idxs)) # Output data: if 'stdout' in opts: - confirm = True if (gen_what == "keys" and not g.quiet) else False - write_to_stdout(addr_data_str,gen_what,confirm) + confirm = True if (what == "keys" and not g.quiet) else False + write_to_stdout(addr_data_str,what,confirm) elif not sys.stdout.isatty(): - write_to_stdout(addr_data_str,gen_what,confirm=False) + write_to_stdout(addr_data_str,what,confirm=False) else: confirm = False if g.quiet else True outfile = outfile_base + "." + ( - g.addrfile_ext if 'print_addresses_only' in opts else ( - g.keyfile_ext if 'no_addresses' in opts else ( - g.keylist_ext if 'flat_list' in opts else "akeys")) - ) - write_to_file(outfile,addr_data_str,opts,gen_what,confirm,True) + g.keylist_ext if 'flat_list' in opts else ( + g.keyfile_ext if opts['gen_what'] == ("keys") else ( + g.addrfile_ext if opts['gen_what'] == ("addrs") else "akeys"))) + write_to_file(outfile,addr_data_str,opts,what,confirm,True) -if not 'no_addresses' in opts: +if 'addrs' in opts['gen_what']: msg("Checksum for address data {}: {}".format(outfile_base,addr_data_chksum)) if 'save_checksum' in opts: a = "address data checksum" diff --git a/mmgen-passchg b/mmgen-passchg index 7165532e..74d65826 100755 --- a/mmgen-passchg +++ b/mmgen-passchg @@ -26,6 +26,7 @@ from mmgen.util import * import mmgen.config as g help_data = { + 'prog_name': g.prog_name, 'desc': """Change the passphrase, hash preset or label of an {} deterministic wallet""".format(g.proj_name), 'usage': "[opts] [filename]", diff --git a/mmgen-pywallet b/mmgen-pywallet index 1169420e..50ea3898 100755 --- a/mmgen-pywallet +++ b/mmgen-pywallet @@ -86,6 +86,8 @@ help_data = { } opts,cmd_args = parse_opts(sys.argv,help_data) +from mmgen.Opts import check_incompatible_opts +check_incompatible_opts(opts,('json','keys','addrs','keysforaddrs')) if len(cmd_args) == 1: from mmgen.util import check_infile diff --git a/mmgen-tool b/mmgen-tool index 8c13eedb..c0c76d03 100755 --- a/mmgen-tool +++ b/mmgen-tool @@ -19,13 +19,10 @@ mmgen-tool: Perform various Bitcoin-related operations - part of the MMGen suite """ -import sys, os -from hashlib import sha256 - +import sys import mmgen.config as g import mmgen.tool as tool from mmgen.Opts import * -from mmgen.util import pretty_hexdump help_data = { 'prog_name': g.prog_name, diff --git a/mmgen-txsign b/mmgen-txsign index a11b7242..6e4cd4c8 100755 --- a/mmgen-txsign +++ b/mmgen-txsign @@ -89,6 +89,11 @@ Seed data supplied in files must have the following extensions: opts,infiles = parse_opts(sys.argv,help_data) +for l in ( +('tx_id', 'info'), +('keys_from_file','all_keys_from_file') +): check_incompatible_opts(opts,l) + if "quiet" in opts: g.quiet = True if 'from_incog_hex' in opts or 'from_incog_hidden' in opts: opts['from_incog'] = True diff --git a/mmgen-walletgen b/mmgen-walletgen index 9dfce80b..09d90dbd 100755 --- a/mmgen-walletgen +++ b/mmgen-walletgen @@ -25,7 +25,6 @@ from hashlib import sha256 import mmgen.config as g from mmgen.Opts import * from mmgen.license import * -from mmgen.walletgen import * from mmgen.util import * help_data = { diff --git a/mmgen/Opts.py b/mmgen/Opts.py index d0154b15..ab42bb22 100755 --- a/mmgen/Opts.py +++ b/mmgen/Opts.py @@ -16,112 +16,101 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import sys, getopt +import sys import mmgen.config as g +import mmgen.opt.Opts +from mmgen.util import msg,check_infile,check_outfile,check_outdir -def usage(hd): - print "USAGE: %s %s" % (hd['prog_name'], hd['usage']) - sys.exit(2) +def usage(hd): mmgen.opt.Opts.usage(hd) -def print_version_info(): # MMGen only +def print_version_info(): print """ '{g.prog_name}' version {g.version}. Part of the {g.proj_name} suite. Copyright (C) {g.Cdates} by {g.author} {g.email}. """.format(g=g).strip() -def print_help(help_data): - pn = help_data['prog_name'] - pn_len = str(len(pn)+2) - print (" %-"+pn_len+"s %s") % (pn.upper()+":", help_data['desc'].strip()) - print (" %-"+pn_len+"s %s %s")%("USAGE:", pn, help_data['usage'].strip()) - sep = "\n " - print " OPTIONS:"+sep+"%s" % sep.join(help_data['options'].strip().split("\n")) - if "notes" in help_data: - print " %s" % "\n ".join(help_data['notes'][1:-1].split("\n")) - - -def process_opts(argv,help_data,short_opts,long_opts): - - if len(argv) == 2 and argv[1] == '--version': # MMGen only! - print_version_info(); sys.exit() - - if g.debug: - print "Short opts: %s" % repr(short_opts) - print "Long opts: %s" % repr(long_opts) - - long_opts = [i.replace("_","-") for i in long_opts] - - try: cl_opts, args = getopt.getopt(argv[1:], short_opts, long_opts) - except getopt.GetoptError as err: - print str(err); sys.exit(2) - - opts,short_opts_l = {},[] - - for i in short_opts: - if i == ":": short_opts_l[-1] += i - else: short_opts_l += i - - for opt, arg in cl_opts: - if opt in ("-h","--help"): print_help(help_data); sys.exit() - elif opt[:2] == "--" and opt[2:] in long_opts: - opts[opt[2:].replace("-","_")] = True - elif opt[:2] == "--" and opt[2:]+"=" in long_opts: - opts[opt[2:].replace("-","_")] = arg - elif opt[0] == "-" and opt[1] in short_opts_l: - opts[long_opts[short_opts_l.index(opt[1:])].replace("-","_")] = True - elif opt[0] == "-" and opt[1:]+":" in short_opts_l: - opts[long_opts[short_opts_l.index(opt[1:]+":")][:-1].replace("-","_")] = arg - else: assert False, "Invalid option" - - if g.debug: print "User-selected options: %s" % repr(opts) - - return opts,args - +def check_incompatible_opts(opts,incompat_list): + bad = [k for k in opts.keys() if k in incompat_list] + if len(bad) > 1: + msg("Mutually exclusive options: %s" % " ".join( + ["--"+b.replace("_","-") for b in bad])) + sys.exit(1) def parse_opts(argv,help_data): - lines = help_data['options'].strip().split("\n") - import re - pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{1,64})(=| )(.+)" - rep = r"-{0}, --{1}{w}{3}" - opt_data = [list(m.groups()) for m in [re.match(pat,l) for l in lines] if m] -# for o in opt_data: print o -# sys.exit() + if len(argv) == 2 and argv[1] == '--version': + print_version_info(); sys.exit() - for d in opt_data: - if d[2] == " ": d[2] = "" - short_opts = "".join([d[0]+d[2].replace("=",":") for d in opt_data]) - long_opts = [d[1].replace("-","_")+d[2] for d in opt_data] - help_data['options'] = "\n".join( - [rep.format(w=" ", *m.groups()) - if m else k for m,k in [(re.match(pat,l),l) for l in lines]] - ) - opts,infiles = process_opts(argv,help_data,short_opts,long_opts) + opts,args,short_opts,long_opts = mmgen.opt.Opts.parse_opts(argv,help_data) + + if g.debug: + print "short opts: %s" % repr(short_opts) + print "long opts: %s" % repr(long_opts) + print "user-selected opts: %s" % repr(opts) + print "cmd args: %s" % repr(args) + + for l in ( + ('outdir', 'export_incog_hidden'), + ('from_incog_hidden','from_incog','from_seed','from_mnemonic','from_brain'), + ('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic', + 'export_seed'), + ('quiet','verbose') + ): check_incompatible_opts(opts,l) # check_opts() doesn't touch opts[] if not check_opts(opts,long_opts): sys.exit(1) - # If unset, set these to the default values in mmgen.config: + # If unset, set these to default values in mmgen.config: for v in g.cl_override_vars: if v in opts: typeconvert_override_var(opts,v) else: opts[v] = eval("g."+v) - if g.debug: print "processed opts: %s" % opts + if g.debug: print "opts after typeconvert: %s" % opts - return opts,infiles + return opts,args def show_opts_and_cmd_args(opts,cmd_args): print "Processed options: %s" % repr(opts) print "Cmd args: %s" % repr(cmd_args) - -# Everything below here is MMGen-specific: - -from mmgen.util import msg,check_infile - def check_opts(opts,long_opts): + def opt_splits(val,sep,n,what): + sepword = "comma" if sep == "," else ( + "colon" if sep == ":" else ("'"+sep+"'")) + try: l = val.split(sep) + except: + msg("'%s': invalid %s (not %s-separated list)" % (val,what,sepword)) + return False + + if len(l) == n: return True + else: + msg("'%s': invalid %s (%s %s-separated items required)" % + (val,what,n,sepword)) + return False + + def opt_compares(val,op,target,what): + if not eval("%s %s %s" % (val, op, target)): + msg("%s: invalid %s (not %s %s)" % (val,what,op,target)) + return False + return True + + def opt_is_int(val,what): + try: int(val) + except: + msg("'%s': invalid %s (not an integer)" % (val,what)) + return False + return True + + def opt_is_in_list(val,lst,what): + if val not in lst: + q,sep = ("'","','") if type(lst[0]) == str else ("",",") + msg("{q}{}{q}: invalid {}\nValid options: {q}{}{q}".format( + val,what,sep.join([str(i) for i in sorted(lst)]),q=q)) + return False + return True + for opt,val in opts.items(): what = "parameter for '--%s' option" % opt.replace("_","-") @@ -133,121 +122,49 @@ def check_opts(opts,long_opts): continue if opt == 'outdir': - what = "output directory" - import os - if os.path.isdir(val): - if os.access(val, os.W_OK|os.X_OK): - opts[opt] = os.path.normpath(val) - else: - msg("Requested %s '%s' is unwritable by you" % (what,val)) - return False - else: - msg("Requested %s '%s' does not exist" % (what,val)) - return False - + check_outdir(val) # exits on error elif opt == 'label': - - if len(val) > g.max_wallet_label_len: - msg("Label must be %s characters or less" % - g.max_wallet_label_len) + if not opt_compares(len(val),"<=",g.max_wallet_label_len,"label length"): return False - + try: val.decode("ascii") + except: + msg("ERROR: label contains a non-ASCII symbol") + return False + w = "character in label" for ch in list(val): - chs = g.wallet_label_symbols - if ch not in chs: - msg("'%s': ERROR: label contains an illegal symbol" % val) - msg("The following symbols are permitted:\n%s" % "".join(chs)) - return False + if not opt_is_in_list(ch,g.wallet_label_symbols,w): return False elif opt == 'export_incog_hidden' or opt == 'from_incog_hidden': - try: - if opt == 'export_incog_hidden': - outfile,offset = val.split(",") - else: - outfile,offset,seed_len = val.split(",") - except: - msg("'%s': invalid %s" % (val,what)) - return False - - try: - o = int(offset) - except: - msg("'%s': invalid 'o' %s (not an integer)" % (offset,what)) - return False - - if o < 0: - msg("'%s': invalid 'o' %s (less than zero)" % (offset,what)) - return False - if opt == 'from_incog_hidden': - try: - sl = int(seed_len) - except: - msg("'%s': invalid 'l' %s (not an integer)" % (sl,what)) - return False - - if sl not in g.seed_lens: - msg("'%s': invalid 'l' %s (valid choices: %s)" % - (sl,what," ".join(str(i) for i in g.seed_lens))) - return False - - import os, stat - try: mode = os.stat(outfile).st_mode - except: - msg("Unable to stat requested %s '%s'" % (what,outfile)) - return False - - if not (stat.S_ISREG(mode) or stat.S_ISBLK(mode)): - msg("Requested %s '%s' is not a file or block device" % - (what,outfile)) - return False - - ac,m = (os.W_OK,"writ") \ - if "export_incog_hidden" in opts else (os.R_OK,"read") - if not os.access(outfile, ac): - msg("Requested %s '%s' is un%sable by you" % (what,outfile,m)) - return False - + if not opt_splits(val,",",3,what): return False + infile,offset,seed_len = val.split(",") + check_infile(infile) + w = "seed length " + what + if not opt_is_int(seed_len,w): return False + if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False + else: + if not opt_splits(val,",",2,what): return False + outfile,offset = val.split(",") + check_outfile(outfile) + w = "offset " + what + if not opt_is_int(offset,w): return False + if not opt_compares(offset,">=",0,what): return False elif opt == 'from_brain': - try: - l,p = val.split(",") - except: - msg("'%s': invalid %s" % (val,what)) - return False - - try: - int(l) - except: - msg("'%s': invalid 'l' %s (not an integer)" % (l,what)) - return False - - if int(l) not in g.seed_lens: - msg("'%s': invalid 'l' %s. Options: %s" % - (l, what, ", ".join([str(i) for i in g.seed_lens]))) - return False - - if p not in g.hash_presets: - hps = ", ".join([i for i in sorted(g.hash_presets.keys())]) - msg("'%s': invalid 'p' %s. Options: %s" % (p, what, hps)) - return False + if not opt_splits(val,",",2,what): return False + l,p = val.split(",") + w = "seed length " + what + if not opt_is_int(l,w): return False + if not opt_is_in_list(int(l),g.seed_lens,w): return False + w = "hash preset " + what + if not opt_is_in_list(p,g.hash_presets.keys(),w): return False elif opt == 'seed_len': - if val not in g.seed_lens: - msg("'%s': invalid %s. Options: %s" - % (val,what,", ".join([str(i) for i in g.seed_lens]))) - return False + if not opt_is_int(val,what): return False + if not opt_is_in_list(int(val),g.seed_lens,what): return False elif opt == 'hash_preset': - if val not in g.hash_presets: - msg("'%s': invalid %s. Options: %s" - % (val,what,", ".join(sorted(g.hash_presets.keys())))) - return False + if not opt_is_in_list(val,g.hash_presets.keys(),what): return False elif opt == 'usr_randchars': - try: v = int(val) - except: - msg("'%s': invalid value for %s (not an integer)" % (val,what)) - return False - if v != 0 and not (g.min_urandchars <= v <= g.max_urandchars): - msg("'%s': invalid %s (must be >= %s and <= %s (or zero))" - % (v,what,g.min_urandchars,g.max_urandchars)) - return False + if not opt_is_int(val,what): return False + if not opt_compares(val,">=",g.min_urandchars,what): return False + if not opt_compares(val,"<=",g.max_urandchars,what): return False else: if g.debug: print "check_opts(): No test for opt '%s'" % opt diff --git a/mmgen/addr.py b/mmgen/addr.py index 9543fccc..2f133664 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -33,36 +33,33 @@ addrmsgs = { # MMGen address file # # This file is editable. -# Everything following a hash symbol '#' is a comment and ignored by {}. +# Everything following a hash symbol '#' is a comment and ignored by {pnm}. # A text label of {} characters or less may be added to the right of each # address, and it will be appended to the bitcoind wallet label upon import. # The label may contain any printable ASCII symbol. -""".strip().format(g.proj_name,g.max_addr_label_len) +""".strip().format(g.max_addr_label_len,pnm=g.proj_name), + 'no_keyconv_msg': """ +Executable '{kcexe}' unavailable. Falling back on (slow) internal ECDSA library. +Please install '{kcexe}' from the {vanityg} package on your system for much +faster address generation. +""".format(kcexe=g.keyconv_exec, vanityg="vanitygen") } def test_for_keyconv(): - """ - Test for the presence of 'keyconv' utility on system - """ from subprocess import Popen, PIPE try: p = Popen([g.keyconv_exec, '-h'], stdout=PIPE, stderr=PIPE) except: - sys.stderr.write(""" -Executable '%s' unavailable. Falling back on (slow) internal ECDSA library. -Please install '%s' from the %s package on your system for much -faster address generation. - -""" % (g.keyconv_exec, g.keyconv_exec, "vanitygen")) + msg(addrmsgs['no_keyconv_msg']) return False - else: - return True + + return True def generate_addrs(seed, addrnums, opts): - if not 'no_addresses' in opts: + if 'addrs' in opts['gen_what']: if 'no_keyconv' in opts or test_for_keyconv() == False: msg("Using (slow) internal ECDSA library for address generation") from mmgen.bitcoin import privnum2addr @@ -71,73 +68,62 @@ def generate_addrs(seed, addrnums, opts): from subprocess import Popen, PIPE keyconv = "keyconv" - a,t_addrs,i,out = sorted(addrnums),len(addrnums),0,[] + fmt = "num addr" if opts['gen_what'] == ("addrs") else ( + "num sec wif" if opts['gen_what'] == ("keys") else "num sec wif addr") - while a: - seed = sha512(seed).digest() - i += 1 # round /i/ + from collections import namedtuple + addrinfo = namedtuple("addrinfo",fmt) + addrinfo_args = "%s" % ",".join(fmt.split()) - if g.debug: print "Seed round %s: %s" % (i, hexlify(seed)) + t_addrs,num,pos,out = len(addrnums),0,0,[] + addrnums.sort() # needed only if caller didn't sort - if i < a[0]: continue + try: + while pos != t_addrs: + seed = sha512(seed).digest() + num += 1 # round - a.pop(0) + if g.debug: print "Seed round %s: %s" % (num, hexlify(seed)) + if num != addrnums[pos]: continue - qmsg_r("\rGenerating %s %s (%s of %s)" % - (opts['gen_what'], i, t_addrs-len(a), t_addrs)) + pos += 1 - # Secret key is double sha256 of seed hash round /i/ - sec = sha256(sha256(seed).digest()).hexdigest() - wif = numtowif(int(sec,16)) + qmsg_r("\rGenerating %s %s (%s of %s)" % + (opts['gen_what'][-1],num,pos,t_addrs)) - if g.debug: - print "Privkey round %s:\n hex: %s\n wif: %s" % (i, sec, wif) + # Secret key is double sha256 of seed hash round /num/ + sec = sha256(sha256(seed).digest()).hexdigest() + wif = numtowif(int(sec,16)) - d = { 'num': i } + if 'addrs' in opts['gen_what']: addr = \ + Popen([keyconv, wif], stdout=PIPE).stdout.readline().split()[1] \ + if keyconv else privnum2addr(int(sec,16)) - if not 'print_addresses_only' in opts: - d['sec'] = sec - d['wif'] = wif + out.append(eval("addrinfo("+addrinfo_args+")")) - if not 'no_addresses' in opts: - if keyconv: - p = Popen([keyconv, wif], stdout=PIPE) - addr = dict([j.split() for j in \ - p.stdout.readlines()])['Address:'] - else: - addr = privnum2addr(int(sec,16)) + except KeyboardInterrupt: + msg("\nUser interrupt") + sys.exit(1) - d['addr'] = addr - - out.append(d) - - w = opts['gen_what'] - if t_addrs == 1: - import re - w = re.sub('e*s$','',w) + w = 'key' if 'keys' in opts['gen_what'] else 'address' + if t_addrs != 1: w = w+"s" if w == 'key' else w+"es" qmsg("\rGenerated %s %s%s"%(t_addrs, w, " "*15)) return out -def generate_keys(seed, addrnums): - o = {'no_addresses': True, 'gen_what': "keys"} - return generate_addrs(seed, addrnums, o) - def format_addr_data(addr_data, addr_data_chksum, seed_id, addr_idxs, opts): if 'flat_list' in opts: - return "\n\n".join(["# %s:%s %s\n%s" % (seed_id,d['num'],d['addr'],d['wif']) + return "\n\n".join(["# {}:{d.num} {d.addr}\n{d.wif}".format(seed_id,d=d) for d in addr_data])+"\n\n" - start = addr_data[0]['num'] - end = addr_data[-1]['num'] - fs = " %-{}s %s".format(len(str(end))) - out = [] + fs = " {:<%s} {}" % len(str(addr_data[-1].num)) - if not 'no_addresses' in opts: - if not 'stdout' in opts: out.append(addrmsgs['addrfile_header'] + "\n") + if 'addrs' not in opts['gen_what']: out = [] + else: + out = [] if 'stdout' in opts else [addrmsgs['addrfile_header']+"\n"] out.append("# Address data checksum for {}[{}]: {}".format( seed_id, fmt_addr_idxs(addr_idxs), addr_data_chksum)) out.append("# Record this value to a secure location\n") @@ -145,14 +131,16 @@ def format_addr_data(addr_data, addr_data_chksum, seed_id, addr_idxs, opts): out.append("%s {" % seed_id.upper()) for d in addr_data: - if 'no_addresses' in opts: - out.append(fs % (d['num'], "wif: " + d['wif'])) + if 'addrs' in opts['gen_what']: # First line with number + out.append(fs.format(d.num, d.addr)) else: - out.append(fs % (d['num'], d['addr'])) - if 'b16' in opts: - out.append(fs % ("", "hex: " + d['sec'])) - if 'print_secret' in opts and not 'no_addresses' in opts: - out.append(fs % ("", "wif: " + d['wif'])) + out.append(fs.format(d.num, "wif: "+d.wif)) + + if 'keys' in opts['gen_what']: # Subsequent lines + if 'b16' in opts: + out.append(fs.format("", "hex: "+d.sec)) + if 'addrs' in opts['gen_what']: + out.append(fs.format("", "wif: "+d.wif)) out.append("}") diff --git a/mmgen/tests/addr.py b/mmgen/tests/addr.py deleted file mode 100755 index ae6a7c05..00000000 --- a/mmgen/tests/addr.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -addr.py: Test suite for mmgen.addr module -""" - -from mmgen.addr import * - -tests = "none", - -if (len(sys.argv) == 1): - print "Available tests: %s" % " ".join(tests) diff --git a/mmgen/tests/mn_electrum.py b/mmgen/tests/mn_electrum.py deleted file mode 100755 index cae7bbd2..00000000 --- a/mmgen/tests/mn_electrum.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -mn_electrum.py: Test suite for mmgen.mn_electrum module -""" - -from mmgen.mn_electrum import * - -print electrum_words.strip() diff --git a/mmgen/tests/mn_tirosh.py b/mmgen/tests/mn_tirosh.py deleted file mode 100755 index bfd513ac..00000000 --- a/mmgen/tests/mn_tirosh.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -mn_tirosh.py: Test suite for mmgen.mn_tirosh module -""" - -from mmgen.mn_tirosh import * - -print tirosh_words.strip() diff --git a/mmgen/tests/util.py b/mmgen/tests/util.py deleted file mode 100755 index 3541e790..00000000 --- a/mmgen/tests/util.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -util.py: Test suite for mmgen.util module -""" - -from mmgen.util import * - -tests = "none", - -if (len(sys.argv) == 1): - print "Available tests: %s" % " ".join(tests) diff --git a/mmgen/tests/walletgen.py b/mmgen/tests/walletgen.py deleted file mode 100755 index c4a7ffe4..00000000 --- a/mmgen/tests/walletgen.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -walletgen.py: Test suite for mmgen.walletgen module -""" - -from mmgen.walletgen import * - -tests = "none", - -if (len(sys.argv) == 1): - print "Available tests: %s" % " ".join(tests) diff --git a/mmgen/tool.py b/mmgen/tool.py index ac266c10..5c0f242e 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -424,8 +424,10 @@ def encrypt(infile,outfile="",hash_preset=''): enc_d = encrypt_data(sha256(nonce+d).digest() + nonce + d, key, int(ba.hexlify(iv),16)) if outfile == '-': sys.stdout.write(salt+iv+enc_d) - else: write_to_file((outfile or infile+"."+g.mmenc_ext), - salt+iv+enc_d,opts,"encrypted data",True,True) + else: + if not outfile: + outfile = os.path.basename(infile) + "." + g.mmenc_ext + write_to_file(outfile, salt+iv+enc_d, opts,"encrypted data",True,True) def decrypt(infile,outfile="",hash_preset=''): d = get_data_from_file(infile,"encrypted data") @@ -439,12 +441,15 @@ def decrypt(infile,outfile="",hash_preset=''): from hashlib import sha256 if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest(): out = dec_d[sha256_len+nonce_len:] - if outfile == '-': sys.stdout.write(out) + if outfile == '-': sys.stdout.write(out) else: - import re - of = re.sub(r'\.%s$'%g.mmenc_ext,r'',infile) - if of == infile: of = infile+".dec" - write_to_file((outfile or of),out,opts,"decrypted data",True,True) + if not outfile: + outfile = os.path.basename(infile) + if outfile[-len(g.mmenc_ext)-1:] == "."+g.mmenc_ext: + outfile = outfile[:-len(g.mmenc_ext)-1] + else: + outfile = outfile + ".dec" + write_to_file(outfile, out, opts,"decrypted data",True,True) else: msg("Incorrect passphrase or hash preset") diff --git a/mmgen/tx.py b/mmgen/tx.py index 01f9fb77..ed7ca856 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -628,13 +628,13 @@ def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds,opts,gen_pairs=Fals seed = get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts) addr_ids = [int(i[9:]) for i in mmgen_addrs if i[:8] == seed_id] - from mmgen.addr import generate_keys,generate_addrs + from mmgen.addr import generate_addrs if gen_pairs: - o = {"gen_what":"addresses"} - ret += [("%s:%s" % (seed_id,i['num']),i['addr']) - for i in generate_addrs(seed, addr_ids, o)] + ret += [("{}:{}".format(seed_id,i.num),i.addr) + for i in generate_addrs(seed, addr_ids, {'gen_what':("addrs")})] else: - ret += [i['wif'] for i in generate_keys(seed, addr_ids)] + ret += [i.wif for i in generate_addrs( + seed,addr_ids,{'gen_what':("keys")})] return ret diff --git a/mmgen/util.py b/mmgen/util.py index f096bd90..0b0f13c5 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -256,25 +256,36 @@ def make_iv_chksum(s): return sha256(s).hexdigest()[:8].upper() -def check_infile(f): +def check_file_type_and_access(fname,ftype): import os, stat - try: mode = os.stat(f).st_mode + typ2,tdesc2,access,action = (stat.S_ISLNK,"symbolic link",os.R_OK,"read")\ + if ftype == "input file" else (stat.S_ISBLK,"block device",os.W_OK,"writ") + + if ftype == "directory": + typ1,typ2,tdesc = stat.S_ISDIR,stat.S_ISDIR,"directory" + else: + typ1,tdesc = stat.S_ISREG,"regular file or "+tdesc2 + + try: mode = os.stat(fname).st_mode except: - msg("Unable to stat requested input file '%s'" % f) + msg("Unable to stat requested %s '%s'" % (ftype,fname)) sys.exit(1) - if not stat.S_ISREG(mode) or stat.S_ISLNK(mode): - msg("Requested input file '%s' is not a file" % f) + if not (typ1(mode) or typ2(mode)): + msg("Requested %s '%s' is not a %s" % (ftype,fname,tdesc)) sys.exit(1) - if not os.access(f, os.R_OK): - msg("Requested input file '%s' is unreadable by you" % f) + if not os.access(fname, access): + msg("Requested %s '%s' is un%sable by you" % (ftype,fname,action)) sys.exit(1) return True +def check_infile(f): return check_file_type_and_access(f,"input file") +def check_outfile(f): return check_file_type_and_access(f,"output file") +def check_outdir(f): return check_file_type_and_access(f,"directory") def _validate_addr_num(n): diff --git a/mmgen/walletgen.py b/mmgen/walletgen.py deleted file mode 100755 index b82051bf..00000000 --- a/mmgen/walletgen.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013-2014 by philemon -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -walletgen.py: Routines used for seed generation and wallet creation -""" - -import sys -import mmgen.config as g -from mmgen.util import msg, msg_r, qmsg, qmsg_r, get_char, prompt_and_get_char -from binascii import hexlify - -