diff --git a/mmgen-addrgen b/mmgen-addrgen index e88eb62a..3cb6c725 100755 --- a/mmgen-addrgen +++ b/mmgen-addrgen @@ -44,20 +44,22 @@ else: extra_help_data = ("","","") help_data = { 'prog_name': sys.argv[0].split("/")[-1], 'desc': """Generate a list or range of {} from a {} wallet, - mnemonic, seed or password""".format(gen_what,proj_name), + mnemonic, seed or password""".format(gen_what,proj_name), 'usage':"[opts] [infile]
", 'options': """ -h, --help Print this help message{} -d, --outdir d Specify an alternate directory 'd' for output --e, --echo-passphrase Display passphrase or mnemonic on screen upon entry +-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 instead of 'keyconv' -l, --seed-len N Length of seed. Options: {} (default: {}) -p, --hash-preset p Use scrypt.hash() parameters from preset 'p' when hashing password (default: '{}') --P, --show-hash-presets Show information on available hash presets --q, --quiet Suppress warnings; overwrite files without asking +-P, --passwd-file f Get passphrase from file 'f' +-q, --quiet Suppress warnings; overwrite files without + prompting -S, --stdout Print {W} to stdout -v, --verbose Produce more verbose output{} @@ -90,9 +92,9 @@ the 'p' parameter of the '--from-brain' option The '--from-brain' option also requires the user to specify a seed length (the 'l' parameter) -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 +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( extra_help_data[0], ", ".join([str(i) for i in seed_lens]), @@ -105,16 +107,16 @@ invocations with that passphrase ) } -so = "h","A","d:","e","K","l:","p:","P","q","S","v","x","b:","m","s" -lo = "help","no_addresses","outdir=","echo_passphrase","no_keyconv",\ - "seed_len=","hash_preset=","show_hash_presets","quiet","stdout",\ - "verbose","b16","from_brain=","from_mnemonic","from_seed" +short_opts = ["h","A","d:","e","H","K","l:","p:","P:","q","S", + "v","x","b:","m","s"] +long_opts = ["help","no_addresses","outdir=","echo_passphrase", + "show_hash_presets","no_keyconv","seed_len=","hash_preset=", + "passwd_file=","quiet","stdout","verbose","b16","from_brain=", + "from_mnemonic","from_seed"] if invoked_as == "addrgen": - short_opts = so[0:1] + so[2:10] + so[11:] - long_opts = lo[0:1] + lo[2:10] + lo[11:] -else: - short_opts,long_opts = so,lo + for i in "A","x": short_opts.remove(i) + for i in "no_addresses","b16": long_opts.remove(i) opts,cmd_args = process_opts(sys.argv,help_data,"".join(short_opts),long_opts) @@ -132,10 +134,6 @@ set_if_unset_and_typeconvert(opts,( # Exits on invalid input check_opts(opts,('hash_preset','seed_len','outdir','from_brain')) -if debug: - print "Processed options: %s" % repr(opts) - print "Cmd args: %s" % repr(cmd_args) - if len(cmd_args) == 1 and ( 'from_mnemonic' in opts or 'from_brain' in opts or diff --git a/mmgen-addrimport b/mmgen-addrimport index 05364dec..02251abe 100755 --- a/mmgen-addrimport +++ b/mmgen-addrimport @@ -33,9 +33,9 @@ help_data = { watching wallet""", 'usage':"[opts] [mmgen address file]", 'options': """ --h, --help Print this help message --l, --addrlist f Import the non-mmgen Bitcoin addresses listed in file 'f' --q, --quiet Suppress warnings +-h, --help Print this help message +-l, --addrlist f Import the non-mmgen Bitcoin addresses listed in file 'f' +-q, --quiet Suppress warnings """ } diff --git a/mmgen-passchg b/mmgen-passchg index 9bc37adb..57391fc0 100755 --- a/mmgen-passchg +++ b/mmgen-passchg @@ -33,12 +33,13 @@ help_data = { 'options': """ -h, --help Print this help message -d, --outdir d Specify an alternate directory 'd' for output +-H, --show-hash-presets Show information on available hash presets -k, --keep-old-passphrase Keep old passphrase (use when changing hash strength or label only) -L, --label l Change the wallet's label to 'l' -p, --hash-preset p Change scrypt.hash() parameters to preset 'p' (default: '{}') --P, --show-hash-presets Show information on available hash presets +-P, --passwd-file f Get new passphrase from file 'f' -v, --verbose Produce more verbose output NOTE: The key ID will change if either the passphrase or hash preset @@ -46,9 +47,9 @@ NOTE: The key ID will change if either the passphrase or hash preset """.format(hash_preset) } -short_opts = "hd:kL:p:Pv" -long_opts = "help","outdir=","keep_old_passphrase","label=","hash_preset=",\ - "show_hash_presets","verbose" +short_opts = "hd:HkL:p:P:v" +long_opts = "help","outdir=","show_hash_presets","keep_old_passphrase",\ + "label=","hash_preset=","passwd_file=","verbose" opts,cmd_args = Opts.process_opts(sys.argv,help_data,short_opts,long_opts) @@ -65,11 +66,12 @@ infile = cmd_args[0] # Old key: label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile,opts) seed_id,key_id = metadata[:2] -oldp = "" if 'keep_old_passphrase' in opts else "old " # Repeat on incorrect pw entry +prompt = "Enter %spassphrase: " % ("" + if 'keep_old_passphrase' in opts else "old ") while True: - passwd = " ".join(get_words("","",("Enter %spassphrase: " % oldp),opts)) + passwd = get_mmgen_passphrase(prompt,{}) key = make_key(passwd, salt, hash_preset) seed = decrypt_seed(enc_seed, key, seed_id, key_id) if seed: break @@ -97,8 +99,7 @@ else: if 'keep_old_passphrase' in opts: msg("Keeping old passphrase by user request") else: - new_passwd = get_first_passphrase_from_user( - "new passphrase", {'quiet': True }) + new_passwd = get_new_passphrase("new passphrase", opts) if new_passwd == passwd: msg("Passphrase is unchanged") diff --git a/mmgen-pywallet b/mmgen-pywallet index 27890e4b..1ac4b8ab 100755 --- a/mmgen-pywallet +++ b/mmgen-pywallet @@ -46,7 +46,7 @@ mmgen-pywallet: Dump contents of a bitcoind wallet to file from mmgen.Opts import * from mmgen.utils import msg from bsddb.db import * -import os, sys, time +import sys, time import json import logging import struct @@ -75,17 +75,20 @@ help_data = { 'options': """ -h, --help Print this help message -d, --outdir d Specify an alternate directory 'd' for output +-e, --echo-passphrase Display passphrase on screen upon entry -j, --json Dump wallet in json format -k, --keys Dump all private keys (flat list) -a, --addrs Dump all addresses (flat list) -K, --keysforaddrs f Dump private keys for addresses listed in file 'f' --q, --quiet Suppress warnings; overwrite files without asking +-P, --passwd-file f Get passphrase from file 'f' +-q, --quiet Suppress warnings; overwrite files without prompting -S, --stdout Dump to stdout rather than file """ } -short_opts = "hd:jkaK:qS" -long_opts = "help","outdir=","json","keys","addrs","keysforaddrs=","quiet","stdout" +short_opts = "hd:ejkaK:P:qS" +long_opts = "help","outdir=","echo_passphrase","json","keys","addrs",\ + "keysforaddrs=","passwd_file=","quiet","stdout" opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts) @@ -1572,8 +1575,8 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction if password == None and \ ('json' in opts or 'keysforaddrs' in opts or 'keys' in opts): - from mmgen.utils import my_getpass - password = my_getpass("Enter password: ") + from mmgen.utils import get_bitcoind_passphrase + password = get_bitcoind_passphrase("Enter password: ",opts) if password != None: global crypter @@ -1646,21 +1649,14 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction del(json_db['names']) -def parse_wallet_file_arg(path): - - s = path.rfind("/") - - if path[0] == '/': - if s == 0: return "/", path - else: return path[0:s], path[s+1:] - else: - curdir = os.path.abspath(".") - if s == -1: return curdir,path - else: return curdir + "/" + path[0:s], path[s+1:] +# Non-portable. For Windows, works only if supplied filename is in current dir # main() -db_dir,db_file = parse_wallet_file_arg(cmd_args[0]) +import os.path +infile = os.path.abspath(cmd_args[0]) +db_dir,db_file = os.path.dirname(infile),os.path.basename(infile) + # print "[%s] [%s]" % (db_dir,db_file) db_env = create_env(db_dir) @@ -1673,18 +1669,16 @@ if json_db.get('minversion') > max_version: wallet_addrs = [i['addr'] for i in json_db['keys']] -data,ext,what = [],"","" - if 'json' in opts: - data = [ json.dumps(json_db, sort_keys=True, indent=4) ] + data = [json.dumps(json_db, sort_keys=True, indent=4)] ext,what = "json","json dump" elif 'keys' in opts: - for i in json_db['keys']: data.append(i['sec']) + data = sorted([i['sec'] for i in json_db['keys']]) ext,what = "keys","private keys" elif 'addrs' in opts: - for i in json_db['keys']: data.append(i['addr']) + data = sorted([i['addr'] for i in json_db['keys']]) ext,what = "addrs","addresses" elif 'keysforaddrs' in opts: @@ -1696,7 +1690,7 @@ elif 'keysforaddrs' in opts: data.append(json_db['keys'][idx]['sec']) except: msg("WARNING: Address '%s' not found" % addr) - ext,what = "keys","private keys" + data,ext,what = sorted(data),"keys","private keys" len_arg = "%s" % len(wallet_addrs) \ if len(data) == len(wallet_addrs) or ext == "json" \ diff --git a/mmgen-txcreate b/mmgen-txcreate index 01af238e..60c445e3 100755 --- a/mmgen-txcreate +++ b/mmgen-txcreate @@ -26,7 +26,7 @@ from mmgen.Opts import * from mmgen.license import * from mmgen.config import * from mmgen.tx import * -from mmgen.utils import check_opts, msg, user_confirm +from mmgen.utils import check_opts, msg, msg_r, user_confirm from decimal import Decimal prog_name = sys.argv[0].split("/")[-1] @@ -40,12 +40,13 @@ help_data = { -d, --outdir d Specify an alternate directory 'd' for output -e, --echo-passphrase Print passphrase to screen when typing it -i, --info Display unspent outputs and exit --q, --quiet Suppress warnings; overwrite files without asking +-q, --quiet Suppress warnings; overwrite files without + prompting Outputs to spend are chosen by the user via a menu. -Ages of transactions are approximate based on an estimated block discovery -time of %s minutes. +Ages of transactions are approximate based on an average block discovery +interval of %s minutes. """ % mins_per_block } @@ -88,21 +89,29 @@ if not 'quiet' in opts and not 'info' in opts: do_license_msg() # End test us = c.listunspent() + +if not us: + msg_r(""" +No spendable outputs found! Import addresses with balances into your +watch-only wallet using 'mmgen-addrimport' and then re-run this program. +""") + sys.exit(2) + # write_to_file("listunspent.json",repr(us)) # sys.exit() unspent = sort_and_view(us) total = trim_exponent(sum([i.amount for i in unspent])) -msg("Total unspent: %s BTC" % total) +msg("Total unspent: %s BTC (%s outputs)" % (total, len(unspent))) if 'info' in opts: sys.exit(0) send_amt = sum(tx_out.values()) -msg("Total amount to spend: %s BTC\n%s unspent outputs total" % - (send_amt, len(unspent))) +msg("Total amount to spend: %s BTC" % send_amt) while True: - sel_nums = select_outputs(unspent,"Choose the outputs to spend: ") + sel_nums = select_outputs(unspent, + "Enter a range or space-separated list of outputs to spend: ") msg("Selected outputs: %s" % " ".join(str(i) for i in sel_nums)) sel_unspent = [unspent[i-1] for i in sel_nums] diff --git a/mmgen-txsend b/mmgen-txsend index 3407f944..f3ef0ed7 100755 --- a/mmgen-txsend +++ b/mmgen-txsend @@ -36,7 +36,7 @@ help_data = { 'options': """ -h, --help Print this help message -d, --outdir d Specify an alternate directory 'd' for output --q, --quiet Suppress warnings; overwrite files without asking +-q, --quiet Suppress warnings; overwrite files without prompting """ } diff --git a/mmgen-txsign b/mmgen-txsign index eda1bfe6..3ce132b3 100755 --- a/mmgen-txsign +++ b/mmgen-txsign @@ -36,16 +36,19 @@ 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 --f, --force-wallet-dat Force the use of wallet.dat as a key source -i, --info Display information about the transaction and exit +-I, --tx_id Display transaction ID and exit -k, --keys-from-file k Provide additional key data from file 'k' --q, --quiet Suppress warnings; overwrite files without asking +-P, --passwd-file f Get passphrase from file 'f' +-q, --quiet Suppress warnings; overwrite files without + prompting -b, --from-brain l,p Generate keys from a user-created password, i.e. a "brainwallet", using seed length 'l' and hash preset 'p' (comma-separated) -m, --from-mnemonic Generate keys from an electrum-like mnemonic -s, --from-seed Generate keys from a seed in .{} format +-w, --use-wallet-dat Use the keys in the bitcoind wallet.dat file too Transactions with either mmgen or non-mmgen input addresses may be signed. For non-mmgen inputs, the bitcoind wallet.dat is used as the key source. @@ -70,9 +73,10 @@ Seed data supplied in files must have the following extensions: """.format(seed_ext,wallet_ext,seed_ext,mn_ext,brain_ext) } -short_opts = "hd:efik:qb:ms" -long_opts = "help","outdir=","echo_passphrase","force_wallet_dat","info",\ - "keys_from_file=","quiet","from_brain=","from_mnemonic","from_seed" +short_opts = "hd:eiIk:P:qb:msw" +long_opts = "help","outdir=","echo_passphrase","info","tx_id",\ + "keys_from_file=","passwd_file=","quiet","from_brain=",\ + "from_mnemonic","from_seed","use_wallet_dat" opts,infiles = process_opts(sys.argv,help_data,short_opts,long_opts) @@ -83,62 +87,18 @@ if 'keys_from_file' in opts: check_infile(opts['keys_from_file']) if not infiles: usage(help_data) for i in infiles: check_infile(i) -# Begin execution -c = connect_to_bitcoind() +def get_keys_for_mmgen_addrs(mmgen_addrs,infiles): -tx_file = infiles.pop(0) -tx_data = get_lines_from_file(tx_file,"transaction data") - -metadata,tx_hex,sig_data,inputs_data = parse_tx_data(tx_data,tx_file) - -if 'info' in opts: - view_tx_data(c,inputs_data,tx_hex,metadata) - sys.exit(0) - -if not 'quiet' in opts: do_license_msg() - -msg("Successfully opened transaction file '%s'" % tx_file) - -if user_confirm("View transaction data? ",default_yes=False): - view_tx_data(c,inputs_data,tx_hex,metadata) - -# Are inputs mmgen addresses? -mmgen_addrs,other_addrs,keys = [],[],[] - -for i in inputs_data: - if verify_mmgen_label(i['account']): - mmgen_addrs.append(i) - else: - other_addrs.append(i) - -if mmgen_addrs and not 'force_wallet_dat' in opts: - # Check that all the seed IDs are the same: seed_ids = list(set([i['account'][:8] for i in mmgen_addrs])) - ext_data = ( - (wallet_ext, {}), - (mn_ext, {"from_mnemonic":True}), - (seed_ext, {"from_seed": True}), - (brain_ext, opts) - ) + seed_ids_save = seed_ids[0:] + keys = [] + while seed_ids: infile = False if infiles: infile = infiles.pop() - ext = infile.split(".")[-1] - for e,o in ext_data: - if e == ext: - if e == brain_ext: - if "from_brain" not in opts: - msg( - "'--from-brain' option must be specified for brainwallet file") - sys.exit(2) - seed = get_seed_retry(infile,o); break - else: - msg("Invalid file extension: '.%s'\nValid extensions: '.%s'" % - (ext,"' '.".join([i[0] for i in ext_data]))) - sys.exit(2) - + seed = get_seed(infile,opts) elif "from_brain" in opts or "from_mnemonic" in opts or "from_seed" in opts: msg("Need data for seed ID %s" % seed_ids[0]) seed = get_seed_retry("",opts) @@ -158,65 +118,123 @@ if mmgen_addrs and not 'force_wallet_dat' in opts: from mmgen.addr import generate_keys keys += [i['wif'] for i in generate_keys(seed, seed_id_addrs)] else: - msg("Seed source produced an invalid seed ID (%s)" % seed_id) - if infile: - msg("Invalid input file: %s" % infile) - sys.exit(2) + if seed_id in seed_ids_save: + msg_r("Ignoring duplicate seed source") + if infile: msg(" '%s'" % infile) + else: msg(" for ID %s" % seed_id) + else: + msg("Seed source produced an invalid seed ID (%s)" % seed_id) + if infile: + msg("Invalid input file: %s" % infile) + sys.exit(2) - if other_addrs: - if 'keys_from_file' in opts: - keys += get_lines_from_file(opts['keys_from_file'], - "additional key data") - else: - msg(""" -A key file must be supplied (option '-f') for the following non-mmgen -address%s: %s""" % ( - "" if len(other_addrs) == 1 else "es", - " ".join([i['address'] for i in other_addrs]) - )) - sys.exit(2) + return keys - sig_tx = sign_transaction(c,tx_hex,sig_data,keys) -elif 'keys_from_file' in opts: - keys = get_lines_from_file(opts['keys_from_file'],"key data") - - sig_tx = sign_transaction(c,tx_hex,sig_data,keys) -else: - prompt = "Enter passphrase for bitcoind wallet: " - if 'echo_passphrase' in opts: - password = my_raw_input(prompt) - else: - password = my_getpass(prompt) - - wallet_enc = True - from mmgen.rpc import exceptions +def sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys): try: - c.walletpassphrase(password, 9999) - except exceptions.WalletWrongEncState: - msg("Wallet is unencrypted") - wallet_enc = False - except exceptions.WalletPassphraseIncorrect: - msg("Passphrase incorrect") - sys.exit(3) - except exceptions.WalletAlreadyUnlocked: - msg("WARNING: Wallet already unlocked!") - else: - msg("Passphrase OK") + sig_tx = sign_transaction(c,tx_hex,sig_data,keys) + except: + from mmgen.rpc import exceptions + msg("Using keys in wallet.dat as per user request") + prompt = "Enter passphrase for bitcoind wallet: " + while True: + passwd = get_bitcoind_passphrase(prompt,opts) - sig_tx = sign_transaction(c,tx_hex,sig_data) + try: + c.walletpassphrase(passwd, 9999) + except exceptions.WalletPassphraseIncorrect: + msg("Passphrase incorrect") + else: + msg("Passphrase OK"); break + + sig_tx = sign_transaction(c,tx_hex,sig_data,keys) - if wallet_enc: - c.walletlock() msg("Locking wallet") + try: + c.walletlock() + except: + msg("Failed to lock wallet") + + return sig_tx + + +def missing_keys_errormsg(other_addrs): + msg(""" +A key file (option '-f') or wallet.dat (option '-w') must be supplied +for the following non-mmgen address%s: %s""" % + ("" if len(other_addrs) == 1 else "es", + " ".join([i['address'] for i in other_addrs]) + )) + +# Begin execution + +c = connect_to_bitcoind() + +tx_file = infiles.pop(0) +m = "" if 'tx_id' in opts else "transaction data" +tx_data = get_lines_from_file(tx_file,m) + +metadata,tx_hex,sig_data,inputs_data = parse_tx_data(tx_data,tx_file) + +if 'tx_id' in opts: + msg(metadata[0]) + sys.exit(0) + +if 'info' in opts: + view_tx_data(c,inputs_data,tx_hex,metadata) + sys.exit(0) + +if not 'quiet' in opts: do_license_msg() + +msg("Successfully opened transaction file '%s'" % tx_file) + +if user_confirm("View transaction data? ",default_yes=False): + view_tx_data(c,inputs_data,tx_hex,metadata) + + +# Are inputs mmgen addresses? +mmgen_addrs,other_addrs = [],[] + +for i in inputs_data: + if verify_mmgen_label(i['account']): + mmgen_addrs.append(i) + else: + other_addrs.append(i) + + +if 'keys_from_file' in opts: + keys = get_lines_from_file(opts['keys_from_file'],"key data") +else: + keys = [] + +if mmgen_addrs: + if other_addrs and not keys and not 'use_wallet_dat' in opts: + missing_keys_errormsg(other_addrs) + sys.exit(2) + + keys += get_keys_for_mmgen_addrs(mmgen_addrs,infiles) + + if 'use_wallet_dat' in opts: + sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys) + else: + sig_tx = sign_transaction(c,tx_hex,sig_data,keys) +elif other_addrs: + if 'use_wallet_dat' in opts: + sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys) + else: + if keys: + sig_tx = sign_transaction(c,tx_hex,sig_data,keys) + else: + missing_keys_errormsg(other_addrs) + sys.exit(2) if sig_tx['complete']: msg("Signing completed") + prompt = "Save signed transaction?" + if user_confirm(prompt,default_yes=True): + print_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,opts) else: msg("Some keys were missing. Transaction could not be signed.") sys.exit(3) - -prompt = "Save signed transaction?" -if user_confirm(prompt,default_yes=True): - print_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,opts) diff --git a/mmgen-walletchk b/mmgen-walletchk index 335531fb..6e3c16cb 100755 --- a/mmgen-walletchk +++ b/mmgen-walletchk @@ -36,15 +36,16 @@ help_data = { -d, --outdir d Specify an alternate directory 'd' for output -e, --echo-passphrase Print passphrase to screen when typing it -m, --export-mnemonic Export the wallet's mnemonic to file +-P, --passwd-file f Get passphrase from file 'f' -s, --export-seed Export the wallet's seed to file -S, --stdout Print seed or mnemonic data to standard output -v, --verbose Produce more verbose output """ } -short_opts = "hd:emsSv" +short_opts = "hd:emP:sSv" long_opts = "help","outdir=","echo_passphrase","export_mnemonic",\ - "export_seed","stdout","verbose" + "passwd_file=","export_seed","stdout","verbose" opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts) diff --git a/mmgen-walletgen b/mmgen-walletgen index 8e9ceb17..9bad153f 100755 --- a/mmgen-walletgen +++ b/mmgen-walletgen @@ -37,14 +37,16 @@ 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 +-H, --show-hash-presets Show information on available hash presets -l, --seed-len n Create seed of length 'n'. Options: {} (default: {}) -L, --label l Label to identify this wallet (32 chars max. Allowed symbols: A-Z, a-z, 0-9, " ", "_", ".") -p, --hash-preset p Use scrypt.hash() parameters from preset 'p' (default: '{}') --P, --show-hash-presets Show information on available hash presets --q, --quiet Suppress warnings; overwrite files without asking +-P, --passwd-file f Get passphrase from file 'f' +-q, --quiet Suppress warnings; overwrite files without + prompting -u, --usr-randlen n Get 'n' characters of randomness from the user (default: {}) @@ -77,11 +79,11 @@ The '--from-brain' option also requires the user to specify a seed length (the 'l' parameter), which overrides both the default and any one given in 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. +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( - ", ".join([str(i) for i in seed_lens]), + ",".join([str(i) for i in seed_lens]), seed_len, hash_preset, usr_randlen, @@ -90,10 +92,10 @@ invocations with that passphrase. ) } -short_opts = "hd:el:L:p:Pqu:b:ms" -long_opts = "help","outdir=","echo_passphrase","seed_len=","label=",\ - "hash_preset=","show_hash_presets","quiet","usr_randlen=",\ - "from_brain=","from_mnemonic","from_seed" +short_opts = "hd:eHl:L:p:P:qu:b:ms" +long_opts = "help","outdir=","echo_passphrase","show_hash_presets","seed_len=",\ + "label=","hash_preset=","passwd_file=","quiet","usr_randlen=",\ + "from_brain=","from_mnemonic","from_seed" opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts) @@ -131,6 +133,9 @@ if not 'quiet' in opts: do_license_msg() msg_r("Acquiring random data from your computer...") +from time import sleep +sleep(1) + try: from Crypto import Random r = Random.new() @@ -162,8 +167,15 @@ else: salt = os_rand_data[1] + usr_rand_data salt = sha256(salt).digest()[:salt_len] -passwd = get_first_passphrase_from_user( - "{} wallet passphrase".format(proj_name), opts) +if not 'quiet' in opts: + msg(""" +Now you must choose a passphrase to encrypt the seed with. A key will be +generated from your passphrase using a hash preset of '%s'. Please note that +no strength checking of passphrases is performed. For an empty passphrase, +just hit ENTER twice. +""" % opts['hash_preset']) + +passwd = get_new_passphrase("{} wallet passphrase".format(proj_name), opts) key = make_key(passwd, salt, opts['hash_preset']) diff --git a/mmgen/addr.py b/mmgen/addr.py index fd7518a8..295d0cc2 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -38,9 +38,9 @@ def test_for_keyconv(): p = Popen([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. +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. """ % (keyconv_exec, keyconv_exec, "vanitygen")) return False @@ -152,12 +152,14 @@ def format_addr_data(addrlist, seed_chksum, opts): # MMGen address file # # This file is editable. -# Everything following a hash symbol '#' is ignored. -# A label may be added to the right of each address, and it will be -# appended to the bitcoind wallet label upon import (max. {} characters, -# allowed characters: A-Za-z0-9, plus '{}'). -""".format(max_wallet_addr_label_len, - "', '".join(wallet_addr_label_symbols)).strip() +# Everything following a hash symbol '#' is a comment and ignored by {}. +# 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 ASCII letters, numerals, and the symbols +# '{}' and '{}'. +""".format(proj_name.capitalize(),max_wallet_addr_label_len, + "', '".join(wallet_addr_label_symbols[0:-1]), + wallet_addr_label_symbols[-1]).strip() data = [] if not 'stdout' in opts: data.append(header + "\n") data.append("%s {" % seed_chksum.upper()) diff --git a/mmgen/config.py b/mmgen/config.py index cea4161d..d0925d6d 100755 --- a/mmgen/config.py +++ b/mmgen/config.py @@ -19,10 +19,14 @@ config.py: Constants and configuration options for the mmgen suite """ proj_name = "mmgen" + wallet_ext = "mmdat" seed_ext = "mmseed" mn_ext = "mmwords" brain_ext = "mmbrain" + +seed_exts = wallet_ext, seed_ext, mn_ext, brain_ext + default_wl = "electrum" #default_wl = "tirosh" diff --git a/mmgen/license.py b/mmgen/license.py index 72eb76a2..e29df791 100755 --- a/mmgen/license.py +++ b/mmgen/license.py @@ -30,7 +30,7 @@ gpl = { and you are welcome to redistribute it under certain conditions. """, 'prompt': """ -Press 'c' for conditions, 'w' for warranty info, or ENTER to continue: +Press 'w' for conditions and warranty info, or 'c' to continue: """, 'conditions': """ TERMS AND CONDITIONS @@ -582,60 +582,20 @@ reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. -""", - 'warranty': """ - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. """ } -def do_pager(text): - import os - pager = os.environ['PAGER'] if 'PAGER' in os.environ else 'more' - - p = os.popen(pager, 'w') - p.write(text) - p.close() - msg_r("\r") - - def do_license_msg(): msg(gpl['warning']) + prompt = "%s " % gpl['prompt'].strip() while True: - - prompt = "%s " % gpl['prompt'].strip() reply = get_char(prompt) - - if reply == 'c': do_pager(gpl['conditions']) - elif reply == 'w': do_pager(gpl['warranty']) - else: msg(""); break + if reply == 'w': + from mmgen.utils import do_pager + do_pager(gpl['conditions'],"END OF CONDITIONS AND WARRANTY") + elif reply == 'c': + msg(""); break + else: + msg_r("\r") + msg("") diff --git a/mmgen/mnemonic.py b/mmgen/mnemonic.py index 8144b1ff..34a5c8e5 100755 --- a/mmgen/mnemonic.py +++ b/mmgen/mnemonic.py @@ -49,12 +49,12 @@ def get_seed_from_mnemonic(mn,wl): if len(mn) not in mnemonic_lens: msg("Bad mnemonic (%i words). Allowed numbers of words: %s" % - (len(mn)," ".join([str(i) for i in mnemonic_lens]))) + (len(mn),", ".join([str(i) for i in mnemonic_lens]))) return False - for w in mn: + for n,w in enumerate(mn,1): if w not in wl: - msg("Bad mnemonic: '%s' is not in the wordlist" % w) + msg("Bad mnemonic: word number %s is not in the wordlist" % n) return False from binascii import unhexlify diff --git a/mmgen/rpc/connection.py b/mmgen/rpc/connection.py index 19482895..6623ddda 100755 --- a/mmgen/rpc/connection.py +++ b/mmgen/rpc/connection.py @@ -64,7 +64,12 @@ class BitcoinConnection(object): try: return self.proxy.importaddress(address,label) except JSONRPCException as e: - raise _wrap_exception(e.error) + if e.error['message'] == "Method not found": + from mmgen.utils import msg + msg(""" +ERROR: 'importaddress' method not found. Is your bitcoind enabled for +watch-only addresses?""") + else: raise _wrap_exception(e.error) # sendrawtransaction