1use core::{
4 convert::TryFrom,
5 fmt::{self, Debug, Display},
6 str::FromStr,
7};
8
9use bytes::Bytes;
10use cometbft_proto::serializers::cow_str::CowStr;
11use cometbft_proto::Protobuf;
12use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
13use subtle_encoding::{base64, Encoding, Hex};
14
15use crate::{error::Error, prelude::*};
16
17pub const SHA256_HASH_SIZE: usize = 32;
19
20#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
22pub enum Algorithm {
23 Sha256,
25}
26
27#[derive(Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
29pub enum Hash {
30 Sha256([u8; SHA256_HASH_SIZE]),
32 #[default]
34 None,
35}
36
37impl Protobuf<Vec<u8>> for Hash {}
38
39impl TryFrom<Vec<u8>> for Hash {
41 type Error = Error;
42
43 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
44 if value.is_empty() {
45 return Ok(Hash::None);
46 }
47 Hash::from_bytes(Algorithm::Sha256, &value)
48 }
49}
50
51impl From<Hash> for Vec<u8> {
52 fn from(value: Hash) -> Self {
53 match value {
54 Hash::Sha256(s) => s.to_vec(),
55 Hash::None => vec![],
56 }
57 }
58}
59
60impl AsRef<[u8]> for Hash {
61 fn as_ref(&self) -> &[u8] {
62 match self {
63 Hash::Sha256(ref h) => h.as_ref(),
64 Hash::None => &[],
65 }
66 }
67}
68
69impl From<Hash> for Bytes {
70 fn from(h: Hash) -> Self {
71 Self::copy_from_slice(h.as_ref())
72 }
73}
74
75impl TryFrom<Bytes> for Hash {
76 type Error = Error;
77
78 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
79 Self::from_bytes(Algorithm::Sha256, value.as_ref())
80 }
81}
82
83impl Hash {
84 pub fn from_bytes(alg: Algorithm, bytes: &[u8]) -> Result<Hash, Error> {
86 if bytes.is_empty() {
87 return Ok(Hash::None);
88 }
89 match alg {
90 Algorithm::Sha256 => {
91 if bytes.len() == SHA256_HASH_SIZE {
92 let mut h = [0u8; SHA256_HASH_SIZE];
93 h.copy_from_slice(bytes);
94 Ok(Hash::Sha256(h))
95 } else {
96 Err(Error::invalid_hash_size())
97 }
98 },
99 }
100 }
101
102 pub fn from_hex_upper(alg: Algorithm, s: &str) -> Result<Hash, Error> {
104 if s.is_empty() {
105 return Ok(Hash::None);
106 }
107 match alg {
108 Algorithm::Sha256 => {
109 let mut h = [0u8; SHA256_HASH_SIZE];
110 Hex::upper_case()
111 .decode_to_slice(s.as_bytes(), &mut h)
112 .map_err(Error::subtle_encoding)?;
113 Ok(Hash::Sha256(h))
114 },
115 }
116 }
117
118 pub fn algorithm(self) -> Algorithm {
120 match self {
121 Hash::Sha256(_) => Algorithm::Sha256,
122 Hash::None => Algorithm::Sha256,
123 }
124 }
125
126 pub fn as_bytes(&self) -> &[u8] {
128 match self {
129 Hash::Sha256(ref h) => h.as_ref(),
130 Hash::None => &[],
131 }
132 }
133
134 pub fn is_empty(&self) -> bool {
136 self == &Hash::None
137 }
138}
139
140impl Debug for Hash {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 match self {
143 Hash::Sha256(_) => write!(f, "Hash::Sha256({self})"),
144 Hash::None => write!(f, "Hash::None"),
145 }
146 }
147}
148
149impl Display for Hash {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 let hex = match self {
152 Hash::Sha256(ref h) => Hex::upper_case().encode_to_string(h).unwrap(),
153 Hash::None => String::new(),
154 };
155
156 write!(f, "{hex}")
157 }
158}
159
160impl FromStr for Hash {
161 type Err = Error;
162
163 fn from_str(s: &str) -> Result<Self, Error> {
164 Self::from_hex_upper(Algorithm::Sha256, s)
165 }
166}
167
168impl<'de> Deserialize<'de> for Hash {
169 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
170 let hex = CowStr::deserialize(deserializer)?;
171
172 if hex.is_empty() {
173 Err(D::Error::custom("empty hash"))
174 } else {
175 Ok(Self::from_str(&hex).map_err(|e| D::Error::custom(format!("{e}")))?)
176 }
177 }
178}
179
180impl Serialize for Hash {
181 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
182 self.to_string().serialize(serializer)
183 }
184}
185
186pub mod allow_empty {
188 use super::*;
189
190 pub fn serialize<S>(value: &Hash, serializer: S) -> Result<S::Ok, S::Error>
192 where
193 S: Serializer,
194 {
195 value.to_string().serialize(serializer)
196 }
197
198 pub fn deserialize<'de, D>(deserializer: D) -> Result<Hash, D::Error>
201 where
202 D: Deserializer<'de>,
203 {
204 let hex = CowStr::deserialize(deserializer)?;
205 Hash::from_str(&hex).map_err(serde::de::Error::custom)
206 }
207}
208
209#[derive(Clone, PartialEq, Eq, Default)]
211pub struct AppHash(Vec<u8>);
212
213impl Protobuf<Vec<u8>> for AppHash {}
214
215impl TryFrom<Vec<u8>> for AppHash {
216 type Error = Error;
217
218 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
219 Ok(AppHash(value))
220 }
221}
222impl From<AppHash> for Vec<u8> {
223 fn from(value: AppHash) -> Self {
224 value.0
225 }
226}
227
228impl TryFrom<Bytes> for AppHash {
229 type Error = Error;
230
231 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
232 Ok(AppHash(value.to_vec()))
233 }
234}
235impl From<AppHash> for Bytes {
236 fn from(value: AppHash) -> Self {
237 value.0.into()
238 }
239}
240
241impl AppHash {
242 pub fn as_bytes(&self) -> &[u8] {
244 &self.0
245 }
246
247 pub fn from_hex_upper(s: &str) -> Result<Self, Error> {
249 #[allow(clippy::manual_is_multiple_of)]
251 if s.len() % 2 != 0 {
252 return Err(Error::invalid_app_hash_length());
253 }
254 let mut h = vec![0; s.len() / 2];
255 Hex::upper_case()
256 .decode_to_slice(s.as_bytes(), &mut h)
257 .map_err(Error::subtle_encoding)?;
258 Ok(AppHash(h))
259 }
260
261 pub fn from_base64(s: &str) -> Result<Self, Error> {
263 let h = base64::decode(s).map_err(Error::subtle_encoding)?;
264 Ok(AppHash(h))
265 }
266}
267
268impl AsRef<[u8]> for AppHash {
269 fn as_ref(&self) -> &[u8] {
270 self.0.as_ref()
271 }
272}
273
274impl Debug for AppHash {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 write!(
277 f,
278 "AppHash({})",
279 Hex::upper_case().encode_to_string(&self.0).unwrap()
280 )
281 }
282}
283
284impl Display for AppHash {
285 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286 write!(
287 f,
288 "{}",
289 Hex::upper_case().encode_to_string(&self.0).unwrap()
290 )
291 }
292}
293
294impl FromStr for AppHash {
295 type Err = Error;
296
297 fn from_str(s: &str) -> Result<Self, Error> {
298 Self::from_hex_upper(s).or_else(|_| Self::from_base64(s))
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[derive(Debug, serde::Deserialize)]
307 struct AppHashTest {
308 #[serde(default)]
309 #[serde(with = "crate::serializers::apphash")]
310 pub app_hash: AppHash,
311 }
312
313 #[derive(Debug, serde::Deserialize)]
314 struct HashTest {
315 hash: Hash,
316 #[serde(with = "super::allow_empty")]
317 empty_hash: Hash,
318 }
319
320 #[test]
321 fn apphash_decode_base64() {
322 let test = serde_json::from_str::<AppHashTest>(
323 r#"{"app_hash":"MfX9f+bYoI8IioRb4YT/8/VhPvtNjgWFgTi4mmMSkBc="}"#,
324 )
325 .unwrap();
326
327 assert_eq!(
328 test.app_hash.as_ref(),
329 &[
330 0x31, 0xF5, 0xFD, 0x7F, 0xE6, 0xD8, 0xA0, 0x8F, 0x08, 0x8A, 0x84, 0x5B, 0xE1, 0x84,
331 0xFF, 0xF3, 0xF5, 0x61, 0x3E, 0xFB, 0x4D, 0x8E, 0x05, 0x85, 0x81, 0x38, 0xB8, 0x9A,
332 0x63, 0x12, 0x90, 0x17
333 ]
334 );
335 }
336
337 #[test]
338 fn apphash_decode_hex() {
339 let test = serde_json::from_str::<AppHashTest>(
340 r#"{"app_hash":"31F5FD7FE6D8A08F088A845BE184FFF3F5613EFB4D8E05858138B89A63129017"}"#,
341 )
342 .unwrap();
343
344 assert_eq!(
345 test.app_hash.as_ref(),
346 &[
347 0x31, 0xF5, 0xFD, 0x7F, 0xE6, 0xD8, 0xA0, 0x8F, 0x08, 0x8A, 0x84, 0x5B, 0xE1, 0x84,
348 0xFF, 0xF3, 0xF5, 0x61, 0x3E, 0xFB, 0x4D, 0x8E, 0x05, 0x85, 0x81, 0x38, 0xB8, 0x9A,
349 0x63, 0x12, 0x90, 0x17
350 ]
351 );
352 }
353
354 #[test]
355 fn hash_decode_hex() {
356 let s = r#"{
357 "hash": "9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08",
358 "empty_hash": ""
359 }"#;
360
361 let expected_hash = &[
362 0x9F, 0x86, 0xD0, 0x81, 0x88, 0x4C, 0x7D, 0x65, 0x9A, 0x2F, 0xEA, 0xA0, 0xC5, 0x5A,
363 0xD0, 0x15, 0xA3, 0xBF, 0x4F, 0x1B, 0x2B, 0x0B, 0x82, 0x2C, 0xD1, 0x5D, 0x6C, 0x15,
364 0xB0, 0xF0, 0x0A, 0x08,
365 ];
366
367 let test = serde_json::from_str::<HashTest>(s).unwrap();
368 assert_eq!(test.hash.as_ref(), expected_hash);
369 assert_eq!(test.empty_hash, Hash::None);
370
371 let json_value = serde_json::from_str::<serde_json::Value>(s).unwrap();
373 let test = serde_json::from_value::<HashTest>(json_value).unwrap();
374 assert_eq!(test.hash.as_ref(), expected_hash);
375 assert_eq!(test.empty_hash, Hash::None);
376 }
377}