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