def test_regressions() -> None: script_list: List[List[Command]] = [ [1], ["OP_1"], [51], [b"\x01"], ["01"], ["AA"], ["aa"], ["AAAA"], [0], [""], [b""], ["OP_0"], [-1], ["OP_1NEGATE"], [0x81], ["81"], ] with warnings.catch_warnings(): warnings.simplefilter("ignore") for s in script_list: serialized = serialize(s) assert serialize(parse(serialized)) == serialized
def test_legacy_p2pkh() -> None: pub_key = "04280c8f66bf2ccaeb3f60a19ad4a06365f8bd6178aab0e709df2173df8f553366549aec336aae8742a84702b6c7c3052d89f5d76d535ec3716e72187956351613" signature = "3045022100ea43c4800d1a860ec89b5273898a146cfb01d34ff4c364d24a110c480d0e3f7502201c82735577f932f1ca8e1c54bf653e0f8e74e408fe83666bc85cac4472ec950801" script_sig = serialize([signature, pub_key]) out_point = OutPoint( "d8343a35ba951684f2969eafe833d9e6fe436557b9707ae76802875952e860fc", 1) tx_in = TxIn(out_point, script_sig, 0xFFFFFFFF) tx_out1 = TxOut( 2017682, bytes.fromhex("76a91413bd20236d0da56492c325dce289b4da35b4b5bd88ac")) tx_out2 = TxOut( 1049154982, bytes.fromhex("76a914da169b45781ca210f8c11617ba66bd843da76b1688ac")) tx = Tx(1, 0, [tx_in], [tx_out1, tx_out2]) script_pub_key = serialize([ "OP_DUP", "OP_HASH160", "82ac30f58baf99ec9d14e6181eee076f4e27f69c", "OP_EQUALVERIFY", "OP_CHECKSIG", ]) utxo = TxOut(1051173696, script_pub_key) hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.ALL) assert dsa.verify_(hash_, pub_key, bytes.fromhex(signature)[:-1])
def test_add_and_eq() -> None: script_1 = serialize(["OP_2", "OP_3", "OP_ADD", "OP_5"]) script_2 = serialize(["OP_EQUAL"]) assert Script(script_1) + Script(script_2) == Script(script_1 + script_2) with pytest.raises(TypeError): _ = Script(script_1) + script_2
def test_nulldata() -> None: scripts: List[List[Command]] = [["OP_RETURN", "11" * 79], ["OP_RETURN", "00" * 79]] for script_pub_key in scripts: assert script_pub_key == parse(serialize(script_pub_key)) assert script_pub_key == parse(serialize(script_pub_key).hex())
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_p2ms_3() -> None: # tx_id 33ac2af1a6f894276713b59ed09ce1a20fed5b36d169f20a3fe831dc45564d57 # output n 0 keys: List[Command] = [ "036D568125A969DC78B963B494FA7ED5F20EE9C2F2FC2C57F86C5DF63089F2ED3A", "03FE4E6231D614D159741DF8371FA3B31AB93B3D28A7495CDAA0CD63A2097015C7", ] cmds: List[Command] = ["OP_1", *keys, "OP_2", "OP_CHECKMULTISIG"] script_pub_key = ScriptPubKey(serialize(cmds)) assert script_pub_key == ScriptPubKey.p2ms(1, keys) pub_keys = script_pub_key.addresses exp_pub_keys = [ "1Ng4YU2e2H3E86syX2qrsmD9opBHZ42vCF", "14XufxyGiY6ZBJsFYHJm6awdzpJdtsP1i3", ] for pub_key, key, exp_pub_key in zip(pub_keys, keys, exp_pub_keys): assert pub_key == b58.p2pkh(key) assert pub_key == exp_pub_key # tx 56214420a7c4dcc4832944298d169a75e93acf9721f00656b2ee0e4d194f9970 # input n 1 cmds_sig: List[Command] = [ "OP_0", "3045022100dba1e9b1c8477fd364edcc1f81845928202daf465a1e2d92904c13c88761cbd002200add6af863dfdb7efb95f334baec041e90811ae9d81624f9f87f33a56761f29401", ] script_sig = Script(serialize(cmds_sig)) script = script_sig + script_pub_key # parse(serialize(*)) is to enforce same string case convention assert script.asm == parse(serialize(cmds_sig + cmds))
def legacy_script(script_pub_key: Octets) -> List[bytes]: script_s: List[bytes] = [] current_script: List[Command] = [] for token in parse(script_pub_key)[::-1]: if token == "OP_CODESEPARATOR": # nosec required for python < 3.8 script_s.append(serialize(current_script[::-1])) else: current_script.append(token) script_s.append(serialize(current_script[::-1])) return script_s[::-1]
def test_serialize_bytes_command() -> None: length = 75 b = b"\x0A" * length assert len(serialize([b])) == length + 1 b = b"\x0A" * (length + 1) assert len(serialize([b])) == (length + 1) + 2 length = 255 b = b"\x0A" * length assert len(serialize([b])) == length + 2 b = b"\x0A" * (length + 1) assert len(serialize([b])) == (length + 1) + 3
def test_control_block() -> None: script_tree = [[(0xC0, ["OP_2"])], [(0xC0, ["OP_3"])]] pubkey = output_pubkey(None, script_tree)[0] script, control = input_script_sig(None, script_tree, 0) assert check_output_pubkey(pubkey, serialize(script), control) prvkey = 123456 internal_pubkey = mult(prvkey) script_tree = [[(0xC0, ["OP_2"])], [(0xC0, ["OP_3"])]] pubkey = output_pubkey(internal_pubkey, script_tree)[0] script, control = input_script_sig(internal_pubkey, script_tree, 0) assert check_output_pubkey(pubkey, serialize(script), control)
def test_p2pk() -> None: pub_key = "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" signature = "304402200A5C6163F07B8D3B013C4D1D6DBA25E780B39658D79BA37AF7057A3B7F15FFA102201FD9B4EAA9943F734928B99A83592C2E7BF342EA2680F6A2BB705167966B742001" script_pub_key = serialize([pub_key, "OP_CHECKSIG"]) script_sig = serialize([signature]) founding_tx_script = serialize(["OP_0", "OP_0"]) tx_in = TxIn(OutPoint(b"\x00" * 32, 0xFFFFFFFF), founding_tx_script, 0xFFFFFFFF) funding_tx = Tx(1, 0, [tx_in], [TxOut(0, script_pub_key)]) tx_in = TxIn(OutPoint(funding_tx.id, 0), script_sig, 0xFFFFFFFF) receiving_tx = Tx(1, 0, [tx_in], [TxOut(0, b"")]) hash_ = sig_hash.from_tx(funding_tx.vout, receiving_tx, 0, sig_hash.ALL) assert dsa.verify_(hash_, pub_key, bytes.fromhex(signature)[:-1])
def test_exceptions() -> None: script_pub_key: List[Command] = ["OP_2", "OP_3", "OP_ADD", "OP_5", "OP_RETURN_244"] err_msg = "invalid string command: OP_RETURN_244" with pytest.raises(BTClibValueError, match=err_msg): serialize(script_pub_key) with pytest.raises(TypeError): serialize(["OP_2", "OP_3", "OP_ADD", "OP_5", serialize]) # type: ignore err_msg = "too many bytes for OP_PUSHDATA: " with pytest.raises(BTClibValueError, match=err_msg): script_pub_key = ["1f" * 521, "OP_DROP"] serialize(script_pub_key) # A script_pub_key with OP_PUSHDATA4 can't be decoded script_bytes = "4e09020000" + "0A" * 521 + "75" # ['0A'*521, 'OP_DROP'] err_msg = "Invalid pushdata length: " with pytest.raises(BTClibValueError, match=err_msg): parse(script_bytes) # and can't be encoded script_pub_key_ = ["0A" * 521, "OP_DROP"] err_msg = "too many bytes for OP_PUSHDATA: " with pytest.raises(BTClibValueError, match=err_msg): serialize(script_pub_key_)
def test_simple_scripts() -> None: script_list: List[List[Command]] = [ ["OP_2", "OP_3", "OP_ADD", "OP_5", "OP_EQUAL"], [0x1ADD, "OP_1ADD", 0x1ADE, "OP_EQUAL"], [26, "OP_1NEGATE", "OP_ADD", 26, "OP_EQUAL"], [0x7FFFFFFF, "OP_1NEGATE", "OP_ADD", 0x7FFFFFFF, "OP_EQUAL"], [0x80000000, "OP_1NEGATE", "OP_ADD", 0x7FFFFFFF, "OP_EQUAL"], [0xFFFFFFFF - 1, "OP_1NEGATE", "OP_ADD", 0x7FFFFFFF, "OP_EQUAL"], [0xFFFFFFFF, "OP_1NEGATE", "OP_ADD", 0x7FFFFFFF, "OP_EQUAL"], ["1F" * 250, "OP_DROP"], ["1F" * 520, "OP_DROP"], ] for script_pub_key in script_list: assert script_pub_key == parse(serialize(script_pub_key)) assert script_pub_key == parse(serialize(script_pub_key).hex())
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 == "3BJxz2r8zY7LxJfdGjUpjjHNh6YEiitvf2" 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 _address_from_v0_witness(wit_prg: Octets, network: str = "mainnet") -> str: "Return the legacy base58 p2sh-wrapped SegWit v0 address." # check witness program wit_prg = b32.check_witness(0, wit_prg) redeem_script = serialize(["OP_0", wit_prg]) return p2sh(redeem_script, network)
def test_p2pk_anyonecanpay() -> None: pub_key = "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf" signature = "304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281" script_pub_key = serialize([pub_key, "OP_CHECKSIG"]) script_sig = serialize([signature]) founding_tx_script = serialize(["OP_0", "OP_0"]) tx_in = TxIn(OutPoint(b"\x00" * 32, 0xFFFFFFFF), founding_tx_script, 0xFFFFFFFF) funding_tx = Tx(1, 0, [tx_in], [TxOut(0, script_pub_key)]) tx_in = TxIn(OutPoint(funding_tx.id, 0), script_sig, 0xFFFFFFFF) receiving_tx = Tx(1, 0, [tx_in], [TxOut(0, b"")]) hash_ = sig_hash.from_tx(funding_tx.vout, receiving_tx, 0, sig_hash.ANYONECANPAY | sig_hash.ALL) assert dsa.verify_(hash_, pub_key, bytes.fromhex(signature)[:-1])
def test_wrapped_p2tr() -> None: script = [ "OP_1", "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf", ] utxo = TxOut( 100000000, serialize(["OP_HASH160", hash160(serialize(script)), "OP_EQUAL"])) tx_in = TxIn(OutPoint(), serialize(script), 1, Witness(["0A" * 32])) tx = Tx(vin=[tx_in], vout=[TxOut(100000000, "")]) err_msg = "Taproot scripts cannot be wrapped in p2sh" with pytest.raises(BTClibValueError, match=err_msg): sig_hash.from_tx([utxo], tx, 0, 0)
def test_non_standard_script_in_p2wsh() -> None: network = "mainnet" fed_pub_keys: List[Command] = ["00" * 33, "11" * 33, "22" * 33] rec_pub_keys: List[Command] = ["77" * 33, "88" * 33, "99" * 33] # fmt: off redeem_script_cmds: List[Command] = [ "OP_IF", "OP_2", *fed_pub_keys, "OP_3", "OP_CHECKMULTISIG", # noqa E131 "OP_ELSE", 500, "OP_CHECKLOCKTIMEVERIFY", "OP_DROP", # noqa E131 "OP_2", *rec_pub_keys, "OP_3", "OP_CHECKMULTISIG", # noqa E131 "OP_ENDIF", ] # fmt: on redeem_script = serialize(redeem_script_cmds) assert redeem_script_cmds == parse(redeem_script) payload = sha256(redeem_script) script_pub_key = ( "00207b5310339c6001f75614daa5083839fa54d46165f6c56025cc54d397a85a5708") assert script_pub_key == ScriptPubKey.p2wsh(redeem_script).script.hex() addr = "bc1q0df3qvuuvqqlw4s5m2jsswpelf2dgct97mzkqfwv2nfe02z62uyq7n4zjj" assert addr == address(script_pub_key, network) assert addr == b32.address_from_witness(0, payload, network)
def witness_v0_script(script_pub_key: Octets) -> List[bytes]: script_type, payload = type_and_payload(script_pub_key) if script_type == "p2wpkh": script = serialize( ["OP_DUP", "OP_HASH160", payload, "OP_EQUALVERIFY", "OP_CHECKSIG"]) return [script] script_s: List[bytes] = [] current_script: List[Command] = [] for token in parse(script_pub_key)[::-1]: if token == "OP_CODESEPARATOR": # nosec required for python < 3.8 script_s.append(serialize(current_script[::-1])) current_script.append(token) script_s.append(serialize(current_script[::-1])) return script_s[::-1]
def test_p2wsh() -> None: # self-consistency pub_key = "02 cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" redeem_script = ScriptPubKey.p2pkh(pub_key).script payload = sha256(redeem_script) script_pub_key = serialize(["OP_0", payload]) assert_p2wsh(script_pub_key) assert script_pub_key == ScriptPubKey.p2wsh(redeem_script).script assert ("p2wsh", payload) == type_and_payload(script_pub_key) # bech32 address network = "mainnet" addr = b32.p2wsh(redeem_script, 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.p2wsh_p2sh(redeem_script, network) assert addr == "39GUePMSQ4mADpihVLd8cFQ2tih9Fy4qkz" err_msg = "invalid witness version: " with pytest.raises(BTClibValueError, match=err_msg): assert_p2wsh(b"\x33" + script_pub_key[1:]) err_msg = "invalid redeem script hash length marker: " with pytest.raises(BTClibValueError, match=err_msg): assert_p2wsh(script_pub_key[:1] + b"\x00" + script_pub_key[2:])
def test_op_int_serialization() -> None: for i in range(-1, 17): op_int_str = f"OP_{i}" if i > -1 else "OP_1NEGATE" serialized_op_int = serialize([op_int_str]) assert len(serialized_op_int) == 1 assert [op_int_str] == parse(serialized_op_int)
def test_integer_serialization() -> None: assert ["OP_0"] == parse(b"\x00") with warnings.catch_warnings(): warnings.simplefilter("ignore") assert serialize([0]) != b"\x00" for i in range(1, 17): serialized_int = serialize([i]) assert [hex_string(i)] == parse(serialized_int) for i in range(17, 128): serialized_int = serialize([i]) # e.g., i = 26 assert [hex_string(i)] == parse(serialized_int) for i in range(128, 256): serialized_int = serialize([i])
def test_single_byte_serialization() -> None: for i in range(256): hex_str = hex_string(i) # e.g., "1A" serialized_byte = serialize([hex_str]) assert len(serialized_byte) == 2 assert serialized_byte[0] == 1 assert [hex_str] == parse(serialized_byte)
def test_p2wsh_p2sh() -> None: # leading/trailing spaces should be tolerated pub = " 02 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" script_pub_key: List[Command] = [pub, "OP_CHECKSIG"] witness_script_bytes = serialize(script_pub_key) b58.p2wsh_p2sh(witness_script_bytes) b58.p2wsh_p2sh(witness_script_bytes, "testnet")
def p2pk( cls: Type[_ScriptPubKey], key: Key, network: Optional[str] = None, check_validity: bool = True, ) -> _ScriptPubKey: "Return the p2pk ScriptPubKey of the provided Key." payload, network = pub_keyinfo_from_key(key, network) script = serialize([payload, "OP_CHECKSIG"]) return cls(script, network, check_validity)
def p2wsh( cls: Type[_ScriptPubKey], redeem_script: Octets, network: str = "mainnet", check_validity: bool = True, ) -> _ScriptPubKey: "Return the p2wsh ScriptPubKey of the provided redeem script." script_h256 = sha256(redeem_script) script = serialize(["OP_0", script_h256]) return cls(script, network, check_validity)
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 finalize_psbt(psbt: Psbt) -> Psbt: """Finalize the Psbt. The Input Finalizer must only accept a PSBT. For each input, the Input Finalizer determines if the input has enough data to pass validation. If it does, it must construct the 0x07 Finalized scriptSig and 0x08 Finalized scriptWitness and place them into the input key-value map. All other data except the UTXO and unknown fields in the input key-value map should be cleared from the PSBT. The UTXO should be kept to allow Transaction Extractors to verify the final network serialized transaction. """ psbt = deepcopy(psbt) psbt.assert_valid() # TODO: finalizers must fail to finalize inputs # which have signatures that do not match the specified sign_ type for psbt_in in psbt.inputs: if not psbt_in.partial_sigs: raise BTClibValueError("missing signatures") sigs = psbt_in.partial_sigs.values() # https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki#motivation cmds: List[bytes] = [b""] if len(sigs) > 1 else [] cmds += sigs if psbt_in.witness_script: psbt_in.final_script_sig = serialize([psbt_in.redeem_script]) psbt_in.final_script_witness = Witness(cmds + [psbt_in.witness_script]) else: psbt_in.final_script_sig = serialize(cmds + [psbt_in.redeem_script]) psbt_in.partial_sigs = {} psbt_in.sig_hash_type = None psbt_in.redeem_script = b"" psbt_in.witness_script = b"" psbt_in.hd_key_paths = {} return psbt
def p2tr( cls: Type["ScriptPubKey"], internal_key: Optional[Key] = None, script_path: Optional[TaprootScriptTree] = None, network: str = "mainnet", check_validity: bool = True, ) -> "ScriptPubKey": "Return the p2tr ScriptPubKey of the provided script tree." pub_key = output_pubkey(internal_key, script_path)[0] script = serialize(["OP_1", pub_key]) return cls(script, network, check_validity)
def test_p2pkh() -> None: pub_key = "038282263212C609D9EA2A6E3E172DE238D8C39CABD5AC1CA10646E23FD5F51508" signature = "304402206E05A6FE23C59196FFE176C9DDC31E73A9885638F9D1328D47C0C703863B8876022076FEB53811AA5B04E0E79F938EB19906CC5E67548BC555A8E8B8B0FC603D840C01" script_pub_key = serialize([ "OP_DUP", "OP_HASH160", "1018853670F9F3B0582C5B9EE8CE93764AC32B93", "OP_EQUALVERIFY", "OP_CHECKSIG", ]) script_sig = serialize([signature, pub_key]) founding_tx_script = serialize(["OP_0", "OP_0"]) tx_in = TxIn(OutPoint(b"\x00" * 32, 0xFFFFFFFF), founding_tx_script, 0xFFFFFFFF) funding_tx = Tx(1, 0, [tx_in], [TxOut(0, script_pub_key)]) tx_in = TxIn(OutPoint(funding_tx.id, 0), script_sig, 0xFFFFFFFF) receiving_tx = Tx(1, 0, [tx_in], [TxOut(0, b"")]) hash_ = sig_hash.from_tx(funding_tx.vout, receiving_tx, 0, sig_hash.ALL) assert dsa.verify_(hash_, pub_key, bytes.fromhex(signature)[:-1])
def test_nulldata3() -> None: err_msg = "invalid nulldata payload length: " with pytest.raises(BTClibValueError, match=err_msg): payload = "00" * 81 ScriptPubKey.nulldata(payload) # wrong data length: 32 in 35-bytes nulldata script; # it should have been 33 script_pub_key = serialize(["OP_RETURN", b"\x00" * 33]) script_pub_key = script_pub_key[:1] + b"\x20" + script_pub_key[2:] assert not is_nulldata(script_pub_key) # wrong data length: 32 in 83-bytes nulldata script; # it should have been 80 script_pub_key = serialize(["OP_RETURN", b"\x00" * 80]) script_pub_key = script_pub_key[:2] + b"\x20" + script_pub_key[3:] assert not is_nulldata(script_pub_key) # missing OP_PUSHDATA1 (0x4c) in 83-bytes nulldata script, # got 0x20 instead script_pub_key = serialize(["OP_RETURN", b"\x00" * 80]) script_pub_key = script_pub_key[:1] + b"\x20" + script_pub_key[2:] assert not is_nulldata(script_pub_key) assert len(serialize(["OP_RETURN", b"\x00" * 75])) == 77 assert len(serialize(["OP_RETURN", b"\x00" * 76])) == 79 script_pub_key = serialize(["OP_RETURN", b"\x00" * 76])[:-1] assert not is_nulldata(script_pub_key)