New script launcher, better KeyboardInterrupt handling, bugfixes in term.py
This commit is contained in:
parent
bfd0cda562
commit
2b183c18f3
36 changed files with 3247 additions and 2975 deletions
184
mmgen-addrgen
184
mmgen-addrgen
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -17,183 +17,9 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-addrgen: Generate a list or range of addresses from a mmgen
|
||||
deterministic wallet.
|
||||
Call as 'btc-keygen' to allow key generation.
|
||||
mmgen-addrgen: Generate a series or range of addresses from an MMGen
|
||||
deterministic wallet
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
from mmgen.addr import *
|
||||
from mmgen.tx import make_addr_data_chksum
|
||||
|
||||
what = "keys" if sys.argv[0].split("-")[-1] == "keygen" else "addresses"
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': """Generate a list or range of {} from an {g.proj_name} wallet,
|
||||
mnemonic, seed or password""".format(what,g=g),
|
||||
'usage':"[opts] [infile] <address list>",
|
||||
'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
|
||||
instead of 'keyconv'
|
||||
-l, --seed-len= N Length of seed. Options: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when
|
||||
hashing password (default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-S, --stdout Print {what} to stdout
|
||||
-v, --verbose Produce more verbose output{}
|
||||
|
||||
-b, --from-brain= l,p Generate {what} from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate {what} from an incognito wallet
|
||||
-X, --from-incog-hex Generate {what} from incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden=f,o,l Generate {what} from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate {what} from an electrum-like mnemonic
|
||||
-s, --from-seed Generate {what} from a seed in .{g.seed_ext} format
|
||||
""".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 '{}-txsign'".format(g.proj_name.lower()),
|
||||
"\n-x, --b16 Print secret keys in hexadecimal too"
|
||||
)
|
||||
if what == "keys" else ("","","")),
|
||||
seed_lens=", ".join([str(i) for i in g.seed_lens]),
|
||||
what=what, g=g
|
||||
),
|
||||
'notes': """
|
||||
|
||||
Addresses are given in a comma-separated list. Hyphen-separated ranges are
|
||||
also allowed.{}
|
||||
|
||||
If available, the external 'keyconv' program will be used for address
|
||||
generation.
|
||||
|
||||
Data for the --from-<what> options will be taken from <infile> if <infile>
|
||||
is specified. Otherwise, the user will be prompted to enter the data.
|
||||
|
||||
For passphrases all combinations of whitespace are equal, and leading and
|
||||
trailing space are ignored. This permits reading passphrase data from a
|
||||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
attacks, the brainwallet hash preset must be specified by the user, using
|
||||
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
|
||||
""".format("\n\nBy default, both addresses and secret keys are generated."
|
||||
if what == "keys" else "")
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
if 'verbose' in opts: g.verbose = True
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
|
||||
opts['from_incog'] = True
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
if len(cmd_args) == 1 and (
|
||||
'from_mnemonic' in opts
|
||||
or 'from_brain' in opts
|
||||
or 'from_seed' in opts
|
||||
or 'from_incog_hidden' in opts
|
||||
):
|
||||
infile,addr_idx_arg = "",cmd_args[0]
|
||||
elif len(cmd_args) == 2:
|
||||
infile,addr_idx_arg = cmd_args
|
||||
check_infile(infile)
|
||||
else: usage(help_data)
|
||||
|
||||
addr_idxs = parse_address_list(addr_idx_arg)
|
||||
|
||||
if not addr_idxs: sys.exit(2)
|
||||
|
||||
do_license_msg()
|
||||
|
||||
# Interact with user:
|
||||
if what == "keys" and not g.quiet:
|
||||
confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue')
|
||||
|
||||
# Generate data:
|
||||
|
||||
seed = get_seed_retry(infile,opts)
|
||||
seed_id = make_chksum_8(seed)
|
||||
|
||||
for l in (
|
||||
('flat_list', 'no_addresses'),
|
||||
('flat_list', 'b16'),
|
||||
): warn_incompatible_opts(opts,l)
|
||||
|
||||
opts['gen_what'] = \
|
||||
("addrs") if what == "addresses" else (
|
||||
("keys") if 'no_addresses' in opts else ("addrs","keys"))
|
||||
|
||||
addr_data = generate_addrs(seed, addr_idxs, opts)
|
||||
addr_data_chksum = make_addr_data_chksum([(a.num,a.addr)
|
||||
for a in addr_data]) if 'addrs' in opts['gen_what'] 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))
|
||||
|
||||
if 'flat_list' in opts:
|
||||
confirm = False if g.quiet else True
|
||||
outfile = "%s.%s" % (outfile_base,g.keylist_ext)
|
||||
if (user_confirm("Encrypt key list?")):
|
||||
enc_data = mmgen_encrypt(addr_data_str,"",opts)
|
||||
outfile += "."+g.mmenc_ext
|
||||
write_to_file(outfile,enc_data,opts,"encrypted key list",confirm,True)
|
||||
else:
|
||||
write_to_file(outfile,addr_data_str,opts,"key list",confirm,True)
|
||||
sys.exit()
|
||||
|
||||
# Output data:
|
||||
if 'stdout' in opts:
|
||||
confirm = True if (what == "keys" and not g.quiet) else False
|
||||
write_to_stdout(addr_data_str,what,confirm)
|
||||
elif not sys.stdout.isatty():
|
||||
write_to_stdout(addr_data_str,what,confirm=False)
|
||||
else:
|
||||
confirm = False if g.quiet else True
|
||||
outfile = outfile_base + "." + (
|
||||
g.keylist_ext if 'flat_list' in opts else (
|
||||
g.keyfile_ext if opts['gen_what'] == ("keys") else (
|
||||
g.addrfile_ext if opts['gen_what'] == ("addrs") else "akeys")))
|
||||
write_to_file(outfile,addr_data_str,opts,what,confirm,True)
|
||||
|
||||
if 'addrs' in opts['gen_what']:
|
||||
msg("Checksum for address data {}: {}".format(outfile_base,addr_data_chksum))
|
||||
if 'save_checksum' in opts:
|
||||
a = "address data checksum"
|
||||
write_to_file(outfile_base+".chk",addr_data_chksum,opts,a,confirm,True)
|
||||
else:
|
||||
qmsg("This checksum will be used to verify the address file in the future.")
|
||||
qmsg("Record it to a safe location.")
|
||||
import mmgen.main
|
||||
mmgen.main.main("addrgen")
|
||||
|
|
|
|||
126
mmgen-addrimport
126
mmgen-addrimport
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -17,126 +17,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-addrimport: Import addresses into a bitcoind watching wallet.
|
||||
mmgen-addrimport: Import addresses into a MMGen bitcoind watching wallet
|
||||
"""
|
||||
|
||||
import sys
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.util import *
|
||||
from mmgen.tx import connect_to_bitcoind,parse_addrs_file
|
||||
|
||||
help_data = {
|
||||
'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]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-l, --addrlist= f Import the non-mmgen Bitcoin addresses listed in file 'f'
|
||||
-q, --quiet Suppress warnings
|
||||
-r, --rescan Rescan the blockchain. Required if address to import is
|
||||
on the blockchain and has a balance. Rescanning is slow.
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
|
||||
if len(cmd_args) != 1 and not 'addrlist' in opts:
|
||||
msg("You must specify an mmgen address list (and/or non-mmgen addresses with the '--addrlist' option)")
|
||||
sys.exit(1)
|
||||
|
||||
if cmd_args:
|
||||
check_infile(cmd_args[0])
|
||||
seed_id,addr_data = parse_addrs_file(cmd_args[0])
|
||||
else:
|
||||
seed_id,addr_data = "",[]
|
||||
|
||||
if 'addrlist' in opts:
|
||||
lines = get_lines_from_file(opts['addrlist'],"non-mmgen addresses",
|
||||
trim_comments=True)
|
||||
addr_data += [(None,l) for l in lines]
|
||||
|
||||
from mmgen.bitcoin import verify_addr
|
||||
qmsg_r("Validating addresses...")
|
||||
for i in addr_data:
|
||||
if not verify_addr(i[1],verbose=True):
|
||||
msg("%s: invalid address" % i)
|
||||
sys.exit(2)
|
||||
qmsg("OK")
|
||||
|
||||
import mmgen.config as g
|
||||
g.http_timeout = 3600
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
m = """
|
||||
WARNING: You've chosen the '--rescan' option. Rescanning the block chain is
|
||||
necessary only if an address you're importing is already on the block chain
|
||||
and has a balance. Note that the rescanning process is very slow (>30 min.
|
||||
for each imported address on a low-powered computer).
|
||||
""".strip() if "rescan" in opts else """
|
||||
WARNING: If any of the addresses you're importing is already on the block chain
|
||||
and has a balance, you must exit the program now and rerun it using the
|
||||
'--rescan' option. Otherwise you may ignore this message and continue.
|
||||
""".strip()
|
||||
|
||||
if g.quiet: m = ""
|
||||
confirm_or_exit(m, "continue", expect="YES")
|
||||
|
||||
err_flag = False
|
||||
|
||||
def import_address(addr,label,rescan):
|
||||
try:
|
||||
c.importaddress(addr,label,rescan)
|
||||
except:
|
||||
global err_flag
|
||||
err_flag = True
|
||||
|
||||
|
||||
w1 = len(str(len(addr_data))) * 2 + 2
|
||||
w2 = len(str(max([i[0] for i in addr_data if i[0]]))) + 12
|
||||
|
||||
if "rescan" in opts:
|
||||
import threading
|
||||
import time
|
||||
msg_fmt = "\r%s %-" + str(w1) + "s %-34s %-" + str(w2) + "s"
|
||||
else:
|
||||
msg_fmt = "\r%-" + str(w1) + "s %-34s %-" + str(w2) + "s"
|
||||
|
||||
msg("Importing addresses")
|
||||
for n,i in enumerate(addr_data):
|
||||
if i[0]:
|
||||
comment = " " + i[2] if len(i) == 3 else ""
|
||||
label = "%s:%s%s" % (seed_id,i[0],comment)
|
||||
else: label = "non-mmgen"
|
||||
|
||||
if "rescan" in opts:
|
||||
t = threading.Thread(target=import_address, args=(i[1],label,True))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
start = int(time.time())
|
||||
|
||||
while True:
|
||||
if t.is_alive():
|
||||
elapsed = int(time.time() - start)
|
||||
msg_r(msg_fmt % (
|
||||
secs_to_hms(elapsed),
|
||||
("%s/%s:" % (n+1,len(addr_data))),
|
||||
i[1], "(" + label + ")"
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
else:
|
||||
if err_flag: msg("\nImport failed"); sys.exit(2)
|
||||
msg("\nOK")
|
||||
break
|
||||
else:
|
||||
import_address(i[1],label,rescan=False)
|
||||
msg_r(msg_fmt % (("%s/%s:" % (n+1,len(addr_data))),
|
||||
i[1], "(" + label + ")"))
|
||||
if err_flag: msg("\nImport failed"); sys.exit(2)
|
||||
msg(" - OK")
|
||||
import mmgen.main
|
||||
mmgen.main.main("addrimport")
|
||||
|
|
|
|||
113
mmgen-passchg
113
mmgen-passchg
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,114 +15,11 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-passchg: Change a mmgen deterministic wallet's passphrase, label or
|
||||
mmgen-passchg: Change an MMGen deterministic wallet's passphrase, label or
|
||||
hash preset
|
||||
"""
|
||||
|
||||
import sys
|
||||
from mmgen.Opts import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
import mmgen.config as g
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': """Change the passphrase, hash preset or label of an {}
|
||||
deterministic wallet""".format(g.proj_name),
|
||||
'usage': "[opts] [filename]",
|
||||
'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: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get new passphrase from file 'f'
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
NOTE: The key ID will change if either the passphrase or hash preset are
|
||||
changed
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
if 'verbose' in opts: g.verbose = True
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
|
||||
if len(cmd_args) != 1:
|
||||
msg("One input file must be specified")
|
||||
sys.exit(2)
|
||||
infile = cmd_args[0]
|
||||
|
||||
# Old key:
|
||||
label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile)
|
||||
seed_id,key_id = metadata[:2]
|
||||
|
||||
# Repeat on incorrect pw entry
|
||||
prompt = "Enter %spassphrase: " % (""
|
||||
if 'keep_old_passphrase' in opts else "old ")
|
||||
while True:
|
||||
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
|
||||
|
||||
changed = {}
|
||||
|
||||
if 'label' in opts:
|
||||
if opts['label'] != label:
|
||||
msg("Label changed: '%s' -> '%s'" % (label, opts['label']))
|
||||
changed['label'] = True
|
||||
else:
|
||||
msg("Label is unchanged: '%s'" % (label))
|
||||
else: opts['label'] = label # Copy the old label
|
||||
|
||||
if 'hash_preset' in opts:
|
||||
if hash_preset != opts['hash_preset']:
|
||||
qmsg("Hash preset has changed (%s -> %s)" %
|
||||
(hash_preset, opts['hash_preset']))
|
||||
changed['preset'] = True
|
||||
else:
|
||||
msg("Hash preset is unchanged")
|
||||
else:
|
||||
opts['hash_preset'] = hash_preset
|
||||
|
||||
if 'keep_old_passphrase' in opts:
|
||||
msg("Keeping old passphrase by user request")
|
||||
else:
|
||||
new_passwd = get_new_passphrase("new passphrase", opts)
|
||||
|
||||
if new_passwd == passwd:
|
||||
qmsg("Passphrase is unchanged")
|
||||
else:
|
||||
qmsg("Passphrase has changed")
|
||||
passwd = new_passwd
|
||||
changed['passwd'] = True
|
||||
|
||||
if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
|
||||
qmsg("Will update salt and key ID")
|
||||
|
||||
from hashlib import sha256
|
||||
|
||||
salt = sha256(salt + get_random(128,opts)).digest()[:g.salt_len]
|
||||
key = make_key(passwd, salt, opts['hash_preset'])
|
||||
new_key_id = make_chksum_8(key)
|
||||
qmsg("Key ID changed: %s -> %s" % (key_id,new_key_id))
|
||||
key_id = new_key_id
|
||||
enc_seed = encrypt_seed(seed, key)
|
||||
elif not 'label' in changed:
|
||||
msg("Data unchanged. No file will be written")
|
||||
sys.exit(2)
|
||||
|
||||
write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts)
|
||||
import mmgen.main
|
||||
mmgen.main.main("passchg")
|
||||
|
|
|
|||
1662
mmgen-pywallet
1662
mmgen-pywallet
File diff suppressed because it is too large
Load diff
58
mmgen-tool
58
mmgen-tool
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,57 +15,11 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-tool: Perform various Bitcoin-related operations - part of the MMGen suite
|
||||
mmgen-tool: Perform various Bitcoin-related operations.
|
||||
Part of the MMGen suite
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.config as g
|
||||
import mmgen.tool as tool
|
||||
from mmgen.Opts import *
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Perform various BTC-related operations",
|
||||
'usage': "[opts] <command> <command args>",
|
||||
'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
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
COMMANDS:{}
|
||||
Type '{} <command> --help for usage information on a particular
|
||||
command
|
||||
""".format(tool.command_help,g.prog_name)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
if 'verbose' in opts: g.verbose = True
|
||||
|
||||
if len(cmd_args) < 1:
|
||||
usage(help_data)
|
||||
sys.exit(1)
|
||||
|
||||
command = cmd_args.pop(0)
|
||||
|
||||
if command not in tool.commands.keys():
|
||||
msg("'%s': No such command" % command)
|
||||
sys.exit(1)
|
||||
|
||||
if cmd_args and cmd_args[0] == '--help':
|
||||
tool.tool_usage(g.prog_name, command)
|
||||
sys.exit(0)
|
||||
|
||||
args = tool.process_args(g.prog_name, command, cmd_args)
|
||||
|
||||
tool.opts = opts
|
||||
|
||||
#print command + "(" + ", ".join(args) + ")"
|
||||
eval("tool." + command + "(" + ", ".join(args) + ")")
|
||||
import mmgen.main
|
||||
mmgen.main.main("tool")
|
||||
|
|
|
|||
208
mmgen-txcreate
208
mmgen-txcreate
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,207 +15,11 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-txcreate: Create a BTC transaction, sending to specified addresses
|
||||
mmgen-txcreate: Create a Bitcoin transaction from MMGen- or non-MMGen inputs
|
||||
to MMGen- or non-MMGen outputs
|
||||
"""
|
||||
|
||||
import sys
|
||||
from decimal import Decimal
|
||||
|
||||
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': g.prog_name,
|
||||
'desc': "Create a BTC transaction with outputs to specified addresses",
|
||||
'usage': "[opts] <addr,amt> ... [change addr] [addr file] ...",
|
||||
'options': """
|
||||
-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, --tx-fee= f Transaction fee (default: {g.tx_fee} BTC)
|
||||
-i, --info Display unspent outputs and exit
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
Transaction inputs are chosen from a list of the user's unpent outputs
|
||||
via an interactive menu.
|
||||
|
||||
Ages of transactions are approximate based on an average block creation
|
||||
interval of {g.mins_per_block} minutes.
|
||||
|
||||
Addresses on the command line can be Bitcoin addresses or {pnm} addresses
|
||||
of the form <seed ID>:<number>.
|
||||
|
||||
To send all inputs (minus TX fee) to a single output, specify one address
|
||||
with no amount on the command line.
|
||||
""".format(g=g,pnm=g.proj_name)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
|
||||
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 = {},[],{},[],""
|
||||
|
||||
addrfiles = [a for a in cmd_args if get_extension(a) == g.addrfile_ext]
|
||||
cmd_args = set(cmd_args) - set(addrfiles)
|
||||
|
||||
for a in addrfiles:
|
||||
check_infile(a)
|
||||
addr_data.append(parse_addrs_file(a))
|
||||
|
||||
def mmaddr2btcaddr(c,mmaddr,acct_data,addr_data,b2m_map):
|
||||
# assume mmaddr has already been checked
|
||||
btcaddr,label = mmaddr2btcaddr_bitcoind(c,mmaddr,acct_data)
|
||||
if not btcaddr:
|
||||
if addr_data:
|
||||
btcaddr,label = mmaddr2btcaddr_addrfile(mmaddr,addr_data)
|
||||
else:
|
||||
msg(txmsg['addrfile_no_data_msg'] % mmaddr)
|
||||
sys.exit(2)
|
||||
|
||||
b2m_map[btcaddr] = mmaddr,label
|
||||
return btcaddr
|
||||
|
||||
for a in cmd_args:
|
||||
if "," in a:
|
||||
a1,a2 = a.split(",")
|
||||
if is_btc_addr(a1):
|
||||
btcaddr = a1
|
||||
elif is_mmgen_addr(a1):
|
||||
btcaddr = mmaddr2btcaddr(c,a1,acct_data,addr_data,b2m_map)
|
||||
else:
|
||||
msg("%s: unrecognized subargument in argument '%s'" % (a1,a))
|
||||
sys.exit(2)
|
||||
|
||||
if is_btc_amt(a2):
|
||||
tx_out[btcaddr] = normalize_btc_amt(a2)
|
||||
else:
|
||||
msg("%s: invalid amount in argument '%s'" % (a2,a))
|
||||
sys.exit(2)
|
||||
elif is_mmgen_addr(a) or is_btc_addr(a):
|
||||
if change_addr:
|
||||
msg("ERROR: More than one change address specified: %s, %s" %
|
||||
(change_addr, a))
|
||||
sys.exit(2)
|
||||
change_addr = a if is_btc_addr(a) else \
|
||||
mmaddr2btcaddr(c,a,acct_data,addr_data,b2m_map)
|
||||
tx_out[change_addr] = 0
|
||||
else:
|
||||
msg("%s: unrecognized argument" % a)
|
||||
sys.exit(2)
|
||||
|
||||
if not tx_out:
|
||||
msg("At least one output must be specified on the command line")
|
||||
sys.exit(2)
|
||||
|
||||
tx_fee = opts['tx_fee'] if 'tx_fee' in opts else g.tx_fee
|
||||
tx_fee = normalize_btc_amt(tx_fee)
|
||||
if tx_fee > g.max_tx_fee:
|
||||
msg("Transaction fee too large: %s > %s" % (tx_fee,g.max_tx_fee))
|
||||
sys.exit(2)
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
#write_to_file("bogus_unspent.json", repr(us), opts); sys.exit()
|
||||
|
||||
#if False:
|
||||
if g.bogus_wallet_data:
|
||||
import mmgen.rpc
|
||||
us = eval(get_data_from_file(g.bogus_wallet_data))
|
||||
else:
|
||||
us = c.listunspent()
|
||||
|
||||
if not us: msg(txmsg['no_spendable_outputs']); sys.exit(2)
|
||||
|
||||
unspent = sort_and_view(us,opts)
|
||||
|
||||
total = trim_exponent(sum([i.amount for i in unspent]))
|
||||
|
||||
msg("Total unspent: %s BTC (%s outputs)" % (total, len(unspent)))
|
||||
if 'info' in opts: sys.exit(0)
|
||||
|
||||
send_amt = sum([tx_out[i] for i in tx_out.keys()])
|
||||
msg("Total amount to spend: %s%s" % (
|
||||
(send_amt or "Unknown")," BTC" if send_amt else ""))
|
||||
|
||||
while True:
|
||||
sel_nums = select_outputs(unspent,
|
||||
"Enter a range or space-separated list of outputs to spend: ")
|
||||
msg("Selected output%s: %s" %
|
||||
(("" if len(sel_nums) == 1 else "s"), " ".join(str(i) for i in sel_nums))
|
||||
)
|
||||
sel_unspent = [unspent[i-1] for i in sel_nums]
|
||||
|
||||
mmaddrs = set([parse_mmgen_label(i.account)[0] for i in sel_unspent])
|
||||
mmaddrs.discard("")
|
||||
|
||||
if mmaddrs and len(mmaddrs) < len(sel_unspent):
|
||||
msg(txmsg['mixed_inputs'] % ", ".join(sorted(mmaddrs)))
|
||||
if not user_confirm("Accept?"):
|
||||
continue
|
||||
|
||||
total_in = trim_exponent(sum([i.amount for i in sel_unspent]))
|
||||
change = trim_exponent(total_in - (send_amt + tx_fee))
|
||||
|
||||
if change >= 0:
|
||||
prompt = "Transaction produces %s BTC in change. OK?" % change
|
||||
if user_confirm(prompt,default_yes=True):
|
||||
break
|
||||
else:
|
||||
msg(txmsg['not_enough_btc'] % change)
|
||||
|
||||
if change > 0 and not change_addr:
|
||||
msg(txmsg['throwaway_change'] % change)
|
||||
sys.exit(2)
|
||||
|
||||
if change_addr in tx_out and not change:
|
||||
msg("Warning: Change address will be unused as transaction produces no change")
|
||||
del tx_out[change_addr]
|
||||
|
||||
for k,v in tx_out.items(): tx_out[k] = float(v)
|
||||
|
||||
if change > 0: tx_out[change_addr] = float(change)
|
||||
|
||||
tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
|
||||
|
||||
if g.debug:
|
||||
print "tx_in:", repr(tx_in)
|
||||
print "tx_out:", repr(tx_out)
|
||||
|
||||
tx_hex = c.createrawtransaction(tx_in,tx_out)
|
||||
qmsg("Transaction successfully created")
|
||||
prompt = "View decoded transaction? (y)es, (N)o, (v)iew in pager"
|
||||
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
|
||||
|
||||
if reply and reply in "YyVv":
|
||||
pager = True if reply in "Vv" else False
|
||||
view_tx_data(c,[i.__dict__ for i in sel_unspent],tx_hex,b2m_map,pager=pager)
|
||||
|
||||
prompt = "Save transaction?"
|
||||
if user_confirm(prompt,default_yes=True):
|
||||
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")
|
||||
import mmgen.main
|
||||
mmgen.main.main("txcreate")
|
||||
|
|
|
|||
72
mmgen-txsend
72
mmgen-txsend
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,72 +15,10 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-txsend: Broadcast a Bitcoin transaction to the network
|
||||
mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.tx import *
|
||||
from mmgen.util import msg,check_infile,get_lines_from_file,confirm_or_exit
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Send a Bitcoin transaction signed by {}-txsign".format(g.proj_name.lower()),
|
||||
'usage': "[opts] <signed transaction file>",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
infile = cmd_args[0]; check_infile(infile)
|
||||
else: usage(help_data)
|
||||
|
||||
# Begin execution
|
||||
|
||||
do_license_msg()
|
||||
|
||||
tx_data = get_lines_from_file(infile,"signed transaction data")
|
||||
|
||||
metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,infile)
|
||||
|
||||
qmsg("Signed transaction file '%s' is valid" % infile)
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
prompt = "View transaction data? (y)es, (N)o, (v)iew in pager"
|
||||
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
|
||||
if reply and reply in "YyVv":
|
||||
p = True if reply in "Vv" else False
|
||||
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata,pager=p)
|
||||
|
||||
warn = "Once this transaction is sent, there's no taking it back!"
|
||||
what = "broadcast this transaction to the network"
|
||||
expect = "YES, I REALLY WANT TO DO THIS"
|
||||
|
||||
if g.quiet: warn,expect = "","YES"
|
||||
|
||||
confirm_or_exit(warn, what, expect)
|
||||
|
||||
msg("Sending transaction")
|
||||
|
||||
try:
|
||||
tx_id = c.sendrawtransaction(tx_hex)
|
||||
except:
|
||||
msg("Unable to send transaction")
|
||||
sys.exit(3)
|
||||
|
||||
msg("Transaction sent: %s" % tx_id)
|
||||
|
||||
of = "tx_{}[{}].out".format(*metadata[:2])
|
||||
write_to_file(of, tx_id+"\n",opts,"transaction ID",True,True)
|
||||
import mmgen.main
|
||||
mmgen.main.main("txsend")
|
||||
|
|
|
|||
198
mmgen-txsign
198
mmgen-txsign
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,198 +15,10 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-txsign: Sign a Bitcoin transaction generated by mmgen-txcreate
|
||||
mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.tx import *
|
||||
from mmgen.util import msg,qmsg
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Sign Bitcoin transactions generated by {}-txcreate".format(g.proj_name.lower()),
|
||||
'usage': "[opts] <transaction file> .. [mmgen wallet/seed/words/brainwallet file] .. [addrfile] ..",
|
||||
'options': """
|
||||
-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
|
||||
-i, --info Display information about the transaction and exit
|
||||
-I, --tx-id Display transaction ID and exit
|
||||
-k, --keys-from-file= f Provide additional keys for non-{pnm} addresses
|
||||
-K, --all-keys-from-file=f Like '-k', only use the keyfile as key source
|
||||
for ALL inputs, including {pnm} ones. Can be used
|
||||
for online signing without an {pnm} seed source.
|
||||
{pnm}-to-BTC mappings can optionally be verified
|
||||
using address file(s) listed on the command line
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-V, --skip-key-preverify Skip optional key pre-verification step
|
||||
-b, --from-brain= l,p Generate keys from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p'
|
||||
-w, --use-wallet-dat Get keys from a running bitcoind
|
||||
-g, --from-incog Generate keys from an incognito wallet
|
||||
-X, --from-incog-hex Generate keys from an incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate keys from an electrum-like mnemonic
|
||||
-s, --from-seed Generate keys from a seed in .{g.seed_ext} format
|
||||
""".format(g=g,pnm=g.proj_name),
|
||||
'notes': """
|
||||
|
||||
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
|
||||
{pnl}-addrgen and {pnl}-keygen utilities.
|
||||
|
||||
Data for the --from-<what> options will be taken from a file if a second
|
||||
file is specified on the command line. Otherwise, the user will be
|
||||
prompted to enter the data.
|
||||
|
||||
In cases of transactions with mixed {pnm} and non-{pnm} inputs, non-{pnm}
|
||||
keys must be supplied in a separate file (WIF format, one key per line)
|
||||
using the '--keys-from-file' option. Alternatively, one may get keys from
|
||||
a running bitcoind using the '--force-wallet-dat' option. First import the
|
||||
required {pnm} keys using 'bitcoind importprivkey'.
|
||||
|
||||
For transaction outputs that are {pnm} addresses, {pnm}-to-Bitcoin address
|
||||
mappings are verified. Therefore, seed material for these addresses must
|
||||
be supplied on the command line (but see '--all-keys-from-file').
|
||||
|
||||
Seed data supplied in files must have the following extensions:
|
||||
wallet: '.{g.wallet_ext}'
|
||||
seed: '.{g.seed_ext}'
|
||||
mnemonic: '.{g.mn_ext}'
|
||||
brainwallet: '.{g.brain_ext}'
|
||||
""".format(g=g,pnm=g.proj_name,pnl=g.proj_name.lower())
|
||||
}
|
||||
|
||||
opts,infiles = parse_opts(sys.argv,help_data)
|
||||
|
||||
for l in (
|
||||
('tx_id', 'info'),
|
||||
('keys_from_file','all_keys_from_file')
|
||||
): warn_incompatible_opts(opts,l)
|
||||
|
||||
if "quiet" in opts: g.quiet = True
|
||||
if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
|
||||
opts['from_incog'] = True
|
||||
if 'all_keys_from_file' in opts:
|
||||
opts['keys_from_file'] = opts['all_keys_from_file']
|
||||
opts['skip_key_preverify'] = True
|
||||
|
||||
if not infiles: usage(help_data)
|
||||
for i in infiles: check_infile(i)
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
saved_seeds = {}
|
||||
tx_files = [i for i in set(infiles) if get_extension(i) == g.rawtx_ext]
|
||||
addrfiles = [a for a in set(infiles) if get_extension(a) == g.addrfile_ext]
|
||||
infiles = list(set(infiles) - set(tx_files) - set(addrfiles))
|
||||
|
||||
if not "info" in opts: do_license_msg(immed=True)
|
||||
|
||||
if 'keys_from_file' in opts:
|
||||
from mmgen.crypto import mmgen_decrypt
|
||||
fn = opts['keys_from_file']
|
||||
if get_extension(fn) == g.mmenc_ext:
|
||||
enc_d = get_data_from_file(fn,"encrypted keylist")
|
||||
dec_d = mmgen_decrypt(enc_d,"",opts)
|
||||
if dec_d:
|
||||
keys_from_file = remove_comments(dec_d.split("\n"))
|
||||
else:
|
||||
msg("Decryption of encrypted keylist failed")
|
||||
sys.exit(2)
|
||||
else:
|
||||
keys_from_file = get_lines_from_file(fn,"key data",trim_comments=True)
|
||||
else: keys_from_file = []
|
||||
|
||||
for tx_file in tx_files:
|
||||
m = "" if 'tx_id' in opts else "transaction data"
|
||||
tx_data = get_lines_from_file(tx_file,m)
|
||||
|
||||
metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,tx_file)
|
||||
qmsg("Successfully opened transaction file '%s'" % 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,b2m_map,metadata)
|
||||
sys.exit(0)
|
||||
|
||||
# Are inputs mmgen addresses?
|
||||
mmgen_inputs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
|
||||
other_inputs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
|
||||
|
||||
if 'all_keys_from_file' in opts: other_inputs = inputs_data
|
||||
|
||||
keys = keys_from_file
|
||||
|
||||
if other_inputs and not keys and not 'use_wallet_dat' in opts:
|
||||
missing_keys_errormsg(other_inputs)
|
||||
sys.exit(2)
|
||||
|
||||
if other_inputs and keys and not 'skip_key_preverify' in opts:
|
||||
a = [i['address'] for i in other_inputs]
|
||||
preverify_keys(a, keys)
|
||||
opts['skip_key_preverify'] = True
|
||||
|
||||
if 'all_keys_from_file' in opts:
|
||||
if addrfiles:
|
||||
check_mmgen_to_btc_addr_mappings_addrfile(mmgen_inputs,b2m_map,addrfiles)
|
||||
else:
|
||||
confirm_or_exit(txmsg['skip_mapping_checks_warning'],"continue")
|
||||
else:
|
||||
check_mmgen_to_btc_addr_mappings(
|
||||
mmgen_inputs,b2m_map,infiles,saved_seeds,opts)
|
||||
|
||||
if len(tx_files) > 1:
|
||||
msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files)))
|
||||
|
||||
prompt = "View transaction data? (y)es, (N)o, (v)iew in pager"
|
||||
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
|
||||
if reply and reply in "YyVv":
|
||||
p = True if reply in "Vv" else False
|
||||
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata,pager=p)
|
||||
|
||||
sig_data = [
|
||||
{"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
|
||||
for i in inputs_data]
|
||||
|
||||
if mmgen_inputs and not 'all_keys_from_file' in opts:
|
||||
ml = [i['account'].split()[0] for i in mmgen_inputs]
|
||||
keys += get_keys_for_mmgen_addrs(ml,infiles,saved_seeds,opts)
|
||||
|
||||
if 'use_wallet_dat' in opts:
|
||||
sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
|
||||
else:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
elif other_inputs:
|
||||
if keys:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
else:
|
||||
sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
|
||||
|
||||
if sig_tx['complete']:
|
||||
prompt = "OK\nSave signed transaction?"
|
||||
if user_confirm(prompt,default_yes=True):
|
||||
outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext)
|
||||
data = "{}\n{}\n{}\n{}\n".format(
|
||||
" ".join(metadata[:2] + [make_timestamp()]),
|
||||
sig_tx['hex'],
|
||||
repr(inputs_data),
|
||||
repr(b2m_map)
|
||||
)
|
||||
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)
|
||||
import mmgen.main
|
||||
mmgen.main.main("txsign")
|
||||
|
|
|
|||
103
mmgen-walletchk
103
mmgen-walletchk
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,102 +15,11 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletchk: Check integrity of a mmgen deterministic wallet, display
|
||||
information about it and export seed and mnemonic data
|
||||
mmgen-walletchk: Check integrity of an MMGen deterministic wallet, display
|
||||
information about it and export it to various formats
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import get_seed_from_wallet,wallet_to_incog_data
|
||||
|
||||
help_data = {
|
||||
'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),
|
||||
'usage': "[opts] [filename]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-S, --stdout Print seed or mnemonic data to standard output
|
||||
-v, --verbose Produce more verbose output
|
||||
-g, --export-incog Export wallet to incognito format
|
||||
-X, --export-incog-hex Export wallet to incognito hexadecimal format
|
||||
-G, --export-incog-hidden=f,o Hide incognito data in existing file 'f'
|
||||
at offset 'o' (comma-separated)
|
||||
-m, --export-mnemonic Export the wallet's mnemonic to file
|
||||
-s, --export-seed Export the wallet's seed to file
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
Since good randomness is particularly important for incognito wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user when one of the '--export-incog*' options is
|
||||
selected. If you fully trust your OS's random number generator and wish
|
||||
to disable this option, then specify '-r0' on the command line.
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
if 'verbose' in opts: g.verbose = True
|
||||
if 'export_incog_hidden' in opts or 'export_incog_hex' in opts:
|
||||
opts['export_incog'] = True
|
||||
|
||||
if len(cmd_args) != 1: usage(help_data)
|
||||
|
||||
check_infile(cmd_args[0])
|
||||
|
||||
if 'export_mnemonic' in opts:
|
||||
qmsg("Exporting mnemonic data to file by user request")
|
||||
elif 'export_seed' in opts:
|
||||
qmsg("Exporting seed data to file by user request")
|
||||
elif 'export_incog' in opts:
|
||||
if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
|
||||
qmsg("Exporting wallet to incognito format by user request")
|
||||
incog_enc,seed_id,key_id,iv_id,preset = \
|
||||
wallet_to_incog_data(cmd_args[0],opts)
|
||||
|
||||
if "export_incog_hidden" in opts:
|
||||
export_to_hidden_incog(incog_enc,opts)
|
||||
else:
|
||||
seed_len = (len(incog_enc)-g.salt_len-g.aesctr_iv_len)*8
|
||||
fn = "%s-%s-%s[%s,%s].%s" % (
|
||||
seed_id, key_id, iv_id, seed_len, preset,
|
||||
g.incog_hex_ext if "export_incog_hex" in opts else g.incog_ext
|
||||
)
|
||||
data = pretty_hexdump(incog_enc,2,8,line_nums=False) \
|
||||
if "export_incog_hex" in opts else incog_enc
|
||||
export_to_file(fn, data, opts, "incognito wallet data")
|
||||
|
||||
sys.exit()
|
||||
|
||||
seed = get_seed_from_wallet(cmd_args[0], opts)
|
||||
if seed: qmsg("Wallet is OK")
|
||||
else:
|
||||
msg("Error opening wallet")
|
||||
sys.exit(2)
|
||||
|
||||
if 'export_mnemonic' in opts:
|
||||
wl = get_default_wordlist()
|
||||
from mmgen.mnemonic import get_mnemonic_from_seed
|
||||
p = True if g.debug else False
|
||||
mn = get_mnemonic_from_seed(seed, wl, g.default_wl, print_info=p)
|
||||
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.mn_ext)
|
||||
export_to_file(fn, " ".join(mn)+"\n", opts, "mnemonic data")
|
||||
|
||||
elif 'export_seed' in opts:
|
||||
from mmgen.bitcoin import b58encode_pad
|
||||
data = col4(b58encode_pad(seed))
|
||||
chk = make_chksum_6(b58encode_pad(seed))
|
||||
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
|
||||
export_to_file(fn, "%s %s\n" % (chk,data), opts, "seed data")
|
||||
import mmgen.main
|
||||
mmgen.main.main("walletchk")
|
||||
|
|
|
|||
136
mmgen-walletgen
136
mmgen-walletgen
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,136 +15,10 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletgen: Generate a mmgen deterministic wallet
|
||||
mmgen-walletgen: Generate an MMGen deterministic wallet
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
from hashlib import sha256
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Generate an {} deterministic wallet".format(g.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 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: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-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: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Produce quieter output; overwrite files without
|
||||
prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
|
||||
-b, --from-brain= l,p Generate wallet from a user-created passphrase,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate wallet from an incognito-format wallet
|
||||
-m, --from-mnemonic Generate wallet from an Electrum-like mnemonic
|
||||
-s, --from-seed Generate wallet from a seed in .{g.seed_ext} format
|
||||
""".format(seed_lens=",".join([str(i) for i in g.seed_lens]), g=g),
|
||||
'notes': """
|
||||
|
||||
By default (i.e. when invoked without any of the '--from-<what>' options),
|
||||
{g.prog_name} generates a wallet based on a random seed.
|
||||
|
||||
Data for the --from-<what> options will be taken from <infile> if <infile>
|
||||
is specified. Otherwise, the user will be prompted to enter the data.
|
||||
|
||||
For passphrases all combinations of whitespace are equal, and leading and
|
||||
trailing space are ignored. This permits reading passphrase data from a
|
||||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
|
||||
Since good randomness is particularly important when generating wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user. If you fully trust your OS's random number gener-
|
||||
ator and wish to disable this option, specify '-r0' on the command line.
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
attacks, the brainwallet hash preset must be specified by the user, using
|
||||
the 'p' parameter of the '--from-brain' option. This preset should be
|
||||
stronger than the one used for hashing the seed (i.e. the default value or
|
||||
the one specified in the '--hash-preset' option).
|
||||
|
||||
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.
|
||||
""".format(g=g)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'quiet' in opts: g.quiet = True
|
||||
if 'verbose' in opts: g.verbose = True
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
infile = cmd_args[0]
|
||||
check_infile(infile)
|
||||
ext = infile.split(".")[-1]
|
||||
ok_exts = g.seedfile_exts
|
||||
for e in ok_exts:
|
||||
if e == ext: break
|
||||
else:
|
||||
msg(
|
||||
"Input file must have one of the following extensions: .%s" % ", .".join(ok_exts))
|
||||
sys.exit(1)
|
||||
elif len(cmd_args) == 0:
|
||||
infile = ""
|
||||
else: usage(help_data)
|
||||
|
||||
# Begin execution
|
||||
|
||||
do_license_msg()
|
||||
|
||||
if 'from_brain' in opts and not g.quiet:
|
||||
confirm_or_exit(cmessages['brain_warning'].format(
|
||||
g.proj_name, *get_from_brain_opt_params(opts)),
|
||||
"continue")
|
||||
|
||||
for i in 'from_mnemonic','from_brain','from_seed','from_incog':
|
||||
if infile or (i in opts):
|
||||
seed = get_seed_retry(infile,opts)
|
||||
if "from_incog" in opts or get_extension(infile) == g.incog_ext:
|
||||
qmsg(cmessages['incog'] % make_chksum_8(seed))
|
||||
else: qmsg("")
|
||||
break
|
||||
else:
|
||||
# Truncate random data for smaller seed lengths
|
||||
seed = sha256(get_random(128,opts)).digest()[:opts['seed_len']/8]
|
||||
|
||||
salt = sha256(get_random(128,opts)).digest()[:g.salt_len]
|
||||
|
||||
qmsg(cmessages['choose_wallet_passphrase'] % opts['hash_preset'])
|
||||
|
||||
passwd = get_new_passphrase("{} wallet passphrase".format(g.proj_name), opts)
|
||||
|
||||
key = make_key(passwd, salt, opts['hash_preset'])
|
||||
|
||||
enc_seed = encrypt_seed(seed, key)
|
||||
|
||||
write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)
|
||||
import mmgen.main
|
||||
mmgen.main.main("walletgen")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -16,6 +16,10 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Opts.py: Option handling routines for the MMGen suite
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.config as g
|
||||
import mmgen.opt.Opts
|
||||
|
|
@ -65,6 +69,9 @@ def parse_opts(argv,help_data):
|
|||
if v in opts: typeconvert_override_var(opts,v)
|
||||
else: opts[v] = eval("g."+v)
|
||||
|
||||
if "verbose" in opts: g.verbose = True
|
||||
if "quiet" in opts: g.quiet = True
|
||||
|
||||
if g.debug: print "opts after typeconvert: %s" % opts
|
||||
|
||||
return opts,args
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,10 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
MMGen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
MMGen = Multi-Mode GENerator, a Bitcoin cold storage/tracking solution for
|
||||
the command line
|
||||
"""
|
||||
__all__ = [
|
||||
'rpc',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
addr.py: Address generation/display routines for mmgen suite
|
||||
addr.py: Address generation/display routines for the MMGen suite
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
@ -68,8 +69,8 @@ def generate_addrs(seed, addrnums, opts):
|
|||
from subprocess import Popen, PIPE
|
||||
keyconv = "keyconv"
|
||||
|
||||
fmt = "num addr" if opts['gen_what'] == ("addrs") else (
|
||||
"num sec wif" if opts['gen_what'] == ("keys") else "num sec wif addr")
|
||||
fmt = "num addr" if opts['gen_what'] == ["addrs"] else (
|
||||
"num sec wif" if opts['gen_what'] == ["keys"] else "num sec wif addr")
|
||||
|
||||
from collections import namedtuple
|
||||
addrinfo = namedtuple("addrinfo",fmt)
|
||||
|
|
@ -78,7 +79,9 @@ def generate_addrs(seed, addrnums, opts):
|
|||
t_addrs,num,pos,out = len(addrnums),0,0,[]
|
||||
addrnums.sort() # needed only if caller didn't sort
|
||||
|
||||
try:
|
||||
ws = 'key' if 'keys' in opts['gen_what'] else 'address'
|
||||
if t_addrs != 1: wp = ws+"s" if ws == 'key' else ws+"es"
|
||||
|
||||
while pos != t_addrs:
|
||||
seed = sha512(seed).digest()
|
||||
num += 1 # round
|
||||
|
|
@ -88,8 +91,7 @@ def generate_addrs(seed, addrnums, opts):
|
|||
|
||||
pos += 1
|
||||
|
||||
qmsg_r("\rGenerating %s %s (%s of %s)" %
|
||||
(opts['gen_what'][-1],num,pos,t_addrs))
|
||||
qmsg_r("\rGenerating %s #%s (%s of %s)" % (ws,num,pos,t_addrs))
|
||||
|
||||
# Secret key is double sha256 of seed hash round /num/
|
||||
sec = sha256(sha256(seed).digest()).hexdigest()
|
||||
|
|
@ -101,14 +103,7 @@ def generate_addrs(seed, addrnums, opts):
|
|||
|
||||
out.append(eval("addrinfo("+addrinfo_args+")"))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
|
||||
w = 'key' if 'keys' in opts['gen_what'] else 'address'
|
||||
if t_addrs != 1: w = w+"s" if w == 'key' else w+"es"
|
||||
|
||||
qmsg("\rGenerated %s %s%s"%(t_addrs, w, " "*15))
|
||||
qmsg("\rGenerated %s %s%s"%(t_addrs, wp, " "*15))
|
||||
|
||||
return out
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# MMGen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
bitcoin.py: Bitcoin address/key conversion functions
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
config.py: Constants and configuration options for the mmgen suite
|
||||
config.py: Constants and configuration options for the MMGen suite
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
crypto.py: Cryptographic and related routines for the mmgen-tool utility
|
||||
crypto.py: Cryptographic and related routines for the 'mmgen-tool' utility
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
@ -368,27 +369,30 @@ def _get_seed_from_brain_passphrase(words,opts):
|
|||
# Vars for mmgen_*crypt functions only
|
||||
salt_len,sha256_len,nonce_len = 32,32,32
|
||||
|
||||
def mmgen_encrypt(data,hash_preset,opts):
|
||||
def mmgen_encrypt(data,what="data",hash_preset='3',opts={}):
|
||||
salt,iv,nonce = get_random(salt_len,opts),\
|
||||
get_random(g.aesctr_iv_len,opts), get_random(nonce_len,opts)
|
||||
hp,m = (hash_preset,"user-requested") if hash_preset else ('3',"default")
|
||||
vmsg("Encrypting %s" % what)
|
||||
qmsg("Using %s hash preset of '%s'" % (m,hp))
|
||||
passwd = get_new_passphrase("passphrase",{})
|
||||
key = make_key(passwd, salt, hp)
|
||||
enc_d = encrypt_data(sha256(nonce+data).digest() + nonce + data, key,
|
||||
int(hexlify(iv),16))
|
||||
int(hexlify(iv),16), what=what)
|
||||
return salt+iv+enc_d
|
||||
|
||||
|
||||
def mmgen_decrypt(data,hash_preset,opts):
|
||||
def mmgen_decrypt(data,what="data",hash_preset='3',opts={}):
|
||||
dstart = salt_len + g.aesctr_iv_len
|
||||
salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
|
||||
hp,m = (hash_preset,"user-requested") if hash_preset else ('3',"default")
|
||||
vmsg("Preparing to decrypt %s" % what)
|
||||
qmsg("Using %s hash preset of '%s'" % (m,hp))
|
||||
passwd = get_mmgen_passphrase("Enter passphrase: ",{})
|
||||
key = make_key(passwd, salt, hp)
|
||||
dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16))
|
||||
dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), what)
|
||||
if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
|
||||
vmsg("Success. Passphrase and hash preset are correct")
|
||||
return dec_d[sha256_len+nonce_len:]
|
||||
else:
|
||||
msg("Incorrect passphrase or hash preset")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
license.py: Show the license
|
||||
"""
|
||||
|
|
@ -594,6 +595,7 @@ def do_license_msg(immed=False):
|
|||
prompt = "%s " % gpl['prompt'].strip()
|
||||
|
||||
while True:
|
||||
from mmgen.util import my_raw_input
|
||||
reply = get_char(prompt, immed_chars="wc" if immed else "")
|
||||
if reply == 'w':
|
||||
from mmgen.term import do_pager
|
||||
|
|
|
|||
44
mmgen/main.py
Executable file
44
mmgen/main.py
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
main.py - Script launcher for the MMGen suite
|
||||
"""
|
||||
|
||||
import sys, termios
|
||||
from mmgen.util import msg
|
||||
|
||||
def main(progname):
|
||||
fd = sys.stdin.fileno()
|
||||
old = termios.tcgetattr(fd)
|
||||
try:
|
||||
if progname == "addrgen": import mmgen.main_addrgen
|
||||
elif progname == "addrimport": import mmgen.main_addrimport
|
||||
elif progname == "keygen": import mmgen.main_addrgen
|
||||
elif progname == "passchg": import mmgen.main_passchg
|
||||
elif progname == "pywallet": import mmgen.main_pywallet
|
||||
elif progname == "tool": import mmgen.main_tool
|
||||
elif progname == "txcreate": import mmgen.main_txcreate
|
||||
elif progname == "txsend": import mmgen.main_txsend
|
||||
elif progname == "txsign": import mmgen.main_txsign
|
||||
elif progname == "walletchk": import mmgen.main_walletchk
|
||||
elif progname == "walletgen": import mmgen.main_walletgen
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
sys.exit(1)
|
||||
191
mmgen/main_addrgen.py
Executable file
191
mmgen/main_addrgen.py
Executable file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-addrgen: Generate a series or range of addresses from an MMGen
|
||||
deterministic wallet
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
from mmgen.addr import *
|
||||
from mmgen.tx import make_addr_data_chksum
|
||||
|
||||
what = "keys" if sys.argv[0].split("-")[-1] == "keygen" else "addresses"
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': """Generate a list or range of {} from an {g.proj_name} wallet,
|
||||
mnemonic, seed or password""".format(what,g=g),
|
||||
'usage':"[opts] [infile] <address list>",
|
||||
'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
|
||||
instead of 'keyconv'
|
||||
-l, --seed-len= N Length of seed. Options: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when
|
||||
hashing password (default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-S, --stdout Print {what} to stdout
|
||||
-v, --verbose Produce more verbose output{}
|
||||
|
||||
-b, --from-brain= l,p Generate {what} from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate {what} from an incognito wallet
|
||||
-X, --from-incog-hex Generate {what} from incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden=f,o,l Generate {what} from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate {what} from an electrum-like mnemonic
|
||||
-s, --from-seed Generate {what} from a seed in .{g.seed_ext} format
|
||||
""".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 '{}-txsign'".format(g.proj_name.lower()),
|
||||
"\n-x, --b16 Print secret keys in hexadecimal too"
|
||||
)
|
||||
if what == "keys" else ("","","")),
|
||||
seed_lens=", ".join([str(i) for i in g.seed_lens]),
|
||||
what=what, g=g
|
||||
),
|
||||
'notes': """
|
||||
|
||||
Addresses are given in a comma-separated list. Hyphen-separated ranges are
|
||||
also allowed.{}
|
||||
|
||||
If available, the external 'keyconv' program will be used for address
|
||||
generation.
|
||||
|
||||
Data for the --from-<what> options will be taken from <infile> if <infile>
|
||||
is specified. Otherwise, the user will be prompted to enter the data.
|
||||
|
||||
For passphrases all combinations of whitespace are equal, and leading and
|
||||
trailing space are ignored. This permits reading passphrase data from a
|
||||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
attacks, the brainwallet hash preset must be specified by the user, using
|
||||
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
|
||||
""".format("\n\nBy default, both addresses and secret keys are generated."
|
||||
if what == "keys" else "")
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
|
||||
opts['from_incog'] = True
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
if len(cmd_args) == 1 and (
|
||||
'from_mnemonic' in opts
|
||||
or 'from_brain' in opts
|
||||
or 'from_seed' in opts
|
||||
or 'from_incog_hidden' in opts
|
||||
):
|
||||
infile,addr_idx_arg = "",cmd_args[0]
|
||||
elif len(cmd_args) == 2:
|
||||
infile,addr_idx_arg = cmd_args
|
||||
check_infile(infile)
|
||||
else: usage(help_data)
|
||||
|
||||
addr_idxs = parse_address_list(addr_idx_arg)
|
||||
|
||||
if not addr_idxs: sys.exit(2)
|
||||
|
||||
do_license_msg()
|
||||
|
||||
# Interact with user:
|
||||
if what == "keys" and not g.quiet:
|
||||
confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue')
|
||||
|
||||
# Generate data:
|
||||
|
||||
seed = get_seed_retry(infile,opts)
|
||||
seed_id = make_chksum_8(seed)
|
||||
|
||||
for l in (
|
||||
('flat_list', 'no_addresses'),
|
||||
('flat_list', 'b16'),
|
||||
): warn_incompatible_opts(opts,l)
|
||||
|
||||
opts['gen_what'] = \
|
||||
["addrs"] if what == "addresses" else (
|
||||
["keys"] if 'no_addresses' in opts else ["addrs","keys"])
|
||||
addr_data = generate_addrs(seed, addr_idxs, opts)
|
||||
addr_data_chksum = make_addr_data_chksum([(a.num,a.addr)
|
||||
for a in addr_data]) if 'addrs' in opts['gen_what'] 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))
|
||||
|
||||
if 'flat_list' in opts and user_confirm("Encrypt key list?"):
|
||||
hp = get_hash_preset_from_user('3')
|
||||
addr_data_str = mmgen_encrypt(addr_data_str,"key list",hp,opts)
|
||||
enc_ext = "." + g.mmenc_ext
|
||||
else: enc_ext = ""
|
||||
|
||||
# Output data:
|
||||
if 'stdout' in opts or not sys.stdout.isatty():
|
||||
if enc_ext and sys.stdout.isatty():
|
||||
msg("Cannot write encrypted data to screen. Exiting")
|
||||
sys.exit(2)
|
||||
c = True if (what == "keys" and not g.quiet and sys.stdout.isatty()) else False
|
||||
write_to_stdout(addr_data_str,what,c)
|
||||
else:
|
||||
confirm_overwrite = False if g.quiet else True
|
||||
outfile = "%s.%s%s" % (outfile_base, (
|
||||
g.keylist_ext if 'flat_list' in opts else (
|
||||
g.keyfile_ext if opts['gen_what'] == ("keys") else (
|
||||
g.addrfile_ext if opts['gen_what'] == ("addrs") else "akeys"))), enc_ext)
|
||||
write_to_file(outfile,addr_data_str,opts,what,confirm_overwrite,True)
|
||||
|
||||
if 'addrs' in opts['gen_what']:
|
||||
msg("Checksum for address data {}: {}".format(outfile_base,addr_data_chksum))
|
||||
if 'save_checksum' in opts:
|
||||
a = "address data checksum"
|
||||
write_to_file(outfile_base+".chk",addr_data_chksum,opts,a,False,True)
|
||||
else:
|
||||
qmsg("This checksum will be used to verify the address file in the future.")
|
||||
qmsg("Record it to a safe location.")
|
||||
140
mmgen/main_addrimport.py
Executable file
140
mmgen/main_addrimport.py
Executable file
|
|
@ -0,0 +1,140 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-addrimport: Import addresses into a MMGen bitcoind watching wallet
|
||||
"""
|
||||
|
||||
import sys
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.util import *
|
||||
from mmgen.tx import connect_to_bitcoind,parse_addrs_file
|
||||
|
||||
help_data = {
|
||||
'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]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-l, --addrlist= f Import the non-mmgen Bitcoin addresses listed in file 'f'
|
||||
-q, --quiet Suppress warnings
|
||||
-r, --rescan Rescan the blockchain. Required if address to import is
|
||||
on the blockchain and has a balance. Rescanning is slow.
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if len(cmd_args) != 1 and not 'addrlist' in opts:
|
||||
msg("You must specify an mmgen address list (and/or non-mmgen addresses with the '--addrlist' option)")
|
||||
sys.exit(1)
|
||||
|
||||
if cmd_args:
|
||||
check_infile(cmd_args[0])
|
||||
seed_id,addr_data = parse_addrs_file(cmd_args[0])
|
||||
else:
|
||||
seed_id,addr_data = "",[]
|
||||
|
||||
if 'addrlist' in opts:
|
||||
lines = get_lines_from_file(opts['addrlist'],"non-mmgen addresses",
|
||||
trim_comments=True)
|
||||
addr_data += [(None,l) for l in lines]
|
||||
|
||||
from mmgen.bitcoin import verify_addr
|
||||
qmsg_r("Validating addresses...")
|
||||
for i in addr_data:
|
||||
if not verify_addr(i[1],verbose=True):
|
||||
msg("%s: invalid address" % i)
|
||||
sys.exit(2)
|
||||
qmsg("OK")
|
||||
|
||||
import mmgen.config as g
|
||||
g.http_timeout = 3600
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
m = """
|
||||
WARNING: You've chosen the '--rescan' option. Rescanning the block chain is
|
||||
necessary only if an address you're importing is already on the block chain
|
||||
and has a balance. Note that the rescanning process is very slow (>30 min.
|
||||
for each imported address on a low-powered computer).
|
||||
""".strip() if "rescan" in opts else """
|
||||
WARNING: If any of the addresses you're importing is already on the block chain
|
||||
and has a balance, you must exit the program now and rerun it using the
|
||||
'--rescan' option. Otherwise you may ignore this message and continue.
|
||||
""".strip()
|
||||
|
||||
if g.quiet: m = ""
|
||||
confirm_or_exit(m, "continue", expect="YES")
|
||||
|
||||
err_flag = False
|
||||
|
||||
def import_address(addr,label,rescan):
|
||||
try:
|
||||
c.importaddress(addr,label,rescan)
|
||||
except:
|
||||
global err_flag
|
||||
err_flag = True
|
||||
|
||||
|
||||
w1 = len(str(len(addr_data))) * 2 + 2
|
||||
w2 = len(str(max([i[0] for i in addr_data if i[0]]))) + 12
|
||||
|
||||
if "rescan" in opts:
|
||||
import threading
|
||||
import time
|
||||
msg_fmt = "\r%s %-" + str(w1) + "s %-34s %-" + str(w2) + "s"
|
||||
else:
|
||||
msg_fmt = "\r%-" + str(w1) + "s %-34s %-" + str(w2) + "s"
|
||||
|
||||
msg("Importing addresses")
|
||||
for n,i in enumerate(addr_data):
|
||||
if i[0]:
|
||||
comment = " " + i[2] if len(i) == 3 else ""
|
||||
label = "%s:%s%s" % (seed_id,i[0],comment)
|
||||
else: label = "non-mmgen"
|
||||
|
||||
if "rescan" in opts:
|
||||
t = threading.Thread(target=import_address, args=(i[1],label,True))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
start = int(time.time())
|
||||
|
||||
while True:
|
||||
if t.is_alive():
|
||||
elapsed = int(time.time() - start)
|
||||
msg_r(msg_fmt % (
|
||||
secs_to_hms(elapsed),
|
||||
("%s/%s:" % (n+1,len(addr_data))),
|
||||
i[1], "(" + label + ")"
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
else:
|
||||
if err_flag: msg("\nImport failed"); sys.exit(2)
|
||||
msg("\nOK")
|
||||
break
|
||||
else:
|
||||
import_address(i[1],label,rescan=False)
|
||||
msg_r(msg_fmt % (("%s/%s:" % (n+1,len(addr_data))),
|
||||
i[1], "(" + label + ")"))
|
||||
if err_flag: msg("\nImport failed"); sys.exit(2)
|
||||
msg(" - OK")
|
||||
127
mmgen/main_passchg.py
Executable file
127
mmgen/main_passchg.py
Executable file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-passchg: Change an MMGen deterministic wallet's passphrase, label or
|
||||
hash preset
|
||||
"""
|
||||
|
||||
import sys
|
||||
from mmgen.Opts import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
import mmgen.config as g
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': """Change the passphrase, hash preset or label of an {}
|
||||
deterministic wallet""".format(g.proj_name),
|
||||
'usage': "[opts] [filename]",
|
||||
'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: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get new passphrase from file 'f'
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
NOTE: The key ID will change if either the passphrase or hash preset are
|
||||
changed
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
|
||||
if len(cmd_args) != 1:
|
||||
msg("One input file must be specified")
|
||||
sys.exit(2)
|
||||
infile = cmd_args[0]
|
||||
|
||||
# Old key:
|
||||
label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile)
|
||||
seed_id,key_id = metadata[:2]
|
||||
|
||||
# Repeat on incorrect pw entry
|
||||
prompt = "Enter %spassphrase: " % (""
|
||||
if 'keep_old_passphrase' in opts else "old ")
|
||||
while True:
|
||||
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
|
||||
|
||||
changed = {}
|
||||
|
||||
if 'label' in opts:
|
||||
if opts['label'] != label:
|
||||
msg("Label changed: '%s' -> '%s'" % (label, opts['label']))
|
||||
changed['label'] = True
|
||||
else:
|
||||
msg("Label is unchanged: '%s'" % (label))
|
||||
else: opts['label'] = label # Copy the old label
|
||||
|
||||
if 'hash_preset' in opts:
|
||||
if hash_preset != opts['hash_preset']:
|
||||
qmsg("Hash preset has changed (%s -> %s)" %
|
||||
(hash_preset, opts['hash_preset']))
|
||||
changed['preset'] = True
|
||||
else:
|
||||
msg("Hash preset is unchanged")
|
||||
else:
|
||||
opts['hash_preset'] = hash_preset
|
||||
|
||||
if 'keep_old_passphrase' in opts:
|
||||
msg("Keeping old passphrase by user request")
|
||||
else:
|
||||
new_passwd = get_new_passphrase("new passphrase", opts)
|
||||
|
||||
if new_passwd == passwd:
|
||||
qmsg("Passphrase is unchanged")
|
||||
else:
|
||||
qmsg("Passphrase has changed")
|
||||
passwd = new_passwd
|
||||
changed['passwd'] = True
|
||||
|
||||
if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
|
||||
qmsg("Will update salt and key ID")
|
||||
|
||||
from hashlib import sha256
|
||||
|
||||
salt = sha256(salt + get_random(128,opts)).digest()[:g.salt_len]
|
||||
key = make_key(passwd, salt, opts['hash_preset'])
|
||||
new_key_id = make_chksum_8(key)
|
||||
qmsg("Key ID changed: %s -> %s" % (key_id,new_key_id))
|
||||
key_id = new_key_id
|
||||
enc_seed = encrypt_seed(seed, key)
|
||||
elif not 'label' in changed:
|
||||
msg("Data unchanged. No file will be written")
|
||||
sys.exit(2)
|
||||
|
||||
write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts)
|
||||
1677
mmgen/main_pywallet.py
Executable file
1677
mmgen/main_pywallet.py
Executable file
File diff suppressed because it is too large
Load diff
70
mmgen/main_tool.py
Executable file
70
mmgen/main_tool.py
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-tool: Perform various Bitcoin-related operations.
|
||||
Part of the MMGen suite
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.config as g
|
||||
import mmgen.tool as tool
|
||||
from mmgen.Opts import *
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Perform various BTC-related operations",
|
||||
'usage': "[opts] <command> <command args>",
|
||||
'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
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
COMMANDS:{}
|
||||
Type '{} <command> --help for usage information on a particular
|
||||
command
|
||||
""".format(tool.command_help,g.prog_name)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if len(cmd_args) < 1:
|
||||
usage(help_data)
|
||||
sys.exit(1)
|
||||
|
||||
command = cmd_args.pop(0)
|
||||
|
||||
if command not in tool.commands.keys():
|
||||
msg("'%s': No such command" % command)
|
||||
sys.exit(1)
|
||||
|
||||
if cmd_args and cmd_args[0] == '--help':
|
||||
tool.tool_usage(g.prog_name, command)
|
||||
sys.exit(0)
|
||||
|
||||
args = tool.process_args(g.prog_name, command, cmd_args)
|
||||
|
||||
tool.opts = opts
|
||||
|
||||
#print command + "(" + ", ".join(args) + ")"
|
||||
eval("tool." + command + "(" + ", ".join(args) + ")")
|
||||
221
mmgen/main_txcreate.py
Executable file
221
mmgen/main_txcreate.py
Executable file
|
|
@ -0,0 +1,221 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-txcreate: Create a Bitcoin transaction from MMGen- or non-MMGen inputs
|
||||
to MMGen- or non-MMGen outputs
|
||||
"""
|
||||
|
||||
import sys
|
||||
from decimal import Decimal
|
||||
|
||||
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': g.prog_name,
|
||||
'desc': "Create a BTC transaction with outputs to specified addresses",
|
||||
'usage': "[opts] <addr,amt> ... [change addr] [addr file] ...",
|
||||
'options': """
|
||||
-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, --tx-fee= f Transaction fee (default: {g.tx_fee} BTC)
|
||||
-i, --info Display unspent outputs and exit
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
Transaction inputs are chosen from a list of the user's unpent outputs
|
||||
via an interactive menu.
|
||||
|
||||
Ages of transactions are approximate based on an average block creation
|
||||
interval of {g.mins_per_block} minutes.
|
||||
|
||||
Addresses on the command line can be Bitcoin addresses or {pnm} addresses
|
||||
of the form <seed ID>:<number>.
|
||||
|
||||
To send all inputs (minus TX fee) to a single output, specify one address
|
||||
with no amount on the command line.
|
||||
""".format(g=g,pnm=g.proj_name)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
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 = {},[],{},[],""
|
||||
|
||||
addrfiles = [a for a in cmd_args if get_extension(a) == g.addrfile_ext]
|
||||
cmd_args = set(cmd_args) - set(addrfiles)
|
||||
|
||||
for a in addrfiles:
|
||||
check_infile(a)
|
||||
addr_data.append(parse_addrs_file(a))
|
||||
|
||||
def mmaddr2btcaddr(c,mmaddr,acct_data,addr_data,b2m_map):
|
||||
# assume mmaddr has already been checked
|
||||
btcaddr,label = mmaddr2btcaddr_bitcoind(c,mmaddr,acct_data)
|
||||
if not btcaddr:
|
||||
if addr_data:
|
||||
btcaddr,label = mmaddr2btcaddr_addrfile(mmaddr,addr_data)
|
||||
else:
|
||||
msg(txmsg['addrfile_no_data_msg'] % mmaddr)
|
||||
sys.exit(2)
|
||||
|
||||
b2m_map[btcaddr] = mmaddr,label
|
||||
return btcaddr
|
||||
|
||||
for a in cmd_args:
|
||||
if "," in a:
|
||||
a1,a2 = a.split(",")
|
||||
if is_btc_addr(a1):
|
||||
btcaddr = a1
|
||||
elif is_mmgen_addr(a1):
|
||||
btcaddr = mmaddr2btcaddr(c,a1,acct_data,addr_data,b2m_map)
|
||||
else:
|
||||
msg("%s: unrecognized subargument in argument '%s'" % (a1,a))
|
||||
sys.exit(2)
|
||||
|
||||
if is_btc_amt(a2):
|
||||
tx_out[btcaddr] = normalize_btc_amt(a2)
|
||||
else:
|
||||
msg("%s: invalid amount in argument '%s'" % (a2,a))
|
||||
sys.exit(2)
|
||||
elif is_mmgen_addr(a) or is_btc_addr(a):
|
||||
if change_addr:
|
||||
msg("ERROR: More than one change address specified: %s, %s" %
|
||||
(change_addr, a))
|
||||
sys.exit(2)
|
||||
change_addr = a if is_btc_addr(a) else \
|
||||
mmaddr2btcaddr(c,a,acct_data,addr_data,b2m_map)
|
||||
tx_out[change_addr] = 0
|
||||
else:
|
||||
msg("%s: unrecognized argument" % a)
|
||||
sys.exit(2)
|
||||
|
||||
if not tx_out:
|
||||
msg("At least one output must be specified on the command line")
|
||||
sys.exit(2)
|
||||
|
||||
tx_fee = opts['tx_fee'] if 'tx_fee' in opts else g.tx_fee
|
||||
tx_fee = normalize_btc_amt(tx_fee)
|
||||
if tx_fee > g.max_tx_fee:
|
||||
msg("Transaction fee too large: %s > %s" % (tx_fee,g.max_tx_fee))
|
||||
sys.exit(2)
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
#write_to_file("bogus_unspent.json", repr(us), opts); sys.exit()
|
||||
|
||||
#if False:
|
||||
if g.bogus_wallet_data:
|
||||
import mmgen.rpc
|
||||
us = eval(get_data_from_file(g.bogus_wallet_data))
|
||||
else:
|
||||
us = c.listunspent()
|
||||
|
||||
if not us: msg(txmsg['no_spendable_outputs']); sys.exit(2)
|
||||
|
||||
unspent = sort_and_view(us,opts)
|
||||
|
||||
total = trim_exponent(sum([i.amount for i in unspent]))
|
||||
|
||||
msg("Total unspent: %s BTC (%s outputs)" % (total, len(unspent)))
|
||||
if 'info' in opts: sys.exit(0)
|
||||
|
||||
send_amt = sum([tx_out[i] for i in tx_out.keys()])
|
||||
msg("Total amount to spend: %s%s" % (
|
||||
(send_amt or "Unknown")," BTC" if send_amt else ""))
|
||||
|
||||
while True:
|
||||
sel_nums = select_outputs(unspent,
|
||||
"Enter a range or space-separated list of outputs to spend: ")
|
||||
msg("Selected output%s: %s" %
|
||||
(("" if len(sel_nums) == 1 else "s"), " ".join(str(i) for i in sel_nums))
|
||||
)
|
||||
sel_unspent = [unspent[i-1] for i in sel_nums]
|
||||
|
||||
mmaddrs = set([parse_mmgen_label(i.account)[0] for i in sel_unspent])
|
||||
mmaddrs.discard("")
|
||||
|
||||
if mmaddrs and len(mmaddrs) < len(sel_unspent):
|
||||
msg(txmsg['mixed_inputs'] % ", ".join(sorted(mmaddrs)))
|
||||
if not user_confirm("Accept?"):
|
||||
continue
|
||||
|
||||
total_in = trim_exponent(sum([i.amount for i in sel_unspent]))
|
||||
change = trim_exponent(total_in - (send_amt + tx_fee))
|
||||
|
||||
if change >= 0:
|
||||
prompt = "Transaction produces %s BTC in change. OK?" % change
|
||||
if user_confirm(prompt,default_yes=True):
|
||||
break
|
||||
else:
|
||||
msg(txmsg['not_enough_btc'] % change)
|
||||
|
||||
if change > 0 and not change_addr:
|
||||
msg(txmsg['throwaway_change'] % change)
|
||||
sys.exit(2)
|
||||
|
||||
if change_addr in tx_out and not change:
|
||||
msg("Warning: Change address will be unused as transaction produces no change")
|
||||
del tx_out[change_addr]
|
||||
|
||||
for k,v in tx_out.items(): tx_out[k] = float(v)
|
||||
|
||||
if change > 0: tx_out[change_addr] = float(change)
|
||||
|
||||
tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
|
||||
|
||||
if g.debug:
|
||||
print "tx_in:", repr(tx_in)
|
||||
print "tx_out:", repr(tx_out)
|
||||
|
||||
tx_hex = c.createrawtransaction(tx_in,tx_out)
|
||||
qmsg("Transaction successfully created")
|
||||
prompt = "View decoded transaction? (y)es, (N)o, (v)iew in pager"
|
||||
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
|
||||
|
||||
if reply and reply in "YyVv":
|
||||
pager = True if reply in "Vv" else False
|
||||
view_tx_data(c,[i.__dict__ for i in sel_unspent],tx_hex,b2m_map,pager=pager)
|
||||
|
||||
prompt = "Save transaction?"
|
||||
if user_confirm(prompt,default_yes=True):
|
||||
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")
|
||||
85
mmgen/main_txsend.py
Executable file
85
mmgen/main_txsend.py
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.tx import *
|
||||
from mmgen.util import msg,check_infile,get_lines_from_file,confirm_or_exit
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Send a Bitcoin transaction signed by {}-txsign".format(g.proj_name.lower()),
|
||||
'usage': "[opts] <signed transaction file>",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
infile = cmd_args[0]; check_infile(infile)
|
||||
else: usage(help_data)
|
||||
|
||||
# Begin execution
|
||||
|
||||
do_license_msg()
|
||||
|
||||
tx_data = get_lines_from_file(infile,"signed transaction data")
|
||||
|
||||
metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,infile)
|
||||
|
||||
qmsg("Signed transaction file '%s' is valid" % infile)
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
prompt = "View transaction data? (y)es, (N)o, (v)iew in pager"
|
||||
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
|
||||
if reply and reply in "YyVv":
|
||||
p = True if reply in "Vv" else False
|
||||
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata,pager=p)
|
||||
|
||||
warn = "Once this transaction is sent, there's no taking it back!"
|
||||
what = "broadcast this transaction to the network"
|
||||
expect = "YES, I REALLY WANT TO DO THIS"
|
||||
|
||||
if g.quiet: warn,expect = "","YES"
|
||||
|
||||
confirm_or_exit(warn, what, expect)
|
||||
|
||||
msg("Sending transaction")
|
||||
|
||||
try:
|
||||
tx_id = c.sendrawtransaction(tx_hex)
|
||||
except:
|
||||
msg("Unable to send transaction")
|
||||
sys.exit(3)
|
||||
|
||||
msg("Transaction sent: %s" % tx_id)
|
||||
|
||||
of = "tx_{}[{}].out".format(*metadata[:2])
|
||||
write_to_file(of, tx_id+"\n",opts,"transaction ID",True,True)
|
||||
213
mmgen/main_txsign.py
Executable file
213
mmgen/main_txsign.py
Executable file
|
|
@ -0,0 +1,213 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.tx import *
|
||||
from mmgen.util import msg,qmsg
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Sign Bitcoin transactions generated by {}-txcreate".format(g.proj_name.lower()),
|
||||
'usage': "[opts] <transaction file> .. [mmgen wallet/seed/words/brainwallet file] .. [addrfile] ..",
|
||||
'options': """
|
||||
-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
|
||||
-i, --info Display information about the transaction and exit
|
||||
-I, --tx-id Display transaction ID and exit
|
||||
-k, --keys-from-file= f Provide additional keys for non-{pnm} addresses
|
||||
-K, --all-keys-from-file=f Like '-k', only use the keyfile as key source
|
||||
for ALL inputs, including {pnm} ones. Can be used
|
||||
for online signing without an {pnm} seed source.
|
||||
{pnm}-to-BTC mappings can optionally be verified
|
||||
using address file(s) listed on the command line
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
-V, --skip-key-preverify Skip optional key pre-verification step
|
||||
-b, --from-brain= l,p Generate keys from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p'
|
||||
-w, --use-wallet-dat Get keys from a running bitcoind
|
||||
-g, --from-incog Generate keys from an incognito wallet
|
||||
-X, --from-incog-hex Generate keys from an incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate keys from an electrum-like mnemonic
|
||||
-s, --from-seed Generate keys from a seed in .{g.seed_ext} format
|
||||
""".format(g=g,pnm=g.proj_name),
|
||||
'notes': """
|
||||
|
||||
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
|
||||
{pnl}-addrgen and {pnl}-keygen utilities.
|
||||
|
||||
Data for the --from-<what> options will be taken from a file if a second
|
||||
file is specified on the command line. Otherwise, the user will be
|
||||
prompted to enter the data.
|
||||
|
||||
In cases of transactions with mixed {pnm} and non-{pnm} inputs, non-{pnm}
|
||||
keys must be supplied in a separate file (WIF format, one key per line)
|
||||
using the '--keys-from-file' option. Alternatively, one may get keys from
|
||||
a running bitcoind using the '--force-wallet-dat' option. First import the
|
||||
required {pnm} keys using 'bitcoind importprivkey'.
|
||||
|
||||
For transaction outputs that are {pnm} addresses, {pnm}-to-Bitcoin address
|
||||
mappings are verified. Therefore, seed material for these addresses must
|
||||
be supplied on the command line (but see '--all-keys-from-file').
|
||||
|
||||
Seed data supplied in files must have the following extensions:
|
||||
wallet: '.{g.wallet_ext}'
|
||||
seed: '.{g.seed_ext}'
|
||||
mnemonic: '.{g.mn_ext}'
|
||||
brainwallet: '.{g.brain_ext}'
|
||||
""".format(g=g,pnm=g.proj_name,pnl=g.proj_name.lower())
|
||||
}
|
||||
|
||||
opts,infiles = parse_opts(sys.argv,help_data)
|
||||
|
||||
for l in (
|
||||
('tx_id', 'info'),
|
||||
('keys_from_file','all_keys_from_file')
|
||||
): warn_incompatible_opts(opts,l)
|
||||
|
||||
if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
|
||||
opts['from_incog'] = True
|
||||
if 'all_keys_from_file' in opts:
|
||||
opts['keys_from_file'] = opts['all_keys_from_file']
|
||||
# opts['skip_key_preverify'] = True
|
||||
|
||||
if not infiles: usage(help_data)
|
||||
for i in infiles: check_infile(i)
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
saved_seeds = {}
|
||||
tx_files = [i for i in set(infiles) if get_extension(i) == g.rawtx_ext]
|
||||
addrfiles = [a for a in set(infiles) if get_extension(a) == g.addrfile_ext]
|
||||
infiles = list(set(infiles) - set(tx_files) - set(addrfiles))
|
||||
|
||||
if not "info" in opts: do_license_msg(immed=True)
|
||||
|
||||
if 'keys_from_file' in opts:
|
||||
from mmgen.crypto import mmgen_decrypt
|
||||
fn = opts['keys_from_file']
|
||||
d = get_data_from_file(fn,"keylist")
|
||||
if get_extension(fn) == g.mmenc_ext or not \
|
||||
is_b58_str(remove_comments(d.split("\n"))[0][:55]):
|
||||
qmsg("Keylist appears to be encrypted")
|
||||
while True:
|
||||
hp = get_hash_preset_from_user('3')
|
||||
d_dec = mmgen_decrypt(d,"encrypted keylist",hp,opts)
|
||||
if d_dec: d = d_dec; break
|
||||
else: msg("Trying again...")
|
||||
keys_from_file = remove_comments(d.split("\n"))
|
||||
else: keys_from_file = []
|
||||
|
||||
for tx_file in tx_files:
|
||||
m = "" if 'tx_id' in opts else "transaction data"
|
||||
tx_data = get_lines_from_file(tx_file,m)
|
||||
|
||||
metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,tx_file)
|
||||
qmsg("Successfully opened transaction file '%s'" % 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,b2m_map,metadata)
|
||||
sys.exit(0)
|
||||
|
||||
# Are inputs mmgen addresses?
|
||||
mmgen_inputs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
|
||||
other_inputs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
|
||||
|
||||
if 'all_keys_from_file' in opts: other_inputs = inputs_data
|
||||
|
||||
keys = keys_from_file
|
||||
|
||||
if other_inputs and not keys and not 'use_wallet_dat' in opts:
|
||||
missing_keys_errormsg(other_inputs)
|
||||
sys.exit(2)
|
||||
|
||||
if other_inputs and keys and not 'skip_key_preverify' in opts:
|
||||
a = [i['address'] for i in other_inputs]
|
||||
preverify_keys(a, keys)
|
||||
opts['skip_key_preverify'] = True
|
||||
|
||||
if 'all_keys_from_file' in opts:
|
||||
if addrfiles:
|
||||
check_mmgen_to_btc_addr_mappings_addrfile(mmgen_inputs,b2m_map,addrfiles)
|
||||
else:
|
||||
confirm_or_exit(txmsg['skip_mapping_checks_warning'],"continue")
|
||||
else:
|
||||
check_mmgen_to_btc_addr_mappings(
|
||||
mmgen_inputs,b2m_map,infiles,saved_seeds,opts)
|
||||
|
||||
if len(tx_files) > 1:
|
||||
msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files)))
|
||||
|
||||
prompt = "View transaction data? (y)es, (N)o, (v)iew in pager"
|
||||
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
|
||||
if reply and reply in "YyVv":
|
||||
p = True if reply in "Vv" else False
|
||||
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata,pager=p)
|
||||
|
||||
sig_data = [
|
||||
{"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
|
||||
for i in inputs_data]
|
||||
|
||||
if mmgen_inputs and not 'all_keys_from_file' in opts:
|
||||
ml = [i['account'].split()[0] for i in mmgen_inputs]
|
||||
keys += get_keys_for_mmgen_addrs(ml,infiles,saved_seeds,opts)
|
||||
|
||||
if 'use_wallet_dat' in opts:
|
||||
sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
|
||||
else:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
elif other_inputs:
|
||||
if keys:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
else:
|
||||
sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
|
||||
|
||||
if sig_tx['complete']:
|
||||
prompt = "OK\nSave signed transaction?"
|
||||
if user_confirm(prompt,default_yes=True):
|
||||
outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext)
|
||||
data = "{}\n{}\n{}\n{}\n".format(
|
||||
" ".join(metadata[:2] + [make_timestamp()]),
|
||||
sig_tx['hex'],
|
||||
repr(inputs_data),
|
||||
repr(b2m_map)
|
||||
)
|
||||
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)
|
||||
115
mmgen/main_walletchk.py
Executable file
115
mmgen/main_walletchk.py
Executable file
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletchk: Check integrity of an MMGen deterministic wallet, display
|
||||
information about it and export it to various formats
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import get_seed_from_wallet,wallet_to_incog_data
|
||||
|
||||
help_data = {
|
||||
'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),
|
||||
'usage': "[opts] [filename]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-S, --stdout Print seed or mnemonic data to standard output
|
||||
-v, --verbose Produce more verbose output
|
||||
-g, --export-incog Export wallet to incognito format
|
||||
-X, --export-incog-hex Export wallet to incognito hexadecimal format
|
||||
-G, --export-incog-hidden=f,o Hide incognito data in existing file 'f'
|
||||
at offset 'o' (comma-separated)
|
||||
-m, --export-mnemonic Export the wallet's mnemonic to file
|
||||
-s, --export-seed Export the wallet's seed to file
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
Since good randomness is particularly important for incognito wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user when one of the '--export-incog*' options is
|
||||
selected. If you fully trust your OS's random number generator and wish
|
||||
to disable this option, then specify '-r0' on the command line.
|
||||
"""
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'export_incog_hidden' in opts or 'export_incog_hex' in opts:
|
||||
opts['export_incog'] = True
|
||||
|
||||
if len(cmd_args) != 1: usage(help_data)
|
||||
|
||||
check_infile(cmd_args[0])
|
||||
|
||||
if 'export_mnemonic' in opts:
|
||||
qmsg("Exporting mnemonic data to file by user request")
|
||||
elif 'export_seed' in opts:
|
||||
qmsg("Exporting seed data to file by user request")
|
||||
elif 'export_incog' in opts:
|
||||
if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
|
||||
qmsg("Exporting wallet to incognito format by user request")
|
||||
incog_enc,seed_id,key_id,iv_id,preset = \
|
||||
wallet_to_incog_data(cmd_args[0],opts)
|
||||
|
||||
if "export_incog_hidden" in opts:
|
||||
export_to_hidden_incog(incog_enc,opts)
|
||||
else:
|
||||
seed_len = (len(incog_enc)-g.salt_len-g.aesctr_iv_len)*8
|
||||
fn = "%s-%s-%s[%s,%s].%s" % (
|
||||
seed_id, key_id, iv_id, seed_len, preset,
|
||||
g.incog_hex_ext if "export_incog_hex" in opts else g.incog_ext
|
||||
)
|
||||
data = pretty_hexdump(incog_enc,2,8,line_nums=False) \
|
||||
if "export_incog_hex" in opts else incog_enc
|
||||
export_to_file(fn, data, opts, "incognito wallet data")
|
||||
|
||||
sys.exit()
|
||||
|
||||
seed = get_seed_from_wallet(cmd_args[0], opts)
|
||||
if seed: qmsg("Wallet is OK")
|
||||
else:
|
||||
msg("Error opening wallet")
|
||||
sys.exit(2)
|
||||
|
||||
if 'export_mnemonic' in opts:
|
||||
wl = get_default_wordlist()
|
||||
from mmgen.mnemonic import get_mnemonic_from_seed
|
||||
p = True if g.debug else False
|
||||
mn = get_mnemonic_from_seed(seed, wl, g.default_wl, print_info=p)
|
||||
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.mn_ext)
|
||||
export_to_file(fn, " ".join(mn)+"\n", opts, "mnemonic data")
|
||||
|
||||
elif 'export_seed' in opts:
|
||||
from mmgen.bitcoin import b58encode_pad
|
||||
data = col4(b58encode_pad(seed))
|
||||
chk = make_chksum_6(b58encode_pad(seed))
|
||||
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
|
||||
export_to_file(fn, "%s %s\n" % (chk,data), opts, "seed data")
|
||||
149
mmgen/main_walletgen.py
Executable file
149
mmgen/main_walletgen.py
Executable file
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletgen: Generate an MMGen deterministic wallet
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
from hashlib import sha256
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.Opts import *
|
||||
from mmgen.license import *
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
|
||||
help_data = {
|
||||
'prog_name': g.prog_name,
|
||||
'desc': "Generate an {} deterministic wallet".format(g.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 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: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-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: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Produce quieter output; overwrite files without
|
||||
prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
|
||||
-b, --from-brain= l,p Generate wallet from a user-created passphrase,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate wallet from an incognito-format wallet
|
||||
-m, --from-mnemonic Generate wallet from an Electrum-like mnemonic
|
||||
-s, --from-seed Generate wallet from a seed in .{g.seed_ext} format
|
||||
""".format(seed_lens=",".join([str(i) for i in g.seed_lens]), g=g),
|
||||
'notes': """
|
||||
|
||||
By default (i.e. when invoked without any of the '--from-<what>' options),
|
||||
{g.prog_name} generates a wallet based on a random seed.
|
||||
|
||||
Data for the --from-<what> options will be taken from <infile> if <infile>
|
||||
is specified. Otherwise, the user will be prompted to enter the data.
|
||||
|
||||
For passphrases all combinations of whitespace are equal, and leading and
|
||||
trailing space are ignored. This permits reading passphrase data from a
|
||||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
|
||||
Since good randomness is particularly important when generating wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user. If you fully trust your OS's random number gener-
|
||||
ator and wish to disable this option, specify '-r0' on the command line.
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
attacks, the brainwallet hash preset must be specified by the user, using
|
||||
the 'p' parameter of the '--from-brain' option. This preset should be
|
||||
stronger than the one used for hashing the seed (i.e. the default value or
|
||||
the one specified in the '--hash-preset' option).
|
||||
|
||||
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.
|
||||
""".format(g=g)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
infile = cmd_args[0]
|
||||
check_infile(infile)
|
||||
ext = infile.split(".")[-1]
|
||||
ok_exts = g.seedfile_exts
|
||||
for e in ok_exts:
|
||||
if e == ext: break
|
||||
else:
|
||||
msg(
|
||||
"Input file must have one of the following extensions: .%s" % ", .".join(ok_exts))
|
||||
sys.exit(1)
|
||||
elif len(cmd_args) == 0:
|
||||
infile = ""
|
||||
else: usage(help_data)
|
||||
|
||||
# Begin execution
|
||||
|
||||
do_license_msg()
|
||||
|
||||
if 'from_brain' in opts and not g.quiet:
|
||||
confirm_or_exit(cmessages['brain_warning'].format(
|
||||
g.proj_name, *get_from_brain_opt_params(opts)),
|
||||
"continue")
|
||||
|
||||
for i in 'from_mnemonic','from_brain','from_seed','from_incog':
|
||||
if infile or (i in opts):
|
||||
seed = get_seed_retry(infile,opts)
|
||||
if "from_incog" in opts or get_extension(infile) == g.incog_ext:
|
||||
qmsg(cmessages['incog'] % make_chksum_8(seed))
|
||||
else: qmsg("")
|
||||
break
|
||||
else:
|
||||
# Truncate random data for smaller seed lengths
|
||||
seed = sha256(get_random(128,opts)).digest()[:opts['seed_len']/8]
|
||||
|
||||
salt = sha256(get_random(128,opts)).digest()[:g.salt_len]
|
||||
|
||||
qmsg(cmessages['choose_wallet_passphrase'] % opts['hash_preset'])
|
||||
|
||||
passwd = get_new_passphrase("{} wallet passphrase".format(g.proj_name), opts)
|
||||
|
||||
key = make_key(passwd, salt, opts['hash_preset'])
|
||||
|
||||
enc_seed = encrypt_seed(seed, key)
|
||||
|
||||
write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mnemonic.py: Mnemomic routines for the mmgen suite
|
||||
mnemonic.py: Mnemomic routines for the MMGen suite
|
||||
"""
|
||||
|
||||
wl_checksums = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
term.py: Terminal-handling routines for the mmgen suite
|
||||
term.py: Terminal-handling routines for the MMGen suite
|
||||
"""
|
||||
|
||||
import sys, os, struct
|
||||
|
|
@ -30,16 +31,12 @@ def _kb_hold_protect_unix():
|
|||
|
||||
timeout = float(0.3)
|
||||
|
||||
try:
|
||||
while True:
|
||||
key = select([sys.stdin], [], [], timeout)[0]
|
||||
if key: sys.stdin.read(1)
|
||||
else: break
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
finally:
|
||||
else:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
break
|
||||
|
||||
|
||||
def _get_keypress_unix(prompt="",immed_chars="",prehold_protect=True):
|
||||
|
|
@ -51,33 +48,27 @@ def _get_keypress_unix(prompt="",immed_chars="",prehold_protect=True):
|
|||
old = termios.tcgetattr(fd)
|
||||
tty.setcbreak(fd)
|
||||
|
||||
try:
|
||||
while True:
|
||||
# Protect against held-down key before read()
|
||||
key = select([sys.stdin], [], [], timeout)[0]
|
||||
ch = sys.stdin.read(1)
|
||||
if prehold_protect:
|
||||
if key: continue
|
||||
if immed_chars == "ALL" or ch in immed_chars:
|
||||
return ch
|
||||
if immed_chars == "ALL_EXCEPT_ENTER" and not ch in "\n\r":
|
||||
return ch
|
||||
if immed_chars == "ALL" or ch in immed_chars: break
|
||||
if immed_chars == "ALL_EXCEPT_ENTER" and not ch in "\n\r": break
|
||||
# Protect against long keypress
|
||||
key = select([sys.stdin], [], [], timeout)[0]
|
||||
if key: continue
|
||||
else: return ch
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
finally:
|
||||
if not key: break
|
||||
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
return ch
|
||||
|
||||
|
||||
|
||||
def _kb_hold_protect_mswin():
|
||||
|
||||
timeout = float(0.5)
|
||||
|
||||
try:
|
||||
while True:
|
||||
hit_time = time.time()
|
||||
while True:
|
||||
|
|
@ -86,9 +77,6 @@ def _kb_hold_protect_mswin():
|
|||
break
|
||||
if float(time.time() - hit_time) > timeout:
|
||||
return
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
|
||||
|
|
@ -96,7 +84,6 @@ def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
|
|||
msg_r(prompt)
|
||||
timeout = float(0.5)
|
||||
|
||||
try:
|
||||
while True:
|
||||
if msvcrt.kbhit():
|
||||
ch = msvcrt.getch()
|
||||
|
|
@ -114,9 +101,6 @@ def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
|
|||
if msvcrt.kbhit(): break
|
||||
if float(time.time() - hit_time) > timeout:
|
||||
return ch
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _get_terminal_size_linux():
|
||||
|
|
@ -221,13 +205,7 @@ def do_pager(text):
|
|||
p = Popen([pager], stdin=PIPE, shell=shell)
|
||||
except: pass
|
||||
else:
|
||||
try:
|
||||
p.communicate(text+end+"\n")
|
||||
except KeyboardInterrupt:
|
||||
# Has no effect. Why?
|
||||
if pager != "less":
|
||||
msg("\n(User interrupt)\n")
|
||||
finally:
|
||||
msg_r("\r")
|
||||
break
|
||||
else: print text+end
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ def b58_randenc():
|
|||
sys.exit(9)
|
||||
|
||||
def keyconv_compare_randloop(loops, quiet=False):
|
||||
try:
|
||||
for i in range(1,int(loops)+1):
|
||||
|
||||
|
||||
|
|
@ -56,8 +55,6 @@ def keyconv_compare_randloop(loops, quiet=False):
|
|||
else:
|
||||
print "%s iterations completed" % i
|
||||
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
|
||||
def keyconv_compare(wif,quiet=False):
|
||||
do_msg = nomsg if quiet else msg
|
||||
|
|
@ -144,7 +141,6 @@ def b58tohex_pad(s_in, quiet=False):
|
|||
b58tohex(s_in,f_dec=b.b58decode_pad, f_enc=b.b58encode_pad, quiet=quiet)
|
||||
|
||||
def hextob58_pad_randloop(loops, quiet=False):
|
||||
try:
|
||||
for i in range(1,int(loops)+1):
|
||||
r = hexlify(get_random(32))
|
||||
hextob58(r,f_enc=b.b58encode_pad, f_dec=b.b58decode_pad, quiet=quiet)
|
||||
|
|
@ -153,8 +149,6 @@ def hextob58_pad_randloop(loops, quiet=False):
|
|||
sys.stderr.write("\riteration: %i " % i)
|
||||
|
||||
sys.stderr.write("\r%s iterations completed\n" % i)
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
|
||||
def test_wiftohex(s_in,f_dec=b.wiftohex,f_enc=b.numtowif):
|
||||
print "Input: %s" % s_in
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,8 +15,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
tool.py: Routines and data for the mmgen-tool utility
|
||||
tool.py: Routines and data for the 'mmgen-tool' utility
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
@ -415,9 +416,9 @@ def hex2wif(hexpriv,compressed=False):
|
|||
print bitcoin.hextowif(hexpriv,compressed)
|
||||
|
||||
|
||||
def encrypt(infile,outfile="",hash_preset=''):
|
||||
def encrypt(infile,outfile="",hash_preset='3'):
|
||||
data = get_data_from_file(infile,"data for encryption")
|
||||
enc_d = mmgen_encrypt(data,hash_preset,opts)
|
||||
enc_d = mmgen_encrypt(data,"",hash_preset,opts)
|
||||
if outfile == '-':
|
||||
write_to_stdout(enc_d,"encrypted data",confirm=True)
|
||||
else:
|
||||
|
|
@ -426,9 +427,9 @@ def encrypt(infile,outfile="",hash_preset=''):
|
|||
write_to_file(outfile, enc_d, opts,"encrypted data",True,True)
|
||||
|
||||
|
||||
def decrypt(infile,outfile="",hash_preset=''):
|
||||
def decrypt(infile,outfile="",hash_preset='3'):
|
||||
enc_d = get_data_from_file(infile,"encrypted data")
|
||||
dec_d = mmgen_decrypt(enc_d,hash_preset,opts)
|
||||
dec_d = mmgen_decrypt(enc_d,"",hash_preset,opts)
|
||||
if outfile == '-':
|
||||
write_to_stdout(dec_d,"decrypted data",confirm=True)
|
||||
else:
|
||||
|
|
|
|||
12
mmgen/tx.py
12
mmgen/tx.py
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
tx.py: Bitcoin transaction routines
|
||||
"""
|
||||
|
|
@ -453,6 +454,12 @@ def is_btc_addr(s):
|
|||
from mmgen.bitcoin import verify_addr
|
||||
return verify_addr(s)
|
||||
|
||||
def is_b58_str(s):
|
||||
from mmgen.bitcoin import b58a
|
||||
for ch in s:
|
||||
if ch not in b58a: return False
|
||||
return True
|
||||
|
||||
|
||||
def mmaddr2btcaddr_bitcoind(c,mmaddr,acct_data):
|
||||
|
||||
|
|
@ -720,8 +727,7 @@ def preverify_keys(addrs_orig, keys_orig):
|
|||
|
||||
if addrs:
|
||||
s = "" if len(addrs) == 1 else "es"
|
||||
msg("No keys found for the following non-%s address%s:" %
|
||||
(g.proj_name,s))
|
||||
msg("No keys found for the following address%s:" % s)
|
||||
print " %s" % "\n ".join(addrs)
|
||||
sys.exit(2)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C) 2013-2014 by philemon <mmgen-py@yandex.com>
|
||||
# Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
util.py: Low-level routines imported by other modules for the MMGen suite
|
||||
"""
|
||||
|
|
@ -342,11 +343,11 @@ def write_to_stdout(data, what, confirm=True):
|
|||
sys.stdout.write(data)
|
||||
|
||||
|
||||
def write_to_file(outfile,data,opts,what="data",confirm=False,verbose=False):
|
||||
def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose=False):
|
||||
|
||||
if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile)
|
||||
|
||||
if confirm:
|
||||
if confirm_overwrite:
|
||||
from os import stat
|
||||
try:
|
||||
stat(outfile)
|
||||
|
|
@ -370,11 +371,9 @@ def export_to_file(outfile, data, opts, what="data"):
|
|||
|
||||
if 'stdout' in opts:
|
||||
write_to_stdout(data, what, confirm=True)
|
||||
elif not sys.stdout.isatty():
|
||||
write_to_stdout(data, what, confirm=False)
|
||||
else:
|
||||
c = False if g.quiet else True
|
||||
write_to_file(outfile,data,opts,what,c,True)
|
||||
confirm_overwrite = False if g.quiet else True
|
||||
write_to_file(outfile,data,opts,what,confirm_overwrite,True)
|
||||
|
||||
|
||||
from mmgen.bitcoin import b58decode_pad,b58encode_pad
|
||||
|
|
@ -430,9 +429,9 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
|
|||
outfile="{}-{}[{},{}].{}".format(
|
||||
seed_id,key_id,seed_len,hash_preset,g.wallet_ext)
|
||||
|
||||
c = False if g.quiet else True
|
||||
d = "\n".join((chk,)+lines)+"\n"
|
||||
write_to_file(outfile,d,opts,"wallet",c,True)
|
||||
confirm_overwrite = False if g.quiet else True
|
||||
write_to_file(outfile,d,opts,"wallet",confirm_overwrite,True)
|
||||
|
||||
if g.verbose:
|
||||
display_control_data(label,metadata,hash_preset,salt,enc_seed)
|
||||
|
|
@ -684,22 +683,29 @@ def export_to_hidden_incog(incog_enc,opts):
|
|||
msg("Data written to file '%s' at offset %s" %
|
||||
(os.path.relpath(outfile),offset))
|
||||
|
||||
|
||||
from mmgen.term import kb_hold_protect,get_char
|
||||
|
||||
def get_hash_preset_from_user(hp='3'):
|
||||
p = "Enter hash preset, or hit ENTER to accept the default ('%s'): " % hp
|
||||
while True:
|
||||
ret = my_raw_input(p)
|
||||
if ret:
|
||||
if ret in g.hash_presets.keys(): return ret
|
||||
else:
|
||||
msg("Invalid input. Valid choices are %s" %
|
||||
", ".join(sorted(g.hash_presets.keys())))
|
||||
continue
|
||||
else: return hp
|
||||
|
||||
|
||||
def my_raw_input(prompt,echo=True):
|
||||
msg_r(prompt)
|
||||
kb_hold_protect()
|
||||
try:
|
||||
if echo:
|
||||
reply = raw_input("")
|
||||
else:
|
||||
from getpass import getpass
|
||||
reply = getpass("")
|
||||
except KeyboardInterrupt:
|
||||
msg("\nUser interrupt")
|
||||
sys.exit(1)
|
||||
|
||||
kb_hold_protect()
|
||||
return reply
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue