X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=lib%2Fbitcoin.py;h=0d499420819a7746969d5094e15182ba856d0513;hb=3cbe11a42473af52e7c5e002c36aaaf32646f627;hp=178aea374dbcef4be3c3561e769b6c6eb86f540e;hpb=73c33820dbf17eadd049ebeb5d295e124bfadfc6;p=electrum-nvc.git diff --git a/lib/bitcoin.py b/lib/bitcoin.py index 178aea3..0d49942 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -24,6 +24,7 @@ import sys import hmac from util import print_error +from version import SEED_PREFIX try: import ecdsa @@ -44,6 +45,7 @@ MIN_RELAY_TX_FEE = 1000 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s)) DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e)) + def pw_encode(s, password): if password: secret = Hash(password) @@ -51,6 +53,7 @@ def pw_encode(s, password): else: return s + def pw_decode(s, password): if password is not None: secret = Hash(password) @@ -63,17 +66,16 @@ def pw_decode(s, password): return s - - - def rev_hex(s): return s.decode('hex')[::-1].encode('hex') + def int_to_hex(i, length=1): s = hex(i)[2:].rstrip('L') s = "0"*(2*length - len(s)) + s return rev_hex(s) + def var_int(i): # https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer if i<0xfd: @@ -85,6 +87,7 @@ def var_int(i): else: return "ff"+int_to_hex(i,8) + def op_push(i): if i<0x4c: return int_to_hex(i) @@ -96,25 +99,27 @@ def op_push(i): return '4e' + int_to_hex(i,4) - def sha256(x): return hashlib.sha256(x).digest() + def Hash(x): if type(x) is unicode: x=x.encode('utf-8') return sha256(sha256(x)) + hash_encode = lambda x: x[::-1].encode('hex') hash_decode = lambda x: x.decode('hex')[::-1] hmac_sha_512 = lambda x,y: hmac.new(x, y, hashlib.sha512).digest() + def mnemonic_to_seed(mnemonic, passphrase): from pbkdf2 import PBKDF2 import hmac PBKDF2_ROUNDS = 2048 return PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64) -from version import SEED_PREFIX + is_new_seed = lambda x: hmac_sha_512("Seed version", x.encode('utf8')).encode('hex')[0:2].startswith(SEED_PREFIX) def is_old_seed(seed): @@ -200,7 +205,7 @@ 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, addrtype = 0): +def hash_160_to_bc_address(h160, addrtype = 8): vh160 = chr(addrtype) + h160 h = Hash(vh160) addr = vh160 + h[0:4] @@ -214,6 +219,7 @@ def bc_address_to_hash_160(addr): __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' __b58base = len(__b58chars) + def b58encode(v): """ encode v, which is a string of bytes, to base58.""" @@ -237,6 +243,7 @@ def b58encode(v): return (__b58chars[0]*nPad) + result + def b58decode(v, length): """ decode v into a string of len bytes.""" long_value = 0L @@ -266,6 +273,7 @@ def EncodeBase58Check(vchIn): hash = Hash(vchIn) return b58encode(vchIn + hash[0:4]) + def DecodeBase58Check(psz): vchRet = b58decode(psz, None) key = vchRet[0:-4] @@ -277,15 +285,17 @@ def DecodeBase58Check(psz): else: return key + def PrivKeyToSecret(privkey): return privkey[9:9+32] -def SecretToASecret(secret, compressed=False, addrtype=0): + +def SecretToASecret(secret, compressed=False, addrtype=8): vchIn = chr((addrtype+128)&255) + secret if compressed: vchIn += '\01' return EncodeBase58Check(vchIn) -def ASecretToSecret(key, addrtype=0): +def ASecretToSecret(key, addrtype=8): vch = DecodeBase58Check(key) if vch and vch[0] == chr((addrtype+128)&255): return vch[1:] @@ -299,15 +309,19 @@ def regenerate_key(sec): b = b[0:32] return EC_KEY(b) + def GetPubKey(pubkey, compressed=False): return i2o_ECPublicKey(pubkey, compressed) + def GetPrivKey(pkey, compressed=False): return i2d_ECPrivateKey(pkey, compressed) + def GetSecret(pkey): return ('%064x' % pkey.secret).decode('hex') + def is_compressed(sec): b = ASecretToSecret(sec) return len(b) == 33 @@ -365,7 +379,7 @@ from ecdsa.util import string_to_number, number_to_string def msg_magic(message): varint = var_int(len(message)) encoded_varint = "".join([chr(int(varint[i:i+2], 16)) for i in xrange(0, len(varint), 2)]) - return "\x18Bitcoin Signed Message:\n" + encoded_varint + message + return "\x18Novacoin Signed Message:\n" + encoded_varint + message def verify_message(address, signature, message): @@ -464,7 +478,7 @@ class EC_KEY(object): def sign_message(self, message, compressed, address): private_key = ecdsa.SigningKey.from_secret_exponent( self.secret, curve = SECP256k1 ) public_key = private_key.get_verifying_key() - signature = private_key.sign_digest_deterministic( Hash( msg_magic(message) ), hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_string ) + signature = private_key.sign_digest_deterministic( Hash( msg_magic(message) ), hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_string_canonize ) assert public_key.verify_digest( signature, Hash( msg_magic(message) ), sigdecode = ecdsa.util.sigdecode_string) for i in range(4): sig = base64.b64encode( chr(27 + i + (4 if compressed else 0)) + signature ) @@ -626,37 +640,87 @@ def _CKD_pub(cK, c, s): return cK_n, c_n +BITCOIN_HEADER_PRIV = "0488ade4" +BITCOIN_HEADER_PUB = "0488b21e" + +TESTNET_HEADER_PRIV = "04358394" +TESTNET_HEADER_PUB = "043587cf" + +BITCOIN_HEADERS = (BITCOIN_HEADER_PUB, BITCOIN_HEADER_PRIV) +TESTNET_HEADERS = (TESTNET_HEADER_PUB, TESTNET_HEADER_PRIV) + +def _get_headers(testnet): + """Returns the correct headers for either testnet or bitcoin, in the form + of a 2-tuple, like (public, private).""" + if testnet: + return TESTNET_HEADERS + else: + return BITCOIN_HEADERS + def deserialize_xkey(xkey): + xkey = DecodeBase58Check(xkey) assert len(xkey) == 78 - assert xkey[0:4].encode('hex') in ["0488ade4", "0488b21e"] + + xkey_header = xkey[0:4].encode('hex') + # Determine if the key is a bitcoin key or a testnet key. + if xkey_header in TESTNET_HEADERS: + head = TESTNET_HEADER_PRIV + elif xkey_header in BITCOIN_HEADERS: + head = BITCOIN_HEADER_PRIV + else: + raise Exception("Unknown xkey header: '%s'" % xkey_header) + depth = ord(xkey[4]) fingerprint = xkey[5:9] child_number = xkey[9:13] c = xkey[13:13+32] - if xkey[0:4].encode('hex') == "0488ade4": + if xkey[0:4].encode('hex') == head: K_or_k = xkey[13+33:] else: K_or_k = xkey[13+32:] return depth, fingerprint, child_number, c, K_or_k +def get_xkey_name(xkey, testnet=False): + depth, fingerprint, child_number, c, K = deserialize_xkey(xkey) + n = int(child_number.encode('hex'), 16) + if n & BIP32_PRIME: + child_id = "%d'"%(n - BIP32_PRIME) + else: + child_id = "%d"%n + if depth == 0: + return '' + elif depth == 1: + return child_id + else: + raise BaseException("xpub depth error") + -def bip32_root(seed): +def xpub_from_xprv(xprv, testnet=False): + depth, fingerprint, child_number, c, k = deserialize_xkey(xprv) + K, cK = get_pubkeys_from_secret(k) + header_pub, _ = _get_headers(testnet) + xpub = header_pub.decode('hex') + chr(depth) + fingerprint + child_number + c + cK + return EncodeBase58Check(xpub) + + +def bip32_root(seed, testnet=False): import hmac + header_pub, header_priv = _get_headers(testnet) seed = seed.decode('hex') - I = hmac.new("Bitcoin seed", seed, hashlib.sha512).digest() + I = hmac.new("Novacoin seed", seed, hashlib.sha512).digest() master_k = I[0:32] master_c = I[32:] K, cK = get_pubkeys_from_secret(master_k) - xprv = ("0488ADE4" + "00" + "00000000" + "00000000").decode("hex") + master_c + chr(0) + master_k - xpub = ("0488B21E" + "00" + "00000000" + "00000000").decode("hex") + master_c + cK + xprv = (header_priv + "00" + "00000000" + "00000000").decode("hex") + master_c + chr(0) + master_k + xpub = (header_pub + "00" + "00000000" + "00000000").decode("hex") + master_c + cK return EncodeBase58Check(xprv), EncodeBase58Check(xpub) - -def bip32_private_derivation(xprv, branch, sequence): +def bip32_private_derivation(xprv, branch, sequence, testnet=False): + header_pub, header_priv = _get_headers(testnet) depth, fingerprint, child_number, c, k = deserialize_xkey(xprv) assert sequence.startswith(branch) sequence = sequence[len(branch):] @@ -671,13 +735,13 @@ def bip32_private_derivation(xprv, branch, sequence): fingerprint = hash_160(parent_cK)[0:4] child_number = ("%08X"%i).decode('hex') K, cK = get_pubkeys_from_secret(k) - xprv = "0488ADE4".decode('hex') + chr(depth) + fingerprint + child_number + c + chr(0) + k - xpub = "0488B21E".decode('hex') + chr(depth) + fingerprint + child_number + c + cK + xprv = header_priv.decode('hex') + chr(depth) + fingerprint + child_number + c + chr(0) + k + xpub = header_pub.decode('hex') + chr(depth) + fingerprint + child_number + c + cK return EncodeBase58Check(xprv), EncodeBase58Check(xpub) - -def bip32_public_derivation(xpub, branch, sequence): +def bip32_public_derivation(xpub, branch, sequence, testnet=False): + header_pub, _ = _get_headers(testnet) depth, fingerprint, child_number, c, cK = deserialize_xkey(xpub) assert sequence.startswith(branch) sequence = sequence[len(branch):] @@ -690,18 +754,11 @@ def bip32_public_derivation(xpub, branch, sequence): fingerprint = hash_160(parent_cK)[0:4] child_number = ("%08X"%i).decode('hex') - xpub = "0488B21E".decode('hex') + chr(depth) + fingerprint + child_number + c + cK + xpub = header_pub.decode('hex') + chr(depth) + fingerprint + child_number + c + cK return EncodeBase58Check(xpub) - - def bip32_private_key(sequence, k, chain): for i in sequence: k, chain = CKD_priv(k, chain, i) return SecretToASecret(k, True) - - - - -