linera_base/crypto/
hash.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5//! Defines hashing primitives used by the Linera protocol.
6
7#[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/// A Keccak256 value.
33#[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    /// Computes a hash.
45    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    /// Reads the bytes of the hash value.
52    pub fn as_bytes(&self) -> &B256 {
53        &self.0
54    }
55
56    /// Force the last 12 bytes of the hash to be zeroes. This is currently used for EVM compatibility
57    pub fn make_evm_compatible(&mut self) {
58        self.0[20..32].fill(0);
59    }
60
61    /// Returns the hash of `TestString(s)`, for testing purposes.
62    #[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
70/// Temporary struct to extend `Keccak256` with `io::Write`.
71struct 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/// A vector of cryptographic hashes.
85/// This is used to represent a hash of a list of hashes.
86#[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");