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