Skip to main content

agglayer_primitives/
digest.rs

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}