diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index fd47f9dd..2582fd74 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -298,6 +298,34 @@ impl Witness { let pos = decode_cursor(&self.content, self.indices_start, index)?; self.element_at(pos) } + + /// Get Tapscript following BIP341 rules regarding accounting for an annex. + /// + /// This does not guarantee that this represents a P2TR [`Witness`]. + /// It merely gets the second to last or third to last element depending + /// on the first byte of the last element being equal to 0x50. + pub fn get_tapscript(&self) -> Option<&[u8]> { + let len = self.len(); + self + .last() + .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 len >= 2 && last_elem.first().filter(|&&v| v == 0x50).is_some() { + // account for the extra item removed from the end + 3 + } else { + // otherwise script is 2nd from last + 2 + } + }) + .filter(|&script_pos_from_last| len >= script_pos_from_last) + .and_then(|script_pos_from_last| { + self.nth(len - script_pos_from_last) + }) + } } impl Index for Witness { @@ -551,6 +579,35 @@ mod test { assert_eq!(witness_serialized, serialize(&witness)); } + #[test] + fn test_get_tapscript() { + let tapscript = Vec::from_hex("deadbeef").unwrap(); + let control_block = Vec::from_hex("02").unwrap(); + // annex starting with 0x50 causes the branching logic. + let annex = Vec::from_hex("50").unwrap(); + + let witness_vec = vec![tapscript.clone(), control_block.clone()]; + let witness_vec_annex = vec![tapscript.clone(), control_block, annex]; + + let witness_serialized: Vec = serialize(&witness_vec); + let witness_serialized_annex: Vec = serialize(&witness_vec_annex); + + let witness = Witness { + content: append_u32_vec(witness_serialized[1..].to_vec(), &[0, 5]), + witness_elements: 2, + indices_start: 7, + }; + let witness_annex = Witness { + content: append_u32_vec(witness_serialized_annex[1..].to_vec(), &[0, 5, 7]), + witness_elements: 3, + indices_start: 9, + }; + + // With or without annex, the tapscript should be returned. + assert_eq!(witness.get_tapscript(), Some(&tapscript[..])); + assert_eq!(witness_annex.get_tapscript(), Some(&tapscript[..])); + } + #[test] fn test_tx() { let s = "02000000000102b44f26b275b8ad7b81146ba3dbecd081f9c1ea0dc05b97516f56045cfcd3df030100000000ffffffff1cb4749ae827c0b75f3d0a31e63efc8c71b47b5e3634a4c698cd53661cab09170100000000ffffffff020b3a0500000000001976a9143ea74de92762212c96f4dd66c4d72a4deb20b75788ac630500000000000016001493a8dfd1f0b6a600ab01df52b138cda0b82bb7080248304502210084622878c94f4c356ce49c8e33a063ec90f6ee9c0208540888cfab056cd1fca9022014e8dbfdfa46d318c6887afd92dcfa54510e057565e091d64d2ee3a66488f82c0121026e181ffb98ebfe5a64c983073398ea4bcd1548e7b971b4c175346a25a1c12e950247304402203ef00489a0d549114977df2820fab02df75bebb374f5eee9e615107121658cfa02204751f2d1784f8e841bff6d3bcf2396af2f1a5537c0e4397224873fbd3bfbe9cf012102ae6aa498ce2dd204e9180e71b4fb1260fe3d1a95c8025b34e56a9adf5f278af200000000";