commonware_consensus/simplex/
types.rs

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