renamed: scripts/pywallet.py -> mmgen-pywallet

This commit is contained in:
The MMGen Project 2014-02-02 22:53:47 +04:00
commit e18c75eb36
3 changed files with 135 additions and 164 deletions

View file

@ -1,4 +1,23 @@
#!/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-pywallet: Dump contents of a bitcoind wallet to file
"""
# Changes by Philemon:
# password entry at prompt
@ -24,6 +43,7 @@
# Alex Martelli (http://www.aleax.it)
# Ported from C code written by Laurent Haan (http://www.progressive-coding.com)
from mmgen.Opts import *
from mmgen.utils import msg
from bsddb.db import *
import os, sys, time
@ -46,15 +66,42 @@ json_db = {}
private_keys = []
password = None
# def determine_db_dir():
# import os
# import os.path
# import platform
# if platform.system() == "Darwin":
# return os.path.expanduser("~/Library/Application Support/Bitcoin/")
# elif platform.system() == "Windows":
# return os.path.join(os.environ['APPDATA'], "Bitcoin")
# return os.path.expanduser("~/.bitcoin")
prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
'desc': "Dump contents of a bitcoind wallet to file",
'usage': "[opts] <bitcoind wallet file>",
'options': """
-h, --help Print this help message
-d, --outdir d Specify an alternate directory 'd' for output
-j, --json Dump wallet in json format
-k, --keys Dump all private keys (flat list)
-a, --addrs Dump all addresses (flat list)
-K, --keysforaddrs f Dump private keys for addresses listed in file 'f'
-q, --quiet Suppress warnings; overwrite files without asking
-S, --stdout Dump to stdout rather than file
"""
}
short_opts = "hd:jkaK:qS"
long_opts = "help","outdir=","json","keys","addrs","keysforaddrs=","quiet","stdout"
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
from mmgen.utils import check_infile,check_opts
if len(cmd_args) == 1:
check_infile(cmd_args[0])
else:
usage(help_data)
check_opts(opts,('outdir',))
if ('json' not in opts and 'keys' not in opts
and 'addrs' not in opts and 'keysforaddrs' not in opts):
usage(help_data)
# from the SlowAES project, http://code.google.com/p/slowaes (aes.py)
@ -1465,7 +1512,6 @@ def update_wallet(db, type, data):
# wallet.dat reader / writer
def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transactions, transaction_filter):
global password
db = open_wallet(db_env, db_file)
@ -1475,6 +1521,8 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction
def item_callback(type, d):
global password
if type == "name":
json_db['names'][d['hash']] = d['name']
@ -1522,7 +1570,12 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction
mkey['vchOtherDerivationParameters'] = d['vchOtherDerivationParameters'].encode('hex')
json_db['mkey'] = mkey
if password:
if password == None and \
('json' in opts or 'keysforaddrs' in opts or 'keys' in opts):
from mmgen.utils import my_getpass
password = my_getpass("Enter password: ")
if password != None:
global crypter
if crypter == 'pycrypto':
crypter = Crypter_pycrypto()
@ -1543,7 +1596,7 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction
elif type == "acc":
json_db['acc'] = d['account']
msg("Account %s (current key: %s)"%(d['account'], public_key_to_bc_address(d['public_key'])))
# msg("Account %s (current key: %s)"%(d['account'], public_key_to_bc_address(d['public_key'])))
elif type == "acentry":
json_db['acentry'] = (d['account'], d['nCreditDebit'], d['otherAccount'], time.ctime(d['nTime']), d['n'], d['comment'])
@ -1565,14 +1618,7 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction
else:
k["reserve"] = 1
crypted = 'mkey' in json_db.keys()
if (crypted and not password) and \
(options.dump or options.keysforaddrs or options.privkeys):
print("Encrypted wallet. Specify '--password' to decrypt")
sys.exit(1)
if crypted and password:
if 'mkey' in json_db.keys() and password != None:
check = True
for k in json_db['keys']:
ckey = k['ckey'].decode('hex')
@ -1599,157 +1645,74 @@ def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transaction
del(json_db['pool'])
del(json_db['names'])
def importprivkey(db, sec):
pkey = regenerate_key(sec)
if not pkey:
return False
compressed = is_compressed(sec)
secret = GetSecret(pkey)
private_key = GetPrivKey(pkey, compressed)
public_key = GetPubKey(pkey, compressed)
addr = public_key_to_bc_address(public_key)
print "Address: %s" % addr
print "Privkey: %s" % SecretToASecret(secret, compressed)
global crypter, password, json_db
crypted = 'mkey' in json_db.keys()
if crypted:
if password:
crypter.SetIV(Hash(public_key))
crypted_key = crypter.Encrypt(secret)
update_wallet(db, 'ckey', { 'public_key' : public_key, 'crypted_key' : crypted_key })
update_wallet(db, 'name', { 'hash' : addr, 'name' : '' })
return True
else:
logging.error("password not specified")
sys.exit(1)
else:
update_wallet(db, 'key', { 'public_key' : public_key, 'private_key' : private_key })
update_wallet(db, 'name', { 'hash' : addr, 'name' : '' })
return True
return False
def parse_wallet_file_arg(path):
import os
try:
os.stat(path)
except:
print "File '%s' does not exist" % path
sys.exit(1)
s = path.rfind("/")
if path[0] == '/':
if s == 0:
return "/", path
else:
return path[0:s], path[s+1:]
if s == 0: return "/", path
else: return path[0:s], path[s+1:]
else:
curdir = os.path.abspath(".")
if s == -1:
return curdir,path
else:
return curdir + "/" + path[0:s], path[s+1:]
if s == -1: return curdir,path
else: return curdir + "/" + path[0:s], path[s+1:]
# main()
from optparse import OptionParser
def main():
global max_version, addrtype
parser = OptionParser(usage="%prog [options] walletfile", version="%prog 1.2")
parser.add_option("--dumpwallet", dest="dump", action="store_true",
help="dump wallet in json format")
parser.add_option("--privkeys", dest="privkeys", action="store_true",
help="dump all private keys in wallet (flat list)")
parser.add_option("--addrs", dest="addrs", action="store_true",
help="dump all addresses in wallet (flat list)")
parser.add_option("--keysforaddrs", dest="keysforaddrs",
help="dump all private keys corresponding to addresses in file KEYSFORADDRS (flat list)")
parser.add_option("--importprivkey", dest="key",
help="import private key from vanitygen")
parser.add_option("--testnet", dest="testnet", action="store_true",
help="use testnet subdirectory and address type")
parser.add_option("--password", dest="password", action="store_true",
help="prompt for password for the encrypted wallet")
global options
(options, args) = parser.parse_args()
if options.dump is None and options.key is None \
and options.keysforaddrs is None \
and options.addrs is None and options.privkeys is None:
print "A mandatory option is missing\n"
parser.print_help()
sys.exit(1)
db_dir,db_file = parse_wallet_file_arg(args[0])
db_dir,db_file = parse_wallet_file_arg(cmd_args[0])
# print "[%s] [%s]" % (db_dir,db_file)
# sys.exit()
if options.testnet: addrtype = 111
db_env = create_env(db_dir)
db_env = create_env(db_dir)
read_wallet(json_db, db_env, db_file, True, True, "")
global password
if json_db.get('minversion') > max_version:
print "Version mismatch (must be <= %d)" % max_version
exit(1)
if options.password:
from mmgen.utils import my_getpass
password = my_getpass("Enter password: ")
wallet_addrs = [i['addr'] for i in json_db['keys']]
read_wallet(json_db, db_env, db_file, True, True, "")
data,ext,what = [],"",""
if json_db.get('minversion') > max_version:
print "Version mismatch (must be <= %d)" % max_version
exit(1)
if 'json' in opts:
data = [ json.dumps(json_db, sort_keys=True, indent=4) ]
ext,what = "json","json dump"
if options.dump:
print json.dumps(json_db, sort_keys=True, indent=4)
elif 'keys' in opts:
for i in json_db['keys']: data.append(i['sec'])
ext,what = "keys","private keys"
elif options.privkeys:
for i in json_db['keys']:
print i['sec']
elif 'addrs' in opts:
for i in json_db['keys']: data.append(i['addr'])
ext,what = "addrs","addresses"
elif options.addrs:
for i in json_db['keys']:
print i['addr']
elif 'keysforaddrs' in opts:
from mmgen.utils import get_lines_from_file
usr_addrs = get_lines_from_file(opts['keysforaddrs'],"addresses")
for addr in usr_addrs:
try:
idx = wallet_addrs.index(addr)
data.append(json_db['keys'][idx]['sec'])
except:
msg("WARNING: Address '%s' not found" % addr)
ext,what = "keys","private keys"
elif options.keysforaddrs:
from mmgen.utils import get_lines_from_file
addrs = get_lines_from_file(options.keysforaddrs,"addresses")
for i in json_db['keys']:
if i['addr'] in addrs:
print i['sec']
len_arg = "%s" % len(wallet_addrs) \
if len(data) == len(wallet_addrs) or ext == "json" \
else "%s:%s" % (len(data),len(wallet_addrs))
elif options.key:
if options.key in private_keys:
print "Already exists"
else:
db = open_wallet(db_env, db_file, writable=True)
from mmgen.utils import make_chksum_8,write_walletdat_dump_to_file,write_to_stdout
wallet_id = make_chksum_8(str(sorted(wallet_addrs)))
if importprivkey(db, options.key):
print "Imported successfully"
else:
print "Bad private key"
data = "\n".join(data) + "\n"
db.close()
if __name__ == '__main__':
main()
# Output data
if 'stdout' in opts:
if 'addrs' in opts or 'quiet' in opts: confirm = False
else: confirm = True
write_to_stdout(data,"secret keys",confirm)
elif not sys.stdout.isatty():
write_to_stdout(data,"secret keys",confirm=False)
else:
write_walletdat_dump_to_file(wallet_id, data, len_arg, ext, what, opts)

View file

@ -103,15 +103,15 @@ def check_opts(opts,keys):
try: mode = os.stat(d).st_mode
except:
msg("Unable to stat requested %s '%s'. Aborting" % (what,d))
msg("Unable to stat requested %s '%s'" % (what,d))
sys.exit(1)
if not stat.S_ISDIR(mode):
msg("Requested %s '%s' is not a directory. Aborting" %(what,d))
msg("Requested %s '%s' is not a directory" % (what,d))
sys.exit(1)
if not os.access(d, os.W_OK|os.X_OK):
msg("Requested %s '%s' is unwritable by you. Aborting"%(what,d))
msg("Requested %s '%s' is unwritable by you" % (what,d))
sys.exit(1)
elif key == 'label':
@ -266,15 +266,15 @@ def check_infile(f):
try: mode = os.stat(f).st_mode
except:
msg("Unable to stat requested input file '%s'. Aborting" % f)
msg("Unable to stat requested input file '%s'" % f)
sys.exit(1)
if not stat.S_ISREG(mode) or stat.S_ISLNK(mode):
msg("Requested input file '%s' is not a file. Aborting" % f)
msg("Requested input file '%s' is not a file" % f)
sys.exit(1)
if not os.access(f, os.R_OK):
msg("Requested input file '%s' is unreadable by you. Aborting" % f)
msg("Requested input file '%s' is unreadable by you" % f)
sys.exit(1)
@ -394,13 +394,13 @@ def encrypt_seed(seed, key, opts):
if dec_seed == seed: msg("done")
else:
msg("FAILED.\nDecrypted seed doesn't match original seed. Aborting.")
msg("FAILED.\nDecrypted seed doesn't match original seed")
sys.exit(2)
return enc_seed
def write_to_stdout(data, what, confirm=True):
def write_to_stdout(data, what, confirm=True):
if sys.stdout.isatty() and confirm:
confirm_or_exit("",'output {} to screen'.format(what))
elif not sys.stdout.isatty():
@ -423,7 +423,7 @@ def open_file_or_exit(filename,mode):
f = open(filename, mode)
except:
what = "reading" if mode == 'r' else "writing"
msg("Unable to open file '%s' for %s" % (infile,what))
msg("Unable to open file '%s' for %s" % (filename,what))
sys.exit(2)
return f
@ -561,6 +561,14 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
_display_control_data(label,metadata,hash_preset,salt,enc_seed)
def write_walletdat_dump_to_file(wallet_id,data,num_keys,ext,what,opts):
outfile = "wd_{}[{}].{}".format(wallet_id,num_keys,ext)
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
write_to_file(outfile,data,confirm=False)
msg("wallet.dat %s saved to file '%s'" % (what,outfile))
def compare_checksums(chksum1, desc1, chksum2, desc2):
if chksum1.lower() == chksum2.lower():

View file

@ -67,7 +67,7 @@ displayed on the screen.
"User random data successfully acquired. Press ENTER to continue: ")
raw_input()
except:
msg("\nUser random input interrupted. Aborting")
msg("\nUser random input interrupted")
sys.exit(1)
finally:
os.system("stty sane")