diff --git a/mmgen-addrgen b/mmgen-addrgen index 23ac14e0..ab4e0198 100755 --- a/mmgen-addrgen +++ b/mmgen-addrgen @@ -29,17 +29,19 @@ from mmgen.Opts import * from mmgen.license import * 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" help_data = { - 'prog_name': sys.argv[0].split("/")[-1], + '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), 'usage':"[opts] [infile]
", 'options': """ -h, --help Print this help message{} -d, --outdir= d Specify an alternate directory 'd' for output +-c, --save-checksum Save address list checksum to file -e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry{} -H, --show-hash-presets Show information on available hash presets -K, --no-keyconv Use internal libraries for address generation @@ -66,10 +68,10 @@ help_data = { """.format( *( ( -"\n-A, --no-addresses Print only secret keys, no addresses", -"\n-f, --flat-list Produce a flat list of keys suitable for use with" + -"\n 'mmgen-txsign'", -"\n-x, --b16 Print secret keys in hexadecimal too" +"\n-A, --no-addresses Print only secret keys, no addresses", +"\n-f, --flat-list Produce a flat list of keys suitable for use with" + +"\n '{}-txsign'".format(g.proj_name.lower()), +"\n-x, --b16 Print secret keys in hexadecimal too" ) if gen_what == "keys" else ("","","")), seed_lens=", ".join([str(i) for i in g.seed_lens]), @@ -148,29 +150,36 @@ if gen_what == "addresses": else: if not 'no_addresses' in opts: opts['print_secret'] = True -seed = get_seed_retry(infile,opts) -seed_id = make_chksum_8(seed) +seed = get_seed_retry(infile,opts) +seed_id = make_chksum_8(seed) -addr_data = generate_addrs(seed, addr_idxs, opts) -addr_data_chksum = make_chksum_8( - " ".join(["{} {}".format(a['num'],a['addr']) for a in addr_data]), - sep=True -) -addr_data_str = format_addr_data( +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_str = format_addr_data( addr_data, addr_data_chksum, seed_id, addr_idxs, opts) +outfile_base = "{}[{}]".format(seed_id, fmt_addr_idxs(addr_idxs)) + # Output data: if 'stdout' in opts: - if gen_what == "keys" and not g.quiet: - confirm = True - else: confirm = False + confirm = True if (gen_what == "keys" and not g.quiet) else False write_to_stdout(addr_data_str,gen_what,confirm) elif not sys.stdout.isatty(): write_to_stdout(addr_data_str,gen_what,confirm=False) else: - write_addr_data_to_file(seed, addr_data_str, addr_idxs, opts) + 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) -msg(""" -Checksum for address data {}[{}]: {} -Remember this checksum or save it to a secure location -""".format(seed_id, fmt_addr_idxs(addr_idxs), addr_data_chksum).strip()) +if not 'no_addresses' in opts: + 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,confirm,True) + else: + qmsg("Save this information to a secure location") diff --git a/mmgen-addrimport b/mmgen-addrimport index 10b0ac23..bebd9cf0 100755 --- a/mmgen-addrimport +++ b/mmgen-addrimport @@ -27,7 +27,7 @@ from mmgen.util import * from mmgen.tx import connect_to_bitcoind,parse_addrs_file help_data = { - 'prog_name': sys.argv[0].split("/")[-1], + 'prog_name': g.prog_name, 'desc': """Import addresses (both {pnm} and non-{pnm}) into a bitcoind watching wallet""".format(pnm=g.proj_name), 'usage':"[opts] [mmgen address file]", diff --git a/mmgen-pywallet b/mmgen-pywallet index fff14028..1169420e 100755 --- a/mmgen-pywallet +++ b/mmgen-pywallet @@ -43,9 +43,6 @@ mmgen-pywallet: Dump contents of a bitcoind wallet to file # Alex Martelli (http://www.aleax.it) # Ported from C code written by Laurent Haan (http://www.progressive-coding.com) -from mmgen.Opts import * -from mmgen.util import msg -import mmgen.config as g from bsddb.db import * import sys, time import json @@ -61,16 +58,18 @@ import hashlib import random import math +import mmgen.config as g +from mmgen.Opts import * +from mmgen.util import msg + max_version = 60000 addrtype = 0 json_db = {} private_keys = [] password = None -prog_name = sys.argv[0].split("/")[-1] - help_data = { - 'prog_name': prog_name, + 'prog_name': g.prog_name, 'desc': "Dump contents of a bitcoind wallet to file", 'usage': "[opts] ", 'options': """ diff --git a/mmgen-tool b/mmgen-tool index f902cdbe..e84a07e0 100755 --- a/mmgen-tool +++ b/mmgen-tool @@ -22,17 +22,17 @@ mmgen-tool: Perform various Bitcoin-related operations - part of the MMGen suite import sys, os from hashlib import sha256 -from mmgen.Opts import * import mmgen.config as g -from mmgen.util import pretty_hexdump import mmgen.tool as tool -prog_name = sys.argv[0].split("/")[-1] +from mmgen.Opts import * +from mmgen.util import pretty_hexdump help_data = { - 'prog_name': prog_name, + 'prog_name': g.prog_name, 'desc': "Perform various BTC-related operations", 'usage': "[opts] ", 'options': """ +-d, --outdir= d Specify an alternate directory 'd' for output -h, --help Print this help message -q, --quiet Produce quieter output -r, --usr-randchars=n Get 'n' characters of additional randomness from @@ -44,7 +44,7 @@ help_data = { COMMANDS:{} Type '{} --help for usage information on a particular command -""".format(tool.command_help,prog_name) +""".format(tool.command_help,g.prog_name) } opts,cmd_args = parse_opts(sys.argv,help_data) @@ -63,10 +63,10 @@ if command not in tool.commands.keys(): sys.exit(1) if cmd_args and cmd_args[0] == '--help': - tool.tool_usage(prog_name, command) + tool.tool_usage(g.prog_name, command) sys.exit(0) -args = tool.process_args(prog_name, command, cmd_args) +args = tool.process_args(g.prog_name, command, cmd_args) tool.opts = opts diff --git a/mmgen-txcreate b/mmgen-txcreate index e81bd0c7..a6590ad9 100755 --- a/mmgen-txcreate +++ b/mmgen-txcreate @@ -20,19 +20,16 @@ mmgen-txcreate: Create a BTC transaction, sending to specified addresses """ import sys -#from hashlib import sha256 - -from mmgen.Opts import * -from mmgen.license import * -import mmgen.config as g -from mmgen.tx import * -from mmgen.util import msg, msg_r, user_confirm from decimal import Decimal -prog_name = sys.argv[0].split("/")[-1] +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 help_data = { - 'prog_name': prog_name, + 'prog_name': g.prog_name, 'desc': "Create a BTC transaction with outputs to specified addresses", 'usage': "[opts] ... [change addr] [addr file] ...", 'options': """ @@ -69,6 +66,7 @@ if g.debug: show_opts_and_cmd_args(opts,cmd_args) c = connect_to_bitcoind() if not 'info' in opts: + do_license_msg(immed=True) tx_out,addr_data,b2m_map,acct_data,change_addr = {},[],{},[],"" @@ -132,9 +130,9 @@ if not 'info' in opts: if g.debug: show_opts_and_cmd_args(opts,cmd_args) -if not 'info' in opts: do_license_msg(immed=True) +#write_to_file("bogus_unspent.json", repr(us), opts); sys.exit() -#write_to_file("bogus_unspent.json", repr(us)); sys.exit() +#if False: if g.bogus_wallet_data: import mmgen.rpc us = eval(get_data_from_file(g.bogus_wallet_data)) @@ -143,7 +141,7 @@ else: if not us: msg(txmsg['no_spendable_outputs']); sys.exit(2) -unspent = sort_and_view(us) +unspent = sort_and_view(us,opts) total = trim_exponent(sum([i.amount for i in unspent])) @@ -209,6 +207,15 @@ if reply and reply in "YyVv": prompt = "Save transaction?" if user_confirm(prompt,default_yes=True): - write_tx_to_file(tx_hex,sel_unspent,send_amt or change,b2m_map,opts) + 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) + data = "{} {} {}\n{}\n{}\n{}\n".format( + tx_id, amt, make_timestamp(), + tx_hex, + repr([i.__dict__ for i in sel_unspent]), + repr(b2m_map) + ) + write_to_file(outfile,data,opts,"transaction",False,True) else: msg("Transaction not saved") diff --git a/mmgen-txsend b/mmgen-txsend index 75c0018e..89a6ab78 100755 --- a/mmgen-txsend +++ b/mmgen-txsend @@ -21,17 +21,15 @@ mmgen-txsend: Broadcast a Bitcoin transaction to the network import sys +import mmgen.config as g from mmgen.Opts import * from mmgen.license import * -import mmgen.config as g from mmgen.tx import * from mmgen.util import msg,check_infile,get_lines_from_file,confirm_or_exit -prog_name = sys.argv[0].split("/")[-1] - help_data = { - 'prog_name': prog_name, - 'desc': "Send a Bitcoin transaction signed by mmgen-txsign", + 'prog_name': g.prog_name, + 'desc': "Send a Bitcoin transaction signed by {}-txsign".format(g.proj_name.lower()), 'usage': "[opts] ", 'options': """ -h, --help Print this help message @@ -84,4 +82,5 @@ except: msg("Transaction sent: %s" % tx_id) -write_sent_tx_num_to_file(tx_id,metadata,opts) +of = "tx_{}[{}].out".format(*metadata[:2]) +write_to_file(of, tx_id+"\n",opts,"transaction ID",True,True) diff --git a/mmgen-txsign b/mmgen-txsign index 7bd71485..a11b7242 100755 --- a/mmgen-txsign +++ b/mmgen-txsign @@ -21,15 +21,15 @@ mmgen-txsign: Sign a Bitcoin transaction generated by mmgen-txcreate import sys +import mmgen.config as g from mmgen.Opts import * from mmgen.license import * -import mmgen.config as g from mmgen.tx import * from mmgen.util import msg,qmsg help_data = { - 'prog_name': sys.argv[0].split("/")[-1], - 'desc': "Sign Bitcoin transactions generated by mmgen-txcreate", + 'prog_name': g.prog_name, + 'desc': "Sign Bitcoin transactions generated by {}-txcreate".format(g.proj_name.lower()), 'usage': "[opts] .. [mmgen wallet/seed/words/brainwallet file] .. [addrfile] ..", 'options': """ -h, --help Print this help message @@ -63,7 +63,7 @@ help_data = { Transactions with either {pnm} or non-{pnm} input addresses may be signed. For non-{pnm} inputs, the bitcoind wallet.dat is used as the key source. For {pnm} inputs, key data is generated from your seed as with the -mmgen-addrgen and mmgen-keygen utilities. +{pnl}-addrgen and {pnl}-keygen utilities. Data for the --from- options will be taken from a file if a second file is specified on the command line. Otherwise, the user will be @@ -84,7 +84,7 @@ Seed data supplied in files must have the following extensions: seed: '.{g.seed_ext}' mnemonic: '.{g.mn_ext}' brainwallet: '.{g.brain_ext}' -""".format(g=g,pnm=g.proj_name) +""".format(g=g,pnm=g.proj_name,pnl=g.proj_name.lower()) } opts,infiles = parse_opts(sys.argv,help_data) @@ -184,7 +184,14 @@ for tx_file in tx_files: if sig_tx['complete']: prompt = "OK\nSave signed transaction?" if user_confirm(prompt,default_yes=True): - write_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,inputs_data,b2m_map,opts) + 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) + ) + write_to_file(outfile,data,opts,"signed transaction",True,True) else: msg("failed\nSome keys were missing. Transaction could not be signed.") sys.exit(3) diff --git a/mmgen-walletchk b/mmgen-walletchk index ac938d00..16b2ea31 100755 --- a/mmgen-walletchk +++ b/mmgen-walletchk @@ -21,12 +21,12 @@ mmgen-walletchk: Check integrity of a mmgen deterministic wallet, display """ import sys +import mmgen.config as g from mmgen.Opts import * - from mmgen.util import * help_data = { - 'prog_name': sys.argv[0].split("/")[-1], + 'prog_name': g.prog_name, 'desc': """Check integrity of an {} deterministic wallet, display its information, and export seed and mnemonic data. """.format(g.proj_name), @@ -89,7 +89,7 @@ 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, "incognito wallet data", opts) + export_to_file(fn, data, opts, "incognito wallet data") sys.exit() @@ -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", "mnemonic data", opts) + export_to_file(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), "seed data", opts) + export_to_file(fn, "%s %s\n" % (chk,data), opts, "seed data") diff --git a/mmgen-walletgen b/mmgen-walletgen index 91d0a15f..9dfce80b 100755 --- a/mmgen-walletgen +++ b/mmgen-walletgen @@ -22,15 +22,14 @@ mmgen-walletgen: Generate a mmgen deterministic wallet import sys, os from hashlib import sha256 +import mmgen.config as g from mmgen.Opts import * from mmgen.license import * -import mmgen.config as g from mmgen.walletgen import * from mmgen.util import * -prog_name = sys.argv[0].split("/")[-1] help_data = { - 'prog_name': prog_name, + 'prog_name': g.prog_name, 'desc': "Generate an {} deterministic wallet".format(g.proj_name), 'usage': "[opts] [infile]", 'options': """ @@ -61,7 +60,7 @@ help_data = { 'notes': """ By default (i.e. when invoked without any of the '--from-' options), -{prog_name} generates a wallet based on a random seed. +{g.prog_name} generates a wallet based on a random seed. Data for the --from- options will be taken from if is specified. Otherwise, the user will be prompted to enter the data. @@ -91,7 +90,7 @@ the '--seed-len' option. 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(prog_name=prog_name) +""".format(g=g) } opts,cmd_args = parse_opts(sys.argv,help_data) diff --git a/mmgen/addr.py b/mmgen/addr.py index e9298507..9543fccc 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -133,43 +133,27 @@ def format_addr_data(addr_data, addr_data_chksum, seed_id, addr_idxs, opts): start = addr_data[0]['num'] end = addr_data[-1]['num'] - - wif_msg = "" - if ('b16' in opts and 'print_secret' in opts) \ - or 'no_addresses' in opts: - wif_msg = " (wif)" - - fa = "%s%%-%ss %%-%ss %%s" % ( - " "*2, len(str(end)) + (0 if 'no_addresses' in opts else 1), - (5 if 'print_secret' in opts else 1) + len(wif_msg) - ) - + fs = " %-{}s %s".format(len(str(end))) out = [] - if not 'stdout' in opts: out.append(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") + + if not 'no_addresses' in opts: + if not 'stdout' in opts: out.append(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") + out.append("%s {" % seed_id.upper()) for d in addr_data: - col1 = d['num'] if 'no_addresses' in opts: - if 'b16' in opts: - out.append(fa % (col1, " (hex):", d['sec'])) - col1 = "" - out.append(fa % (col1, " (wif):", d['wif'])) - if 'b16' in opts: out.append("") - elif 'print_secret' in opts: - if 'b16' in opts: - out.append(fa % (col1, "sec (hex):", d['sec'])) - col1 = "" - out.append(fa % (col1, "sec"+wif_msg+":", d['wif'])) - out.append(fa % ("", "addr:", d['addr'])) - out.append("") + out.append(fs % (d['num'], "wif: " + d['wif'])) else: - out.append(fa % (col1, "", d['addr'])) + 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'])) - if not out[-1]: out.pop() out.append("}") return "\n".join(out) + "\n" @@ -191,26 +175,3 @@ def fmt_addr_idxs(addr_idxs): prev = i return "".join([str(i) for i in ret]) - - -def write_addr_data_to_file(seed, addr_data_str, addr_idxs, opts): - - if 'print_addresses_only' in opts: ext = g.addrfile_ext - elif 'no_addresses' in opts: ext = g.keyfile_ext - elif 'flat_list' in opts: ext = g.keylist_ext - else: ext = "akeys" - - if 'b16' in opts: ext = ext.replace("keys","xkeys") - from mmgen.util import write_to_file, make_chksum_8 - outfile = "{}[{}].{}".format( - make_chksum_8(seed), - fmt_addr_idxs(addr_idxs), - ext - ) - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - - write_to_file(outfile,addr_data_str) - - dtype = "Address" if 'print_addresses_only' in opts else "Key" - msg("%s data saved to file '%s'" % (dtype,outfile)) diff --git a/mmgen/config.py b/mmgen/config.py index 5b87a31d..b6587d5a 100755 --- a/mmgen/config.py +++ b/mmgen/config.py @@ -19,6 +19,9 @@ config.py: Constants and configuration options for the mmgen suite """ +import sys, os +prog_name = os.path.basename(sys.argv[0]) + author = "Philemon" email = "" Cdates = '2013-2014' diff --git a/mmgen/tool.py b/mmgen/tool.py index 2e2d7bb6..181edfcd 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -420,7 +420,8 @@ 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,True,True) + else: write_to_file((outfile or infile+"."+g.mmenc_ext), + salt+iv+enc_d,opts,"encrypted data",True,True) def decrypt(infile,outfile="",hash_preset=''): d = get_data_from_file(infile,"encrypted data") @@ -439,6 +440,6 @@ def decrypt(infile,outfile="",hash_preset=''): 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, True,True) + write_to_file((outfile or of),out,opts,"decrypted data",True,True) else: msg("Incorrect passphrase or hash preset") diff --git a/mmgen/tx.py b/mmgen/tx.py index 2c6dd8d9..9bb09b7f 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -19,11 +19,12 @@ tx.py: Bitcoin transaction routines """ -from binascii import unhexlify -from mmgen.util import * import sys, os +from binascii import unhexlify from decimal import Decimal + import mmgen.config as g +from mmgen.util import * txmsg = { 'not_enough_btc': "Not enough BTC in the inputs for this transaction (%s BTC)", @@ -35,9 +36,9 @@ 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 mmgen-txsign. +file using the '-k' option to {}-txsign. -Selected mmgen inputs: %s""", +Selected mmgen inputs: %s""".format(g.proj_name.lower()), 'too_many_acct_addresses': """ ERROR: More than one address found for account: "%s". The tracking "wallet.dat" file appears to have been altered by a non-{g.proj_name} @@ -60,8 +61,8 @@ tracking wallet, or supply an address file for it on the command line. """.strip(), 'no_spendable_outputs': """ No spendable outputs found! Import addresses with balances into your -watch-only wallet using 'mmgen-addrimport' and then re-run this program. -""".strip(), +watch-only wallet using '{}-addrimport' and then re-run this program. +""".strip().format(g.proj_name.lower()), 'mapping_error': """ MMGen -> BTC address mappings differ! In transaction: %s @@ -82,12 +83,6 @@ The {pnm}-to-BTC mappings for these addresses cannot be verified! """.strip().format(pnm=g.proj_name), } -# Deleted text: -# Alternatively, you may import the mmgen keys into the wallet.dat of your -# offline bitcoind, first generating the required keys with mmgen-keygen and -# then running mmgen-txsign with the '-f' option to force the use of -# wallet.dat as the key source. - def connect_to_bitcoind(): @@ -168,44 +163,6 @@ def get_bitcoind_cfg_options(cfg_keys): return cfg -def write_tx_to_file(tx,sel_unspent,send_amt,b2m_map,opts): - tx_id = make_chksum_6(unhexlify(tx)).upper() - outfile = "tx_%s[%s].%s" % (tx_id,send_amt,g.rawtx_ext) - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - data = "{} {} {}\n{}\n{}\n{}\n".format( - tx_id, send_amt, make_timestamp(), - tx, - repr([i.__dict__ for i in sel_unspent]), - repr(b2m_map) - ) - write_to_file(outfile,data,confirm=False) - msg("Transaction data saved to file '%s'" % outfile) - - -def write_signed_tx_to_file(tx,sig_tx,metadata,inputs_data,b2m_map,opts): - tx_id = make_chksum_6(unhexlify(tx)).upper() - outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext) - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - data = "{}\n{}\n{}\n{}\n".format( - " ".join(metadata[:2] + [make_timestamp()]), - sig_tx, - repr(inputs_data), - repr(b2m_map) - ) - write_to_file(outfile,data,confirm=False) - msg("Signed transaction saved to file '%s'" % outfile) - - -def write_sent_tx_num_to_file(tx,metadata,opts): - outfile = "tx_{}[{}].out".format(*metadata[:2]) - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - write_to_file(outfile,tx+"\n",confirm=False) - msg("Transaction ID saved to file '%s'" % outfile) - - def format_unspent_outputs_for_printing(out,sort_info,total): pfs = " %-4s %-67s %-34s %-12s %-13s %-8s %-10s %s" @@ -227,7 +184,7 @@ def format_unspent_outputs_for_printing(out,sort_info,total): ) -def sort_and_view(unspent): +def sort_and_view(unspent,opts): def s_amt(i): return i.amount def s_txid(i): return "%s %03s" % (i.txid,i.vout) @@ -242,9 +199,9 @@ def sort_and_view(unspent): unspent.sort(key=s_age,reverse=reverse) # Reverse age sort by default total = trim_exponent(sum([i.amount for i in unspent])) + max_acct_len = max([len(i.account) for i in unspent]) hdr_fmt = "UNSPENT OUTPUTS (sort order: %s) Total BTC: %s" - options_msg = """ Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen @@ -253,31 +210,31 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen "('q' = quit sorting, 'p' = print to file, 'v' = pager view, 'w' = wide view): " from copy import deepcopy - write_to_file_msg = "" - msg("") - from mmgen.term import get_terminal_size - max_acct_len = max([len(i.account) for i in unspent]) + write_to_file_msg = "" + msg("") while True: cols = get_terminal_size()[0] if cols < g.min_screen_width: - msg("mmgen-txcreate requires a screen at least %s characters wide" % - g.min_screen_width) + msg("%s-txcreate requires a screen at least %s characters wide" % + (g.proj_name.lower(),g.min_screen_width)) sys.exit(2) addr_w = min(34+((1+max_acct_len) if show_mmaddr else 0),cols-46) + acct_w = min(max_acct_len, max(24,int(addr_w-10))) + btaddr_w = addr_w - acct_w - 1 tx_w = max(11,min(64, cols-addr_w-32)) + txdots = "..." if tx_w < 64 else "" fs = " %-4s %-" + str(tx_w) + "s %-2s %-" + str(addr_w) + "s %-13s %-s" - a = "Age(d)" if show_days else "Conf." - table_hdr = fs % ("Num","TX id Vout","","Address", "Amount (BTC)",a) + table_hdr = fs % ("Num","TX id Vout","","Address","Amount (BTC)", + "Age(d)" if show_days else "Conf.") unsp = deepcopy(unspent) for i in unsp: i.skip = "" if group and (sort == "address" or sort == "txid"): - for n in range(len(unsp)-1): - a,b = unsp[n],unsp[n+1] + for a,b in [(unsp[i],unsp[i+1]) for i in range(len(unsp)-1)]: if sort == "address" and a.address == b.address: b.skip = "addr" elif sort == "txid" and a.txid == b.txid: b.skip = "txid" @@ -286,18 +243,14 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen lfill = 3 - len(amt.split(".")[0]) if "." in amt else 3 - len(amt) i.amt = " "*lfill + amt i.days = int(i.confirmations * g.mins_per_block / (60*24)) - + i.age = i.days if show_days else i.confirmations i.mmid,i.label = parse_mmgen_label(i.account) if i.skip == "addr": i.addr = "|" + "." * 33 else: - if show_mmaddr and i.mmid: - acct_w = min(max_acct_len, max(24,int(addr_w-10))) - btaddr_w = addr_w - acct_w - 1 - + if show_mmaddr: dots = ".." if btaddr_w < len(i.address) else "" - i.addr = "%s%s %s" % ( i.address[:btaddr_w-len(dots)], dots, @@ -305,20 +258,17 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen else: i.addr = i.address - dots = "..." if tx_w < 64 else "" i.tx = " " * (tx_w-4) + "|..." if i.skip == "txid" \ - else i.txid[:tx_w-len(dots)]+dots + else i.txid[:tx_w-len(txdots)]+txdots sort_info = ["reverse"] if reverse else [] sort_info.append(sort if sort else "unsorted") if group and (sort == "address" or sort == "txid"): sort_info.append("grouped") - out = [hdr_fmt % (" ".join(sort_info), total), table_hdr] - - for n,i in enumerate(unsp): - d = i.days if show_days else i.confirmations - out.append(fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,d)) + out = [hdr_fmt % (" ".join(sort_info), total), table_hdr] + out += [fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,i.age) + for n,i in enumerate(unsp)] msg("\n".join(out) +"\n\n" + write_to_file_msg + options_msg) write_to_file_msg = "" @@ -344,10 +294,10 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen elif reply == 'e': pass elif reply == 'q': pass elif reply == 'p': - data = format_unspent_outputs_for_printing(unsp,sort_info,total) - outfile = "listunspent[%s].out" % ",".join(sort_info) - write_to_file(outfile, data) - write_to_file_msg = "Data written to '%s'\n\n" % outfile + d = format_unspent_outputs_for_printing(unsp,sort_info,total) + of = "listunspent[%s].out" % ",".join(sort_info) + write_to_file(of, d, opts,"",False,False) + write_to_file_msg = "Data written to '%s'\n\n" % of elif reply == 'v': do_pager("\n".join(out)) continue @@ -572,16 +522,18 @@ Only ASCII printable characters are permitted. """.strip() % (ch,label)) sys.exit(3) +def make_addr_data_chksum(addr_data): + nchars = 24 + return make_chksum_N( + " ".join(["{} {}".format(*d[:2]) for d in addr_data]), nchars, sep=True + ) def check_addr_data_hash(seed_id,addr_data): def s_addrdata(a): return int(a[0]) - addr_data.sort(key=s_addrdata) - addr_data_chksum = make_chksum_8( - " ".join(["{} {}".format(*d[:2]) for d in addr_data]), sep=True - ) + addr_data_chksum = make_addr_data_chksum(sorted(addr_data,key=s_addrdata)) from mmgen.addr import fmt_addr_idxs fl = fmt_addr_idxs([int(a[0]) for a in addr_data]) - msg("Computed address data checksum for '{}[{}]': {}".format( + msg("Computed checksum for addr data {}[{}]: {}".format( seed_id,fl,addr_data_chksum)) msg("Check this value against your records") diff --git a/mmgen/util.py b/mmgen/util.py index 31446d37..303d97c0 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -24,7 +24,7 @@ from hashlib import sha256 from binascii import hexlify,unhexlify import mmgen.config as g -from mmgen.bitcoin import b58decode_pad +from mmgen.bitcoin import b58decode_pad,b58encode_pad from mmgen.term import * def msg(s): sys.stderr.write(s + "\n") @@ -48,7 +48,6 @@ def get_extension(f): import os return os.path.splitext(f)[1][1:] - def get_random_data_from_user(uchars): if g.quiet: msg("Enter %s random symbols" % uchars) @@ -241,6 +240,12 @@ def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False): else: msg_r("\r") +def make_chksum_N(s,n,sep=False): + if n%4 or not (4 <= n <= 64): return False + s = sha256(sha256(s).digest()).hexdigest().upper() + sep = " " if sep else "" + return sep.join([s[i*4:i*4+4] for i in range(n/4)]) + def make_chksum_8(s,sep=False): s = sha256(sha256(s).digest()).hexdigest()[:8].upper() return "{} {}".format(s[:4],s[4:]) if sep else s @@ -427,9 +432,9 @@ def open_file_or_exit(filename,mode): return f -def write_to_file(outfile,data,confirm=False,verbose=False): +def write_to_file(outfile,data,opts,what="data",confirm=False,verbose=False): - if verbose: qmsg("Writing data to file '%s'" % outfile) + if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) if confirm: from os import stat @@ -441,27 +446,26 @@ def write_to_file(outfile,data,confirm=False,verbose=False): confirm_or_exit("","File '%s' already exists\nOverwrite?" % outfile) f = open_file_or_exit(outfile,'w') - try: f.write(data) except: - msg("Failed to write to file '%s'" % outfile) + msg("Failed to write %s to file '%s'" % (what,outfile)) sys.exit(2) - f.close + if verbose: msg("%s written to file '%s'" % (what.capitalize(),outfile)) -def export_to_file(outfile, data, label, opts): + + +def export_to_file(outfile, data, opts, what="data"): if 'stdout' in opts: - write_to_stdout(data, label, confirm=True) + write_to_stdout(data, what, confirm=True) elif not sys.stdout.isatty(): - write_to_stdout(data, label, confirm=False) + write_to_stdout(data, what, confirm=False) else: - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - write_to_file(outfile, data, confirm=False if g.quiet else True) - msg("%s saved to file '%s'" % (label.capitalize(), outfile)) + c = False if g.quiet else True + write_to_file(outfile,data,opts,what,c,True) def _display_control_data(label,metadata,hash_preset,salt,enc_seed): @@ -522,23 +526,13 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts): seed_id = make_chksum_8(seed) seed_len = str(len(seed)*8) pw_status = "NE" if len(passwd) else "E" - hash_preset = opts['hash_preset'] - - outfile="{}-{}[{},{}].{}".format(seed_id,key_id,seed_len,hash_preset,g.wallet_ext) - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - label = opts['label'] if 'label' in opts else "No Label" - - from mmgen.bitcoin import b58encode_pad - + metadata = seed_id.lower(),key_id.lower(),seed_len,\ + pw_status,make_timestamp() sf = b58encode_pad(salt) esf = b58encode_pad(enc_seed) - metadata = seed_id.lower(),key_id.lower(),\ - seed_len,pw_status,make_timestamp() - lines = ( label, "{} {} {} {} {}".format(*metadata), @@ -548,23 +542,17 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts): ) chk = make_chksum_6(" ".join(lines)) + outfile="{}-{}[{},{}].{}".format( + seed_id,key_id,seed_len,hash_preset,g.wallet_ext) - confirm = False if g.quiet else True - write_to_file(outfile, "\n".join((chk,)+lines)+"\n", confirm) + c = False if g.quiet else True + d = "\n".join((chk,)+lines)+"\n" + write_to_file(outfile,d,opts,"wallet",c,True) - msg("Wallet saved to file '%s'" % outfile) if g.verbose: _display_control_data(label,metadata,hash_preset,salt,enc_seed) -def write_walletdat_dump_to_file(wallet_id,data,num_keys,ext,what,opts): - outfile = "wd_{}[{}].{}".format(wallet_id,num_keys,ext) - if 'outdir' in opts: - outfile = "%s/%s" % (opts['outdir'], outfile) - write_to_file(outfile,data,confirm=False) - msg("wallet.dat %s saved to file '%s'" % (what,outfile)) - - def _compare_checksums(chksum1, desc1, chksum2, desc2): if chksum1.lower() == chksum2.lower(): @@ -637,7 +625,7 @@ def get_data_from_wallet(infile,silent=False): # Don't make this a qmsg: User will be prompted for passphrase and must see # the filename. - if not silent: + if not silent and not g.quiet: msg("Getting {} wallet data from file '{}'".format(g.proj_name,infile)) f = open_file_or_exit(infile, 'r') @@ -864,7 +852,7 @@ def get_seed_from_incog_wallet( passwd = get_mmgen_passphrase(prompt,opts) - msg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets))) + qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets))) while True: p = "Enter hash preset for %s wallet (default='%s'): " hp = my_raw_input(p % (g.proj_name, g.hash_preset))