Implement Display for block::Header

Not all the fields within `block::Header` implement `Display` however
a block header can reasonably be displayed by using 160 hex characters.

Implement `Display` for `block::Header` by printing the header in hex in
the same layout as we hash it in `block_hash`.
This commit is contained in:
Tobin C. Harding 2025-04-16 17:22:34 +10:00
parent 7d05078b6a
commit e3b059cebf
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
4 changed files with 56 additions and 1 deletions

View File

@ -111,6 +111,7 @@ name = "bitcoin-primitives"
version = "0.101.0" version = "0.101.0"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"arrayvec",
"bincode", "bincode",
"bitcoin-internals", "bitcoin-internals",
"bitcoin-units", "bitcoin-units",

View File

@ -110,6 +110,7 @@ name = "bitcoin-primitives"
version = "0.101.0" version = "0.101.0"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"arrayvec",
"bincode", "bincode",
"bitcoin-internals", "bitcoin-internals",
"bitcoin-units", "bitcoin-units",

View File

@ -16,7 +16,7 @@ exclude = ["tests", "contrib"]
[features] [features]
default = ["std", "hex"] default = ["std", "hex"]
std = ["alloc", "hashes/std", "hex?/std", "internals/std", "units/std"] std = ["alloc", "hashes/std", "hex?/std", "internals/std", "units/std", "arrayvec/std"]
alloc = ["hashes/alloc", "hex?/alloc", "internals/alloc", "units/alloc"] alloc = ["hashes/alloc", "hex?/alloc", "internals/alloc", "units/alloc"]
serde = ["dep:serde", "hashes/serde", "hex?/serde", "internals/serde", "units/serde", "alloc", "hex"] serde = ["dep:serde", "hashes/serde", "hex?/serde", "internals/serde", "units/serde", "alloc", "hex"]
arbitrary = ["dep:arbitrary", "units/arbitrary"] arbitrary = ["dep:arbitrary", "units/arbitrary"]
@ -26,6 +26,7 @@ hex = ["dep:hex", "hashes/hex", "internals/hex"]
hashes = { package = "bitcoin_hashes", path = "../hashes", default-features = false } hashes = { package = "bitcoin_hashes", path = "../hashes", default-features = false }
internals = { package = "bitcoin-internals", path = "../internals" } internals = { package = "bitcoin-internals", path = "../internals" }
units = { package = "bitcoin-units", path = "../units", default-features = false } units = { package = "bitcoin-units", path = "../units", default-features = false }
arrayvec = { version = "0.7", default-features = false }
arbitrary = { version = "1.4", optional = true } arbitrary = { version = "1.4", optional = true }
hex = { package = "hex-conservative", version = "0.3.0", default-features = false, optional = true } hex = { package = "hex-conservative", version = "0.3.0", default-features = false, optional = true }

View File

@ -207,6 +207,27 @@ impl Header {
} }
} }
#[cfg(feature = "hex")]
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use fmt::Write as _;
use hex::DisplayHex as _;
let mut buf = arrayvec::ArrayString::<160>::new();
write!(
&mut buf,
"{}{}{}{}{}{}",
self.version.to_consensus().to_le_bytes().as_hex(),
self.prev_blockhash.as_byte_array().as_hex(),
self.merkle_root.as_byte_array().as_hex(),
self.time.to_u32().to_le_bytes().as_hex(),
self.bits.to_consensus().to_le_bytes().as_hex(),
self.nonce.to_le_bytes().as_hex(),
)?;
fmt::Display::fmt(&buf, f)
}
}
impl fmt::Debug for Header { impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Header") f.debug_struct("Header")
@ -535,4 +556,35 @@ mod tests {
); );
assert_eq!(format!("{:?}", header), expected); assert_eq!(format!("{:?}", header), expected);
} }
#[test]
#[cfg(feature = "hex")]
fn header_display() {
let seconds: u32 = 1_653_195_600; // Arbitrary timestamp: May 22nd, 5am UTC.
let header = Header {
version: Version::TWO,
prev_blockhash: BlockHash::from_byte_array([0xab; 32]),
merkle_root: TxMerkleNode::from_byte_array([0xcd; 32]),
time: BlockTime::from(seconds),
bits: CompactTarget::from_consensus(0xbeef),
nonce: 0xcafe,
};
let want = concat!(
"02000000", // version
"abababababababababababababababababababababababababababababababab", // prev_blockhash
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", // merkle_root
"50c38962", // time
"efbe0000", // bits
"feca0000", // nonce
);
assert_eq!(want.len(), 160);
assert_eq!(format!("{}", header), want);
// Check how formatting options are handled.
let want = format!("{:.20}", want);
let got = format!("{:.20}", header);
assert_eq!(got, want);
}
} }