Improved tx scripts; bug fix in password-handling code; whitespace cleanups

modified:   mmgen-txcreate
	modified:   mmgen-txsend
	modified:   mmgen-txsign
	modified:   mmgen/config.py
	modified:   mmgen/license.py
	modified:   mmgen/tx.py
	modified:   mmgen/utils.py
This commit is contained in:
The MMGen Project 2013-12-05 22:28:01 +04:00
commit 4912d086a2
7 changed files with 0 additions and 303 deletions

View file

@ -36,17 +36,6 @@ help_data = {
'desc': "Send BTC from specified outputs to specified addresses",
'usage': "[opts] <recipient address> <amount> <transaction fee> <change address>",
'options': """
<<<<<<< HEAD
-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
-q, --quiet Suppress warnings; overwrite files without asking
"""
}
short_opts = "hd:eq"
long_opts = "help","outdir=","echo_passphrase","quiet"
=======
-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
@ -60,7 +49,6 @@ time of %s minutes.
short_opts = "hd:eiq"
long_opts = "help","outdir=","echo_passphrase","info","quiet"
>>>>>>> my
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
@ -77,13 +65,6 @@ if len(cmd_args) == 4:
elif len(cmd_args) == 3:
rcpt_addr,send_amt,tx_fee = cmd_args
change_addr = ""
<<<<<<< HEAD
else: usage(help_data)
check_address(rcpt_addr)
send_amt = check_btc_amt(send_amt)
tx_fee = check_btc_amt(tx_fee)
=======
elif len(cmd_args) == 0 and 'info' in opts:
pass
else: usage(help_data)
@ -92,32 +73,10 @@ if not 'info' in opts:
check_address(rcpt_addr)
send_amt = check_btc_amt(send_amt)
tx_fee = check_btc_amt(tx_fee)
>>>>>>> my
# Begin execution
c = connect_to_bitcoind()
<<<<<<< HEAD
if not 'quiet' in opts: do_license_msg()
unspent = sort_and_view(c.listunspent())
total = remove_exponent(sum([i.amount for i in unspent]))
msg("Total unspent: %s BTC" % total)
msg("Amount to spend: %s BTC" % send_amt)
msg("%s unspent outputs total" % len(unspent))
sel_unspent = select_outputs(unspent,"Choose the outputs to spend: ")
total_in = remove_exponent(sum([o.amount for o in sel_unspent]))
change = remove_exponent(total_in - (send_amt + tx_fee))
if change < 0:
msg(txmsg['not_enough_btc'] % change)
sys.exit(2)
elif change > 0 and not change_addr:
=======
if not 'quiet' in opts and not 'info' in opts: do_license_msg()
unspent = sort_and_view(c.listunspent())
@ -144,7 +103,6 @@ while True:
msg(txmsg['not_enough_btc'] % change)
if change > 0 and not change_addr:
>>>>>>> my
msg(txmsg['throwaway_change'] % (change, total_in-tx_fee))
sys.exit(2)
@ -152,11 +110,7 @@ tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
tx_out = {rcpt_addr:float(send_amt), change_addr:float(change)}
tx_hex = c.createrawtransaction(tx_in,tx_out)
<<<<<<< HEAD
msg("Transaction successfully created\n")
=======
msg("Transaction successfully created")
>>>>>>> my
prompt = "View decoded transaction?"
if user_confirm(prompt,default_yes=False):
view_tx_data(c,[i.__dict__ for i in sel_unspent],tx_hex)

View file

