def test_address_creation(self): """ Test address creation https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses """ self.assertEqual( PublicKey.from_hex( '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' ).to_address('P2PKH', ), '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs') self.assertNotEqual( PublicKey.from_hex( '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' ).to_address('P2PKH', compressed=False), '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs') self.assertEqual( PublicKey.from_hex( '03b82761f2482254b93fdf45f26c5d00bd51883fb7cd143080318c5be9746a5f5f' ).to_address('P2WPKH-P2SH'), '33x3UHfxVvJNqd275WG9XprVfepEUeASoj') self.assertEqual( PublicKey.from_hex( '03727fcbaff7eadb840b13bfd5b3d258530f0c1208bf02d8537606d096f069d2b5' ).to_address('P2WPKH'), 'bc1qsxe29au72mvjf7vsfhmlcdd5seuslnnkmgw4ws')
def legacy_address(public_key: PublicKey, version_byte: bytes) -> str: """https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses""" bts = public_key.encode( compressed=False) if isinstance(public_key, PublicKey) else public_key hashed = hash160(bts) payload = version_byte + hashed return hashed_payload_to_address(payload)
def deserialize(cls, bts: bytes, network='btc'): def read(n): nonlocal bts data, bts = bts[:n], bts[n:] return data net = read(4) is_private = net in get_network_attr('extended_prv', network).values() is_public = net in get_network_attr('extended_pub', network).values() assert is_public ^ is_private, f'Invalid network bytes : {bytes_to_hex(net)}' address_lookup = {val: key for key, val in ( get_network_attr('extended_prv', network) if is_private else get_network_attr('extended_pub', network)).items()} constructor = XPrv if is_private else XPub depth = bytes_to_int(read(1)) assert depth in range(256), f'Invalid depth : {depth}' fingerprint = read(4) i = bytes_to_int(read(4)) if depth == 0: i = None path = None else: ih = f'{i}' if i < 2 ** 31 else f"{i - 2 ** 31}h" path = '/'.join([constructor.root_path] + ['x' for _ in range(depth - 1)] + [ih]) code = read(32) key = read(33) key = PrivateKey(key, network=network) if is_private else PublicKey.decode(key, network=network) assert not bts, 'Leftover bytes' return constructor(key, code, depth=depth, i=i, parent=fingerprint, path=path, address_type=address_lookup[net])
def child(self, i: int) -> 'XPub': hardened = i >= 1 << 31 if hardened: raise KeyDerivationError('Cannot derive a hardened key from an extended public key') I = hmac.new(key=self.code, msg=self.key_data() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() I_L, I_R = I[:32], I[32:] key = PrivateKey(I_L).to_public().point + self.key.point ret_code = I_R path = self.path + f'/{i}' # TODO add point at infinity check return XPub( PublicKey(key, network=self.key.network), ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path, address_type=self.type.value )
def test_private_to_public(self): private = PrivateKey.from_wif( 'L2AnMo4KYaNTKFwgd2ZSsgcxAo8QSwJ9QYSiBSm44a4WZrwPKTum') self.assertEqual( private.to_public(), PublicKey.from_hex( '03b82761f2482254b93fdf45f26c5d00bd51883fb7cd143080318c5be9746a5f5f' ))
def to_p2wpkh_p2sh(public_key: PublicKey) -> 'str': return legacy_address( witness_byte(witver=0) + push(hash160(public_key.encode(compressed=True))), version_byte=get_network_attr('scripthash', public_key.network))
def to_p2pkh(public_key: PublicKey, compressed=False) -> 'str': return legacy_address( public_key.encode(compressed=True) if compressed else public_key, version_byte=get_network_attr('keyhash', public_key.network))
def pubkey_to_bech32(public_key: PublicKey, witver: int) -> str: """https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program""" witprog = hash160(public_key.encode(compressed=True)) return bech32.encode(get_network_attr('hrp', public_key.network), witver, witprog)