commonware_consensus/simplex/
types.rs

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