sui_types/
signature_verification.rs1use 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
19const VERIFIED_CERTIFICATE_CACHE_SIZE: usize = 100_000;
24
25pub struct VerifiedDigestCache<D, V = ()> {
26 inner: RwLock<LruCache<D, V>>,
27 cache_hits_counter: IntCounter,
28 cache_misses_counter: IntCounter,
29 cache_evictions_counter: IntCounter,
30}
31
32impl<D: Hash + Eq + Copy, V: Clone> VerifiedDigestCache<D, V> {
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 get_cached(&self, digest: &D) -> Option<V> {
61 let inner = self.inner.read();
62 if let Some(value) = inner.peek(digest) {
63 self.cache_hits_counter.inc();
64 Some(value.clone())
65 } else {
66 self.cache_misses_counter.inc();
67 None
68 }
69 }
70
71 pub fn cache_with_value(&self, digest: D, value: V) {
72 let mut inner = self.inner.write();
73 if let Some(old) = inner.push(digest, value)
74 && old.0 != digest
75 {
76 self.cache_evictions_counter.inc();
77 }
78 }
79
80 pub fn clear(&self) {
81 let mut inner = self.inner.write();
82 inner.clear();
83 }
84
85 pub fn new_empty() -> Self {
87 Self::new(
88 IntCounter::new("test_cache_hits", "test cache hits").unwrap(),
89 IntCounter::new("test_cache_misses", "test cache misses").unwrap(),
90 IntCounter::new("test_cache_evictions", "test cache evictions").unwrap(),
91 )
92 }
93}
94
95impl<D: Hash + Eq + Copy> VerifiedDigestCache<D, ()> {
96 pub fn cache_digest(&self, digest: D) {
97 self.cache_with_value(digest, ())
98 }
99
100 pub fn cache_digests(&self, digests: Vec<D>) {
101 let mut inner = self.inner.write();
102 digests.into_iter().for_each(|d| {
103 if let Some(old) = inner.push(d, ())
104 && old.0 != d
105 {
106 self.cache_evictions_counter.inc();
107 }
108 });
109 }
110
111 pub fn is_verified<F, G>(&self, digest: D, verify_callback: F, uncached_checks: G) -> SuiResult
112 where
113 F: FnOnce() -> SuiResult,
114 G: FnOnce() -> SuiResult,
115 {
116 if !self.is_cached(&digest) {
117 verify_callback()?;
118 self.cache_digest(digest);
119 } else {
120 uncached_checks()?;
122 }
123 Ok(())
124 }
125}
126
127pub fn verify_sender_signed_data_message_signatures(
131 txn: &SenderSignedData,
132 current_epoch: EpochId,
133 verify_params: &VerifyParams,
134 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
135 aliased_addresses: Vec<(SuiAddress, NonEmpty<SuiAddress>)>,
136) -> SuiResult<Vec<u8>> {
137 let intent_message = txn.intent_message();
138 assert_eq!(intent_message.intent, Intent::sui_transaction());
139
140 let required_signers = txn.intent_message().value.required_signers();
142 fp_ensure!(
143 txn.inner().tx_signatures.len() == required_signers.len(),
144 SuiErrorKind::SignerSignatureNumberMismatch {
145 actual: txn.inner().tx_signatures.len(),
146 expected: required_signers.len()
147 }
148 .into()
149 );
150
151 if intent_message.value.is_system_tx() {
154 return Ok((0..required_signers.len() as u8).collect());
156 }
157
158 let sig_mapping = txn.get_signer_sig_mapping(verify_params.verify_legacy_zklogin_address)?;
161
162 let mut signer_to_sig_index = Vec::with_capacity(required_signers.len());
163 for signer in required_signers.iter() {
164 let alias_set = aliased_addresses
165 .iter()
166 .find(|(addr, _)| *addr == *signer)
167 .map(|(_, aliases)| aliases.clone())
168 .unwrap_or(NonEmpty::new(*signer));
169
170 let Some(sig_index) = alias_set
172 .iter()
173 .find_map(|alias| sig_mapping.get(alias).map(|(idx, _)| *idx))
174 else {
175 return Err(SuiErrorKind::SignerSignatureAbsent {
176 expected: alias_set
177 .iter()
178 .map(|s| s.to_string())
179 .collect::<Vec<_>>()
180 .join(" or "),
181 actual: sig_mapping.keys().map(|s| s.to_string()).collect(),
182 }
183 .into());
184 };
185 signer_to_sig_index.push(sig_index);
186 }
187
188 for (signer, (_, signature)) in sig_mapping {
190 signature.verify_authenticator(
191 intent_message,
192 signer,
193 current_epoch,
194 verify_params,
195 zklogin_inputs_cache.clone(),
196 )?;
197 }
198 Ok(signer_to_sig_index)
199}