Added transaction creation/signing scripts

modified:   MANIFEST
	new file:   mmgen-txcreate
	new file:   mmgen-txsign
	modified:   mmgen/__init__.py
	modified:   mmgen/bitcoin.py
	modified:   mmgen/config.py
	modified:   mmgen/license.py
	new file:   mmgen/tx.py
	modified:   mmgen/utils.py
	new file:   scripts/bitcoind-walletunlock.py
	modified:   setup.py
This commit is contained in:
philemon 2013-12-05 00:10:05 +04:00
commit 35ddbc029c
11 changed files with 708 additions and 74 deletions

View file

@ -2,6 +2,8 @@
mmgen-addrgen
mmgen-keygen
mmgen-passchg
mmgen-txcreate
mmgen-txsign
mmgen-walletchk
mmgen-walletgen
setup.py
@ -14,5 +16,6 @@ mmgen/license.py
mmgen/mn_electrum.py
mmgen/mn_tirosh.py
mmgen/mnemonic.py
mmgen/tx.py
mmgen/utils.py
mmgen/walletgen.py

106
mmgen-txcreate Executable file
View file

@ -0,0 +1,106 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by 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: Send BTC from specified outputs to specified addresses
"""
import sys
#from hashlib import sha256
from mmgen.Opts import *
from mmgen.license import *
from mmgen.config import *
from mmgen.tx import *
from mmgen.utils import check_opts, msg, user_confirm
from decimal import Decimal
prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
'desc': "Send BTC from specified outputs to specified addresses",
'usage': "[opts] <recipient address> <amount> <transaction fee> <change address>",
'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
-q, --quiet Suppress warnings; overwrite files without asking
"""
}
short_opts = "hd:eq"
long_opts = "help","outdir=","echo_passphrase","quiet"
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
# Exits on invalid input
check_opts(opts, ('outdir',))
if debug:
print "Processed options: %s" % repr(opts)
print "Cmd args: %s" % repr(cmd_args)
if len(cmd_args) == 4:
rcpt_addr,send_amt,tx_fee,change_addr = cmd_args
check_address(change_addr)
elif len(cmd_args) == 3:
rcpt_addr,send_amt,tx_fee = cmd_args
change_addr = ""
else: usage(help_data)
check_address(rcpt_addr)
send_amt = check_btc_amt(send_amt)
tx_fee = check_btc_amt(tx_fee)
# Begin execution
c = connect_to_bitcoind()
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:
msg(txmsg['throwaway_change'] % (change, total_in-tx_fee))
sys.exit(2)
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)
msg("Transaction successfully created\n")
prompt = "View decoded transaction?"
if user_confirm(prompt,default_yes=False):
view_tx_data(c,[i.__dict__ for i in sel_unspent],tx_hex)
prompt = "Save transaction?"
if user_confirm(prompt,default_yes=True):
print_tx_to_file(tx_hex,sel_unspent,send_amt,opts)

118
mmgen-txsign Executable file
View file

@ -0,0 +1,118 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by 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 Bitcoin transaction generated by mmgen-txcreate
"""
import sys
#from hashlib import sha256
from mmgen.Opts import *
from mmgen.license import *
from mmgen.config import *
from mmgen.tx import *
from mmgen.utils import check_opts, msg, user_confirm, check_infile, get_lines_from_file, my_getpass, my_raw_input
prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
'desc': "Sign a Bitcoin transaction generated by mmgen-txcreate",
'usage': "[opts] <transaction 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
-i, --info Just show info about the transaction and exit
-q, --quiet Suppress warnings; overwrite files without asking
"""
}
short_opts = "hd:eiq"
long_opts = "help","outdir=","echo_passphrase","info","quiet"
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
# Exits on invalid input
check_opts(opts, ('outdir',))
if debug:
print "Processed options: %s" % repr(opts)
print "Cmd args: %s" % repr(cmd_args)
if len(cmd_args) == 1:
infile = cmd_args[0]
check_infile(infile)
else: usage(help_data)
# Begin execution
c = connect_to_bitcoind()
tx_data = get_lines_from_file(infile,"transaction data")
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)
sys.exit(0)
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):
view_tx_data(c,inputs_data,tx_hex,timestamp)
prompt = "Enter bitcoind passphrase: "
if 'echo_passphrase' in opts:
password = my_raw_input(prompt)
else:
password = my_getpass(prompt)
wallet_enc = True
from bitcoinrpc import exceptions
try:
c.walletpassphrase(password, 9999)
except exceptions.WalletWrongEncState:
msg("Wallet is unencrypted")
wallet_enc = False
except exceptions.WalletPassphraseIncorrect:
msg("Passphrase incorrect")
sys.exit(3)
except exceptions.WalletAlreadyUnlocked:
msg("WARNING: Wallet already unlocked!")
# 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)

