def from_tx(prevouts: List[TxOut], tx: Tx, vin_i: int, hash_type: int) -> bytes: script = prevouts[vin_i].script_pub_key.script if is_p2tr(script): annex = b"" witness = tx.vin[vin_i].script_witness if len(witness.stack) >= 2 and witness.stack[-1][0] == 0x50: annex = witness.stack[-1] witness.stack = witness.stack[:-1] if len(witness.stack) == 0: raise BTClibValueError("Empty stack") ext = b"" if len(witness.stack) > 1: leaf_version = witness.stack[-1][0] & 0xFE preimage = leaf_version.to_bytes(1, "big") preimage += var_bytes.serialize(witness.stack[-2]) tapleaf_hash = tagged_hash(b"TapLeaf", preimage) ext = tapleaf_hash + b"\x00\xff\xff\xff\xff" return taproot( tx, vin_i, [x.value for x in prevouts], [x.script_pub_key for x in prevouts], hash_type, int(bool(ext)), annex, ext, ) # handle all p2sh-wrapped scripts if is_p2sh(script): script = tx.vin[vin_i].script_sig if is_p2wpkh(script): script_ = witness_v0_script(script)[0] return segwit_v0(script_, tx, vin_i, hash_type, prevouts[vin_i].value) if is_p2wsh(script): # the real script is contained in the witness script_ = witness_v0_script(tx.vin[vin_i].script_witness.stack[-1])[0] return segwit_v0(script_, tx, vin_i, hash_type, prevouts[vin_i].value) if is_p2tr(script): raise BTClibValueError("Taproot scripts cannot be wrapped in p2sh") script_ = legacy_script(script)[0] return legacy(script_, tx, vin_i, hash_type)
def test_valid_script_path() -> None: fname = "tapscript_test_vector.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: data = json.load(file_) for x in data: prevouts = [TxOut.parse(prevout) for prevout in x["prevouts"]] index = x["index"] if not is_p2tr(prevouts[index].script_pub_key.script): continue script_sig = x["success"]["scriptSig"] assert not script_sig witness = Witness(x["success"]["witness"]) if len(witness.stack) >= 2 and witness.stack[-1][0] == 0x50: witness.stack = witness.stack[:-1] # check script paths if len(witness.stack) < 2: continue Q = type_and_payload(prevouts[index].script_pub_key.script)[1] script = witness.stack[-2] control = witness.stack[-1] assert check_output_pubkey(Q, script, control)
def test_invalid_taproot_key_path() -> None: fname = "tapscript_test_vector.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: data = json.load(file_) for x in filter(lambda x: "failure" in x.keys(), data): tx = Tx.parse(x["tx"]) prevouts = [TxOut.parse(prevout) for prevout in x["prevouts"]] index = x["index"] if not is_p2tr(prevouts[index].script_pub_key.script): continue witness = Witness(x["failure"]["witness"]) tx.vin[index].script_witness = witness # check only key paths if (len(witness.stack) == 1 or len(witness.stack) == 2 and witness.stack[-1][0] == 0x50): with pytest.raises( (BTClibRuntimeError, BTClibValueError, AssertionError)): assert not x["failure"]["scriptSig"] sighash_type = 0 # all signature = witness.stack[0][:64] if len(witness.stack[0]) == 65: sighash_type = witness.stack[0][-1] if sighash_type == 0: raise BTClibValueError( "invalid sighash 0 in 65 bytes signature") msg_hash = sig_hash.from_tx(prevouts, tx, index, sighash_type) pub_key = type_and_payload( prevouts[index].script_pub_key.script)[1] ssa.assert_as_valid_(msg_hash, pub_key, signature)
def test_valid_taproot_key_path() -> None: fname = "tapscript_test_vector.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: data = json.load(file_) for x in filter(lambda x: "TAPROOT" in x["flags"], data): tx = Tx.parse(x["tx"]) prevouts = [TxOut.parse(prevout) for prevout in x["prevouts"]] index = x["index"] if not is_p2tr(prevouts[index].script_pub_key.script): continue assert not x["success"]["scriptSig"] witness = Witness(x["success"]["witness"]) tx.vin[index].script_witness = witness if (len(witness.stack) == 1 or len(witness.stack) == 2 and witness.stack[-1][0] == 0x50): sighash_type = 0 # all signature = witness.stack[0][:64] if len(witness.stack[0]) == 65: sighash_type = witness.stack[0][-1] assert sighash_type != 0 msg_hash = sig_hash.from_tx(prevouts, tx, index, sighash_type) pub_key = type_and_payload( prevouts[index].script_pub_key.script)[1] ssa.assert_as_valid_(msg_hash, pub_key, signature)