witness: Improve element access methods with get() and get_back()

Enhance Witness struct element access methods:

- Rename `nth()` to `get()` for clearer slice-like element retrieval
- Introduce `get_back()` method for flexible reverse indexing
- Remove redundant `second_to_last()` and `third_to_last()` methods
- Add `#[track_caller]` to index implementation for better error tracking
- Update all references to use new method names
- Improve documentation with usage examples

The changes provide a more intuitive and consistent approach to
accessing witness elements.
This commit is contained in:
Erick Cestari 2025-03-04 11:49:11 -03:00
commit 3ca3218c23
10 changed files with 132 additions and 61 deletions

View File

@ -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 // 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. // signature, and the second one a public key.
assert_eq!(witness.len(), 2); assert_eq!(witness.len(), 2);
let sig_bytes = witness.nth(0).unwrap(); let sig_bytes = witness.get(0).unwrap();
let pk_bytes = witness.nth(1).unwrap(); let pk_bytes = witness.get(1).unwrap();
let sig = ecdsa::Signature::from_slice(sig_bytes).expect("failed to parse sig"); 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) //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 { 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 = ecdsa::Signature::from_slice(sig_bytes).expect("failed to parse sig");
let sig_len = sig_bytes.len() - 1; //last byte is EcdsaSighashType sighash flag 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 //ECDSA signature in DER format lengths are between 70 and 72 bytes

View File

