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