Bugfixes, new commands for 'mmgen-tool'

This commit is contained in:
philemon 2014-07-27 15:40:27 +04:00
commit 75ab55a2d3
9 changed files with 138 additions and 75 deletions

View file

@ -137,7 +137,7 @@ if not 'info' in opts: do_license_msg(immed=True)
#write_to_file("bogus_unspent.json", repr(us)); sys.exit()
if g.bogus_wallet_data:
import mmgen.rpc
us = eval(get_data_from_file("bogus_unspent.json"))
us = eval(get_data_from_file(g.bogus_wallet_data))
else:
us = c.listunspent()

View file

@ -107,6 +107,7 @@ for tx_file in tx_files:
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])
@ -135,7 +136,6 @@ for tx_file in tx_files:
if len(tx_files) > 1:
msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files)))
qmsg("Successfully opened transaction file '%s'" % tx_file)
prompt = "View transaction data? (y)es, (N)o, (v)iew in pager"
reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)

View file

@ -141,6 +141,11 @@ if g.debug: display_user_random_data(usr_keys,key_timings)
usr_rand_data = sha256(usr_keys).digest() + \
sha256("".join(key_timings)).digest()
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)
@ -156,11 +161,12 @@ else:
salt = os_rand_data[1] + usr_rand_data
salt = sha256(salt).digest()[:g.salt_len]
qmsg("""Now you must choose a passphrase to encrypt the seed with. A key will be
qmsg("""
Now you must choose a passphrase to encrypt the wallet with. A key will be
generated from your passphrase using a hash preset of '%s'. Please note that
no strength checking of passphrases is performed. For an empty passphrase,
just hit ENTER twice.
""" % opts['hash_preset'])
""".strip() % opts['hash_preset'])
passwd = get_new_passphrase("{} wallet passphrase".format(g.proj_name), opts)

View file

@ -94,6 +94,8 @@ def parse_opts(argv,help_data):
)
opts,infiles = process_opts(argv,help_data,short_opts,long_opts)
if g.debug: print "processed user opts: %s" % opts
if not check_opts(opts,long_opts): sys.exit(1) # MMGen only!
return opts,infiles
@ -121,8 +123,8 @@ def check_opts(opts,long_opts):
# Check for file existence and readability
if opt in ('keys_from_file','addrlist','passwd_file','keysforaddrs'):
check_infile(val)
return True
check_infile(val) # exits on error
continue
if opt == 'outdir':
what = "output directory"

View file

@ -51,17 +51,22 @@ b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
# The "zero address":
# 1111111111111111111114oLvT2 (use step2 = ("0" * 40) to generate)
#
def pubhex2addr(pubhex):
def pubhex2hexaddr(pubhex):
step1 = sha256(unhexlify(pubhex)).digest()
step2 = hashlib_new('ripemd160',step1).hexdigest()
return hashlib_new('ripemd160',step1).hexdigest()
def hexaddr2addr(hexaddr):
# See above:
extra_ones = (len(step2) - len(step2.lstrip("0"))) / 2
step3 = sha256(unhexlify('00'+step2)).digest()
step4 = sha256(step3).hexdigest()
pubkey = int(step2 + step4[:8], 16)
extra_ones = (len(hexaddr) - len(hexaddr.lstrip("0"))) / 2
step1 = sha256(unhexlify('00'+hexaddr)).digest()
step2 = sha256(step1).hexdigest()
pubkey = int(hexaddr + step2[:8], 16)
return "1" + ("1" * extra_ones) + _numtob58(pubkey)
def verify_addr(addr,verbose=False):
def pubhex2addr(pubhex):
return hexaddr2addr(pubhex2hexaddr(pubhex))
def verify_addr(addr,verbose=False,return_hex=False):
if addr[0] != "1":
if verbose: print "%s: Invalid address" % addr
@ -78,7 +83,7 @@ def verify_addr(addr,verbose=False):
if verbose: print "Invalid checksum in address %s" % ("1" + addr)
return False
return True
return addr_hex[:40] if return_hex else True
# Reworked code from here:
@ -172,14 +177,17 @@ def hextowif(hexpriv,compressed=False):
key = step1 + step3[:8]
return _numtob58(int(key,16))
def privnum2addr(numpriv,compressed=False):
def privnum2pubhex(numpriv,compressed=False):
pko = ecdsa.SigningKey.from_secret_exponent(numpriv,secp256k1)
pubkey = hexlify(pko.get_verifying_key().to_string())
if compressed:
p = '03' if pubkey[-1] in "13579bdf" else '02'
return pubhex2addr(p+pubkey[:64])
return p+pubkey[:64]
else:
return pubhex2addr('04'+pubkey)
return '04'+pubkey
def privnum2addr(numpriv,compressed=False):
return pubhex2addr(privnum2pubhex(numpriv,compressed))
# Used only in test suite. To check validity, recode with numtowif()
def wiftonum(wifpriv):

