Skip to main content

blockchain_zc_parser/
hash.rs

1//! Hash types used throughout the blockchain data structures.
2
3use core::fmt;
4
5/// A 32-byte hash (SHA-256d / double-SHA-256) stored as a zero-copy reference.
6///
7/// The inner reference borrows from the original parse buffer, so no heap
8/// allocation is required.
9#[derive(Clone, Copy, PartialEq, Eq)]
10pub struct Hash32<'a>(pub &'a [u8; 32]);
11
12impl<'a> Hash32<'a> {
13    /// View the hash as a raw byte slice.
14    #[inline]
15    pub fn as_bytes(&self) -> &[u8] {
16        self.0.as_slice()
17    }
18
19    /// Return the inner fixed-size reference.
20    #[inline]
21    pub fn as_array(&self) -> &[u8; 32] {
22        self.0
23    }
24
25    /// Verify that this hash matches an owned array.
26    #[inline]
27    pub fn eq_array(&self, other: &[u8; 32]) -> bool {
28        self.0 == other
29    }
30}
31
32impl fmt::Debug for Hash32<'_> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "Hash32(")?;
35        // Bitcoin convention: display bytes in reverse (little-endian txid)
36        for b in self.0.iter().rev() {
37            write!(f, "{b:02x}")?;
38        }
39        write!(f, ")")
40    }
41}
42
43impl fmt::Display for Hash32<'_> {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        for b in self.0.iter().rev() {
46            write!(f, "{b:02x}")?;
47        }
48        Ok(())
49    }
50}
51
52/// A 20-byte hash (RIPEMD-160 of SHA-256, used in P2PKH / P2SH addresses).
53#[derive(Clone, Copy, PartialEq, Eq)]
54pub struct Hash20<'a>(pub &'a [u8; 20]);
55
56impl fmt::Debug for Hash20<'_> {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        write!(f, "Hash20(")?;
59        for b in self.0.iter() {
60            write!(f, "{b:02x}")?;
61        }
62        write!(f, ")")
63    }
64}
65
66/// Compute double-SHA-256 of `data` and return a newly allocated `[u8; 32]`.
67///
68/// This is the only place where allocation (stack frame) is required; the
69/// result is typically compared against a zero-copy reference hash and then
70/// discarded.
71#[cfg(feature = "std")]
72pub fn double_sha256(data: &[u8]) -> [u8; 32] {
73    use sha2::{Digest, Sha256};
74    let first = Sha256::digest(data);
75    Sha256::digest(first).into()
76}