Version 0.7.4
Additions/improvements:
mmgen-txsign - sign multiple transactions in one operation
mmgen-addrimport - skip rescanning block chain for new addresses
mmgen-tool - new functions:
Bitcoind operations:
listaccounts - like 'bitcoind listaccounts' but shows MMGen wallet balances
too
getbalance - like 'bitcoind getbalance' but shows confirmed/unconfirmed,
spendable/unspendable
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)
This commit is contained in:
parent
330c3d82a0
commit
242ff6acfa
8 changed files with 189 additions and 90 deletions
|
|
@ -38,7 +38,8 @@ help_data = {
|
|||
-v, --verbose Produce more verbose output
|
||||
|
||||
COMMANDS:{}
|
||||
Type '{} <command> --help for usage information on a particular command
|
||||
Type '{} <command> --help for usage information on a particular
|
||||
command
|
||||
""".format(command_help,prog_name)
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +63,7 @@ if cmd_args and cmd_args[0] == '--help':
|
|||
tool_usage(prog_name, command)
|
||||
sys.exit(0)
|
||||
|
||||
args = process_args(prog_name, command, cmd_args) if cmd_args else []
|
||||
args = process_args(prog_name, command, cmd_args)
|
||||
|
||||
#print command + "(" + ", ".join(args) + ")"
|
||||
eval(command + "(" + ", ".join(args) + ")")
|
||||
|
|
|
|||
|
|
@ -77,7 +77,10 @@ qmsg("Signed transaction file '%s' is valid" % infile)
|
|||
|
||||
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".upper()
|
||||
expect = "YES, I REALLY WANT TO DO THIS"
|
||||
|
||||
if g.quiet: warn,expect = "","YES"
|
||||
|
||||
confirm_or_exit(warn, what, expect)
|
||||
|
||||
msg("Sending transaction")
|
||||
|
|
|
|||
113
mmgen-txsign
113
mmgen-txsign
|
|
@ -29,8 +29,8 @@ from mmgen.util import msg,qmsg
|
|||
|
||||
help_data = {
|
||||
'prog_name': sys.argv[0].split("/")[-1],
|
||||
'desc': "Sign a Bitcoin transaction generated by mmgen-txcreate",
|
||||
'usage': "[opts] <transaction file> [mmgen wallet/seed/words/brain file]...",
|
||||
'desc': "Sign Bitcoin transactions generated by mmgen-txcreate",
|
||||
'usage': "[opts] <transaction file>,.. [mmgen wallet/seed/words/brainwallet file]...",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir d Specify an alternate directory 'd' for output
|
||||
|
|
@ -101,70 +101,77 @@ for i in infiles: check_infile(i)
|
|||
|
||||
c = connect_to_bitcoind()
|
||||
|
||||
tx_file = infiles.pop(0)
|
||||
m = "" if 'tx_id' in opts else "transaction data"
|
||||
tx_data = get_lines_from_file(tx_file,m)
|
||||
seeds = {}
|
||||
tx_files = [i for i in set(infiles) if get_extension(i) == g.rawtx_ext]
|
||||
infiles = list(set(infiles) - set(tx_files))
|
||||
|
||||
metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,tx_file)
|
||||
if not "info" in opts: do_license_msg(immed=True)
|
||||
|
||||
if 'tx_id' in opts:
|
||||
msg(metadata[0])
|
||||
sys.exit(0)
|
||||
keys_from_file = get_lines_from_file(opts['keys_from_file'],"key data",
|
||||
remove_comments=True) if 'keys_from_file' in opts else []
|
||||
|
||||
if 'info' in opts:
|
||||
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata)
|
||||
sys.exit(0)
|
||||
for tx_file in tx_files:
|
||||
m = "" if 'tx_id' in opts else "transaction data"
|
||||
tx_data = get_lines_from_file(tx_file,m)
|
||||
|
||||
do_license_msg(immed=True)
|
||||
metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,tx_file)
|
||||
|
||||
if 'tx_id' in opts:
|
||||
msg(metadata[0])
|
||||
sys.exit(0)
|
||||
|
||||
if 'info' in opts:
|
||||
view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata)
|
||||
sys.exit(0)
|
||||
|
||||
# Are inputs mmgen addresses?
|
||||
mmgen_addrs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
|
||||
other_addrs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
|
||||
mmgen_addrs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
|
||||
other_addrs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
|
||||
|
||||
keys = get_lines_from_file(opts['keys_from_file'],"key data",remove_comments=True) \
|
||||
if 'keys_from_file' in opts else []
|
||||
keys = keys_from_file
|
||||
|
||||
if other_addrs and not keys and not 'use_wallet_dat' in opts:
|
||||
missing_keys_errormsg(other_addrs)
|
||||
sys.exit(2)
|
||||
if other_addrs and not keys and not 'use_wallet_dat' in opts:
|
||||
missing_keys_errormsg(other_addrs)
|
||||
sys.exit(2)
|
||||
|
||||
if other_addrs and keys and not 'skip_key_preverify' in opts:
|
||||
a = [i['address'] for i in other_addrs]
|
||||
preverify_keys(a, keys)
|
||||
if other_addrs and keys and not 'skip_key_preverify' in opts:
|
||||
a = [i['address'] for i in other_addrs]
|
||||
preverify_keys(a, keys)
|
||||
opts['skip_key_preverify'] = True
|
||||
|
||||
seeds = {}
|
||||
check_mmgen_to_btc_addr_mappings(inputs_data,b2m_map,infiles,seeds,opts)
|
||||
check_mmgen_to_btc_addr_mappings(inputs_data,b2m_map,infiles,seeds,opts)
|
||||
|
||||
qmsg("Successfully opened transaction file '%s'" % tx_file)
|
||||
if len(tx_files) > 1: msg("\nTransaction #%s:" % (tx_files.index(tx_file)+1))
|
||||
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)
|
||||
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)
|
||||
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]
|
||||
sig_data = [
|
||||
{"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
|
||||
for i in inputs_data]
|
||||
|
||||
if mmgen_addrs:
|
||||
ml = [i['account'].split()[0] for i in mmgen_addrs]
|
||||
keys += get_keys_for_mmgen_addrs(ml,infiles,seeds,opts)
|
||||
if mmgen_addrs:
|
||||
ml = [i['account'].split()[0] for i in mmgen_addrs]
|
||||
keys += get_keys_for_mmgen_addrs(ml,infiles,seeds,opts)
|
||||
|
||||
if 'use_wallet_dat' in opts:
|
||||
sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,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_addrs:
|
||||
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):
|
||||
print_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,opts)
|
||||
else:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
elif other_addrs:
|
||||
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):
|
||||
print_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,opts)
|
||||
else:
|
||||
msg("failed\nSome keys were missing. Transaction could not be signed.")
|
||||
sys.exit(3)
|
||||
msg("failed\nSome keys were missing. Transaction could not be signed.")
|
||||
sys.exit(3)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ incog_hex_ext = "mmincox"
|
|||
|
||||
seedfile_exts = wallet_ext, seed_ext, mn_ext, brain_ext, incog_ext
|
||||
|
||||
rawtx_ext = "raw"
|
||||
sigtx_ext = "sig"
|
||||
addrfile_ext = "addrs"
|
||||
keyfile_ext = "keys"
|
||||
|
||||
|
|
|
|||
105
mmgen/tool.py
105
mmgen/tool.py
|
|
@ -51,13 +51,21 @@ commands = {
|
|||
"mn_rand192": ['wordlist [str="electrum"]'],
|
||||
"mn_rand256": ['wordlist [str="electrum"]'],
|
||||
"mn_stats": ['wordlist [str="electrum"]'],
|
||||
"mn_printlist": ['wordlist [str="electrum"]']
|
||||
"mn_printlist": ['wordlist [str="electrum"]'],
|
||||
"id8": ['<infile> [str]'],
|
||||
"id6": ['<infile> [str]'],
|
||||
"listaccounts": ['minconf [int=1]'],
|
||||
"getbalance": ['minconf [int=1]'],
|
||||
}
|
||||
|
||||
command_help = """
|
||||
General operations:
|
||||
hexdump - encode binary data in formatted hexadecimal form
|
||||
unhexdump - decode formatted hexadecimal data
|
||||
File operations
|
||||
hexdump - encode data into formatted hexadecimal form (file or stdin)
|
||||
unhexdump - decode formatted hexadecimal data (file or stdin)
|
||||
|
||||
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)
|
||||
|
||||
Bitcoin operations:
|
||||
strtob58 - convert a string to base 58
|
||||
|
|
@ -68,13 +76,20 @@ randwif - generate a random private key in WIF format
|
|||
randpair - generate a random private key/address pair
|
||||
wif2addr - generate a Bitcoin address from a key in WIF format
|
||||
|
||||
Mnemonic operations (choose "electrum" (default), "tirosh" or "all" wordlists):
|
||||
Mnemonic operations (choose "electrum" (default), "tirosh" or "all"
|
||||
wordlists):
|
||||
mn_rand128 - generate random 128-bit mnemonic
|
||||
mn_rand192 - generate random 192-bit mnemonic
|
||||
mn_rand256 - generate random 256-bit mnemonic
|
||||
mn_stats - show stats for mnemonic wordlist
|
||||
mn_printlist - print mnemonic wordlist
|
||||
|
||||
Bitcoind operations (bitcoind must be running):
|
||||
listaccounts - like 'bitcoind listaccounts' but shows MMGen wallet balances
|
||||
too
|
||||
getbalance - like 'bitcoind getbalance' but shows confirmed/unconfirmed,
|
||||
spendable/unspendable
|
||||
|
||||
IMPORTANT NOTE: Though MMGen mnemonics use the Electrum wordlist, they're
|
||||
computed using a different algorithm and are NOT Electrum-compatible!
|
||||
"""
|
||||
|
|
@ -119,17 +134,23 @@ def process_args(prog_name, command, uargs):
|
|||
|
||||
ret = []
|
||||
|
||||
def normalize_arg(arg, arg_type):
|
||||
if arg_type == "bool":
|
||||
if arg.lower() in ("true","yes","1","on"): return "True"
|
||||
if arg.lower() in ("false","no","0","off"): return "False"
|
||||
return arg
|
||||
|
||||
for i in range(len(cargs_req)):
|
||||
arg,arg_type = uargs_req[i], cargs_req[i][1]
|
||||
if arg == "true" or arg == "false": arg = arg.capitalize()
|
||||
arg_type = cargs_req[i][1]
|
||||
arg = normalize_arg(uargs_req[i], arg_type)
|
||||
if arg_type == "str":
|
||||
ret.append('"%s"' % (arg))
|
||||
elif test_type(arg_type, arg, "#"+str(i+1)):
|
||||
ret.append('%s' % (arg))
|
||||
|
||||
for k in uargs_nam.keys():
|
||||
arg,arg_type = uargs_nam[k], cargs_nam[k][0]
|
||||
if arg == "true" or arg == "false": arg = arg.capitalize()
|
||||
arg_type = cargs_nam[k][0]
|
||||
arg = normalize_arg(uargs_nam[k], arg_type)
|
||||
if arg_type == "str":
|
||||
ret.append('%s="%s"' % (k, arg))
|
||||
elif test_type(arg_type, arg, "'"+k+"'"):
|
||||
|
|
@ -149,13 +170,13 @@ def print_convert_results(indata,enc,dec,no_recode=False):
|
|||
msg("WARNING! Recoded number doesn't match input stringwise!")
|
||||
|
||||
def hexdump(infile, cols=8, line_nums=True):
|
||||
data = get_data_from_file(infile)
|
||||
o = pretty_hexdump(data, 2, cols, line_nums)
|
||||
d = sys.stdin.read() if infile == "-" else get_data_from_file(infile)
|
||||
o = pretty_hexdump(d, 2, cols, line_nums)
|
||||
print o
|
||||
|
||||
def unhexdump(infile):
|
||||
data = get_data_from_file(infile)
|
||||
o = decode_pretty_hexdump(data)
|
||||
d = sys.stdin.read() if infile == "-" else get_data_from_file(infile)
|
||||
o = decode_pretty_hexdump(d)
|
||||
sys.stdout.write(o)
|
||||
|
||||
def strtob58(s):
|
||||
|
|
@ -242,3 +263,61 @@ def mn_stats(wordlist="electrum"):
|
|||
def mn_printlist(wordlist="electrum"):
|
||||
l = get_wordlist(wordlist)
|
||||
print "%s" % l.strip()
|
||||
|
||||
def id8(infile):
|
||||
d = sys.stdin.read() if infile == "-" else get_data_from_file(infile)
|
||||
print make_chksum_8(d)
|
||||
|
||||
def id6(infile):
|
||||
d = sys.stdin.read() if infile == "-" else get_data_from_file(infile)
|
||||
print make_chksum_6(d)
|
||||
|
||||
|
||||
def listaccounts(minconf=1):
|
||||
from mmgen.tx import connect_to_bitcoind,trim_exponent,is_mmgen_addr
|
||||
def s_mmgen(i):
|
||||
ma = i[0].split(" ")[0] if " " in i[0] else i[0]
|
||||
if is_mmgen_addr(ma):
|
||||
mmid,idx = ma.split(":")
|
||||
return mmid + ":" + ("%04i" % int(idx))
|
||||
else:
|
||||
return "G"+i[0]
|
||||
|
||||
c = connect_to_bitcoind()
|
||||
data = [(a,c.getbalance(a,minconf)) for a in c.listaccounts()]
|
||||
data.sort(key=s_mmgen)
|
||||
col_w = max([len(d[0]) for d in data])
|
||||
fs = "%-"+str(col_w)+"s %s"
|
||||
print fs % ("ACCOUNT","BALANCE")
|
||||
totals = {}
|
||||
for d in data:
|
||||
ma = d[0].split(" ")[0] if " " in d[0] else d[0]
|
||||
if is_mmgen_addr(ma):
|
||||
mmid = ma.split(":")[0]
|
||||
if mmid not in totals: totals[mmid] = 0
|
||||
totals[mmid] += d[1]
|
||||
print fs % (
|
||||
d[0] if d[0] else 'TOTAL:',
|
||||
trim_exponent(d[1])
|
||||
)
|
||||
print "\nMMGEN WALLET BALANCES"
|
||||
for k in totals.keys():
|
||||
print "%s: %s" % (k, trim_exponent(totals[k]))
|
||||
|
||||
def getbalance(minconf=1):
|
||||
from mmgen.tx import connect_to_bitcoind,trim_exponent,is_mmgen_addr
|
||||
c = connect_to_bitcoind()
|
||||
data = c.listunspent(0)
|
||||
o = [0,0,0,0,0,0] # su,sb,sc, uu,ub,uc
|
||||
for d in data:
|
||||
j = 0 if d.spendable else 3
|
||||
if d.confirmations == 0: o[j] += d.amount
|
||||
k = 1 if d.confirmations < minconf else 2
|
||||
o[j+k] += d.amount
|
||||
|
||||
fs = "{}:\n {:<12} unconfirmed\n {:<12} <{M} {C}\n {:<12} >={M} {C}"
|
||||
for lbl,n in ("Spendable",0),("Unspendable",3):
|
||||
if sum(o[n:3+n]) == 0:
|
||||
print "{}: {}".format(lbl,"NONE")
|
||||
else:
|
||||
print fs.format(lbl,o[n+0],o[n+1],o[n+2],M=minconf,C="confirmations")
|
||||
|
|
|
|||
27
mmgen/tx.py
27
mmgen/tx.py
|
|
@ -141,7 +141,7 @@ def get_bitcoind_cfg_options(cfg_keys):
|
|||
|
||||
def print_tx_to_file(tx,sel_unspent,send_amt,b2m_map,opts):
|
||||
tx_id = make_chksum_6(unhexlify(tx)).upper()
|
||||
outfile = "tx_%s[%s].raw" % (tx_id,send_amt)
|
||||
outfile = "tx_%s[%s].%s" % (tx_id,send_amt,g.rawtx_ext)
|
||||
if 'outdir' in opts:
|
||||
outfile = "%s/%s" % (opts['outdir'], outfile)
|
||||
metadata = "%s %s %s" % (tx_id, send_amt, make_timestamp())
|
||||
|
|
@ -156,7 +156,7 @@ def print_tx_to_file(tx,sel_unspent,send_amt,b2m_map,opts):
|
|||
|
||||
def print_signed_tx_to_file(tx,sig_tx,metadata,opts):
|
||||
tx_id = make_chksum_6(unhexlify(tx)).upper()
|
||||
outfile = "tx_{}[{}].sig".format(*metadata[:2])
|
||||
outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext)
|
||||
if 'outdir' in opts:
|
||||
outfile = "%s/%s" % (opts['outdir'], outfile)
|
||||
data = "%s\n%s\n" % (" ".join(metadata),sig_tx)
|
||||
|
|
@ -174,16 +174,17 @@ def print_sent_tx_to_file(tx,metadata,opts):
|
|||
|
||||
def format_unspent_outputs_for_printing(out,sort_info,total):
|
||||
|
||||
pfs = " %-4s %-67s %-34s %-12s %-13s %-10s %s"
|
||||
pfs = " %-4s %-67s %-34s %-12s %-13s %-8s %-10s %s"
|
||||
pout = [pfs % ("Num","TX id,Vout","Address","MMgen ID",
|
||||
"Amount (BTC)","Age (days)", "Comment")]
|
||||
"Amount (BTC)","Confirms","Age (days)", "Comment")]
|
||||
|
||||
for n,i in enumerate(out):
|
||||
addr = "=" if i.skip == "addr" and "grouped" in sort_info else i.address
|
||||
tx = " " * 63 + "=" \
|
||||
if i.skip == "txid" and "grouped" in sort_info else str(i.txid)
|
||||
|
||||
s = pfs % (str(n+1)+")", tx+","+str(i.vout),addr,i.mmid,i.amt,i.days,i.label)
|
||||
s = pfs % (str(n+1)+")", tx+","+str(i.vout),addr,
|
||||
i.mmid,i.amt,i.confirmations,i.days,i.label)
|
||||
pout.append(s.rstrip())
|
||||
|
||||
return \
|
||||
|
|
@ -200,14 +201,16 @@ def sort_and_view(unspent):
|
|||
def s_age(i): return i.confirmations
|
||||
def s_mmgen(i): return i.account
|
||||
|
||||
sort,group,show_mmaddr,reverse = "",False,False,False
|
||||
sort,group,show_days,show_mmaddr,reverse = "age",False,False,True,True
|
||||
unspent.sort(key=s_age,reverse=reverse) # Reverse age sort by default
|
||||
|
||||
total = trim_exponent(sum([i.amount for i in unspent]))
|
||||
|
||||
hdr_fmt = "UNSPENT OUTPUTS (sort order: %s) Total BTC: %s"
|
||||
|
||||
options_msg = """
|
||||
Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr
|
||||
Display options: [g]roup, show [m]mgen addr, r[e]draw screen
|
||||
Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
|
||||
""".strip()
|
||||
prompt = \
|
||||
"('q' = quit sorting, 'p' = print to file, 'v' = pager view, 'w' = wide view): "
|
||||
|
|
@ -230,8 +233,8 @@ Display options: [g]roup, show [m]mgen addr, r[e]draw screen
|
|||
addr_w = min(34+((1+max_acct_len) if show_mmaddr else 0),cols-46)
|
||||
tx_w = max(11,min(64, cols-addr_w-32))
|
||||
fs = " %-4s %-" + str(tx_w) + "s %-2s %-" + str(addr_w) + "s %-13s %-s"
|
||||
table_hdr = fs % ("Num","TX id Vout","","Address",
|
||||
"Amount (BTC)","Age(d)")
|
||||
a = "Age(d)" if show_days else "Confirms"
|
||||
table_hdr = fs % ("Num","TX id Vout","","Address", "Amount (BTC)",a)
|
||||
|
||||
unsp = deepcopy(unspent)
|
||||
for i in unsp: i.skip = ""
|
||||
|
|
@ -277,12 +280,13 @@ Display options: [g]roup, show [m]mgen addr, r[e]draw screen
|
|||
out = [hdr_fmt % (" ".join(sort_info), total), table_hdr]
|
||||
|
||||
for n,i in enumerate(unsp):
|
||||
out.append(fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,i.days))
|
||||
d = i.days if show_days else i.confirmations
|
||||
out.append(fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,d))
|
||||
|
||||
msg("\n".join(out) +"\n\n" + print_to_file_msg + options_msg)
|
||||
print_to_file_msg = ""
|
||||
|
||||
immed_chars = "atdAMrgmeqpvw"
|
||||
immed_chars = "atDdAMrgmeqpvw"
|
||||
skip_prompt = False
|
||||
|
||||
while True:
|
||||
|
|
@ -290,6 +294,7 @@ Display options: [g]roup, show [m]mgen addr, r[e]draw screen
|
|||
|
||||
if reply == 'a': unspent.sort(key=s_amt); sort = "amount"
|
||||
elif reply == 't': unspent.sort(key=s_txid); sort = "txid"
|
||||
elif reply == 'D': show_days = False if show_days else True
|
||||
elif reply == 'd': unspent.sort(key=s_addr); sort = "address"
|
||||
elif reply == 'A': unspent.sort(key=s_age); sort = "age"
|
||||
elif reply == 'M':
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ def vmsg_r(s):
|
|||
|
||||
def bail(): sys.exit(9)
|
||||
|
||||
def get_extension(f): return f.split(".")[-1]
|
||||
def get_extension(f):
|
||||
try: return f.split(".")[-1]
|
||||
except: return ""
|
||||
|
||||
def my_raw_input(prompt,echo=True,allowed_chars=""):
|
||||
try:
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -3,7 +3,7 @@ from distutils.core import setup
|
|||
|
||||
setup(
|
||||
name = 'mmgen',
|
||||
version = '0.7.3b',
|
||||
version = '0.7.4',
|
||||
author = 'Philemon',
|
||||
author_email = 'mmgen-py@yandex.com',
|
||||
url = 'https://github.com/mmgen/mmgen',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue