Skip to content

Commit 1a3cb16

Browse files
committed
Merge rust-bitcoin#3626: backport: Fix bug in witness stack getters
e9285f5 backport: Add unit test for tapscript function (Tobin C. Harding) ec736fc backport: Fix bug in witness stack getters (Tobin C. Harding) Pull request description: Backport, as a single patch, the following two commits from master - commit: `195615c14 Fix bug in witness stack getters` - commit: `1ed3fc927 Add unit test for tapscript function` From PR rust-bitcoin#3601 ACKs for top commit: apoelstra: ACK e9285f5; successfully ran local tests Tree-SHA512: 08e176f4d6a4da77082769aafb1808f690a20a0216af1fdc1245b4d2be77d00873a1c8b42f65a2b14371a3996ac13ae58c206193947cc8258306e4cc901b2489
2 parents 1650e42 + e9285f5 commit 1a3cb16

File tree

1 file changed

+43
-22
lines changed

1 file changed

+43
-22
lines changed

bitcoin/src/blockdata/witness.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,15 @@ impl Witness {
380380
}
381381
}
382382

383+
/// Returns the third-to-last element in the witness, if any.
384+
pub fn third_to_last(&self) -> Option<&[u8]> {
385+
if self.witness_elements <= 2 {
386+
None
387+
} else {
388+
self.nth(self.witness_elements - 3)
389+
}
390+
}
391+
383392
/// Return the nth element in the witness, if any
384393
pub fn nth(&self, index: usize) -> Option<&[u8]> {
385394
let pos = decode_cursor(&self.content, self.indices_start, index)?;
@@ -394,19 +403,15 @@ impl Witness {
394403
/// [Script::is_p2tr](crate::blockdata::script::Script::is_p2tr) to
395404
/// check whether this is actually a Taproot witness.
396405
pub fn tapscript(&self) -> Option<&Script> {
397-
self.last().and_then(|last| {
398-
// From BIP341:
399-
// If there are at least two witness elements, and the first byte of
400-
// the last element is 0x50, this last element is called annex a
401-
// and is removed from the witness stack.
402-
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
403-
self.nth(self.len() - 3).map(Script::from_bytes)
404-
} else if self.len() >= 2 {
405-
self.nth(self.len() - 2).map(Script::from_bytes)
406+
if self.is_empty() {
407+
return None;
408+
}
409+
410+
if self.taproot_annex().is_some() {
411+
self.third_to_last().map(Script::from_bytes)
406412
} else {
407-
None
413+
self.second_to_last().map(Script::from_bytes)
408414
}
409-
})
410415
}
411416

412417
/// Get the taproot control block following BIP341 rules.
@@ -417,19 +422,15 @@ impl Witness {
417422
/// [Script::is_p2tr](crate::blockdata::script::Script::is_p2tr) to
418423
/// check whether this is actually a Taproot witness.
419424
pub fn taproot_control_block(&self) -> Option<&[u8]> {
420-
self.last().and_then(|last| {
421-
// From BIP341:
422-
// If there are at least two witness elements, and the first byte of
423-
// the last element is 0x50, this last element is called annex a
424-
// and is removed from the witness stack.
425-
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
426-
self.nth(self.len() - 2)
427-
} else if self.len() >= 2 {
428-
Some(last)
425+
if self.is_empty() {
426+
return None;
427+
}
428+
429+
if self.taproot_annex().is_some() {
430+
self.second_to_last()
429431
} else {
430-
None
432+
self.last()
431433
}
432-
})
433434
}
434435

435436
/// Get the taproot annex following BIP341 rules.
@@ -756,6 +757,26 @@ mod test {
756757
assert_eq!(witness_annex.tapscript(), Some(Script::from_bytes(&tapscript[..])));
757758
}
758759

760+
#[test]
761+
fn test_get_tapscript_from_keypath() {
762+
let signature = hex!("deadbeef");
763+
// annex starting with 0x50 causes the branching logic.
764+
let annex = hex!("50");
765+
766+
let witness_vec = vec![signature.clone()];
767+
let witness_vec_annex = vec![signature.clone(), annex];
768+
769+
let witness_serialized: Vec<u8> = serialize(&witness_vec);
770+
let witness_serialized_annex: Vec<u8> = serialize(&witness_vec_annex);
771+
772+
let witness = deserialize::<Witness>(&witness_serialized[..]).unwrap();
773+
let witness_annex = deserialize::<Witness>(&witness_serialized_annex[..]).unwrap();
774+
775+
// With or without annex, no tapscript should be returned.
776+
assert_eq!(witness.tapscript(), None);
777+
assert_eq!(witness_annex.tapscript(), None);
778+
}
779+
759780
#[test]
760781
fn test_get_control_block() {
761782
let tapscript = hex!("deadbeef");

0 commit comments

Comments
 (0)