From 9dc54c414d936e61c27219a94910d7ccffb5fbaf Mon Sep 17 00:00:00 2001 From: philemon Date: Thu, 7 Aug 2014 21:39:25 +0400 Subject: [PATCH] Code cleanups, small interface improvements --- mmgen/config.py | 1 + mmgen/crypto.py | 16 ++++++++------ mmgen/main_addrgen.py | 28 ++++++++++++------------ mmgen/main_passchg.py | 2 +- mmgen/main_txcreate.py | 8 +++---- mmgen/main_txsign.py | 48 +++++++++++++++++++++-------------------- mmgen/main_walletchk.py | 14 ++++++------ mmgen/main_walletgen.py | 2 +- mmgen/tx.py | 35 ++++++++++++++++-------------- mmgen/util.py | 32 ++++++++++++++++++--------- 10 files changed, 103 insertions(+), 83 deletions(-) diff --git a/mmgen/config.py b/mmgen/config.py index 0a3ce065..65e4f595 100755 --- a/mmgen/config.py +++ b/mmgen/config.py @@ -49,6 +49,7 @@ seedfile_exts = wallet_ext, seed_ext, mn_ext, brain_ext, incog_ext rawtx_ext = "raw" sigtx_ext = "sig" addrfile_ext = "addrs" +addrfile_chksum_ext = "chk" keyfile_ext = "keys" keylist_ext = "keylist" mmenc_ext = "mmenc" diff --git a/mmgen/crypto.py b/mmgen/crypto.py index eaaa669f..d4622090 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -119,11 +119,13 @@ def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32): return scrypt.hash(passwd, salt, 2**N, r, p, buflen=buflen) -def make_key(passwd, salt, hash_preset, what="key"): +def make_key(passwd, salt, hash_preset, what="key", verbose=False): - vmsg_r("Generating %s. Please wait..." % what) + if g.verbose or verbose: + msg_r("Generating %s. Please wait..." % what) key = scrypt_hash_passphrase(passwd, salt, hash_preset) - vmsg("done") + if g.verbose or verbose: + msg("done") if g.debug: print "Key: %s" % hexlify(key) return key @@ -169,14 +171,14 @@ def get_random(length,opts): from Crypto import Random os_rand = Random.new().read(length) if 'usr_randchars' in opts and opts['usr_randchars'] not in (0,-1): - kwhat = "a key from OS random data + " + kwhat = "a key from OS random data plus " if not g.user_entropy: g.user_entropy = sha256( get_random_data_from_user(opts['usr_randchars'])).digest() kwhat += "user entropy" else: kwhat += "saved user entropy" - key = make_key(g.user_entropy, "", '2', what=kwhat) + key = make_key(g.user_entropy, "", '2', what=kwhat, verbose=True) return encrypt_data(os_rand,key,what="random data",verify=False) else: return os_rand @@ -204,7 +206,7 @@ def get_seed_from_wallet( def get_seed_from_incog_wallet( infile, opts, - prompt="{} wallet".format(g.proj_name), + prompt_what="{} incognito wallet".format(g.proj_name), silent=False, hex_input=False ): @@ -236,7 +238,7 @@ def get_seed_from_incog_wallet( vmsg(cmessages['incog_iv_id_hidden' if "from_incog_hidden" in opts else 'incog_iv_id']) - passwd = get_mmgen_passphrase(prompt,opts) + passwd = get_mmgen_passphrase(prompt_what,opts) qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets))) while True: diff --git a/mmgen/main_addrgen.py b/mmgen/main_addrgen.py index 8b45a420..1c38ee5a 100755 --- a/mmgen/main_addrgen.py +++ b/mmgen/main_addrgen.py @@ -35,9 +35,9 @@ 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, + 'desc': """Generate a range or list of {} from an {g.proj_name} wallet, mnemonic, seed or password""".format(what,g=g), - 'usage':"[opts] [infile]
", + 'usage':"[opts] [infile]
", 'options': """ -h, --help Print this help message{} -d, --outdir= d Specify an alternate directory 'd' for output @@ -50,7 +50,7 @@ help_data = { (default: {g.seed_len}) -p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when hashing password (default: '{g.hash_preset}') --P, --passwd-file= f Get passphrase from file 'f' +-P, --passwd-file= f Get MMGen wallet passphrase from file 'f' -q, --quiet Suppress warnings; overwrite files without prompting -S, --stdout Print {what} to stdout @@ -159,8 +159,16 @@ addr_data_str = format_addr_data( addr_data, addr_data_chksum, seed_id, addr_idxs, opts) outfile_base = "{}[{}]".format(seed_id, fmt_addr_idxs(addr_idxs)) +if 'addrs' in opts['gen_what']: + qmsg("Checksum for address data %s: %s" % (outfile_base,addr_data_chksum)) + if 'save_checksum' in opts: + write_to_file(outfile_base+"."+g.addrfile_chksum_ext, + addr_data_chksum+"\n",opts,"address data checksum",True,True,False) + else: + qmsg("This checksum will be used to verify the address file in the future.") + qmsg("Record it to a safe location.") -if 'flat_list' in opts and user_confirm("Encrypt key list?"): +if 'flat_list' in opts and keypress_confirm("Encrypt key list?"): addr_data_str = mmgen_encrypt(addr_data_str,"key list","",opts) enc_ext = "." + g.mmenc_ext else: enc_ext = "" @@ -176,15 +184,7 @@ else: confirm_overwrite = False if g.quiet else True outfile = "%s.%s%s" % (outfile_base, ( 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"))), enc_ext) + g.keyfile_ext if opts['gen_what'] == ["keys"] else ( + g.addrfile_ext if opts['gen_what'] == ["addrs"] else "akeys"))), enc_ext) write_to_file(outfile,addr_data_str,opts,what,confirm_overwrite,True) -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" - write_to_file(outfile_base+".chk",addr_data_chksum,opts,a,False,True) - else: - qmsg("This checksum will be used to verify the address file in the future.") - qmsg("Record it to a safe location.") diff --git a/mmgen/main_passchg.py b/mmgen/main_passchg.py index 54be6c54..92194ae2 100755 --- a/mmgen/main_passchg.py +++ b/mmgen/main_passchg.py @@ -41,7 +41,7 @@ help_data = { -L, --label= l Change the wallet's label to 'l' -p, --hash-preset= p Change scrypt.hash() parameters to preset 'p' (default: '{g.hash_preset}') --P, --passwd-file= f Get new passphrase from file 'f' +-P, --passwd-file= f Get new MMGen wallet passphrase from file 'f' -r, --usr-randchars= n Get 'n' characters of additional randomness from user (min={g.min_urandchars}, max={g.max_urandchars}) -q, --quiet Suppress warnings; overwrite files without diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index 7f7e94d8..0cf24c0e 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -28,7 +28,7 @@ import mmgen.config as g from mmgen.Opts import * from mmgen.license import * from mmgen.tx import * -from mmgen.util import msg, msg_r, user_confirm +from mmgen.util import msg, msg_r, keypress_confirm help_data = { 'prog_name': g.prog_name, @@ -165,7 +165,7 @@ while True: if mmaddrs and len(mmaddrs) < len(sel_unspent): msg(txmsg['mixed_inputs'] % ", ".join(sorted(mmaddrs))) - if not user_confirm("Accept?"): + if not keypress_confirm("Accept?"): continue total_in = trim_exponent(sum([i.amount for i in sel_unspent])) @@ -173,7 +173,7 @@ while True: if change >= 0: prompt = "Transaction produces %s BTC in change. OK?" % change - if user_confirm(prompt,default_yes=True): + if keypress_confirm(prompt,default_yes=True): break else: msg(txmsg['not_enough_btc'] % change) @@ -206,7 +206,7 @@ if reply and reply in "YyVv": view_tx_data(c,[i.__dict__ for i in sel_unspent],tx_hex,b2m_map,pager=pager) prompt = "Save transaction?" -if user_confirm(prompt,default_yes=True): +if keypress_confirm(prompt,default_yes=True): amt = send_amt or change tx_id = make_chksum_6(unhexlify(tx_hex)).upper() outfile = "tx_%s[%s].%s" % (tx_id,amt,g.rawtx_ext) diff --git a/mmgen/main_txsign.py b/mmgen/main_txsign.py index 50fad675..dcbd8c89 100755 --- a/mmgen/main_txsign.py +++ b/mmgen/main_txsign.py @@ -44,7 +44,7 @@ help_data = { for online signing without an {pnm} seed source. {pnm}-to-BTC mappings can optionally be verified using address file(s) listed on the command line --P, --passwd-file= f Get passphrase from file 'f' +-P, --passwd-file= f Get MMGen wallet or bitcoind passphrase from file 'f' -q, --quiet Suppress warnings; overwrite files without prompting -v, --verbose Produce more verbose output @@ -127,7 +127,12 @@ if 'keys_from_file' in opts: keys_from_file = remove_comments(d.split("\n")) else: keys_from_file = [] -for tx_file in tx_files: +tx_num_str = "" +for tx_num,tx_file in enumerate(tx_files,1): + if len(tx_files) > 1: + msg("\nTransaction #%s of %s:" % (tx_num,len(tx_files))) + tx_num_str = " #%s" % tx_num + m = "" if 'tx_id' in opts else "transaction data" tx_data = get_lines_from_file(tx_file,m) @@ -169,11 +174,8 @@ for tx_file in tx_files: check_mmgen_to_btc_addr_mappings( mmgen_inputs,b2m_map,seed_files,saved_seeds,opts) - if len(tx_files) > 1: - msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files))) - - prompt = "View transaction data? (y)es, (N)o, (v)iew in pager" - reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True) + p = "View data for transaction{}? (y)es, (N)o, (v)iew in pager" + reply = prompt_and_get_char(p.format(tx_num_str),"YyNnVv",enter_ok=True) if reply and reply in "YyVv": p = True if reply in "Vv" else False view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata,pager=p) @@ -187,27 +189,27 @@ for tx_file in tx_files: keys += get_keys_for_mmgen_addrs(ml,seed_files,saved_seeds,opts) if 'use_wallet_dat' in opts: - sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts) + sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys,opts) else: - sig_tx = sign_transaction(c,tx_hex,sig_data,keys) + sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys) elif other_inputs: if keys: - sig_tx = sign_transaction(c,tx_hex,sig_data,keys) + sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys) else: - sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts) + sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys,opts) if sig_tx['complete']: - prompt = "OK\nSave signed transaction?" - if user_confirm(prompt,default_yes=True): - outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext) - data = "{}\n{}\n{}\n{}\n".format( - " ".join(metadata[:2] + [make_timestamp()]), - sig_tx['hex'], - repr(inputs_data), - repr(b2m_map) - ) - confirm = False if g.quiet else True - write_to_file(outfile,data,opts,"signed transaction",confirm,True) + msg("OK") + outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext) + data = "{}\n{}\n{}\n{}\n".format( + " ".join(metadata[:2] + [make_timestamp()]), + sig_tx['hex'], + repr(inputs_data), + repr(b2m_map) + ) + w = "signed transaction{}".format(tx_num_str) + write_to_file(outfile,data,opts,w,(not g.quiet),True,False) else: - msg("failed\nSome keys were missing. Transaction could not be signed.") + msg_r("failed\nSome keys were missing. ") + msg("Transaction %scould not be signed." % tx_num_str) sys.exit(3) diff --git a/mmgen/main_walletchk.py b/mmgen/main_walletchk.py index 22859d04..7e5d2dfb 100755 --- a/mmgen/main_walletchk.py +++ b/mmgen/main_walletchk.py @@ -25,7 +25,7 @@ import sys import mmgen.config as g from mmgen.Opts import * from mmgen.util import * -from mmgen.crypto import get_seed_from_wallet,wallet_to_incog_data +from mmgen.crypto import get_seed_retry,wallet_to_incog_data help_data = { 'prog_name': g.prog_name, @@ -37,7 +37,7 @@ help_data = { -h, --help Print this help message -d, --outdir= d Specify an alternate directory 'd' for output -e, --echo-passphrase Print passphrase to screen when typing it --P, --passwd-file= f Get passphrase from file 'f' +-P, --passwd-file= f Get MMGen wallet passphrase from file 'f' -q, --quiet Suppress warnings; overwrite files without prompting -r, --usr-randchars= n Get 'n' characters of additional randomness from user (min={g.min_urandchars}, max={g.max_urandchars}) @@ -89,12 +89,12 @@ elif 'export_incog' in opts: ) data = pretty_hexdump(incog_enc,2,8,line_nums=False) \ if "export_incog_hex" in opts else incog_enc - export_to_file(fn, data, opts, "incognito wallet data") + write_to_file_or_stdout(fn, data, opts, "incognito wallet data") sys.exit() -seed = get_seed_from_wallet(cmd_args[0], opts) -if seed: qmsg("Wallet is OK") +seed = get_seed_retry(cmd_args[0], opts) +if seed: msg("Wallet is OK") else: msg("Error opening wallet") sys.exit(2) @@ -105,11 +105,11 @@ if 'export_mnemonic' in opts: p = True if g.debug else False mn = get_mnemonic_from_seed(seed, wl, g.default_wl, print_info=p) fn = "%s.%s" % (make_chksum_8(seed).upper(), g.mn_ext) - export_to_file(fn, " ".join(mn)+"\n", opts, "mnemonic data") + write_to_file_or_stdout(fn, " ".join(mn)+"\n", opts, "mnemonic data") elif 'export_seed' in opts: from mmgen.bitcoin import b58encode_pad data = col4(b58encode_pad(seed)) chk = make_chksum_6(b58encode_pad(seed)) fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext) - export_to_file(fn, "%s %s\n" % (chk,data), opts, "seed data") + write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), opts, "seed data") diff --git a/mmgen/main_walletgen.py b/mmgen/main_walletgen.py index 265711bf..f7dbb350 100755 --- a/mmgen/main_walletgen.py +++ b/mmgen/main_walletgen.py @@ -44,7 +44,7 @@ help_data = { Allowed symbols: A-Z, a-z, 0-9, " ", "_", ".") -p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' (default: '{g.hash_preset}') --P, --passwd-file= f Get passphrase from file 'f' +-P, --passwd-file= f Get MMGen wallet passphrase from file 'f' -q, --quiet Produce quieter output; overwrite files without prompting -r, --usr-randchars= n Get 'n' characters of additional randomness from diff --git a/mmgen/tx.py b/mmgen/tx.py index fa5eef91..04c5e56b 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -39,7 +39,7 @@ address was specified. NOTE: This transaction uses a mixture of both mmgen and non-mmgen inputs, which makes the signing process more complicated. When signing the transaction, keys for the non-mmgen inputs must be supplied in a separate -file using the '-k' option to {}-txsign. +file using either the '-k' or '-K' option to '{}-txsign'. Selected mmgen inputs: %s""".format(g.proj_name.lower()), 'too_many_acct_addresses': """ @@ -52,10 +52,9 @@ No data found for MMgen address '%s'. Please import this address into your tracking wallet, or supply an address file for it on the command line. """.strip(), 'addrfile_warn_msg': """ -Warning: no data for address '{mmaddr}' was found in the tracking wallet, so -this information was taken from the user-supplied address file. You're strongly -advised to import this address into your tracking wallet before proceeding with -this transaction. The address will not be tracked until you do so. +Warning: output address '{mmaddr}' is not in the tracking wallet, which means +its balance will not be tracked. You're strongly advised to import the address +into your tracking wallet before broadcasting this transaction. """.strip(), 'addrfile_fail_msg': """ No data for MMgen address '{mmaddr}' could be found in either the tracking @@ -496,12 +495,14 @@ def mmaddr2btcaddr_addrfile(mmaddr,addr_data,silent=False): if j[0] == mmidx: if not silent: msg(txmsg['addrfile_warn_msg'].format(mmaddr=mmaddr)) - if not user_confirm("Continue anyway?"): + if not keypress_confirm("Continue anyway?"): sys.exit(1) return j[1:] if len(j) == 3 else (j[1],"") if silent: return "","" - else: msg(txmsg['addrfile_fail_msg'].format(mmaddr=mmaddr)); sys.exit(2) + else: + msg(txmsg['addrfile_fail_msg'].format(mmaddr=mmaddr)) + sys.exit(2) def check_mmgen_to_btc_addr_mappings(mmgen_inputs,b2m_map,infiles,saved_seeds,opts): @@ -594,13 +595,13 @@ def parse_addrs_file(f): sys.exit(3) -def sign_transaction(c,tx_hex,sig_data,keys=None): +def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None): if keys: qmsg("%s keys total" % len(keys)) if g.debug: print "Keys:\n %s" % "\n ".join(keys) - msg_r("Signing transaction...") + msg_r("Signing transaction{}...".format(tx_num_str)) from mmgen.rpc import exceptions try: sig_tx = c.signrawtransaction(tx_hex,sig_data,keys) @@ -646,18 +647,19 @@ def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds,opts,gen_pairs=Fals from mmgen.addr import generate_addrs if gen_pairs: ret += [("{}:{}".format(seed_id,i.num),i.addr) - for i in generate_addrs(seed, addr_ids, {'gen_what':["addrs"]})] + for i in generate_addrs(seed,addr_ids, + {'gen_what':["addrs"]})] else: - ret += [i.wif for i in generate_addrs( - seed,addr_ids,{'gen_what':["keys"]})] + ret += [i.wif for i in generate_addrs(seed,addr_ids, + {'gen_what':["keys"]})] return ret -def sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts): +def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys,opts): try: - sig_tx = sign_transaction(c,tx_hex,sig_data,keys) + sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys) except: from mmgen.rpc import exceptions msg("Using keys in wallet.dat as per user request") @@ -672,7 +674,7 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts): else: msg("Passphrase OK"); break - sig_tx = sign_transaction(c,tx_hex,sig_data,keys) + sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys) msg("Locking wallet") try: @@ -761,5 +763,6 @@ def check_mmgen_to_btc_addr_mappings_addrfile(mmgen_inputs,b2m_map,addrfiles): sys.exit(3) if missing: - confirm_or_exit(txmsg['missing_mappings'] % " ".join(missing),"continue") + confirm_or_exit(txmsg['missing_mappings'] % + " ".join(missing),"continue") else: qmsg("Address mappings OK") diff --git a/mmgen/util.py b/mmgen/util.py index 2c5a7865..9d706546 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -311,6 +311,11 @@ def get_new_passphrase(what, opts, passchg=False): def confirm_or_exit(message, question, expect="YES"): + if not confirm_or_false(message, question, expect): + msg("Exiting at user request") + sys.exit(2) + +def confirm_or_false(message, question, expect="YES"): vmsg("") @@ -322,11 +327,11 @@ def confirm_or_exit(message, question, expect="YES"): p = question+" "+conf_msg if question[0].isupper() else \ "Are you sure you want to %s?\n%s" % (question,conf_msg) - if my_raw_input(p).strip() != expect: - msg("Exiting at user request") - sys.exit(2) + ret = True if my_raw_input(p).strip() == expect else False vmsg("") + return ret + def write_to_stdout(data, what, confirm=True): @@ -342,7 +347,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): +def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True): if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile) @@ -351,7 +356,13 @@ def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose= except: pass else: if confirm_overwrite: - confirm_or_exit("","File '%s' already exists\nOverwrite?" % outfile) + q = "File '%s' already exists\nOverwrite?" % outfile + if exit_on_error: + confirm_or_exit("",q) + else: + if not confirm_or_false("",q): + msg("Not overwriting file at user request") + return False else: msg("Overwriting file '%s'" % outfile) @@ -364,11 +375,12 @@ def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose= f.close if verbose: msg("%s written to file '%s'" % (what.capitalize(),outfile)) + return True -def export_to_file(outfile, data, opts, what="data"): +def write_to_file_or_stdout(outfile, data, opts, what="data"): - if 'stdout' in opts: + if 'stdout' in opts or not sys.stdout.isatty(): write_to_stdout(data, what, confirm=True) else: confirm_overwrite = False if g.quiet else True @@ -615,9 +627,9 @@ def mark_passwd_file_as_used(opts): passwd_file_used = True -def get_mmgen_passphrase(pinfo,opts,passchg=False): +def get_mmgen_passphrase(prompt_info,opts,passchg=False): prompt = "Enter {}passphrase for {}: ".format( - "old " if passchg else "",pinfo) + "old " if passchg else "",prompt_info) if 'passwd_file' in opts: mark_passwd_file_as_used(opts) return " ".join(_get_words_from_file(opts['passwd_file'],"passphrase")) @@ -712,7 +724,7 @@ def my_raw_input(prompt,echo=True): return reply -def user_confirm(prompt,default_yes=False,verbose=False): +def keypress_confirm(prompt,default_yes=False,verbose=False): q = "(Y/n)" if default_yes else "(y/N)"