sui_types/
signature_verification.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use nonempty::NonEmpty;
5use shared_crypto::intent::Intent;
6
7use crate::base_types::SuiAddress;
8use crate::committee::EpochId;
9use crate::digests::ZKLoginInputsDigest;
10use crate::error::{SuiErrorKind, SuiResult};
11use crate::signature::VerifyParams;
12use crate::transaction::{SenderSignedData, TransactionDataAPI};
13use lru::LruCache;
14use parking_lot::RwLock;
15use prometheus::IntCounter;
16use std::hash::Hash;
17use std::sync::Arc;
18
19// Cache up to this many verified certs. We will need to tune this number in the future - a decent
20// guess to start with is that it should be 10-20 times larger than peak transactions per second,
21// on the assumption that we should see most certs twice within about 10-20 seconds at most:
22// Once via RPC, once via consensus.
23const VERIFIED_CERTIFICATE_CACHE_SIZE: usize = 100_000;
24
25pub struct VerifiedDigestCache<D> {
26    inner: RwLock<LruCache<D, ()>>,
27    cache_hits_counter: IntCounter,
28    cache_misses_counter: IntCounter,
29    cache_evictions_counter: IntCounter,
30}
31
32impl<D: Hash + Eq + Copy> VerifiedDigestCache<D> {
33    pub fn new(
34        cache_hits_counter: IntCounter,
35        cache_misses_counter: IntCounter,
36        cache_evictions_counter: IntCounter,
37    ) -> Self {
38        Self {
39            inner: RwLock::new(LruCache::new(
40                std::num::NonZeroUsize::new(VERIFIED_CERTIFICATE_CACHE_SIZE).unwrap(),
41            )),
42            cache_hits_counter,
43            cache_misses_counter,
44            cache_evictions_counter,
45        }
46    }
47
48    pub fn is_cached(&self, digest: &D) -> bool {
49        let inner = self.inner.read();
50        if inner.contains(digest) {
51            self.cache_hits_counter.inc();
52            true
53        } else {
54            self.cache_misses_counter.inc();
55            false
56        }
57    }
58
59    pub fn cache_digest(&self, digest: D) {
60        let mut inner = self.inner.write();
61        if let Some(old) = inner.push(digest, ())
62            && old.0 != digest
63        {
64            self.cache_evictions_counter.inc();
65        }
66    }
67
68    pub fn cache_digests(&self, digests: Vec<D>) {
69        let mut inner = self.inner.write();
70        digests.into_iter().for_each(|d| {
71            if let Some(old) = inner.push(d, ())
72                && old.0 != d
73            {
74                self.cache_evictions_counter.inc();
75            }
76        });
77    }
78
79    pub fn is_verified<F, G>(&self, digest: D, verify_callback: F, uncached_checks: G) -> SuiResult
80    where
81        F: FnOnce() -> SuiResult,
82        G: FnOnce() -> SuiResult,
83    {
84        if !self.is_cached(&digest) {
85            verify_callback()?;
86            self.cache_digest(digest);
87        } else {
88            // Checks that are required to be performed outside the cache.
89            uncached_checks()?;
90        }
91        Ok(())
92    }
93
94    pub fn clear(&self) {
95        let mut inner = self.inner.write();
96        inner.clear();
97    }
98
99    // Initialize an empty cache when the cache is not needed (in testing scenarios, graphql and rosetta initialization).
100    pub fn new_empty() -> Self {
101        Self::new(
102            IntCounter::new("test_cache_hits", "test cache hits").unwrap(),
103            IntCounter::new("test_cache_misses", "test cache misses").unwrap(),
104            IntCounter::new("test_cache_evictions", "test cache evictions").unwrap(),
105        )
106    }
107}
108
109/// Does crypto validation for a transaction which may be user-provided, or may be from a checkpoint.
110pub fn verify_sender_signed_data_message_signatures(
111    txn: &SenderSignedData,
112    current_epoch: EpochId,
113    verify_params: &VerifyParams,
114    zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
115    aliased_addresses: Vec<(SuiAddress, NonEmpty<SuiAddress>)>,
116) -> SuiResult {
117    let intent_message = txn.intent_message();
118    assert_eq!(intent_message.intent, Intent::sui_transaction());
119
120    // 1. System transactions do not require signatures. User-submitted transactions are verified not to
121    // be system transactions before this point
122    if intent_message.value.is_system_tx() {
123        return Ok(());
124    }
125
126    // 2. One signature per signer is required.
127    let required_signers = txn.intent_message().value.required_signers();
128    fp_ensure!(
129        txn.inner().tx_signatures.len() == required_signers.len(),
130        SuiErrorKind::SignerSignatureNumberMismatch {
131            actual: txn.inner().tx_signatures.len(),
132            expected: required_signers.len()
133        }
134        .into()
135    );
136
137    // 3. Each signer must provide a signature from one of the set of allowed aliases.
138    let present_sigs = txn.get_signer_sig_mapping(verify_params.verify_legacy_zklogin_address)?;
139    let required_signer_alias_sets = required_signers.map(|s| {
140        aliased_addresses
141            .iter()
142            .find(|(addr, _)| *addr == s)
143            .map(|(_, aliases)| aliases.clone())
144            .unwrap_or(NonEmpty::new(s))
145    });
146    for s in required_signer_alias_sets {
147        if !s.iter().any(|s| present_sigs.contains_key(s)) {
148            return Err(SuiErrorKind::SignerSignatureAbsent {
149                expected: s
150                    .iter()
151                    .map(|s| s.to_string())
152                    .collect::<Vec<_>>()
153                    .join(" or "),
154                actual: present_sigs.keys().map(|s| s.to_string()).collect(),
155            }
156            .into());
157        }
158    }
159
160    // 4. Every signature must be valid.
161    for (signer, signature) in present_sigs {
162        signature.verify_authenticator(
163            intent_message,
164            signer,
165            current_epoch,
166            verify_params,
167            zklogin_inputs_cache.clone(),
168        )?;
169    }
170    Ok(())
171}