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