primitives: Add hash type name to Debug output
What changed, and why it matters
This commit only changes how hash values look when printed for debugging. It adds the type name (like 'Txid(...)') around the hexadecimal hash string so developers can more easily tell which kind of hash they are looking at. It does not change how hashes are compared, stored, serialized, or used in Bitcoin logic.
No security action needed. This is a cosmetic developer-experience change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch replaces a generic Debug implementation for primitive hash newtypes with a macro-generated one that wraps the inner hash in a debug_tuple named after the newtype (e.g., Txid(…), BlockHash(…), WScriptHash(…)). The inner hash’s own Debug/Display formatting remains unchanged. No cryptographic, consensus, serialization, parsing, or equality behavior is modified. Tests and expected debug strings are updated accordingly.
Changed components
primitives/src/hash_types/*bitcoin/src/blockdata/transaction.rs (debug test expectations)Inspect captured patch +38 / −16
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index ba751ae7..70af986e 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -2105,16 +2105,16 @@ mod tests {
fn outpoint_format() {
let outpoint = OutPoint::COINBASE_PREVOUT;
- let debug = "OutPoint { txid: 0000000000000000000000000000000000000000000000000000000000000000, vout: 4294967295 }";
+ let debug = "OutPoint { txid: Txid(0000000000000000000000000000000000000000000000000000000000000000), vout: 4294967295 }";
assert_eq!(debug, format!("{:?}", &outpoint));
let display = "0000000000000000000000000000000000000000000000000000000000000000:4294967295";
assert_eq!(display, format!("{}", &outpoint));
- let pretty_debug = "OutPoint {\n txid: 0x0000000000000000000000000000000000000000000000000000000000000000,\n vout: 4294967295,\n}";
+ let pretty_debug = "OutPoint {\n txid: Txid(\n 0x0000000000000000000000000000000000000000000000000000000000000000,\n ),\n vout: 4294967295,\n}";
assert_eq!(pretty_debug, format!("{:#?}", &outpoint));
- let debug_txid = "0000000000000000000000000000000000000000000000000000000000000000";
+ let debug_txid = "Txid(0000000000000000000000000000000000000000000000000000000000000000)";
assert_eq!(debug_txid, format!("{:?}", &outpoint.txid));
let display_txid = "0000000000000000000000000000000000000000000000000000000000000000";
diff --git a/primitives/src/hash_types/block_hash.rs b/primitives/src/hash_types/block_hash.rs
index 99c666d8..d31f5ba3 100644
--- a/primitives/src/hash_types/block_hash.rs
+++ b/primitives/src/hash_types/block_hash.rs
@@ -17,6 +17,8 @@ use internals::write_err;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockHash(sha256d::Hash);
+super::impl_debug!(BlockHash);
+
impl BlockHash {
/// Dummy hash used as the previous blockhash of the genesis block.
pub const GENESIS_PREVIOUS_BLOCK_HASH: Self = Self::from_byte_array([0; 32]);
diff --git a/primitives/src/hash_types/generic.rs b/primitives/src/hash_types/generic.rs
index 70ffd9d5..01a92a0e 100644
--- a/primitives/src/hash_types/generic.rs
+++ b/primitives/src/hash_types/generic.rs
@@ -57,10 +57,6 @@ impl str::FromStr for HashType {
}
}
-impl fmt::Debug for HashType {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }
-}
-
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for HashType {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
diff --git a/primitives/src/hash_types/mod.rs b/primitives/src/hash_types/mod.rs
index 7bf3bc2d..8fb183e9 100644
--- a/primitives/src/hash_types/mod.rs
+++ b/primitives/src/hash_types/mod.rs
@@ -110,6 +110,18 @@ macro_rules! impl_serde(
#[cfg(feature = "serde")]
pub(in crate::hash_types) use impl_serde;
+macro_rules! impl_debug {
+ ($ty:ident) => {
+ impl core::fmt::Debug for HashType {
+ #[inline]
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ f.debug_tuple(stringify!($ty)).field(&self.0).finish()
+ }
+ }
+ };
+}
+pub(in crate::hash_types) use impl_debug;
+
/// Functions used by serde impls of all hashes.
#[cfg(feature = "serde")]
pub mod serde_details {
@@ -202,7 +214,7 @@ mod tests {
let mut a = [0xab; 32];
a[0] = 0xff; // Just so we can see which way the array is printing.
let tc = Txid::from_byte_array(a);
- let want = "abababababababababababababababababababababababababababababababff";
+ let want = "Txid(abababababababababababababababababababababababababababababababff)";
(tc, want)
}
diff --git a/primitives/src/hash_types/ntxid.rs b/primitives/src/hash_types/ntxid.rs
index 1e81106a..25ef936a 100644
--- a/primitives/src/hash_types/ntxid.rs
+++ b/primitives/src/hash_types/ntxid.rs
@@ -2,9 +2,8 @@
//! The `Txid` type.
-use core::fmt;
#[cfg(feature = "hex")]
-use core::str;
+use core::{fmt, str};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
@@ -24,6 +23,8 @@ use hashes::sha256d;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Ntxid(sha256d::Hash);
+super::impl_debug!(Ntxid);
+
// The new hash wrapper type.
type HashType = Ntxid;
// The inner hash type from `hashes`.
diff --git a/primitives/src/hash_types/script_hash.rs b/primitives/src/hash_types/script_hash.rs
index a4186a44..c80c4211 100644
--- a/primitives/src/hash_types/script_hash.rs
+++ b/primitives/src/hash_types/script_hash.rs
@@ -21,6 +21,8 @@ use crate::script::{Script, ScriptHashableTag, MAX_REDEEM_SCRIPT_SIZE};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ScriptHash(hash160::Hash);
+super::impl_debug!(ScriptHash);
+
impl ScriptHash {
/// Constructs a new `ScriptHash` after first checking the script size.
///
diff --git a/primitives/src/hash_types/transaction_merkle_node.rs b/primitives/src/hash_types/transaction_merkle_node.rs
index ce115fd9..8ebab1dd 100644
--- a/primitives/src/hash_types/transaction_merkle_node.rs
+++ b/primitives/src/hash_types/transaction_merkle_node.rs
@@ -19,6 +19,8 @@ use crate::Txid;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TxMerkleNode(sha256d::Hash);
+super::impl_debug!(TxMerkleNode);
+
// The new hash wrapper type.
type HashType = TxMerkleNode;
// The inner hash type from `hashes`.
diff --git a/primitives/src/hash_types/txid.rs b/primitives/src/hash_types/txid.rs
index 880684c1..4851357f 100644
--- a/primitives/src/hash_types/txid.rs
+++ b/primitives/src/hash_types/txid.rs
@@ -4,9 +4,8 @@
//!
//! In order to print and parse txids enable the "hex" feature.
-use core::fmt;
#[cfg(feature = "hex")]
-use core::str;
+use core::{fmt, str};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
@@ -24,6 +23,8 @@ use crate::OutPoint;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Txid(sha256d::Hash);
+super::impl_debug!(Txid);
+
impl Txid {
/// The `Txid` used in a coinbase prevout.
///
diff --git a/primitives/src/hash_types/witness_commitment.rs b/primitives/src/hash_types/witness_commitment.rs
index e6697191..91a1c9fc 100644
--- a/primitives/src/hash_types/witness_commitment.rs
+++ b/primitives/src/hash_types/witness_commitment.rs
@@ -2,9 +2,8 @@
//! The `WitnessCommitment` type.
-use core::fmt;
#[cfg(feature = "hex")]
-use core::str;
+use core::{fmt, str};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
@@ -14,6 +13,8 @@ use hashes::sha256d;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WitnessCommitment(sha256d::Hash);
+super::impl_debug!(WitnessCommitment);
+
impl WitnessCommitment {
/// Dummy hash used as the previous blockhash of the genesis block.
pub const GENESIS_PREVIOUS_BLOCK_HASH: Self = Self::from_byte_array([0; 32]);
diff --git a/primitives/src/hash_types/witness_merkle_node.rs b/primitives/src/hash_types/witness_merkle_node.rs
index 111ff525..396d2bae 100644
--- a/primitives/src/hash_types/witness_merkle_node.rs
+++ b/primitives/src/hash_types/witness_merkle_node.rs
@@ -19,6 +19,8 @@ use crate::Wtxid;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WitnessMerkleNode(sha256d::Hash);
+super::impl_debug!(WitnessMerkleNode);
+
// The new hash wrapper type.
type HashType = WitnessMerkleNode;
// The inner hash type from `hashes`.
diff --git a/primitives/src/hash_types/witness_script_hash.rs b/primitives/src/hash_types/witness_script_hash.rs
index 3ac39fe9..59a5aee7 100644
--- a/primitives/src/hash_types/witness_script_hash.rs
+++ b/primitives/src/hash_types/witness_script_hash.rs
@@ -21,6 +21,8 @@ use crate::script::{WitnessScript, MAX_WITNESS_SCRIPT_SIZE};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WScriptHash(sha256::Hash);
+super::impl_debug!(WScriptHash);
+
impl WScriptHash {
/// Constructs a new `WScriptHash` after first checking the script size.
///
diff --git a/primitives/src/hash_types/wtxid.rs b/primitives/src/hash_types/wtxid.rs
index cdb27517..5ce787d7 100644
--- a/primitives/src/hash_types/wtxid.rs
+++ b/primitives/src/hash_types/wtxid.rs
@@ -4,9 +4,8 @@
//!
//! In order to print and parse txids enable the "hex" feature.
-use core::fmt;
#[cfg(feature = "hex")]
-use core::str;
+use core::{fmt, str};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
@@ -16,6 +15,8 @@ use hashes::sha256d;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Wtxid(sha256d::Hash);
+super::impl_debug!(Wtxid);
+
impl Wtxid {
/// The `Wtxid` of a coinbase transaction.
///
Why this scored 15/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.