@ -20,40 +20,17 @@ mmgen-txsend: Broadcast a Bitcoin transaction to the network
"""
import sys
<<<<<<< HEAD
#from hashlib import sha256
=======
>>>>>>> my
from mmgen.Opts import *
from mmgen.license import *
from mmgen.config import *
from mmgen.tx import *
<<<<<<< HEAD
from mmgen.utils import msg,check_infile,get_lines_from_file,confirm_or_exit
=======
from mmgen.utils import msg,check_opts,check_infile,get_lines_from_file,confirm_or_exit
>>>>>>> my
prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
<<<<<<< HEAD
'desc': "Sign a Bitcoin transaction generated by mmgen-txcreate",
'usage': "[opts] <transaction file>",
'options': """
-h, --help Print this help message
-q, --quiet Suppress warnings; overwrite files without asking
"""
}
short_opts = "hq"
long_opts = "help","quiet"
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
=======
'desc': "Send a Bitcoin transaction signed by mmgen-txsign",
'usage': "[opts] <signed transaction file>",
'options': """
@ -71,7 +48,6 @@ opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
# Exits on invalid input
check_opts(opts, ('outdir',))
>>>>>>> my
if len(cmd_args) == 1:
infile = cmd_args[0]
check_infile(infile)
@ -79,11 +55,6 @@ else: usage(help_data)
# Begin execution
<<<<<<< HEAD
c = connect_to_bitcoind()
tx_sig = get_lines_from_file(infile,"signed transaction")[0]
=======
try:
metadata,tx_sig = get_lines_from_file(infile,"signed transaction")
except:
@ -94,24 +65,10 @@ metadata = metadata.split()
if len(metadata) != 3:
msg("Invalid file header")
sys.exit(3)
>>>>>>> my
from binascii import unhexlify
try: unhexlify(tx_sig)
except:
<<<<<<< HEAD
msg("Invalid transaction data")
sys.exit(3)
msg("Successfully opened signed transaction file '%s'" % infile)
confirm_or_exit("", "broadcast this transaction to the network")
msg("Sending transaction")
try:
tx = c.sendrawtransaction(tx_sig)
=======
msg("Invalid signed transaction data")
sys.exit(3)
@ -131,15 +88,10 @@ c = connect_to_bitcoind()
try:
tx = c.sendrawtransaction(tx_sig)
# tx = "deadbeef"
>>>>>>> my
except:
msg("Unable to send transaction")
sys.exit(3)
msg("Transaction sent: %s" % tx)
<<<<<<< HEAD
print_sent_tx_to_file(tx)
=======
print_sent_tx_to_file(tx,metadata,opts)
>>>>>>> my

View file

@ -35,19 +35,11 @@ help_data = {
'desc': "Sign a Bitcoin transaction generated by mmgen-txcreate",
'usage': "[opts] <transaction file>",
'options': """
<<<<<<< HEAD
-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 Just show info about the transaction and exit
-q, --quiet Suppress warnings; overwrite files without asking
=======
-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
-q, --quiet Suppress warnings; overwrite files without asking
>>>>>>> my
"""
}
@ -74,17 +66,10 @@ c = connect_to_bitcoind()
tx_data = get_lines_from_file(infile,"transaction data")
<<<<<<< HEAD
timestamp,tx_hex,sig_data,inputs_data = parse_tx_data(tx_data)
if 'info' in opts:
view_tx_data(c,inputs_data,tx_hex,timestamp)
=======
metadata,tx_hex,sig_data,inputs_data = parse_tx_data(tx_data)
if 'info' in opts:
view_tx_data(c,inputs_data,tx_hex,metadata)
>>>>>>> my
sys.exit(0)
if not 'quiet' in opts and not 'info' in opts: do_license_msg()
@ -92,15 +77,9 @@ if not 'quiet' in opts and not 'info' in opts: do_license_msg()
msg("Successfully opened transaction file '%s'" % infile)
if user_confirm("View transaction data? ",default_yes=False):
<<<<<<< HEAD
view_tx_data(c,inputs_data,tx_hex,timestamp)
prompt = "Enter bitcoind passphrase: "
=======
view_tx_data(c,inputs_data,tx_hex,metadata)
prompt = "Enter passphrase for bitcoind wallet: "
>>>>>>> my
if 'echo_passphrase' in opts:
password = my_raw_input(prompt)
else:
@ -119,26 +98,6 @@ except exceptions.WalletPassphraseIncorrect:
sys.exit(3)
except exceptions.WalletAlreadyUnlocked:
msg("WARNING: Wallet already unlocked!")
<<<<<<< HEAD
# signrawtransaction <hex string> [{"txid":txid,"vout":n,"scriptPubKey":hex,"redeemScript":hex},...] [<privatekey1>,...] [sighashtype="ALL"]
try:
sig_tx = c.signrawtransaction(tx_hex,sig_data)
except:
msg("Failed to sign transaction")
if wallet_enc: c.walletlock()
sys.exit(3)
finally:
if wallet_enc: c.walletlock()
if not sig_tx['complete']:
msg("signrawtransaction() returned failure")
sys.exit(3)
prompt = "Save signed transaction?"
if user_confirm(prompt,default_yes=True):
print_signed_tx_to_file(tx_hex,sig_tx['hex'],opts)
=======
else:
msg("Passphrase OK")
@ -165,4 +124,3 @@ else:
prompt = "Save signed transaction?"
if user_confirm(prompt,default_yes=True):
print_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,opts)
>>>>>>> my

