1use std::{collections::BTreeSet, sync::Arc};
6
7use either::Either;
8use fastcrypto::traits::{AggregateAuthenticator, ToFromBytes};
9use futures::pin_mut;
10use iota_metrics::monitored_scope;
11use iota_sdk_types::{
12 CertificateDigest, SenderSignedDataDigest, SenderSignedTransaction, crypto::Intent,
13};
14use iota_types::{
15 base_types::AuthorityName,
16 committee::Committee,
17 crypto::{AuthorityPublicKey, AuthoritySignInfoTrait, VerificationObligation},
18 error::{IotaError, IotaResult},
19 message_envelope::Message,
20 messages_checkpoint::{CheckpointSummaryExt, SignedCheckpointSummary},
21 messages_consensus::{AuthorityCapabilitiesDigest, SignedAuthorityCapabilitiesV1},
22 signature::VerifyParams,
23 signature_verification::{VerifiedDigestCache, verify_sender_signed_data_message_signatures},
24 transaction::{CertifiedTransaction, VerifiedCertificate},
25};
26use itertools::{Itertools as _, izip};
27use parking_lot::{Mutex, MutexGuard};
28use prometheus_filtered::{IntCounter, Registry, register_int_counter_with_registry};
29use tap::TapFallible;
30use tokio::{
31 runtime::Handle,
32 sync::oneshot,
33 time::{Duration, timeout},
34};
35use tracing::{Instrument, instrument, trace_span};
36const BATCH_TIMEOUT_MS: Duration = Duration::from_millis(10);
39
40const MAX_BATCH_SIZE: usize = 8;
46
47type Sender = oneshot::Sender<IotaResult<VerifiedCertificate>>;
48
49struct CertBuffer {
50 certs: Vec<CertifiedTransaction>,
51 senders: Vec<Sender>,
52 id: u64,
53}
54
55impl CertBuffer {
56 fn new(capacity: usize) -> Self {
57 Self {
58 certs: Vec::with_capacity(capacity),
59 senders: Vec::with_capacity(capacity),
60 id: 0,
61 }
62 }
63
64 fn take_and_replace(mut guard: MutexGuard<'_, Self>) -> Self {
67 let this = &mut *guard;
68 let mut new = CertBuffer::new(this.capacity());
69 new.id = this.id + 1;
70 std::mem::swap(&mut new, this);
71 new
72 }
73
74 fn capacity(&self) -> usize {
75 debug_assert_eq!(self.certs.capacity(), self.senders.capacity());
76 self.certs.capacity()
77 }
78
79 fn len(&self) -> usize {
80 debug_assert_eq!(self.certs.len(), self.senders.len());
81 self.certs.len()
82 }
83
84 fn push(&mut self, tx: Sender, cert: CertifiedTransaction) {
85 self.senders.push(tx);
86 self.certs.push(cert);
87 }
88}
89
90pub struct SignatureVerifier {
95 committee: Arc<Committee>,
96 non_committee_validators: BTreeSet<AuthorityName>,
97
98 certificate_cache: VerifiedDigestCache<CertificateDigest>,
99 signed_data_cache: VerifiedDigestCache<SenderSignedDataDigest>,
100 authority_capability_cache: VerifiedDigestCache<AuthorityCapabilitiesDigest>,
101
102 verify_params: VerifyParams,
104
105 queue: Mutex<CertBuffer>,
106 pub metrics: Arc<SignatureVerifierMetrics>,
107}
108
109impl SignatureVerifier {
110 pub fn new_with_batch_size(
111 committee: Arc<Committee>,
112 non_committee_validators: BTreeSet<AuthorityName>,
113 batch_size: usize,
114 metrics: Arc<SignatureVerifierMetrics>,
115 accept_passkey_in_multisig: bool,
116 additional_multisig_checks: bool,
117 ) -> Self {
118 Self {
119 committee,
120 non_committee_validators,
121 certificate_cache: VerifiedDigestCache::new(
122 metrics.certificate_signatures_cache_hits.clone(),
123 metrics.certificate_signatures_cache_misses.clone(),
124 metrics.certificate_signatures_cache_evictions.clone(),
125 ),
126 signed_data_cache: VerifiedDigestCache::new(
127 metrics.signed_data_cache_hits.clone(),
128 metrics.signed_data_cache_misses.clone(),
129 metrics.signed_data_cache_evictions.clone(),
130 ),
131 authority_capability_cache: VerifiedDigestCache::new(
132 metrics.authority_capabilities_cache_hits.clone(),
133 metrics.authority_capabilities_cache_misses.clone(),
134 metrics.authority_capabilities_cache_evictions.clone(),
135 ),
136 queue: Mutex::new(CertBuffer::new(batch_size)),
137 metrics,
138 verify_params: VerifyParams::new(
139 accept_passkey_in_multisig,
140 additional_multisig_checks,
141 ),
142 }
143 }
144
145 pub fn new(
146 committee: Arc<Committee>,
147 non_committee_validators: BTreeSet<AuthorityName>,
148 metrics: Arc<SignatureVerifierMetrics>,
149 accept_passkey_in_multisig: bool,
150 additional_multisig_checks: bool,
151 ) -> Self {
152 Self::new_with_batch_size(
153 committee,
154 non_committee_validators,
155 MAX_BATCH_SIZE,
156 metrics,
157 accept_passkey_in_multisig,
158 additional_multisig_checks,
159 )
160 }
161
162 #[instrument(level = "trace", skip_all)]
164 pub fn verify_certs_and_checkpoints(
165 &self,
166 certs: Vec<&CertifiedTransaction>,
167 checkpoints: Vec<&SignedCheckpointSummary>,
168 authority_capabilities: Vec<&SignedAuthorityCapabilitiesV1>,
169 ) -> IotaResult {
170 for cert in &certs {
173 self.verify_tx(cert.data())?;
174 }
175
176 for cap in &authority_capabilities {
179 self.verify_authority_capabilities(cap)?;
180 }
181
182 batch_verify_all_certificates_and_checkpoints(&self.committee, &certs, &checkpoints)?;
183 Ok(())
184 }
185
186 pub async fn verify_cert(&self, cert: CertifiedTransaction) -> IotaResult<VerifiedCertificate> {
188 let cert_digest = cert.certificate_digest();
189 if self.certificate_cache.is_cached(&cert_digest) {
190 return Ok(VerifiedCertificate::new_unchecked(cert));
191 }
192 self.verify_tx(cert.data())?;
193 self.verify_cert_skip_cache(cert)
194 .await
195 .tap_ok(|_| self.certificate_cache.cache_digest(cert_digest))
196 }
197
198 pub async fn multi_verify_certs(
199 &self,
200 certs: Vec<CertifiedTransaction>,
201 ) -> Vec<IotaResult<VerifiedCertificate>> {
202 let mut futures = Vec::with_capacity(certs.len());
205 for cert in certs {
206 futures.push(self.verify_cert(cert));
207 }
208 futures::future::join_all(futures).await
209 }
210
211 pub async fn verify_cert_skip_cache(
213 &self,
214 cert: CertifiedTransaction,
215 ) -> IotaResult<VerifiedCertificate> {
216 if cert.auth_sig().epoch != self.committee.epoch() {
219 return Err(IotaError::WrongEpoch {
220 expected_epoch: self.committee.epoch(),
221 actual_epoch: cert.auth_sig().epoch,
222 });
223 }
224
225 self.verify_cert_inner(cert).await
226 }
227
228 async fn verify_cert_inner(
229 &self,
230 cert: CertifiedTransaction,
231 ) -> IotaResult<VerifiedCertificate> {
232 let (tx, rx) = oneshot::channel();
237 pin_mut!(rx);
238
239 let prev_id_or_buffer = {
240 let mut queue = self.queue.lock();
241 queue.push(tx, cert);
242 if queue.len() == queue.capacity() {
243 Either::Right(CertBuffer::take_and_replace(queue))
244 } else {
245 Either::Left(queue.id)
246 }
247 };
248 let prev_id = match prev_id_or_buffer {
249 Either::Left(prev_id) => prev_id,
250 Either::Right(buffer) => {
251 self.metrics.full_batches.inc();
252 self.process_queue(buffer)
253 .instrument(trace_span!("SignatureVerifier::process_queue"))
254 .await;
255 return rx.try_recv().unwrap();
257 }
258 };
259
260 if let Ok(res) = timeout(BATCH_TIMEOUT_MS, &mut rx).await {
261 return res.unwrap();
263 }
264 self.metrics.timeouts.inc();
265
266 let buffer = {
267 let queue = self.queue.lock();
268 if prev_id == queue.id {
270 debug_assert_ne!(queue.len(), queue.capacity());
271 Some(CertBuffer::take_and_replace(queue))
272 } else {
273 None
274 }
275 };
276
277 if let Some(buffer) = buffer {
278 self.metrics.partial_batches.inc();
279 self.process_queue(buffer).await;
280 return rx.try_recv().unwrap();
282 }
283
284 rx.await.unwrap()
287 }
288
289 async fn process_queue(&self, buffer: CertBuffer) {
290 let committee = self.committee.clone();
291 let metrics = self.metrics.clone();
292 Handle::current()
293 .spawn_blocking(move || Self::process_queue_sync(committee, metrics, buffer))
294 .await
295 .expect("Spawn blocking should not fail");
296 }
297
298 #[instrument(level = "trace", skip_all)]
299 fn process_queue_sync(
300 committee: Arc<Committee>,
301 metrics: Arc<SignatureVerifierMetrics>,
302 buffer: CertBuffer,
303 ) {
304 let _scope = monitored_scope("BatchCertificateVerifier::process_queue");
305
306 let results = batch_verify_certificates(&committee, &buffer.certs.iter().collect_vec());
307 izip!(
308 results.into_iter(),
309 buffer.certs.into_iter(),
310 buffer.senders.into_iter(),
311 )
312 .for_each(|(result, cert, tx)| {
313 tx.send(match result {
314 Ok(()) => {
315 metrics.total_verified_certs.inc();
316 Ok(VerifiedCertificate::new_unchecked(cert))
317 }
318 Err(e) => {
319 metrics.total_failed_certs.inc();
320 Err(e)
321 }
322 })
323 .ok();
324 });
325 }
326
327 #[instrument(level = "trace", skip_all, fields(tx_digest = ?signed_tx.digest()))]
328 pub fn verify_tx(&self, signed_tx: &SenderSignedTransaction) -> IotaResult {
329 self.signed_data_cache.is_verified(
330 signed_tx.full_message_digest(),
331 || verify_sender_signed_data_message_signatures(signed_tx, &self.verify_params),
332 || Ok(()),
333 )
334 }
335
336 #[instrument(level = "trace", skip_all)]
337 pub fn verify_authority_capabilities(
338 &self,
339 signed_authority_capabilities: &SignedAuthorityCapabilitiesV1,
340 ) -> IotaResult {
341 let epoch = self.committee.epoch();
342 self.authority_capability_cache.is_verified(
343 signed_authority_capabilities.cache_digest(epoch),
344 || {
345 let authority_name = signed_authority_capabilities.data().authority;
347 if !self.non_committee_validators.contains(&authority_name) {
348 return Err(IotaError::IncorrectSigner {
349 error: "Signer must be part of non-committee active validators".to_string(),
350 });
351 }
352
353 let mut obligation = VerificationObligation::default();
355 let idx = obligation.add_message(
356 signed_authority_capabilities.data(),
357 epoch, Intent::iota_app(signed_authority_capabilities.scope()),
360 );
361
362 let authority_key = AuthorityPublicKey::from_bytes(authority_name.as_bytes())
364 .map_err(|_| IotaError::IncorrectSigner {
365 error: "Invalid authority public key bytes".to_string(),
366 })?;
367 obligation
368 .public_keys
369 .get_mut(idx)
370 .ok_or(IotaError::InvalidAuthenticator)?
371 .push(&authority_key);
372
373 obligation
374 .signatures
375 .get_mut(idx)
376 .ok_or(IotaError::InvalidAuthenticator)?
377 .add_signature(signed_authority_capabilities.auth_sig().clone())
378 .map_err(|_| IotaError::InvalidSignature {
379 error: "Failed to add authority signature to obligation".to_string(),
380 })?;
381
382 obligation.verify_all()
383 },
384 || Ok(()),
385 )
386 }
387
388 pub fn clear_signature_cache(&self) {
389 self.certificate_cache.clear();
390 self.authority_capability_cache.clear();
391 self.signed_data_cache.clear();
392 }
393}
394
395pub struct SignatureVerifierMetrics {
396 pub certificate_signatures_cache_hits: IntCounter,
397 pub certificate_signatures_cache_misses: IntCounter,
398 pub certificate_signatures_cache_evictions: IntCounter,
399 pub signed_data_cache_hits: IntCounter,
400 pub signed_data_cache_misses: IntCounter,
401 pub signed_data_cache_evictions: IntCounter,
402 pub authority_capabilities_cache_hits: IntCounter,
403 pub authority_capabilities_cache_misses: IntCounter,
404 pub authority_capabilities_cache_evictions: IntCounter,
405 timeouts: IntCounter,
406 full_batches: IntCounter,
407 partial_batches: IntCounter,
408 total_verified_certs: IntCounter,
409 total_failed_certs: IntCounter,
410}
411
412impl SignatureVerifierMetrics {
413 pub fn new(registry: &Registry) -> Arc<Self> {
414 Arc::new(Self {
415 certificate_signatures_cache_hits: register_int_counter_with_registry!(
416 "certificate_signatures_cache_hits",
417 "Number of certificates which were known to be verified because of signature cache.",
418 registry
419 )
420 .unwrap(),
421 certificate_signatures_cache_misses: register_int_counter_with_registry!(
422 "certificate_signatures_cache_misses",
423 "Number of certificates which missed the signature cache",
424 registry
425 )
426 .unwrap(),
427 certificate_signatures_cache_evictions: register_int_counter_with_registry!(
428 "certificate_signatures_cache_evictions",
429 "Number of times we evict a pre-existing key were known to be verified because of signature cache.",
430 registry
431 )
432 .unwrap(),
433 signed_data_cache_hits: register_int_counter_with_registry!(
434 "signed_data_cache_hits",
435 "Number of signed data which were known to be verified because of signature cache.",
436 registry
437 )
438 .unwrap(),
439 signed_data_cache_misses: register_int_counter_with_registry!(
440 "signed_data_cache_misses",
441 "Number of signed data which missed the signature cache.",
442 registry
443 )
444 .unwrap(),
445 signed_data_cache_evictions: register_int_counter_with_registry!(
446 "signed_data_cache_evictions",
447 "Number of times we evict a pre-existing signed data were known to be verified because of signature cache.",
448 registry
449 )
450 .unwrap(),
451 authority_capabilities_cache_hits: register_int_counter_with_registry!(
452 "authority_capabilities_cache_hits",
453 "Number of authority capabilities which were known to be verified because of capabilities cache.",
454 registry
455 )
456 .unwrap(),
457 authority_capabilities_cache_misses: register_int_counter_with_registry!(
458 "authority_capabilities_cache_misses",
459 "Number of authority capabilities which missed the capabilities cache.",
460 registry
461 )
462 .unwrap(),
463 authority_capabilities_cache_evictions: register_int_counter_with_registry!(
464 "authority_capabilities_cache_evictions",
465 "Number of times we evict a pre-existing authority capabilities that were known to be verified.",
466 registry
467 )
468 .unwrap(),
469 timeouts: register_int_counter_with_registry!(
470 "async_batch_verifier_timeouts",
471 "Number of times batch verifier times out and verifies a partial batch",
472 registry
473 )
474 .unwrap(),
475 full_batches: register_int_counter_with_registry!(
476 "async_batch_verifier_full_batches",
477 "Number of times batch verifier verifies a full batch",
478 registry
479 )
480 .unwrap(),
481 partial_batches: register_int_counter_with_registry!(
482 "async_batch_verifier_partial_batches",
483 "Number of times batch verifier verifies a partial batch",
484 registry
485 )
486 .unwrap(),
487 total_verified_certs: register_int_counter_with_registry!(
488 "async_batch_verifier_total_verified_certs",
489 "Total number of certs batch verifier has verified",
490 registry
491 )
492 .unwrap(),
493 total_failed_certs: register_int_counter_with_registry!(
494 "async_batch_verifier_total_failed_certs",
495 "Total number of certs batch verifier has rejected",
496 registry
497 )
498 .unwrap(),
499 })
500 }
501}
502
503#[instrument(level = "trace", skip_all)]
505pub fn batch_verify_all_certificates_and_checkpoints(
506 committee: &Committee,
507 certs: &[&CertifiedTransaction],
508 checkpoints: &[&SignedCheckpointSummary],
509) -> IotaResult {
510 for ckpt in checkpoints {
513 ckpt.data().verify_epoch(committee.epoch())?;
514 }
515
516 batch_verify(committee, certs, checkpoints)
517}
518
519#[instrument(level = "trace", skip_all)]
522pub fn batch_verify_certificates(
523 committee: &Committee,
524 certs: &[&CertifiedTransaction],
525) -> Vec<IotaResult> {
526 let verify_params = VerifyParams::default();
528 match batch_verify(committee, certs, &[]) {
529 Ok(_) => vec![Ok(()); certs.len()],
530
531 Err(_) if certs.len() > 1 => certs
533 .iter()
534 .map(|c| c.verify_signatures_authenticated(committee, &verify_params))
537 .collect(),
538
539 Err(e) => vec![Err(e)],
540 }
541}
542
543fn batch_verify(
544 committee: &Committee,
545 certs: &[&CertifiedTransaction],
546 checkpoints: &[&SignedCheckpointSummary],
547) -> IotaResult {
548 let mut obligation = VerificationObligation::default();
549
550 for cert in certs {
551 let idx = obligation.add_message(cert.data(), cert.epoch(), Intent::iota_app(cert.scope()));
552 cert.auth_sig()
553 .add_to_verification_obligation(committee, &mut obligation, idx)?;
554 }
555
556 for ckpt in checkpoints {
557 let idx = obligation.add_message(ckpt.data(), ckpt.epoch(), Intent::iota_app(ckpt.scope()));
558 ckpt.auth_sig()
559 .add_to_verification_obligation(committee, &mut obligation, idx)?;
560 }
561
562 obligation.verify_all()
563}