View file

@ -22,7 +22,7 @@ config.py: Constants and configuration options for the mmgen suite
author = "Philemon"
email = "<mmgen-py@yandex.com>"
Cdates = '2013-2014'
version = '0.7.4'
version = '0.7.5'
quiet,verbose = False,False
min_screen_width = 80
@ -62,9 +62,9 @@ http_timeout = 30
keyconv_exec = "keyconv"
from os import getenv
debug = True if getenv("MMGEN_DEBUG") else False
no_license = True if getenv("MMGEN_NOLICENSE") else False
bogus_wallet_data = True if getenv("MMGEN_BOGUS_WALLET_DATA") else False
debug = getenv("MMGEN_DEBUG")
no_license = getenv("MMGEN_NOLICENSE")
bogus_wallet_data = getenv("MMGEN_BOGUS_WALLET_DATA")
mins_per_block = 8.5
passwd_max_tries = 5

View file

@ -21,31 +21,29 @@ tool.py: Routines and data for the mmgen-tool utility
import sys
import mmgen.bitcoin as bitcoin
import binascii as ba
from mmgen.util import *
from mmgen.tx import *
def Msg(s): sys.stdout.write(s + "\n")
def Msg_r(s): sys.stdout.write(s)
def Vmsg(s):
if g.verbose: sys.stdout.write(s + "\n")
def Vmsg_r(s):
if g.verbose: sys.stdout.write(s)
commands = {
# "keyconv_compare": ['wif [str]'],
# "keyconv_compare_randloop": ['iterations [int]'],
# "hextob58_pad": ['hexnum [str]],
# "b58tohex_pad": ['b58num [str]'],
# "hextob58_pad_randloop": ['iterations [int]'],
# "test_wiftohex": ['wif [str]'],
# "hextosha256": ['hexnum [str]'],
# "hextowiftopubkey": ['hexnum [str]'],
# "pubhextoaddr": ['hexnum [str]'],
# "hextowif_comp": ['hexnum [str]'],
# "wiftohex_comp": ['wif [str]'],
# "privhextoaddr_comp": ['hexnum [str]'],
# "wiftoaddr_comp": ['wif [str]'],
"strtob58": ['<string> [str]'],
"hextob58": ['<hex number> [str]'],
"b58tohex": ['<b58 number> [str]'],
"b58randenc": [],
"getrand": ['bytes [int=32]'],
"randwif": ['compressed [bool=False]'],
"randpair": ['compressed [bool=False]'],
"wif2hex": ['<wif> [str]', 'compressed [bool=False]'],
"wif2addr": ['<wif> [str]', 'compressed [bool=False]'],
"hex2wif": ['<private key in hex format> [str]', 'compressed [bool=False]'],
"hexdump": ['<infile> [str]', 'cols [int=8]', 'line_nums [bool=True]'],
"unhexdump": ['<infile> [str]'],
"mn_rand128": ['wordlist [str="electrum"]'],
@ -58,7 +56,16 @@ commands = {
"listaddresses": ['minconf [int=1]', 'showempty [bool=False]'],
"getbalance": ['minconf [int=1]'],
"viewtx": ['<MMGen tx file> [str]'],
"check_addrfile": ['<MMGen addr file> [str]']
"check_addrfile": ['<MMGen addr file> [str]'],
"hexreverse": ['<hexadecimal string> [str]'],
"sha256x2": ['<str, hexstr or filename> [str]',
'hex_input [bool=False]','file_input [bool=False]'],
"hexlify": ['<string> [str]'],
"hexaddr2addr": ['<btc address in hex format> [str]'],
"addr2hexaddr": ['<btc address> [str]'],
"pubkey2addr": ['<public key in hex format> [str]'],
"pubkey2hexaddr": ['<public key in hex format> [str]'],
"privhex2addr": ['<private key in hex format> [str]','compressed [bool=False]'],
}
command_help = """
@ -69,6 +76,7 @@ command_help = """
MMGen-specific operations
id8 - generate 8-character MMGen ID checksum for file (or stdin)
id6 - generate 6-character MMGen ID checksum for file (or stdin)
check_addrfile - compute checksum and address list for MMGen address file
Bitcoin operations:
strtob58 - convert a string to base 58
@ -77,7 +85,20 @@ command_help = """
b58randenc - generate a random 32-byte number and convert it to base 58
randwif - generate a random private key in WIF format
randpair - generate a random private key/address pair
wif2hex - convert a private key from WIF to hex format
hex2wif - convert a private key from hex to WIF format
wif2addr - generate a Bitcoin address from a key in WIF format
pubkey2addr - convert Bitcoin public key to address
pubkey2hexaddr - convert Bitcoin public key to address in hex format
hexaddr2addr - convert Bitcoin address from hex to base58 format
addr2hexaddr - convert Bitcoin address from base58 to hex format
privhex2addr - generate Bitcoin address from private key in hex format
Miscellaneous operations:
hexreverse - reverse bytes of a hexadecimal string
hexlify - display string in hexadecimal format
sha256x2 - compute a double sha256 hash of data
getrand - print 'n' bytes (default 32) of random data in hex format
Mnemonic operations (choose "electrum" (default), "tirosh" or "all"
wordlists):
@ -92,7 +113,6 @@ command_help = """
getbalance - like 'bitcoind getbalance' but shows confirmed/unconfirmed,
spendable/unspendable
viewtx - show raw transaction in human-readable form
check_addrfile - compute checksum and address list for MMGen address file
IMPORTANT NOTE: Though MMGen mnemonics use the Electrum wordlist, they're
computed using a different algorithm and are NOT Electrum-compatible!
@ -117,7 +137,6 @@ def process_args(prog_name, command, uargs):
n = len(cargs_req)
if len(uargs_req) != n:
print "ERROR: %s argument%s required" % (n, " is" if n==1 else "s are")
tool_usage(prog_name, command)
sys.exit(1)
@ -166,15 +185,16 @@ def process_args(prog_name, command, uargs):
# Individual commands
def print_convert_results(indata,enc,dec,no_recode=False):
vmsg("Input: [%s]" % indata)
vmsg_r("Encoded data: ["); msg_r(enc); vmsg_r("]"); msg("")
Vmsg("Input: [%s]" % indata)
Vmsg_r("Encoded data: ["); Msg_r(enc); Vmsg_r("]"); Msg("")
if not no_recode:
vmsg("Recoded data: [%s]" % dec)
Vmsg("Recoded data: [%s]" % dec)
if indata != dec:
msg("WARNING! Recoded number doesn't match input stringwise!")
Msg("WARNING! Recoded number doesn't match input stringwise!")
def hexdump(infile, cols=8, line_nums=True):
print pretty_hexdump(get_data_from_file(infile,dash=True), 2, cols, line_nums)
print pretty_hexdump(get_data_from_file(infile,dash=True),
cols=cols, line_nums=line_nums)
def unhexdump(infile):
sys.stdout.write(decode_pretty_hexdump(get_data_from_file(infile,dash=True)))
@ -185,15 +205,15 @@ def strtob58(s):
print_convert_results(s,enc,dec)
def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode):
enc = f_enc(unhexlify(s))
dec = hexlify(f_dec(enc))
enc = f_enc(ba.unhexlify(s))
dec = ba.hexlify(f_dec(enc))
print_convert_results(s,enc,dec)
def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
tmp = f_enc(s)
if tmp == False: sys.exit(1)
enc = hexlify(tmp)
dec = f_dec(unhexlify(enc))
enc = ba.hexlify(tmp)
dec = f_dec(ba.unhexlify(enc))
print_convert_results(s,enc,dec)
def get_random(length):
@ -204,28 +224,31 @@ def b58randenc():
r = get_random(32)
enc = bitcoin.b58encode(r)
dec = bitcoin.b58decode(enc)
print_convert_results(hexlify(r),enc,hexlify(dec))
print_convert_results(ba.hexlify(r),enc,ba.hexlify(dec))
def getrand(bytes='32'):
print ba.hexlify(get_random(int(bytes)))
def randwif(compressed=False):
r_hex = hexlify(get_random(32))
r_hex = ba.hexlify(get_random(32))
enc = bitcoin.hextowif(r_hex,compressed)
print_convert_results(r_hex,enc,"",no_recode=True)
def randpair(compressed=False):
r_hex = hexlify(get_random(32))
r_hex = ba.hexlify(get_random(32))
wif = bitcoin.hextowif(r_hex,compressed)
addr = bitcoin.privnum2addr(int(r_hex,16),compressed)
vmsg("Key (hex): %s" % r_hex)
vmsg_r("Key (WIF): "); msg(wif)
vmsg_r("Addr: "); msg(addr)
Vmsg("Key (hex): %s" % r_hex)
Vmsg_r("Key (WIF): "); Msg(wif)
Vmsg_r("Addr: "); Msg(addr)
def wif2addr(s_in,compressed=False):
s_enc = bitcoin.wiftohex(s_in,compressed)
def wif2addr(wif,compressed=False):
s_enc = bitcoin.wiftohex(wif,compressed)
if s_enc == False:
msg("Invalid address")
Msg("Invalid address")
sys.exit(1)
addr = bitcoin.privnum2addr(int(s_enc,16),compressed)
vmsg_r("Addr: "); msg(addr)
Vmsg_r("Addr: "); Msg(addr)
from mmgen.mnemonic import *
from mmgen.mn_electrum import electrum_words as el
@ -236,7 +259,7 @@ wordlists = sorted(wl_checksums.keys())
def get_wordlist(wordlist):
wordlist = wordlist.lower()
if wordlist not in wordlists:
msg('"%s": invalid wordlist. Valid choices: %s' %
Msg('"%s": invalid wordlist. Valid choices: %s' %
(wordlist,'"'+'" "'.join(wordlists)+'"'))
sys.exit(1)
return el if wordlist == "electrum" else tl
@ -246,10 +269,10 @@ def do_random_mn(nbytes,wordlist):
wlists = wordlists if wordlist == "all" else [wordlist]
for wl in wlists:
l = get_wordlist(wl)
if wl == wlists[0]: vmsg("Seed: %s" % hexlify(r))
if wl == wlists[0]: Vmsg("Seed: %s" % ba.hexlify(r))
mn = get_mnemonic_from_seed(r,l.strip().split("\n"),
wordlist,print_info=False)
vmsg("%s wordlist mnemonic:" % (wl.capitalize()))
Vmsg("%s wordlist mnemonic:" % (wl.capitalize()))
print " ".join(mn)
def mn_rand128(wordlist="electrum"): do_random_mn(16,wordlist)
@ -340,3 +363,37 @@ def viewtx(infile):
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata)
def check_addrfile(infile): parse_addrs_file(infile)
def hexreverse(hex_str):
print ba.hexlify(decode_pretty_hexdump(hex_str)[::-1])
def hexlify(s):
print ba.hexlify(s)
def sha256x2(s, file_input=False, hex_input=False):
from hashlib import sha256
if file_input: b = get_data_from_file(s)
elif hex_input: b = decode_pretty_hexdump(s)
else: b = s
print sha256(sha256(b).digest()).hexdigest()
def hexaddr2addr(hexaddr):
print bitcoin.hexaddr2addr(hexaddr)
def addr2hexaddr(addr):
print bitcoin.verify_addr(addr,return_hex=True)
def pubkey2hexaddr(pubkeyhex):
print bitcoin.pubhex2hexaddr(pubkeyhex)
def pubkey2addr(pubkeyhex):
print bitcoin.pubhex2addr(pubkeyhex)
def privhex2addr(privkeyhex,compressed=False):
print bitcoin.privnum2addr(int(privkeyhex,16),compressed)
def wif2hex(wif,compressed=False):
print bitcoin.wiftohex(wif,compressed)
def hex2wif(hexpriv,compressed=False):
print bitcoin.hextowif(hexpriv,compressed)

