1#[cfg(with_testing)]
8use std::ops::RangeInclusive;
9use std::{borrow::Cow, fmt, io, str::FromStr};
10
11#[cfg(with_testing)]
12use alloy_primitives::FixedBytes;
13use alloy_primitives::{Keccak256, B256};
14use linera_witty::{
15 GuestPointer, HList, InstanceWithMemory, Layout, Memory, Runtime, RuntimeError, RuntimeMemory,
16 WitLoad, WitStore, WitType,
17};
18#[cfg(with_testing)]
19use proptest::{
20 collection::{vec, VecStrategy},
21 prelude::{Arbitrary, Strategy},
22 strategy,
23};
24use serde::{Deserialize, Serialize};
25
26use crate::{
27 crypto::{BcsHashable, CryptoError, Hashable},
28 doc_scalar,
29};
30
31#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
33#[cfg_attr(with_testing, derive(Default))]
34pub struct CryptoHash(B256);
35
36impl CryptoHash {
37 pub fn new<'de, T: BcsHashable<'de>>(value: &T) -> Self {
39 let mut hasher = Keccak256Ext(Keccak256::new());
40 value.write(&mut hasher);
41 CryptoHash(hasher.0.finalize())
42 }
43
44 pub fn as_bytes(&self) -> &B256 {
46 &self.0
47 }
48
49 pub fn make_evm_compatible(&mut self) {
51 self.0[20..32].fill(0);
52 }
53
54 #[cfg(with_testing)]
56 pub fn test_hash(s: impl Into<String>) -> Self {
57 use crate::crypto::TestString;
58
59 CryptoHash::new(&TestString::new(s))
60 }
61}
62
63struct Keccak256Ext(Keccak256);
65
66impl io::Write for Keccak256Ext {
67 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
68 self.0.update(buf);
69 Ok(buf.len())
70 }
71
72 fn flush(&mut self) -> io::Result<()> {
73 Ok(())
74 }
75}
76
77#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Serialize, Deserialize)]
80#[cfg_attr(with_testing, derive(Default))]
81pub struct CryptoHashVec(pub Vec<CryptoHash>);
82
83impl BcsHashable<'_> for CryptoHashVec {}
84
85impl Serialize for CryptoHash {
86 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87 where
88 S: serde::ser::Serializer,
89 {
90 if serializer.is_human_readable() {
91 serializer.serialize_str(&self.to_string())
92 } else {
93 serializer.serialize_newtype_struct("CryptoHash", &self.as_bytes().0)
94 }
95 }
96}
97
98impl<'de> Deserialize<'de> for CryptoHash {
99 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
100 where
101 D: serde::de::Deserializer<'de>,
102 {
103 if deserializer.is_human_readable() {
104 let s = String::deserialize(deserializer)?;
105 let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
106 Ok(value)
107 } else {
108 #[derive(Deserialize)]
109 #[serde(rename = "CryptoHash")]
110 struct Foo([u8; 32]);
111
112 let value = Foo::deserialize(deserializer)?;
113 Ok(Self(value.0.into()))
114 }
115 }
116}
117
118impl FromStr for CryptoHash {
119 type Err = CryptoError;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 let value = hex::decode(s)?;
123 (value.as_slice()).try_into()
124 }
125}
126
127impl TryFrom<&[u8]> for CryptoHash {
128 type Error = CryptoError;
129
130 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
131 if value.len() != B256::len_bytes() {
132 return Err(CryptoError::IncorrectHashSize(value.len()));
133 }
134 Ok(Self(B256::from_slice(value)))
135 }
136}
137
138impl From<CryptoHash> for [u8; 32] {
139 fn from(crypto_hash: CryptoHash) -> Self {
140 crypto_hash.0 .0
141 }
142}
143
144impl From<[u64; 4]> for CryptoHash {
145 fn from(integers: [u64; 4]) -> Self {
146 CryptoHash(crate::crypto::u64_array_to_be_bytes(integers).into())
147 }
148}
149
150impl From<CryptoHash> for [u64; 4] {
151 fn from(crypto_hash: CryptoHash) -> Self {
152 crate::crypto::be_bytes_to_u64_array(crypto_hash.0.as_ref())
153 }
154}
155
156impl fmt::Display for CryptoHash {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 let prec = f.precision().unwrap_or(self.0.len() * 2);
159 hex::encode(&self.0[..prec.div_ceil(2)]).fmt(f)
160 }
161}
162
163impl fmt::Debug for CryptoHash {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "{}", hex::encode(&self.0[..8]))
166 }
167}
168
169impl WitType for CryptoHash {
170 const SIZE: u32 = <(u64, u64, u64, u64) as WitType>::SIZE;
171 type Layout = <(u64, u64, u64, u64) as WitType>::Layout;
172 type Dependencies = HList![];
173
174 fn wit_type_name() -> Cow<'static, str> {
175 "crypto-hash".into()
176 }
177
178 fn wit_type_declaration() -> Cow<'static, str> {
179 concat!(
180 " record crypto-hash {\n",
181 " part1: u64,\n",
182 " part2: u64,\n",
183 " part3: u64,\n",
184 " part4: u64,\n",
185 " }\n",
186 )
187 .into()
188 }
189}
190
191impl WitLoad for CryptoHash {
192 fn load<Instance>(
193 memory: &Memory<'_, Instance>,
194 location: GuestPointer,
195 ) -> Result<Self, RuntimeError>
196 where
197 Instance: InstanceWithMemory,
198 <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
199 {
200 let (part1, part2, part3, part4) = WitLoad::load(memory, location)?;
201 Ok(CryptoHash::from([part1, part2, part3, part4]))
202 }
203
204 fn lift_from<Instance>(
205 flat_layout: <Self::Layout as linera_witty::Layout>::Flat,
206 memory: &Memory<'_, Instance>,
207 ) -> Result<Self, RuntimeError>
208 where
209 Instance: InstanceWithMemory,
210 <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
211 {
212 let (part1, part2, part3, part4) = WitLoad::lift_from(flat_layout, memory)?;
213 Ok(CryptoHash::from([part1, part2, part3, part4]))
214 }
215}
216
217impl WitStore for CryptoHash {
218 fn store<Instance>(
219 &self,
220 memory: &mut Memory<'_, Instance>,
221 location: GuestPointer,
222 ) -> Result<(), RuntimeError>
223 where
224 Instance: InstanceWithMemory,
225 <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
226 {
227 let [part1, part2, part3, part4] = (*self).into();
228 (part1, part2, part3, part4).store(memory, location)
229 }
230
231 fn lower<Instance>(
232 &self,
233 memory: &mut Memory<'_, Instance>,
234 ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
235 where
236 Instance: InstanceWithMemory,
237 <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
238 {
239 let [part1, part2, part3, part4] = (*self).into();
240 (part1, part2, part3, part4).lower(memory)
241 }
242}
243
244#[cfg(with_testing)]
245impl Arbitrary for CryptoHash {
246 type Parameters = ();
247 type Strategy = strategy::Map<VecStrategy<RangeInclusive<u8>>, fn(Vec<u8>) -> CryptoHash>;
248
249 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
250 vec(u8::MIN..=u8::MAX, FixedBytes::<32>::len_bytes())
251 .prop_map(|vector| CryptoHash(B256::from_slice(&vector[..])))
252 }
253}
254
255doc_scalar!(CryptoHash, "A Keccak256 value");