Skip to main content

commonware_consensus/simplex/
types.rs

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