Skip to content

Commit 3870121

Browse files
committed
Merge rust-bitcoin#3601: Fix bug in witness stack getters
1ed3fc9 Add unit test for tapscript function (Tobin C. Harding) 195615c Fix bug in witness stack getters (Tobin C. Harding) Pull request description: In rust-bitcoin#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: rust-bitcoin#3598 ACKs for top commit: sanket1729: ACK 1ed3fc9 apoelstra: ACK 1ed3fc9; successfully ran local tests Tree-SHA512: fdaa254a05e58a5a8800ac3316ee1dd2e6da83910fa4860156683ba27c594be19549f22cdf7f170142767733240eba2b31f3a31c10626a1ba5c21409e62b9ec5
2 parents a9bc1c7 + 1ed3fc9 commit 3870121

File tree

2 files changed

+47
-26
lines changed

2 files changed

+47
-26
lines changed

bitcoin/src/blockdata/witness.rs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -147,19 +147,15 @@ crate::internal_macros::define_extension_trait! {
147147
///
148148
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
149149
fn tapscript(&self) -> Option<&Script> {
150-
self.last().and_then(|last| {
151-
// From BIP341:
152-
// If there are at least two witness elements, and the first byte of
153-
// the last element is 0x50, this last element is called annex a
154-
// and is removed from the witness stack.
155-
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
156-
self.nth(self.len() - 3).map(Script::from_bytes)
157-
} else if self.len() >= 2 {
158-
self.nth(self.len() - 2).map(Script::from_bytes)
159-
} else {
160-
None
161-
}
162-
})
150+
if self.is_empty() {
151+
return None;
152+
}
153+
154+
if self.taproot_annex().is_some() {
155+
self.third_to_last().map(Script::from_bytes)
156+
} else {
157+
self.second_to_last().map(Script::from_bytes)
158+
}
163159
}
164160

165161
/// Get the taproot control block following BIP341 rules.
@@ -170,19 +166,15 @@ crate::internal_macros::define_extension_trait! {
170166
///
171167
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
172168
fn taproot_control_block(&self) -> Option<&[u8]> {
173-
self.last().and_then(|last| {
174-
// From BIP341:
175-
// If there are at least two witness elements, and the first byte of
176-
// the last element is 0x50, this last element is called annex a
177-
// and is removed from the witness stack.
178-
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
179-
self.nth(self.len() - 2)
180-
} else if self.len() >= 2 {
181-
Some(last)
182-
} else {
183-
None
184-
}
185-
})
169+
if self.is_empty() {
170+
return None;
171+
}
172+
173+
if self.taproot_annex().is_some() {
174+
self.second_to_last()
175+
} else {
176+
self.last()
177+
}
186178
}
187179

188180
/// Get the taproot annex following BIP341 rules.
@@ -314,6 +306,26 @@ mod test {
314306
assert_eq!(witness_annex.tapscript(), Some(Script::from_bytes(&tapscript[..])));
315307
}
316308

309+
#[test]
310+
fn test_get_tapscript_from_keypath() {
311+
let signature = hex!("deadbeef");
312+
// annex starting with 0x50 causes the branching logic.
313+
let annex = hex!("50");
314+
315+
let witness_vec = vec![signature.clone()];
316+
let witness_vec_annex = vec![signature.clone(), annex];
317+
318+
let witness_serialized: Vec<u8> = serialize(&witness_vec);
319+
let witness_serialized_annex: Vec<u8> = serialize(&witness_vec_annex);
320+
321+
let witness = deserialize::<Witness>(&witness_serialized[..]).unwrap();
322+
let witness_annex = deserialize::<Witness>(&witness_serialized_annex[..]).unwrap();
323+
324+
// With or without annex, no tapscript should be returned.
325+
assert_eq!(witness.tapscript(), None);
326+
assert_eq!(witness_annex.tapscript(), None);
327+
}
328+
317329
#[test]
318330
fn test_get_control_block() {
319331
let tapscript = hex!("deadbeef");

primitives/src/witness.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ impl Witness {
192192
}
193193
}
194194

195+
/// Returns the third-to-last element in the witness, if any.
196+
pub fn third_to_last(&self) -> Option<&[u8]> {
197+
if self.witness_elements <= 2 {
198+
None
199+
} else {
200+
self.nth(self.witness_elements - 3)
201+
}
202+
}
203+
195204
/// Return the nth element in the witness, if any
196205
pub fn nth(&self, index: usize) -> Option<&[u8]> {
197206
let pos = decode_cursor(&self.content, self.indices_start, index)?;

0 commit comments

Comments
 (0)