Skip to main content

commonware_consensus/simplex/
types.rs

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