diff --git a/bitcoin/examples/sighash.rs b/bitcoin/examples/sighash.rs index bc29bbda7..e4bf819ef 100644 --- a/bitcoin/examples/sighash.rs +++ b/bitcoin/examples/sighash.rs @@ -29,8 +29,8 @@ fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, amount: Amount) { // BIP-141: The witness must consist of exactly 2 items (≤ 520 bytes each). The first one a // signature, and the second one a public key. assert_eq!(witness.len(), 2); - let sig_bytes = witness.nth(0).unwrap(); - let pk_bytes = witness.nth(1).unwrap(); + let sig_bytes = witness.get(0).unwrap(); + let pk_bytes = witness.get(1).unwrap(); let sig = ecdsa::Signature::from_slice(sig_bytes).expect("failed to parse sig"); @@ -118,7 +118,7 @@ fn compute_sighash_p2wsh(raw_tx: &[u8], inp_idx: usize, amount: Amount) { //in an M of N multisig, the witness elements from 1 (0-based) to M-2 are signatures (with sighash flags as the last byte) for n in 1..=witness.len() - 2 { - let sig_bytes = witness.nth(n).expect("out of bounds"); + let sig_bytes = witness.get(n).expect("out of bounds"); let sig = ecdsa::Signature::from_slice(sig_bytes).expect("failed to parse sig"); let sig_len = sig_bytes.len() - 1; //last byte is EcdsaSighashType sighash flag //ECDSA signature in DER format lengths are between 70 and 72 bytes diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index 99d42f316..89b998081 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -257,7 +257,7 @@ impl<'a> P2TrSpend<'a> { }), 2 if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => { let spend = P2TrSpend::Key { - // signature: witness.second_to_last().expect("len > 1"), + // signature: witness.get_back(1).expect("len > 1"), annex: witness.last(), }; Some(spend) @@ -267,15 +267,15 @@ impl<'a> P2TrSpend<'a> { // arm. 3.. if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => { let spend = P2TrSpend::Script { - leaf_script: Script::from_bytes(witness.third_to_last().expect("len > 2")), - control_block: witness.second_to_last().expect("len > 1"), + leaf_script: Script::from_bytes(witness.get_back(2).expect("len > 2")), + control_block: witness.get_back(1).expect("len > 1"), annex: witness.last(), }; Some(spend) } _ => { let spend = P2TrSpend::Script { - leaf_script: Script::from_bytes(witness.second_to_last().expect("len > 1")), + leaf_script: Script::from_bytes(witness.get_back(1).expect("len > 1")), control_block: witness.last().expect("len > 0"), annex: None, }; @@ -514,13 +514,10 @@ mod test { assert_eq!(expected_wit[i], wit_el.to_lower_hex_string()); } assert_eq!(expected_wit[1], tx.input[0].witness.last().unwrap().to_lower_hex_string()); - assert_eq!( - expected_wit[0], - tx.input[0].witness.second_to_last().unwrap().to_lower_hex_string() - ); - assert_eq!(expected_wit[0], tx.input[0].witness.nth(0).unwrap().to_lower_hex_string()); - assert_eq!(expected_wit[1], tx.input[0].witness.nth(1).unwrap().to_lower_hex_string()); - assert_eq!(None, tx.input[0].witness.nth(2)); + assert_eq!(expected_wit[0], tx.input[0].witness.get_back(1).unwrap().to_lower_hex_string()); + assert_eq!(expected_wit[0], tx.input[0].witness.get(0).unwrap().to_lower_hex_string()); + assert_eq!(expected_wit[1], tx.input[0].witness.get(1).unwrap().to_lower_hex_string()); + assert_eq!(None, tx.input[0].witness.get(2)); assert_eq!(expected_wit[0], tx.input[0].witness[0].to_lower_hex_string()); assert_eq!(expected_wit[1], tx.input[0].witness[1].to_lower_hex_string()); diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs index f8328b820..29b0e7743 100644 --- a/primitives/src/witness.rs +++ b/primitives/src/witness.rs @@ -185,37 +185,41 @@ impl Witness { /// Returns the last element in the witness, if any. #[inline] - pub fn last(&self) -> Option<&[u8]> { - if self.witness_elements == 0 { + pub fn last(&self) -> Option<&[u8]> { self.get_back(0) } + + /// Retrieves an element from the end of the witness by its reverse index. + /// + /// `index` is 0-based from the end, where 0 is the last element, 1 is the second-to-last, etc. + /// + /// Returns `None` if the requested index is beyond the witness's elements. + /// + /// # Examples + /// ``` + /// use bitcoin_primitives::witness::Witness; + /// + /// let mut witness = Witness::new(); + /// witness.push(b"A"); + /// witness.push(b"B"); + /// witness.push(b"C"); + /// witness.push(b"D"); + /// + /// assert_eq!(witness.get_back(0), Some(b"D".as_slice())); + /// assert_eq!(witness.get_back(1), Some(b"C".as_slice())); + /// assert_eq!(witness.get_back(2), Some(b"B".as_slice())); + /// assert_eq!(witness.get_back(3), Some(b"A".as_slice())); + /// assert_eq!(witness.get_back(4), None); + /// ``` + pub fn get_back(&self, index: usize) -> Option<&[u8]> { + if self.witness_elements <= index { None } else { - self.nth(self.witness_elements - 1) + self.get(self.witness_elements - 1 - index) } } - /// Returns the second-to-last element in the witness, if any. + /// Returns a specific element from the witness by its index, if any. #[inline] - pub fn second_to_last(&self) -> Option<&[u8]> { - if self.witness_elements <= 1 { - None - } else { - self.nth(self.witness_elements - 2) - } - } - - /// Returns the third-to-last element in the witness, if any. - #[inline] - 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 - #[inline] - pub fn nth(&self, index: usize) -> Option<&[u8]> { + pub fn get(&self, index: usize) -> Option<&[u8]> { let pos = decode_cursor(&self.content, self.indices_start, index)?; self.element_at(pos) } @@ -274,8 +278,9 @@ pub struct Iter<'a> { impl Index for Witness { type Output = [u8]; + #[track_caller] #[inline] - fn index(&self, index: usize) -> &Self::Output { self.nth(index).expect("out of bounds") } + fn index(&self, index: usize) -> &Self::Output { self.get(index).expect("out of bounds") } } impl<'a> Iterator for Iter<'a> { @@ -468,12 +473,12 @@ mod test { let mut witness = Witness::default(); assert!(witness.is_empty()); assert_eq!(witness.last(), None); - assert_eq!(witness.second_to_last(), None); + assert_eq!(witness.get_back(1), None); - assert_eq!(witness.nth(0), None); - assert_eq!(witness.nth(1), None); - assert_eq!(witness.nth(2), None); - assert_eq!(witness.nth(3), None); + assert_eq!(witness.get(0), None); + assert_eq!(witness.get(1), None); + assert_eq!(witness.get(2), None); + assert_eq!(witness.get(3), None); // Push a single byte element onto the witness stack. let push = [11_u8]; @@ -491,13 +496,13 @@ mod test { let element_0 = push.as_slice(); assert_eq!(element_0, &witness[0]); - assert_eq!(witness.second_to_last(), None); + assert_eq!(witness.get_back(1), None); assert_eq!(witness.last(), Some(element_0)); - assert_eq!(witness.nth(0), Some(element_0)); - assert_eq!(witness.nth(1), None); - assert_eq!(witness.nth(2), None); - assert_eq!(witness.nth(3), None); + assert_eq!(witness.get(0), Some(element_0)); + assert_eq!(witness.get(1), None); + assert_eq!(witness.get(2), None); + assert_eq!(witness.get(3), None); // Now push 2 byte element onto the witness stack. let push = [21u8, 22u8]; @@ -514,12 +519,12 @@ mod test { let element_1 = push.as_slice(); assert_eq!(element_1, &witness[1]); - assert_eq!(witness.nth(0), Some(element_0)); - assert_eq!(witness.nth(1), Some(element_1)); - assert_eq!(witness.nth(2), None); - assert_eq!(witness.nth(3), None); + assert_eq!(witness.get(0), Some(element_0)); + assert_eq!(witness.get(1), Some(element_1)); + assert_eq!(witness.get(2), None); + assert_eq!(witness.get(3), None); - assert_eq!(witness.second_to_last(), Some(element_0)); + assert_eq!(witness.get_back(1), Some(element_0)); assert_eq!(witness.last(), Some(element_1)); // Now push another 2 byte element onto the witness stack. @@ -537,13 +542,13 @@ mod test { let element_2 = push.as_slice(); assert_eq!(element_2, &witness[2]); - assert_eq!(witness.nth(0), Some(element_0)); - assert_eq!(witness.nth(1), Some(element_1)); - assert_eq!(witness.nth(2), Some(element_2)); - assert_eq!(witness.nth(3), None); + assert_eq!(witness.get(0), Some(element_0)); + assert_eq!(witness.get(1), Some(element_1)); + assert_eq!(witness.get(2), Some(element_2)); + assert_eq!(witness.get(3), None); - assert_eq!(witness.third_to_last(), Some(element_0)); - assert_eq!(witness.second_to_last(), Some(element_1)); + assert_eq!(witness.get_back(2), Some(element_0)); + assert_eq!(witness.get_back(1), Some(element_1)); assert_eq!(witness.last(), Some(element_2)); } @@ -574,8 +579,8 @@ mod test { let indices_start = elements.len(); let witness = Witness::from_parts__unstable(content.clone(), witness_elements, indices_start); - assert_eq!(witness.nth(0).unwrap(), [11_u8]); - assert_eq!(witness.nth(1).unwrap(), [21_u8, 22]); + assert_eq!(witness.get(0).unwrap(), [11_u8]); + assert_eq!(witness.get(1).unwrap(), [21_u8, 22]); assert_eq!(witness.size(), 6); }