Fix bug in witness stack getters

In #2646 we introduced a bug in the taproot witness stack getter
functions, of which we have three:

- `tapscript`
- `taproot_control_block`
- `taproot_annex`

Each returns `Some` if a possible bytes slice is found (with no other
guarantees).

Use `taproot_annex` combined with getters from `primitives` to implement
the other two getters. This simplifies the code and fixes the bug.

Add an additional getter to `primitives` `Witness::third_from_last`.

Fix: #3598
This commit is contained in:
Tobin C. Harding 2024-11-11 13:09:13 +11:00
parent 73e33e5808
commit 195615c14d
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
2 changed files with 27 additions and 26 deletions

View File

@ -147,19 +147,15 @@ crate::internal_macros::define_extension_trait! {
/// ///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness. /// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
fn tapscript(&self) -> Option<&Script> { fn tapscript(&self) -> Option<&Script> {
self.last().and_then(|last| { if self.is_empty() {
// From BIP341: return None;
// 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 self.taproot_annex().is_some() {
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) { self.third_to_last().map(Script::from_bytes)
self.nth(self.len() - 3).map(Script::from_bytes) } else {
} else if self.len() >= 2 { self.second_to_last().map(Script::from_bytes)
self.nth(self.len() - 2).map(Script::from_bytes)
} else {
None
} }
})
} }
/// Get the taproot control block following BIP341 rules. /// Get the taproot control block following BIP341 rules.
@ -170,19 +166,15 @@ crate::internal_macros::define_extension_trait! {
/// ///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness. /// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
fn taproot_control_block(&self) -> Option<&[u8]> { fn taproot_control_block(&self) -> Option<&[u8]> {
self.last().and_then(|last| { if self.is_empty() {
// From BIP341: return None;
// 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 self.taproot_annex().is_some() {
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) { self.second_to_last()
self.nth(self.len() - 2) } else {
} else if self.len() >= 2 { self.last()
Some(last)
} else {
None
} }
})
} }
/// Get the taproot annex following BIP341 rules. /// Get the taproot annex following BIP341 rules.

View File

@ -192,6 +192,15 @@ impl Witness {
} }
} }
/// Returns the third-to-last element in the witness, if any.
pub fn third_to_last(&self) -> Option<&[u8]> {
if self.witness_elements <= 2 {
None
} else {
self.nth(self.witness_elements - 3)
}
}
/// Return the nth element in the witness, if any /// Return the nth element in the witness, if any
pub fn nth(&self, index: usize) -> Option<&[u8]> { pub fn nth(&self, index: usize) -> Option<&[u8]> {
let pos = decode_cursor(&self.content, self.indices_start, index)?; let pos = decode_cursor(&self.content, self.indices_start, index)?;