iota_types/
signature_verification.rs1#[cfg(not(target_arch = "wasm32"))]
6use std::hash::Hash;
7
8use iota_sdk_types::crypto::Intent;
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::IntCounter;
16
17use crate::{
18 error::{IotaError, IotaResult},
19 signature::VerifyParams,
20 transaction::{SenderSignedData, TransactionDataAPI},
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: &SenderSignedData,
122 verify_params: &VerifyParams,
123) -> IotaResult {
124 let intent_message = txn.intent_message();
125 assert_eq!(intent_message.intent, Intent::iota_transaction());
126
127 if intent_message.value.is_system_tx() {
131 return Ok(());
132 }
133
134 let signers: NonEmpty<_> = txn.intent_message().value.signers();
136 fp_ensure!(
137 txn.inner().tx_signatures.len() == signers.len(),
138 IotaError::SignerSignatureNumberMismatch {
139 actual: txn.inner().tx_signatures.len(),
140 expected: signers.len()
141 }
142 );
143
144 let present_sigs = txn.get_signer_sig_mapping()?;
146 for s in signers {
147 if !present_sigs.contains_key(&s) {
148 return Err(IotaError::SignerSignatureAbsent {
149 expected: s.to_string(),
150 actual: present_sigs.keys().map(|s| s.to_string()).collect(),
151 });
152 }
153 }
154
155 for (signer, signature) in present_sigs {
157 signature.verify_authenticator(intent_message, signer, verify_params)?;
158 }
159 Ok(())
160}