def test_p2sh() -> None: # https://medium.com/@darosior/bitcoin-raw-transactions-part-2-p2sh-94df206fee8d network = "mainnet" address = "37k7toV1Nv4DfmQbmZ8KuZDQCYK9x5KpzP" script_pub_key = serialize([ "OP_2DUP", "OP_EQUAL", "OP_NOT", "OP_VERIFY", "OP_SHA1", "OP_SWAP", "OP_SHA1", "OP_EQUAL", ]) assert script_pub_key.hex() == "6e879169a77ca787" assert address == b58.p2sh(script_pub_key, network) script_hash = hash160(script_pub_key) assert ("p2sh", script_hash, network) == b58.h160_from_address(address) assert ("p2sh", script_hash, network) == b58.h160_from_address(" " + address + " ") assert script_hash.hex() == "4266fc6f2c2861d7fe229b279a79803afca7ba34" script_sig: List[Command] = ["OP_HASH160", script_hash.hex(), "OP_EQUAL"] serialize(script_sig)
def test_p2pkh_from_pub_key() -> None: # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses pub_key = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" address = "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs" assert address == b58.p2pkh(pub_key) assert address == b58.p2pkh(pub_key, compressed=True) _, h160, _ = b58.h160_from_address(address) assert h160 == hash160(pub_key) # trailing/leading spaces in address string assert address == b58.p2pkh(" " + pub_key) assert h160 == hash160(" " + pub_key) assert address == b58.p2pkh(pub_key + " ") assert h160 == hash160(pub_key + " ") uncompr_pub_key = bytes_from_point(point_from_octets(pub_key), compressed=False) uncompr_address = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM" assert uncompr_address == b58.p2pkh(uncompr_pub_key, compressed=False) assert uncompr_address == b58.p2pkh(uncompr_pub_key) _, uncompr_h160, _ = b58.h160_from_address(uncompr_address) assert uncompr_h160 == hash160(uncompr_pub_key) err_msg = "not a private or uncompressed public key: " with pytest.raises(BTClibValueError, match=err_msg): assert uncompr_address == b58.p2pkh(pub_key, compressed=False) err_msg = "not a private or compressed public key: " with pytest.raises(BTClibValueError, match=err_msg): assert address == b58.p2pkh(uncompr_pub_key, compressed=True)
def test_exceptions() -> None: pub_key = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" payload = b"\xf5" + hash160(pub_key) invalid_address = b58encode(payload) with pytest.raises(BTClibValueError, match="invalid base58 address prefix: "): b58.h160_from_address(invalid_address) with pytest.raises(BTClibValueError, match="not a private or public key: "): b58.p2pkh(pub_key + "00")
def test_address_from_wif() -> None: q = 0x19E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 test_cases: List[Tuple[bool, str, str, str]] = [ ( False, "mainnet", "5J1geo9kcAUSM6GJJmhYRX1eZEjvos9nFyWwPstVziTVueRJYvW", "1LPM8SZ4RQDMZymUmVSiSSvrDfj1UZY9ig", ), ( True, "mainnet", "Kx621phdUCp6sgEXPSHwhDTrmHeUVrMkm6T95ycJyjyxbDXkr162", "1HJC7kFvXHepkSzdc8RX6khQKkAyntdfkB", ), ( False, "testnet", "91nKEXyJCPYaK9maw7bTJ7ZcCu6dy2gybvNtUWF1LTCYggzhZgy", "mzuJRVe3ERecM6F6V4R6GN9B5fKiPC9HxF", ), ( True, "testnet", "cNT1UjhUuGWN37hnmr754XxvPWwtAJTSq8bcCQ4pUrdxqxbA1iU1", "mwp9QoLuLK65XZUFKhPtvfujBjmgkZnmPx", ), ] for compressed, network, wif, address in test_cases: assert wif == b58.wif_from_prv_key(q, network, compressed) assert prv_keyinfo_from_prv_key(wif) == (q, network, compressed) assert address == b58.p2pkh(wif) script_type, payload, net = b58.h160_from_address(address) assert net == network assert script_type == "p2pkh" if compressed: b32_address = b32.p2wpkh(wif) assert (0, payload, net) == b32.witness_from_address(b32_address) b58_address = b58.p2wpkh_p2sh(wif) script_bin = hash160(b"\x00\x14" + payload) assert ("p2sh", script_bin, net) == b58.h160_from_address(b58_address) else: err_msg = "not a private or compressed public key: " with pytest.raises(BTClibValueError, match=err_msg): b32.p2wpkh(wif) # type: ignore with pytest.raises(BTClibValueError, match=err_msg): b58.p2wpkh_p2sh(wif) # type: ignore
def test_address_from_h160() -> None: address = "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs" assert address == b58.address_from_h160(*b58.h160_from_address(address)) address = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM" assert address == b58.address_from_h160(*b58.h160_from_address(address)) address = "37k7toV1Nv4DfmQbmZ8KuZDQCYK9x5KpzP" assert address == b58.address_from_h160(*b58.h160_from_address(address)) with pytest.raises(BTClibValueError, match="invalid script type: "): b58.address_from_h160("p2pk", b"\x00" * 20)
def assert_as_valid(msg: Octets, addr: String, sig: Union[Sig, String], lower_s: bool = True) -> None: # Private function for test/dev purposes # It raises Errors, while verify should always return True or False if isinstance(sig, Sig): sig.assert_valid() else: sig = Sig.b64decode(sig) # first two bits in rf are reserved for key_id # key_id = 00; key_id = 01; key_id = 10; key_id = 11 # 27-27 = 000000; 28-27 = 000001; 29-27 = 000010; 30-27 = 000011 # 31-27 = 000100; 32-27 = 000101; 33-27 = 000110; 34-27 = 000111 # 35-27 = 001000; 36-27 = 001001; 37-27 = 001010; 38-27 = 001011 # 39-27 = 001100; 40-27 = 001101; 41-27 = 001110; 42-27 = 001111 key_id = sig.rf - 27 & 0b11 magic_msg = magic_message(msg) Q = dsa.recover_pub_key(key_id, magic_msg, sig.dsa_sig, lower_s, sha256) compressed = sig.rf > 30 # signature is valid only if the provided address is matched pub_key = bytes_from_point(Q, compressed=compressed) if has_segwit_prefix(addr): wit_ver, h160, _ = witness_from_address(addr) if wit_ver != 0 or len(h160) != 20: raise BTClibValueError(f"not a p2wpkh address: {addr!r}") if not (30 < sig.rf < 35 or sig.rf > 38): raise BTClibValueError( f"invalid p2wpkh address recovery flag: {sig.rf}") if hash160(pub_key) != h160: raise BTClibValueError(f"invalid p2wpkh address: {addr!r}") return script_type, h160, _ = h160_from_address(addr) if script_type == "p2pkh": if sig.rf > 34: raise BTClibValueError( f"invalid p2pkh address recovery flag: {sig.rf}") if hash160(pub_key) != h160: raise BTClibValueError(f"invalid p2pkh address: {addr!r}") return # must be P2WPKH-P2SH if not 30 < sig.rf < 39: raise BTClibValueError( f"invalid p2wpkh-p2sh address recovery flag: {sig.rf}") script_pk = b"\x00\x14" + hash160(pub_key) if hash160(script_pk) != h160: raise BTClibValueError(f"invalid p2wpkh-p2sh address: {addr!r}")
def from_address(cls: Type["ScriptPubKey"], addr: String, check_validity: bool = True) -> "ScriptPubKey": "Return the ScriptPubKey of the input bech32/base58 address." if b32.has_segwit_prefix(addr): wit_ver, wit_prg, network = b32.witness_from_address(addr) return cls(serialize([op_int(wit_ver), wit_prg]), network, check_validity) script_type, h160, network = b58.h160_from_address(addr) if script_type == "p2sh": commands: List[Command] = ["OP_HASH160", h160, "OP_EQUAL"] else: # it must be "p2pkh" commands = [ "OP_DUP", "OP_HASH160", h160, "OP_EQUALVERIFY", "OP_CHECKSIG", ] return cls(serialize(commands), network, check_validity)