iota_types/
signature_verification.rs1#[cfg(not(target_arch = "wasm32"))]
6use std::hash::Hash;
7
8use iota_sdk_types::SenderSignedTransaction;
9#[cfg(not(target_arch = "wasm32"))]
10use lru::LruCache;
11use nonempty::NonEmpty;
12#[cfg(not(target_arch = "wasm32"))]
13use parking_lot::RwLock;
14#[cfg(not(target_arch = "wasm32"))]
15use prometheus_filtered::IntCounter;
16
17use crate::{
18 error::{IotaError, IotaResult},
19 signature::{AuthenticatorTrait, VerifyParams},
20 transaction::{SenderSignedTransactionAPI, TransactionAPI},
21};
22
23#[cfg(not(target_arch = "wasm32"))]
29const VERIFIED_CERTIFICATE_CACHE_SIZE: usize = 100_000;
30
31#[cfg(not(target_arch = "wasm32"))]
32pub struct VerifiedDigestCache<D> {
33 inner: RwLock<LruCache<D, ()>>,
34 cache_hits_counter: IntCounter,
35 cache_misses_counter: IntCounter,
36 cache_evictions_counter: IntCounter,
37}
38
39#[cfg(not(target_arch = "wasm32"))]
40impl<D: Hash + Eq + Copy> VerifiedDigestCache<D> {
41 pub fn new(
42 cache_hits_counter: IntCounter,
43 cache_misses_counter: IntCounter,
44 cache_evictions_counter: IntCounter,
45 ) -> Self {
46 Self {
47 inner: RwLock::new(LruCache::new(
48 std::num::NonZeroUsize::new(VERIFIED_CERTIFICATE_CACHE_SIZE).unwrap(),
49 )),
50 cache_hits_counter,
51 cache_misses_counter,
52 cache_evictions_counter,
53 }
54 }
55
56 pub fn is_cached(&self, digest: &D) -> bool {
57 let inner = self.inner.read();
58 if inner.contains(digest) {
59 self.cache_hits_counter.inc();
60 true
61 } else {
62 self.cache_misses_counter.inc();
63 false
64 }
65 }
66
67 pub fn cache_digest(&self, digest: D) {
68 let mut inner = self.inner.write();
69 if let Some(old) = inner.push(digest, ()) {
70 if old.0 != digest {
71 self.cache_evictions_counter.inc();
72 }
73 }
74 }
75
76 pub fn cache_digests(&self, digests: Vec<D>) {
77 let mut inner = self.inner.write();
78 digests.into_iter().for_each(|d| {
79 if let Some(old) = inner.push(d, ()) {
80 if old.0 != d {
81 self.cache_evictions_counter.inc();
82 }
83 }
84 });
85 }
86
87 pub fn is_verified<F, G>(&self, digest: D, verify_callback: F, uncached_checks: G) -> IotaResult
88 where
89 F: FnOnce() -> IotaResult,
90 G: FnOnce() -> IotaResult,
91 {
92 if !self.is_cached(&digest) {
93 verify_callback()?;
94 self.cache_digest(digest);
95 } else {
96 uncached_checks()?;
98 }
99 Ok(())
100 }
101
102 pub fn clear(&self) {
103 let mut inner = self.inner.write();
104 inner.clear();
105 }
106
107 pub fn new_empty() -> Self {
110 Self::new(
111 IntCounter::new("test_cache_hits", "test cache hits").unwrap(),
112 IntCounter::new("test_cache_misses", "test cache misses").unwrap(),
113 IntCounter::new("test_cache_evictions", "test cache evictions").unwrap(),
114 )
115 }
116}
117
118pub fn verify_sender_signed_data_message_signatures(
121 txn: &SenderSignedTransaction,
122 verify_params: &VerifyParams,
123) -> IotaResult {
124 let tx = txn.transaction();
125
126 if tx.is_system_tx() {
130 return Ok(());
131 }
132
133 let signers: NonEmpty<_> = tx.signers();
135 fp_ensure!(
136 txn.signatures().len() == signers.len(),
137 IotaError::SignerSignatureNumberMismatch {
138 actual: txn.signatures().len(),
139 expected: signers.len()
140 }
141 );
142
143 let present_sigs = txn.get_signer_sig_mapping()?;
145 for s in signers {
146 if !present_sigs.contains_key(&s) {
147 return Err(IotaError::SignerSignatureAbsent {
148 expected: s.to_string(),
149 actual: present_sigs.keys().map(|s| s.to_string()).collect(),
150 });
151 }
152 }
153
154 let intent_message = txn.intent_message();
156 for (signer, signature) in present_sigs {
157 signature.verify_claims(&intent_message, signer, verify_params)?;
158 }
159 Ok(())
160}