Code cleanups, streamlined code in 'mmgen/addr.py'
This commit is contained in:
parent
4f4c749bbf
commit
7e079f1b7a
17 changed files with 220 additions and 450 deletions
|
|
@ -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] <address list>",
|
||||
'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"
|
||||
|
|
|
|||
|
|
@ -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]",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
283
mmgen/Opts.py
283
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
118
mmgen/addr.py
118
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("}")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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)
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
mn_electrum.py: Test suite for mmgen.mn_electrum module
|
||||
"""
|
||||
|
||||
from mmgen.mn_electrum import *
|
||||
|
||||
print electrum_words.strip()
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
mn_tirosh.py: Test suite for mmgen.mn_tirosh module
|
||||
"""
|
||||
|
||||
from mmgen.mn_tirosh import *
|
||||
|
||||
print tirosh_words.strip()
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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)
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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)
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
10
mmgen/tx.py
10
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue