iota_types/
signature_verification.rs1use std::{hash::Hash, sync::Arc};
6
7use lru::LruCache;
8use nonempty::NonEmpty;
9use parking_lot::RwLock;
10use prometheus::IntCounter;
11use shared_crypto::intent::Intent;
12
13use crate::{
14 committee::EpochId,
15 digests::ZKLoginInputsDigest,
16 error::{IotaError, IotaResult},
17 signature::VerifyParams,
18 transaction::{SenderSignedData, TransactionDataAPI},
19};
20
21const VERIFIED_CERTIFICATE_CACHE_SIZE: usize = 20000;
27
28pub struct VerifiedDigestCache<D> {
29 inner: RwLock<LruCache<D, ()>>,
30 cache_hits_counter: IntCounter,
31 cache_misses_counter: IntCounter,
32 cache_evictions_counter: IntCounter,
33}
34
35impl<D: Hash + Eq + Copy> VerifiedDigestCache<D> {
36 pub fn new(
37 cache_hits_counter: IntCounter,
38 cache_misses_counter: IntCounter,
39 cache_evictions_counter: IntCounter,
40 ) -> Self {
41 Self {
42 inner: RwLock::new(LruCache::new(
43 std::num::NonZeroUsize::new(VERIFIED_CERTIFICATE_CACHE_SIZE).unwrap(),
44 )),
45 cache_hits_counter,
46 cache_misses_counter,
47 cache_evictions_counter,
48 }
49 }
50
51 pub fn is_cached(&self, digest: &D) -> bool {
52 let inner = self.inner.read();
53 if inner.contains(digest) {
54 self.cache_hits_counter.inc();
55 true
56 } else {
57 self.cache_misses_counter.inc();
58 false
59 }
60 }
61
62 pub fn cache_digest(&self, digest: D) {
63 let mut inner = self.inner.write();
64 if let Some(old) = inner.push(digest, ()) {
65 if old.0 != digest {
66 self.cache_evictions_counter.inc();
67 }
68 }
69 }
70
71 pub fn cache_digests(&self, digests: Vec<D>) {
72 let mut inner = self.inner.write();
73 digests.into_iter().for_each(|d| {
74 if let Some(old) = inner.push(d, ()) {
75 if old.0 != d {
76 self.cache_evictions_counter.inc();
77 }
78 }
79 });
80 }
81
82 pub fn is_verified<F, G>(&self, digest: D, verify_callback: F, uncached_checks: G) -> IotaResult
83 where
84 F: FnOnce() -> IotaResult,
85 G: FnOnce() -> IotaResult,
86 {
87 if !self.is_cached(&digest) {
88 verify_callback()?;
89 self.cache_digest(digest);
90 } else {
91 uncached_checks()?;
93 }
94 Ok(())
95 }
96
97 pub fn clear(&self) {
98 let mut inner = self.inner.write();
99 inner.clear();
100 }
101
102 pub fn new_empty() -> Self {
105 Self::new(
106 IntCounter::new("test_cache_hits", "test cache hits").unwrap(),
107 IntCounter::new("test_cache_misses", "test cache misses").unwrap(),
108 IntCounter::new("test_cache_evictions", "test cache evictions").unwrap(),
109 )
110 }
111}
112
113pub fn verify_sender_signed_data_message_signatures(
116 txn: &SenderSignedData,
117 current_epoch: EpochId,
118 verify_params: &VerifyParams,
119 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
120) -> IotaResult {
121 let intent_message = txn.intent_message();
122 assert_eq!(intent_message.intent, Intent::iota_transaction());
123
124 if intent_message.value.is_system_tx() {
128 return Ok(());
129 }
130
131 let signers: NonEmpty<_> = txn.intent_message().value.signers();
133 fp_ensure!(
134 txn.inner().tx_signatures.len() == signers.len(),
135 IotaError::SignerSignatureNumberMismatch {
136 actual: txn.inner().tx_signatures.len(),
137 expected: signers.len()
138 }
139 );
140
141 let present_sigs = txn.get_signer_sig_mapping()?;
143 for s in signers {
144 if !present_sigs.contains_key(&s) {
145 return Err(IotaError::SignerSignatureAbsent {
146 expected: s.to_string(),
147 actual: present_sigs.keys().map(|s| s.to_string()).collect(),
148 });
149 }
150 }
151
152 for (signer, signature) in present_sigs {
154 signature.verify_authenticator(
155 intent_message,
156 signer,
157 current_epoch,
158 verify_params,
159 zklogin_inputs_cache.clone(),
160 )?;
161 }
162 Ok(())
163}