1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (c) Facebook, Inc. and its affiliates.
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! A wrapper for hashable types to memoize the hash.

use std::borrow::Cow;

use custom_debug_derive::Debug;
use serde::{Deserialize, Serialize};

use crate::crypto::{BcsHashable, CryptoHash};

/// Wrapper type around hashed instance of `T` type.
#[derive(Debug)]
pub struct Hashed<T> {
    value: T,
    /// Hash of the value (used as key for storage).
    hash: CryptoHash,
}

impl<T> Hashed<T> {
    /// Creates an instance of [`Hashed`] with the given `hash` value.
    ///
    /// Note on usage: This method is unsafe because it allows the caller to create a Hashed
    /// with a hash that doesn't match the value. This is necessary for the rewrite state when
    /// signers sign over old `Certificate` type.
    pub fn unchecked_new(value: T, hash: CryptoHash) -> Self {
        Self { value, hash }
    }

    /// Creates an instance of [`Hashed`] with the given `value`.
    ///
    /// Note: Contrary to its `unchecked_new` counterpart, this method is safe because it
    /// calculates the hash from the value.
    pub fn new<'de>(value: T) -> Self
    where
        T: BcsHashable<'de>,
    {
        let hash = CryptoHash::new(&value);
        Self { value, hash }
    }

    /// Returns the hash.
    pub fn hash(&self) -> CryptoHash {
        self.hash
    }

    /// Returns a reference to the value, without the hash.
    pub fn inner(&self) -> &T {
        &self.value
    }

    /// Consumes the hashed value and returns the value without the hash.
    pub fn into_inner(self) -> T {
        self.value
    }
}

impl<T: Serialize> Serialize for Hashed<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.value.serialize(serializer)
    }
}

impl<'de, T: BcsHashable<'de>> Deserialize<'de> for Hashed<T> {
    fn deserialize<D>(deserializer: D) -> Result<Hashed<T>, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Ok(Hashed::new(T::deserialize(deserializer)?))
    }
}

impl<T: Clone> Clone for Hashed<T> {
    fn clone(&self) -> Self {
        Self {
            value: self.value.clone(),
            hash: self.hash,
        }
    }
}

impl<T: async_graphql::OutputType> async_graphql::TypeName for Hashed<T> {
    fn type_name() -> Cow<'static, str> {
        format!("Hashed{}", T::type_name()).into()
    }
}

#[async_graphql::Object(cache_control(no_cache), name_type)]
impl<T: async_graphql::OutputType + Clone> Hashed<T> {
    #[graphql(derived(name = "hash"))]
    async fn _hash(&self) -> CryptoHash {
        self.hash()
    }

    #[graphql(derived(name = "value"))]
    async fn _value(&self) -> T {
        self.inner().clone()
    }
}

impl<T> PartialEq for Hashed<T> {
    fn eq(&self, other: &Self) -> bool {
        self.hash() == other.hash()
    }
}

impl<T> Eq for Hashed<T> {}