View file

@ -31,11 +31,7 @@ mnemonic_lens = [i / 32 * 3 for i in seed_lens]
from os import getenv
debug = True if getenv("MMGEN_DEBUG") else False
<<<<<<< HEAD
mins_per_block = 9
=======
mins_per_block = 8.5
>>>>>>> my
passwd_max_tries = 5
max_randlen,min_randlen = 80,5
usr_randlen = 20

View file

@ -25,17 +25,10 @@ from mmgen.utils import msg, msg_r, get_char
gpl = {
'warning': """
<<<<<<< HEAD
{} Copyright (C) 2013 by Philemon <mmgen-py@yandex.com>. This program
comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
welcome to redistribute it under certain conditions.
""".format(proj_name),
=======
MMGen Copyright (C) 2013 by Philemon <mmgen-py@yandex.com>. This
program comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
""",
>>>>>>> my
'prompt': """
Press 'c' for conditions, 'w' for warranty info, or ENTER to continue:
""",
@ -636,11 +629,7 @@ def do_pager(text):
def do_license_msg():
<<<<<<< HEAD
msg("%s\n" % gpl['warning'].strip())
=======
msg(gpl['warning'])
>>>>>>> my
while True:
@ -649,8 +638,4 @@ def do_license_msg():
if reply == 'c': do_pager(gpl['conditions'])
elif reply == 'w': do_pager(gpl['warranty'])
<<<<<<< HEAD
else: msg("\n"); break
=======
else: msg(""); break
>>>>>>> my

View file

@ -50,17 +50,10 @@ def connect_to_bitcoind():
return c
<<<<<<< HEAD
def remove_exponent(d):
'''Remove exponent and trailing zeros.
'''
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
=======
def trim_exponent(d):
'''Remove exponent and trailing zeros.
'''
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>>>>>> my
def check_address(rcpt_address):
from mmgen.bitcoin import verify_addr
@ -80,11 +73,7 @@ def check_btc_amt(send_amt):
msg("%s: Too many decimal places in amount" % send_amt)
sys.exit(3)
<<<<<<< HEAD
return remove_exponent(retval)
=======
return trim_exponent(retval)
>>>>>>> my
def get_cfg_options(cfg_keys):
@ -120,37 +109,15 @@ def print_tx_to_file(tx,sel_unspent,send_amt,opts):
outfile = "%s[%s].tx" % (tx_id,send_amt)
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
<<<<<<< HEAD
input_data = sel_unspent
data = "%s\n%s\n%s\n%s\n" % (
make_timestamp(), tx, repr(sig_data),
=======
metadata = "%s %s %s" % (tx_id, send_amt, make_timestamp())
data = "%s\n%s\n%s\n%s\n" % (
metadata, tx, repr(sig_data),
>>>>>>> my
repr([i.__dict__ for i in sel_unspent])
)
write_to_file(outfile,data,confirm=False)
msg("Transaction data saved to file '%s'" % outfile)
<<<<<<< HEAD
def print_signed_tx_to_file(tx,sig_tx,opts):
tx_id = make_chksum_8(unhexlify(tx))
outfile = "%s.txsig" % tx_id
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
write_to_file(outfile,sig_tx+"\n",confirm=False)
msg("Signed transaction saved to file '%s'" % outfile)
def print_sent_tx_to_file(tx):
outfile = "tx.out"
write_to_file(outfile,tx+"\n",confirm=False)
msg("Transaction ID saved to file '%s'" % outfile)
=======
def print_signed_tx_to_file(tx,sig_tx,metadata,opts):
tx_id = make_chksum_8(unhexlify(tx))
outfile = "{}[{}].txsig".format(*metadata[:2])
@ -169,7 +136,6 @@ def print_sent_tx_to_file(tx,metadata,opts):
msg("Transaction ID saved to file '%s'" % outfile)
>>>>>>> my
def sort_and_view(unspent):
def s_amt(a,b): return cmp(a.amount,b.amount)
@ -178,19 +144,12 @@ def sort_and_view(unspent):
def s_addr(a,b): return cmp(a.address,b.address)
def s_age(a,b): return cmp(b.confirmations,a.confirmations)
<<<<<<< HEAD
fs = "%-4s %-11s %-2s %-34s %13s"
sort,group,reverse = "",False,False
from copy import deepcopy
=======
fs = " %-4s %-11s %-2s %-34s %13s %-s"
fs_hdr = " %-4s %-11s %-4s %-35s %-9s %-s"
sort,group,reverse = "",False,False
from copy import deepcopy
msg("")
>>>>>>> my
while True:
out = deepcopy(unspent)
for i in out: i.skip = ""
@ -203,26 +162,11 @@ def sort_and_view(unspent):
out[n+1].skip = "t"
output = []
<<<<<<< HEAD
output.append("Sort order: %s%s%s" % (
=======
output.append("UNSPENT OUTPUTS (sort order: %s%s%s)" % (
>>>>>>> my
"reverse " if reverse else "",
sort if sort else "None",
" (grouped)" if group and (sort == "address" or sort == "txid") else ""
))
<<<<<<< HEAD
output.append(fs % ("Num","TX id","Vout","Address","Amount "))
for n,i in enumerate(out):
amt = str(remove_exponent(i.amount))
fill = 8 - len(amt.split(".")[-1]) if "." in amt else 9
addr = " |" + "-"*32 if i.skip == "d" else i.address
txid = " |---" if i.skip == "t" else i.txid[:8]+"..."
output.append(fs % (str(n+1)+")", txid,i.vout,addr,amt+(" "*fill)))
=======
output.append(fs_hdr % ("Num","TX id","Vout","Address","Amount",
"Age (days)"))
@ -234,7 +178,6 @@ def sort_and_view(unspent):
days = int(i.confirmations * mins_per_block / (60*24))
output.append(fs % (str(n+1)+")", txid,i.vout,addr,amt+(" "*fill),days))
>>>>>>> my
while True:
reply = get_char("\n".join(output) +
@ -259,82 +202,48 @@ Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [g]roup
return unspent
<<<<<<< HEAD
def view_tx_data(c,inputs_data,tx_hex,timestamp=""):
=======
def view_tx_data(c,inputs_data,tx_hex,metadata=[]):
>>>>>>> my
td = c.decoderawtransaction(tx_hex)
msg("TRANSACTION DATA:\n")
<<<<<<< HEAD
if timestamp: msg("Timestamp: %s\n" % timestamp)
=======
if metadata: msg(
"Header: [ID: {}] [Amount: {} BTC] [Time: {}]\n".format(*metadata))
>>>>>>> my
msg("Inputs:")
total_in = 0
for n,i in enumerate(td['vin']):
for j in inputs_data:
if j['txid'] == i['txid'] and j['vout'] == i['vout']:
<<<<<<< HEAD
days = j['confirmations'] * mins_per_block / (60*24)
total_in += j['amount']
msg("""
%-3s tx,vout: %s,%s
=======
days = int(j['confirmations'] * mins_per_block / (60*24))
total_in += j['amount']
msg(" " + """
%-2s tx,vout: %s,%s
>>>>>>> my
address: %s
amount: %s BTC
confirmations: %s (around %s days)
""".strip() %
(n+1,i['txid'],i['vout'],j['address'],
<<<<<<< HEAD
remove_exponent(j['amount']),j['confirmations'],days)+"\n")
break
msg("Total input: %s BTC\n" % remove_exponent(total_in))
=======
trim_exponent(j['amount']),j['confirmations'],days)+"\n")
break
msg("Total input: %s BTC\n" % trim_exponent(total_in))
>>>>>>> my
total_out = 0
msg("Outputs:")
for n,i in enumerate(td['vout']):
total_out += i['value']
<<<<<<< HEAD
msg("""
%-3s address: %s
=======
msg(" " + """
%-2s address: %s
>>>>>>> my
amount: %s BTC
""".strip() % (
n,
i['scriptPubKey']['addresses'][0],
<<<<<<< HEAD
remove_exponent(i['value']))
+ "\n")
msg("Total output: %s BTC" % remove_exponent(total_out))
msg("TX fee: %s BTC\n" % remove_exponent(total_in-total_out))
=======
trim_exponent(i['value']))
+ "\n")
msg("Total output: %s BTC" % trim_exponent(total_out))
msg("TX fee: %s BTC\n" % trim_exponent(total_in-total_out))
>>>>>>> my
def parse_tx_data(tx_data):
@ -343,11 +252,6 @@ def parse_tx_data(tx_data):
msg("'%s': not a transaction file" % infile)
sys.exit(2)
<<<<<<< HEAD
try: unhexlify(tx_data[1])
except:
msg("Transaction data is invalid")
=======
err_fmt = "Transaction %s is invalid"
if len(tx_data[0].split()) != 3:
@ -357,33 +261,21 @@ def parse_tx_data(tx_data):
try: unhexlify(tx_data[1])
except:
msg(err_fmt % "hex data")
>>>>>>> my
sys.exit(2)
try:
sig_data = eval(tx_data[2])
except:
<<<<<<< HEAD
msg("Signature data is invalid")
=======
msg(err_fmt % "signature data")
>>>>>>> my
sys.exit(2)
try:
inputs_data = eval(tx_data[3])
except:
<<<<<<< HEAD
msg("Inputs data is invalid")
sys.exit(2)
return tx_data[0],tx_data[1],sig_data,inputs_data
=======
msg(err_fmt % "inputs data")
sys.exit(2)
return tx_data[0].split(),tx_data[1],sig_data,inputs_data
>>>>>>> my
def select_outputs(unspent,prompt):

View file

@ -192,11 +192,6 @@ future, you must continue using these same parameters
def confirm_or_exit(message, question, expect="YES"):
<<<<<<< HEAD
m = message.strip()
if m: msg(m)
=======
>>>>>>> my
msg("")
m = message.strip()
@ -209,13 +204,8 @@ def confirm_or_exit(message, question, expect="YES"):
else:
prompt = "Are you sure you want to %s?\n%s" % (question,conf_msg)
<<<<<<< HEAD
if my_raw_input(prompt).strip() != "YES":
msg("Program aborted by user")
=======
if my_raw_input(prompt).strip() != expect:
msg("Exiting at user request")
>>>>>>> my
sys.exit(2)
msg("")
@ -330,18 +320,6 @@ just hit ENTER twice.
return ret
for i in range(passwd_max_tries):
<<<<<<< HEAD
if 'echo_passphrase' in opts:
return my_raw_input("Enter %s: " % what)
else:
ret = my_getpass("Enter %s: " % what)
if ret == my_getpass("Repeat %s: " % what):
s = " (empty)" if not len(ret) else ""
msg("%ss match%s" % (what.capitalize(),s))
return ret
else:
msg("%ss do not match" % what.capitalize())
=======
ret = " ".join(_get_words_from_user(opts,"Enter %s: " % what))
ret2 = " ".join(_get_words_from_user(opts,"Repeat %s: " % what))
if debug: print "Passphrases: [%s] [%s]" % (ret,ret2)
@ -351,7 +329,6 @@ just hit ENTER twice.
return ret
else:
msg("%ss do not match" % what.capitalize())
>>>>>>> my
msg("User failed to duplicate passphrase in %s attempts" % passwd_max_tries)
sys.exit(2)
@ -538,11 +515,7 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
esf = b58encode_pad(enc_seed)
metadata = seed_id.lower(),key_id.lower(),\
<<<<<<< HEAD
seed_len,pw_status,make_timestamp()
=======
seed_len,pw_status,make_timestamp()
>>>>>>> my
lines = (
label,
@ -678,11 +651,7 @@ def _get_words_from_user(opts, prompt):
def _get_words_from_file(infile,what):
msg("Getting %s from file '%s'" % (what,infile))
f = open_file_or_exit(infile, 'r')
<<<<<<< HEAD
lines = f.readlines(); f.close()
=======
data = f.read(); f.close()
>>>>>>> my
# split() also strips
return data.split()
@ -694,15 +663,6 @@ def get_lines_from_file(infile,what):
return [i.strip("\n") for i in lines]
def get_lines_from_file(infile,what):
msg("Getting %s from file '%s'" % (what,infile))
f = open_file_or_exit(infile,'r')
data = f.read(); f.close()
# split() doesn't strip final newline
return data.strip().split("\n")
def get_words(infile,what,prompt,opts):
if infile:
words = _get_words_from_file(infile,what)