From c876aca3e85115d5e933fca0235ca4bdde0536b5 Mon Sep 17 00:00:00 2001 From: junderw Date: Tue, 18 Oct 2022 11:52:19 +0900 Subject: [PATCH] Fix: Parse the inner witness_script properly for p2tr --- src/util/script.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/util/script.rs b/src/util/script.rs index 61be03e6..378bb00f 100644 --- a/src/util/script.rs +++ b/src/util/script.rs @@ -51,6 +51,7 @@ pub fn get_innerscripts(txin: &TxIn, prevout: &TxOut) -> InnerScripts { // Wrapped witnessScript for P2WSH or P2SH-P2WSH spends let witness_script = if prevout.script_pubkey.is_v0_p2wsh() + || prevout.script_pubkey.is_v1_p2tr() || redeem_script.as_ref().map_or(false, |s| s.is_v0_p2wsh()) { let witness = &txin.witness; @@ -63,7 +64,39 @@ pub fn get_innerscripts(txin: &TxIn, prevout: &TxOut) -> InnerScripts { #[cfg(feature = "liquid")] let wit_to_vec = Clone::clone; - witness.iter().last().map(wit_to_vec).map(Script::from) + let inner_script_slice = if prevout.script_pubkey.is_v1_p2tr() { + // Witness stack is potentially very large + // so we avoid to_vec() or iter().collect() for performance + let w_len = witness.len(); + witness + .last() + // Get the position of the script spend script (if it exists) + .map(|last_elem| { + // From BIP341: + // If there are at least two witness elements, and the first byte of + // the last element is 0x50, this last element is called annex a + // and is removed from the witness stack. + if w_len >= 2 && last_elem.get(0).filter(|&&v| v == 0x50).is_some() { + // account for the extra item removed from the end + 3 + } else { + // otherwise script is 2nd from last + 2 + } + }) + // Convert to None if not script spend + // Note: Option doesn't have filter_map() method + .filter(|&script_pos_from_last| w_len >= script_pos_from_last) + .map(|script_pos_from_last| { + // Can't use second_to_last() since it might be 3rd to last + witness.iter().nth(w_len - script_pos_from_last) + }) + .flatten() + } else { + witness.last() + }; + + inner_script_slice.map(wit_to_vec).map(Script::from) } else { None };