primitives: Add derives to encoders and decoders
What changed, and why it matters
This commit adds standard Rust traits (`Clone` and `Debug`) to a collection of data-encoding helper structs in the rust-bitcoin library. These traits let developers duplicate encoder/decoder objects and print them for debugging. There is no security vulnerability here; it is a routine API-quality improvement.
No security action required. Treat as a normal API ergonomics improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch derives Debug and Clone on encoder and decoder newtypes/wrappers in primitives/src/{block,transaction,witness,script/*,hash_types/*}.rs. The commit message explicitly states these derives were accidentally omitted and were already present in the sibling consensus_encoding module. No logic, bounds checks, cryptographic operations, or serialization behavior is changed.
Changed components
primitives/src/block.rsprimitives/src/hash_types/block_hash.rsprimitives/src/hash_types/transaction_merkle_node.rsprimitives/src/hash_types/witness_merkle_node.rsprimitives/src/script/borrowed.rsprimitives/src/script/owned.rsprimitives/src/transaction.rsprimitives/src/witness.rsInspect captured patch +28 / −0
diff --git a/primitives/src/block.rs b/primitives/src/block.rs
index 2ce10c17..c08d2f0d 100644
--- a/primitives/src/block.rs
+++ b/primitives/src/block.rs
@@ -336,6 +336,7 @@ impl std::error::Error for ParseBlockError {
#[cfg(feature = "alloc")]
encoding::encoder_newtype! {
/// The encoder for the [`Block`] type.
+ #[derive(Debug, Clone)]
pub struct BlockEncoder<'e>(
Encoder2<HeaderEncoder<'e>, Encoder2<CompactSizeEncoder, SliceEncoder<'e, Transaction>>>
);
@@ -369,6 +370,7 @@ type BlockInnerDecoder = Decoder2<HeaderDecoder, VecDecoder<Transaction>>;
///
/// This decoder can only produce a `Block<Unchecked>`.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
pub struct BlockDecoder(BlockInnerDecoder);
#[cfg(feature = "alloc")]
@@ -638,6 +640,7 @@ impl std::error::Error for ParseHeaderError {
encoding::encoder_newtype_exact! {
/// The encoder for the [`Header`] type.
+ #[derive(Debug, Clone)]
pub struct HeaderEncoder<'e>(
encoding::Encoder6<
VersionEncoder<'e>,
@@ -675,6 +678,7 @@ type HeaderInnerDecoder = Decoder6<
>;
/// The decoder for the [`Header`] type.
+#[derive(Debug, Clone)]
pub struct HeaderDecoder(HeaderInnerDecoder);
impl HeaderDecoder {
@@ -899,6 +903,7 @@ impl Default for Version {
encoding::encoder_newtype_exact! {
/// The encoder for the [`Version`] type.
+ #[derive(Debug, Clone)]
pub struct VersionEncoder<'e>(encoding::ArrayEncoder<4>);
}
@@ -912,6 +917,7 @@ impl encoding::Encodable for Version {
}
/// The decoder for the [`Version`] type.
+#[derive(Debug, Clone)]
pub struct VersionDecoder(encoding::ArrayDecoder<4>);
impl VersionDecoder {
diff --git a/primitives/src/hash_types/block_hash.rs b/primitives/src/hash_types/block_hash.rs
index 65684396..5cb0a4dc 100644
--- a/primitives/src/hash_types/block_hash.rs
+++ b/primitives/src/hash_types/block_hash.rs
@@ -48,10 +48,12 @@ impl encoding::Decodable for BlockHash {
encoding::encoder_newtype_exact! {
/// The encoder for the [`BlockHash`] type.
+ #[derive(Debug, Clone)]
pub struct BlockHashEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>);
}
/// The decoder for the [`BlockHash`] type.
+#[derive(Debug, Clone)]
pub struct BlockHashDecoder(encoding::ArrayDecoder<32>);
impl BlockHashDecoder {
diff --git a/primitives/src/hash_types/transaction_merkle_node.rs b/primitives/src/hash_types/transaction_merkle_node.rs
index c8746b58..8a1e3df1 100644
--- a/primitives/src/hash_types/transaction_merkle_node.rs
+++ b/primitives/src/hash_types/transaction_merkle_node.rs
@@ -68,10 +68,12 @@ impl encoding::Decodable for TxMerkleNode {
encoding::encoder_newtype_exact! {
/// The encoder for the [`TxMerkleNode`] type.
+ #[derive(Debug, Clone)]
pub struct TxMerkleNodeEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>);
}
/// The decoder for the [`TxMerkleNode`] type.
+#[derive(Debug, Clone)]
pub struct TxMerkleNodeDecoder(encoding::ArrayDecoder<32>);
impl TxMerkleNodeDecoder {
diff --git a/primitives/src/hash_types/witness_merkle_node.rs b/primitives/src/hash_types/witness_merkle_node.rs
index 05f8ced6..be1d3d05 100644
--- a/primitives/src/hash_types/witness_merkle_node.rs
+++ b/primitives/src/hash_types/witness_merkle_node.rs
@@ -68,10 +68,12 @@ impl encoding::Decodable for WitnessMerkleNode {
encoding::encoder_newtype_exact! {
/// The encoder for the [`WitnessMerkleNode`] type.
+ #[derive(Debug, Clone)]
pub struct WitnessMerkleNodeEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>);
}
/// The decoder for the [`WitnessMerkleNode`] type.
+#[derive(Debug, Clone)]
pub struct WitnessMerkleNodeDecoder(encoding::ArrayDecoder<32>);
impl WitnessMerkleNodeDecoder {
diff --git a/primitives/src/script/borrowed.rs b/primitives/src/script/borrowed.rs
index 4e44861b..80ac2841 100644
--- a/primitives/src/script/borrowed.rs
+++ b/primitives/src/script/borrowed.rs
@@ -182,6 +182,7 @@ impl<T> Script<T> {
encoding::encoder_newtype_exact! {
/// The encoder for the [`Script<T>`] type.
+ #[derive(Debug, Clone)]
pub struct ScriptEncoder<'e>(Encoder2<CompactSizeEncoder, BytesEncoder<'e>>);
}
diff --git a/primitives/src/script/owned.rs b/primitives/src/script/owned.rs
index b5d043ee..71ed7216 100644
--- a/primitives/src/script/owned.rs
+++ b/primitives/src/script/owned.rs
@@ -184,6 +184,7 @@ impl<T> DerefMut for ScriptBuf<T> {
}
/// The decoder for the [`ScriptBuf`] type.
+#[derive(Debug, Clone)]
pub struct ScriptBufDecoder<T>(ByteVecDecoder, PhantomData<T>);
impl<T> ScriptBufDecoder<T> {
diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs
index ff8087d6..ddc3524b 100644
--- a/primitives/src/transaction.rs
+++ b/primitives/src/transaction.rs
@@ -337,6 +337,7 @@ type TransactionEncoderInner<'e> = Encoder6<
#[cfg(feature = "alloc")]
encoding::encoder_newtype! {
/// The encoder for the [`Transaction`] type.
+ #[derive(Debug, Clone)]
pub struct TransactionEncoder<'e>(TransactionEncoderInner<'e>);
}
@@ -430,6 +431,7 @@ impl std::error::Error for ParseTransactionError {
/// The decoder for the [`Transaction`] type.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
pub struct TransactionDecoder {
state: TransactionDecoderState,
}
@@ -665,6 +667,7 @@ impl encoding::Decodable for Transaction {
/// The state of the transiting decoder.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
enum TransactionDecoderState {
/// Decoding the transaction version.
Version(VersionDecoder),
@@ -858,6 +861,7 @@ impl TxIn {
#[cfg(feature = "alloc")]
encoding::encoder_newtype_exact! {
/// The encoder for the [`TxIn`] type.
+ #[derive(Debug, Clone)]
pub struct TxInEncoder<'e>(
Encoder3<OutPointEncoder<'e>, ScriptEncoder<'e>, SequenceEncoder<'e>>
);
@@ -881,6 +885,7 @@ impl encoding::Encodable for TxIn {
/// Encodes the witnesses from a list of inputs.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
pub struct WitnessesEncoder<'e> {
inputs: &'e [TxIn],
/// Encoder for the current witness being encoded.
@@ -936,6 +941,7 @@ type TxInInnerDecoder = Decoder3<OutPointDecoder, ScriptSigBufDecoder, SequenceD
/// The decoder for the [`TxIn`] type.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
pub struct TxInDecoder(TxInInnerDecoder);
#[cfg(feature = "alloc")]
@@ -1033,6 +1039,7 @@ pub struct TxOut {
#[cfg(feature = "alloc")]
encoding::encoder_newtype_exact! {
/// The encoder for the [`TxOut`] type.
+ #[derive(Debug, Clone)]
pub struct TxOutEncoder<'e>(Encoder2<AmountEncoder<'e>, ScriptEncoder<'e>>);
}
@@ -1053,6 +1060,7 @@ type TxOutInnerDecoder = Decoder2<AmountDecoder, ScriptPubKeyBufDecoder>;
/// The decoder for the [`TxOut`] type.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
pub struct TxOutDecoder(TxOutInnerDecoder);
#[cfg(feature = "alloc")]
@@ -1144,6 +1152,7 @@ impl OutPoint {
encoding::encoder_newtype_exact! {
/// The encoder for the [`OutPoint`] type.
+ #[derive(Debug, Clone)]
pub struct OutPointEncoder<'e>(Encoder2<BytesEncoder<'e>, ArrayEncoder<4>>);
}
@@ -1211,6 +1220,7 @@ fn parse_vout(s: &str) -> Result<u32, ParseOutPointError> {
/// The decoder for the [`OutPoint`] type.
// 32 for the txid + 4 for the vout
+#[derive(Debug, Clone)]
pub struct OutPointDecoder(encoding::ArrayDecoder<36>);
impl OutPointDecoder {
@@ -1513,6 +1523,7 @@ impl From<Version> for u32 {
encoding::encoder_newtype_exact! {
/// The encoder for the [`Version`] type.
+ #[derive(Debug, Clone)]
pub struct VersionEncoder<'e>(encoding::ArrayEncoder<4>);
}
@@ -1526,6 +1537,7 @@ impl encoding::Encodable for Version {
}
/// The decoder for the [`Version`] type.
+#[derive(Debug, Clone)]
pub struct VersionDecoder(encoding::ArrayDecoder<4>);
impl VersionDecoder {
diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs
index bcb200a7..9b846cd7 100644
--- a/primitives/src/witness.rs
+++ b/primitives/src/witness.rs
@@ -259,6 +259,7 @@ fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<
}
/// The encoder for the [`Witness`] type.
+#[derive(Debug, Clone)]
pub struct WitnessEncoder<'e>(Encoder2<CompactSizeEncoder, BytesEncoder<'e>>);
impl encoding::Encodable for Witness {
@@ -286,6 +287,7 @@ impl encoding::Encoder for WitnessEncoder<'_> {
/// The decoder for the [`Witness`] type.
#[cfg(feature = "alloc")]
+#[derive(Debug, Clone)]
pub struct WitnessDecoder {
/// The single buffer that will become the Witness content.
/// The index entries are written at the beginning, then rotated in [`Self::end`].
Why this scored 19/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.