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