Add get_tapscript to Witness

This new method will check the last witness element to see if it starts with 0x50, and
depending on the result it will return the second to last or third to last witness
element according to BIP341.

In its current state, Witness can not know what type of script it is fulfilling,
so it is up to the caller to verify if the previous output is a taproot output or not.
This commit is contained in:
junderw 2022-11-03 10:31:17 +09:00
parent 4226d60205
commit 3c0d5aed73
No known key found for this signature in database
GPG Key ID: B256185D3A971908
1 changed files with 57 additions and 0 deletions

View File

@ -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<usize> 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<u8> = serialize(&witness_vec);
let witness_serialized_annex: Vec<u8> = 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";