Skip to main content

aptos_crypto_link/
validatable.rs

1// Copyright (c) Aptos
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module provides the `Validate` trait and `Validatable` type in order to aid in deferred
5//! validation.
6
7use crate::ValidCryptoMaterial;
8use anyhow::Result;
9use once_cell::sync::OnceCell;
10use serde::{Deserialize, Serialize};
11use std::hash::Hash;
12
13/// The `Validate` trait is used in tandem with the `Validatable` type in order to provide deferred
14/// validation for types.
15///
16/// ## Trait Contract
17///
18/// Any type `V` which implement this trait must adhere to the following contract:
19///
20/// * `V` and `V::Unvalidated` are byte-for-byte equivalent.
21/// * `V` and `V::Unvalidated` have equivalent `Hash` implementations.
22/// * `V` and `V::Unvalidated` must have equivalent `Serialize` and `Deserialize` implementation.
23///   This means that `V` and `V:Unvalidated` have equivalent serialized formats and that you can
24///   deserialize a `V::Unvalidated` from a `V` that was previously serialized.
25pub trait Validate: Sized {
26    /// The unvalidated form of some type `V`
27    type Unvalidated: ValidCryptoMaterial;
28
29    /// Attempt to validate a `V::Unvalidated` and returning a validated `V` on success
30    fn validate(unvalidated: &Self::Unvalidated) -> Result<Self>;
31
32    /// Return the unvalidated form of type `V`
33    fn to_unvalidated(&self) -> Self::Unvalidated;
34}
35
36/// Used in connection with the `Validate` trait to be able to represent types which can benefit
37/// from deferred validation as a performance optimization.
38#[derive(Clone, Debug)]
39pub struct Validatable<V: Validate> {
40    unvalidated: V::Unvalidated,
41    maybe_valid: OnceCell<V>,
42}
43
44impl<V: Validate> Validatable<V> {
45    /// Create a new `Validatable` from a validated type. This will assume the input has been validated
46    /// by the caller and as a result `Validatable::<V>::validate().is_ok()` will always return true.
47    pub fn from_validated(valid: V) -> Self {
48        let unvalidated = valid.to_unvalidated();
49
50        let maybe_valid = OnceCell::new();
51        maybe_valid.set(valid).unwrap_or_else(|_| unreachable!());
52
53        Self {
54            unvalidated,
55            maybe_valid,
56        }
57    }
58
59    /// Create a new `Validatable` from an unvalidated type
60    pub fn from_unvalidated(unvalidated: V::Unvalidated) -> Self {
61        Self {
62            unvalidated,
63            maybe_valid: OnceCell::new(),
64        }
65    }
66
67    /// Return a reference to the unvalidated form `V::Unvalidated`
68    pub fn unvalidated(&self) -> &V::Unvalidated {
69        &self.unvalidated
70    }
71
72    /// Try to validate the unvalidated form, returning `Some(&V)` on success and `None` on failure.
73    pub fn valid(&self) -> Option<&V> {
74        self.validate().ok()
75    }
76
77    // TODO maybe optimize to only try once and keep track when we fail. This would avoid multiple calls to validate() by valid() when validation fails
78    /// Attempt to validate `V::Unvalidated` and return a reference to a valid `V`
79    pub fn validate(&self) -> Result<&V> {
80        self.maybe_valid
81            .get_or_try_init(|| V::validate(&self.unvalidated))
82    }
83}
84
85/// Serializes a `Validatable<V>` using the `serde::Serialize` implementation of `V::Unvalidated`
86impl<V> Serialize for Validatable<V>
87where
88    V: Validate + Serialize,
89    V::Unvalidated: Serialize,
90{
91    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92    where
93        S: serde::Serializer,
94    {
95        self.unvalidated.serialize(serializer)
96    }
97}
98
99/// Deserializes a `Validatable<V>` using the `serde::Deserialize` implementation of `V::Unvalidated`.
100/// Does *not* perform validation on the deserialized `V::Unvalidated` object.
101impl<'de, V> Deserialize<'de> for Validatable<V>
102where
103    V: Validate,
104    V::Unvalidated: Deserialize<'de>,
105{
106    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
107    where
108        D: serde::Deserializer<'de>,
109    {
110        let unvalidated = <V::Unvalidated>::deserialize(deserializer)?;
111        Ok(Self::from_unvalidated(unvalidated))
112    }
113}
114
115/// Simply calls the equality operator of `V::Unvalidated`
116impl<V> PartialEq for Validatable<V>
117where
118    V: Validate,
119    V::Unvalidated: PartialEq,
120{
121    fn eq(&self, other: &Self) -> bool {
122        self.unvalidated == other.unvalidated
123    }
124}
125
126impl<V> Eq for Validatable<V>
127where
128    V: Validate,
129    V::Unvalidated: Eq,
130{
131}
132
133/// Simply calls the `Hash` implementation of `V::Unvalidated`
134impl<V> Hash for Validatable<V>
135where
136    V: Validate,
137    V::Unvalidated: Hash,
138{
139    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
140        self.unvalidated.hash(state);
141    }
142}