View file

@ -373,14 +373,8 @@ def view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata=[],pager=False):
days = int(j['confirmations'] * g.mins_per_block / (60*24))
total_in += j['amount']
addr = j['address']
if j['account']:
tmp = j['account'].split(None,1)
mmid,label = tmp if len(tmp) == 2 else (tmp[0],"")
label = label or ""
else:
mmid,label = "",""
mmid,label = parse_mmgen_label(j['account']) \
if 'account' in j else ("","")
mmid_str = ((34-len(addr))*" " + " (%s)" % mmid) if mmid else ""
for d in (
@ -650,6 +644,7 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts):
or "from_seed" in opts or "from_incog" in opts:
msg("Need data for seed ID %s" % seed_id)
seed = get_seed_retry("",opts)
msg("User input produced seed ID %s" % make_chksum_8(seed))
else:
msg("ERROR: No seed source found for seed ID: %s" % seed_id)
sys.exit(2)

View file

@ -279,7 +279,7 @@ def _scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
return scrypt.hash(passwd, salt, 2**N, r, p, buflen=buflen)
def _get_from_brain_opt_params(opts):
def get_from_brain_opt_params(opts):
l,p = opts['from_brain'].split(",")
return(int(l),p)
@ -287,7 +287,7 @@ def _get_from_brain_opt_params(opts):
def _get_seed_from_brain_passphrase(words,opts):
bp = " ".join(words)
if g.debug: print "Sanitized brain passphrase: %s" % bp
seed_len,hash_preset = _get_from_brain_opt_params(opts)
seed_len,hash_preset = get_from_brain_opt_params(opts)
if g.debug: print "Brainwallet l = %s, p = %s" % (seed_len,hash_preset)
vmsg_r("Hashing brainwallet data. Please wait...")
# Use buflen arg of scrypt.hash() to get seed of desired length
@ -905,11 +905,6 @@ def get_seed(infile,opts,silent=False):
if 'from_brain' not in opts:
msg("'--from-brain' parameters must be specified for brainwallet file")
sys.exit(2)
if not g.quiet:
confirm_or_exit(
cmessages['brain_warning'].format(
g.proj_name, *_get_from_brain_opt_params(opts)),
"continue")
prompt = "Enter brainwallet passphrase: "
words = _get_words(infile,"brainwallet data",prompt,opts)
seed = _get_seed_from_brain_passphrase(words,opts)
@ -996,7 +991,7 @@ def export_to_hidden_incog(incog_enc,opts):
"Data written to file")
def pretty_hexdump(data,gw,cols,line_nums=False):
def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
r = 1 if len(data) % gw else 0
return "".join(
[