View file

@ -28,6 +28,7 @@ __all__ = [
'mnemonic.py',
'mn_tirosh.py',
'Opts.py',
'tx.py',
'utils.py',
'walletgen.py'
]

View file

@ -66,6 +66,30 @@ def privnum2addr(numpriv):
pubkey = hexlify(pko.get_verifying_key().to_string())
return _pubhex2addr('04'+pubkey)
def verify_addr(addr):
if addr[0] != "1":
print "%s Invalid address" % addr
return False
addr,lz = addr[1:],0
while addr[0] == "1": addr = addr[1:]; lz += 1
addr_hex = lz * "00" + hex(_b58tonum(addr))[2:].rstrip("L")
if len(addr_hex) != 48:
print "%s Invalid address" % addr
return False
step1 = sha256(unhexlify('00'+addr_hex[:40])).digest()
step2 = sha256(step1).hexdigest()
if step2[:8] != addr_hex[40:]:
print "Invalid checksum in address %s" % addr
return False
return True
# Reworked code from here:
def _numtob58(num):

View file

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

View file

@ -21,18 +21,16 @@ license.py: Show the license
import sys
from mmgen.config import proj_name
from mmgen.utils import msg, msg_r
from mmgen.utils import msg, msg_r, get_char
gpl = {
'warning': """
{} 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.
{} 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),
'prompt': """
Press 'c' for conditions, 'w' for warranty info,
or ENTER to continue:
Press 'c' for conditions, 'w' for warranty info, or ENTER to continue:
""",
'conditions': """
TERMS AND CONDITIONS
@ -620,30 +618,24 @@ copy of the Program in return for a fee.
"""
}
def do_pager(text):
import os
pager = os.environ['PAGER'] if 'PAGER' in os.environ else 'more'
p = os.popen(pager, 'w')
p.write(text)
p.close()
msg_r("\r")
def do_license_msg():
ls = "\n "
msg(" " + ls.join(gpl['warning'].strip().split("\n")))
msg("%s\n" % gpl['warning'].strip())
try:
import os
os.system(
"stty -icanon min 1 time 0 -echo -echoe -echok -echonl -crterase noflsh"
)
pager = os.environ['PAGER'] if 'PAGER' in os.environ else 'more'
while True:
while True:
msg_r(ls + ls.join(gpl['prompt'].strip().split("\n")) + " ")
reply = sys.stdin.read(1)
if reply == 'c':
m = gpl['conditions']
elif reply == 'w':
m = gpl['warranty']
else: break
p = os.popen(pager, 'w')
p.write(m)
p.close()
except:
msg("\nInterrupted by user")
sys.exit(1)
finally:
os.system("stty sane")
prompt = "%s " % gpl['prompt'].strip()
reply = get_char(prompt)
if reply == 'c': do_pager(gpl['conditions'])
elif reply == 'w': do_pager(gpl['warranty'])
else: msg("\n"); break

285
mmgen/tx.py Executable file
View file

@ -0,0 +1,285 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by 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/>.
"""
tx.py: Bitcoin transaction routines
"""
from binascii import unhexlify
from mmgen.utils import msg,msg_r,write_to_file,my_raw_input,get_char,make_chksum_8,make_timestamp
import sys, os
from bitcoinrpc.connection import *
from decimal import Decimal
from mmgen.config import *
txmsg = {
'not_enough_btc': "Not enough BTC in the inputs for this transaction (%s BTC)",
'throwaway_change': """
ERROR: This transaction produces change (%s BTC); however, no change
address was specified. Total inputs - transaction fee = %s BTC.
To create a valid transaction with no change address, send this sum to the
specified recipient address.
""".strip()
}
def connect_to_bitcoind():
host,port,user,passwd = "localhost",8332,"rpcuser","rpcpassword"
cfg = get_cfg_options((user,passwd))
try:
c = BitcoinConnection(cfg[user],cfg[passwd],host,port)
except:
msg("Unable to establish RPC connection with bitcoind")
sys.exit(2)
return c
def remove_exponent(d):
'''Remove exponent and trailing zeros.
'''
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
def check_address(rcpt_address):
from mmgen.bitcoin import verify_addr
if not verify_addr(rcpt_address):
sys.exit(3)
def check_btc_amt(send_amt):
from decimal import Decimal
try:
retval = Decimal(send_amt)
except:
msg("%s: Invalid amount" % send_amt)
sys.exit(3)
if retval.as_tuple()[-1] < -8:
msg("%s: Too many decimal places in amount" % send_amt)
sys.exit(3)
return remove_exponent(retval)
def get_cfg_options(cfg_keys):
cfg_file = "%s/%s" % (os.environ["HOME"], ".bitcoin/bitcoin.conf")
try:
f = open(cfg_file)
except:
msg("Unable to open file '%s' for reading" % cfg_file)
sys.exit(2)
cfg = {}
for line in f.readlines():
s = line.translate(None,"\n\t ").split("=")
for k in cfg_keys:
if s[0] == k: cfg[k] = s[1]
f.close()
for k in cfg_keys:
if not k in cfg:
msg("Configuration option '%s' must be set in %s" % (k,cfg_file))
sys.exit(2)
return cfg
def print_tx_to_file(tx,sel_unspent,send_amt,opts):
sig_data = [{"txid":i.txid,"vout":i.vout,"scriptPubKey":i.scriptPubKey}
for i in sel_unspent]
tx_id = make_chksum_8(unhexlify(tx))
outfile = "%s[%s].tx" % (tx_id,send_amt)
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
input_data = sel_unspent
data = "%s\n%s\n%s\n%s\n" % (
make_timestamp(), tx, repr(sig_data),
repr([i.__dict__ for i in sel_unspent])
)
write_to_file(outfile,data,confirm=False)
msg("Transaction data saved to file '%s'" % outfile)
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 sort_and_view(unspent):
def s_amt(a,b): return cmp(a.amount,b.amount)
def s_txid(a,b):
return cmp("%s %03s" % (a.txid,a.vout), "%s %03s" % (b.txid,b.vout))
def s_addr(a,b): return cmp(a.address,b.address)
def s_age(a,b): return cmp(b.confirmations,a.confirmations)
fs = "%-4s %-11s %-2s %-34s %13s"
sort,group,reverse = "",False,False
from copy import deepcopy
while True:
out = deepcopy(unspent)
for i in out: i.skip = ""
for n in range(len(out)):
if group and n < len(out)-1:
a,b = out[n],out[n+1]
if sort == "address" and a.address == b.address:
out[n+1].skip = "d"
elif sort == "txid" and a.txid == b.txid:
out[n+1].skip = "t"
output = []
output.append("Sort order: %s%s%s" % (
"reverse " if reverse else "",
sort if sort else "None",
" (grouped)" if group and (sort == "address" or sort == "txid") else ""
))
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)))
while True:
reply = get_char("\n".join(output) +
"""\n
Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [g]roup
(Type 'q' to quit sorting): """).strip()
if reply == 'a': unspent.sort(s_amt); sort = "amount"; break
elif reply == 't': unspent.sort(s_txid); sort = "txid"; break
elif reply == 'd': unspent.sort(s_addr); sort = "address"; break
elif reply == 'A': unspent.sort(s_age); sort = "age"; break
elif reply == 'r':
reverse = False if reverse else True
unspent.reverse()
break
elif reply == 'g': group = False if group else True; break
elif reply == 'q': break
else: msg("Invalid input")
msg("\n")
if reply == 'q': break
return unspent
def view_tx_data(c,inputs_data,tx_hex,timestamp=""):
td = c.decoderawtransaction(tx_hex)
msg("TRANSACTION DATA:\n")
if timestamp: msg("Timestamp: %s\n" % timestamp)
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']:
days = j['confirmations'] * mins_per_block / (60*24)
total_in += j['amount']
msg("""
%-3s tx,vout: %s,%s
address: %s
amount: %s BTC
confirmations: %s (around %s days)
""".strip() %
(n+1,i['txid'],i['vout'],j['address'],
remove_exponent(j['amount']),j['confirmations'],days)+"\n")
break
msg("Total input: %s BTC\n" % remove_exponent(total_in))
total_out = 0
msg("Outputs:")
for n,i in enumerate(td['vout']):
total_out += i['value']
msg("""
%-3s address: %s
amount: %s BTC
""".strip() % (
n,
i['scriptPubKey']['addresses'][0],
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))
def parse_tx_data(tx_data):
if len(tx_data) != 4:
msg("'%s': not a transaction file" % infile)
sys.exit(2)
try: unhexlify(tx_data[1])
except:
msg("Transaction data is invalid")
sys.exit(2)
try:
sig_data = eval(tx_data[2])
except:
msg("Signature data is invalid")
sys.exit(2)
try:
inputs_data = eval(tx_data[3])
except:
msg("Inputs data is invalid")
sys.exit(2)
return tx_data[0],tx_data[1],sig_data,inputs_data
def select_outputs(unspent,prompt):
while True:
reply = my_raw_input(prompt).strip()
if reply:
selected = ()
try:
selected = [int(i) - 1 for i in reply.split()]
except: pass
else:
for i in selected:
if i < 0 or i >= len(unspent):
msg(
"Input must be a number or numbers between 1 and %s" % len(unspent))
break
else: break
msg("'%s': Invalid input" % reply)
return [unspent[i] for i in selected]

View file

@ -28,27 +28,44 @@ def msg_r(s): sys.stderr.write(s)
def bail(): sys.exit(9)
def _my_getpass(prompt):
def my_getpass(prompt):
from getpass import getpass
# getpass prompts to stderr, so no trickery required as with raw_input()
try: pw = getpass(prompt)
except:
msg("\nInterrupted by user")
msg("\nUser interrupt")
sys.exit(1)
return pw
def get_char(prompt):
def _my_raw_input(prompt):
import os
msg_r(prompt)
os.system(
"stty -icanon min 1 time 0 -echo -echoe -echok -echonl -crterase noflsh"
)
try: ch = sys.stdin.read(1)
except:
os.system("stty sane")
msg("\nUser interrupt")
sys.exit(1)
else:
os.system("stty sane")
return ch
def my_raw_input(prompt):
msg_r(prompt)
try: pw = raw_input()
try: reply = raw_input()
except:
msg("\nInterrupted by user")
msg("\nUser interrupt")
sys.exit(1)
return pw
return reply
def _get_hash_params(hash_preset):
@ -175,7 +192,8 @@ future, you must continue using these same parameters
def confirm_or_exit(message, question):
if message.strip(): msg(message.strip())
m = message.strip()
if m: msg(m)
msg("")
conf_msg = "Type uppercase 'YES' to confirm: "
@ -185,13 +203,28 @@ def confirm_or_exit(message, question):
else:
prompt = "Are you sure you want to %s?\n%s" % (question,conf_msg)
if _my_raw_input(prompt).strip() != "YES":
if my_raw_input(prompt).strip() != "YES":
msg("Program aborted by user")
sys.exit(2)
msg("")
def user_confirm(prompt,default_yes=False):
q = "(Y/n)" if default_yes else "(y/N)"
while True:
reply = get_char("%s %s: " % (prompt, q)).strip()
msg("")
if not reply:
return True if default_yes else False
elif reply in 'yY': return True
elif reply in 'nN': return False
else: msg("Invalid reply")
def set_if_unset_and_typeconvert(opts,item):
for opt,var,dtype in item:
@ -282,10 +315,10 @@ just hit ENTER twice.
for i in range(passwd_max_tries):
if 'echo_passphrase' in opts:
return _my_raw_input("Enter %s: " % what)
return my_raw_input("Enter %s: " % what)
else:
ret = _my_getpass("Enter %s: " % what)
if ret == _my_getpass("Repeat %s: " % what):
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
@ -359,6 +392,16 @@ def get_default_wordlist():
return wl.strip().split("\n")
def open_file_or_exit(filename,mode):
try:
f = open(filename, mode)
except:
what = "reading" if mode == 'r' else "writing"
msg("Unable to open file '%s' for %s" % (infile,what))
sys.exit(2)
return f
def write_to_file(outfile,data,confirm=False):
if confirm:
@ -370,11 +413,7 @@ def write_to_file(outfile,data,confirm=False):
else:
confirm_or_exit("","File '%s' already exists\nOverwrite?" % outfile)
try:
f = open(outfile,'w')
except:
msg("Failed to open file '%s' for writing" % outfile)
sys.exit(2)
f = open_file_or_exit(outfile,'w')
try:
f.write(data)
@ -446,12 +485,12 @@ def col4(s):
nondiv = 1 if len(s) % 4 else 0
return " ".join([s[4*i:4*i+4] for i in range(len(s)/4 + nondiv)])
def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
def make_timestamp():
import time
tv = time.gmtime(time.time())[:6]
ts_hdr = "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
return "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
seed_id = make_chksum_8(seed)
seed_len = str(len(seed)*8)
@ -470,7 +509,8 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
sf = b58encode_pad(salt)
esf = b58encode_pad(enc_seed)
metadata = seed_id.lower(),key_id.lower(),seed_len,pw_status,ts_hdr
metadata = seed_id.lower(),key_id.lower(),\
seed_len,pw_status,make_timestamp()
lines = (
label,
@ -560,11 +600,7 @@ def get_data_from_wallet(infile,opts):
msg("Getting {} wallet data from file: {}".format(proj_name,infile))
try:
f = open(infile, 'r')
except:
msg("Unable to open file '" + infile + "' for reading")
sys.exit(2)
f = open_file_or_exit(infile, 'r')
lines = [i.strip() for i in f.readlines()]
f.close()
@ -602,23 +638,27 @@ def get_data_from_wallet(infile,opts):
def _get_words_from_user(opts, prompt):
# split() also strips
if 'echo_passphrase' in opts:
return _my_raw_input(prompt).split()
return my_raw_input(prompt).split()
else:
return _my_getpass(prompt).split()
return my_getpass(prompt).split()
def _get_words_from_file(infile,what):
msg("Getting %s data from file '%s'" % (what,infile))
try:
f = open(infile, 'r')
except:
msg("Unable to open file '%s' for reading" % infile)
sys.exit(2)
lines = f.readlines()
f.close()
msg("Getting %s from file '%s'" % (what,infile))
f = open_file_or_exit(infile, 'r')
lines = f.readlines(); f.close()
# split() also strips
return [w for l in lines for w in l.split()]
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)
@ -709,7 +749,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
def get_seed(infile,opts,no_wallet=False):
if 'from_mnemonic' in opts:
prompt = "Enter mnemonic: "
words = get_words(infile,"mnemonic",prompt,opts)
words = get_words(infile,"mnemonic data",prompt,opts)
wl = get_default_wordlist()
from mmgen.mnemonic import get_seed_from_mnemonic
@ -723,11 +763,11 @@ def get_seed(infile,opts,no_wallet=False):
*_get_from_brain_opt_params(opts)),
"continue")
prompt = "Enter brainwallet passphrase: "
words = get_words(infile,"brainwallet",prompt,opts)
words = get_words(infile,"brainwallet data",prompt,opts)
return _get_seed_from_brain_passphrase(words,opts)
elif 'from_seed' in opts:
prompt = "Enter seed in %s format: " % seed_ext
words = get_words(infile,"seed",prompt,opts)
words = get_words(infile,"seed data",prompt,opts)
return get_seed_from_seed_data(words)
elif no_wallet:
return False

View file

@ -0,0 +1,61 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by 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/>.
"""
bitcoind-walletunlock.py: Unlock a Bitcoin wallet securely
"""
import sys
from mmgen.Opts import *
from mmgen.tx import *
from mmgen.utils import msg, my_getpass, my_raw_input
prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
'desc': "Unlock a Bitcoin wallet securely",
'usage': "[opts]",
'options': """
-h, --help Print this help message
-e, --echo-passphrase Print passphrase to screen when typing it
"""
}
short_opts = "he"
long_opts = "help","echo_passphrase"
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
c = connect_to_bitcoind()
prompt = "Enter passphrase: "
if 'echo_passphrase' in opts:
password = my_raw_input(prompt)
else:
password = my_getpass(prompt)
from bitcoinrpc import exceptions
try:
c.walletpassphrase(password, 9999)
except exceptions.WalletWrongEncState:
msg("Wallet is unencrypted")
except exceptions.WalletPassphraseIncorrect:
msg("Passphrase incorrect")
except exceptions.WalletAlreadyUnlocked:
msg("WARNING: Wallet already unlocked!")

View file

@ -3,7 +3,7 @@ from distutils.core import setup
setup(
name = 'mmgen',
version = '0.6',
version = '0.6.1',
author = 'Philemon',
author_email = 'mmgen-py@yandex.com',
url = 'https://github.com/mmgen/mmgen',
@ -17,6 +17,7 @@ setup(
'mmgen.mnemonic',
'mmgen.mn_tirosh',
'mmgen.Opts',
'mmgen.tx',
'mmgen.utils',
'mmgen.walletgen'
],
@ -25,6 +26,8 @@ setup(
'mmgen-keygen',
'mmgen-passchg',
'mmgen-walletchk',
'mmgen-walletgen'
'mmgen-walletgen',
'mmgen-txcreate',
'mmgen-txsign'
])]
)