@ -257,7 +257,7 @@ impl<'a> P2TrSpend<'a> {
}), }),
2 if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => { 2 if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => {
let spend = P2TrSpend::Key { let spend = P2TrSpend::Key {
// signature: witness.second_to_last().expect("len > 1"), // signature: witness.get_back(1).expect("len > 1"),
annex: witness.last(), annex: witness.last(),
}; };
Some(spend) Some(spend)
@ -267,15 +267,15 @@ impl<'a> P2TrSpend<'a> {
// arm. // arm.
3.. if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => { 3.. if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => {
let spend = P2TrSpend::Script { let spend = P2TrSpend::Script {
leaf_script: Script::from_bytes(witness.third_to_last().expect("len > 2")), leaf_script: Script::from_bytes(witness.get_back(2).expect("len > 2")),
control_block: witness.second_to_last().expect("len > 1"), control_block: witness.get_back(1).expect("len > 1"),
annex: witness.last(), annex: witness.last(),
}; };
Some(spend) Some(spend)
} }
_ => { _ => {
let spend = P2TrSpend::Script { 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"), control_block: witness.last().expect("len > 0"),
annex: None, annex: None,
}; };
@ -514,13 +514,10 @@ mod test {
assert_eq!(expected_wit[i], wit_el.to_lower_hex_string()); 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[1], tx.input[0].witness.last().unwrap().to_lower_hex_string());
assert_eq!( assert_eq!(expected_wit[0], tx.input[0].witness.get_back(1).unwrap().to_lower_hex_string());
expected_wit[0], assert_eq!(expected_wit[0], tx.input[0].witness.get(0).unwrap().to_lower_hex_string());
tx.input[0].witness.second_to_last().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.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[0].to_lower_hex_string()); 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()); assert_eq!(expected_wit[1], tx.input[0].witness[1].to_lower_hex_string());

View File

@ -70,6 +70,7 @@ where
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl Block<Unchecked> { impl Block<Unchecked> {
/// Constructs a new `Block` without doing any validation. /// Constructs a new `Block` without doing any validation.
#[inline]
pub fn new_unchecked(header: Header, transactions: Vec<Transaction>) -> Block<Unchecked> { pub fn new_unchecked(header: Header, transactions: Vec<Transaction>) -> Block<Unchecked> {
Block { header, transactions, witness_root: None, marker: PhantomData::<Unchecked> } Block { header, transactions, witness_root: None, marker: PhantomData::<Unchecked> }
} }
@ -78,6 +79,7 @@ impl Block<Unchecked> {
/// ///
/// You should only use this function if you trust the block i.e., it comes from a trusted node. /// You should only use this function if you trust the block i.e., it comes from a trusted node.
#[must_use] #[must_use]
#[inline]
pub fn assume_checked(self, witness_root: Option<WitnessMerkleNode>) -> Block<Checked> { pub fn assume_checked(self, witness_root: Option<WitnessMerkleNode>) -> Block<Checked> {
Block { Block {
header: self.header, header: self.header,
@ -88,37 +90,44 @@ impl Block<Unchecked> {
} }
/// Decomposes block into its constituent parts. /// Decomposes block into its constituent parts.
#[inline]
pub fn into_parts(self) -> (Header, Vec<Transaction>) { (self.header, self.transactions) } pub fn into_parts(self) -> (Header, Vec<Transaction>) { (self.header, self.transactions) }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl Block<Checked> { impl Block<Checked> {
/// Gets a reference to the block header. /// Gets a reference to the block header.
#[inline]
pub fn header(&self) -> &Header { &self.header } pub fn header(&self) -> &Header { &self.header }
/// Gets a reference to the block's list of transactions. /// Gets a reference to the block's list of transactions.
#[inline]
pub fn transactions(&self) -> &[Transaction] { &self.transactions } pub fn transactions(&self) -> &[Transaction] { &self.transactions }
/// Returns the cached witness root if one is present. /// Returns the cached witness root if one is present.
/// ///
/// It is assumed that a block will have the witness root calculated and cached as part of the /// It is assumed that a block will have the witness root calculated and cached as part of the
/// validation process. /// validation process.
#[inline]
pub fn cached_witness_root(&self) -> Option<WitnessMerkleNode> { self.witness_root } pub fn cached_witness_root(&self) -> Option<WitnessMerkleNode> { self.witness_root }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<V: Validation> Block<V> { impl<V: Validation> Block<V> {
/// Returns the block hash. /// Returns the block hash.
#[inline]
pub fn block_hash(&self) -> BlockHash { self.header.block_hash() } pub fn block_hash(&self) -> BlockHash { self.header.block_hash() }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<Block> for BlockHash { impl From<Block> for BlockHash {
#[inline]
fn from(block: Block) -> BlockHash { block.block_hash() } fn from(block: Block) -> BlockHash { block.block_hash() }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<&Block> for BlockHash { impl From<&Block> for BlockHash {
#[inline]
fn from(block: &Block) -> BlockHash { block.block_hash() } fn from(block: &Block) -> BlockHash { block.block_hash() }
} }
@ -212,10 +221,12 @@ impl fmt::Debug for Header {
} }
impl From<Header> for BlockHash { impl From<Header> for BlockHash {
#[inline]
fn from(header: Header) -> BlockHash { header.block_hash() } fn from(header: Header) -> BlockHash { header.block_hash() }
} }
impl From<&Header> for BlockHash { impl From<&Header> for BlockHash {
#[inline]
fn from(header: &Header) -> BlockHash { header.block_hash() } fn from(header: &Header) -> BlockHash { header.block_hash() }
} }
@ -263,6 +274,7 @@ impl Version {
/// Returns the inner `i32` value. /// Returns the inner `i32` value.
/// ///
/// This is the data type used in consensus code in Bitcoin Core. /// This is the data type used in consensus code in Bitcoin Core.
#[inline]
pub fn to_consensus(self) -> i32 { self.0 } pub fn to_consensus(self) -> i32 { self.0 }
/// Checks whether the version number is signalling a soft fork at the given bit. /// Checks whether the version number is signalling a soft fork at the given bit.
@ -286,6 +298,7 @@ impl Version {
} }
impl Default for Version { impl Default for Version {
#[inline]
fn default() -> Version { Self::NO_SOFT_FORK_SIGNALLING } fn default() -> Version { Self::NO_SOFT_FORK_SIGNALLING }
} }

View File

@ -102,6 +102,7 @@ impl LockTime {
/// ///
/// # Ok::<_, units::parse::PrefixedHexError>(()) /// # Ok::<_, units::parse::PrefixedHexError>(())
/// ``` /// ```
#[inline]
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> { pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let lock_time = parse::hex_u32_prefixed(s)?; let lock_time = parse::hex_u32_prefixed(s)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
@ -119,6 +120,7 @@ impl LockTime {
/// ///
/// # Ok::<_, units::parse::UnprefixedHexError>(()) /// # Ok::<_, units::parse::UnprefixedHexError>(())
/// ``` /// ```
#[inline]
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> { pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
let lock_time = parse::hex_u32_unprefixed(s)?; let lock_time = parse::hex_u32_unprefixed(s)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
@ -320,6 +322,7 @@ impl From<Time> for LockTime {
} }
impl fmt::Debug for LockTime { impl fmt::Debug for LockTime {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LockTime::*; use LockTime::*;

View File

@ -89,6 +89,7 @@ impl LockTime {
/// ///
/// # Ok::<_, bitcoin_primitives::relative::DisabledLockTimeError>(()) /// # Ok::<_, bitcoin_primitives::relative::DisabledLockTimeError>(())
/// ``` /// ```
#[inline]
pub fn from_consensus(n: u32) -> Result<Self, DisabledLockTimeError> { pub fn from_consensus(n: u32) -> Result<Self, DisabledLockTimeError> {
let sequence = crate::Sequence::from_consensus(n); let sequence = crate::Sequence::from_consensus(n);
sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n)) sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n))
@ -370,12 +371,14 @@ impl fmt::Display for LockTime {
impl convert::TryFrom<Sequence> for LockTime { impl convert::TryFrom<Sequence> for LockTime {
type Error = DisabledLockTimeError; type Error = DisabledLockTimeError;
#[inline]
fn try_from(seq: Sequence) -> Result<LockTime, DisabledLockTimeError> { fn try_from(seq: Sequence) -> Result<LockTime, DisabledLockTimeError> {
LockTime::from_sequence(seq) LockTime::from_sequence(seq)
} }
} }
impl From<LockTime> for Sequence { impl From<LockTime> for Sequence {
#[inline]
fn from(lt: LockTime) -> Sequence { lt.to_sequence() } fn from(lt: LockTime) -> Sequence { lt.to_sequence() }
} }
@ -387,10 +390,12 @@ pub struct DisabledLockTimeError(u32);
impl DisabledLockTimeError { impl DisabledLockTimeError {
/// Accessor for the `u32` whose "disable" flag was set, preventing /// Accessor for the `u32` whose "disable" flag was set, preventing
/// it from being parsed as a relative locktime. /// it from being parsed as a relative locktime.
#[inline]
pub fn disabled_locktime_value(&self) -> u32 { self.0 } pub fn disabled_locktime_value(&self) -> u32 { self.0 }
} }
impl fmt::Display for DisabledLockTimeError { impl fmt::Display for DisabledLockTimeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "lock time 0x{:08x} has disable flag set", self.0) write!(f, "lock time 0x{:08x} has disable flag set", self.0)
} }
@ -417,6 +422,7 @@ impl IncompatibleHeightError {
} }
impl fmt::Display for IncompatibleHeightError { impl fmt::Display for IncompatibleHeightError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
@ -447,6 +453,7 @@ impl IncompatibleTimeError {
} }
impl fmt::Display for IncompatibleTimeError { impl fmt::Display for IncompatibleTimeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,

View File

@ -440,6 +440,7 @@ impl From<u8> for Opcode {
} }
impl fmt::Debug for Opcode { impl fmt::Debug for Opcode {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
} }

View File

@ -24,9 +24,11 @@ pub struct CompactTarget(u32);
impl CompactTarget { impl CompactTarget {
/// Constructs a new [`CompactTarget`] from a consensus encoded `u32`. /// Constructs a new [`CompactTarget`] from a consensus encoded `u32`.
#[inline]
pub fn from_consensus(bits: u32) -> Self { Self(bits) } pub fn from_consensus(bits: u32) -> Self { Self(bits) }
/// Returns the consensus encoded `u32` representation of this [`CompactTarget`]. /// Returns the consensus encoded `u32` representation of this [`CompactTarget`].
#[inline]
pub fn to_consensus(self) -> u32 { self.0 } pub fn to_consensus(self) -> u32 { self.0 }
} }

View File

@ -124,12 +124,14 @@ impl Sequence {
} }
/// Constructs a new `Sequence` from a prefixed hex string. /// Constructs a new `Sequence` from a prefixed hex string.
#[inline]
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> { pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let lock_time = parse::hex_u32_prefixed(s)?; let lock_time = parse::hex_u32_prefixed(s)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
} }
/// Constructs a new `Sequence` from an unprefixed hex string. /// Constructs a new `Sequence` from an unprefixed hex string.
#[inline]
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> { pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
let lock_time = parse::hex_u32_unprefixed(s)?; let lock_time = parse::hex_u32_unprefixed(s)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
@ -203,23 +205,28 @@ impl Sequence {
/// Returns the low 16 bits from sequence number. /// Returns the low 16 bits from sequence number.
/// ///
/// BIP-68 only uses the low 16 bits for relative lock value. /// BIP-68 only uses the low 16 bits for relative lock value.
#[inline]
fn low_u16(&self) -> u16 { self.0 as u16 } fn low_u16(&self) -> u16 { self.0 as u16 }
} }
impl Default for Sequence { impl Default for Sequence {
/// The default value of sequence is 0xffffffff. /// The default value of sequence is 0xffffffff.
#[inline]
fn default() -> Self { Sequence::MAX } fn default() -> Self { Sequence::MAX }
} }
impl From<Sequence> for u32 { impl From<Sequence> for u32 {
#[inline]
fn from(sequence: Sequence) -> u32 { sequence.0 } fn from(sequence: Sequence) -> u32 { sequence.0 }
} }
impl fmt::Display for Sequence { impl fmt::Display for Sequence {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
impl fmt::LowerHex for Sequence { impl fmt::LowerHex for Sequence {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -228,10 +235,12 @@ internals::impl_to_hex_from_lower_hex!(Sequence, |sequence: &Sequence| {
}); });
impl fmt::UpperHex for Sequence { impl fmt::UpperHex for Sequence {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
} }
impl fmt::Debug for Sequence { impl fmt::Debug for Sequence {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 10 because its 8 digits + 2 for the '0x' // 10 because its 8 digits + 2 for the '0x'
write!(f, "Sequence({:#010x})", self.0) write!(f, "Sequence({:#010x})", self.0)

View File

@ -115,15 +115,19 @@ impl Transaction {
pub const MAX_STANDARD_WEIGHT: Weight = Weight::from_wu(400_000); pub const MAX_STANDARD_WEIGHT: Weight = Weight::from_wu(400_000);
/// Returns a reference to the transaction inputs. /// Returns a reference to the transaction inputs.
#[inline]
pub fn inputs(&self) -> &[TxIn] { &self.input } pub fn inputs(&self) -> &[TxIn] { &self.input }
/// Returns a mutable reference to the transaction inputs. /// Returns a mutable reference to the transaction inputs.
#[inline]
pub fn inputs_mut(&mut self) -> &mut [TxIn] { &mut self.input } pub fn inputs_mut(&mut self) -> &mut [TxIn] { &mut self.input }
/// Returns a reference to the transaction outputs. /// Returns a reference to the transaction outputs.
#[inline]
pub fn outputs(&self) -> &[TxOut] { &self.output } pub fn outputs(&self) -> &[TxOut] { &self.output }
/// Returns a mutable reference to the transaction outputs. /// Returns a mutable reference to the transaction outputs.
#[inline]
pub fn outputs_mut(&mut self) -> &mut [TxOut] { &mut self.output } pub fn outputs_mut(&mut self) -> &mut [TxOut] { &mut self.output }
/// Computes a "normalized TXID" which does not include any signatures. /// Computes a "normalized TXID" which does not include any signatures.
@ -155,6 +159,7 @@ impl Transaction {
/// witness fields themselves). For non-SegWit transactions which do not have any SegWit data, /// witness fields themselves). For non-SegWit transactions which do not have any SegWit data,
/// this will be equal to [`Transaction::compute_wtxid()`]. /// this will be equal to [`Transaction::compute_wtxid()`].
#[doc(alias = "txid")] #[doc(alias = "txid")]
#[inline]
pub fn compute_txid(&self) -> Txid { pub fn compute_txid(&self) -> Txid {
let hash = hash_transaction(self, false); let hash = hash_transaction(self, false);
Txid::from_byte_array(hash.to_byte_array()) Txid::from_byte_array(hash.to_byte_array())
@ -166,6 +171,7 @@ impl Transaction {
/// witness fields themselves). For non-SegWit transactions which do not have any SegWit data, /// witness fields themselves). For non-SegWit transactions which do not have any SegWit data,
/// this will be equal to [`Transaction::compute_txid()`]. /// this will be equal to [`Transaction::compute_txid()`].
#[doc(alias = "wtxid")] #[doc(alias = "wtxid")]
#[inline]
pub fn compute_wtxid(&self) -> Wtxid { pub fn compute_wtxid(&self) -> Wtxid {
let hash = hash_transaction(self, self.uses_segwit_serialization()); let hash = hash_transaction(self, self.uses_segwit_serialization());
Wtxid::from_byte_array(hash.to_byte_array()) Wtxid::from_byte_array(hash.to_byte_array())
@ -173,6 +179,7 @@ impl Transaction {
/// Returns whether or not to serialize transaction as specified in BIP-144. /// Returns whether or not to serialize transaction as specified in BIP-144.
// This is duplicated in `bitcoin`, if you change it please do so in both places. // This is duplicated in `bitcoin`, if you change it please do so in both places.
#[inline]
fn uses_segwit_serialization(&self) -> bool { fn uses_segwit_serialization(&self) -> bool {
if self.input.iter().any(|input| !input.witness.is_empty()) { if self.input.iter().any(|input| !input.witness.is_empty()) {
return true; return true;
@ -185,6 +192,7 @@ impl Transaction {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl cmp::PartialOrd for Transaction { impl cmp::PartialOrd for Transaction {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) } fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
} }
@ -201,21 +209,25 @@ impl cmp::Ord for Transaction {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<Transaction> for Txid { impl From<Transaction> for Txid {
#[inline]
fn from(tx: Transaction) -> Txid { tx.compute_txid() } fn from(tx: Transaction) -> Txid { tx.compute_txid() }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<&Transaction> for Txid { impl From<&Transaction> for Txid {
#[inline]
fn from(tx: &Transaction) -> Txid { tx.compute_txid() } fn from(tx: &Transaction) -> Txid { tx.compute_txid() }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<Transaction> for Wtxid { impl From<Transaction> for Wtxid {
#[inline]
fn from(tx: Transaction) -> Wtxid { tx.compute_wtxid() } fn from(tx: Transaction) -> Wtxid { tx.compute_wtxid() }
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<&Transaction> for Wtxid { impl From<&Transaction> for Wtxid {
#[inline]
fn from(tx: &Transaction) -> Wtxid { tx.compute_wtxid() } fn from(tx: &Transaction) -> Wtxid { tx.compute_wtxid() }
} }
@ -377,6 +389,7 @@ impl OutPoint {
} }
impl fmt::Display for OutPoint { impl fmt::Display for OutPoint {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.txid, self.vout) write!(f, "{}:{}", self.txid, self.vout)
} }
@ -439,6 +452,7 @@ pub enum ParseOutPointError {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl From<Infallible> for ParseOutPointError { impl From<Infallible> for ParseOutPointError {
#[inline]
fn from(never: Infallible) -> Self { match never {} } fn from(never: Infallible) -> Self { match never {} }
} }
@ -553,10 +567,12 @@ impl Version {
} }
impl fmt::Display for Version { impl fmt::Display for Version {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
impl From<Version> for u32 { impl From<Version> for u32 {
#[inline]
fn from(version: Version) -> Self { version.0 } fn from(version: Version) -> Self { version.0 }
} }

View File

@ -99,18 +99,22 @@ impl Witness {
} }
/// Convenience method to create an array of byte-arrays from this witness. /// Convenience method to create an array of byte-arrays from this witness.
#[inline]
pub fn to_vec(&self) -> Vec<Vec<u8>> { self.iter().map(|s| s.to_vec()).collect() } pub fn to_vec(&self) -> Vec<Vec<u8>> { self.iter().map(|s| s.to_vec()).collect() }
/// Returns `true` if the witness contains no element. /// Returns `true` if the witness contains no element.
#[inline]
pub fn is_empty(&self) -> bool { self.witness_elements == 0 } pub fn is_empty(&self) -> bool { self.witness_elements == 0 }
/// Returns a struct implementing [`Iterator`]. /// Returns a struct implementing [`Iterator`].
#[must_use = "iterators are lazy and do nothing unless consumed"] #[must_use = "iterators are lazy and do nothing unless consumed"]
#[inline]
pub fn iter(&self) -> Iter { pub fn iter(&self) -> Iter {
Iter { inner: self.content.as_slice(), indices_start: self.indices_start, current_index: 0 } Iter { inner: self.content.as_slice(), indices_start: self.indices_start, current_index: 0 }
} }
/// Returns the number of elements this witness holds. /// Returns the number of elements this witness holds.
#[inline]
pub fn len(&self) -> usize { self.witness_elements } pub fn len(&self) -> usize { self.witness_elements }
/// Returns the number of bytes this witness contributes to a transactions total size. /// Returns the number of bytes this witness contributes to a transactions total size.
@ -130,6 +134,7 @@ impl Witness {
} }
/// Clear the witness. /// Clear the witness.
#[inline]
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.content.clear(); self.content.clear();
self.witness_elements = 0; self.witness_elements = 0;
@ -137,6 +142,7 @@ impl Witness {
} }
/// Push a new element on the witness, requires an allocation. /// Push a new element on the witness, requires an allocation.
#[inline]
pub fn push<T: AsRef<[u8]>>(&mut self, new_element: T) { pub fn push<T: AsRef<[u8]>>(&mut self, new_element: T) {
self.push_slice(new_element.as_ref()); self.push_slice(new_element.as_ref());
} }
@ -178,34 +184,42 @@ impl Witness {
} }
/// Returns the last element in the witness, if any. /// Returns the last element in the witness, if any.
pub fn last(&self) -> Option<&[u8]> { #[inline]
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 None
} else { } 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.
pub fn second_to_last(&self) -> Option<&[u8]> { #[inline]
if self.witness_elements <= 1 { pub fn get(&self, index: usize) -> Option<&[u8]> {
None
} else {
self.nth(self.witness_elements - 2)
}
}
/// 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
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)?;
self.element_at(pos) self.element_at(pos)
} }
@ -264,7 +278,9 @@ pub struct Iter<'a> {
impl Index<usize> for Witness { impl Index<usize> for Witness {
type Output = [u8]; type Output = [u8];
fn index(&self, index: usize) -> &Self::Output { self.nth(index).expect("out of bounds") } #[track_caller]
#[inline]
fn index(&self, index: usize) -> &Self::Output { self.get(index).expect("out of bounds") }
} }
impl<'a> Iterator for Iter<'a> { impl<'a> Iterator for Iter<'a> {
@ -281,6 +297,7 @@ impl<'a> Iterator for Iter<'a> {
Some(&slice[..end]) Some(&slice[..end])
} }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
let total_count = (self.inner.len() - self.indices_start) / 4; let total_count = (self.inner.len() - self.indices_start) / 4;
let remaining = total_count - self.current_index; let remaining = total_count - self.current_index;
@ -294,6 +311,7 @@ impl<'a> IntoIterator for &'a Witness {
type IntoIter = Iter<'a>; type IntoIter = Iter<'a>;
type Item = &'a [u8]; type Item = &'a [u8];
#[inline]
fn into_iter(self) -> Self::IntoIter { self.iter() } fn into_iter(self) -> Self::IntoIter { self.iter() }
} }
@ -381,22 +399,27 @@ impl<'de> serde::Deserialize<'de> for Witness {
} }
impl From<Vec<Vec<u8>>> for Witness { impl From<Vec<Vec<u8>>> for Witness {
#[inline]
fn from(vec: Vec<Vec<u8>>) -> Self { Witness::from_slice(&vec) } fn from(vec: Vec<Vec<u8>>) -> Self { Witness::from_slice(&vec) }
} }
impl From<&[&[u8]]> for Witness { impl From<&[&[u8]]> for Witness {
#[inline]
fn from(slice: &[&[u8]]) -> Self { Witness::from_slice(slice) } fn from(slice: &[&[u8]]) -> Self { Witness::from_slice(slice) }
} }
impl From<&[Vec<u8>]> for Witness { impl From<&[Vec<u8>]> for Witness {
#[inline]
fn from(slice: &[Vec<u8>]) -> Self { Witness::from_slice(slice) } fn from(slice: &[Vec<u8>]) -> Self { Witness::from_slice(slice) }
} }
impl From<Vec<&[u8]>> for Witness { impl From<Vec<&[u8]>> for Witness {
#[inline]
fn from(vec: Vec<&[u8]>) -> Self { Witness::from_slice(&vec) } fn from(vec: Vec<&[u8]>) -> Self { Witness::from_slice(&vec) }
} }
impl Default for Witness { impl Default for Witness {
#[inline]
fn default() -> Self { Self::new() } fn default() -> Self { Self::new() }
} }
@ -450,12 +473,12 @@ mod test {
let mut witness = Witness::default(); let mut witness = Witness::default();
assert!(witness.is_empty()); assert!(witness.is_empty());
assert_eq!(witness.last(), None); 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.get(0), None);
assert_eq!(witness.nth(1), None); assert_eq!(witness.get(1), None);
assert_eq!(witness.nth(2), None); assert_eq!(witness.get(2), None);
assert_eq!(witness.nth(3), None); assert_eq!(witness.get(3), None);
// Push a single byte element onto the witness stack. // Push a single byte element onto the witness stack.
let push = [11_u8]; let push = [11_u8];
@ -473,13 +496,13 @@ mod test {
let element_0 = push.as_slice(); let element_0 = push.as_slice();
assert_eq!(element_0, &witness[0]); 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.last(), Some(element_0));
assert_eq!(witness.nth(0), Some(element_0)); assert_eq!(witness.get(0), Some(element_0));
assert_eq!(witness.nth(1), None); assert_eq!(witness.get(1), None);
assert_eq!(witness.nth(2), None); assert_eq!(witness.get(2), None);
assert_eq!(witness.nth(3), None); assert_eq!(witness.get(3), None);
// Now push 2 byte element onto the witness stack. // Now push 2 byte element onto the witness stack.
let push = [21u8, 22u8]; let push = [21u8, 22u8];
@ -496,12 +519,12 @@ mod test {
let element_1 = push.as_slice(); let element_1 = push.as_slice();
assert_eq!(element_1, &witness[1]); assert_eq!(element_1, &witness[1]);
assert_eq!(witness.nth(0), Some(element_0)); assert_eq!(witness.get(0), Some(element_0));
assert_eq!(witness.nth(1), Some(element_1)); assert_eq!(witness.get(1), Some(element_1));
assert_eq!(witness.nth(2), None); assert_eq!(witness.get(2), None);
assert_eq!(witness.nth(3), 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)); assert_eq!(witness.last(), Some(element_1));
// Now push another 2 byte element onto the witness stack. // Now push another 2 byte element onto the witness stack.
@ -519,13 +542,13 @@ mod test {
let element_2 = push.as_slice(); let element_2 = push.as_slice();
assert_eq!(element_2, &witness[2]); assert_eq!(element_2, &witness[2]);
assert_eq!(witness.nth(0), Some(element_0)); assert_eq!(witness.get(0), Some(element_0));
assert_eq!(witness.nth(1), Some(element_1)); assert_eq!(witness.get(1), Some(element_1));
assert_eq!(witness.nth(2), Some(element_2)); assert_eq!(witness.get(2), Some(element_2));
assert_eq!(witness.nth(3), None); assert_eq!(witness.get(3), None);
assert_eq!(witness.third_to_last(), Some(element_0)); assert_eq!(witness.get_back(2), Some(element_0));
assert_eq!(witness.second_to_last(), Some(element_1)); assert_eq!(witness.get_back(1), Some(element_1));
assert_eq!(witness.last(), Some(element_2)); assert_eq!(witness.last(), Some(element_2));
} }
@ -556,8 +579,8 @@ mod test {
let indices_start = elements.len(); let indices_start = elements.len();
let witness = let witness =
Witness::from_parts__unstable(content.clone(), witness_elements, indices_start); Witness::from_parts__unstable(content.clone(), witness_elements, indices_start);
assert_eq!(witness.nth(0).unwrap(), [11_u8]); assert_eq!(witness.get(0).unwrap(), [11_u8]);
assert_eq!(witness.nth(1).unwrap(), [21_u8, 22]); assert_eq!(witness.get(1).unwrap(), [21_u8, 22]);
assert_eq!(witness.size(), 6); assert_eq!(witness.size(), 6);
} }