Windows port fully functional; fixes, enhancements
This commit is contained in:
parent
afa3fe609a
commit
dcab2602b6
19 changed files with 477 additions and 358 deletions
|
|
@ -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] <address list>",
|
||||
'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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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" \
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
}
|
||||
|
||||
|
|
|
|||
222
mmgen-txsign
222
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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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("")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <hex string> [allowhighfees=false]
|
||||
def sendrawtransaction(self,tx):
|
||||
|
|
|
|||
|
|
@ -104,8 +104,9 @@ class AuthServiceProxy(object):
|
|||
'Authorization' : self.__authhdr,
|
||||
'Content-type' : 'application/json' })
|
||||
except:
|
||||
print "Unable to connect to bitcoind. Exiting"
|
||||
from mmgen.utils import msg
|
||||
import sys
|
||||
msg("\nUnable to connect to bitcoind.")
|
||||
sys.exit(2)
|
||||
|
||||
httpresp = self.__conn.getresponse()
|
||||
|
|
|
|||
49
mmgen/tx.py
49
mmgen/tx.py
|
|
@ -65,9 +65,10 @@ def connect_to_bitcoind():
|
|||
return c
|
||||
|
||||
|
||||
def trim_exponent(d):
|
||||
def trim_exponent(n):
|
||||
'''Remove exponent and trailing zeros.
|
||||
'''
|
||||
d = Decimal(n)
|
||||
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
|
||||
|
||||
|
||||
|
|
@ -95,7 +96,16 @@ def check_btc_amt(send_amt):
|
|||
|
||||
def get_cfg_options(cfg_keys):
|
||||
|
||||
cfg_file = "%s/%s" % (os.environ["HOME"], ".bitcoin/bitcoin.conf")
|
||||
if "HOME" in os.environ:
|
||||
cfg_file = "%s/%s" % (os.environ["HOME"], ".bitcoin/bitcoin.conf")
|
||||
elif "HOMEPATH" in os.environ:
|
||||
# Windows:
|
||||
cfg_file = "%s%s" % (os.environ["HOMEPATH"],
|
||||
r"\Application Data\Bitcoin\bitcoin.conf")
|
||||
else:
|
||||
msg("Unable to find bitcoin configuration file")
|
||||
sys.exit(3)
|
||||
|
||||
try:
|
||||
f = open(cfg_file)
|
||||
except:
|
||||
|
|
@ -186,7 +196,7 @@ def sort_and_view(unspent):
|
|||
total
|
||||
)]
|
||||
output.append(fs % ("Num","TX id Vout","","Address","Amount (BTC)",
|
||||
"Age (days)"))
|
||||
"Age(days)"))
|
||||
|
||||
for i in out:
|
||||
amt = str(trim_exponent(i.amount))
|
||||
|
|
@ -209,12 +219,17 @@ def sort_and_view(unspent):
|
|||
|
||||
output.append(fs % (str(n+1)+")",txid,i.vout,addr,i.amt,i.days))
|
||||
|
||||
skip_body = False
|
||||
while True:
|
||||
reply = get_char("\n".join(output) +
|
||||
"""\n
|
||||
if skip_body: skip_body = False
|
||||
else:
|
||||
msg("\n".join(output))
|
||||
msg("""
|
||||
Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr
|
||||
View options: [g]roup, show [m]mgen addr
|
||||
(Type 'q' to quit sorting, 'p' to print to file): """).strip()
|
||||
View options: [g]roup, show [m]mgen addr""")
|
||||
|
||||
reply = get_char(
|
||||
"(Type 'q' to quit sorting, 'p' to print to file, 'P' to view in pager): ")
|
||||
if reply == 'a': unspent.sort(s_amt); sort = "amount"; break
|
||||
elif reply == 't': unspent.sort(s_txid); sort = "txid"; break
|
||||
elif reply == 'd': unspent.sort(s_addr); sort = "address"; break
|
||||
|
|
@ -241,13 +256,19 @@ View options: [g]roup, show [m]mgen addr
|
|||
i.address,mmid,i.amt,i.days,cmt)
|
||||
pout.append(os.rstrip())
|
||||
|
||||
outdata = "Unspent outputs ({} UTC)\n\n{}\n\nTotal BTC: {}\n".format(
|
||||
make_timestr(), "\n".join(pout), total
|
||||
)
|
||||
outfile = "listunspent.out"
|
||||
sort_info = (
|
||||
("reverse," if reverse else "") +
|
||||
(sort if sort else "unsorted")
|
||||
)
|
||||
outdata = \
|
||||
"Unspent outputs ({} UTC)\nSort order: {}\n\n{}\n\nTotal BTC: {}\n".format(
|
||||
make_timestr(), sort_info, "\n".join(pout), total
|
||||
)
|
||||
outfile = "listunspent[%s].out" % sort_info
|
||||
write_to_file(outfile, outdata)
|
||||
skip_body = True
|
||||
msg("\nData written to '%s'" % outfile)
|
||||
sys.exit(1)
|
||||
elif reply == 'P': do_pager("\n".join(output))
|
||||
elif reply == 'q': break
|
||||
else: msg("Invalid input")
|
||||
|
||||
|
|
@ -288,7 +309,7 @@ def view_tx_data(c,inputs_data,tx_hex,metadata=[]):
|
|||
msg("TRANSACTION DATA:\n")
|
||||
|
||||
if metadata: msg(
|
||||
"Header: [ID: {}] [Amount: {} BTC] [Time: {}]\n".format(*metadata))
|
||||
"Header: [Tx ID: {}] [Amount: {} BTC] [Time: {}]\n".format(*metadata))
|
||||
|
||||
msg("Inputs:")
|
||||
total_in = 0
|
||||
|
|
@ -300,7 +321,7 @@ def view_tx_data(c,inputs_data,tx_hex,metadata=[]):
|
|||
msg(" " + """
|
||||
%-2s tx,vout: %s,%s
|
||||
address: %s
|
||||
label: %s
|
||||
ID/label: %s
|
||||
amount: %s BTC
|
||||
confirmations: %s (around %s days)
|
||||
""".strip() %
|
||||
|
|
|
|||
261
mmgen/utils.py
261
mmgen/utils.py
|
|
@ -40,20 +40,44 @@ def my_getpass(prompt):
|
|||
|
||||
return pw
|
||||
|
||||
def get_char(prompt):
|
||||
term = False
|
||||
|
||||
def get_char(prompt=""):
|
||||
|
||||
import os
|
||||
msg_r(prompt)
|
||||
os.system(
|
||||
"stty -icanon min 1 time 0 -echo -echoe -echok -echonl -crterase noflsh"
|
||||
)
|
||||
try: ch = sys.stdin.read(1)
|
||||
|
||||
global term
|
||||
|
||||
if not term:
|
||||
try:
|
||||
import tty, termios
|
||||
term = "unix"
|
||||
except:
|
||||
try:
|
||||
import msvcrt
|
||||
term = "mswin"
|
||||
except:
|
||||
msg("Unable to set terminal mode")
|
||||
sys.exit(2)
|
||||
|
||||
try:
|
||||
if term == "unix":
|
||||
import tty, termios
|
||||
fd = sys.stdin.fileno()
|
||||
old = termios.tcgetattr(fd)
|
||||
tty.setcbreak(fd)
|
||||
ch = sys.stdin.read(1)
|
||||
elif term == "mswin":
|
||||
import msvcrt
|
||||
ch = msvcrt.getch()
|
||||
if ord(ch) == 3:
|
||||
raise KeyboardInterrupt
|
||||
except:
|
||||
os.system("stty sane")
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
else:
|
||||
os.system("stty sane")
|
||||
finally:
|
||||
if term == "unix":
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
|
||||
return ch
|
||||
|
||||
|
|
@ -320,39 +344,29 @@ def parse_address_list(arg,sep=","):
|
|||
return sorted(set(ret))
|
||||
|
||||
|
||||
def get_first_passphrase_from_user(what, opts):
|
||||
"""
|
||||
Prompt the user for a passphrase and return it
|
||||
def get_new_passphrase(what, opts):
|
||||
|
||||
Supported options: echo_passphrase
|
||||
"""
|
||||
|
||||
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'])
|
||||
|
||||
if 'echo_passphrase' in opts:
|
||||
ret = " ".join(_get_words_from_user(opts,"Enter %s: " % what))
|
||||
if ret == "": msg("Empty passphrase")
|
||||
return ret
|
||||
|
||||
for i in range(passwd_max_tries):
|
||||
ret = " ".join(_get_words_from_user(opts,"Enter %s: " % what))
|
||||
ret2 = " ".join(_get_words_from_user(opts,"Repeat %s: " % what))
|
||||
if debug: print "Passphrases: [%s] [%s]" % (ret,ret2)
|
||||
if ret2 == ret:
|
||||
s = " (empty)" if not len(ret) else ""
|
||||
msg("%ss match%s" % (what.capitalize(),s))
|
||||
return ret
|
||||
if 'passwd_file' in opts:
|
||||
pw = " ".join(_get_words_from_file(opts['passwd_file'],what))
|
||||
elif 'echo_passphrase' in opts:
|
||||
pw = " ".join(_get_words_from_user(("Enter %s: " % what), opts))
|
||||
else:
|
||||
for i in range(passwd_max_tries):
|
||||
pw = " ".join(_get_words_from_user(("Enter %s: " % what),opts))
|
||||
pw2 = " ".join(_get_words_from_user(("Repeat %s: " % what),opts))
|
||||
if debug: print "Passphrases: [%s] [%s]" % (pw,pw2)
|
||||
if pw == pw2:
|
||||
msg("%ss match" % what.capitalize())
|
||||
break
|
||||
else:
|
||||
msg("%ss do not match" % what.capitalize())
|
||||
else:
|
||||
msg("%ss do not match" % what.capitalize())
|
||||
msg("User failed to duplicate passphrase in %s attempts" %
|
||||
passwd_max_tries)
|
||||
sys.exit(2)
|
||||
|
||||
msg("User failed to duplicate passphrase in %s attempts" % passwd_max_tries)
|
||||
sys.exit(2)
|
||||
if pw == "": msg("WARNING: Empty passphrase")
|
||||
return pw
|
||||
|
||||
|
||||
def _scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
|
||||
|
|
@ -404,9 +418,12 @@ def write_to_stdout(data, what, confirm=True):
|
|||
if sys.stdout.isatty() and confirm:
|
||||
confirm_or_exit("",'output {} to screen'.format(what))
|
||||
elif not sys.stdout.isatty():
|
||||
import os
|
||||
of = os.readlink("/proc/%d/fd/1" % os.getpid())
|
||||
msg("Writing data to file '%s'" % of)
|
||||
try:
|
||||
import os
|
||||
of = os.readlink("/proc/%d/fd/1" % os.getpid())
|
||||
msg("Redirecting output to file '%s'" % of)
|
||||
except:
|
||||
msg("Redirecting output to file")
|
||||
sys.stdout.write(data)
|
||||
|
||||
|
||||
|
|
@ -682,24 +699,28 @@ def get_data_from_wallet(infile,opts,silent=False):
|
|||
return label,metadata,hash_preset,res['salt'],res['enc_seed']
|
||||
|
||||
|
||||
def _get_words_from_user(opts, prompt):
|
||||
def _get_words_from_user(prompt, opts):
|
||||
# split() also strips
|
||||
if 'echo_passphrase' in opts:
|
||||
return my_raw_input(prompt).split()
|
||||
words = my_raw_input(prompt).split()
|
||||
else:
|
||||
return my_getpass(prompt).split()
|
||||
words = my_getpass(prompt).split()
|
||||
if debug: print "Sanitized input: [%s]" % " ".join(words)
|
||||
return words
|
||||
|
||||
|
||||
def _get_words_from_file(infile,what):
|
||||
msg("Getting %s from file '%s'" % (what,infile))
|
||||
f = open_file_or_exit(infile, 'r')
|
||||
data = f.read(); f.close()
|
||||
# split() also strips
|
||||
return data.split()
|
||||
words = f.read().split()
|
||||
f.close()
|
||||
if debug: print "Sanitized input: [%s]" % " ".join(words)
|
||||
return words
|
||||
|
||||
|
||||
def get_lines_from_file(infile,what):
|
||||
msg("Getting %s from file '%s'" % (what,infile))
|
||||
def get_lines_from_file(infile,what=""):
|
||||
if what != "": msg("Getting %s from file '%s'" % (what,infile))
|
||||
f = open_file_or_exit(infile,'r')
|
||||
lines = f.read().splitlines(); f.close()
|
||||
return lines
|
||||
|
|
@ -713,15 +734,6 @@ def get_data_from_file(infile,what="data"):
|
|||
return data
|
||||
|
||||
|
||||
def get_words(infile,what,prompt,opts):
|
||||
if infile:
|
||||
words = _get_words_from_file(infile,what)
|
||||
else:
|
||||
words = _get_words_from_user(opts,prompt)
|
||||
if debug: print "Sanitized input: [%s]" % " ".join(words)
|
||||
return words
|
||||
|
||||
|
||||
def _get_seed_from_seed_data(words):
|
||||
|
||||
if not _check_mmseed_format(words):
|
||||
|
|
@ -746,6 +758,34 @@ def _get_seed_from_seed_data(words):
|
|||
msg("Invalid checksum for {} seed".format(proj_name))
|
||||
return False
|
||||
|
||||
passwd_file_used = False
|
||||
|
||||
def mark_passwd_file_as_used(opts):
|
||||
global passwd_file_used
|
||||
if passwd_file_used:
|
||||
msg_r("WARNING: Reusing passphrase from file '%s'." % opts['passwd_file'])
|
||||
msg(" This may not be what you want!")
|
||||
passwd_file_used = True
|
||||
|
||||
|
||||
def get_mmgen_passphrase(prompt,opts):
|
||||
if 'passwd_file' in opts:
|
||||
mark_passwd_file_as_used(opts)
|
||||
return " ".join(_get_words_from_file(opts['passwd_file'],"passphrase"))
|
||||
else:
|
||||
return " ".join(_get_words_from_user(prompt,opts))
|
||||
|
||||
|
||||
def get_bitcoind_passphrase(prompt,opts):
|
||||
if 'passwd_file' in opts:
|
||||
mark_passwd_file_as_used(opts)
|
||||
return get_data_from_file(opts['passwd_file'],"passphrase").strip("\r\n")
|
||||
else:
|
||||
if 'echo_passphrase' in opts:
|
||||
return my_raw_input(prompt)
|
||||
else:
|
||||
return my_getpass(prompt)
|
||||
|
||||
|
||||
def get_seed_from_wallet(
|
||||
infile,
|
||||
|
|
@ -759,7 +799,7 @@ def get_seed_from_wallet(
|
|||
|
||||
if 'verbose' in opts: _display_control_data(*wdata)
|
||||
|
||||
passwd = " ".join(get_words("","",prompt,opts))
|
||||
passwd = get_mmgen_passphrase(prompt,opts)
|
||||
|
||||
key = make_key(passwd, salt, hash_preset)
|
||||
|
||||
|
|
@ -809,41 +849,63 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
|
|||
return dec_seed
|
||||
|
||||
|
||||
def get_seed(infile,opts,silent=False):
|
||||
if 'from_mnemonic' in opts:
|
||||
prompt = "Enter mnemonic: "
|
||||
what = "mnemonic"
|
||||
words = get_words(infile,"mnemonic data",prompt,opts)
|
||||
def _get_words(infile,what,prompt,opts):
|
||||
if infile:
|
||||
return _get_words_from_file(infile,what)
|
||||
else:
|
||||
return _get_words_from_user(prompt,opts)
|
||||
|
||||
|
||||
def get_seed(infile,opts,silent=False):
|
||||
|
||||
ext = infile.split(".")[-1]
|
||||
|
||||
if ext == mn_ext: source = "mnemonic"
|
||||
elif ext == brain_ext: source = "brainwallet"
|
||||
elif ext == seed_ext: source = "seed"
|
||||
elif ext == wallet_ext: source = "wallet"
|
||||
elif 'from_mnemonic' in opts: source = "mnemonic"
|
||||
elif 'from_brain' in opts: source = "brainwallet"
|
||||
elif 'from_seed' in opts: source = "seed"
|
||||
else:
|
||||
if infile: msg(
|
||||
"Invalid file extension for file: %s\nValid extensions: '.%s'" %
|
||||
(infile, "', '.".join(seed_exts)))
|
||||
else: msg("No seed source type specified and no file supplied")
|
||||
sys.exit(2)
|
||||
|
||||
if source == "mnemonic":
|
||||
prompt = "Enter mnemonic: "
|
||||
words = _get_words(infile,"mnemonic data",prompt,opts)
|
||||
wl = get_default_wordlist()
|
||||
from mmgen.mnemonic import get_seed_from_mnemonic
|
||||
seed = get_seed_from_mnemonic(words,wl)
|
||||
elif 'from_brain' in opts:
|
||||
elif source == "brainwallet":
|
||||
if 'from_brain' not in opts:
|
||||
msg("'--from-brain' parameters must be specified for brainwallet file")
|
||||
sys.exit(2)
|
||||
if 'quiet' not in opts:
|
||||
confirm_or_exit(
|
||||
cmessages['brain_warning'].format(
|
||||
proj_name.capitalize(),
|
||||
*_get_from_brain_opt_params(opts)),
|
||||
"continue")
|
||||
proj_name.capitalize(), *_get_from_brain_opt_params(opts)),
|
||||
"continue")
|
||||
prompt = "Enter brainwallet passphrase: "
|
||||
what = "brainwallet"
|
||||
words = get_words(infile,"brainwallet data",prompt,opts)
|
||||
words = _get_words(infile,"brainwallet data",prompt,opts)
|
||||
seed = _get_seed_from_brain_passphrase(words,opts)
|
||||
elif 'from_seed' in opts:
|
||||
elif source == "seed":
|
||||
prompt = "Enter seed in %s format: " % seed_ext
|
||||
what = "seed"
|
||||
words = get_words(infile,"seed data",prompt,opts)
|
||||
words = _get_words(infile,"seed data",prompt,opts)
|
||||
seed = _get_seed_from_seed_data(words)
|
||||
else:
|
||||
return get_seed_from_wallet(infile, opts, silent=silent)
|
||||
elif source == "wallet":
|
||||
seed = get_seed_from_wallet(infile, opts, silent=silent)
|
||||
|
||||
if infile and not seed:
|
||||
msg("Invalid %s file: %s" % (what,infile))
|
||||
msg("Invalid %s file: %s" % (source,infile))
|
||||
sys.exit(2)
|
||||
|
||||
return seed
|
||||
|
||||
# Repeat if data entry is incorrect
|
||||
# Repeat if entered data is invalid
|
||||
def get_seed_retry(infile,opts):
|
||||
silent = False
|
||||
while True:
|
||||
|
|
@ -863,5 +925,48 @@ def remove_blanks_comments(lines):
|
|||
|
||||
return ret
|
||||
|
||||
def do_pager(text,endmsg=""):
|
||||
import os
|
||||
if sys.platform.startswith("linux"):
|
||||
if 'PAGER' in os.environ and os.environ['PAGER']:
|
||||
try:
|
||||
p = os.popen(os.environ['PAGER'], 'w')
|
||||
except:
|
||||
print text
|
||||
else:
|
||||
try:
|
||||
p.write(text)
|
||||
p.close()
|
||||
except:
|
||||
p.close()
|
||||
msg_r("\r")
|
||||
else:
|
||||
print text
|
||||
elif sys.platform.startswith("win"):
|
||||
try:
|
||||
import msvcrt
|
||||
except:
|
||||
print text
|
||||
else:
|
||||
try:
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
p = Popen(["more","/C"], stdin=PIPE, shell=True)
|
||||
if endmsg:
|
||||
p.stdin.write("%s\n%s\n\n" % (text,endmsg))
|
||||
else:
|
||||
p.stdin.write(text)
|
||||
except:
|
||||
msg("\nUser exit")
|
||||
|
||||
from time import sleep
|
||||
# Flush stdin
|
||||
while msvcrt.kbhit(): msvcrt.getch()
|
||||
sleep(1)
|
||||
while msvcrt.kbhit(): msvcrt.getch()
|
||||
msg("")
|
||||
else:
|
||||
print text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print get_lines_from_file("/tmp/lines","test file")
|
||||
print "utils.py"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ walletgen.py: Routines used for seed generation and wallet creation
|
|||
"""
|
||||
|
||||
import sys
|
||||
from mmgen.utils import msg, msg_r
|
||||
from mmgen.utils import msg, msg_r, get_char
|
||||
from binascii import hexlify
|
||||
|
||||
def get_random_data_from_user(opts):
|
||||
|
|
@ -47,30 +47,18 @@ displayed on the screen.
|
|||
|
||||
user_rand_data,intervals = "",[]
|
||||
|
||||
try:
|
||||
import os
|
||||
os.system(
|
||||
"stty -icanon min 1 time 0 -echo -echoe -echok -echonl -crterase noflsh"
|
||||
)
|
||||
for i in range(ulen):
|
||||
user_rand_data += sys.stdin.read(1)
|
||||
msg_r("\r" + prompt % (ulen - i - 1))
|
||||
now = time.time()
|
||||
intervals.append(now - saved_time)
|
||||
saved_time = now
|
||||
if 'quiet' in opts:
|
||||
msg_r("\r")
|
||||
else:
|
||||
msg_r("\rThank you. That's enough." + " "*15 + "\n\n")
|
||||
time.sleep(0.5)
|
||||
msg_r(
|
||||
"User random data successfully acquired. Press ENTER to continue: ")
|
||||
raw_input()
|
||||
except:
|
||||
msg("\nUser random input interrupted")
|
||||
sys.exit(1)
|
||||
finally:
|
||||
os.system("stty sane")
|
||||
for i in range(ulen):
|
||||
user_rand_data += get_char()
|
||||
msg_r("\r" + prompt % (ulen - i - 1))
|
||||
now = time.time()
|
||||
intervals.append(now - saved_time)
|
||||
saved_time = now
|
||||
if 'quiet' in opts:
|
||||
msg_r("\r")
|
||||
else:
|
||||
msg_r("\rThank you. That's enough." + " "*15 + "\n\n")
|
||||
time.sleep(0.5)
|
||||
get_char("User random data successfully acquired. Press ENTER to continue: ")
|
||||
|
||||
return user_rand_data, ["{:.22f}".format(i) for i in intervals]
|
||||
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -3,7 +3,7 @@ from distutils.core import setup
|
|||
|
||||
setup(
|
||||
name = 'mmgen',
|
||||
version = '0.6.5',
|
||||
version = '0.6.7',
|
||||
author = 'Philemon',
|
||||
author_email = 'mmgen-py@yandex.com',
|
||||
url = 'https://github.com/mmgen/mmgen',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue