1use serde::{Deserialize, Serialize};
4use sha2::{Digest as Sha2Digest, Sha256};
5use sha3::Keccak256;
6
7use crate::error::{Error, Result};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct Hash {
13 #[serde(with = "hash_serde")]
14 bytes: [u8; 32],
15}
16
17mod hash_serde {
18 use serde::{Deserialize, Deserializer, Serializer};
19
20 pub fn serialize<S>(bytes: &[u8; 32], s: S) -> std::result::Result<S::Ok, S::Error>
21 where
22 S: Serializer,
23 {
24 s.serialize_str(&format!("0x{}", hex::encode(bytes)))
25 }
26
27 pub fn deserialize<'de, D>(d: D) -> std::result::Result<[u8; 32], D::Error>
28 where
29 D: Deserializer<'de>,
30 {
31 let hex_str = String::deserialize(d)?;
32 let hex_str = hex_str.strip_prefix("0x").unwrap_or(&hex_str);
33 let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?;
34 bytes
35 .try_into()
36 .map_err(|_| serde::de::Error::custom("hash must be 32 bytes"))
37 }
38}
39
40impl Hash {
41 pub fn from_bytes(bytes: [u8; 32]) -> Self {
43 Self { bytes }
44 }
45
46 pub fn from_hex(hex_str: &str) -> Result<Self> {
48 let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
49
50 let bytes = hex::decode(hex_str).map_err(|e| Error::InvalidHex(e.to_string()))?;
51
52 if bytes.len() != 32 {
53 return Err(Error::InvalidHashLength {
54 expected: 32,
55 actual: bytes.len(),
56 });
57 }
58
59 let mut arr = [0u8; 32];
60 arr.copy_from_slice(&bytes);
61 Ok(Self::from_bytes(arr))
62 }
63
64 pub fn as_bytes(&self) -> &[u8; 32] {
66 &self.bytes
67 }
68
69 pub fn to_hex(&self) -> String {
71 hex::encode(self.bytes)
72 }
73
74 pub fn to_hex_prefixed(&self) -> String {
76 format!("0x{}", self.to_hex())
77 }
78
79 pub fn zero() -> Self {
81 Self { bytes: [0u8; 32] }
82 }
83}
84
85impl AsRef<[u8]> for Hash {
86 fn as_ref(&self) -> &[u8] {
87 &self.bytes
88 }
89}
90
91impl std::fmt::Display for Hash {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 write!(f, "0x{}", self.to_hex())
94 }
95}
96
97pub fn sha256(data: &[u8]) -> Hash {
114 let mut hasher = Sha256::new();
115 hasher.update(data);
116 let result = hasher.finalize();
117
118 let mut bytes = [0u8; 32];
119 bytes.copy_from_slice(&result);
120 Hash::from_bytes(bytes)
121}
122
123pub fn sha256_hex(data: &[u8]) -> String {
125 sha256(data).to_hex_prefixed()
126}
127
128pub fn keccak256(data: &[u8]) -> Hash {
145 let mut hasher = Keccak256::new();
146 hasher.update(data);
147 let result = hasher.finalize();
148
149 let mut bytes = [0u8; 32];
150 bytes.copy_from_slice(&result);
151 Hash::from_bytes(bytes)
152}
153
154pub fn keccak256_hex(data: &[u8]) -> String {
156 keccak256(data).to_hex_prefixed()
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_sha256() {
165 let hash = sha256(b"hello");
166 assert_eq!(
168 hash.to_hex(),
169 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
170 );
171 }
172
173 #[test]
174 fn test_sha256_hex() {
175 let hash = sha256_hex(b"hello");
176 assert!(hash.starts_with("0x"));
177 assert_eq!(hash.len(), 66); }
179
180 #[test]
181 fn test_keccak256() {
182 let hash = keccak256(b"hello");
183 assert_eq!(
185 hash.to_hex(),
186 "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
187 );
188 }
189
190 #[test]
191 fn test_hash_from_hex() {
192 let original = sha256(b"test");
193 let from_hex = Hash::from_hex(&original.to_hex()).unwrap();
194 let from_hex_prefixed = Hash::from_hex(&original.to_hex_prefixed()).unwrap();
195
196 assert_eq!(original, from_hex);
197 assert_eq!(original, from_hex_prefixed);
198 }
199
200 #[test]
201 fn test_hash_serde() {
202 let hash = sha256(b"test");
203 let json = serde_json::to_string(&hash).unwrap();
204 let restored: Hash = serde_json::from_str(&json).unwrap();
205
206 assert_eq!(hash, restored);
207 assert!(json.contains("0x")); }
209
210 #[test]
211 fn test_concat_hashes() {
212 fn concat_hashes(left: &Hash, right: &Hash) -> Hash {
213 let mut combined = [0u8; 64];
214 combined[..32].copy_from_slice(left.as_bytes());
215 combined[32..].copy_from_slice(right.as_bytes());
216 sha256(&combined)
217 }
218
219 let h1 = sha256(b"left");
220 let h2 = sha256(b"right");
221 let combined = concat_hashes(&h1, &h2);
222
223 let combined2 = concat_hashes(&h1, &h2);
225 assert_eq!(combined, combined2);
226
227 let combined_reversed = concat_hashes(&h2, &h1);
229 assert_ne!(combined, combined_reversed);
230 }
231}