# pywallet.py 1.0 # # based on http://github.com/gavinandresen/bitcointools # # Usage: pywallet.py [options] # # Options: # --version show program's version number and exit # -h, --help show this help message and exit # --dumpwallet dump wallet in json format # --importprivkey=KEY import private key from vanitygen # --datadir=DATADIR wallet directory (defaults to bitcoin default) from bsddb.db import * import os, sys, time import json import logging import struct import StringIO import traceback import socket import types import string import exceptions import Crypto.Hash.SHA256 as SHA256 import Crypto.Hash.RIPEMD160 as RIPEMD160 from Crypto.PublicKey.pubkey import * from ctypes import * TESTNET = 0 json_db = {} private_keys = [] 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") dlls = list() if 'win' in sys.platform: for d in ('libeay32.dll', 'libssl32.dll', 'ssleay32.dll'): try: dlls.append( cdll.LoadLibrary(d) ) except: pass else: dlls.append( cdll.LoadLibrary('libssl.so') ) class BIGNUM_Struct (Structure): _fields_ = [("d",c_void_p),("top",c_int),("dmax",c_int),("neg",c_int),("flags",c_int)] class BN_CTX_Struct (Structure): _fields_ = [ ("_", c_byte) ] BIGNUM = POINTER( BIGNUM_Struct ) BN_CTX = POINTER( BN_CTX_Struct ) def load_func( name, args, returns = c_int): d = sys.modules[ __name__ ].__dict__ f = None for dll in dlls: try: f = getattr(dll, name) f.argtypes = args f.restype = returns d[ name ] = f return except: pass raise ImportError('Unable to load required functions from SSL dlls') load_func( 'BN_new', [], BIGNUM ) load_func( 'BN_CTX_new', [], BN_CTX ) load_func( 'BN_CTX_free', [BN_CTX], None ) load_func( 'BN_num_bits', [BIGNUM], c_int ) load_func( 'BN_bn2bin', [BIGNUM, c_char_p] ) load_func( 'BN_bin2bn', [c_char_p, c_int, BIGNUM], BIGNUM ) load_func( 'EC_KEY_new_by_curve_name', [c_int], c_void_p ) load_func( 'EC_KEY_get0_group', [c_void_p], c_void_p) load_func( 'EC_KEY_get0_private_key', [c_void_p], BIGNUM) load_func( 'EC_POINT_new', [c_void_p], c_void_p) load_func( 'EC_POINT_free', [c_void_p]) load_func( 'EC_POINT_mul', [c_void_p, c_void_p, BIGNUM, c_void_p, BIGNUM, BN_CTX], c_int) load_func( 'EC_KEY_set_private_key', [c_void_p, BIGNUM], c_void_p) load_func( 'EC_KEY_set_public_key', [c_void_p, c_void_p], c_void_p) load_func( 'i2d_ECPrivateKey', [ c_void_p, POINTER(POINTER(c_char))], c_int ) load_func( 'i2o_ECPublicKey', [ c_void_p, POINTER(POINTER(c_char))], c_int ) def BN_num_bytes(a): return ((BN_num_bits(a)+7)/8) NID_secp256k1 = 714 pkey = 0 def EC_KEY_regenerate_key(eckey, priv_key): group = EC_KEY_get0_group(eckey) ctx = BN_CTX_new() pub_key = EC_POINT_new(group) EC_POINT_mul(group, pub_key, priv_key, None, None, ctx) EC_KEY_set_private_key(eckey, priv_key) EC_KEY_set_public_key(eckey, pub_key) EC_POINT_free(pub_key) BN_CTX_free(ctx) def GetSecret(pkey): bn = EC_KEY_get0_private_key(pkey) nSize = BN_num_bytes(bn) b = create_string_buffer(nSize) BN_bn2bin(bn, b) return b.raw def GetPrivKey(pkey): nSize = i2d_ECPrivateKey(pkey, None) p = create_string_buffer(nSize) i2d_ECPrivateKey(pkey, byref(cast(p, POINTER(c_char)))) return p.raw def GetPubKey(pkey): nSize = i2o_ECPublicKey(pkey, None) p = create_string_buffer(nSize) i2o_ECPublicKey(pkey, byref(cast(p, POINTER(c_char)))) return p.raw def Hash(data): h1 = SHA256.new(data).digest() h2 = SHA256.new(h1).digest() return h2 def EncodeBase58Check(vchIn): hash = Hash(vchIn) return b58encode(vchIn + hash[0:4]) def DecodeBase58Check(psz): vchRet = b58decode(psz, None) vch = vchRet[0:-4] chk = vchRet[-4:] hash = Hash(vch)[0:4] if hash != chk: return None else: return vch def SecretToASecret(privkey): vchSecret = privkey[9:9+32] # add 1-byte version number vchIn = "\x80" + vchSecret return EncodeBase58Check(vchIn) def ASecretToSecret(key): vch = DecodeBase58Check(key) if vch: return vch[1:] else: return False def importprivkey(db, key): vchSecret = ASecretToSecret(key) if not vchSecret: return False pkey = EC_KEY_new_by_curve_name(NID_secp256k1) bn = BN_bin2bn(vchSecret, 32, BN_new()) EC_KEY_regenerate_key(pkey, bn) secret = GetSecret(pkey) private_key = GetPrivKey(pkey) public_key = GetPubKey(pkey) addr = public_key_to_bc_address(public_key) print "Address: %s" % addr print "Privkey: %s" % SecretToASecret(private_key) type = 'key' data = { 'public_key' : public_key, 'private_key' : private_key } update_wallet(db, type, data) type = 'name' data = { 'hash' : addr, 'name' : '' } update_wallet(db, type, data) return True __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' __b58base = len(__b58chars) def b58encode(v): """ encode v, which is a string of bytes, to base58. """ long_value = 0L for (i, c) in enumerate(v[::-1]): long_value += (256**i) * ord(c) result = '' while long_value >= __b58base: div, mod = divmod(long_value, __b58base) result = __b58chars[mod] + result long_value = div result = __b58chars[long_value] + result # Bitcoin does a little leading-zero-compression: # leading 0-bytes in the input become leading-1s nPad = 0 for c in v: if c == '\0': nPad += 1 else: break return (__b58chars[0]*nPad) + result def b58decode(v, length): """ decode v into a string of len bytes """ long_value = 0L for (i, c) in enumerate(v[::-1]): long_value += __b58chars.find(c) * (__b58base**i) result = '' while long_value >= 256: div, mod = divmod(long_value, 256) result = chr(mod) + result long_value = div result = chr(long_value) + result nPad = 0 for c in v: if c == __b58chars[0]: nPad += 1 else: break result = chr(0)*nPad + result if length is not None and len(result) != length: return None return result def hash_160(public_key): h1 = SHA256.new(public_key).digest() h2 = RIPEMD160.new(h1).digest() return h2 def public_key_to_bc_address(public_key): h160 = hash_160(public_key) return hash_160_to_bc_address(h160) def hash_160_to_bc_address(h160): if TESTNET: vh160 = "\x6f" + h160 # \x6f is testnet else: vh160 = "\x00" + h160 # \x00 is version 0 h3=SHA256.new(SHA256.new(vh160).digest()).digest() addr=vh160+h3[0:4] return b58encode(addr) def bc_address_to_hash_160(addr): bytes = b58decode(addr, 25) return bytes[1:21] def long_hex(bytes): return bytes.encode('hex_codec') def short_hex(bytes): t = bytes.encode('hex_codec') if len(t) < 32: return t return t[0:32]+"..."+t[-32:] def create_env(db_dir=None): if db_dir is None: db_dir = determine_db_dir() db_env = DBEnv(0) r = db_env.open(db_dir, (DB_CREATE|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL| DB_INIT_TXN|DB_THREAD|DB_RECOVER)) return db_env def parse_CAddress(vds): d = {} d['nVersion'] = vds.read_int32() d['nTime'] = vds.read_uint32() d['nServices'] = vds.read_uint64() d['pchReserved'] = vds.read_bytes(12) d['ip'] = socket.inet_ntoa(vds.read_bytes(4)) d['port'] = vds.read_uint16() return d def deserialize_CAddress(d): return d['ip']+":"+str(d['port'])+" (lastseen: %s)"%(time.ctime(d['nTime']),) def parse_setting(setting, vds): if setting[0] == "f": # flag (boolean) settings return str(vds.read_boolean()) elif setting[0:4] == "addr": # CAddress d = parse_CAddress(vds) return deserialize_CAddress(d) elif setting == "nTransactionFee": return vds.read_int64() elif setting == "nLimitProcessors": return vds.read_int32() return 'unknown setting' class SerializationError(Exception): """ Thrown when there's a problem deserializing or serializing """ class BCDataStream(object): def __init__(self): self.input = None self.read_cursor = 0 def clear(self): self.input = None self.read_cursor = 0 def write(self, bytes): # Initialize with string of bytes if self.input is None: self.input = bytes else: self.input += bytes def map_file(self, file, start): # Initialize with bytes from file self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) self.read_cursor = start def seek_file(self, position): self.read_cursor = position def close_file(self): self.input.close() def read_string(self): # Strings are encoded depending on length: # 0 to 252 : 1-byte-length followed by bytes (if any) # 253 to 65,535 : byte'253' 2-byte-length followed by bytes # 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes # ... and the Bitcoin client is coded to understand: # greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string # ... but I don't think it actually handles any strings that big. if self.input is None: raise SerializationError("call write(bytes) before trying to deserialize") try: length = self.read_compact_size() except IndexError: raise SerializationError("attempt to read past end of buffer") return self.read_bytes(length) def write_string(self, string): # Length-encoded as with read-string self.write_compact_size(len(string)) self.write(string) def read_bytes(self, length): try: result = self.input[self.read_cursor:self.read_cursor+length] self.read_cursor += length return result except IndexError: raise SerializationError("attempt to read past end of buffer") return '' def read_boolean(self): return self.read_bytes(1)[0] != chr(0) def read_int16(self): return self._read_num('