def test_p2pkh_from_pubkey(self): # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses pub = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" addr = p2pkh(pub) self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs') _, h160, _, _ = h160_from_b58address(addr) self.assertEqual(h160, hash160(pub)) uncompr_pub = bytes_from_point( point_from_octets(pub), compressed=False) addr = p2pkh(uncompr_pub, compressed=False) self.assertEqual(addr, b'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM') _, h160, _, _ = h160_from_b58address(addr) self.assertEqual(h160, hash160(uncompr_pub)) # trailing/leading spaces in string pub = ' 02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' addr = p2pkh(pub) self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs') _, h160, _, _ = h160_from_b58address(addr) self.assertEqual(h160, hash160(pub)) pub = '02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352 ' addr = p2pkh(pub) self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')
def test_p2wpkh(self): # https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki # leading/trailing spaces should be tolerated pub = " 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2" "815B16F81798" addr = b"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" self.assertEqual(addr, p2wpkh(pub)) addr = b"tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" self.assertEqual(addr, p2wpkh(pub, "testnet")) # http://bitcoinscri.pt/pages/segwit_native_p2wpkh pub = "02 530c548d402670b13ad8887ff99c294e67fc18097d236d57880c69" "261b42def7" addr = b"bc1qg9stkxrszkdqsuj92lm4c7akvk36zvhqw7p6ck" self.assertEqual(addr, p2wpkh(pub)) _, wp, _, _ = witness_from_b32address(addr) self.assertEqual(bytes(wp), hash160(pub)) # Wrong size (65-bytes) for compressed SEC key uncompr_pub = bytes_from_point(point_from_octets(pub), compressed=False) self.assertRaises(ValueError, p2wpkh, uncompr_pub) # p2wpkh(uncompr_pub) # Wrong pubkey size: 34 instead of 33 self.assertRaises(ValueError, p2wpkh, pub + "00") # p2wpkh(pub + '00') # Witness program length (21) is not 20 self.assertRaises(ValueError, b32address_from_witness, 0, hash160(pub) + b"\x00")
def test_p2sh(self): script_type = "p2sh" # self-consistency pubkey = "02" "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" pubkey_hash = hash160(pubkey) redeem_script = scriptPubKey_from_payload("p2pkh", pubkey_hash) payload = hash160(redeem_script) script = encode(["OP_HASH160", payload, "OP_EQUAL"]) # straight to the scriptPubKey scriptPubKey = p2sh(redeem_script) self.assertEqual(scriptPubKey.hex(), script.hex()) # to the scriptPubKey in two steps (through payload) scriptPubKey = scriptPubKey_from_payload(script_type, payload) self.assertEqual(scriptPubKey.hex(), script.hex()) # back from the scriptPubKey to the payload script_type2, payload2, m2 = payload_from_scriptPubKey(scriptPubKey) self.assertEqual(script_type, script_type2) self.assertEqual(0, m2) self.assertEqual(payload.hex(), payload2.hex()) script_type2, payload2, m2 = payload_from_scriptPubKey(script) self.assertEqual(script_type, script_type2) self.assertEqual(0, m2) self.assertEqual(payload.hex(), payload2.hex()) # data -> payload is not invertible (hash functions) # address network = "mainnet" address = base58address.p2sh(decode(redeem_script), network) address2 = address_from_scriptPubKey(scriptPubKey, network) self.assertEqual(address, address2) prefix = NETWORKS[network]["p2sh"] address2 = b58address_from_h160(prefix, payload, network) self.assertEqual(address, address2) scriptPubKey2, network2 = scriptPubKey_from_address(address) self.assertEqual(scriptPubKey2, scriptPubKey) self.assertEqual(network2, network) # documented test case: https://learnmeabitcoin.com/guide/p2sh payload = "748284390f9e263a4b766a75d0633c50426eb875" script = "a914748284390f9e263a4b766a75d0633c50426eb87587" scriptPubKey = scriptPubKey_from_payload(script_type, payload) self.assertEqual(scriptPubKey.hex(), script) network = "mainnet" address = b"3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V" address2 = address_from_scriptPubKey(scriptPubKey, network) self.assertEqual(address, address2) scriptPubKey2, network2 = scriptPubKey_from_address(address) self.assertEqual(scriptPubKey2, scriptPubKey) self.assertEqual(network2, network) # invalid size: 21 bytes instead of 20 self.assertRaises(ValueError, scriptPubKey_from_payload, "00" * 21, "p2sh")
def test_selfconsistency(self): # OP_RETURN data = "time-stamped data".encode().hex() # opcodes = ['OP_RETURN', data.hex()] opcodes = nulldata_scriptPubKey(data) scriptPubKey = encode(opcodes) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2) # p2pk pubkey = "04 cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf f7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4" # opcodes = [pubkey, 'OP_CHECKSIG'] opcodes = p2pk_scriptPubKey(pubkey) scriptPubKey = encode(opcodes) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2) # multi-sig pubkey2 = "04 61cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d765 19aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af" # opcodes = [1, pubkey, pubKey2, 2, 'OP_CHECKMULTISIG'] opcodes = p2ms_scriptPubKey(1, (pubkey, pubkey2)) scriptPubKey = encode(opcodes) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2) # p2pkh pubkey_hash = hash160(pubkey).hex() # opcodes = ['OP_DUP', 'OP_HASH160', pubkey_hash.hex(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'] opcodes = p2pkh_scriptPubKey(pubkey_hash) scriptPubKey = encode(opcodes) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2) # p2sh (p2pkh-p2sh) redeem_script_hash = hash160(scriptPubKey).hex() # opcodes = ['OP_HASH160', redeem_script_hash.hex(), 'OP_EQUAL'] opcodes = p2sh_scriptPubKey(redeem_script_hash) scriptPubKey = encode(opcodes) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2) # p2wpkh # opcodes = [0, pubkey_hash.hex()] opcodes = p2wpkh_scriptPubKey(pubkey_hash) scriptPubKey = encode(opcodes) self.assertEqual(scriptPubKey.hex(), "0014"+pubkey_hash) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2) # p2wsh witness_script = [pubkey, 'OP_CHECKSIG'] witness_script_bytes = encode(witness_script) witness_script_hash = sha256(witness_script_bytes) # opcodes = [0, witness_script_hash.hex()] opcodes = p2wsh_scriptPubKey(witness_script_hash.hex()) scriptPubKey = encode(opcodes) self.assertEqual(scriptPubKey.hex(), "0020"+witness_script_hash.hex()) opcodes2 = decode(scriptPubKey) self.assertEqual(opcodes, opcodes2)
def test_address_scriptPubKey(self): pubkey = "03 a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f" pubkey_hash = hash160(pubkey).hex() opcodes = [0, pubkey_hash] address_from_scriptPubKey(opcodes) opcodes2, _ = scriptPubKey_from_address(address_from_scriptPubKey(opcodes)) self.assertEqual(opcodes, opcodes2) opcodes = ['OP_DUP', 'OP_HASH160', pubkey_hash, 'OP_EQUALVERIFY', 'OP_CHECKSIG'] opcodes2, _ = scriptPubKey_from_address(address_from_scriptPubKey(opcodes)) self.assertEqual(opcodes, opcodes2) script_hash = hash160(encode(opcodes)).hex() opcodes = ['OP_HASH160',script_hash, 'OP_EQUAL'] opcodes2, _ = scriptPubKey_from_address(address_from_scriptPubKey(opcodes)) self.assertEqual(opcodes, opcodes2) script_hash = sha256(encode(opcodes)).hex() opcodes = [0, script_hash] opcodes2, _ = scriptPubKey_from_address(address_from_scriptPubKey(opcodes)) self.assertEqual(opcodes, opcodes2) # Unknown script opcodes = [16, pubkey_hash] self.assertRaises(ValueError, address_from_scriptPubKey, opcodes) #address_from_scriptPubKey(opcodes) # Unhandled witness version (16) wp = hash160(pubkey)[2:] addr = b32address_from_witness(16, wp) self.assertRaises(ValueError, scriptPubKey_from_address, addr)
def test_p2wpkh() -> None: # https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki # leading/trailing spaces should be tolerated pub = " 02 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" addr = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" assert addr == b32.p2wpkh(pub) addr = "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" assert addr == b32.p2wpkh(pub, "testnet") # http://bitcoinscri.pt/pages/segwit_native_p2wpkh pub = "02 530c548d402670b13ad8887ff99c294e67fc18097d236d57880c69261b42def7" addr = "bc1qg9stkxrszkdqsuj92lm4c7akvk36zvhqw7p6ck" assert addr == b32.p2wpkh(pub) _, wit_prg, _ = b32.witness_from_address(addr) assert wit_prg == hash160(pub) uncompr_pub = bytes_from_point(point_from_octets(pub), compressed=False) err_msg = "not a private or compressed public key: " with pytest.raises(BTClibValueError, match=err_msg): b32.p2wpkh(uncompr_pub) with pytest.raises(BTClibValueError, match=err_msg): b32.p2wpkh(pub + "00") err_msg = "invalid size: " with pytest.raises(BTClibValueError, match=err_msg): b32.address_from_witness(0, hash160(pub) + b"\x00")
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_utils(self): s_spaces = " 0C 28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D " b = bytes_from_octets(s_spaces) s = b.hex() # lower case, no spaces self.assertNotEqual(s, s_spaces) self.assertEqual(hash160(s_spaces), hash160(bytes_from_octets(s))) self.assertEqual(hash256(s_spaces), hash256(bytes_from_octets(s)))
def test_p2wpkh_address(self): # https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki # leading/trailing spaces should be tolerated pub = " 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" addr = b'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4' self.assertEqual(addr, p2wpkh_address(pub)) addr = b'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx' self.assertEqual(addr, p2wpkh_address(pub, 'testnet')) # http://bitcoinscri.pt/pages/segwit_native_p2wpkh_address pub = "02530c548d402670b13ad8887ff99c294e67fc18097d236d57880c69261b42def7" addr = b'bc1qg9stkxrszkdqsuj92lm4c7akvk36zvhqw7p6ck' self.assertEqual(addr, p2wpkh_address(pub)) _, _, wp = _decode(addr) self.assertEqual(bytes(wp), hash160(pub)) # Uncompressed pubkey uncompr_pub = octets_from_point(point_from_octets(pub, ec), False, ec) self.assertRaises(ValueError, p2wpkh_address, uncompr_pub) # p2wpkh_address(uncompr_pub) # Wrong pubkey size: 34 instead of 33 self.assertRaises(ValueError, p2wpkh_address, pub + '00') # p2wpkh_address(pub + '00') # Witness program length (21) is not 20 self.assertRaises(ValueError, _p2wpkh_address, hash160(pub) + b'\x00', True, "mainnet")
def test_hash160_hash256() -> None: test_vectors = (plain_prv_keys + net_unaware_compressed_pub_keys + net_unaware_uncompressed_pub_keys) for hexstring in test_vectors: b = bytes_from_octets(hexstring) s = b.hex() # lower case, no spaces assert hash160(hexstring) == hash160(s) assert hash256(hexstring) == hash256(s)
def test_utils(self): b = bytes_from_octets(int_with_whitespaces) s = b.hex() # lower case, no spaces self.assertNotEqual(int_with_whitespaces, s) self.assertEqual(hash160(int_with_whitespaces), hash160(s)) self.assertEqual(hash256(int_with_whitespaces), hash256(s)) i = secrets.randbits(256) self.assertEqual(i, int_from_integer(i)) self.assertEqual(i, int_from_integer(i.to_bytes(32, "big"))) self.assertEqual(i, int_from_integer(hex(i)))
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 assert_signable(self) -> None: self.assert_valid() for i, tx_in in enumerate(self.tx.vin): non_witness_utxo = self.inputs[i].non_witness_utxo witness_utxo = self.inputs[i].witness_utxo redeem_script = self.inputs[i].redeem_script if witness_utxo: script_pub_key = witness_utxo.script_pub_key script_type, payload = type_and_payload(script_pub_key.script) if script_type == "p2sh": script_type, _ = type_and_payload(redeem_script) if script_type not in ("p2wpkh", "p2wsh"): raise BTClibValueError( "script type not it ('p2wpkh', 'p2wsh')") elif non_witness_utxo: script_pub_key = non_witness_utxo.vout[ tx_in.prev_out.vout].script_pub_key _, payload = type_and_payload(script_pub_key.script) else: err_msg = "missing script_pub_key" raise BTClibValueError(err_msg) if redeem_script and payload != hash160(redeem_script): raise BTClibValueError("invalid redeem script hash160") if self.inputs[i].witness_script: if redeem_script: _, payload = type_and_payload(redeem_script) if payload != sha256(self.inputs[i].witness_script): raise BTClibValueError("invalid witness script sha256")
def test_p2wpkh() -> None: # self-consistency pub_key = "02 cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" payload = hash160(pub_key) script_pub_key = serialize(["OP_0", payload]) assert_p2wpkh(script_pub_key) assert script_pub_key == ScriptPubKey.p2wpkh(pub_key).script assert ("p2wpkh", payload) == type_and_payload(script_pub_key) # bech32 address network = "mainnet" addr = b32.p2wpkh(pub_key, network) assert addr == address(script_pub_key, network) assert addr == b32.address_from_witness(0, payload, network) # back from the address to the script_pub_key assert script_pub_key == ScriptPubKey.from_address(addr).script assert network == ScriptPubKey.from_address(addr).network # p2sh-wrapped base58 address addr = b58.p2wpkh_p2sh(pub_key, network) assert addr == b58.address_from_v0_witness(payload, network) err_msg = "invalid witness version: " with pytest.raises(BTClibValueError, match=err_msg): assert_p2wpkh(b"\x33" + script_pub_key[1:]) err_msg = "invalid pub_key hash length marker: " with pytest.raises(BTClibValueError, match=err_msg): assert_p2wpkh(script_pub_key[:1] + b"\x00" + script_pub_key[2:])
def test_witness(self): pub = "03 a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f" b58addr = p2wpkh_p2sh(pub) _, h160, network, is_script_hash = h160_from_b58address(b58addr) self.assertEqual(network, 'mainnet') self.assertEqual(is_script_hash, True) #?!?!? self.assertEqual(len(h160), 20) b58addr = _b58segwitaddress(hash160(to_pubkey_bytes(pub, True, ec)), network) _, h160_2, network, is_script_hash = h160_from_b58address(b58addr) self.assertEqual(network, 'mainnet') self.assertEqual(is_script_hash, True) #?!?!? self.assertEqual(len(h160), 20) self.assertEqual(h160.hex(), h160_2.hex()) wscript = "a8a58c2d034b28bf90c8803f5a53f769a4" b58addr = p2wsh_p2sh(wscript) _, h160, network, is_script_hash = h160_from_b58address(b58addr) self.assertEqual(network, 'mainnet') self.assertEqual(is_script_hash, True) #?!?!? self.assertEqual(len(h160), 20) b58addr = _b58segwitaddress(sha256(wscript), network) _, h160_2, network, is_script_hash = h160_from_b58address(b58addr) self.assertEqual(network, 'mainnet') self.assertEqual(is_script_hash, True) #?!?!? self.assertEqual(len(h160), 20) self.assertEqual(h160.hex(), h160_2.hex()) # Invalid witness program length (19) self.assertRaises(ValueError, _b58segwitaddress, h160[:-1], network)
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_p2wsh() -> None: # self-consistency pubkey = "02 cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" pubkey_hash = hash160(pubkey) redeem_script = scriptPubKey_from_payload("p2pkh", pubkey_hash) payload = sha256(redeem_script) scriptPubKey = script.encode([0, payload]) assert scriptPubKey == p2wsh(script.decode(redeem_script)) # to the scriptPubKey in two steps (through payload) script_type = "p2wsh" assert scriptPubKey == scriptPubKey_from_payload(script_type, payload) # back from the scriptPubKey to the payload assert (script_type, payload, 0) == payload_from_scriptPubKey(scriptPubKey) # bech32 address network = "mainnet" address = bech32address.p2wsh(redeem_script, network) assert address == address_from_scriptPubKey(scriptPubKey, network) wit_ver = 0 assert address == b32address_from_witness(wit_ver, payload, network) # back from the address to the scriptPubKey assert (scriptPubKey, network) == scriptPubKey_from_address(address) # p2sh-wrapped base58 address address = base58address.p2wsh_p2sh(redeem_script, network) assert address == b58address_from_witness(payload, network)
def test_p2sh(self): # https://medium.com/@darosior/bitcoin-raw-transactions-part-2-p2sh-94df206fee8d script = ['OP_2DUP', 'OP_EQUAL', 'OP_NOT', 'OP_VERIFY', 'OP_SHA1', 'OP_SWAP', 'OP_SHA1', 'OP_EQUAL'] script_bytes = encode(script) self.assertEqual(script_bytes.hex(), '6e879169a77ca787') network = 'mainnet' addr = p2sh(script_bytes, network) self.assertEqual(addr, b'37k7toV1Nv4DfmQbmZ8KuZDQCYK9x5KpzP') _, redeem_script_hash, network2, is_p2sh = h160_from_b58address(addr) self.assertEqual(network, network2) self.assertTrue(is_p2sh) self.assertEqual(redeem_script_hash, hash160(script_bytes)) self.assertEqual(redeem_script_hash.hex(), '4266fc6f2c2861d7fe229b279a79803afca7ba34') output_script = ['OP_HASH160', redeem_script_hash.hex(), 'OP_EQUAL'] _ = encode(output_script) # address with trailing/leading spaces _, h160, _, _ = h160_from_b58address( ' 37k7toV1Nv4DfmQbmZ8KuZDQCYK9x5KpzP ') self.assertEqual(redeem_script_hash, h160)
def test_p2pkh(self): script_type = 'p2pkh' # self-consistency pubkey = "04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4" payload = hash160(pubkey) script = encode(['OP_DUP', 'OP_HASH160', payload, 'OP_EQUALVERIFY', 'OP_CHECKSIG']) # straight to the scriptPubKey scriptPubKey = p2pkh(pubkey) self.assertEqual(scriptPubKey.hex(), script.hex()) # to the scriptPubKey in two steps (through payload) scriptPubKey = scriptPubKey_from_payload(script_type, payload) self.assertEqual(scriptPubKey.hex(), script.hex()) # back from the scriptPubKey to the payload script_type2, payload2, m2 = payload_from_scriptPubKey(scriptPubKey) self.assertEqual(script_type, script_type2) self.assertEqual(0, m2) self.assertEqual(payload.hex(), payload2.hex()) script_type2, payload2, m2 = payload_from_scriptPubKey(script) self.assertEqual(script_type, script_type2) self.assertEqual(0, m2) self.assertEqual(payload.hex(), payload2.hex()) # data -> payload is not invertible (hash functions) # address network = 'mainnet' address = base58address.p2pkh(pubkey, network) address2 = address_from_scriptPubKey(scriptPubKey, network) self.assertEqual(address, address2) prefix = p2pkh_prefix_from_network(network) address2 = b58address_from_h160(prefix, payload) self.assertEqual(address, address2) scriptPubKey2, network2 = scriptPubKey_from_address(address) self.assertEqual(scriptPubKey2, scriptPubKey) self.assertEqual(network2, network) # documented test case: https://learnmeabitcoin.com/guide/p2pkh payload = "12ab8dc588ca9d5787dde7eb29569da63c3a238c" script = "76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac" scriptPubKey = scriptPubKey_from_payload(script_type, payload) self.assertEqual(scriptPubKey.hex(), script) network = 'mainnet' address = b"12higDjoCCNXSA95xZMWUdPvXNmkAduhWv" address2 = address_from_scriptPubKey(scriptPubKey, network) self.assertEqual(address, address2) scriptPubKey2, network2 = scriptPubKey_from_address(address) self.assertEqual(scriptPubKey2, scriptPubKey) self.assertEqual(network2, network) # Invalid size: 11 bytes instead of 20 self.assertRaises( ValueError, scriptPubKey_from_payload, "00" * 11, 'p2pkh')
def fingerprint(key: Key, network: Optional[str] = None) -> bytes: """Return the public key fingerprint from a private/public key. The fingerprint is the last four bytes of the compressed public key HASH160. """ pub_key, _ = pub_keyinfo_from_key(key, network, compressed=True) return hash160(pub_key)[:4]
def hash160_from_key(key: Key, network: Optional[str] = None, compressed: Optional[bool] = None) -> H160_Net: """Return (public key HASH160, nettwork) from a private/public key. HASH160 is RIPEMD160(SHA256). """ pub_key, network = pub_keyinfo_from_key(key, network, compressed) return hash160(pub_key), network
def test_exceptions() -> None: pubkey = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" payload = b"\xf5" + hash160(pubkey) invalid_address = b58encode(payload) with pytest.raises(ValueError, match="invalid base58 address prefix: "): h160_from_b58address(invalid_address) with pytest.raises(ValueError, match="not a private or public key: "): p2pkh(pubkey + "00")
def test_p2sh() -> None: # self-consistency pubkey = "02 cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" pubkey_hash = hash160(pubkey) redeem_script = script_pubkey_from_payload("p2pkh", pubkey_hash) payload = hash160(redeem_script) script_pubkey = script.serialize(["OP_HASH160", payload, "OP_EQUAL"]) assert script_pubkey == p2sh(redeem_script) # to the script_pubkey in two steps (through payload) script_type = "p2sh" assert script_pubkey == script_pubkey_from_payload(script_type, payload) # back from the script_pubkey to the payload assert (script_type, payload, 0) == payload_from_script_pubkey(script_pubkey) # base58 address network = "mainnet" address = base58address.p2sh(script.deserialize(redeem_script), network) assert address == address_from_script_pubkey(script_pubkey, network) prefix = NETWORKS[network].p2sh assert address == b58address_from_h160(prefix, payload, network) # back from the address to the script_pubkey assert (script_pubkey, network) == script_pubkey_from_address(address) # documented test case: https://learnmeabitcoin.com/guide/p2sh payload = "748284390f9e263a4b766a75d0633c50426eb875" script_pubkey = "a914" + payload + "87" assert script_pubkey == script_pubkey_from_payload(script_type, payload).hex() address = b"3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V" assert address == address_from_script_pubkey(script_pubkey, network) assert (bytes.fromhex(script_pubkey), network) == script_pubkey_from_address(address) # invalid size: 21 bytes instead of 20 err_msg = "invalid size: " with pytest.raises(BTClibValueError, match=err_msg): script_pubkey_from_payload(script_type, "00" * 21)
def p2sh( cls: Type[_ScriptPubKey], redeem_script: Octets, network: str = "mainnet", check_validity: bool = True, ) -> _ScriptPubKey: "Return the p2sh ScriptPubKey of the provided redeem script." script_h160 = hash160(redeem_script) script = serialize(["OP_HASH160", script_h160, "OP_EQUAL"]) return cls(script, network, check_validity)
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_exceptions(self): # Invalid base58 address prefix b'\xf5' payload = b'\xf5' pubkey = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" payload += hash160(pubkey) invalid_address = b58encode(payload) self.assertRaises(ValueError, h160_from_b58address, invalid_address) # _h160_from_b58address(invalid_address) # Invalid SEC pubkey length: 34-bytes self.assertRaises(ValueError, p2pkh, pubkey + '00', True)
def __ckd(xkey: _ExtendedBIP32KeyData, index: int) -> None: xkey.depth += 1 xkey.index = index if xkey.key[0] == 0: # private key Q_bytes = bytes_from_point(mult(xkey.prv_key_int)) xkey.parent_fingerprint = hash160(Q_bytes)[:4] if xkey.is_hardened(): # hardened derivation hmac_ = hmac.new( xkey.chain_code, xkey.key + index.to_bytes(4, byteorder="big", signed=False), "sha512", ).digest() else: # normal derivation hmac_ = hmac.new( xkey.chain_code, Q_bytes + index.to_bytes(4, byteorder="big", signed=False), "sha512", ).digest() xkey.chain_code = hmac_[32:] offset = int.from_bytes(hmac_[:32], byteorder="big", signed=False) xkey.prv_key_int = (xkey.prv_key_int + offset) % ec.n xkey.key = b"\x00" + xkey.prv_key_int.to_bytes( 32, byteorder="big", signed=False) xkey.pub_key_point = INF else: # public key xkey.parent_fingerprint = hash160(xkey.key)[:4] if xkey.is_hardened(): raise BTClibValueError( "invalid hardened derivation from public key") hmac_ = hmac.new( xkey.chain_code, xkey.key + index.to_bytes(4, byteorder="big", signed=False), "sha512", ).digest() xkey.chain_code = hmac_[32:] offset = int.from_bytes(hmac_[:32], byteorder="big", signed=False) xkey.pub_key_point = ec.add(xkey.pub_key_point, mult(offset)) xkey.key = bytes_from_point(xkey.pub_key_point) xkey.prv_key_int = 0
def test_p2wsh(self): script_type = "p2wsh" # self-consistency pubkey = "02" "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" pubkey_hash = hash160(pubkey) redeem_script = scriptPubKey_from_payload("p2pkh", pubkey_hash) payload = sha256(redeem_script) script = encode([0, payload]) # straight to the scriptPubKey scriptPubKey = p2wsh(decode(redeem_script)) self.assertEqual(scriptPubKey.hex(), script.hex()) # to the scriptPubKey in two steps (through payload) scriptPubKey = scriptPubKey_from_payload(script_type, payload) self.assertEqual(scriptPubKey.hex(), script.hex()) # back from the scriptPubKey to the payload script_type2, payload2, m2 = payload_from_scriptPubKey(scriptPubKey) self.assertEqual(script_type, script_type2) self.assertEqual(0, m2) self.assertEqual(payload.hex(), payload2.hex()) script_type2, payload2, m2 = payload_from_scriptPubKey(script) self.assertEqual(script_type, script_type2) self.assertEqual(0, m2) self.assertEqual(payload.hex(), payload2.hex()) # data -> payload is not invertible (hash functions) # bech32 address network = "mainnet" address = bech32address.p2wsh(redeem_script, network) address2 = address_from_scriptPubKey(scriptPubKey, network) self.assertEqual(address, address2) address2 = b32address_from_witness(0, payload, network) self.assertEqual(address, address2) scriptPubKey2, network2 = scriptPubKey_from_address(address) self.assertEqual(scriptPubKey2, scriptPubKey) self.assertEqual(network2, network) # p2sh-wrapped base58 address address = base58address.p2wsh_p2sh(redeem_script, network) address2 = b58address_from_witness(payload, network) self.assertEqual(address, address2)
def crack_prv_key(parent_xpub: BIP32Key, child_xprv: BIP32Key) -> str: if isinstance(parent_xpub, BIP32KeyData): p = copy.copy(parent_xpub) else: p = BIP32KeyData.b58decode(parent_xpub) if p.key[0] not in (2, 3): err_msg = "extended parent key is not a public key: " err_msg += f"{p.b58encode()}" raise BTClibValueError(err_msg) if isinstance(child_xprv, BIP32KeyData): c = child_xprv else: c = BIP32KeyData.b58decode(child_xprv) if c.key[0] != 0: err_msg = "extended child key is not a private key: " err_msg += f"{c.b58encode()}" raise BTClibValueError(err_msg) # check depth if c.depth != p.depth + 1: raise BTClibValueError("not a parent's child: wrong depths") # check fingerprint if c.parent_fingerprint != hash160(p.key)[:4]: raise BTClibValueError( "not a parent's child: wrong parent fingerprint") if c.is_hardened(): raise BTClibValueError("hardened child derivation") p.version = c.version hmac_ = hmac.new( p.chain_code, p.key + c.index.to_bytes(4, byteorder="big", signed=False), "sha512", ).digest() child_q = int.from_bytes(c.key[1:], byteorder="big", signed=False) offset = int.from_bytes(hmac_[:32], byteorder="big", signed=False) parent_q = (child_q - offset) % ec.n p.key = b"\x00" + parent_q.to_bytes(32, byteorder="big", signed=False) return p.b58encode()
def test_p2pkh() -> None: # self-consistency pub_key = ( "04 " "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" "f7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4") payload = hash160(pub_key) script_pub_key = serialize( ["OP_DUP", "OP_HASH160", payload, "OP_EQUALVERIFY", "OP_CHECKSIG"]) assert_p2pkh(script_pub_key) assert script_pub_key == ScriptPubKey.p2pkh(pub_key).script assert ("p2pkh", payload) == type_and_payload(script_pub_key) # base58 address network = "mainnet" addr = b58.p2pkh(pub_key, network) assert addr == address(script_pub_key, network) assert addr == b58.address_from_h160("p2pkh", payload, network) # back from the address to the script_pub_key assert script_pub_key == ScriptPubKey.from_address(addr).script assert network == ScriptPubKey.from_address(addr).network # documented test case: https://learnmeabitcoin.com/guide/p2pkh payload = bytes.fromhex("12ab8dc588ca9d5787dde7eb29569da63c3a238c") script_pub_key = bytes.fromhex("76a914") + payload + bytes.fromhex("88ac") assert_p2pkh(script_pub_key) addr = "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv" assert addr == address(script_pub_key, network) assert script_pub_key == ScriptPubKey.from_address(addr).script assert network == ScriptPubKey.from_address(addr).network err_msg = "missing final OP_EQUALVERIFY, OP_CHECKSIG" with pytest.raises(BTClibValueError, match=err_msg): assert_p2pkh(script_pub_key[:-2] + b"\x40\x40") err_msg = "missing leading OP_DUP, OP_HASH160" with pytest.raises(BTClibValueError, match=err_msg): assert_p2pkh(b"\x40\x40" + script_pub_key[2:]) err_msg = "invalid pub_key hash length marker: " with pytest.raises(BTClibValueError, match=err_msg): assert_p2pkh(script_pub_key[:2] + b"\x40" + script_pub_key[3:])