def test_wif_exceptions(self): # not a 32-bytes private key badq = 33 * b'\x02' self.assertRaises(ValueError, wif_from_prvkey, badq, True) #wif_from_prvkey(badq, True) # private key not in (0, n) badq = ec.n self.assertRaises(ValueError, wif_from_prvkey, badq, True) #wif_from_prvkey(badq, True) # Not a private key WIF: missing leading 0x80 payload = b'\x81' + octets_from_int(badq, ec.psize) badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a compressed WIF: missing trailing 0x01 payload = b'\x80' + octets_from_int(badq, ec.psize) + b'\x00' badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a WIF: wrong size (35) payload = b'\x80' + octets_from_int(badq, ec.psize) + b'\x01\x00' badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a WIF: private key not in (0, n) payload = b'\x80' + octets_from_int(badq, ec.psize) badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif)
def test_utils(self): # root key, zero depth xkey = b"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" xdict = bip32.parse(xkey) decoded_key = base58.decode(xkey, 78) self.assertEqual(xdict["version"], decoded_key[:4]) self.assertEqual(xdict["depth"], decoded_key[4]) self.assertEqual(xdict["parent_fingerprint"], decoded_key[5:9]) self.assertEqual(xdict["index"], decoded_key[9:13]) self.assertEqual(xdict["chain_code"], decoded_key[13:45]) self.assertEqual(xdict["key"], decoded_key[45:]) # zero depth with non-zero parent_fingerprint f2 = b'\x01\x01\x01\x01' invalid_key = base58.encode(xkey[:5] + f2 + xkey[9:]) self.assertRaises(ValueError, bip32.parse, invalid_key) # bip32.parse(invalid_key) # zero depth with non-zero index i2 = b'\x01\x01\x01\x01' invalid_key = base58.encode(xkey[:9] + i2 + xkey[13:]) self.assertRaises(ValueError, bip32.parse, invalid_key) # bip32.parse(invalid_key) # non-zero depth (255) with zero parent_fingerprint d2 = b'ff' invalid_key = base58.encode(xkey[:4] + d2 + xkey[5:]) self.assertRaises(ValueError, bip32.parse, invalid_key) # bip32.parse(invalid_key) # master key provided self.assertRaises(ValueError, bip32.parent_fingerprint, xkey) # bip32.parent_fingerprint(xkey) f = bip32.fingerprint(xkey) child_key = bip32.ckd(xkey, 0) f2 = bip32.parent_fingerprint(child_key) self.assertEqual(f, f2) # Derivation path final depth 256>255 self.assertRaises(ValueError, bip32.derive, child_key, "." + 255 * "/0") #bip32.derive(child_key, "."+255*"/0") # Empty derivation path self.assertRaises(ValueError, bip32.derive, child_key, "") #bip32.derive(child_key, "") # Invalid derivation path root: ";" self.assertRaises(ValueError, bip32.derive, child_key, ";/0") #bip32.derive(child_key, ";/0") # Derivation path depth 256>255 self.assertRaises(ValueError, bip32.derive, child_key, "." + 256 * "/0") #bip32.derive(child_key, "." + 256*"/0") # xkey is not a public one self.assertRaises(ValueError, bip32.p2pkh_address_from_xpub, xkey)
def test_hello_world(self): self.assertEqual(base58.encode(b'hello world'), b'StV1DL6CwTryKyV') self.assertEqual(base58.decode(b'StV1DL6CwTryKyV'), b'hello world') self.assertEqual(base58.decode(base58.encode(b'hello world')), b'hello world') self.assertEqual(base58.encode(base58.decode(b'StV1DL6CwTryKyV')), b'StV1DL6CwTryKyV')
def test_trailing_zeros(self): self.assertEqual(base58.encode(b'\x00\x00hello world'), b'11StV1DL6CwTryKyV') self.assertEqual(base58.decode(b'11StV1DL6CwTryKyV'), b'\x00\x00hello world') self.assertEqual(base58.decode(base58.encode(b'\0\0hello world')), b'\x00\x00hello world') self.assertEqual(base58.encode(base58.decode(b'11StV1DL6CwTryKyV')), b'11StV1DL6CwTryKyV')
def test_exceptions(self): # Invalid base58 address prefix b'\xf5' payload = b'\xf5' pubkey = '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' payload += hash160(pubkey) invalid_address = base58.encode(payload) self.assertRaises(ValueError, h160_from_base58_address, invalid_address) #_h160_from_base58_address(invalid_address) # Invalid SEC pubkey length: 34-bytes self.assertRaises(ValueError, p2pkh_address, pubkey + '00')
def test_wif(self): # https://en.bitcoin.it/wiki/Wallet_import_format prvkey = 0xC28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D uncompressedKey = b'\x80' + prvkey.to_bytes(32, byteorder='big') uncompressedWIF = b'5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ' wif = base58.encode(uncompressedKey) self.assertEqual(wif, uncompressedWIF) key = base58.decode(uncompressedWIF) self.assertEqual(key, uncompressedKey) compressedKey = b'\x80' + \ prvkey.to_bytes(32, byteorder='big') + b'\x01' compressedWIF = b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617' wif = base58.encode(compressedKey) self.assertEqual(wif, compressedWIF) key = base58.decode(compressedWIF) self.assertEqual(key, compressedKey) # string compressedWIF = b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617' key = base58.decode(compressedWIF) self.assertEqual(key, compressedKey)
def test_exceptions(self): # int is not hex-string or bytes self.assertRaises(TypeError, base58.encode, 3) encoded = base58.encode(b"test") # unexpected decoded length wrong_length = len(encoded) - 1 self.assertRaises(ValueError, base58.decode, encoded, wrong_length) # checksum is invalid invalidChecksum = encoded[:-4] + b'1111' self.assertRaises(ValueError, base58.decode, invalidChecksum, 4) # non-ascii character self.assertRaises(ValueError, base58.decode, "hèllo world")
def address_from_pubkey_bytes(inp, version=b'\x00'): vh160 = version + hash160(inp) return base58.encode(vh160)
print(h4.hex()) print( "\n*** [7] First 4 bytes of the second SHA-256 hash used as address checksum:" ) print(h4[:4].hex()) print("\n*** [8] checksum added at the end of extended RIPEMD-160 hash:") addr = vh160 + h4[:4] print(addr.hex()) print("\n*** [9] Base58 encoded address from uncompressed PubKey") address = base58._encode(addr) print(address) assert (address == b'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM') assert (base58.encode(vh160) == b'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM') print("\n*** steps [5]-[9] are also known as Base58Check encode") def pubkey_bytes_from_prvkey(prvkey, compressed=True): PubKey = mult(prvkey) if compressed: prefix = b'\x02' if (PubKey[1] % 2 == 0) else b'\x03' return prefix + PubKey[0].to_bytes(32, byteorder='big') else: prefix = b'\x04' return prefix + PubKey[0].to_bytes(32, byteorder='big') + \ PubKey[1].to_bytes(32, byteorder='big')
h1 = sha256(payload).digest() print(h1.hex()) print("\n*** [4] SHA-256 hashing of the SHA-256:") h2 = sha256(h1).digest() print(h2.hex()) print("\n*** [5] First 4 bytes of the double SHA-256 used as checksum:") print(h2[:4].hex()) print("\n*** [6] checksum added at the end of extended key:") checksummed_payload = payload + h2[:4] print(checksummed_payload.hex()) print("\n*** [7] Base58 encoding") wif = base58.encode(checksummed_payload) print(wif) assert wif == b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617', "failure" assert base58.encode_check( payload ) == b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617', "failure" print("\n****** WIF to private key ******") print("\n*** [1] Base58 WIF") print(wif) compressed = len(wif) - 51 print("compressed" if (compressed == 1) else "uncompressed") print("\n*** [2] Base58 decoding") checksummed_payload = base58.decode(wif)
print("\n*** [6] SHA-256 hashing of the result of the previous SHA-256 hash:") h4 = hashlib.sha256(h3).digest() print(h4.hex()) print( "\n*** [7] First 4 bytes of the second SHA-256 hash used as address checksum:" ) print(h4[:4].hex()) print("\n*** [8] checksum added at the end of extended RIPEMD-160 hash:") addr = vh160 + h4[:4] print(addr.hex()) print("\n*** [9] Base58 encoded address from uncompressed PubKey") address = base58.encode(addr) assert (address == b'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM') print(address) print("\n*** steps [5]-[9] are also known as Base58Check encode") def pubkey_bytes_from_prvkey(prvkey, compressed=True): PubKey = mult(ec, prvkey, ec.G) if compressed: prefix = b'\x02' if (PubKey[1] % 2 == 0) else b'\x03' return prefix + PubKey[0].to_bytes(32, byteorder='big') else: prefix = b'\x04' return prefix + PubKey[0].to_bytes(32, byteorder='big') + \ PubKey[1].to_bytes(32, byteorder='big')
def test_empty(self): self.assertEqual(base58.encode(b''), b'') self.assertEqual(base58.decode(b''), b'') self.assertEqual(base58.decode(base58.encode(b'')), b'') self.assertEqual(base58.encode(base58.decode(b'')), b'')
def test_exceptions(self): # valid xprv xprv = b'xprv9s21ZrQH143K2oxHiQ5f7D7WYgXD9h6HAXDBuMoozDGGiYHWsq7TLBj2yvGuHTLSPCaFmUyN1v3fJRiY2A4YuNSrqQMPVLZKt76goL6LP7L' # master key provided self.assertRaises(ValueError, bip32.index, xprv) # bip32.index(xprv) # invalid index self.assertRaises(ValueError, bip32.ckd, xprv, 'invalid index') #bip32.ckd(xprv, 'invalid index') # a 4 bytes int is required, not 3 self.assertRaises(ValueError, bip32.ckd, xprv, "800000") #bip32.ckd(xprv, "800000") # Invalid derivation path root: "" self.assertRaises(ValueError, bip32.derive, xprv, '/1') #bip32.derive(xprv, '/1') # object of type 'int' has no len() self.assertRaises(TypeError, bip32.derive, xprv, 1) #bip32.derive(xprv, 1) # invalid checksum xprv = b'xppp9s21ZrQH143K2oxHiQ5f7D7WYgXD9h6HAXDBuMoozDGGiYHWsq7TLBj2yvGuHTLSPCaFmUyN1v3fJRiY2A4YuNSrqQMPVLZKt76goL6LP7L' self.assertRaises(ValueError, bip32.ckd, xprv, 0x80000000) #bip32.ckd(xprv, 0x80000000) # invalid extended key version version = b'\x04\x88\xAD\xE5' xkey = version + b'\x00' * 74 xkey = base58.encode(xkey) self.assertRaises(ValueError, bip32.ckd, xkey, 0x80000000) #bip32.ckd(xkey, 0x80000000) # invalid private version version = b'\x04\x88\xAD\xE5' seed = "5b56c417303faa3fcba7e57400e120a0ca83ec5a4fc9ffba757fbe63fbd77a89a1a3be4c67196f57c39a88b76373733891bfaba16ed27a813ceed498804c0570" self.assertRaises(ValueError, bip32.rootxprv_from_seed, seed, version) #bip32.rootxprv_from_seed(seed, version) # extended key is not a private one xpub = b'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy' self.assertRaises(ValueError, bip32.xpub_from_xprv, xpub) # bip32.xpub_from_xprv(xpub) # Absolute derivation path for non-master key self.assertRaises(ValueError, bip32.derive, xpub, "m/44'/0'/1'/0/10") #bip32.derive(xpub, "m/0/1") # empty derivation path self.assertRaises(ValueError, bip32.derive, xpub, "") #bip32.derive(xpub, "") # extended key is not a public one self.assertRaises(ValueError, bip32.p2pkh_address_from_xpub, xprv) # bip32.p2pkh_address_from_xpub(xprv) # xkey is not a public one xprv = b'xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS' self.assertRaises(ValueError, bip32.address_from_xpub, xprv) # bip32.address_from_xpub(xprv) self.assertRaises(ValueError, bip32.p2wpkh_address_from_xpub, xprv) # bip32.p2wpkh_address_from_xpub(xprv) self.assertRaises(ValueError, bip32.p2wpkh_p2sh_address_from_xpub, xprv)
def test_mainnet(self): # bitcoin core derivation style rootxprv = b'xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS' # m/0'/0'/463' addr1 = b'1DyfBWxhVLmrJ7keyiHeMbt7N3UdeGU4G5' indexes = [0x80000000, 0x80000000, 0x80000000 + 463] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, indexes))) self.assertEqual(addr, addr1) path = "m/0'/0'/463'" addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, path))) self.assertEqual(addr, addr1) # m/0'/0'/267' addr2 = b'11x2mn59Qy43DjisZWQGRResjyQmgthki' indexes = [0x80000000, 0x80000000, 0x80000000 + 267] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, indexes))) self.assertEqual(addr, addr2) path = "m/0'/0'/267'" addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, path))) self.assertEqual(addr, addr2) xkey_version = bip32._PRV_VERSIONS[0] seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad" seed = bytes.fromhex(seed) xprv = bip32.rootxprv_from_seed(seed, xkey_version) xpub = b'xpub661MyMwAqRbcFMYjmw8C6dJV97a4oLss6hb3v9wTQn2X48msQB61RCaLGtNhzgPCWPaJu7SvuB9EBSFCL43kTaFJC3owdaMka85uS154cEh' self.assertEqual(bip32.xpub_from_xprv(xprv), xpub) ind = [0, 0] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(xprv, ind))) self.assertEqual(addr, b'1FcfDbWwGs1PmyhMVpCAhoTfMnmSuptH6g') ind = [0, 1] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(xprv, ind))) self.assertEqual(addr, b'1K5GjYkZnPFvMDTGaQHTrVnd8wjmrtfR5x') ind = [0, 2] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(xprv, ind))) self.assertEqual(addr, b'1PQYX2uN7NYFd7Hq22ECMzfDcKhtrHmkfi') ind = [1, 0] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(xprv, ind))) self.assertEqual(addr, b'1BvSYpojWoWUeaMLnzbkK55v42DbizCoyq') ind = [1, 1] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(xprv, ind))) self.assertEqual(addr, b'1NXB59hF4QzYpFrB7o6usLBjbk2D3ZqxAL') ind = [1, 2] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(xprv, ind))) self.assertEqual(addr, b'16NLYkKtvYhW1Jp86tbocku3gxWcvitY1w') # version/key mismatch in extended parent key temp = base58.decode(rootxprv) bad_xprv = base58.encode(temp[0:45] + b'\x01' + temp[46:]) self.assertRaises(ValueError, bip32.ckd, bad_xprv, 1) #bip32.ckd(bad_xprv, 1) # version/key mismatch in extended parent key xpub = bip32.xpub_from_xprv(rootxprv) temp = base58.decode(xpub) bad_xpub = base58.encode(temp[0:45] + b'\x00' + temp[46:]) self.assertRaises(ValueError, bip32.ckd, bad_xpub, 1) #bip32.ckd(bad_xpub, 1) # no private/hardened derivation from pubkey self.assertRaises(ValueError, bip32.ckd, xpub, 0x80000000)
child_number = b'\x00\x00\x00\x00' # the fingerprint of the parent's public key (0x00000000 if master key) fingerprint = b'\x00\x00\x00\x00' idf = depth + fingerprint + child_number # master private key, master public key, chain code hd = HMAC(b"Bitcoin seed", seed.to_bytes(seed_bytes, byteorder='big'), sha512).digest() qbytes = hd[:32] q = int(qbytes.hex(), 16) % ec.n qbytes = b'\x00' + q.to_bytes(32, byteorder='big') Q = mult(q, ec.G) Qbytes = octets_from_point(Q, True) chain_code = hd[32:] #extended keys ext_prv = encode(xprv + idf + chain_code + qbytes) print("\nm") print(ext_prv) ext_pub = encode(xpub + idf + chain_code + Qbytes) print("M") print(ext_pub) assert ext_prv == b"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "failure" assert ext_pub == b"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", "failure" # ==first (0) hardened child== depth = b'\x01' child_n = 0 + 0x80000000 #hardened child_number = child_n.to_bytes(4, byteorder='big') fingerprint = h160(Qbytes)[:4] idf = depth + fingerprint + child_number
h2 = sha256(h1).digest() print(h2.hex()) print("\n*** [5] First 4 bytes of the double SHA-256 used as checksum:") print(h2[:4].hex()) print("\n*** [6] checksum added at the end of extended key:") checksummed_payload = payload + h2[:4] print(checksummed_payload.hex()) print("\n*** [7] Base58 encoding") wif = base58._encode(checksummed_payload) print(wif) assert wif == b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617', "failure" assert base58.encode( payload ) == b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617', "failure" print("\n****** WIF to private key ******") print("\n*** [1] Base58 WIF") print(wif) compressed = len(wif) - 51 print("compressed" if (compressed == 1) else "uncompressed") print("\n*** [2] Base58 decoding") checksummed_payload = base58._decode(wif) print(checksummed_payload.hex()) print("\n*** [3] Extended key (checksum verified)") payload, checksum = checksummed_payload[:-4], checksummed_payload[-4:]
h2 = sha256(h1).digest() print(h2.hex()) print("\n*** [5] First 4 bytes of the double SHA-256 used as checksum:") print(h2[:4].hex()) print("\n*** [6] checksum added at the end of the payload:") checksummed_payload = payload + h2[:4] print(checksummed_payload.hex()) print("\n*** [7] Base58 encoding") wif = base58._encode(checksummed_payload) print(wif) assert wif == b'5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ', "failure" assert base58.encode( payload ) == b'5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ', "failure" print("\n****** WIF to private key ******") print("\n*** [1] Base58 WIF") print(wif) compressed = len(wif) - 51 print("compressed" if (compressed == 1) else "uncompressed") print("\n*** [2] Base58 decoding") checksummed_payload = base58._decode(wif) print(checksummed_payload.hex()) print("\n*** [3] payload (checksum verified)") payload, checksum = checksummed_payload[:-4], checksummed_payload[-4:]