1use std::{fmt, ops::Deref};
2
3use alloy_primitives::B256;
4use hex::FromHex;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7use crate::{
8 utils::{FromBool, FromU256},
9 U256,
10};
11
12#[derive(Default, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13#[cfg_attr(feature = "testutils", derive(arbitrary::Arbitrary))]
14pub struct Digest(pub [u8; 32]);
15
16impl Deref for Digest {
17 type Target = [u8; 32];
18
19 #[inline]
20 fn deref(&self) -> &Self::Target {
21 &self.0
22 }
23}
24
25impl AsRef<[u8]> for Digest {
26 #[inline]
27 fn as_ref(&self) -> &[u8] {
28 &self.0
29 }
30}
31
32impl fmt::Display for Digest {
33 #[inline]
34 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
35 write!(f, "{self:#x}")
36 }
37}
38
39impl fmt::Debug for Digest {
40 #[inline]
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "{self:x}")
43 }
44}
45
46impl fmt::UpperHex for Digest {
47 #[inline]
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 if f.alternate() {
50 write!(f, "0x")?;
51 }
52
53 for byte in &self.0 {
54 write!(f, "{byte:02X}")?;
55 }
56
57 Ok(())
58 }
59}
60
61impl Digest {
62 pub const ZERO: Digest = Digest([0u8; 32]);
63
64 #[inline]
65 pub fn as_slice(&self) -> &[u8] {
66 self.0.as_slice()
67 }
68
69 #[inline]
70 pub fn as_bytes(&self) -> &[u8; 32] {
71 &self.0
72 }
73}
74
75impl From<[u8; 32]> for Digest {
76 #[inline]
77 fn from(bytes: [u8; 32]) -> Self {
78 Self(bytes)
79 }
80}
81
82impl From<B256> for Digest {
83 #[inline]
84 fn from(bytes: B256) -> Self {
85 Self(bytes.into())
86 }
87}
88
89impl From<Digest> for B256 {
90 #[inline]
91 fn from(bytes: Digest) -> Self {
92 Self::from(bytes.0)
93 }
94}
95
96impl fmt::LowerHex for Digest {
97 #[inline]
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 if f.alternate() {
100 write!(f, "0x")?;
101 }
102
103 for byte in &self.0 {
104 write!(f, "{byte:02x}")?;
105 }
106
107 Ok(())
108 }
109}
110
111impl<'de> Deserialize<'de> for Digest {
112 #[inline]
113 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
114 where
115 D: Deserializer<'de>,
116 {
117 if deserializer.is_human_readable() {
118 let s = <String>::deserialize(deserializer)?;
119
120 let s = s.trim_start_matches("0x");
121 let s = <[u8; 32]>::from_hex(s).map_err(serde::de::Error::custom)?;
122
123 Ok(Digest(s))
124 } else {
125 #[derive(::serde::Deserialize)]
126 #[serde(rename = "agglayer_primitives::Digest")]
127 struct Value([u8; 32]);
128
129 let value = Value::deserialize(deserializer)?;
130 Ok(Digest(value.0))
131 }
132 }
133}
134
135impl Serialize for Digest {
136 #[inline]
137 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
138 where
139 S: Serializer,
140 {
141 if serializer.is_human_readable() {
142 format!("{self:#x}").serialize(serializer)
143 } else {
144 serializer.serialize_newtype_struct("agglayer_primitives::Digest", &self.0)
145 }
146 }
147}
148
149const DIGEST_FROM_BOOL_TRUE: Digest = Digest([
150 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151]);
152
153const DIGEST_FROM_BOOL_FALSE: Digest = Digest([
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155]);
156
157impl FromBool for Digest {
158 #[inline]
159 fn from_bool(b: bool) -> Self {
160 if b {
161 DIGEST_FROM_BOOL_TRUE
162 } else {
163 DIGEST_FROM_BOOL_FALSE
164 }
165 }
166}
167
168impl FromU256 for Digest {
169 #[inline]
170 fn from_u256(u: U256) -> Self {
171 Self(u.to_be_bytes())
172 }
173}
174
175impl TryFrom<&[u8]> for Digest {
176 type Error = std::array::TryFromSliceError;
177
178 #[inline]
179 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
180 <[u8; 32]>::try_from(value).map(Digest)
181 }
182}
183
184impl TryFrom<Vec<u8>> for Digest {
185 type Error = std::array::TryFromSliceError;
186
187 #[inline]
188 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
189 Self::try_from(&*value)
190 }
191}
192
193impl From<Digest> for Vec<u8> {
194 #[inline]
195 fn from(value: Digest) -> Self {
196 value.0.to_vec()
197 }
198}
199
200#[cfg(any(test, feature = "testutils"))]
201impl rand::distr::Distribution<Digest> for rand::distr::StandardUniform {
202 #[inline]
203 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Digest {
204 use rand::RngExt as _;
205
206 let raw: [u8; 32] = rng.random();
207 Digest(raw)
208 }
209}