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
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/// A Keccak256 value.
32#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
33#[cfg_attr(with_testing, derive(Default))]
34pub struct CryptoHash(B256);
35
36impl CryptoHash {
37    /// Computes a hash.
38    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    /// Reads the bytes of the hash value.
45    pub fn as_bytes(&self) -> &B256 {
46        &self.0
47    }
48
49    /// Force the last 12 bytes of the hash to be zeroes. This is currently used for EVM compatibility
50    pub fn make_evm_compatible(&mut self) {
51        self.0[20..32].fill(0);
52    }
53
54    /// Returns the hash of `TestString(s)`, for testing purposes.
55    #[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
63/// Temporary struct to extend `Keccak256` with `io::Write`.
64struct 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/// A vector of cryptographic hashes.
78/// This is used to represent a hash of a list of hashes.
79#[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<[u8; 32]> for CryptoHash {
145    fn from(bytes: [u8; 32]) -> Self {
146        CryptoHash(B256::from(bytes))
147    }
148}
149
150impl From<[u64; 4]> for CryptoHash {
151    fn from(integers: [u64; 4]) -> Self {
152        CryptoHash(crate::crypto::u64_array_to_be_bytes(integers).into())
153    }
154}
155
156impl From<CryptoHash> for [u64; 4] {
157    fn from(crypto_hash: CryptoHash) -> Self {
158        crate::crypto::be_bytes_to_u64_array(crypto_hash.0.as_ref())
159    }
160}
161
162impl fmt::Display for CryptoHash {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        let prec = f.precision().unwrap_or(self.0.len() * 2);
165        hex::encode(&self.0[..prec.div_ceil(2)]).fmt(f)
166    }
167}
168
169impl fmt::Debug for CryptoHash {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        write!(f, "{}", hex::encode(&self.0[..8]))
172    }
173}
174
175impl WitType for CryptoHash {
176    const SIZE: u32 = <(u64, u64, u64, u64) as WitType>::SIZE;
177    type Layout = <(u64, u64, u64, u64) as WitType>::Layout;
178    type Dependencies = HList![];
179
180    fn wit_type_name() -> Cow<'static, str> {
181        "crypto-hash".into()
182    }
183
184    fn wit_type_declaration() -> Cow<'static, str> {
185        concat!(
186            "    record crypto-hash {\n",
187            "        part1: u64,\n",
188            "        part2: u64,\n",
189            "        part3: u64,\n",
190            "        part4: u64,\n",
191            "    }\n",
192        )
193        .into()
194    }
195}
196
197impl WitLoad for CryptoHash {
198    fn load<Instance>(
199        memory: &Memory<'_, Instance>,
200        location: GuestPointer,
201    ) -> Result<Self, RuntimeError>
202    where
203        Instance: InstanceWithMemory,
204        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
205    {
206        let (part1, part2, part3, part4) = WitLoad::load(memory, location)?;
207        Ok(CryptoHash::from([part1, part2, part3, part4]))
208    }
209
210    fn lift_from<Instance>(
211        flat_layout: <Self::Layout as linera_witty::Layout>::Flat,
212        memory: &Memory<'_, Instance>,
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::lift_from(flat_layout, memory)?;
219        Ok(CryptoHash::from([part1, part2, part3, part4]))
220    }
221}
222
223impl WitStore for CryptoHash {
224    fn store<Instance>(
225        &self,
226        memory: &mut Memory<'_, Instance>,
227        location: GuestPointer,
228    ) -> Result<(), RuntimeError>
229    where
230        Instance: InstanceWithMemory,
231        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
232    {
233        let [part1, part2, part3, part4] = (*self).into();
234        (part1, part2, part3, part4).store(memory, location)
235    }
236
237    fn lower<Instance>(
238        &self,
239        memory: &mut Memory<'_, Instance>,
240    ) -> Result<<Self::Layout as Layout>::Flat, 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).lower(memory)
247    }
248}
249
250#[cfg(with_testing)]
251impl Arbitrary for CryptoHash {
252    type Parameters = ();
253    type Strategy = strategy::Map<VecStrategy<RangeInclusive<u8>>, fn(Vec<u8>) -> CryptoHash>;
254
255    fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
256        vec(u8::MIN..=u8::MAX, FixedBytes::<32>::len_bytes())
257            .prop_map(|vector| CryptoHash(B256::from_slice(&vector[..])))
258    }
259}
260
261doc_scalar!(CryptoHash, "A Keccak256 value");