Skip to main content

lichen_core/
hash.rs

1// Lichen Core - Cryptographic Hash
2
3use serde::{Deserialize, Serialize};
4use sha2::{Digest, Sha256};
5use std::fmt;
6
7/// 32-byte hash (SHA-256)
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
9pub struct Hash(pub [u8; 32]);
10
11impl Hash {
12    /// Create hash from bytes
13    pub const fn new(bytes: [u8; 32]) -> Self {
14        Hash(bytes)
15    }
16
17    /// Hash arbitrary data
18    #[allow(clippy::self_named_constructors)]
19    pub fn hash(data: &[u8]) -> Self {
20        let mut hasher = Sha256::new();
21        hasher.update(data);
22        let result = hasher.finalize();
23        let mut hash = [0u8; 32];
24        hash.copy_from_slice(&result);
25        Hash(hash)
26    }
27
28    /// PERF-OPT 7: Hash two byte slices without concatenating them first.
29    /// Avoids a heap allocation (Vec::with_capacity + extend_from_slice)
30    /// on every call in hot paths like Merkle leaf computation where
31    /// we hash(pubkey || account_bytes). With 100+ dirty accounts per
32    /// block, this eliminates 100+ allocations per state root computation.
33    pub fn hash_two_parts(part_a: &[u8], part_b: &[u8]) -> Self {
34        let mut hasher = Sha256::new();
35        hasher.update(part_a);
36        hasher.update(part_b);
37        let result = hasher.finalize();
38        let mut hash = [0u8; 32];
39        hash.copy_from_slice(&result);
40        Hash(hash)
41    }
42
43    /// Convert to hex string
44    pub fn to_hex(&self) -> String {
45        hex::encode(self.0)
46    }
47
48    /// Parse from hex string
49    pub fn from_hex(s: &str) -> Result<Self, String> {
50        let bytes = hex::decode(s).map_err(|e| format!("Invalid hex: {}", e))?;
51        if bytes.len() != 32 {
52            return Err(format!("Invalid length: {} (expected 32)", bytes.len()));
53        }
54        let mut hash = [0u8; 32];
55        hash.copy_from_slice(&bytes);
56        Ok(Hash(hash))
57    }
58}
59
60impl fmt::Display for Hash {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        write!(f, "{}", self.to_hex())
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_hash() {
72        let data = b"The moss is ready!";
73        let hash = Hash::hash(data);
74        println!("Hash: {}", hash);
75        assert_ne!(hash.0, [0u8; 32]);
76    }
77
78    #[test]
79    fn test_hex_roundtrip() {
80        let original = Hash::hash(b"Lichen");
81        let hex = original.to_hex();
82        let parsed = Hash::from_hex(&hex).unwrap();
83        assert_eq!(original, parsed);
84    }
85}