Skip to main content

commonware_consensus/simplex/
types.rs

1//! Types used in [crate::simplex].
2
3use crate::{
4    simplex::scheme,
5    types::{Epoch, Participant, Round, View},
6    Epochable, Viewable,
7};
8use bytes::{Buf, BufMut};
9use commonware_codec::{varint::UInt, EncodeSize, Error, Read, ReadExt, ReadRangeExt, Write};
10use commonware_cryptography::{
11    certificate::{Attestation, Scheme},
12    Digest, PublicKey,
13};
14use commonware_parallel::Strategy;
15use commonware_utils::N3f1;
16use rand_core::CryptoRngCore;
17use std::{collections::HashSet, fmt::Debug, hash::Hash};
18
19/// Context is a collection of metadata from consensus about a given payload.
20/// It provides information about the current epoch/view and the parent payload that new proposals are built on.
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct Context<D: Digest, P: PublicKey> {
23    /// Current round of consensus.
24    pub round: Round,
25    /// Leader of the current round.
26    pub leader: P,
27    /// Parent the payload is built on.
28    ///
29    /// If there is a gap between the current view and the parent view, the participant
30    /// must possess a nullification for each discarded view to safely vote on the proposed
31    /// payload (any view without a nullification may eventually be finalized and skipping
32    /// it would result in a fork).
33    pub parent: (View, D),
34}
35
36impl<D: Digest, P: PublicKey> Epochable for Context<D, P> {
37    fn epoch(&self) -> Epoch {
38        self.round.epoch()
39    }
40}
41
42impl<D: Digest, P: PublicKey> Viewable for Context<D, P> {
43    fn view(&self) -> View {
44        self.round.view()
45    }
46}
47
48impl<D: Digest, P: PublicKey> Write for Context<D, P> {
49    fn write(&self, buf: &mut impl BufMut) {
50        self.round.write(buf);
51        self.leader.write(buf);
52        self.parent.write(buf);
53    }
54}
55
56impl<D: Digest, P: PublicKey> EncodeSize for Context<D, P> {
57    fn encode_size(&self) -> usize {
58        self.round.encode_size() + self.leader.encode_size() + self.parent.encode_size()
59    }
60}
61
62impl<D: Digest, P: PublicKey> Read for Context<D, P> {
63    type Cfg = ();
64
65    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
66        let round = Round::read(reader)?;
67        let leader = P::read(reader)?;
68        let parent = <(View, D)>::read_cfg(reader, &((), ()))?;
69
70        Ok(Self {
71            round,
72            leader,
73            parent,
74        })
75    }
76}
77
78#[cfg(feature = "arbitrary")]
79impl<D: Digest, P: PublicKey> arbitrary::Arbitrary<'_> for Context<D, P>
80where
81    D: for<'a> arbitrary::Arbitrary<'a>,
82    P: for<'a> arbitrary::Arbitrary<'a>,
83{
84    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
85        Ok(Self {
86            round: Round::arbitrary(u)?,
87            leader: P::arbitrary(u)?,
88            parent: (View::arbitrary(u)?, D::arbitrary(u)?),
89        })
90    }
91}
92
93/// Attributable is a trait that provides access to the signer index.
94/// This is used to identify which participant signed a given message.
95pub trait Attributable {
96    /// Returns the index of the signer (validator) who produced this message.
97    fn signer(&self) -> Participant;
98}
99
100/// A map of [Attributable] items keyed by their signer index.
101///
102/// The key for each item is automatically inferred from [Attributable::signer()].
103/// Each signer can insert at most one item.
104pub struct AttributableMap<T: Attributable> {
105    data: Vec<Option<T>>,
106    added: usize,
107}
108
109impl<T: Attributable> AttributableMap<T> {
110    /// Creates a new [AttributableMap] with the given number of participants.
111    pub fn new(participants: usize) -> Self {
112        // `resize_with` avoids requiring `T: Clone` while pre-filling with `None`.
113        let mut data = Vec::with_capacity(participants);
114        data.resize_with(participants, || None);
115
116        Self { data, added: 0 }
117    }
118
119    /// Clears all existing items from the [AttributableMap].
120    pub fn clear(&mut self) {
121        self.data.fill_with(|| None);
122        self.added = 0;
123    }
124
125    /// Inserts an item into the map, using [Attributable::signer()] as the key,
126    /// if it has not been added yet.
127    ///
128    /// Returns `true` if the item was inserted, `false` if an item from this
129    /// signer already exists or if the signer index is out of bounds.
130    pub fn insert(&mut self, item: T) -> bool {
131        let index: usize = item.signer().into();
132        if index >= self.data.len() {
133            return false;
134        }
135        if self.data[index].is_some() {
136            return false;
137        }
138        self.data[index] = Some(item);
139        self.added += 1;
140        true
141    }
142
143    /// Returns the number of items in the [AttributableMap].
144    pub const fn len(&self) -> usize {
145        self.added
146    }
147
148    /// Returns `true` if the [AttributableMap] is empty.
149    pub const fn is_empty(&self) -> bool {
150        self.added == 0
151    }
152
153    /// Returns a reference to the item associated with the given signer, if present.
154    pub fn get(&self, signer: Participant) -> Option<&T> {
155        self.data.get(<usize>::from(signer))?.as_ref()
156    }
157
158    /// Returns an iterator over items in the map, ordered by signer index
159    /// ([Attributable::signer()]).
160    pub fn iter(&self) -> impl Iterator<Item = &T> {
161        self.data.iter().filter_map(|o| o.as_ref())
162    }
163}
164
165/// Tracks notarize/nullify/finalize votes for a view.
166///
167/// Each vote type is stored in its own [`AttributableMap`] so a validator can only
168/// contribute one vote per phase. The tracker is reused across rounds/views to keep
169/// allocations stable.
170pub struct VoteTracker<S: Scheme, D: Digest> {
171    /// Per-signer notarize votes keyed by validator index.
172    notarizes: AttributableMap<Notarize<S, D>>,
173    /// Per-signer nullify votes keyed by validator index.
174    nullifies: AttributableMap<Nullify<S>>,
175    /// Per-signer finalize votes keyed by validator index.
176    ///
177    /// Finalize votes include the proposal digest so the entire certificate can be
178    /// reconstructed once the quorum threshold is hit.
179    finalizes: AttributableMap<Finalize<S, D>>,
180}
181
182impl<S: Scheme, D: Digest> VoteTracker<S, D> {
183    /// Creates a tracker sized for `participants` validators.
184    pub fn new(participants: usize) -> Self {
185        Self {
186            notarizes: AttributableMap::new(participants),
187            nullifies: AttributableMap::new(participants),
188            finalizes: AttributableMap::new(participants),
189        }
190    }
191
192    /// Inserts a notarize vote if the signer has not already voted.
193    pub fn insert_notarize(&mut self, vote: Notarize<S, D>) -> bool {
194        self.notarizes.insert(vote)
195    }
196
197    /// Inserts a nullify vote if the signer has not already voted.
198    pub fn insert_nullify(&mut self, vote: Nullify<S>) -> bool {
199        self.nullifies.insert(vote)
200    }
201
202    /// Inserts a finalize vote if the signer has not already voted.
203    pub fn insert_finalize(&mut self, vote: Finalize<S, D>) -> bool {
204        self.finalizes.insert(vote)
205    }
206
207    /// Returns the notarize vote for `signer`, if present.
208    pub fn notarize(&self, signer: Participant) -> Option<&Notarize<S, D>> {
209        self.notarizes.get(signer)
210    }
211
212    /// Returns the nullify vote for `signer`, if present.
213    pub fn nullify(&self, signer: Participant) -> Option<&Nullify<S>> {
214        self.nullifies.get(signer)
215    }
216
217    /// Returns the finalize vote for `signer`, if present.
218    pub fn finalize(&self, signer: Participant) -> Option<&Finalize<S, D>> {
219        self.finalizes.get(signer)
220    }
221
222    /// Iterates over notarize votes in signer order.
223    pub fn iter_notarizes(&self) -> impl Iterator<Item = &Notarize<S, D>> {
224        self.notarizes.iter()
225    }
226
227    /// Iterates over nullify votes in signer order.
228    pub fn iter_nullifies(&self) -> impl Iterator<Item = &Nullify<S>> {
229        self.nullifies.iter()
230    }
231
232    /// Iterates over finalize votes in signer order.
233    pub fn iter_finalizes(&self) -> impl Iterator<Item = &Finalize<S, D>> {
234        self.finalizes.iter()
235    }
236
237    /// Returns how many notarize votes have been recorded.
238    pub fn len_notarizes(&self) -> u32 {
239        u32::try_from(self.notarizes.len()).expect("too many notarize votes")
240    }
241
242    /// Returns how many nullify votes have been recorded.
243    pub fn len_nullifies(&self) -> u32 {
244        u32::try_from(self.nullifies.len()).expect("too many nullify votes")
245    }
246
247    /// Returns how many finalize votes have been recorded.
248    pub fn len_finalizes(&self) -> u32 {
249        u32::try_from(self.finalizes.len()).expect("too many finalize votes")
250    }
251
252    /// Returns `true` if the given signer has a notarize vote recorded.
253    pub fn has_notarize(&self, signer: Participant) -> bool {
254        self.notarizes.get(signer).is_some()
255    }
256
257    /// Returns `true` if a nullify vote has been recorded for `signer`.
258    pub fn has_nullify(&self, signer: Participant) -> bool {
259        self.nullifies.get(signer).is_some()
260    }
261
262    /// Returns `true` if a finalize vote has been recorded for `signer`.
263    pub fn has_finalize(&self, signer: Participant) -> bool {
264        self.finalizes.get(signer).is_some()
265    }
266
267    /// Clears all notarize votes but keeps the allocations for reuse.
268    pub fn clear_notarizes(&mut self) {
269        self.notarizes.clear();
270    }
271
272    /// Clears all finalize votes but keeps the allocations for reuse.
273    pub fn clear_finalizes(&mut self) {
274        self.finalizes.clear();
275    }
276}
277
278/// Identifies the subject of a vote or certificate.
279///
280/// Implementations use the subject to derive domain-separated message bytes for both
281/// individual votes and recovered certificates.
282#[derive(Copy, Clone, Debug)]
283pub enum Subject<'a, D: Digest> {
284    /// Subject for notarize votes and certificates, carrying the proposal.
285    Notarize { proposal: &'a Proposal<D> },
286    /// Subject for nullify votes and certificates, scoped to a round.
287    Nullify { round: Round },
288    /// Subject for finalize votes and certificates, carrying the proposal.
289    Finalize { proposal: &'a Proposal<D> },
290}
291
292impl<D: Digest> Viewable for Subject<'_, D> {
293    fn view(&self) -> View {
294        match self {
295            Subject::Notarize { proposal } => proposal.view(),
296            Subject::Nullify { round } => round.view(),
297            Subject::Finalize { proposal } => proposal.view(),
298        }
299    }
300}
301
302/// Vote represents individual votes ([Notarize], [Nullify], [Finalize]).
303#[derive(Clone, Debug, PartialEq)]
304pub enum Vote<S: Scheme, D: Digest> {
305    /// A validator's notarize vote over a proposal.
306    Notarize(Notarize<S, D>),
307    /// A validator's nullify vote used to skip the current view.
308    Nullify(Nullify<S>),
309    /// A validator's finalize vote over a proposal.
310    Finalize(Finalize<S, D>),
311}
312
313impl<S: Scheme, D: Digest> Write for Vote<S, D> {
314    fn write(&self, writer: &mut impl BufMut) {
315        match self {
316            Self::Notarize(v) => {
317                0u8.write(writer);
318                v.write(writer);
319            }
320            Self::Nullify(v) => {
321                1u8.write(writer);
322                v.write(writer);
323            }
324            Self::Finalize(v) => {
325                2u8.write(writer);
326                v.write(writer);
327            }
328        }
329    }
330}
331
332impl<S: Scheme, D: Digest> EncodeSize for Vote<S, D> {
333    fn encode_size(&self) -> usize {
334        1 + match self {
335            Self::Notarize(v) => v.encode_size(),
336            Self::Nullify(v) => v.encode_size(),
337            Self::Finalize(v) => v.encode_size(),
338        }
339    }
340}
341
342impl<S: Scheme, D: Digest> Read for Vote<S, D> {
343    type Cfg = ();
344
345    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
346        let tag = <u8>::read(reader)?;
347        match tag {
348            0 => {
349                let v = Notarize::read(reader)?;
350                Ok(Self::Notarize(v))
351            }
352            1 => {
353                let v = Nullify::read(reader)?;
354                Ok(Self::Nullify(v))
355            }
356            2 => {
357                let v = Finalize::read(reader)?;
358                Ok(Self::Finalize(v))
359            }
360            _ => Err(Error::Invalid("consensus::simplex::Vote", "Invalid type")),
361        }
362    }
363}
364
365impl<S: Scheme, D: Digest> Epochable for Vote<S, D> {
366    fn epoch(&self) -> Epoch {
367        match self {
368            Self::Notarize(v) => v.epoch(),
369            Self::Nullify(v) => v.epoch(),
370            Self::Finalize(v) => v.epoch(),
371        }
372    }
373}
374
375impl<S: Scheme, D: Digest> Viewable for Vote<S, D> {
376    fn view(&self) -> View {
377        match self {
378            Self::Notarize(v) => v.view(),
379            Self::Nullify(v) => v.view(),
380            Self::Finalize(v) => v.view(),
381        }
382    }
383}
384
385#[cfg(feature = "arbitrary")]
386impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Vote<S, D>
387where
388    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
389    D: for<'a> arbitrary::Arbitrary<'a>,
390{
391    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
392        let tag = u.int_in_range(0..=2)?;
393        match tag {
394            0 => {
395                let v = Notarize::arbitrary(u)?;
396                Ok(Self::Notarize(v))
397            }
398            1 => {
399                let v = Nullify::arbitrary(u)?;
400                Ok(Self::Nullify(v))
401            }
402            2 => {
403                let v = Finalize::arbitrary(u)?;
404                Ok(Self::Finalize(v))
405            }
406            _ => unreachable!(),
407        }
408    }
409}
410
411/// Certificate represents aggregated votes ([Notarization], [Nullification], [Finalization]).
412#[derive(Clone, Debug, PartialEq)]
413pub enum Certificate<S: Scheme, D: Digest> {
414    /// A recovered certificate for a notarization.
415    Notarization(Notarization<S, D>),
416    /// A recovered certificate for a nullification.
417    Nullification(Nullification<S>),
418    /// A recovered certificate for a finalization.
419    Finalization(Finalization<S, D>),
420}
421
422impl<S: Scheme, D: Digest> Write for Certificate<S, D> {
423    fn write(&self, writer: &mut impl BufMut) {
424        match self {
425            Self::Notarization(v) => {
426                0u8.write(writer);
427                v.write(writer);
428            }
429            Self::Nullification(v) => {
430                1u8.write(writer);
431                v.write(writer);
432            }
433            Self::Finalization(v) => {
434                2u8.write(writer);
435                v.write(writer);
436            }
437        }
438    }
439}
440
441impl<S: Scheme, D: Digest> EncodeSize for Certificate<S, D> {
442    fn encode_size(&self) -> usize {
443        1 + match self {
444            Self::Notarization(v) => v.encode_size(),
445            Self::Nullification(v) => v.encode_size(),
446            Self::Finalization(v) => v.encode_size(),
447        }
448    }
449}
450
451impl<S: Scheme, D: Digest> Read for Certificate<S, D> {
452    type Cfg = <S::Certificate as Read>::Cfg;
453
454    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
455        let tag = <u8>::read(reader)?;
456        match tag {
457            0 => {
458                let v = Notarization::read_cfg(reader, cfg)?;
459                Ok(Self::Notarization(v))
460            }
461            1 => {
462                let v = Nullification::read_cfg(reader, cfg)?;
463                Ok(Self::Nullification(v))
464            }
465            2 => {
466                let v = Finalization::read_cfg(reader, cfg)?;
467                Ok(Self::Finalization(v))
468            }
469            _ => Err(Error::Invalid(
470                "consensus::simplex::Certificate",
471                "Invalid type",
472            )),
473        }
474    }
475}
476
477impl<S: Scheme, D: Digest> Epochable for Certificate<S, D> {
478    fn epoch(&self) -> Epoch {
479        match self {
480            Self::Notarization(v) => v.epoch(),
481            Self::Nullification(v) => v.epoch(),
482            Self::Finalization(v) => v.epoch(),
483        }
484    }
485}
486
487impl<S: Scheme, D: Digest> Viewable for Certificate<S, D> {
488    fn view(&self) -> View {
489        match self {
490            Self::Notarization(v) => v.view(),
491            Self::Nullification(v) => v.view(),
492            Self::Finalization(v) => v.view(),
493        }
494    }
495}
496
497#[cfg(feature = "arbitrary")]
498impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Certificate<S, D>
499where
500    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
501    D: for<'a> arbitrary::Arbitrary<'a>,
502{
503    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
504        let tag = u.int_in_range(0..=2)?;
505        match tag {
506            0 => {
507                let v = Notarization::arbitrary(u)?;
508                Ok(Self::Notarization(v))
509            }
510            1 => {
511                let v = Nullification::arbitrary(u)?;
512                Ok(Self::Nullification(v))
513            }
514            2 => {
515                let v = Finalization::arbitrary(u)?;
516                Ok(Self::Finalization(v))
517            }
518            _ => unreachable!(),
519        }
520    }
521}
522
523/// Artifact represents all consensus artifacts (votes and certificates) for storage.
524#[derive(Clone, Debug, PartialEq)]
525pub enum Artifact<S: Scheme, D: Digest> {
526    /// A validator's notarize vote over a proposal.
527    Notarize(Notarize<S, D>),
528    /// A recovered certificate for a notarization.
529    Notarization(Notarization<S, D>),
530    /// A notarization was locally certified.
531    Certification(Round, bool),
532    /// A validator's nullify vote used to skip the current view.
533    Nullify(Nullify<S>),
534    /// A recovered certificate for a nullification.
535    Nullification(Nullification<S>),
536    /// A validator's finalize vote over a proposal.
537    Finalize(Finalize<S, D>),
538    /// A recovered certificate for a finalization.
539    Finalization(Finalization<S, D>),
540}
541
542impl<S: Scheme, D: Digest> Write for Artifact<S, D> {
543    fn write(&self, writer: &mut impl BufMut) {
544        match self {
545            Self::Notarize(v) => {
546                0u8.write(writer);
547                v.write(writer);
548            }
549            Self::Notarization(v) => {
550                1u8.write(writer);
551                v.write(writer);
552            }
553            Self::Certification(r, b) => {
554                2u8.write(writer);
555                r.write(writer);
556                b.write(writer);
557            }
558            Self::Nullify(v) => {
559                3u8.write(writer);
560                v.write(writer);
561            }
562            Self::Nullification(v) => {
563                4u8.write(writer);
564                v.write(writer);
565            }
566            Self::Finalize(v) => {
567                5u8.write(writer);
568                v.write(writer);
569            }
570            Self::Finalization(v) => {
571                6u8.write(writer);
572                v.write(writer);
573            }
574        }
575    }
576}
577
578impl<S: Scheme, D: Digest> EncodeSize for Artifact<S, D> {
579    fn encode_size(&self) -> usize {
580        1 + match self {
581            Self::Notarize(v) => v.encode_size(),
582            Self::Notarization(v) => v.encode_size(),
583            Self::Certification(r, b) => r.encode_size() + b.encode_size(),
584            Self::Nullify(v) => v.encode_size(),
585            Self::Nullification(v) => v.encode_size(),
586            Self::Finalize(v) => v.encode_size(),
587            Self::Finalization(v) => v.encode_size(),
588        }
589    }
590}
591
592impl<S: Scheme, D: Digest> Read for Artifact<S, D> {
593    type Cfg = <S::Certificate as Read>::Cfg;
594
595    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
596        let tag = <u8>::read(reader)?;
597        match tag {
598            0 => {
599                let v = Notarize::read(reader)?;
600                Ok(Self::Notarize(v))
601            }
602            1 => {
603                let v = Notarization::read_cfg(reader, cfg)?;
604                Ok(Self::Notarization(v))
605            }
606            2 => {
607                let r = Round::read(reader)?;
608                let b = bool::read(reader)?;
609                Ok(Self::Certification(r, b))
610            }
611            3 => {
612                let v = Nullify::read(reader)?;
613                Ok(Self::Nullify(v))
614            }
615            4 => {
616                let v = Nullification::read_cfg(reader, cfg)?;
617                Ok(Self::Nullification(v))
618            }
619            5 => {
620                let v = Finalize::read(reader)?;
621                Ok(Self::Finalize(v))
622            }
623            6 => {
624                let v = Finalization::read_cfg(reader, cfg)?;
625                Ok(Self::Finalization(v))
626            }
627            _ => Err(Error::Invalid(
628                "consensus::simplex::Artifact",
629                "Invalid type",
630            )),
631        }
632    }
633}
634
635impl<S: Scheme, D: Digest> Epochable for Artifact<S, D> {
636    fn epoch(&self) -> Epoch {
637        match self {
638            Self::Notarize(v) => v.epoch(),
639            Self::Notarization(v) => v.epoch(),
640            Self::Certification(r, _) => r.epoch(),
641            Self::Nullify(v) => v.epoch(),
642            Self::Nullification(v) => v.epoch(),
643            Self::Finalize(v) => v.epoch(),
644            Self::Finalization(v) => v.epoch(),
645        }
646    }
647}
648
649impl<S: Scheme, D: Digest> Viewable for Artifact<S, D> {
650    fn view(&self) -> View {
651        match self {
652            Self::Notarize(v) => v.view(),
653            Self::Notarization(v) => v.view(),
654            Self::Certification(r, _) => r.view(),
655            Self::Nullify(v) => v.view(),
656            Self::Nullification(v) => v.view(),
657            Self::Finalize(v) => v.view(),
658            Self::Finalization(v) => v.view(),
659        }
660    }
661}
662
663impl<S: Scheme, D: Digest> From<Vote<S, D>> for Artifact<S, D> {
664    fn from(vote: Vote<S, D>) -> Self {
665        match vote {
666            Vote::Notarize(v) => Self::Notarize(v),
667            Vote::Nullify(v) => Self::Nullify(v),
668            Vote::Finalize(v) => Self::Finalize(v),
669        }
670    }
671}
672
673impl<S: Scheme, D: Digest> From<Certificate<S, D>> for Artifact<S, D> {
674    fn from(cert: Certificate<S, D>) -> Self {
675        match cert {
676            Certificate::Notarization(v) => Self::Notarization(v),
677            Certificate::Nullification(v) => Self::Nullification(v),
678            Certificate::Finalization(v) => Self::Finalization(v),
679        }
680    }
681}
682
683#[cfg(feature = "arbitrary")]
684impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Artifact<S, D>
685where
686    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
687    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
688    D: for<'a> arbitrary::Arbitrary<'a>,
689{
690    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
691        let tag = u.int_in_range(0..=6)?;
692        match tag {
693            0 => {
694                let v = Notarize::arbitrary(u)?;
695                Ok(Self::Notarize(v))
696            }
697            1 => {
698                let v = Notarization::arbitrary(u)?;
699                Ok(Self::Notarization(v))
700            }
701            2 => {
702                let r = Round::arbitrary(u)?;
703                let b = bool::arbitrary(u)?;
704                Ok(Self::Certification(r, b))
705            }
706            3 => {
707                let v = Nullify::arbitrary(u)?;
708                Ok(Self::Nullify(v))
709            }
710            4 => {
711                let v = Nullification::arbitrary(u)?;
712                Ok(Self::Nullification(v))
713            }
714            5 => {
715                let v = Finalize::arbitrary(u)?;
716                Ok(Self::Finalize(v))
717            }
718            6 => {
719                let v = Finalization::arbitrary(u)?;
720                Ok(Self::Finalization(v))
721            }
722            _ => unreachable!(),
723        }
724    }
725}
726
727/// Proposal represents a proposed block in the protocol.
728/// It includes the view number, the parent view, and the actual payload (typically a digest of block data).
729#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
730pub struct Proposal<D: Digest> {
731    /// The round in which this proposal is made
732    pub round: Round,
733    /// The view of the parent proposal that this one builds upon
734    pub parent: View,
735    /// The actual payload/content of the proposal (typically a digest of the block data)
736    pub payload: D,
737}
738
739impl<D: Digest> Proposal<D> {
740    /// Creates a new proposal with the specified view, parent view, and payload.
741    pub const fn new(round: Round, parent: View, payload: D) -> Self {
742        Self {
743            round,
744            parent,
745            payload,
746        }
747    }
748}
749
750impl<D: Digest> Write for Proposal<D> {
751    fn write(&self, writer: &mut impl BufMut) {
752        self.round.write(writer);
753        self.parent.write(writer);
754        self.payload.write(writer)
755    }
756}
757
758impl<D: Digest> Read for Proposal<D> {
759    type Cfg = ();
760
761    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
762        let round = Round::read(reader)?;
763        let parent = View::read(reader)?;
764        let payload = D::read(reader)?;
765        Ok(Self {
766            round,
767            parent,
768            payload,
769        })
770    }
771}
772
773impl<D: Digest> EncodeSize for Proposal<D> {
774    fn encode_size(&self) -> usize {
775        self.round.encode_size() + self.parent.encode_size() + self.payload.encode_size()
776    }
777}
778
779impl<D: Digest> Epochable for Proposal<D> {
780    fn epoch(&self) -> Epoch {
781        self.round.epoch()
782    }
783}
784
785impl<D: Digest> Viewable for Proposal<D> {
786    fn view(&self) -> View {
787        self.round.view()
788    }
789}
790
791#[cfg(feature = "arbitrary")]
792impl<D: Digest> arbitrary::Arbitrary<'_> for Proposal<D>
793where
794    D: for<'a> arbitrary::Arbitrary<'a>,
795{
796    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
797        let round = Round::arbitrary(u)?;
798        let parent = View::arbitrary(u)?;
799        let payload = D::arbitrary(u)?;
800        Ok(Self {
801            round,
802            parent,
803            payload,
804        })
805    }
806}
807
808/// Validator vote that endorses a proposal for notarization.
809#[derive(Clone, Debug)]
810pub struct Notarize<S: Scheme, D: Digest> {
811    /// Proposal being notarized.
812    pub proposal: Proposal<D>,
813    /// Scheme-specific attestation material.
814    pub attestation: Attestation<S>,
815}
816
817impl<S: Scheme, D: Digest> Notarize<S, D> {
818    /// Signs a notarize vote for the provided proposal.
819    pub fn sign(scheme: &S, proposal: Proposal<D>) -> Option<Self>
820    where
821        S: scheme::Scheme<D>,
822    {
823        let attestation = scheme.sign::<D>(Subject::Notarize {
824            proposal: &proposal,
825        })?;
826
827        Some(Self {
828            proposal,
829            attestation,
830        })
831    }
832
833    /// Verifies the notarize vote against the provided signing scheme.
834    ///
835    /// This ensures that the notarize signature is valid for the claimed proposal.
836    pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
837    where
838        R: CryptoRngCore,
839        S: scheme::Scheme<D>,
840    {
841        scheme.verify_attestation::<_, D>(
842            rng,
843            Subject::Notarize {
844                proposal: &self.proposal,
845            },
846            &self.attestation,
847            strategy,
848        )
849    }
850
851    /// Returns the round associated with this notarize vote.
852    pub const fn round(&self) -> Round {
853        self.proposal.round
854    }
855}
856
857impl<S: Scheme, D: Digest> PartialEq for Notarize<S, D> {
858    fn eq(&self, other: &Self) -> bool {
859        self.proposal == other.proposal && self.attestation == other.attestation
860    }
861}
862
863impl<S: Scheme, D: Digest> Eq for Notarize<S, D> {}
864
865impl<S: Scheme, D: Digest> Hash for Notarize<S, D> {
866    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
867        self.proposal.hash(state);
868        self.attestation.hash(state);
869    }
870}
871
872impl<S: Scheme, D: Digest> Write for Notarize<S, D> {
873    fn write(&self, writer: &mut impl BufMut) {
874        self.proposal.write(writer);
875        self.attestation.write(writer);
876    }
877}
878
879impl<S: Scheme, D: Digest> EncodeSize for Notarize<S, D> {
880    fn encode_size(&self) -> usize {
881        self.proposal.encode_size() + self.attestation.encode_size()
882    }
883}
884
885impl<S: Scheme, D: Digest> Read for Notarize<S, D> {
886    type Cfg = ();
887
888    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
889        let proposal = Proposal::read(reader)?;
890        let attestation = Attestation::read(reader)?;
891
892        Ok(Self {
893            proposal,
894            attestation,
895        })
896    }
897}
898
899impl<S: Scheme, D: Digest> Attributable for Notarize<S, D> {
900    fn signer(&self) -> Participant {
901        self.attestation.signer
902    }
903}
904
905impl<S: Scheme, D: Digest> Epochable for Notarize<S, D> {
906    fn epoch(&self) -> Epoch {
907        self.proposal.epoch()
908    }
909}
910
911impl<S: Scheme, D: Digest> Viewable for Notarize<S, D> {
912    fn view(&self) -> View {
913        self.proposal.view()
914    }
915}
916
917#[cfg(feature = "arbitrary")]
918impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Notarize<S, D>
919where
920    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
921    D: for<'a> arbitrary::Arbitrary<'a>,
922{
923    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
924        let proposal = Proposal::arbitrary(u)?;
925        let attestation = Attestation::arbitrary(u)?;
926        Ok(Self {
927            proposal,
928            attestation,
929        })
930    }
931}
932
933/// Batch-verifies certificates and returns a per-item result.
934///
935/// Uses bisection to efficiently identify invalid certificates when batch
936/// verification fails.
937pub fn verify_certificates<'a, R, S, D>(
938    rng: &mut R,
939    scheme: &S,
940    certificates: &[(Subject<'a, D>, &'a S::Certificate)],
941    strategy: &impl Strategy,
942) -> Vec<bool>
943where
944    R: CryptoRngCore,
945    S: scheme::Scheme<D>,
946    D: Digest,
947{
948    scheme.verify_certificates_bisect::<_, D, N3f1>(rng, certificates, strategy)
949}
950
951/// Aggregated notarization certificate recovered from notarize votes.
952/// When a proposal is notarized, it means at least 2f+1 validators have voted for it.
953///
954/// Some signing schemes (like [`super::scheme::bls12381_threshold::vrf`]) embed an additional
955/// randomness seed in the certificate. For threshold signatures, the seed can be accessed
956/// via [`super::scheme::bls12381_threshold::vrf::Seedable::seed`].
957#[derive(Clone, Debug)]
958pub struct Notarization<S: Scheme, D: Digest> {
959    /// The proposal that has been notarized.
960    pub proposal: Proposal<D>,
961    /// The recovered certificate for the proposal.
962    pub certificate: S::Certificate,
963}
964
965impl<S: Scheme, D: Digest> Notarization<S, D> {
966    /// Builds a notarization certificate from notarize votes for the same proposal.
967    pub fn from_notarizes<'a, I>(scheme: &S, notarizes: I, strategy: &impl Strategy) -> Option<Self>
968    where
969        I: IntoIterator<Item = &'a Notarize<S, D>>,
970        I::IntoIter: Send,
971    {
972        let mut iter = notarizes.into_iter().peekable();
973        let proposal = iter.peek()?.proposal.clone();
974        let certificate =
975            scheme.assemble::<_, N3f1>(iter.map(|n| n.attestation.clone()), strategy)?;
976
977        Some(Self {
978            proposal,
979            certificate,
980        })
981    }
982
983    /// Verifies the notarization certificate against the provided signing scheme.
984    ///
985    /// This ensures that the certificate is valid for the claimed proposal.
986    pub fn verify<R: CryptoRngCore>(
987        &self,
988        rng: &mut R,
989        scheme: &S,
990        strategy: &impl Strategy,
991    ) -> bool
992    where
993        S: scheme::Scheme<D>,
994    {
995        scheme.verify_certificate::<_, D, N3f1>(
996            rng,
997            Subject::Notarize {
998                proposal: &self.proposal,
999            },
1000            &self.certificate,
1001            strategy,
1002        )
1003    }
1004
1005    /// Returns the round associated with the notarized proposal.
1006    pub const fn round(&self) -> Round {
1007        self.proposal.round
1008    }
1009}
1010
1011impl<S: Scheme, D: Digest> PartialEq for Notarization<S, D> {
1012    fn eq(&self, other: &Self) -> bool {
1013        self.proposal == other.proposal && self.certificate == other.certificate
1014    }
1015}
1016
1017impl<S: Scheme, D: Digest> Eq for Notarization<S, D> {}
1018
1019impl<S: Scheme, D: Digest> Hash for Notarization<S, D> {
1020    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1021        self.proposal.hash(state);
1022        self.certificate.hash(state);
1023    }
1024}
1025
1026impl<S: Scheme, D: Digest> Write for Notarization<S, D> {
1027    fn write(&self, writer: &mut impl BufMut) {
1028        self.proposal.write(writer);
1029        self.certificate.write(writer);
1030    }
1031}
1032
1033impl<S: Scheme, D: Digest> EncodeSize for Notarization<S, D> {
1034    fn encode_size(&self) -> usize {
1035        self.proposal.encode_size() + self.certificate.encode_size()
1036    }
1037}
1038
1039impl<S: Scheme, D: Digest> Read for Notarization<S, D> {
1040    type Cfg = <S::Certificate as Read>::Cfg;
1041
1042    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
1043        let proposal = Proposal::read(reader)?;
1044        let certificate = S::Certificate::read_cfg(reader, cfg)?;
1045
1046        Ok(Self {
1047            proposal,
1048            certificate,
1049        })
1050    }
1051}
1052
1053impl<S: Scheme, D: Digest> Epochable for Notarization<S, D> {
1054    fn epoch(&self) -> Epoch {
1055        self.proposal.epoch()
1056    }
1057}
1058
1059impl<S: Scheme, D: Digest> Viewable for Notarization<S, D> {
1060    fn view(&self) -> View {
1061        self.proposal.view()
1062    }
1063}
1064
1065#[cfg(feature = "arbitrary")]
1066impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Notarization<S, D>
1067where
1068    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
1069    D: for<'a> arbitrary::Arbitrary<'a>,
1070{
1071    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1072        let proposal = Proposal::arbitrary(u)?;
1073        let certificate = S::Certificate::arbitrary(u)?;
1074        Ok(Self {
1075            proposal,
1076            certificate,
1077        })
1078    }
1079}
1080
1081/// Validator vote for nullifying the current round, i.e. skip the current round.
1082/// This is typically used when the leader is unresponsive or fails to propose a valid block.
1083#[derive(Clone, Debug)]
1084pub struct Nullify<S: Scheme> {
1085    /// The round to be nullified (skipped).
1086    pub round: Round,
1087    /// Scheme-specific attestation material.
1088    pub attestation: Attestation<S>,
1089}
1090
1091impl<S: Scheme> PartialEq for Nullify<S> {
1092    fn eq(&self, other: &Self) -> bool {
1093        self.round == other.round && self.attestation == other.attestation
1094    }
1095}
1096
1097impl<S: Scheme> Eq for Nullify<S> {}
1098
1099impl<S: Scheme> Hash for Nullify<S> {
1100    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1101        self.round.hash(state);
1102        self.attestation.hash(state);
1103    }
1104}
1105
1106impl<S: Scheme> Nullify<S> {
1107    /// Signs a nullify vote for the given round.
1108    pub fn sign<D: Digest>(scheme: &S, round: Round) -> Option<Self>
1109    where
1110        S: scheme::Scheme<D>,
1111    {
1112        let attestation = scheme.sign::<D>(Subject::Nullify { round })?;
1113
1114        Some(Self { round, attestation })
1115    }
1116
1117    /// Verifies the nullify vote against the provided signing scheme.
1118    ///
1119    /// This ensures that the nullify signature is valid for the given round.
1120    pub fn verify<R, D: Digest>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
1121    where
1122        R: CryptoRngCore,
1123        S: scheme::Scheme<D>,
1124    {
1125        scheme.verify_attestation::<_, D>(
1126            rng,
1127            Subject::Nullify { round: self.round },
1128            &self.attestation,
1129            strategy,
1130        )
1131    }
1132
1133    /// Returns the round associated with this nullify vote.
1134    pub const fn round(&self) -> Round {
1135        self.round
1136    }
1137}
1138
1139impl<S: Scheme> Write for Nullify<S> {
1140    fn write(&self, writer: &mut impl BufMut) {
1141        self.round.write(writer);
1142        self.attestation.write(writer);
1143    }
1144}
1145
1146impl<S: Scheme> EncodeSize for Nullify<S> {
1147    fn encode_size(&self) -> usize {
1148        self.round.encode_size() + self.attestation.encode_size()
1149    }
1150}
1151
1152impl<S: Scheme> Read for Nullify<S> {
1153    type Cfg = ();
1154
1155    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
1156        let round = Round::read(reader)?;
1157        let attestation = Attestation::read(reader)?;
1158
1159        Ok(Self { round, attestation })
1160    }
1161}
1162
1163impl<S: Scheme> Attributable for Nullify<S> {
1164    fn signer(&self) -> Participant {
1165        self.attestation.signer
1166    }
1167}
1168
1169impl<S: Scheme> Epochable for Nullify<S> {
1170    fn epoch(&self) -> Epoch {
1171        self.round.epoch()
1172    }
1173}
1174
1175impl<S: Scheme> Viewable for Nullify<S> {
1176    fn view(&self) -> View {
1177        self.round.view()
1178    }
1179}
1180
1181#[cfg(feature = "arbitrary")]
1182impl<S: Scheme> arbitrary::Arbitrary<'_> for Nullify<S>
1183where
1184    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
1185{
1186    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1187        let round = Round::arbitrary(u)?;
1188        let attestation = Attestation::arbitrary(u)?;
1189        Ok(Self { round, attestation })
1190    }
1191}
1192
1193/// Aggregated nullification certificate recovered from nullify votes.
1194/// When a view is nullified, the consensus moves to the next view without finalizing a block.
1195#[derive(Clone, Debug)]
1196pub struct Nullification<S: Scheme> {
1197    /// The round in which this nullification is made.
1198    pub round: Round,
1199    /// The recovered certificate for the nullification.
1200    pub certificate: S::Certificate,
1201}
1202
1203impl<S: Scheme> Nullification<S> {
1204    /// Builds a nullification certificate from nullify votes from the same round.
1205    pub fn from_nullifies<'a, I>(scheme: &S, nullifies: I, strategy: &impl Strategy) -> Option<Self>
1206    where
1207        I: IntoIterator<Item = &'a Nullify<S>>,
1208        I::IntoIter: Send,
1209    {
1210        let mut iter = nullifies.into_iter().peekable();
1211        let round = iter.peek()?.round;
1212        let certificate =
1213            scheme.assemble::<_, N3f1>(iter.map(|n| n.attestation.clone()), strategy)?;
1214
1215        Some(Self { round, certificate })
1216    }
1217
1218    /// Verifies the nullification certificate against the provided signing scheme.
1219    ///
1220    /// This ensures that the certificate is valid for the claimed round.
1221    pub fn verify<R: CryptoRngCore, D: Digest>(
1222        &self,
1223        rng: &mut R,
1224        scheme: &S,
1225        strategy: &impl Strategy,
1226    ) -> bool
1227    where
1228        S: scheme::Scheme<D>,
1229    {
1230        scheme.verify_certificate::<_, D, N3f1>(
1231            rng,
1232            Subject::Nullify { round: self.round },
1233            &self.certificate,
1234            strategy,
1235        )
1236    }
1237
1238    /// Returns the round associated with this nullification.
1239    pub const fn round(&self) -> Round {
1240        self.round
1241    }
1242}
1243
1244impl<S: Scheme> PartialEq for Nullification<S> {
1245    fn eq(&self, other: &Self) -> bool {
1246        self.round == other.round && self.certificate == other.certificate
1247    }
1248}
1249
1250impl<S: Scheme> Eq for Nullification<S> {}
1251
1252impl<S: Scheme> Hash for Nullification<S> {
1253    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1254        self.round.hash(state);
1255        self.certificate.hash(state);
1256    }
1257}
1258
1259impl<S: Scheme> Write for Nullification<S> {
1260    fn write(&self, writer: &mut impl BufMut) {
1261        self.round.write(writer);
1262        self.certificate.write(writer);
1263    }
1264}
1265
1266impl<S: Scheme> EncodeSize for Nullification<S> {
1267    fn encode_size(&self) -> usize {
1268        self.round.encode_size() + self.certificate.encode_size()
1269    }
1270}
1271
1272impl<S: Scheme> Read for Nullification<S> {
1273    type Cfg = <S::Certificate as Read>::Cfg;
1274
1275    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
1276        let round = Round::read(reader)?;
1277        let certificate = S::Certificate::read_cfg(reader, cfg)?;
1278
1279        Ok(Self { round, certificate })
1280    }
1281}
1282
1283impl<S: Scheme> Epochable for Nullification<S> {
1284    fn epoch(&self) -> Epoch {
1285        self.round.epoch()
1286    }
1287}
1288
1289impl<S: Scheme> Viewable for Nullification<S> {
1290    fn view(&self) -> View {
1291        self.round.view()
1292    }
1293}
1294
1295#[cfg(feature = "arbitrary")]
1296impl<S: Scheme> arbitrary::Arbitrary<'_> for Nullification<S>
1297where
1298    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
1299{
1300    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1301        let round = Round::arbitrary(u)?;
1302        let certificate = S::Certificate::arbitrary(u)?;
1303        Ok(Self { round, certificate })
1304    }
1305}
1306
1307/// Validator vote to finalize a proposal.
1308/// This happens after a proposal has been notarized, confirming it as the canonical block
1309/// for this round.
1310#[derive(Clone, Debug)]
1311pub struct Finalize<S: Scheme, D: Digest> {
1312    /// Proposal being finalized.
1313    pub proposal: Proposal<D>,
1314    /// Scheme-specific attestation material.
1315    pub attestation: Attestation<S>,
1316}
1317
1318impl<S: Scheme, D: Digest> Finalize<S, D> {
1319    /// Signs a finalize vote for the provided proposal.
1320    pub fn sign(scheme: &S, proposal: Proposal<D>) -> Option<Self>
1321    where
1322        S: scheme::Scheme<D>,
1323    {
1324        let attestation = scheme.sign::<D>(Subject::Finalize {
1325            proposal: &proposal,
1326        })?;
1327
1328        Some(Self {
1329            proposal,
1330            attestation,
1331        })
1332    }
1333
1334    /// Verifies the finalize vote against the provided signing scheme.
1335    ///
1336    /// This ensures that the finalize signature is valid for the claimed proposal.
1337    pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
1338    where
1339        R: CryptoRngCore,
1340        S: scheme::Scheme<D>,
1341    {
1342        scheme.verify_attestation::<_, D>(
1343            rng,
1344            Subject::Finalize {
1345                proposal: &self.proposal,
1346            },
1347            &self.attestation,
1348            strategy,
1349        )
1350    }
1351
1352    /// Returns the round associated with this finalize vote.
1353    pub const fn round(&self) -> Round {
1354        self.proposal.round
1355    }
1356}
1357
1358impl<S: Scheme, D: Digest> PartialEq for Finalize<S, D> {
1359    fn eq(&self, other: &Self) -> bool {
1360        self.proposal == other.proposal && self.attestation == other.attestation
1361    }
1362}
1363
1364impl<S: Scheme, D: Digest> Eq for Finalize<S, D> {}
1365
1366impl<S: Scheme, D: Digest> Hash for Finalize<S, D> {
1367    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1368        self.proposal.hash(state);
1369        self.attestation.hash(state);
1370    }
1371}
1372
1373impl<S: Scheme, D: Digest> Write for Finalize<S, D> {
1374    fn write(&self, writer: &mut impl BufMut) {
1375        self.proposal.write(writer);
1376        self.attestation.write(writer);
1377    }
1378}
1379
1380impl<S: Scheme, D: Digest> EncodeSize for Finalize<S, D> {
1381    fn encode_size(&self) -> usize {
1382        self.proposal.encode_size() + self.attestation.encode_size()
1383    }
1384}
1385
1386impl<S: Scheme, D: Digest> Read for Finalize<S, D> {
1387    type Cfg = ();
1388
1389    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
1390        let proposal = Proposal::read(reader)?;
1391        let attestation = Attestation::read(reader)?;
1392
1393        Ok(Self {
1394            proposal,
1395            attestation,
1396        })
1397    }
1398}
1399
1400impl<S: Scheme, D: Digest> Attributable for Finalize<S, D> {
1401    fn signer(&self) -> Participant {
1402        self.attestation.signer
1403    }
1404}
1405
1406impl<S: Scheme, D: Digest> Epochable for Finalize<S, D> {
1407    fn epoch(&self) -> Epoch {
1408        self.proposal.epoch()
1409    }
1410}
1411
1412impl<S: Scheme, D: Digest> Viewable for Finalize<S, D> {
1413    fn view(&self) -> View {
1414        self.proposal.view()
1415    }
1416}
1417
1418#[cfg(feature = "arbitrary")]
1419impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Finalize<S, D>
1420where
1421    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
1422    D: for<'a> arbitrary::Arbitrary<'a>,
1423{
1424    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1425        let proposal = Proposal::arbitrary(u)?;
1426        let attestation = Attestation::arbitrary(u)?;
1427        Ok(Self {
1428            proposal,
1429            attestation,
1430        })
1431    }
1432}
1433
1434/// Aggregated finalization certificate recovered from finalize votes.
1435/// When a proposal is finalized, it becomes the canonical block for its view.
1436///
1437/// Some signing schemes (like [`super::scheme::bls12381_threshold::vrf`]) embed an additional
1438/// randomness seed in the certificate. For threshold signatures, the seed can be accessed
1439/// via [`super::scheme::bls12381_threshold::vrf::Seedable::seed`].
1440#[derive(Clone, Debug)]
1441pub struct Finalization<S: Scheme, D: Digest> {
1442    /// The proposal that has been finalized.
1443    pub proposal: Proposal<D>,
1444    /// The recovered certificate for the proposal.
1445    pub certificate: S::Certificate,
1446}
1447
1448impl<S: Scheme, D: Digest> Finalization<S, D> {
1449    /// Builds a finalization certificate from finalize votes for the same proposal.
1450    pub fn from_finalizes<'a, I>(scheme: &S, finalizes: I, strategy: &impl Strategy) -> Option<Self>
1451    where
1452        I: IntoIterator<Item = &'a Finalize<S, D>>,
1453        I::IntoIter: Send,
1454    {
1455        let mut iter = finalizes.into_iter().peekable();
1456        let proposal = iter.peek()?.proposal.clone();
1457        let certificate =
1458            scheme.assemble::<_, N3f1>(iter.map(|f| f.attestation.clone()), strategy)?;
1459
1460        Some(Self {
1461            proposal,
1462            certificate,
1463        })
1464    }
1465
1466    /// Verifies the finalization certificate against the provided signing scheme.
1467    ///
1468    /// This ensures that the certificate is valid for the claimed proposal.
1469    pub fn verify<R: CryptoRngCore>(
1470        &self,
1471        rng: &mut R,
1472        scheme: &S,
1473        strategy: &impl Strategy,
1474    ) -> bool
1475    where
1476        S: scheme::Scheme<D>,
1477    {
1478        scheme.verify_certificate::<_, D, N3f1>(
1479            rng,
1480            Subject::Finalize {
1481                proposal: &self.proposal,
1482            },
1483            &self.certificate,
1484            strategy,
1485        )
1486    }
1487
1488    /// Returns the round associated with the finalized proposal.
1489    pub const fn round(&self) -> Round {
1490        self.proposal.round
1491    }
1492}
1493
1494impl<S: Scheme, D: Digest> PartialEq for Finalization<S, D> {
1495    fn eq(&self, other: &Self) -> bool {
1496        self.proposal == other.proposal && self.certificate == other.certificate
1497    }
1498}
1499
1500impl<S: Scheme, D: Digest> Eq for Finalization<S, D> {}
1501
1502impl<S: Scheme, D: Digest> Hash for Finalization<S, D> {
1503    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1504        self.proposal.hash(state);
1505        self.certificate.hash(state);
1506    }
1507}
1508
1509impl<S: Scheme, D: Digest> Write for Finalization<S, D> {
1510    fn write(&self, writer: &mut impl BufMut) {
1511        self.proposal.write(writer);
1512        self.certificate.write(writer);
1513    }
1514}
1515
1516impl<S: Scheme, D: Digest> EncodeSize for Finalization<S, D> {
1517    fn encode_size(&self) -> usize {
1518        self.proposal.encode_size() + self.certificate.encode_size()
1519    }
1520}
1521
1522impl<S: Scheme, D: Digest> Read for Finalization<S, D> {
1523    type Cfg = <S::Certificate as Read>::Cfg;
1524
1525    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
1526        let proposal = Proposal::read(reader)?;
1527        let certificate = S::Certificate::read_cfg(reader, cfg)?;
1528
1529        Ok(Self {
1530            proposal,
1531            certificate,
1532        })
1533    }
1534}
1535
1536impl<S: Scheme, D: Digest> Epochable for Finalization<S, D> {
1537    fn epoch(&self) -> Epoch {
1538        self.proposal.epoch()
1539    }
1540}
1541
1542impl<S: Scheme, D: Digest> Viewable for Finalization<S, D> {
1543    fn view(&self) -> View {
1544        self.proposal.view()
1545    }
1546}
1547
1548#[cfg(feature = "arbitrary")]
1549impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Finalization<S, D>
1550where
1551    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
1552    D: for<'a> arbitrary::Arbitrary<'a>,
1553{
1554    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1555        let proposal = Proposal::arbitrary(u)?;
1556        let certificate = S::Certificate::arbitrary(u)?;
1557        Ok(Self {
1558            proposal,
1559            certificate,
1560        })
1561    }
1562}
1563
1564/// Backfiller is a message type for requesting and receiving missing consensus artifacts.
1565/// This is used to synchronize validators that have fallen behind or just joined the network.
1566#[derive(Clone, Debug, PartialEq)]
1567pub enum Backfiller<S: Scheme, D: Digest> {
1568    /// Request for missing notarizations and nullifications
1569    Request(Request),
1570    /// Response containing requested notarizations and nullifications
1571    Response(Response<S, D>),
1572}
1573
1574impl<S: Scheme, D: Digest> Write for Backfiller<S, D> {
1575    fn write(&self, writer: &mut impl BufMut) {
1576        match self {
1577            Self::Request(request) => {
1578                0u8.write(writer);
1579                request.write(writer);
1580            }
1581            Self::Response(response) => {
1582                1u8.write(writer);
1583                response.write(writer);
1584            }
1585        }
1586    }
1587}
1588
1589impl<S: Scheme, D: Digest> EncodeSize for Backfiller<S, D> {
1590    fn encode_size(&self) -> usize {
1591        1 + match self {
1592            Self::Request(v) => v.encode_size(),
1593            Self::Response(v) => v.encode_size(),
1594        }
1595    }
1596}
1597
1598impl<S: Scheme, D: Digest> Read for Backfiller<S, D> {
1599    type Cfg = (usize, <S::Certificate as Read>::Cfg);
1600
1601    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
1602        let tag = <u8>::read(reader)?;
1603        match tag {
1604            0 => {
1605                let (max_len, _) = cfg;
1606                let v = Request::read_cfg(reader, max_len)?;
1607                Ok(Self::Request(v))
1608            }
1609            1 => {
1610                let v = Response::<S, D>::read_cfg(reader, cfg)?;
1611                Ok(Self::Response(v))
1612            }
1613            _ => Err(Error::Invalid(
1614                "consensus::simplex::Backfiller",
1615                "Invalid type",
1616            )),
1617        }
1618    }
1619}
1620
1621#[cfg(feature = "arbitrary")]
1622impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Backfiller<S, D>
1623where
1624    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
1625    D: for<'a> arbitrary::Arbitrary<'a>,
1626{
1627    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1628        let tag = u.int_in_range(0..=1)?;
1629        match tag {
1630            0 => {
1631                let v = Request::arbitrary(u)?;
1632                Ok(Self::Request(v))
1633            }
1634            1 => {
1635                let v = Response::<S, D>::arbitrary(u)?;
1636                Ok(Self::Response(v))
1637            }
1638            _ => unreachable!(),
1639        }
1640    }
1641}
1642
1643/// Request is a message to request missing notarizations and nullifications.
1644/// This is used by validators who need to catch up with the consensus state.
1645#[derive(Clone, Debug, PartialEq)]
1646#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1647pub struct Request {
1648    /// Unique identifier for this request (used to match responses)
1649    pub id: u64,
1650    /// Views for which notarizations are requested
1651    pub notarizations: Vec<View>,
1652    /// Views for which nullifications are requested
1653    pub nullifications: Vec<View>,
1654}
1655
1656impl Request {
1657    /// Creates a new request for missing notarizations and nullifications.
1658    pub const fn new(id: u64, notarizations: Vec<View>, nullifications: Vec<View>) -> Self {
1659        Self {
1660            id,
1661            notarizations,
1662            nullifications,
1663        }
1664    }
1665}
1666
1667impl Write for Request {
1668    fn write(&self, writer: &mut impl BufMut) {
1669        UInt(self.id).write(writer);
1670        self.notarizations.write(writer);
1671        self.nullifications.write(writer);
1672    }
1673}
1674
1675impl EncodeSize for Request {
1676    fn encode_size(&self) -> usize {
1677        UInt(self.id).encode_size()
1678            + self.notarizations.encode_size()
1679            + self.nullifications.encode_size()
1680    }
1681}
1682
1683impl Read for Request {
1684    type Cfg = usize;
1685
1686    fn read_cfg(reader: &mut impl Buf, max_len: &usize) -> Result<Self, Error> {
1687        let id = UInt::read(reader)?.into();
1688        let mut views = HashSet::new();
1689        let notarizations = Vec::<View>::read_range(reader, ..=*max_len)?;
1690        for view in notarizations.iter() {
1691            if !views.insert(view) {
1692                return Err(Error::Invalid(
1693                    "consensus::simplex::Request",
1694                    "Duplicate notarization",
1695                ));
1696            }
1697        }
1698        let remaining = max_len - notarizations.len();
1699        views.clear();
1700        let nullifications = Vec::<View>::read_range(reader, ..=remaining)?;
1701        for view in nullifications.iter() {
1702            if !views.insert(view) {
1703                return Err(Error::Invalid(
1704                    "consensus::simplex::Request",
1705                    "Duplicate nullification",
1706                ));
1707            }
1708        }
1709        Ok(Self {
1710            id,
1711            notarizations,
1712            nullifications,
1713        })
1714    }
1715}
1716
1717/// Response is a message containing the requested notarizations and nullifications.
1718/// This is sent in response to a Request message.
1719#[derive(Clone, Debug, PartialEq)]
1720pub struct Response<S: Scheme, D: Digest> {
1721    /// Identifier matching the original request
1722    pub id: u64,
1723    /// Notarizations for the requested views
1724    pub notarizations: Vec<Notarization<S, D>>,
1725    /// Nullifications for the requested views
1726    pub nullifications: Vec<Nullification<S>>,
1727}
1728
1729impl<S: Scheme, D: Digest> Response<S, D> {
1730    /// Creates a new response with the given id, notarizations, and nullifications.
1731    pub const fn new(
1732        id: u64,
1733        notarizations: Vec<Notarization<S, D>>,
1734        nullifications: Vec<Nullification<S>>,
1735    ) -> Self {
1736        Self {
1737            id,
1738            notarizations,
1739            nullifications,
1740        }
1741    }
1742
1743    /// Verifies the certificates contained in this response against the signing scheme.
1744    pub fn verify<R: CryptoRngCore>(
1745        &self,
1746        rng: &mut R,
1747        scheme: &S,
1748        strategy: &impl Strategy,
1749    ) -> bool
1750    where
1751        S: scheme::Scheme<D>,
1752    {
1753        // Prepare to verify
1754        if self.notarizations.is_empty() && self.nullifications.is_empty() {
1755            return true;
1756        }
1757
1758        let notarizations = self.notarizations.iter().map(|notarization| {
1759            let context = Subject::Notarize {
1760                proposal: &notarization.proposal,
1761            };
1762
1763            (context, &notarization.certificate)
1764        });
1765
1766        let nullifications = self.nullifications.iter().map(|nullification| {
1767            let context = Subject::Nullify {
1768                round: nullification.round,
1769            };
1770
1771            (context, &nullification.certificate)
1772        });
1773
1774        scheme.verify_certificates::<_, D, _, N3f1>(
1775            rng,
1776            notarizations.chain(nullifications),
1777            strategy,
1778        )
1779    }
1780}
1781
1782impl<S: Scheme, D: Digest> Write for Response<S, D> {
1783    fn write(&self, writer: &mut impl BufMut) {
1784        UInt(self.id).write(writer);
1785        self.notarizations.write(writer);
1786        self.nullifications.write(writer);
1787    }
1788}
1789
1790impl<S: Scheme, D: Digest> EncodeSize for Response<S, D> {
1791    fn encode_size(&self) -> usize {
1792        UInt(self.id).encode_size()
1793            + self.notarizations.encode_size()
1794            + self.nullifications.encode_size()
1795    }
1796}
1797
1798impl<S: Scheme, D: Digest> Read for Response<S, D> {
1799    type Cfg = (usize, <S::Certificate as Read>::Cfg);
1800
1801    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
1802        let (max_len, certificate_cfg) = cfg;
1803        let id = UInt::read(reader)?.into();
1804        let mut views = HashSet::new();
1805        let notarizations = Vec::<Notarization<S, D>>::read_cfg(
1806            reader,
1807            &((..=*max_len).into(), certificate_cfg.clone()),
1808        )?;
1809        for notarization in notarizations.iter() {
1810            if !views.insert(notarization.view()) {
1811                return Err(Error::Invalid(
1812                    "consensus::simplex::Response",
1813                    "Duplicate notarization",
1814                ));
1815            }
1816        }
1817        let remaining = max_len - notarizations.len();
1818        views.clear();
1819        let nullifications = Vec::<Nullification<S>>::read_cfg(
1820            reader,
1821            &((..=remaining).into(), certificate_cfg.clone()),
1822        )?;
1823        for nullification in nullifications.iter() {
1824            if !views.insert(nullification.view()) {
1825                return Err(Error::Invalid(
1826                    "consensus::simplex::Response",
1827                    "Duplicate nullification",
1828                ));
1829            }
1830        }
1831        Ok(Self {
1832            id,
1833            notarizations,
1834            nullifications,
1835        })
1836    }
1837}
1838
1839#[cfg(feature = "arbitrary")]
1840impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Response<S, D>
1841where
1842    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
1843    D: for<'a> arbitrary::Arbitrary<'a>,
1844{
1845    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1846        let id = u.arbitrary()?;
1847        let notarizations = u.arbitrary()?;
1848        let nullifications = u.arbitrary()?;
1849        Ok(Self {
1850            id,
1851            notarizations,
1852            nullifications,
1853        })
1854    }
1855}
1856
1857/// Activity represents all possible activities that can occur in the consensus protocol.
1858/// This includes both regular consensus messages and fault evidence.
1859///
1860/// # Verification
1861///
1862/// Some activities issued by consensus are not guaranteed to be cryptographically verified (i.e. if not needed
1863/// to produce a minimum quorum certificate). Use [`Activity::verified`] to check if an activity may not be verified,
1864/// and [`Activity::verify`] to perform verification.
1865///
1866/// # Activity Filtering
1867///
1868/// For **non-attributable** schemes like [`crate::simplex::scheme::bls12381_threshold`], exposing
1869/// per-validator activity as fault evidence is not safe: with threshold cryptography, any `t` valid partial signatures can
1870/// be used to forge a partial signature for any player.
1871///
1872/// Use [`crate::simplex::scheme::reporter::AttributableReporter`] to automatically filter and
1873/// verify activities based on [`Scheme::is_attributable`].
1874#[derive(Clone, Debug)]
1875pub enum Activity<S: Scheme, D: Digest> {
1876    /// A validator's notarize vote over a proposal.
1877    Notarize(Notarize<S, D>),
1878    /// A recovered certificate for a notarization (scheme-specific).
1879    Notarization(Notarization<S, D>),
1880    /// A notarization was locally certified.
1881    Certification(Notarization<S, D>),
1882    /// A validator's nullify vote used to skip the current view.
1883    Nullify(Nullify<S>),
1884    /// A recovered certificate for a nullification (scheme-specific).
1885    Nullification(Nullification<S>),
1886    /// A validator's finalize vote over a proposal.
1887    Finalize(Finalize<S, D>),
1888    /// A recovered certificate for a finalization (scheme-specific).
1889    Finalization(Finalization<S, D>),
1890    /// Evidence of a validator sending conflicting notarizes (Byzantine behavior).
1891    ConflictingNotarize(ConflictingNotarize<S, D>),
1892    /// Evidence of a validator sending conflicting finalizes (Byzantine behavior).
1893    ConflictingFinalize(ConflictingFinalize<S, D>),
1894    /// Evidence of a validator sending both nullify and finalize for the same view (Byzantine behavior).
1895    NullifyFinalize(NullifyFinalize<S, D>),
1896}
1897
1898impl<S: Scheme, D: Digest> PartialEq for Activity<S, D> {
1899    fn eq(&self, other: &Self) -> bool {
1900        match (self, other) {
1901            (Self::Notarize(a), Self::Notarize(b)) => a == b,
1902            (Self::Notarization(a), Self::Notarization(b)) => a == b,
1903            (Self::Certification(a), Self::Certification(b)) => a == b,
1904            (Self::Nullify(a), Self::Nullify(b)) => a == b,
1905            (Self::Nullification(a), Self::Nullification(b)) => a == b,
1906            (Self::Finalize(a), Self::Finalize(b)) => a == b,
1907            (Self::Finalization(a), Self::Finalization(b)) => a == b,
1908            (Self::ConflictingNotarize(a), Self::ConflictingNotarize(b)) => a == b,
1909            (Self::ConflictingFinalize(a), Self::ConflictingFinalize(b)) => a == b,
1910            (Self::NullifyFinalize(a), Self::NullifyFinalize(b)) => a == b,
1911            _ => false,
1912        }
1913    }
1914}
1915
1916impl<S: Scheme, D: Digest> Eq for Activity<S, D> {}
1917
1918impl<S: Scheme, D: Digest> Hash for Activity<S, D> {
1919    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1920        match self {
1921            Self::Notarize(v) => {
1922                0u8.hash(state);
1923                v.hash(state);
1924            }
1925            Self::Notarization(v) => {
1926                1u8.hash(state);
1927                v.hash(state);
1928            }
1929            Self::Certification(v) => {
1930                2u8.hash(state);
1931                v.hash(state);
1932            }
1933            Self::Nullify(v) => {
1934                3u8.hash(state);
1935                v.hash(state);
1936            }
1937            Self::Nullification(v) => {
1938                4u8.hash(state);
1939                v.hash(state);
1940            }
1941            Self::Finalize(v) => {
1942                5u8.hash(state);
1943                v.hash(state);
1944            }
1945            Self::Finalization(v) => {
1946                6u8.hash(state);
1947                v.hash(state);
1948            }
1949            Self::ConflictingNotarize(v) => {
1950                7u8.hash(state);
1951                v.hash(state);
1952            }
1953            Self::ConflictingFinalize(v) => {
1954                8u8.hash(state);
1955                v.hash(state);
1956            }
1957            Self::NullifyFinalize(v) => {
1958                9u8.hash(state);
1959                v.hash(state);
1960            }
1961        }
1962    }
1963}
1964
1965impl<S: Scheme, D: Digest> Activity<S, D> {
1966    /// Indicates whether the activity is guaranteed to have been verified by consensus.
1967    pub const fn verified(&self) -> bool {
1968        match self {
1969            Self::Notarize(_) => false,
1970            Self::Notarization(_) => true,
1971            Self::Certification(_) => false,
1972            Self::Nullify(_) => false,
1973            Self::Nullification(_) => true,
1974            Self::Finalize(_) => false,
1975            Self::Finalization(_) => true,
1976            Self::ConflictingNotarize(_) => false,
1977            Self::ConflictingFinalize(_) => false,
1978            Self::NullifyFinalize(_) => false,
1979        }
1980    }
1981
1982    /// Verifies the validity of this activity against the signing scheme.
1983    ///
1984    /// This method **always** performs verification regardless of whether the activity has been
1985    /// previously verified. Callers can use [`Activity::verified`] to check if verification is
1986    /// necessary before calling this method.
1987    pub fn verify<R: CryptoRngCore>(
1988        &self,
1989        rng: &mut R,
1990        scheme: &S,
1991        strategy: &impl Strategy,
1992    ) -> bool
1993    where
1994        S: scheme::Scheme<D>,
1995    {
1996        match self {
1997            Self::Notarize(n) => n.verify(rng, scheme, strategy),
1998            Self::Notarization(n) => n.verify(rng, scheme, strategy),
1999            Self::Certification(n) => n.verify(rng, scheme, strategy),
2000            Self::Nullify(n) => n.verify(rng, scheme, strategy),
2001            Self::Nullification(n) => n.verify(rng, scheme, strategy),
2002            Self::Finalize(f) => f.verify(rng, scheme, strategy),
2003            Self::Finalization(f) => f.verify(rng, scheme, strategy),
2004            Self::ConflictingNotarize(c) => c.verify(rng, scheme, strategy),
2005            Self::ConflictingFinalize(c) => c.verify(rng, scheme, strategy),
2006            Self::NullifyFinalize(c) => c.verify(rng, scheme, strategy),
2007        }
2008    }
2009}
2010
2011impl<S: Scheme, D: Digest> Write for Activity<S, D> {
2012    fn write(&self, writer: &mut impl BufMut) {
2013        match self {
2014            Self::Notarize(v) => {
2015                0u8.write(writer);
2016                v.write(writer);
2017            }
2018            Self::Notarization(v) => {
2019                1u8.write(writer);
2020                v.write(writer);
2021            }
2022            Self::Certification(v) => {
2023                2u8.write(writer);
2024                v.write(writer);
2025            }
2026            Self::Nullify(v) => {
2027                3u8.write(writer);
2028                v.write(writer);
2029            }
2030            Self::Nullification(v) => {
2031                4u8.write(writer);
2032                v.write(writer);
2033            }
2034            Self::Finalize(v) => {
2035                5u8.write(writer);
2036                v.write(writer);
2037            }
2038            Self::Finalization(v) => {
2039                6u8.write(writer);
2040                v.write(writer);
2041            }
2042            Self::ConflictingNotarize(v) => {
2043                7u8.write(writer);
2044                v.write(writer);
2045            }
2046            Self::ConflictingFinalize(v) => {
2047                8u8.write(writer);
2048                v.write(writer);
2049            }
2050            Self::NullifyFinalize(v) => {
2051                9u8.write(writer);
2052                v.write(writer);
2053            }
2054        }
2055    }
2056}
2057
2058impl<S: Scheme, D: Digest> EncodeSize for Activity<S, D> {
2059    fn encode_size(&self) -> usize {
2060        1 + match self {
2061            Self::Notarize(v) => v.encode_size(),
2062            Self::Notarization(v) => v.encode_size(),
2063            Self::Certification(v) => v.encode_size(),
2064            Self::Nullify(v) => v.encode_size(),
2065            Self::Nullification(v) => v.encode_size(),
2066            Self::Finalize(v) => v.encode_size(),
2067            Self::Finalization(v) => v.encode_size(),
2068            Self::ConflictingNotarize(v) => v.encode_size(),
2069            Self::ConflictingFinalize(v) => v.encode_size(),
2070            Self::NullifyFinalize(v) => v.encode_size(),
2071        }
2072    }
2073}
2074
2075impl<S: Scheme, D: Digest> Read for Activity<S, D> {
2076    type Cfg = <S::Certificate as Read>::Cfg;
2077
2078    fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
2079        let tag = <u8>::read(reader)?;
2080        match tag {
2081            0 => {
2082                let v = Notarize::<S, D>::read(reader)?;
2083                Ok(Self::Notarize(v))
2084            }
2085            1 => {
2086                let v = Notarization::<S, D>::read_cfg(reader, cfg)?;
2087                Ok(Self::Notarization(v))
2088            }
2089            2 => {
2090                let v = Notarization::<S, D>::read_cfg(reader, cfg)?;
2091                Ok(Self::Certification(v))
2092            }
2093            3 => {
2094                let v = Nullify::<S>::read(reader)?;
2095                Ok(Self::Nullify(v))
2096            }
2097            4 => {
2098                let v = Nullification::<S>::read_cfg(reader, cfg)?;
2099                Ok(Self::Nullification(v))
2100            }
2101            5 => {
2102                let v = Finalize::<S, D>::read(reader)?;
2103                Ok(Self::Finalize(v))
2104            }
2105            6 => {
2106                let v = Finalization::<S, D>::read_cfg(reader, cfg)?;
2107                Ok(Self::Finalization(v))
2108            }
2109            7 => {
2110                let v = ConflictingNotarize::<S, D>::read(reader)?;
2111                Ok(Self::ConflictingNotarize(v))
2112            }
2113            8 => {
2114                let v = ConflictingFinalize::<S, D>::read(reader)?;
2115                Ok(Self::ConflictingFinalize(v))
2116            }
2117            9 => {
2118                let v = NullifyFinalize::<S, D>::read(reader)?;
2119                Ok(Self::NullifyFinalize(v))
2120            }
2121            _ => Err(Error::Invalid(
2122                "consensus::simplex::Activity",
2123                "Invalid type",
2124            )),
2125        }
2126    }
2127}
2128
2129impl<S: Scheme, D: Digest> Epochable for Activity<S, D> {
2130    fn epoch(&self) -> Epoch {
2131        match self {
2132            Self::Notarize(v) => v.epoch(),
2133            Self::Notarization(v) => v.epoch(),
2134            Self::Certification(v) => v.epoch(),
2135            Self::Nullify(v) => v.epoch(),
2136            Self::Nullification(v) => v.epoch(),
2137            Self::Finalize(v) => v.epoch(),
2138            Self::Finalization(v) => v.epoch(),
2139            Self::ConflictingNotarize(v) => v.epoch(),
2140            Self::ConflictingFinalize(v) => v.epoch(),
2141            Self::NullifyFinalize(v) => v.epoch(),
2142        }
2143    }
2144}
2145
2146impl<S: Scheme, D: Digest> Viewable for Activity<S, D> {
2147    fn view(&self) -> View {
2148        match self {
2149            Self::Notarize(v) => v.view(),
2150            Self::Notarization(v) => v.view(),
2151            Self::Certification(v) => v.view(),
2152            Self::Nullify(v) => v.view(),
2153            Self::Nullification(v) => v.view(),
2154            Self::Finalize(v) => v.view(),
2155            Self::Finalization(v) => v.view(),
2156            Self::ConflictingNotarize(v) => v.view(),
2157            Self::ConflictingFinalize(v) => v.view(),
2158            Self::NullifyFinalize(v) => v.view(),
2159        }
2160    }
2161}
2162
2163#[cfg(feature = "arbitrary")]
2164impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Activity<S, D>
2165where
2166    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
2167    S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
2168    D: for<'a> arbitrary::Arbitrary<'a>,
2169{
2170    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2171        let tag = u.int_in_range(0..=9)?;
2172        match tag {
2173            0 => {
2174                let v = Notarize::<S, D>::arbitrary(u)?;
2175                Ok(Self::Notarize(v))
2176            }
2177            1 => {
2178                let v = Notarization::<S, D>::arbitrary(u)?;
2179                Ok(Self::Notarization(v))
2180            }
2181            2 => {
2182                let v = Notarization::<S, D>::arbitrary(u)?;
2183                Ok(Self::Certification(v))
2184            }
2185            3 => {
2186                let v = Nullify::<S>::arbitrary(u)?;
2187                Ok(Self::Nullify(v))
2188            }
2189            4 => {
2190                let v = Nullification::<S>::arbitrary(u)?;
2191                Ok(Self::Nullification(v))
2192            }
2193            5 => {
2194                let v = Finalize::<S, D>::arbitrary(u)?;
2195                Ok(Self::Finalize(v))
2196            }
2197            6 => {
2198                let v = Finalization::<S, D>::arbitrary(u)?;
2199                Ok(Self::Finalization(v))
2200            }
2201            7 => {
2202                let v = ConflictingNotarize::<S, D>::arbitrary(u)?;
2203                Ok(Self::ConflictingNotarize(v))
2204            }
2205            8 => {
2206                let v = ConflictingFinalize::<S, D>::arbitrary(u)?;
2207                Ok(Self::ConflictingFinalize(v))
2208            }
2209            9 => {
2210                let v = NullifyFinalize::<S, D>::arbitrary(u)?;
2211                Ok(Self::NullifyFinalize(v))
2212            }
2213            _ => unreachable!(),
2214        }
2215    }
2216}
2217
2218/// ConflictingNotarize represents evidence of a Byzantine validator sending conflicting notarizes.
2219/// This is used to prove that a validator has equivocated (voted for different proposals in the same view).
2220#[derive(Clone, Debug)]
2221pub struct ConflictingNotarize<S: Scheme, D: Digest> {
2222    /// The first conflicting notarize
2223    notarize_1: Notarize<S, D>,
2224    /// The second conflicting notarize
2225    notarize_2: Notarize<S, D>,
2226}
2227
2228impl<S: Scheme, D: Digest> PartialEq for ConflictingNotarize<S, D> {
2229    fn eq(&self, other: &Self) -> bool {
2230        self.notarize_1 == other.notarize_1 && self.notarize_2 == other.notarize_2
2231    }
2232}
2233
2234impl<S: Scheme, D: Digest> Eq for ConflictingNotarize<S, D> {}
2235
2236impl<S: Scheme, D: Digest> Hash for ConflictingNotarize<S, D> {
2237    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2238        self.notarize_1.hash(state);
2239        self.notarize_2.hash(state);
2240    }
2241}
2242
2243impl<S: Scheme, D: Digest> ConflictingNotarize<S, D> {
2244    /// Creates a new conflicting notarize evidence from two conflicting notarizes.
2245    ///
2246    /// # Panics
2247    ///
2248    /// Panics if the two notarizes do not have the same round and signer, or if they
2249    /// have identical proposals (which would not constitute conflicting evidence).
2250    pub fn new(notarize_1: Notarize<S, D>, notarize_2: Notarize<S, D>) -> Self {
2251        assert_eq!(notarize_1.round(), notarize_2.round());
2252        assert_eq!(notarize_1.signer(), notarize_2.signer());
2253        assert_ne!(
2254            notarize_1.proposal, notarize_2.proposal,
2255            "proposals must differ to constitute conflicting evidence"
2256        );
2257
2258        Self {
2259            notarize_1,
2260            notarize_2,
2261        }
2262    }
2263
2264    /// Verifies that both conflicting signatures are valid, proving Byzantine behavior.
2265    pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
2266    where
2267        R: CryptoRngCore,
2268        S: scheme::Scheme<D>,
2269    {
2270        self.notarize_1.verify(rng, scheme, strategy)
2271            && self.notarize_2.verify(rng, scheme, strategy)
2272    }
2273}
2274
2275impl<S: Scheme, D: Digest> Attributable for ConflictingNotarize<S, D> {
2276    fn signer(&self) -> Participant {
2277        self.notarize_1.signer()
2278    }
2279}
2280
2281impl<S: Scheme, D: Digest> Epochable for ConflictingNotarize<S, D> {
2282    fn epoch(&self) -> Epoch {
2283        self.notarize_1.epoch()
2284    }
2285}
2286
2287impl<S: Scheme, D: Digest> Viewable for ConflictingNotarize<S, D> {
2288    fn view(&self) -> View {
2289        self.notarize_1.view()
2290    }
2291}
2292
2293impl<S: Scheme, D: Digest> Write for ConflictingNotarize<S, D> {
2294    fn write(&self, writer: &mut impl BufMut) {
2295        self.notarize_1.write(writer);
2296        self.notarize_2.write(writer);
2297    }
2298}
2299
2300impl<S: Scheme, D: Digest> Read for ConflictingNotarize<S, D> {
2301    type Cfg = ();
2302
2303    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
2304        let notarize_1 = Notarize::read(reader)?;
2305        let notarize_2 = Notarize::read(reader)?;
2306
2307        if notarize_1.signer() != notarize_2.signer()
2308            || notarize_1.round() != notarize_2.round()
2309            || notarize_1.proposal == notarize_2.proposal
2310        {
2311            return Err(Error::Invalid(
2312                "consensus::simplex::ConflictingNotarize",
2313                "invalid conflicting notarize",
2314            ));
2315        }
2316
2317        Ok(Self {
2318            notarize_1,
2319            notarize_2,
2320        })
2321    }
2322}
2323
2324impl<S: Scheme, D: Digest> EncodeSize for ConflictingNotarize<S, D> {
2325    fn encode_size(&self) -> usize {
2326        self.notarize_1.encode_size() + self.notarize_2.encode_size()
2327    }
2328}
2329
2330#[cfg(feature = "arbitrary")]
2331impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for ConflictingNotarize<S, D>
2332where
2333    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
2334    D: for<'a> arbitrary::Arbitrary<'a>,
2335{
2336    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2337        let notarize_1 = Notarize::arbitrary(u)?;
2338        let notarize_2 = Notarize::arbitrary(u)?;
2339        Ok(Self {
2340            notarize_1,
2341            notarize_2,
2342        })
2343    }
2344}
2345
2346/// ConflictingFinalize represents evidence of a Byzantine validator sending conflicting finalizes.
2347/// Similar to ConflictingNotarize, but for finalizes.
2348#[derive(Clone, Debug)]
2349pub struct ConflictingFinalize<S: Scheme, D: Digest> {
2350    /// The second conflicting finalize
2351    finalize_1: Finalize<S, D>,
2352    /// The second conflicting finalize
2353    finalize_2: Finalize<S, D>,
2354}
2355
2356impl<S: Scheme, D: Digest> PartialEq for ConflictingFinalize<S, D> {
2357    fn eq(&self, other: &Self) -> bool {
2358        self.finalize_1 == other.finalize_1 && self.finalize_2 == other.finalize_2
2359    }
2360}
2361
2362impl<S: Scheme, D: Digest> Eq for ConflictingFinalize<S, D> {}
2363
2364impl<S: Scheme, D: Digest> Hash for ConflictingFinalize<S, D> {
2365    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2366        self.finalize_1.hash(state);
2367        self.finalize_2.hash(state);
2368    }
2369}
2370
2371impl<S: Scheme, D: Digest> ConflictingFinalize<S, D> {
2372    /// Creates a new conflicting finalize evidence from two conflicting finalizes.
2373    ///
2374    /// # Panics
2375    ///
2376    /// Panics if the two finalizes do not have the same round and signer, or if they
2377    /// have identical proposals (which would not constitute conflicting evidence).
2378    pub fn new(finalize_1: Finalize<S, D>, finalize_2: Finalize<S, D>) -> Self {
2379        assert_eq!(finalize_1.round(), finalize_2.round());
2380        assert_eq!(finalize_1.signer(), finalize_2.signer());
2381        assert_ne!(
2382            finalize_1.proposal, finalize_2.proposal,
2383            "proposals must differ to constitute conflicting evidence"
2384        );
2385
2386        Self {
2387            finalize_1,
2388            finalize_2,
2389        }
2390    }
2391
2392    /// Verifies that both conflicting signatures are valid, proving Byzantine behavior.
2393    pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
2394    where
2395        R: CryptoRngCore,
2396        S: scheme::Scheme<D>,
2397    {
2398        self.finalize_1.verify(rng, scheme, strategy)
2399            && self.finalize_2.verify(rng, scheme, strategy)
2400    }
2401}
2402
2403impl<S: Scheme, D: Digest> Attributable for ConflictingFinalize<S, D> {
2404    fn signer(&self) -> Participant {
2405        self.finalize_1.signer()
2406    }
2407}
2408
2409impl<S: Scheme, D: Digest> Epochable for ConflictingFinalize<S, D> {
2410    fn epoch(&self) -> Epoch {
2411        self.finalize_1.epoch()
2412    }
2413}
2414
2415impl<S: Scheme, D: Digest> Viewable for ConflictingFinalize<S, D> {
2416    fn view(&self) -> View {
2417        self.finalize_1.view()
2418    }
2419}
2420
2421impl<S: Scheme, D: Digest> Write for ConflictingFinalize<S, D> {
2422    fn write(&self, writer: &mut impl BufMut) {
2423        self.finalize_1.write(writer);
2424        self.finalize_2.write(writer);
2425    }
2426}
2427
2428impl<S: Scheme, D: Digest> Read for ConflictingFinalize<S, D> {
2429    type Cfg = ();
2430
2431    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
2432        let finalize_1 = Finalize::read(reader)?;
2433        let finalize_2 = Finalize::read(reader)?;
2434
2435        if finalize_1.signer() != finalize_2.signer()
2436            || finalize_1.round() != finalize_2.round()
2437            || finalize_1.proposal == finalize_2.proposal
2438        {
2439            return Err(Error::Invalid(
2440                "consensus::simplex::ConflictingFinalize",
2441                "invalid conflicting finalize",
2442            ));
2443        }
2444
2445        Ok(Self {
2446            finalize_1,
2447            finalize_2,
2448        })
2449    }
2450}
2451
2452impl<S: Scheme, D: Digest> EncodeSize for ConflictingFinalize<S, D> {
2453    fn encode_size(&self) -> usize {
2454        self.finalize_1.encode_size() + self.finalize_2.encode_size()
2455    }
2456}
2457
2458#[cfg(feature = "arbitrary")]
2459impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for ConflictingFinalize<S, D>
2460where
2461    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
2462    D: for<'a> arbitrary::Arbitrary<'a>,
2463{
2464    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2465        let finalize_1 = Finalize::arbitrary(u)?;
2466        let finalize_2 = Finalize::arbitrary(u)?;
2467        Ok(Self {
2468            finalize_1,
2469            finalize_2,
2470        })
2471    }
2472}
2473
2474/// NullifyFinalize represents evidence of a Byzantine validator sending both a nullify and finalize
2475/// for the same view, which is contradictory behavior (a validator should either try to skip a view OR
2476/// finalize a proposal, not both).
2477#[derive(Clone, Debug)]
2478pub struct NullifyFinalize<S: Scheme, D: Digest> {
2479    /// The conflicting nullify
2480    nullify: Nullify<S>,
2481    /// The conflicting finalize
2482    finalize: Finalize<S, D>,
2483}
2484
2485impl<S: Scheme, D: Digest> PartialEq for NullifyFinalize<S, D> {
2486    fn eq(&self, other: &Self) -> bool {
2487        self.nullify == other.nullify && self.finalize == other.finalize
2488    }
2489}
2490
2491impl<S: Scheme, D: Digest> Eq for NullifyFinalize<S, D> {}
2492
2493impl<S: Scheme, D: Digest> Hash for NullifyFinalize<S, D> {
2494    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2495        self.nullify.hash(state);
2496        self.finalize.hash(state);
2497    }
2498}
2499
2500impl<S: Scheme, D: Digest> NullifyFinalize<S, D> {
2501    /// Creates a new nullify-finalize evidence from a nullify and a finalize.
2502    pub fn new(nullify: Nullify<S>, finalize: Finalize<S, D>) -> Self {
2503        assert_eq!(nullify.round, finalize.round());
2504        assert_eq!(nullify.signer(), finalize.signer());
2505
2506        Self { nullify, finalize }
2507    }
2508
2509    /// Verifies that both the nullify and finalize signatures are valid, proving Byzantine behavior.
2510    pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
2511    where
2512        R: CryptoRngCore,
2513        S: scheme::Scheme<D>,
2514    {
2515        self.nullify.verify(rng, scheme, strategy) && self.finalize.verify(rng, scheme, strategy)
2516    }
2517}
2518
2519impl<S: Scheme, D: Digest> Attributable for NullifyFinalize<S, D> {
2520    fn signer(&self) -> Participant {
2521        self.nullify.signer()
2522    }
2523}
2524
2525impl<S: Scheme, D: Digest> Epochable for NullifyFinalize<S, D> {
2526    fn epoch(&self) -> Epoch {
2527        self.nullify.epoch()
2528    }
2529}
2530
2531impl<S: Scheme, D: Digest> Viewable for NullifyFinalize<S, D> {
2532    fn view(&self) -> View {
2533        self.nullify.view()
2534    }
2535}
2536
2537impl<S: Scheme, D: Digest> Write for NullifyFinalize<S, D> {
2538    fn write(&self, writer: &mut impl BufMut) {
2539        self.nullify.write(writer);
2540        self.finalize.write(writer);
2541    }
2542}
2543
2544impl<S: Scheme, D: Digest> Read for NullifyFinalize<S, D> {
2545    type Cfg = ();
2546
2547    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
2548        let nullify = Nullify::read(reader)?;
2549        let finalize = Finalize::read(reader)?;
2550
2551        if nullify.signer() != finalize.signer() || nullify.round != finalize.round() {
2552            return Err(Error::Invalid(
2553                "consensus::simplex::NullifyFinalize",
2554                "mismatched signatures",
2555            ));
2556        }
2557
2558        Ok(Self { nullify, finalize })
2559    }
2560}
2561
2562impl<S: Scheme, D: Digest> EncodeSize for NullifyFinalize<S, D> {
2563    fn encode_size(&self) -> usize {
2564        self.nullify.encode_size() + self.finalize.encode_size()
2565    }
2566}
2567
2568#[cfg(feature = "arbitrary")]
2569impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for NullifyFinalize<S, D>
2570where
2571    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
2572    D: for<'a> arbitrary::Arbitrary<'a>,
2573{
2574    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2575        let nullify = Nullify::arbitrary(u)?;
2576        let finalize = Finalize::arbitrary(u)?;
2577        Ok(Self { nullify, finalize })
2578    }
2579}
2580
2581#[cfg(test)]
2582mod tests {
2583    use super::*;
2584    use crate::simplex::{
2585        quorum,
2586        scheme::{
2587            bls12381_multisig,
2588            bls12381_threshold::{
2589                standard as bls12381_threshold_std, vrf as bls12381_threshold_vrf,
2590            },
2591            ed25519, secp256r1, Scheme,
2592        },
2593    };
2594    use bytes::Bytes;
2595    use commonware_codec::{Decode, DecodeExt, Encode};
2596    use commonware_cryptography::{
2597        bls12381::primitives::variant::{MinPk, MinSig},
2598        certificate::mocks::Fixture,
2599        sha256::Digest as Sha256,
2600    };
2601    use commonware_parallel::Sequential;
2602    use commonware_utils::{test_rng, Faults, N3f1};
2603    use rand::{rngs::StdRng, SeedableRng};
2604
2605    const NAMESPACE: &[u8] = b"test";
2606
2607    // Helper function to create a sample digest
2608    fn sample_digest(v: u8) -> Sha256 {
2609        Sha256::from([v; 32]) // Simple fixed digest for testing
2610    }
2611
2612    /// Generate a fixture using the provided generator function with a specific seed.
2613    fn setup_seeded<S, F>(n: u32, seed: u64, fixture: F) -> Fixture<S>
2614    where
2615        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2616    {
2617        setup_seeded_ns(n, seed, NAMESPACE, fixture)
2618    }
2619
2620    /// Generate a fixture using the provided generator function with a specific seed and namespace.
2621    fn setup_seeded_ns<S, F>(n: u32, seed: u64, namespace: &[u8], fixture: F) -> Fixture<S>
2622    where
2623        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2624    {
2625        let mut rng = StdRng::seed_from_u64(seed);
2626        fixture(&mut rng, namespace, n)
2627    }
2628
2629    #[test]
2630    fn test_proposal_encode_decode() {
2631        let proposal = Proposal::new(
2632            Round::new(Epoch::new(0), View::new(10)),
2633            View::new(5),
2634            sample_digest(1),
2635        );
2636        let encoded = proposal.encode();
2637        let decoded = Proposal::<Sha256>::decode(encoded).unwrap();
2638        assert_eq!(proposal, decoded);
2639    }
2640
2641    fn notarize_encode_decode<S, F>(fixture: F)
2642    where
2643        S: Scheme<Sha256>,
2644        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2645    {
2646        let mut rng = test_rng();
2647        let fixture = fixture(&mut rng, NAMESPACE, 5);
2648        let round = Round::new(Epoch::new(0), View::new(10));
2649        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
2650        let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
2651
2652        let encoded = notarize.encode();
2653        let decoded = Notarize::decode(encoded).unwrap();
2654
2655        assert_eq!(notarize, decoded);
2656        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2657    }
2658
2659    #[test]
2660    fn test_notarize_encode_decode() {
2661        notarize_encode_decode(ed25519::fixture);
2662        notarize_encode_decode(secp256r1::fixture);
2663        notarize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2664        notarize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2665        notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2666        notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2667        notarize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2668        notarize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2669    }
2670
2671    fn notarization_encode_decode<S, F>(fixture: F)
2672    where
2673        S: Scheme<Sha256>,
2674        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2675    {
2676        let mut rng = test_rng();
2677        let fixture = fixture(&mut rng, NAMESPACE, 5);
2678        let proposal = Proposal::new(
2679            Round::new(Epoch::new(0), View::new(10)),
2680            View::new(5),
2681            sample_digest(1),
2682        );
2683        let notarizes: Vec<_> = fixture
2684            .schemes
2685            .iter()
2686            .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
2687            .collect();
2688        let notarization =
2689            Notarization::from_notarizes(&fixture.schemes[0], &notarizes, &Sequential).unwrap();
2690        let encoded = notarization.encode();
2691        let cfg = fixture.schemes[0].certificate_codec_config();
2692        let decoded = Notarization::decode_cfg(encoded, &cfg).unwrap();
2693        assert_eq!(notarization, decoded);
2694        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2695    }
2696
2697    #[test]
2698    fn test_notarization_encode_decode() {
2699        notarization_encode_decode(ed25519::fixture);
2700        notarization_encode_decode(secp256r1::fixture);
2701        notarization_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2702        notarization_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2703        notarization_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2704        notarization_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2705        notarization_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2706        notarization_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2707    }
2708
2709    fn nullify_encode_decode<S, F>(fixture: F)
2710    where
2711        S: Scheme<Sha256>,
2712        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2713    {
2714        let mut rng = test_rng();
2715        let fixture = fixture(&mut rng, NAMESPACE, 5);
2716        let round = Round::new(Epoch::new(0), View::new(10));
2717        let nullify = Nullify::sign::<Sha256>(&fixture.schemes[0], round).unwrap();
2718        let encoded = nullify.encode();
2719        let decoded = Nullify::decode(encoded).unwrap();
2720        assert_eq!(nullify, decoded);
2721        assert!(decoded.verify::<_, Sha256>(&mut rng, &fixture.schemes[0], &Sequential));
2722    }
2723
2724    #[test]
2725    fn test_nullify_encode_decode() {
2726        nullify_encode_decode(ed25519::fixture);
2727        nullify_encode_decode(secp256r1::fixture);
2728        nullify_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2729        nullify_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2730        nullify_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2731        nullify_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2732        nullify_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2733        nullify_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2734    }
2735
2736    fn nullification_encode_decode<S, F>(fixture: F)
2737    where
2738        S: Scheme<Sha256>,
2739        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2740    {
2741        let mut rng = test_rng();
2742        let fixture = fixture(&mut rng, NAMESPACE, 5);
2743        let round = Round::new(Epoch::new(333), View::new(10));
2744        let nullifies: Vec<_> = fixture
2745            .schemes
2746            .iter()
2747            .map(|scheme| Nullify::sign::<Sha256>(scheme, round).unwrap())
2748            .collect();
2749        let nullification =
2750            Nullification::from_nullifies(&fixture.schemes[0], &nullifies, &Sequential).unwrap();
2751        let encoded = nullification.encode();
2752        let cfg = fixture.schemes[0].certificate_codec_config();
2753        let decoded = Nullification::decode_cfg(encoded, &cfg).unwrap();
2754        assert_eq!(nullification, decoded);
2755        assert!(decoded.verify::<_, Sha256>(&mut rng, &fixture.schemes[0], &Sequential));
2756    }
2757
2758    #[test]
2759    fn test_nullification_encode_decode() {
2760        nullification_encode_decode(ed25519::fixture);
2761        nullification_encode_decode(secp256r1::fixture);
2762        nullification_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2763        nullification_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2764        nullification_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2765        nullification_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2766        nullification_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2767        nullification_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2768    }
2769
2770    fn finalize_encode_decode<S, F>(fixture: F)
2771    where
2772        S: Scheme<Sha256>,
2773        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2774    {
2775        let mut rng = test_rng();
2776        let fixture = fixture(&mut rng, NAMESPACE, 5);
2777        let round = Round::new(Epoch::new(0), View::new(10));
2778        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
2779        let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
2780        let encoded = finalize.encode();
2781        let decoded = Finalize::decode(encoded).unwrap();
2782        assert_eq!(finalize, decoded);
2783        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2784    }
2785
2786    #[test]
2787    fn test_finalize_encode_decode() {
2788        finalize_encode_decode(ed25519::fixture);
2789        finalize_encode_decode(secp256r1::fixture);
2790        finalize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2791        finalize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2792        finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2793        finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2794        finalize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2795        finalize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2796    }
2797
2798    fn finalization_encode_decode<S, F>(fixture: F)
2799    where
2800        S: Scheme<Sha256>,
2801        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2802    {
2803        let mut rng = test_rng();
2804        let fixture = fixture(&mut rng, NAMESPACE, 5);
2805        let round = Round::new(Epoch::new(0), View::new(10));
2806        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
2807        let finalizes: Vec<_> = fixture
2808            .schemes
2809            .iter()
2810            .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
2811            .collect();
2812        let finalization =
2813            Finalization::from_finalizes(&fixture.schemes[0], &finalizes, &Sequential).unwrap();
2814        let encoded = finalization.encode();
2815        let cfg = fixture.schemes[0].certificate_codec_config();
2816        let decoded = Finalization::decode_cfg(encoded, &cfg).unwrap();
2817        assert_eq!(finalization, decoded);
2818        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2819    }
2820
2821    #[test]
2822    fn test_finalization_encode_decode() {
2823        finalization_encode_decode(ed25519::fixture);
2824        finalization_encode_decode(secp256r1::fixture);
2825        finalization_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2826        finalization_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2827        finalization_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2828        finalization_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2829        finalization_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2830        finalization_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2831    }
2832
2833    fn backfiller_encode_decode<S, F>(fixture: F)
2834    where
2835        S: Scheme<Sha256>,
2836        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2837    {
2838        let mut rng = test_rng();
2839        let fixture = fixture(&mut rng, NAMESPACE, 5);
2840        let cfg = fixture.schemes[0].certificate_codec_config();
2841        let request = Request::new(
2842            1,
2843            vec![View::new(10), View::new(11)],
2844            vec![View::new(12), View::new(13)],
2845        );
2846        let encoded_request = Backfiller::<S, Sha256>::Request(request.clone()).encode();
2847        let decoded_request =
2848            Backfiller::<S, Sha256>::decode_cfg(encoded_request, &(usize::MAX, cfg.clone()))
2849                .unwrap();
2850        assert!(matches!(decoded_request, Backfiller::Request(r) if r == request));
2851
2852        let round = Round::new(Epoch::new(0), View::new(10));
2853        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
2854        let notarizes: Vec<_> = fixture
2855            .schemes
2856            .iter()
2857            .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
2858            .collect();
2859        let notarization =
2860            Notarization::from_notarizes(&fixture.schemes[0], &notarizes, &Sequential).unwrap();
2861
2862        let nullifies: Vec<_> = fixture
2863            .schemes
2864            .iter()
2865            .map(|scheme| Nullify::sign::<Sha256>(scheme, round).unwrap())
2866            .collect();
2867        let nullification =
2868            Nullification::from_nullifies(&fixture.schemes[0], &nullifies, &Sequential).unwrap();
2869
2870        let response = Response::<S, Sha256>::new(1, vec![notarization], vec![nullification]);
2871        let encoded_response = Backfiller::<S, Sha256>::Response(response.clone()).encode();
2872        let decoded_response =
2873            Backfiller::<S, Sha256>::decode_cfg(encoded_response, &(usize::MAX, cfg)).unwrap();
2874        assert!(matches!(decoded_response, Backfiller::Response(r) if r.id == response.id));
2875    }
2876
2877    #[test]
2878    fn test_backfiller_encode_decode() {
2879        backfiller_encode_decode(ed25519::fixture);
2880        backfiller_encode_decode(secp256r1::fixture);
2881        backfiller_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2882        backfiller_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2883        backfiller_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2884        backfiller_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2885        backfiller_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2886        backfiller_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2887    }
2888
2889    #[test]
2890    fn test_request_encode_decode() {
2891        let request = Request::new(
2892            1,
2893            vec![View::new(10), View::new(11)],
2894            vec![View::new(12), View::new(13)],
2895        );
2896        let encoded = request.encode();
2897        let decoded = Request::decode_cfg(encoded, &usize::MAX).unwrap();
2898        assert_eq!(request, decoded);
2899    }
2900
2901    fn response_encode_decode<S, F>(fixture: F)
2902    where
2903        S: Scheme<Sha256>,
2904        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2905    {
2906        let mut rng = test_rng();
2907        let fixture = fixture(&mut rng, NAMESPACE, 5);
2908        let round = Round::new(Epoch::new(0), View::new(10));
2909        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
2910
2911        let notarizes: Vec<_> = fixture
2912            .schemes
2913            .iter()
2914            .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
2915            .collect();
2916        let notarization =
2917            Notarization::from_notarizes(&fixture.schemes[0], &notarizes, &Sequential).unwrap();
2918
2919        let nullifies: Vec<_> = fixture
2920            .schemes
2921            .iter()
2922            .map(|scheme| Nullify::sign::<Sha256>(scheme, round).unwrap())
2923            .collect();
2924        let nullification =
2925            Nullification::from_nullifies(&fixture.schemes[0], &nullifies, &Sequential).unwrap();
2926
2927        let response = Response::<S, Sha256>::new(1, vec![notarization], vec![nullification]);
2928        let cfg = fixture.schemes[0].certificate_codec_config();
2929        let mut decoded =
2930            Response::<S, Sha256>::decode_cfg(response.encode(), &(usize::MAX, cfg)).unwrap();
2931        assert_eq!(response.id, decoded.id);
2932        assert_eq!(response.notarizations.len(), decoded.notarizations.len());
2933        assert_eq!(response.nullifications.len(), decoded.nullifications.len());
2934
2935        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2936
2937        decoded.nullifications[0].round = Round::new(
2938            decoded.nullifications[0].round.epoch(),
2939            decoded.nullifications[0].round.view().next(),
2940        );
2941        assert!(!decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2942    }
2943
2944    #[test]
2945    fn test_response_encode_decode() {
2946        response_encode_decode(ed25519::fixture);
2947        response_encode_decode(secp256r1::fixture);
2948        response_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2949        response_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2950        response_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2951        response_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2952        response_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2953        response_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2954    }
2955
2956    fn conflicting_notarize_encode_decode<S, F>(fixture: F)
2957    where
2958        S: Scheme<Sha256>,
2959        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
2960    {
2961        let mut rng = test_rng();
2962        let fixture = fixture(&mut rng, NAMESPACE, 5);
2963        let proposal1 = Proposal::new(
2964            Round::new(Epoch::new(0), View::new(10)),
2965            View::new(5),
2966            sample_digest(1),
2967        );
2968        let proposal2 = Proposal::new(
2969            Round::new(Epoch::new(0), View::new(10)),
2970            View::new(5),
2971            sample_digest(2),
2972        );
2973        let notarize1 = Notarize::sign(&fixture.schemes[0], proposal1).unwrap();
2974        let notarize2 = Notarize::sign(&fixture.schemes[0], proposal2).unwrap();
2975        let conflicting = ConflictingNotarize::new(notarize1, notarize2);
2976
2977        let encoded = conflicting.encode();
2978        let decoded = ConflictingNotarize::<S, Sha256>::decode(encoded).unwrap();
2979
2980        assert_eq!(conflicting, decoded);
2981        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
2982    }
2983
2984    #[test]
2985    fn test_conflicting_notarize_encode_decode() {
2986        conflicting_notarize_encode_decode(ed25519::fixture);
2987        conflicting_notarize_encode_decode(secp256r1::fixture);
2988        conflicting_notarize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
2989        conflicting_notarize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
2990        conflicting_notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
2991        conflicting_notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
2992        conflicting_notarize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
2993        conflicting_notarize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
2994    }
2995
2996    fn conflicting_finalize_encode_decode<S, F>(fixture: F)
2997    where
2998        S: Scheme<Sha256>,
2999        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
3000    {
3001        let mut rng = test_rng();
3002        let fixture = fixture(&mut rng, NAMESPACE, 5);
3003        let proposal1 = Proposal::new(
3004            Round::new(Epoch::new(0), View::new(10)),
3005            View::new(5),
3006            sample_digest(1),
3007        );
3008        let proposal2 = Proposal::new(
3009            Round::new(Epoch::new(0), View::new(10)),
3010            View::new(5),
3011            sample_digest(2),
3012        );
3013        let finalize1 = Finalize::sign(&fixture.schemes[0], proposal1).unwrap();
3014        let finalize2 = Finalize::sign(&fixture.schemes[0], proposal2).unwrap();
3015        let conflicting = ConflictingFinalize::new(finalize1, finalize2);
3016
3017        let encoded = conflicting.encode();
3018        let decoded = ConflictingFinalize::<S, Sha256>::decode(encoded).unwrap();
3019
3020        assert_eq!(conflicting, decoded);
3021        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
3022    }
3023
3024    #[test]
3025    fn test_conflicting_finalize_encode_decode() {
3026        conflicting_finalize_encode_decode(ed25519::fixture);
3027        conflicting_finalize_encode_decode(secp256r1::fixture);
3028        conflicting_finalize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
3029        conflicting_finalize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
3030        conflicting_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
3031        conflicting_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
3032        conflicting_finalize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
3033        conflicting_finalize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
3034    }
3035
3036    fn nullify_finalize_encode_decode<S, F>(fixture: F)
3037    where
3038        S: Scheme<Sha256>,
3039        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
3040    {
3041        let mut rng = test_rng();
3042        let fixture = fixture(&mut rng, NAMESPACE, 5);
3043        let round = Round::new(Epoch::new(0), View::new(10));
3044        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
3045        let nullify = Nullify::sign::<Sha256>(&fixture.schemes[0], round).unwrap();
3046        let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
3047        let conflict = NullifyFinalize::new(nullify, finalize);
3048
3049        let encoded = conflict.encode();
3050        let decoded = NullifyFinalize::<S, Sha256>::decode(encoded).unwrap();
3051
3052        assert_eq!(conflict, decoded);
3053        assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
3054    }
3055
3056    #[test]
3057    fn test_nullify_finalize_encode_decode() {
3058        nullify_finalize_encode_decode(ed25519::fixture);
3059        nullify_finalize_encode_decode(secp256r1::fixture);
3060        nullify_finalize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
3061        nullify_finalize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
3062        nullify_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
3063        nullify_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
3064        nullify_finalize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
3065        nullify_finalize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
3066    }
3067
3068    fn notarize_verify_wrong_namespace<S, F>(f: F)
3069    where
3070        S: Scheme<Sha256>,
3071        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3072    {
3073        // Create two fixtures with different namespaces
3074        let mut rng = test_rng();
3075        let fixture = setup_seeded_ns(5, 0, NAMESPACE, &f);
3076        let wrong_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
3077        let round = Round::new(Epoch::new(0), View::new(10));
3078        let proposal = Proposal::new(round, View::new(5), sample_digest(1));
3079        let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
3080
3081        assert!(notarize.verify(&mut rng, &fixture.schemes[0], &Sequential));
3082        assert!(!notarize.verify(&mut rng, &wrong_fixture.schemes[0], &Sequential));
3083    }
3084
3085    #[test]
3086    fn test_notarize_verify_wrong_namespace() {
3087        notarize_verify_wrong_namespace(ed25519::fixture);
3088        notarize_verify_wrong_namespace(secp256r1::fixture);
3089        notarize_verify_wrong_namespace(bls12381_multisig::fixture::<MinPk, _>);
3090        notarize_verify_wrong_namespace(bls12381_multisig::fixture::<MinSig, _>);
3091        notarize_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinPk, _>);
3092        notarize_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinSig, _>);
3093        notarize_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinPk, _>);
3094        notarize_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinSig, _>);
3095    }
3096
3097    fn notarize_verify_wrong_scheme<S, F>(f: F)
3098    where
3099        S: Scheme<Sha256>,
3100        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3101    {
3102        let mut rng = test_rng();
3103        let fixture = setup_seeded(5, 0, &f);
3104        let wrong_fixture = setup_seeded(5, 1, &f);
3105        let round = Round::new(Epoch::new(0), View::new(10));
3106        let proposal = Proposal::new(round, View::new(5), sample_digest(2));
3107        let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
3108
3109        assert!(notarize.verify(&mut rng, &fixture.schemes[0], &Sequential));
3110        assert!(!notarize.verify(&mut rng, &wrong_fixture.verifier, &Sequential));
3111    }
3112
3113    #[test]
3114    fn test_notarize_verify_wrong_scheme() {
3115        notarize_verify_wrong_scheme(ed25519::fixture);
3116        notarize_verify_wrong_scheme(secp256r1::fixture);
3117        notarize_verify_wrong_scheme(bls12381_multisig::fixture::<MinPk, _>);
3118        notarize_verify_wrong_scheme(bls12381_multisig::fixture::<MinSig, _>);
3119        notarize_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinPk, _>);
3120        notarize_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinSig, _>);
3121        notarize_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinPk, _>);
3122        notarize_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinSig, _>);
3123    }
3124
3125    fn notarization_verify_wrong_scheme<S, F>(f: F)
3126    where
3127        S: Scheme<Sha256>,
3128        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3129    {
3130        let mut rng = test_rng();
3131        let fixture = setup_seeded(5, 0, &f);
3132        let wrong_fixture = setup_seeded(5, 1, &f);
3133        let round = Round::new(Epoch::new(0), View::new(10));
3134        let proposal = Proposal::new(round, View::new(5), sample_digest(3));
3135        let quorum = N3f1::quorum(fixture.schemes.len()) as usize;
3136        let notarizes: Vec<_> = fixture
3137            .schemes
3138            .iter()
3139            .take(quorum)
3140            .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
3141            .collect();
3142
3143        let notarization =
3144            Notarization::from_notarizes(&fixture.schemes[0], &notarizes, &Sequential)
3145                .expect("quorum notarization");
3146        assert!(notarization.verify(&mut rng, &fixture.schemes[0], &Sequential));
3147        assert!(!notarization.verify(&mut rng, &wrong_fixture.verifier, &Sequential));
3148    }
3149
3150    #[test]
3151    fn test_notarization_verify_wrong_scheme() {
3152        notarization_verify_wrong_scheme(ed25519::fixture);
3153        notarization_verify_wrong_scheme(secp256r1::fixture);
3154        notarization_verify_wrong_scheme(bls12381_multisig::fixture::<MinPk, _>);
3155        notarization_verify_wrong_scheme(bls12381_multisig::fixture::<MinSig, _>);
3156        notarization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinPk, _>);
3157        notarization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinSig, _>);
3158        notarization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinPk, _>);
3159        notarization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinSig, _>);
3160    }
3161
3162    fn notarization_verify_wrong_namespace<S, F>(f: F)
3163    where
3164        S: Scheme<Sha256>,
3165        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3166    {
3167        // Create two fixtures with different namespaces
3168        let fixture = setup_seeded_ns(5, 0, NAMESPACE, &f);
3169        let wrong_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
3170        let mut rng = test_rng();
3171        let round = Round::new(Epoch::new(0), View::new(10));
3172        let proposal = Proposal::new(round, View::new(5), sample_digest(4));
3173        let quorum = N3f1::quorum(fixture.schemes.len()) as usize;
3174        let notarizes: Vec<_> = fixture
3175            .schemes
3176            .iter()
3177            .take(quorum)
3178            .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
3179            .collect();
3180
3181        let notarization =
3182            Notarization::from_notarizes(&fixture.schemes[0], &notarizes, &Sequential)
3183                .expect("quorum notarization");
3184        assert!(notarization.verify(&mut rng, &fixture.schemes[0], &Sequential));
3185
3186        assert!(!notarization.verify(&mut rng, &wrong_fixture.schemes[0], &Sequential));
3187    }
3188
3189    #[test]
3190    fn test_notarization_verify_wrong_namespace() {
3191        notarization_verify_wrong_namespace(ed25519::fixture);
3192        notarization_verify_wrong_namespace(secp256r1::fixture);
3193        notarization_verify_wrong_namespace(bls12381_multisig::fixture::<MinPk, _>);
3194        notarization_verify_wrong_namespace(bls12381_multisig::fixture::<MinSig, _>);
3195        notarization_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinPk, _>);
3196        notarization_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinSig, _>);
3197        notarization_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinPk, _>);
3198        notarization_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinSig, _>);
3199    }
3200
3201    fn notarization_recover_insufficient_signatures<S, F>(fixture: F)
3202    where
3203        S: Scheme<Sha256>,
3204        F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
3205    {
3206        let mut rng = test_rng();
3207        let fixture = fixture(&mut rng, NAMESPACE, 5);
3208        let quorum_size = quorum(fixture.schemes.len() as u32) as usize;
3209        assert!(quorum_size > 1, "test requires quorum larger than one");
3210        let round = Round::new(Epoch::new(0), View::new(10));
3211        let proposal = Proposal::new(round, View::new(5), sample_digest(5));
3212        let notarizes: Vec<_> = fixture
3213            .schemes
3214            .iter()
3215            .take(quorum_size - 1)
3216            .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
3217            .collect();
3218
3219        assert!(
3220            Notarization::from_notarizes(&fixture.schemes[0], &notarizes, &Sequential).is_none(),
3221            "insufficient votes should not form a notarization"
3222        );
3223    }
3224
3225    #[test]
3226    fn test_notarization_recover_insufficient_signatures() {
3227        notarization_recover_insufficient_signatures(ed25519::fixture);
3228        notarization_recover_insufficient_signatures(secp256r1::fixture);
3229        notarization_recover_insufficient_signatures(bls12381_multisig::fixture::<MinPk, _>);
3230        notarization_recover_insufficient_signatures(bls12381_multisig::fixture::<MinSig, _>);
3231        notarization_recover_insufficient_signatures(bls12381_threshold_vrf::fixture::<MinPk, _>);
3232        notarization_recover_insufficient_signatures(bls12381_threshold_vrf::fixture::<MinSig, _>);
3233        notarization_recover_insufficient_signatures(bls12381_threshold_std::fixture::<MinPk, _>);
3234        notarization_recover_insufficient_signatures(bls12381_threshold_std::fixture::<MinSig, _>);
3235    }
3236
3237    fn conflicting_notarize_detection<S, F>(f: F)
3238    where
3239        S: Scheme<Sha256>,
3240        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3241    {
3242        let mut rng = test_rng();
3243        let fixture = setup_seeded(5, 0, &f);
3244        let wrong_ns_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
3245        let wrong_scheme_fixture = setup_seeded(5, 1, &f);
3246
3247        let round = Round::new(Epoch::new(0), View::new(10));
3248        let proposal1 = Proposal::new(round, View::new(5), sample_digest(6));
3249        let proposal2 = Proposal::new(round, View::new(5), sample_digest(7));
3250
3251        let notarize1 = Notarize::sign(&fixture.schemes[0], proposal1).unwrap();
3252        let notarize2 = Notarize::sign(&fixture.schemes[0], proposal2).unwrap();
3253        let conflict = ConflictingNotarize::new(notarize1, notarize2);
3254
3255        assert!(conflict.verify(&mut rng, &fixture.schemes[0], &Sequential));
3256        assert!(!conflict.verify(&mut rng, &wrong_ns_fixture.schemes[0], &Sequential));
3257        assert!(!conflict.verify(&mut rng, &wrong_scheme_fixture.verifier, &Sequential));
3258    }
3259
3260    #[test]
3261    fn test_conflicting_notarize_detection() {
3262        conflicting_notarize_detection(ed25519::fixture);
3263        conflicting_notarize_detection(secp256r1::fixture);
3264        conflicting_notarize_detection(bls12381_multisig::fixture::<MinPk, _>);
3265        conflicting_notarize_detection(bls12381_multisig::fixture::<MinSig, _>);
3266        conflicting_notarize_detection(bls12381_threshold_vrf::fixture::<MinPk, _>);
3267        conflicting_notarize_detection(bls12381_threshold_vrf::fixture::<MinSig, _>);
3268        conflicting_notarize_detection(bls12381_threshold_std::fixture::<MinPk, _>);
3269        conflicting_notarize_detection(bls12381_threshold_std::fixture::<MinSig, _>);
3270    }
3271
3272    fn nullify_finalize_detection<S, F>(f: F)
3273    where
3274        S: Scheme<Sha256>,
3275        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3276    {
3277        let mut rng = test_rng();
3278        let fixture = setup_seeded(5, 0, &f);
3279        let wrong_ns_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
3280        let wrong_scheme_fixture = setup_seeded(5, 1, &f);
3281
3282        let round = Round::new(Epoch::new(0), View::new(10));
3283        let proposal = Proposal::new(round, View::new(5), sample_digest(8));
3284
3285        let nullify = Nullify::sign::<Sha256>(&fixture.schemes[0], round).unwrap();
3286        let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
3287        let conflict = NullifyFinalize::new(nullify, finalize);
3288
3289        assert!(conflict.verify(&mut rng, &fixture.schemes[0], &Sequential));
3290        assert!(!conflict.verify(&mut rng, &wrong_ns_fixture.schemes[0], &Sequential));
3291        assert!(!conflict.verify(&mut rng, &wrong_scheme_fixture.verifier, &Sequential));
3292    }
3293
3294    #[test]
3295    fn test_nullify_finalize_detection() {
3296        nullify_finalize_detection(ed25519::fixture);
3297        nullify_finalize_detection(secp256r1::fixture);
3298        nullify_finalize_detection(bls12381_multisig::fixture::<MinPk, _>);
3299        nullify_finalize_detection(bls12381_multisig::fixture::<MinSig, _>);
3300        nullify_finalize_detection(bls12381_threshold_vrf::fixture::<MinPk, _>);
3301        nullify_finalize_detection(bls12381_threshold_vrf::fixture::<MinSig, _>);
3302        nullify_finalize_detection(bls12381_threshold_std::fixture::<MinPk, _>);
3303        nullify_finalize_detection(bls12381_threshold_std::fixture::<MinSig, _>);
3304    }
3305
3306    fn finalization_verify_wrong_scheme<S, F>(f: F)
3307    where
3308        S: Scheme<Sha256>,
3309        F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
3310    {
3311        let mut rng = test_rng();
3312        let fixture = setup_seeded(5, 0, &f);
3313        let wrong_fixture = setup_seeded(5, 1, &f);
3314        let round = Round::new(Epoch::new(0), View::new(10));
3315        let proposal = Proposal::new(round, View::new(5), sample_digest(9));
3316        let quorum = N3f1::quorum(fixture.schemes.len()) as usize;
3317        let finalizes: Vec<_> = fixture
3318            .schemes
3319            .iter()
3320            .take(quorum)
3321            .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
3322            .collect();
3323
3324        let finalization =
3325            Finalization::from_finalizes(&fixture.schemes[0], &finalizes, &Sequential)
3326                .expect("quorum finalization");
3327        assert!(finalization.verify(&mut rng, &fixture.schemes[0], &Sequential));
3328        assert!(!finalization.verify(&mut rng, &wrong_fixture.verifier, &Sequential));
3329    }
3330
3331    #[test]
3332    fn test_finalization_wrong_scheme() {
3333        finalization_verify_wrong_scheme(ed25519::fixture);
3334        finalization_verify_wrong_scheme(secp256r1::fixture);
3335        finalization_verify_wrong_scheme(bls12381_multisig::fixture::<MinPk, _>);
3336        finalization_verify_wrong_scheme(bls12381_multisig::fixture::<MinSig, _>);
3337        finalization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinPk, _>);
3338        finalization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinSig, _>);
3339        finalization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinPk, _>);
3340        finalization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinSig, _>);
3341    }
3342
3343    struct MockAttributable(Participant);
3344
3345    impl Attributable for MockAttributable {
3346        fn signer(&self) -> Participant {
3347            self.0
3348        }
3349    }
3350
3351    #[test]
3352    fn test_attributable_map() {
3353        let mut map = AttributableMap::new(5);
3354        assert_eq!(map.len(), 0);
3355        assert!(map.is_empty());
3356
3357        // Test get on empty map
3358        for i in 0..5 {
3359            assert!(map.get(Participant::new(i)).is_none());
3360        }
3361
3362        assert!(map.insert(MockAttributable(Participant::new(3))));
3363        assert_eq!(map.len(), 1);
3364        assert!(!map.is_empty());
3365        let mut iter = map.iter();
3366        assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(3)));
3367        assert!(iter.next().is_none());
3368        drop(iter);
3369
3370        // Test get on existing item
3371        assert!(
3372            matches!(map.get(Participant::new(3)), Some(a) if a.signer() == Participant::new(3))
3373        );
3374
3375        assert!(map.insert(MockAttributable(Participant::new(1))));
3376        assert_eq!(map.len(), 2);
3377        assert!(!map.is_empty());
3378        let mut iter = map.iter();
3379        assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(1)));
3380        assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(3)));
3381        assert!(iter.next().is_none());
3382        drop(iter);
3383
3384        // Test get on both items
3385        assert!(
3386            matches!(map.get(Participant::new(1)), Some(a) if a.signer() == Participant::new(1))
3387        );
3388        assert!(
3389            matches!(map.get(Participant::new(3)), Some(a) if a.signer() == Participant::new(3))
3390        );
3391
3392        // Test get on non-existing items
3393        assert!(map.get(Participant::new(0)).is_none());
3394        assert!(map.get(Participant::new(2)).is_none());
3395        assert!(map.get(Participant::new(4)).is_none());
3396
3397        assert!(!map.insert(MockAttributable(Participant::new(3))));
3398        assert_eq!(map.len(), 2);
3399        assert!(!map.is_empty());
3400        let mut iter = map.iter();
3401        assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(1)));
3402        assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(3)));
3403        assert!(iter.next().is_none());
3404        drop(iter);
3405
3406        // Test out-of-bounds signer indices
3407        assert!(!map.insert(MockAttributable(Participant::new(5))));
3408        assert!(!map.insert(MockAttributable(Participant::new(100))));
3409        assert_eq!(map.len(), 2);
3410
3411        // Test clear
3412        map.clear();
3413        assert_eq!(map.len(), 0);
3414        assert!(map.is_empty());
3415        assert!(map.iter().next().is_none());
3416
3417        // Verify can insert after clear
3418        assert!(map.insert(MockAttributable(Participant::new(2))));
3419        assert_eq!(map.len(), 1);
3420        let mut iter = map.iter();
3421        assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(2)));
3422        assert!(iter.next().is_none());
3423    }
3424
3425    #[test]
3426    #[should_panic(expected = "proposals must differ")]
3427    fn issue_2944_regression_conflicting_notarize_new() {
3428        let mut rng = test_rng();
3429        let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
3430        let proposal = Proposal::new(
3431            Round::new(Epoch::new(0), View::new(10)),
3432            View::new(5),
3433            sample_digest(1),
3434        );
3435        let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
3436        let _ = ConflictingNotarize::new(notarize.clone(), notarize);
3437    }
3438
3439    #[test]
3440    fn issue_2944_regression_conflicting_notarize_decode() {
3441        let mut rng = test_rng();
3442        let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
3443        let proposal = Proposal::new(
3444            Round::new(Epoch::new(0), View::new(10)),
3445            View::new(5),
3446            sample_digest(1),
3447        );
3448        let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
3449
3450        // Manually encode two identical notarizes
3451        let mut buf = Vec::new();
3452        notarize.write(&mut buf);
3453        notarize.write(&mut buf);
3454
3455        // Decoding should fail
3456        let result = ConflictingNotarize::<ed25519::Scheme, Sha256>::decode(Bytes::from(buf));
3457        assert!(result.is_err());
3458    }
3459
3460    #[test]
3461    #[should_panic(expected = "proposals must differ")]
3462    fn issue_2944_regression_conflicting_finalize_new() {
3463        let mut rng = test_rng();
3464        let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
3465        let proposal = Proposal::new(
3466            Round::new(Epoch::new(0), View::new(10)),
3467            View::new(5),
3468            sample_digest(1),
3469        );
3470        let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
3471        let _ = ConflictingFinalize::new(finalize.clone(), finalize);
3472    }
3473
3474    #[test]
3475    fn issue_2944_regression_conflicting_finalize_decode() {
3476        let mut rng = test_rng();
3477        let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
3478        let proposal = Proposal::new(
3479            Round::new(Epoch::new(0), View::new(10)),
3480            View::new(5),
3481            sample_digest(1),
3482        );
3483        let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
3484
3485        // Manually encode two identical finalizes
3486        let mut buf = Vec::new();
3487        finalize.write(&mut buf);
3488        finalize.write(&mut buf);
3489
3490        // Decoding should fail
3491        let result = ConflictingFinalize::<ed25519::Scheme, Sha256>::decode(Bytes::from(buf));
3492        assert!(result.is_err());
3493    }
3494
3495    #[cfg(feature = "arbitrary")]
3496    mod conformance {
3497        use super::*;
3498        use crate::simplex::scheme::bls12381_threshold::vrf as bls12381_threshold_vrf;
3499        use commonware_codec::conformance::CodecConformance;
3500        use commonware_cryptography::{ed25519::PublicKey, sha256::Digest as Sha256Digest};
3501
3502        type Scheme = bls12381_threshold_vrf::Scheme<PublicKey, MinSig>;
3503
3504        commonware_conformance::conformance_tests! {
3505            CodecConformance<Vote<Scheme, Sha256Digest>>,
3506            CodecConformance<Certificate<Scheme, Sha256Digest>>,
3507            CodecConformance<Artifact<Scheme, Sha256Digest>>,
3508            CodecConformance<Proposal<Sha256Digest>>,
3509            CodecConformance<Notarize<Scheme, Sha256Digest>>,
3510            CodecConformance<Notarization<Scheme, Sha256Digest>>,
3511            CodecConformance<Nullify<Scheme>>,
3512            CodecConformance<Nullification<Scheme>>,
3513            CodecConformance<Finalize<Scheme, Sha256Digest>>,
3514            CodecConformance<Finalization<Scheme, Sha256Digest>>,
3515            CodecConformance<Backfiller<Scheme, Sha256Digest>>,
3516            CodecConformance<Request>,
3517            CodecConformance<Response<Scheme, Sha256Digest>>,
3518            CodecConformance<Activity<Scheme, Sha256Digest>>,
3519            CodecConformance<ConflictingNotarize<Scheme, Sha256Digest>>,
3520            CodecConformance<ConflictingFinalize<Scheme, Sha256Digest>>,
3521            CodecConformance<NullifyFinalize<Scheme, Sha256Digest>>,
3522            CodecConformance<Context<Sha256Digest, PublicKey>>
3523        }
3524    }
3525}