linera_base/
hashed.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5//! A wrapper for hashable types to memoize the hash.
6
7use std::borrow::Cow;
8
9use custom_debug_derive::Debug;
10use serde::{Deserialize, Serialize};
11
12use crate::crypto::{BcsHashable, CryptoHash};
13
14/// Wrapper type around hashed instance of `T` type.
15#[derive(Debug)]
16pub struct Hashed<T> {
17    value: T,
18    /// Hash of the value (used as key for storage).
19    hash: CryptoHash,
20}
21
22impl<T> Hashed<T> {
23    /// Creates an instance of [`Hashed`] with the given `hash` value.
24    ///
25    /// Note on usage: This method is unsafe because it allows the caller to create a Hashed
26    /// with a hash that doesn't match the value. This is necessary for the rewrite state when
27    /// signers sign over old `Certificate` type.
28    pub fn unchecked_new(value: T, hash: CryptoHash) -> Self {
29        Self { value, hash }
30    }
31
32    /// Creates an instance of [`Hashed`] with the given `value`.
33    ///
34    /// Note: Contrary to its `unchecked_new` counterpart, this method is safe because it
35    /// calculates the hash from the value.
36    pub fn new<'de>(value: T) -> Self
37    where
38        T: BcsHashable<'de>,
39    {
40        let hash = CryptoHash::new(&value);
41        Self { value, hash }
42    }
43
44    /// Returns the hash.
45    pub fn hash(&self) -> CryptoHash {
46        self.hash
47    }
48
49    /// Returns a reference to the value, without the hash.
50    pub fn inner(&self) -> &T {
51        &self.value
52    }
53
54    /// Consumes the hashed value and returns the value without the hash.
55    pub fn into_inner(self) -> T {
56        self.value
57    }
58}
59
60impl<T: Serialize> Serialize for Hashed<T> {
61    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62    where
63        S: serde::Serializer,
64    {
65        self.value.serialize(serializer)
66    }
67}
68
69impl<'de, T: BcsHashable<'de>> Deserialize<'de> for Hashed<T> {
70    fn deserialize<D>(deserializer: D) -> Result<Hashed<T>, D::Error>
71    where
72        D: serde::Deserializer<'de>,
73    {
74        Ok(Hashed::new(T::deserialize(deserializer)?))
75    }
76}
77
78impl<T: Clone> Clone for Hashed<T> {
79    fn clone(&self) -> Self {
80        Self {
81            value: self.value.clone(),
82            hash: self.hash,
83        }
84    }
85}
86
87impl<T: async_graphql::OutputType> async_graphql::TypeName for Hashed<T> {
88    fn type_name() -> Cow<'static, str> {
89        format!("Hashed{}", T::type_name()).into()
90    }
91}
92
93#[async_graphql::Object(cache_control(no_cache), name_type)]
94impl<T: async_graphql::OutputType + Clone> Hashed<T> {
95    #[graphql(derived(name = "hash"))]
96    async fn _hash(&self) -> CryptoHash {
97        self.hash()
98    }
99
100    #[graphql(derived(name = "value"))]
101    async fn _value(&self) -> T {
102        self.inner().clone()
103    }
104}
105
106impl<T> PartialEq for Hashed<T> {
107    fn eq(&self, other: &Self) -> bool {
108        self.hash() == other.hash()
109    }
110}
111
112impl<T> Eq for Hashed<T> {}