1use std::{
2 any::type_name,
3 collections::{BTreeMap, BTreeSet, HashMap},
4 fmt::Display,
5 hash::Hash,
6 mem,
7 ops::Deref,
8};
9
10use alloy::primitives::U256;
11use hotshot_types::{
12 data::{EpochNumber, ViewNumber},
13 epoch_membership::EpochMembershipCoordinator,
14 message::UpgradeLock,
15 simple_certificate::{
16 Certificate1, Certificate2, SimpleCertificate, Threshold, TimeoutCertificate2,
17 TimeoutCertificate3,
18 },
19 simple_vote::{
20 HasEpoch, LockView, QuorumData2, TimeoutData2, TimeoutLock, Vote2Data, Voteable,
21 },
22 stake_table::StakeTableEntries,
23 traits::{node_implementation::NodeType, signature_key::SignatureKey},
24 vote::{Certificate, HasViewNumber},
25};
26use hotshot_utils::anytrace::{Result, Wrap, bail, ensure};
27use tokio_util::task::JoinMap;
28use tracing::{error, warn};
29
30use crate::{
31 helpers::ViewEpoch,
32 message::{EpochChangeMessage, ReVoteMessage, Unchecked, Validated},
33};
34
35#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct ValidCert<C> {
37 cert: C,
38 epoch: EpochNumber,
39}
40
41impl<C> ValidCert<C> {
42 pub(crate) fn new(cert: C, epoch: EpochNumber) -> Self {
43 Self { cert, epoch }
44 }
45
46 pub fn cert(&self) -> &C {
47 &self.cert
48 }
49
50 pub fn epoch(&self) -> EpochNumber {
51 self.epoch
52 }
53
54 pub fn into_cert(self) -> C {
55 self.cert
56 }
57
58 pub fn map<D, F>(self, f: F) -> ValidCert<D>
59 where
60 F: FnOnce(C) -> D,
61 {
62 ValidCert {
63 cert: f(self.cert),
64 epoch: self.epoch,
65 }
66 }
67}
68
69impl<C> Deref for ValidCert<C> {
70 type Target = C;
71
72 fn deref(&self) -> &Self::Target {
73 self.cert()
74 }
75}
76
77impl<C: HasViewNumber> HasViewNumber for ValidCert<C> {
78 fn view_number(&self) -> ViewNumber {
79 self.cert.view_number()
80 }
81}
82
83pub trait Verifiable<T: NodeType>: HasViewNumber + HasEpoch + Sized {
84 type Key: Copy + Ord + Hash + Display + Send + Sync + 'static;
86
87 type Output: Send + 'static;
88
89 fn key(&self) -> Option<Self::Key>;
90
91 fn epochs(&self) -> impl Iterator<Item = EpochNumber> {
95 self.epoch().into_iter()
96 }
97
98 fn check(
99 self,
100 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
101 threshold: U256,
102 epoch_height: u64,
103 upgrade_lock: &UpgradeLock<T>,
104 memberships: &EpochMembershipCoordinator<T>,
105 ) -> Result<Self::Output>;
106}
107
108impl<T, D, V> Verifiable<T> for SimpleCertificate<T, D, V>
109where
110 T: NodeType,
111 D: Voteable<T> + HasEpoch + Meta + 'static,
112 V: Threshold<T>,
113 Self: Certificate<T, D> + Send + 'static,
114{
115 type Key = ViewEpoch;
116 type Output = Self;
117
118 fn key(&self) -> Option<ViewEpoch> {
119 Some(ViewEpoch(self.view_number(), self.epoch()?))
120 }
121
122 fn check(
123 self,
124 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
125 threshold: U256,
126 epoch_height: u64,
127 upgrade_lock: &UpgradeLock<T>,
128 _: &EpochMembershipCoordinator<T>,
129 ) -> Result<Self> {
130 ensure! {
131 self.data.consistent_with(self.view_number(), epoch_height),
132 "certificate data is inconsistent"
133 }
134 if self.view_number() == ViewNumber::genesis() && D::UNSIGNED_AT_GENESIS_ALLOWED {
135 return Ok(self);
136 }
137 verify_signatures(&self, stake_table, threshold, upgrade_lock)?;
138 Ok(self)
139 }
140}
141
142trait Meta {
144 const UNSIGNED_AT_GENESIS_ALLOWED: bool = false;
145
146 fn consistent_with(&self, view: ViewNumber, epoch_height: u64) -> bool;
152}
153
154impl<T: NodeType> Meta for QuorumData2<T> {
155 const UNSIGNED_AT_GENESIS_ALLOWED: bool = true;
159
160 fn consistent_with(&self, _: ViewNumber, epoch_height: u64) -> bool {
161 self.is_well_formed(epoch_height)
162 }
163}
164
165impl<T: NodeType> Meta for Vote2Data<T> {
166 fn consistent_with(&self, _: ViewNumber, epoch_height: u64) -> bool {
167 self.is_well_formed(epoch_height)
168 }
169}
170
171impl Meta for TimeoutData2 {
172 fn consistent_with(&self, view: ViewNumber, _: u64) -> bool {
173 self.view == view
174 }
175}
176
177pub(crate) fn verify_signatures<T, D, V>(
181 cert: &SimpleCertificate<T, D, V>,
182 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
183 threshold: U256,
184 upgrade_lock: &UpgradeLock<T>,
185) -> Result<()>
186where
187 T: NodeType,
188 D: Voteable<T> + 'static,
189 V: Threshold<T>,
190 SimpleCertificate<T, D, V>: Certificate<T, D>,
191{
192 let Some(signatures) = cert.signatures.as_ref() else {
193 bail!("certificate has no signatures");
194 };
195 let params = <T::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
196 let commit = cert.data_commitment(upgrade_lock)?;
197 <T::SignatureKey as SignatureKey>::check(¶ms, commit.as_ref(), signatures).wrap()
198}
199
200impl<T: NodeType> Verifiable<T> for EpochChangeMessage<T, Unchecked> {
201 type Key = EpochNumber;
202 type Output = EpochChangeMessage<T, Validated>;
203
204 fn key(&self) -> Option<EpochNumber> {
205 self.epoch()
206 }
207
208 fn check(
209 self,
210 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
211 threshold: U256,
212 epoch_height: u64,
213 upgrade_lock: &UpgradeLock<T>,
214 _: &EpochMembershipCoordinator<T>,
215 ) -> Result<Self::Output> {
216 self.well_formed(epoch_height).wrap()?;
217 verify_signatures(&self.cert1, stake_table, threshold, upgrade_lock)?;
218 verify_signatures(&self.cert2, stake_table, threshold, upgrade_lock)?;
219 Ok(self.into_validated())
220 }
221}
222
223impl<T: NodeType> Verifiable<T> for TimeoutCertificate3<T> {
224 type Key = ViewNumber;
225 type Output = Self;
226
227 fn key(&self) -> Option<ViewNumber> {
228 Some(self.view_number())
229 }
230
231 fn epochs(&self) -> impl Iterator<Item = EpochNumber> {
234 std::iter::once(self.epoch).chain(self.timeout_lock().verified_in())
235 }
236
237 fn check(
238 self,
239 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
240 threshold: U256,
241 epoch_height: u64,
242 upgrade_lock: &UpgradeLock<T>,
243 memberships: &EpochMembershipCoordinator<T>,
244 ) -> Result<Self> {
245 verify_timeout3(
246 &self,
247 stake_table,
248 threshold,
249 epoch_height,
250 upgrade_lock,
251 memberships,
252 )?;
253 Ok(self)
254 }
255}
256
257impl<T: NodeType> Verifiable<T> for ReVoteMessage<T, Unchecked> {
258 type Key = ViewNumber;
259 type Output = ReVoteMessage<T, Validated>;
260
261 fn key(&self) -> Option<ViewNumber> {
262 Some(self.view_number())
263 }
264
265 fn epochs(&self) -> impl Iterator<Item = EpochNumber> {
269 let lock = self
270 .revote
271 .timeout
272 .as_ref()
273 .and_then(|tc| tc.timeout_lock().verified_in());
274 std::iter::once(self.revote.epoch).chain(lock)
275 }
276
277 fn check(
278 self,
279 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
280 threshold: U256,
281 epoch_height: u64,
282 upgrade_lock: &UpgradeLock<T>,
283 memberships: &EpochMembershipCoordinator<T>,
284 ) -> Result<Self::Output> {
285 let revote = &self.revote;
286 ensure! {
287 upgrade_lock.certificate_rule(revote.view),
288 "re-vote request for view {} before the certificate rule", revote.view
289 }
290 revote.well_formed(epoch_height).wrap()?;
291 let leader = memberships
292 .membership_for_epoch(Some(revote.epoch))
293 .wrap()?
294 .leader(revote.view)?;
295 ensure! {
296 self.signed_by(&leader),
297 "re-vote request not signed by its leader"
298 }
299 verify_signatures(&revote.cert1, stake_table, threshold, upgrade_lock)?;
300 if let Some(timeout) = &revote.timeout {
301 verify_timeout3(
302 timeout,
303 stake_table,
304 threshold,
305 epoch_height,
306 upgrade_lock,
307 memberships,
308 )?;
309 }
310 Ok(self.into_validated())
311 }
312}
313
314pub(crate) fn verify_timeout3<T: NodeType>(
318 tc: &TimeoutCertificate3<T>,
319 stake_table: &[<T::SignatureKey as SignatureKey>::StakeTableEntry],
320 threshold: U256,
321 epoch_height: u64,
322 upgrade_lock: &UpgradeLock<T>,
323 memberships: &EpochMembershipCoordinator<T>,
324) -> Result<()> {
325 ensure! {
326 upgrade_lock.timeout_epoch_bound(tc.view_number()),
327 "timeout certificate for view {} must not bind its epoch", tc.view_number()
328 }
329 tc.check_signatures(stake_table, threshold, upgrade_lock)?;
330 let TimeoutLock::Recent(cert) = tc.timeout_lock() else {
331 return Ok(());
332 };
333 ensure! {
334 cert.data.is_well_formed(epoch_height),
335 "timeout certificate lock is not well formed"
336 }
337 let epoch = LockView::of(cert).epoch;
338 let membership = memberships.membership_for_epoch(Some(epoch)).wrap()?;
339 let entries = StakeTableEntries::from_iter(membership.stake_table()).0;
340 verify_signatures(cert, &entries, membership.success_threshold(), upgrade_lock)
341}
342
343pub struct CertVerifier<T: NodeType, C: Verifiable<T>> {
357 tasks: JoinMap<C::Key, Option<ValidCert<C::Output>>>,
358 pending_task: BTreeMap<C::Key, HashMap<T::SignatureKey, C>>,
359 pending_membership: BTreeMap<C::Key, HashMap<T::SignatureKey, C>>,
360 completed: BTreeSet<C::Key>,
361 lower_bound: Option<C::Key>,
362 membership: EpochMembershipCoordinator<T>,
363 upgrade_lock: UpgradeLock<T>,
364 invalid_certs: u64,
365}
366
367impl<T: NodeType, C: Verifiable<T> + Send + 'static> CertVerifier<T, C> {
368 pub fn new(membership: EpochMembershipCoordinator<T>, upgrade_lock: UpgradeLock<T>) -> Self {
369 Self {
370 tasks: JoinMap::new(),
371 pending_task: BTreeMap::new(),
372 pending_membership: BTreeMap::new(),
373 completed: BTreeSet::new(),
374 lower_bound: None,
375 membership,
376 upgrade_lock,
377 invalid_certs: 0,
378 }
379 }
380
381 pub fn verify(&mut self, sender: T::SignatureKey, cert: C) -> Option<EpochNumber> {
385 let Some(key) = cert.key() else {
386 warn!(cert = type_name::<C>(), "certificate has no key");
387 return None;
388 };
389
390 let Some(epoch) = cert.epoch() else {
391 warn!(%key, cert = type_name::<C>(), "certificate has no epoch number");
392 return None;
393 };
394
395 if self.is_stale(key) || self.completed.contains(&key) {
396 return None;
397 }
398
399 if let Some(senders) = self.pending_task.get(&key)
400 && senders.contains_key(&sender)
401 {
402 return None;
403 }
404
405 if let Some(senders) = self.pending_membership.get(&key)
406 && senders.contains_key(&sender)
407 {
408 return None;
409 }
410
411 if self.tasks.contains_key(&key) {
412 self.pending_task
413 .entry(key)
414 .or_default()
415 .insert(sender, cert);
416 return None;
417 }
418
419 let Ok(membership) = self.membership.membership_for_epoch(Some(epoch)) else {
420 self.pending_membership
421 .entry(key)
422 .or_default()
423 .insert(sender, cert);
424 return Some(epoch);
425 };
426 let missing = cert
427 .epochs()
428 .find(|epoch| self.membership.membership_for_epoch(Some(*epoch)).is_err());
429 if let Some(missing) = missing {
430 self.pending_membership
431 .entry(key)
432 .or_default()
433 .insert(sender, cert);
434 return Some(missing);
435 }
436
437 let lock = self.upgrade_lock.clone();
438 let epoch_height = *self.membership.epoch_height();
439 let memberships = self.membership.clone();
440
441 self.tasks.spawn_blocking(key, move || {
442 let entries = StakeTableEntries::from_iter(membership.stake_table()).0;
443 let threshold = membership.success_threshold();
444 match cert.check(&entries, threshold, epoch_height, &lock, &memberships) {
445 Ok(valid) => Some(ValidCert::new(valid, epoch)),
446 Err(err) => {
447 warn!(%key, %epoch, %err, cert = type_name::<C>(), "invalid certificate");
448 None
449 },
450 }
451 });
452
453 None
454 }
455
456 pub fn mark_completed(&mut self, key: C::Key) {
460 if self.is_stale(key) {
461 return;
462 }
463 self.completed.insert(key);
464 self.pending_task.remove(&key);
465 self.pending_membership.remove(&key);
466 self.tasks.abort(&key);
467 }
468
469 pub fn retry_pending(&mut self) -> Vec<EpochNumber> {
474 mem::take(&mut self.pending_membership)
475 .into_values()
476 .flatten()
477 .filter_map(|(sender, cert)| self.verify(sender, cert))
478 .collect()
479 }
480
481 pub async fn next(&mut self) -> Option<ValidCert<C::Output>> {
482 loop {
483 match self.tasks.join_next().await? {
484 (key, Ok(Some(cert))) => {
485 if !self.is_stale(key) {
486 self.completed.insert(key);
487 self.pending_task.remove(&key);
488 self.pending_membership.remove(&key);
489 return Some(cert);
490 }
491 },
492 (key, Ok(None)) => {
493 self.invalid_certs += 1;
494 if !self.is_stale(key)
495 && let Some((sender, cert)) = self.next_pending_sender(key)
496 {
497 self.verify(sender, cert);
498 }
499 },
500 (key, Err(err)) => {
501 if err.is_panic() {
502 error!(%key, %err, cert = type_name::<C>(), "cert verification task panic");
503 }
504 if !self.is_stale(key)
505 && let Some((sender, cert)) = self.next_pending_sender(key)
506 {
507 self.verify(sender, cert);
508 }
509 },
510 }
511 }
512 }
513
514 pub fn gc(&mut self, key: C::Key) {
515 self.completed = self.completed.split_off(&key);
516 self.pending_task = self.pending_task.split_off(&key);
517 self.pending_membership = self.pending_membership.split_off(&key);
518 self.lower_bound = Some(key);
519 self.tasks.abort_matching(|k| *k < key);
520 }
521
522 pub fn num_invalid_certs(&self) -> u64 {
523 self.invalid_certs
524 }
525
526 fn next_pending_sender(&mut self, k: C::Key) -> Option<(T::SignatureKey, C)> {
527 let map = self.pending_task.get_mut(&k)?;
528 let sender = map.keys().next().cloned()?;
529 let cert = map.remove(&sender)?;
530 if map.is_empty() {
531 self.pending_task.remove(&k);
532 }
533 Some((sender, cert))
534 }
535
536 fn is_stale(&self, key: C::Key) -> bool {
537 self.lower_bound.is_some_and(|lb| key < lb)
538 }
539}
540
541#[derive(Clone, Copy, Debug)]
543pub enum Completion {
544 PerView,
545 PerViewAndEpoch,
546}
547
548type CompletionKey = (ViewNumber, EpochNumber);
551
552type VerifyOutcome<O> = (CompletionKey, Option<ValidCert<O>>);
554
555pub struct CertBySenderVerifier<T: NodeType, C: Verifiable<T>> {
574 tasks: JoinMap<T::SignatureKey, VerifyOutcome<C::Output>>,
575 in_flight: BTreeMap<CompletionKey, T::SignatureKey>,
580 parked: BTreeMap<CompletionKey, HashMap<T::SignatureKey, C>>,
582 pending: HashMap<T::SignatureKey, C>,
583 completed: BTreeSet<CompletionKey>,
584 completion: Completion,
585 lower_bound: ViewNumber,
586 membership: EpochMembershipCoordinator<T>,
587 upgrade_lock: UpgradeLock<T>,
588 invalid_certs: u64,
589}
590
591impl<T: NodeType, C: Verifiable<T> + Send + 'static> CertBySenderVerifier<T, C>
592where
593 C::Output: HasViewNumber,
594{
595 pub fn new(
596 membership: EpochMembershipCoordinator<T>,
597 upgrade_lock: UpgradeLock<T>,
598 completion: Completion,
599 ) -> Self {
600 Self {
601 tasks: JoinMap::new(),
602 in_flight: BTreeMap::new(),
603 parked: BTreeMap::new(),
604 pending: HashMap::new(),
605 completed: BTreeSet::new(),
606 completion,
607 lower_bound: ViewNumber::genesis(),
608 membership,
609 upgrade_lock,
610 invalid_certs: 0,
611 }
612 }
613
614 pub fn verify(&mut self, sender: T::SignatureKey, cert: C) -> Option<EpochNumber> {
621 let view = cert.view_number();
622
623 if view < self.lower_bound {
624 return None;
625 }
626
627 let Some(epoch) = cert.epoch() else {
628 warn!(%view, cert = type_name::<C>(), "received certificate has no epoch number");
629 return None;
630 };
631
632 let key = self.completion_key(view, epoch);
633 if self.completed.contains(&key) {
634 return None;
635 }
636
637 if let Some(in_flight_sender) = self.in_flight.get(&key) {
638 if *in_flight_sender != sender {
639 self.parked.entry(key).or_default().insert(sender, cert);
640 }
641 return None;
642 }
643
644 if self.tasks.contains_key(&sender) {
645 return None;
646 }
647
648 let Ok(membership) = self.membership.membership_for_epoch(Some(epoch)) else {
649 self.pending.insert(sender, cert);
650 return Some(epoch);
651 };
652 let missing = cert
653 .epochs()
654 .find(|epoch| self.membership.membership_for_epoch(Some(*epoch)).is_err());
655 if let Some(missing) = missing {
656 self.pending.insert(sender, cert);
657 return Some(missing);
658 }
659
660 let lock = self.upgrade_lock.clone();
661 let epoch_height = *self.membership.epoch_height();
662 let memberships = self.membership.clone();
663
664 self.in_flight.insert(key, sender.clone());
665 self.tasks.spawn_blocking(sender, move || {
666 let entries = StakeTableEntries::from_iter(membership.stake_table()).0;
667 let threshold = membership.success_threshold();
668 match cert.check(&entries, threshold, epoch_height, &lock, &memberships) {
669 Ok(valid) => (key, Some(ValidCert::new(valid, epoch))),
670 Err(err) => {
671 warn!(%view, %epoch, %err, cert = type_name::<C>(), "invalid certificate");
672 (key, None)
673 },
674 }
675 });
676
677 None
678 }
679
680 pub fn mark_completed(&mut self, view: ViewNumber, epoch: EpochNumber) {
684 if view < self.lower_bound {
685 return;
686 }
687 let key = self.completion_key(view, epoch);
688 self.completed.insert(key);
689 let and_epoch = matches!(self.completion, Completion::PerViewAndEpoch);
690 self.pending
691 .retain(|_, c| c.view_number() != view || (and_epoch && c.epoch() != Some(epoch)));
692 self.parked.remove(&key);
693 if let Some(sender) = self.in_flight.remove(&key) {
694 self.tasks.abort(&sender);
695 }
696 }
697
698 pub fn retry_pending(&mut self) -> Vec<EpochNumber> {
702 mem::take(&mut self.pending)
703 .into_iter()
704 .filter_map(|(sender, cert)| self.verify(sender, cert))
705 .collect()
706 }
707
708 pub async fn next(&mut self) -> Option<ValidCert<C::Output>> {
709 loop {
710 match self.tasks.join_next().await? {
711 (_, Ok((key, Some(cert)))) => {
712 self.in_flight.remove(&key);
713 self.parked.remove(&key);
714 let (view, _) = key;
715 if view >= self.lower_bound && self.completed.insert(key) {
716 return Some(cert);
717 }
718 },
719 (_, Ok((key, None))) => {
720 self.invalid_certs += 1;
721 self.in_flight.remove(&key);
722 self.promote_parked(key);
723 },
724 (sender, Err(err)) => {
725 if err.is_cancelled() {
729 continue;
730 }
731 if err.is_panic() {
732 error!(?sender, %err, cert = type_name::<C>(), "cert verification task panic");
733 }
734 let key = self
736 .in_flight
737 .iter()
738 .find_map(|(key, s)| (*s == sender).then_some(*key));
739 if let Some(key) = key {
740 self.in_flight.remove(&key);
741 self.promote_parked(key);
742 }
743 },
744 }
745 }
746 }
747
748 pub fn gc(&mut self, view: ViewNumber) {
749 let floor = (view, EpochNumber::new(0));
750 self.completed = self.completed.split_off(&floor);
751 self.parked = self.parked.split_off(&floor);
752 self.pending.retain(|_, c| c.view_number() >= view);
753 let live = self.in_flight.split_off(&floor);
754 for sender in self.in_flight.values() {
755 self.tasks.abort(sender);
756 }
757 self.in_flight = live;
758 self.lower_bound = view;
759 }
760
761 fn completion_key(&self, view: ViewNumber, epoch: EpochNumber) -> CompletionKey {
763 match self.completion {
764 Completion::PerView => (view, EpochNumber::new(0)),
765 Completion::PerViewAndEpoch => (view, epoch),
766 }
767 }
768
769 pub fn num_invalid_certs(&self) -> u64 {
770 self.invalid_certs
771 }
772
773 fn promote_parked(&mut self, key: CompletionKey) {
784 let (view, _) = key;
785 if view < self.lower_bound || self.completed.contains(&key) {
786 self.parked.remove(&key);
787 return;
788 }
789 while let Some((sender, cert)) = self.next_parked_sender(key) {
790 self.verify(sender, cert);
791 if self.in_flight.contains_key(&key) {
792 return;
793 }
794 }
795 }
796
797 fn next_parked_sender(&mut self, key: CompletionKey) -> Option<(T::SignatureKey, C)> {
798 use rand::seq::IteratorRandom;
799
800 let map = self.parked.get_mut(&key)?;
801 let sender = map.keys().choose(&mut rand::thread_rng()).cloned()?;
802 let cert = map.remove(&sender)?;
803 if map.is_empty() {
804 self.parked.remove(&key);
805 }
806 Some((sender, cert))
807 }
808}
809
810pub struct CertVerifiers<T: NodeType> {
812 pub cert1: CertVerifier<T, Certificate1<T>>,
813 pub cert2: CertVerifier<T, Certificate2<T>>,
814 pub timeout: CertBySenderVerifier<T, TimeoutCertificate2<T>>,
815 pub timeout3: CertBySenderVerifier<T, TimeoutCertificate3<T>>,
816 pub advance: CertBySenderVerifier<T, Certificate1<T>>,
817 pub epoch_change: CertVerifier<T, EpochChangeMessage<T, Unchecked>>,
818 pub revote: CertVerifier<T, ReVoteMessage<T, Unchecked>>,
819}
820
821impl<T: NodeType> CertVerifiers<T> {
822 pub fn new(membership: EpochMembershipCoordinator<T>, upgrade_lock: UpgradeLock<T>) -> Self {
823 Self {
824 cert1: CertVerifier::new(membership.clone(), upgrade_lock.clone()),
825 cert2: CertVerifier::new(membership.clone(), upgrade_lock.clone()),
826 timeout: CertBySenderVerifier::new(
827 membership.clone(),
828 upgrade_lock.clone(),
829 Completion::PerView,
830 ),
831 timeout3: CertBySenderVerifier::new(
832 membership.clone(),
833 upgrade_lock.clone(),
834 Completion::PerViewAndEpoch,
835 ),
836 advance: CertBySenderVerifier::new(
837 membership.clone(),
838 upgrade_lock.clone(),
839 Completion::PerViewAndEpoch,
840 ),
841 epoch_change: CertVerifier::new(membership.clone(), upgrade_lock.clone()),
842 revote: CertVerifier::new(membership, upgrade_lock),
843 }
844 }
845
846 pub fn retry_pending<F>(&mut self, mut request: F)
847 where
848 F: FnMut(EpochNumber),
849 {
850 for epoch in self.cert1.retry_pending() {
851 request(epoch);
852 }
853 for epoch in self.cert2.retry_pending() {
854 request(epoch);
855 }
856 for epoch in self.timeout.retry_pending() {
857 request(epoch);
858 }
859 for epoch in self.timeout3.retry_pending() {
860 request(epoch);
861 }
862 for epoch in self.advance.retry_pending() {
863 request(epoch);
864 }
865 for epoch in self.epoch_change.retry_pending() {
866 request(epoch);
867 }
868 for epoch in self.revote.retry_pending() {
869 request(epoch);
870 }
871 }
872
873 pub fn gc(&mut self, view: ViewNumber, epoch: EpochNumber) {
874 self.cert1.gc(ViewEpoch::start(view));
875 self.cert2.gc(ViewEpoch::start(view));
876 self.timeout.gc(view);
877 self.timeout3.gc(view);
878 self.advance.gc(view);
879 self.epoch_change.gc(epoch);
880 self.revote.gc(view);
881 }
882
883 pub fn num_invalid_certs(&self) -> u64 {
884 self.cert1
885 .num_invalid_certs()
886 .saturating_add(self.cert2.num_invalid_certs())
887 .saturating_add(self.timeout.num_invalid_certs())
888 .saturating_add(self.timeout3.num_invalid_certs())
889 .saturating_add(self.advance.num_invalid_certs())
890 .saturating_add(self.epoch_change.num_invalid_certs())
891 .saturating_add(self.revote.num_invalid_certs())
892 }
893}
894
895#[cfg(test)]
896mod tests {
897 use std::marker::PhantomData;
898
899 use committable::Committable;
900 use hotshot_example_types::node_types::TestTypes;
901 use hotshot_types::{
902 data::{EpochNumber, ViewNumber},
903 signature_key::BLSPubKey,
904 simple_certificate::{TimeoutCertificate2, TimeoutCertificate3, TimeoutEvidence},
905 simple_vote::TimeoutData2,
906 traits::signature_key::SignatureKey as _,
907 vote::HasViewNumber,
908 };
909
910 use super::{CertBySenderVerifier, Completion, CompletionKey};
911 use crate::{
912 helpers::{test_timeout_epoch_lock, test_upgrade_lock},
913 tests::common::utils::{
914 TEST_DRB_RESULT, build_cert1, build_timeout_cert, build_timeout_cert3_with_lock,
915 mock_membership,
916 },
917 };
918
919 fn sender(i: u64) -> BLSPubKey {
920 BLSPubKey::generated_from_seed_indexed([0u8; 32], i).0
921 }
922
923 fn verifier() -> CertBySenderVerifier<TestTypes, TimeoutCertificate2<TestTypes>> {
926 CertBySenderVerifier::new(mock_membership(), test_upgrade_lock(), Completion::PerView)
927 }
928
929 fn verifier3() -> CertBySenderVerifier<TestTypes, TimeoutCertificate3<TestTypes>> {
930 CertBySenderVerifier::new(
931 mock_membership(),
932 test_timeout_epoch_lock(),
933 Completion::PerViewAndEpoch,
934 )
935 }
936
937 fn per_view(view: u64) -> CompletionKey {
939 (ViewNumber::new(view), EpochNumber::new(0))
940 }
941
942 fn valid_tc(view: u64) -> TimeoutCertificate2<TestTypes> {
943 let epoch = EpochNumber::genesis();
944 let membership = mock_membership().membership_for_epoch(Some(epoch)).unwrap();
945 let (pub_key, priv_key) = BLSPubKey::generated_from_seed_indexed([0u8; 32], 0);
946 match build_timeout_cert(
947 ViewNumber::new(view),
948 epoch,
949 &membership,
950 &pub_key,
951 &priv_key,
952 ) {
953 TimeoutEvidence::V2(tc) => tc,
954 TimeoutEvidence::V3(_) => unreachable!("build_timeout_cert forms the unbound form"),
955 }
956 }
957
958 fn invalid_tc(view: u64) -> TimeoutCertificate2<TestTypes> {
960 let mut tc = valid_tc(view);
961 tc.signatures = valid_tc(view + 1).signatures;
962 tc
963 }
964
965 fn junk_tc(view: ViewNumber, epoch: EpochNumber) -> TimeoutCertificate2<TestTypes> {
968 let data = TimeoutData2 {
969 view,
970 epoch: Some(epoch),
971 };
972 TimeoutCertificate2::new(data.clone(), data.commit(), view, None, PhantomData)
973 }
974
975 fn junk_tc3(view: ViewNumber, epoch: EpochNumber) -> TimeoutCertificate3<TestTypes> {
976 let membership = mock_membership().membership_for_epoch(Some(epoch)).unwrap();
978 let mut cert = build_timeout_cert3_with_lock(view + 1, epoch, None, &membership);
979 cert.view_number = view;
980 cert
981 }
982
983 #[tokio::test]
987 async fn certificates_of_two_epochs_at_one_view_are_both_verified() {
988 let coordinator = mock_membership();
989 let mut verifier = super::CertVerifier::<TestTypes, super::Certificate1<TestTypes>>::new(
990 coordinator.clone(),
991 test_upgrade_lock(),
992 );
993 let view = ViewNumber::new(12);
994 let (pk, sk) = BLSPubKey::generated_from_seed_indexed([0u8; 32], 0);
995 let cert = |epoch: u64, block: u64| {
996 let epoch = EpochNumber::new(epoch);
997 let membership = coordinator.membership_for_epoch(Some(epoch)).unwrap();
998 let leaf = committable::RawCommitmentBuilder::new("leaf")
999 .u64(block)
1000 .finalize();
1001 build_cert1(leaf, epoch, block, &membership, view, &pk, &sk)
1002 };
1003 let (older, newer) = (cert(1, 10), cert(2, 11));
1004 assert!(verifier.verify(pk, older.clone()).is_none());
1005 assert_eq!(verifier.next().await.map(|c| c.into_cert()), Some(older));
1006 assert!(verifier.verify(pk, newer.clone()).is_none());
1007 assert_eq!(verifier.next().await.map(|c| c.into_cert()), Some(newer));
1008 }
1009
1010 #[tokio::test]
1014 async fn certificate_with_a_lock_of_an_unknown_epoch_waits() {
1015 let (lock_epoch, epoch) = (EpochNumber::new(3), EpochNumber::new(4));
1016 let known = mock_membership();
1017 for e in [lock_epoch, epoch] {
1018 known.membership().register_epoch(e, TEST_DRB_RESULT);
1019 }
1020 let lock_membership = known.membership_for_epoch(Some(lock_epoch)).unwrap();
1021 let membership = known.membership_for_epoch(Some(epoch)).unwrap();
1022 let (pk, sk) = BLSPubKey::generated_from_seed_indexed([0u8; 32], 0);
1023 let leaf = committable::RawCommitmentBuilder::new("leaf")
1024 .u64(29)
1025 .finalize();
1026 let lock = build_cert1(
1027 leaf,
1028 lock_epoch,
1029 29,
1030 &lock_membership,
1031 ViewNumber::new(30),
1032 &pk,
1033 &sk,
1034 );
1035 let tc = build_timeout_cert3_with_lock(ViewNumber::new(31), epoch, Some(lock), &membership);
1036
1037 let coordinator = mock_membership();
1038 coordinator
1039 .membership()
1040 .register_epoch(epoch, TEST_DRB_RESULT);
1041 assert!(
1042 coordinator.membership_for_epoch(Some(epoch)).is_ok()
1043 && coordinator.membership_for_epoch(Some(lock_epoch)).is_err(),
1044 "setup: the certificate's committee is known, the lock's is not"
1045 );
1046 let mut verifier = CertBySenderVerifier::<TestTypes, TimeoutCertificate3<TestTypes>>::new(
1047 coordinator.clone(),
1048 test_timeout_epoch_lock(),
1049 Completion::PerViewAndEpoch,
1050 );
1051 assert_eq!(verifier.verify(sender(1), tc.clone()), Some(lock_epoch));
1052 assert!(verifier.tasks.is_empty(), "nothing is checked yet");
1053
1054 coordinator
1055 .membership()
1056 .register_epoch(lock_epoch, TEST_DRB_RESULT);
1057 assert!(verifier.retry_pending().is_empty());
1058 let valid = verifier
1059 .next()
1060 .await
1061 .expect("verified once the lock's committee is known");
1062 assert_eq!(valid.into_cert(), tc);
1063 }
1064
1065 #[tokio::test]
1069 async fn test_same_view_copies_are_parked() {
1070 crate::helpers::pin_test_version(versions::NEW_PROTOCOL_VERSION);
1072 let mut verifier = verifier();
1073 let view = ViewNumber::new(1);
1074 let tc = valid_tc(1);
1075
1076 verifier.verify(sender(1), tc.clone());
1077 verifier.verify(sender(2), tc.clone());
1078 verifier.verify(sender(3), tc);
1079 assert_eq!(verifier.tasks.len(), 1);
1080 assert_eq!(verifier.parked.get(&per_view(1)).unwrap().len(), 2);
1081
1082 let cert = verifier.next().await.expect("certificate should verify");
1083 assert_eq!(cert.view_number(), view);
1084 assert!(verifier.parked.is_empty());
1085 assert!(verifier.in_flight.is_empty());
1086 assert!(verifier.next().await.is_none());
1087 }
1088
1089 #[tokio::test]
1091 async fn test_same_sender_resubmit_is_dropped() {
1092 crate::helpers::pin_test_version(versions::NEW_PROTOCOL_VERSION);
1094 let mut verifier = verifier();
1095 let view = ViewNumber::new(1);
1096 let tc = valid_tc(1);
1097
1098 verifier.verify(sender(0), tc.clone());
1099 verifier.verify(sender(0), tc);
1100 assert_eq!(verifier.tasks.len(), 1);
1101 assert!(verifier.parked.is_empty());
1102
1103 let cert = verifier.next().await.expect("certificate should verify");
1104 assert_eq!(cert.view_number(), view);
1105 assert!(verifier.next().await.is_none());
1106 }
1107
1108 #[tokio::test]
1110 async fn test_parked_copy_promoted_after_invalid() {
1111 crate::helpers::pin_test_version(versions::NEW_PROTOCOL_VERSION);
1113 let mut verifier = verifier();
1114 let view = ViewNumber::new(1);
1115
1116 verifier.verify(sender(1), invalid_tc(1));
1117 verifier.verify(sender(2), valid_tc(1));
1118 assert_eq!(verifier.tasks.len(), 1);
1119
1120 let cert = verifier
1121 .next()
1122 .await
1123 .expect("parked copy should be verified");
1124 assert_eq!(cert.view_number(), view);
1125 assert_eq!(verifier.num_invalid_certs(), 1);
1126 }
1127
1128 #[tokio::test]
1130 async fn test_distinct_views_verify_concurrently() {
1131 crate::helpers::pin_test_version(versions::NEW_PROTOCOL_VERSION);
1133 let mut verifier = verifier();
1134
1135 verifier.verify(sender(1), valid_tc(1));
1136 verifier.verify(sender(2), valid_tc(2));
1137 assert_eq!(verifier.tasks.len(), 2);
1138
1139 let mut views = vec![
1140 verifier.next().await.unwrap().view_number(),
1141 verifier.next().await.unwrap().view_number(),
1142 ];
1143 views.sort();
1144 assert_eq!(views, vec![ViewNumber::new(1), ViewNumber::new(2)]);
1145 }
1146
1147 #[tokio::test]
1150 async fn test_relabelled_copy_parks_where_epoch_is_unbound() {
1151 let mut verifier = verifier();
1152 let view = ViewNumber::new(1);
1153 let (ours, theirs) = (EpochNumber::genesis(), EpochNumber::genesis() + 1);
1154
1155 verifier.verify(sender(1), junk_tc(view, ours));
1156 verifier.verify(sender(2), junk_tc(view, theirs));
1157 assert_eq!(verifier.tasks.len(), 1);
1158 assert_eq!(verifier.parked.get(&per_view(1)).unwrap().len(), 1);
1159
1160 assert!(verifier.next().await.is_none());
1162 assert_eq!(verifier.num_invalid_certs(), 2);
1163 assert!(verifier.parked.is_empty());
1164 assert!(verifier.in_flight.is_empty());
1165 }
1166
1167 #[tokio::test]
1170 async fn test_distinct_epochs_verify_concurrently_where_epoch_is_bound() {
1171 let mut verifier = verifier3();
1172 let view = ViewNumber::new(1);
1173 let (old, new) = (EpochNumber::genesis(), EpochNumber::genesis() + 1);
1174
1175 verifier.verify(sender(1), junk_tc3(view, old));
1176 verifier.verify(sender(2), junk_tc3(view, new));
1177 assert_eq!(verifier.tasks.len(), 2);
1178 assert!(verifier.parked.is_empty());
1179 assert_eq!(verifier.in_flight.len(), 2);
1180
1181 assert!(verifier.next().await.is_none());
1182 assert_eq!(verifier.num_invalid_certs(), 2);
1183 }
1184
1185 #[tokio::test]
1188 async fn test_mark_completed_clears_view() {
1189 crate::helpers::pin_test_version(versions::NEW_PROTOCOL_VERSION);
1191 let mut verifier = verifier();
1192 let view = ViewNumber::new(1);
1193
1194 verifier.verify(sender(1), valid_tc(1));
1195 verifier.verify(sender(2), valid_tc(1));
1196 verifier.mark_completed(view, EpochNumber::genesis());
1197 assert!(verifier.parked.is_empty());
1198 assert!(verifier.next().await.is_none());
1199 assert!(verifier.tasks.is_empty());
1202 assert!(verifier.in_flight.is_empty());
1203
1204 verifier.verify(sender(3), valid_tc(1));
1205 assert!(verifier.tasks.is_empty());
1206 }
1207
1208 #[tokio::test]
1211 async fn test_gc_clears_stale_state() {
1212 crate::helpers::pin_test_version(versions::NEW_PROTOCOL_VERSION);
1214 let mut verifier = verifier();
1215 let live = ViewNumber::new(5);
1216
1217 verifier.verify(sender(1), valid_tc(1));
1218 verifier.verify(sender(2), valid_tc(1));
1219 verifier.verify(sender(3), valid_tc(5));
1220 verifier.gc(live);
1221
1222 assert!(verifier.parked.is_empty());
1223 assert_eq!(
1224 verifier.in_flight.keys().copied().collect::<Vec<_>>(),
1225 [per_view(5)]
1226 );
1227
1228 verifier.verify(sender(4), valid_tc(1));
1229 assert!(!verifier.in_flight.contains_key(&per_view(1)));
1230 assert!(!verifier.tasks.contains_key(&sender(4)));
1231
1232 let cert = verifier.next().await.expect("live view should verify");
1233 assert_eq!(cert.view_number(), live);
1234 assert!(verifier.next().await.is_none());
1235 }
1236
1237 #[tokio::test]
1247 async fn completing_one_epoch_leaves_the_other_open() {
1248 let mut verifier = verifier3();
1249 let view = ViewNumber::new(1);
1250 let (old, new) = (EpochNumber::genesis(), EpochNumber::genesis() + 1);
1251 let sender = sender(0);
1252
1253 verifier.mark_completed(view, old);
1254
1255 assert!(verifier.verify(sender, junk_tc3(view, new)).is_none());
1258 assert!(verifier.next().await.is_none());
1259 assert_eq!(verifier.num_invalid_certs(), 1);
1260
1261 assert!(verifier.verify(sender, junk_tc3(view, old)).is_none());
1263 assert!(verifier.next().await.is_none());
1264 assert_eq!(verifier.num_invalid_certs(), 1);
1265 }
1266
1267 #[tokio::test]
1275 async fn completing_a_view_retires_every_epoch_of_it() {
1276 let mut verifier = verifier();
1277 let view = ViewNumber::new(1);
1278 let (ours, theirs) = (EpochNumber::genesis(), EpochNumber::genesis() + 1);
1279 let sender = sender(0);
1280
1281 verifier.mark_completed(view, ours);
1282
1283 for epoch in [ours, theirs] {
1284 assert!(verifier.verify(sender, junk_tc(view, epoch)).is_none());
1285 assert!(verifier.next().await.is_none());
1286 assert_eq!(
1287 verifier.num_invalid_certs(),
1288 0,
1289 "a retired view must not be examined again under {epoch}"
1290 );
1291 }
1292
1293 assert!(verifier.verify(sender, junk_tc(view + 1, theirs)).is_none());
1295 assert!(verifier.next().await.is_none());
1296 assert_eq!(verifier.num_invalid_certs(), 1);
1297 }
1298}