primitives: Add consensus encoding traits for WitnessMerkleNode
What changed, and why it matters
This commit adds standard Bitcoin consensus encoding and decoding support for WitnessMerkleNode, a data type used when computing witness commitments in SegWit blocks. It mirrors the existing support already present for TxMerkleNode. There is no indication this fixes a vulnerability; it appears to be a routine API completeness change.
No security action required. Review as normal API maintenance if auditing consensus-encoding coverage.
Security signals we found
No security-relevant keywords in commit title or message
No bug-fix or vulnerability disclosure language present
Change is additive API parity (mirrors existing TxMerkleNode encoding)
No changes to cryptographic operations, validation logic, or resource limits
Evidence from the diff
The patch implements bitcoin_consensus_encoding::Encodable and Decodable for WitnessMerkleNode in primitives/src/hash_types/witness_merkle_node.rs, adding WitnessMerkleNodeEncoder, WitnessMerkleNodeDecoder, and WitnessMerkleNodeDecoderError. API snapshot files are updated to reflect the new trait implementations and public types. The commit message states this is needed so compute_witness_commitment can consensus-encode WitnessMerkleNode hashes into a HashEngine, matching the existing TxMerkleNode implementation.
Changed components
primitives/src/hash_types/witness_merkle_node.rsapi/primitives/all-features.txtapi/primitives/alloc-only.txtapi/primitives/no-features.txtInspect captured patch +91 / −1
diff --git a/api/primitives/all-features.txt b/api/primitives/all-features.txt
index 0ffdd551..d56a98e5 100644
--- a/api/primitives/all-features.txt
+++ b/api/primitives/all-features.txt
@@ -3,6 +3,7 @@
#[repr(transparent)] pub struct bitcoin_primitives::script::Script<T>(_, _)
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::BlockHash
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::TxMerkleNode
+impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::WitnessMerkleNode
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Block
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Header
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Version
@@ -27,6 +28,7 @@ impl bitcoin_consensus_encoding::decode::Decoder for bitcoin_primitives::transac
impl bitcoin_consensus_encoding::decode::Decoder for bitcoin_primitives::witness::WitnessDecoder
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::BlockHash
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::TxMerkleNode
+impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::WitnessMerkleNode
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Block
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Header
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Version
@@ -1383,7 +1385,9 @@ pub fn bitcoin_primitives::WitnessMerkleNode::calculate_root<I: core::iter::trai
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
pub fn bitcoin_primitives::WitnessMerkleNode::combine(&self, other: &Self) -> Self
+pub fn bitcoin_primitives::WitnessMerkleNode::decoder() -> Self::Decoder
pub fn bitcoin_primitives::WitnessMerkleNode::deserialize<D: serde::de::Deserializer<'de>>(d: D) -> core::result::Result<bitcoin_primitives::WitnessMerkleNode, <D as serde::de::Deserializer>::Error>
+pub fn bitcoin_primitives::WitnessMerkleNode::encoder(&self) -> Self::Encoder
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
pub fn bitcoin_primitives::WitnessMerkleNode::from_leaf(leaf: bitcoin_primitives::Wtxid) -> Self
@@ -1974,6 +1978,8 @@ pub type bitcoin_primitives::TxMerkleNode::Encoder<'e> = bitcoin_primitives::mer
pub type bitcoin_primitives::TxMerkleNode::Err = hex_conservative::error::DecodeFixedLengthBytesError
pub type bitcoin_primitives::Txid::Err = hex_conservative::error::DecodeFixedLengthBytesError
pub type bitcoin_primitives::WitnessCommitment::Err = hex_conservative::error::DecodeFixedLengthBytesError
+pub type bitcoin_primitives::WitnessMerkleNode::Decoder = bitcoin_primitives::hash_types::witness_merkle_node::WitnessMerkleNodeDecoder
+pub type bitcoin_primitives::WitnessMerkleNode::Encoder<'e> = bitcoin_primitives::hash_types::witness_merkle_node::WitnessMerkleNodeEncoder
pub type bitcoin_primitives::WitnessMerkleNode::Err = hex_conservative::error::DecodeFixedLengthBytesError
pub type bitcoin_primitives::WitnessScript = bitcoin_primitives::script::Script<bitcoin_primitives::script::WitnessScriptTag>
pub type bitcoin_primitives::WitnessScriptBuf = bitcoin_primitives::script::ScriptBuf<bitcoin_primitives::script::WitnessScriptTag>
diff --git a/api/primitives/alloc-only.txt b/api/primitives/alloc-only.txt
index dcddfe10..caa641ad 100644
--- a/api/primitives/alloc-only.txt
+++ b/api/primitives/alloc-only.txt
@@ -2,6 +2,7 @@
#[repr(transparent)] pub struct bitcoin_primitives::script::Script<T>(_, _)
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::BlockHash
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::TxMerkleNode
+impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::WitnessMerkleNode
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Block
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Header
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Version
@@ -26,6 +27,7 @@ impl bitcoin_consensus_encoding::decode::Decoder for bitcoin_primitives::transac
impl bitcoin_consensus_encoding::decode::Decoder for bitcoin_primitives::witness::WitnessDecoder
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::BlockHash
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::TxMerkleNode
+impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::WitnessMerkleNode
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Block
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Header
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Version
@@ -1228,6 +1230,8 @@ pub fn bitcoin_primitives::WitnessMerkleNode::calculate_root<I: core::iter::trai
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
pub fn bitcoin_primitives::WitnessMerkleNode::combine(&self, other: &Self) -> Self
+pub fn bitcoin_primitives::WitnessMerkleNode::decoder() -> Self::Decoder
+pub fn bitcoin_primitives::WitnessMerkleNode::encoder(&self) -> Self::Encoder
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
pub fn bitcoin_primitives::WitnessMerkleNode::from_leaf(leaf: bitcoin_primitives::Wtxid) -> Self
@@ -1754,6 +1758,8 @@ pub type bitcoin_primitives::TapScript = bitcoin_primitives::script::Script<bitc
pub type bitcoin_primitives::TapScriptBuf = bitcoin_primitives::script::ScriptBuf<bitcoin_primitives::script::TapScriptTag>
pub type bitcoin_primitives::TxMerkleNode::Decoder = bitcoin_primitives::transaction::TxMerkleNodeDecoder
pub type bitcoin_primitives::TxMerkleNode::Encoder<'e> = bitcoin_primitives::merkle_tree::TxMerkleNodeEncoder
+pub type bitcoin_primitives::WitnessMerkleNode::Decoder = bitcoin_primitives::hash_types::witness_merkle_node::WitnessMerkleNodeDecoder
+pub type bitcoin_primitives::WitnessMerkleNode::Encoder<'e> = bitcoin_primitives::hash_types::witness_merkle_node::WitnessMerkleNodeEncoder
pub type bitcoin_primitives::WitnessScript = bitcoin_primitives::script::Script<bitcoin_primitives::script::WitnessScriptTag>
pub type bitcoin_primitives::WitnessScriptBuf = bitcoin_primitives::script::ScriptBuf<bitcoin_primitives::script::WitnessScriptTag>
pub type bitcoin_primitives::block::Block::Decoder = bitcoin_primitives::block::BlockDecoder
diff --git a/api/primitives/no-features.txt b/api/primitives/no-features.txt
index bdbecb94..d710f241 100644
--- a/api/primitives/no-features.txt
+++ b/api/primitives/no-features.txt
@@ -1,5 +1,6 @@
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::BlockHash
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::TxMerkleNode
+impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::WitnessMerkleNode
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::block::Version
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::pow::CompactTarget
impl bitcoin_consensus_encoding::decode::Decodable for bitcoin_primitives::transaction::OutPoint
@@ -12,6 +13,7 @@ impl bitcoin_consensus_encoding::decode::Decoder for bitcoin_primitives::transac
impl bitcoin_consensus_encoding::decode::Decoder for bitcoin_primitives::transaction::VersionDecoder
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::BlockHash
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::TxMerkleNode
+impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::WitnessMerkleNode
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Header
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::block::Version
impl bitcoin_consensus_encoding::encode::Encodable for bitcoin_primitives::pow::CompactTarget
@@ -546,6 +548,8 @@ pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8; 32]
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8]
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
+pub fn bitcoin_primitives::WitnessMerkleNode::decoder() -> Self::Decoder
+pub fn bitcoin_primitives::WitnessMerkleNode::encoder(&self) -> Self::Encoder
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
pub fn bitcoin_primitives::WitnessMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
@@ -723,6 +727,8 @@ pub type bitcoin_primitives::BlockHash::Decoder = bitcoin_primitives::block::Blo
pub type bitcoin_primitives::BlockHash::Encoder<'e> = bitcoin_primitives::block::BlockHashEncoder
pub type bitcoin_primitives::TxMerkleNode::Decoder = bitcoin_primitives::transaction::TxMerkleNodeDecoder
pub type bitcoin_primitives::TxMerkleNode::Encoder<'e> = bitcoin_primitives::merkle_tree::TxMerkleNodeEncoder
+pub type bitcoin_primitives::WitnessMerkleNode::Decoder = bitcoin_primitives::hash_types::witness_merkle_node::WitnessMerkleNodeDecoder
+pub type bitcoin_primitives::WitnessMerkleNode::Encoder<'e> = bitcoin_primitives::hash_types::witness_merkle_node::WitnessMerkleNodeEncoder
pub type bitcoin_primitives::block::BlockHashDecoder::Error = bitcoin_primitives::block::BlockHashDecoderError
pub type bitcoin_primitives::block::BlockHashDecoder::Output = bitcoin_primitives::BlockHash
pub type bitcoin_primitives::block::Header::Encoder<'e> = bitcoin_primitives::block::HeaderEncoder
diff --git a/primitives/src/hash_types/witness_merkle_node.rs b/primitives/src/hash_types/witness_merkle_node.rs
index d10c579d..1eb27b43 100644
--- a/primitives/src/hash_types/witness_merkle_node.rs
+++ b/primitives/src/hash_types/witness_merkle_node.rs
@@ -2,7 +2,7 @@
//! The `WitnessMerkleNode` type.
-#[cfg(not(feature = "hex"))]
+use core::convert::Infallible;
use core::fmt;
#[cfg(feature = "hex")]
use core::str;
@@ -10,6 +10,7 @@ use core::str;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use hashes::sha256d;
+use internals::write_err;
#[cfg(feature = "alloc")]
use crate::merkle_tree::MerkleNode;
@@ -47,3 +48,74 @@ impl WitnessMerkleNode {
/// you should not unwrap the `Option` returned by this method!
pub fn calculate_root<I: Iterator<Item = Wtxid>>(iter: I) -> Option<Self> { MerkleNode::calculate_root(iter) }
}
+
+encoding::encoder_newtype! {
+ /// The encoder for the [`WitnessMerkleNode`] type.
+ pub struct WitnessMerkleNodeEncoder(encoding::ArrayEncoder<32>);
+}
+
+impl encoding::Encodable for WitnessMerkleNode {
+ type Encoder<'e> = WitnessMerkleNodeEncoder;
+ fn encoder(&self) -> Self::Encoder<'_> {
+ WitnessMerkleNodeEncoder(encoding::ArrayEncoder::without_length_prefix(self.to_byte_array()))
+ }
+}
+
+/// The decoder for the [`WitnessMerkleNode`] type.
+pub struct WitnessMerkleNodeDecoder(encoding::ArrayDecoder<32>);
+
+impl WitnessMerkleNodeDecoder {
+ /// Constructs a new [`WitnessMerkleNode`] decoder.
+ pub fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
+}
+
+impl Default for WitnessMerkleNodeDecoder {
+ fn default() -> Self { Self::new() }
+}
+
+impl encoding::Decoder for WitnessMerkleNodeDecoder {
+ type Output = WitnessMerkleNode;
+ type Error = WitnessMerkleNodeDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ Ok(self.0.push_bytes(bytes)?)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let a = self.0.end()?;
+ Ok(WitnessMerkleNode::from_byte_array(a))
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+impl encoding::Decodable for WitnessMerkleNode {
+ type Decoder = WitnessMerkleNodeDecoder;
+ fn decoder() -> Self::Decoder { WitnessMerkleNodeDecoder(encoding::ArrayDecoder::<32>::new()) }
+}
+
+/// An error consensus decoding an `WitnessMerkleNode`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WitnessMerkleNodeDecoderError(encoding::UnexpectedEofError);
+
+impl From<Infallible> for WitnessMerkleNodeDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl From<encoding::UnexpectedEofError> for WitnessMerkleNodeDecoderError {
+ fn from(e: encoding::UnexpectedEofError) -> Self { Self(e) }
+}
+
+impl fmt::Display for WitnessMerkleNodeDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write_err!(f, "sequence decoder error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for WitnessMerkleNodeDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.