use crate::{
simplex::scheme,
types::{Epoch, Participant, Round, View},
Epochable, Viewable,
};
use bytes::{Buf, BufMut};
use commonware_codec::{varint::UInt, EncodeSize, Error, Read, ReadExt, ReadRangeExt, Write};
use commonware_cryptography::{
certificate::{Attestation, Scheme},
Digest, PublicKey,
};
use commonware_parallel::Strategy;
use commonware_utils::N3f1;
use rand_core::CryptoRngCore;
use std::{collections::HashSet, fmt::Debug, hash::Hash};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Context<D: Digest, P: PublicKey> {
pub round: Round,
pub leader: P,
pub parent: (View, D),
}
impl<D: Digest, P: PublicKey> Epochable for Context<D, P> {
fn epoch(&self) -> Epoch {
self.round.epoch()
}
}
impl<D: Digest, P: PublicKey> Viewable for Context<D, P> {
fn view(&self) -> View {
self.round.view()
}
}
impl<D: Digest, P: PublicKey> Write for Context<D, P> {
fn write(&self, buf: &mut impl BufMut) {
self.round.write(buf);
self.leader.write(buf);
self.parent.write(buf);
}
}
impl<D: Digest, P: PublicKey> EncodeSize for Context<D, P> {
fn encode_size(&self) -> usize {
self.round.encode_size() + self.leader.encode_size() + self.parent.encode_size()
}
}
impl<D: Digest, P: PublicKey> Read for Context<D, P> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let round = Round::read(reader)?;
let leader = P::read(reader)?;
let parent = <(View, D)>::read_cfg(reader, &((), ()))?;
Ok(Self {
round,
leader,
parent,
})
}
}
#[cfg(feature = "arbitrary")]
impl<D: Digest, P: PublicKey> arbitrary::Arbitrary<'_> for Context<D, P>
where
D: for<'a> arbitrary::Arbitrary<'a>,
P: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self {
round: Round::arbitrary(u)?,
leader: P::arbitrary(u)?,
parent: (View::arbitrary(u)?, D::arbitrary(u)?),
})
}
}
pub trait Attributable {
fn signer(&self) -> Participant;
}
pub struct AttributableMap<T: Attributable> {
data: Vec<Option<T>>,
added: usize,
}
impl<T: Attributable> AttributableMap<T> {
pub fn new(participants: usize) -> Self {
let mut data = Vec::with_capacity(participants);
data.resize_with(participants, || None);
Self { data, added: 0 }
}
pub fn clear(&mut self) {
self.data.fill_with(|| None);
self.added = 0;
}
pub fn insert(&mut self, item: T) -> bool {
let index: usize = item.signer().into();
if index >= self.data.len() {
return false;
}
if self.data[index].is_some() {
return false;
}
self.data[index] = Some(item);
self.added += 1;
true
}
pub const fn len(&self) -> usize {
self.added
}
pub const fn is_empty(&self) -> bool {
self.added == 0
}
pub fn get(&self, signer: Participant) -> Option<&T> {
self.data.get(<usize>::from(signer))?.as_ref()
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.iter().filter_map(|o| o.as_ref())
}
}
pub struct VoteTracker<S: Scheme, D: Digest> {
notarizes: AttributableMap<Notarize<S, D>>,
nullifies: AttributableMap<Nullify<S>>,
finalizes: AttributableMap<Finalize<S, D>>,
}
impl<S: Scheme, D: Digest> VoteTracker<S, D> {
pub fn new(participants: usize) -> Self {
Self {
notarizes: AttributableMap::new(participants),
nullifies: AttributableMap::new(participants),
finalizes: AttributableMap::new(participants),
}
}
pub fn insert_notarize(&mut self, vote: Notarize<S, D>) -> bool {
self.notarizes.insert(vote)
}
pub fn insert_nullify(&mut self, vote: Nullify<S>) -> bool {
self.nullifies.insert(vote)
}
pub fn insert_finalize(&mut self, vote: Finalize<S, D>) -> bool {
self.finalizes.insert(vote)
}
pub fn notarize(&self, signer: Participant) -> Option<&Notarize<S, D>> {
self.notarizes.get(signer)
}
pub fn nullify(&self, signer: Participant) -> Option<&Nullify<S>> {
self.nullifies.get(signer)
}
pub fn finalize(&self, signer: Participant) -> Option<&Finalize<S, D>> {
self.finalizes.get(signer)
}
pub fn iter_notarizes(&self) -> impl Iterator<Item = &Notarize<S, D>> {
self.notarizes.iter()
}
pub fn iter_nullifies(&self) -> impl Iterator<Item = &Nullify<S>> {
self.nullifies.iter()
}
pub fn iter_finalizes(&self) -> impl Iterator<Item = &Finalize<S, D>> {
self.finalizes.iter()
}
pub fn len_notarizes(&self) -> u32 {
u32::try_from(self.notarizes.len()).expect("too many notarize votes")
}
pub fn len_nullifies(&self) -> u32 {
u32::try_from(self.nullifies.len()).expect("too many nullify votes")
}
pub fn len_finalizes(&self) -> u32 {
u32::try_from(self.finalizes.len()).expect("too many finalize votes")
}
pub fn has_notarize(&self, signer: Participant) -> bool {
self.notarizes.get(signer).is_some()
}
pub fn has_nullify(&self, signer: Participant) -> bool {
self.nullifies.get(signer).is_some()
}
pub fn has_finalize(&self, signer: Participant) -> bool {
self.finalizes.get(signer).is_some()
}
pub fn clear_notarizes(&mut self) {
self.notarizes.clear();
}
pub fn clear_finalizes(&mut self) {
self.finalizes.clear();
}
}
#[derive(Copy, Clone, Debug)]
pub enum Subject<'a, D: Digest> {
Notarize { proposal: &'a Proposal<D> },
Nullify { round: Round },
Finalize { proposal: &'a Proposal<D> },
}
impl<D: Digest> Viewable for Subject<'_, D> {
fn view(&self) -> View {
match self {
Subject::Notarize { proposal } => proposal.view(),
Subject::Nullify { round } => round.view(),
Subject::Finalize { proposal } => proposal.view(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Vote<S: Scheme, D: Digest> {
Notarize(Notarize<S, D>),
Nullify(Nullify<S>),
Finalize(Finalize<S, D>),
}
impl<S: Scheme, D: Digest> Write for Vote<S, D> {
fn write(&self, writer: &mut impl BufMut) {
match self {
Self::Notarize(v) => {
0u8.write(writer);
v.write(writer);
}
Self::Nullify(v) => {
1u8.write(writer);
v.write(writer);
}
Self::Finalize(v) => {
2u8.write(writer);
v.write(writer);
}
}
}
}
impl<S: Scheme, D: Digest> EncodeSize for Vote<S, D> {
fn encode_size(&self) -> usize {
1 + match self {
Self::Notarize(v) => v.encode_size(),
Self::Nullify(v) => v.encode_size(),
Self::Finalize(v) => v.encode_size(),
}
}
}
impl<S: Scheme, D: Digest> Read for Vote<S, D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let tag = <u8>::read(reader)?;
match tag {
0 => {
let v = Notarize::read(reader)?;
Ok(Self::Notarize(v))
}
1 => {
let v = Nullify::read(reader)?;
Ok(Self::Nullify(v))
}
2 => {
let v = Finalize::read(reader)?;
Ok(Self::Finalize(v))
}
_ => Err(Error::Invalid("consensus::simplex::Vote", "Invalid type")),
}
}
}
impl<S: Scheme, D: Digest> Epochable for Vote<S, D> {
fn epoch(&self) -> Epoch {
match self {
Self::Notarize(v) => v.epoch(),
Self::Nullify(v) => v.epoch(),
Self::Finalize(v) => v.epoch(),
}
}
}
impl<S: Scheme, D: Digest> Viewable for Vote<S, D> {
fn view(&self) -> View {
match self {
Self::Notarize(v) => v.view(),
Self::Nullify(v) => v.view(),
Self::Finalize(v) => v.view(),
}
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Vote<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let tag = u.int_in_range(0..=2)?;
match tag {
0 => {
let v = Notarize::arbitrary(u)?;
Ok(Self::Notarize(v))
}
1 => {
let v = Nullify::arbitrary(u)?;
Ok(Self::Nullify(v))
}
2 => {
let v = Finalize::arbitrary(u)?;
Ok(Self::Finalize(v))
}
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Certificate<S: Scheme, D: Digest> {
Notarization(Notarization<S, D>),
Nullification(Nullification<S>),
Finalization(Finalization<S, D>),
}
impl<S: Scheme, D: Digest> Write for Certificate<S, D> {
fn write(&self, writer: &mut impl BufMut) {
match self {
Self::Notarization(v) => {
0u8.write(writer);
v.write(writer);
}
Self::Nullification(v) => {
1u8.write(writer);
v.write(writer);
}
Self::Finalization(v) => {
2u8.write(writer);
v.write(writer);
}
}
}
}
impl<S: Scheme, D: Digest> EncodeSize for Certificate<S, D> {
fn encode_size(&self) -> usize {
1 + match self {
Self::Notarization(v) => v.encode_size(),
Self::Nullification(v) => v.encode_size(),
Self::Finalization(v) => v.encode_size(),
}
}
}
impl<S: Scheme, D: Digest> Read for Certificate<S, D> {
type Cfg = <S::Certificate as Read>::Cfg;
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let tag = <u8>::read(reader)?;
match tag {
0 => {
let v = Notarization::read_cfg(reader, cfg)?;
Ok(Self::Notarization(v))
}
1 => {
let v = Nullification::read_cfg(reader, cfg)?;
Ok(Self::Nullification(v))
}
2 => {
let v = Finalization::read_cfg(reader, cfg)?;
Ok(Self::Finalization(v))
}
_ => Err(Error::Invalid(
"consensus::simplex::Certificate",
"Invalid type",
)),
}
}
}
impl<S: Scheme, D: Digest> Epochable for Certificate<S, D> {
fn epoch(&self) -> Epoch {
match self {
Self::Notarization(v) => v.epoch(),
Self::Nullification(v) => v.epoch(),
Self::Finalization(v) => v.epoch(),
}
}
}
impl<S: Scheme, D: Digest> Viewable for Certificate<S, D> {
fn view(&self) -> View {
match self {
Self::Notarization(v) => v.view(),
Self::Nullification(v) => v.view(),
Self::Finalization(v) => v.view(),
}
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Certificate<S, D>
where
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let tag = u.int_in_range(0..=2)?;
match tag {
0 => {
let v = Notarization::arbitrary(u)?;
Ok(Self::Notarization(v))
}
1 => {
let v = Nullification::arbitrary(u)?;
Ok(Self::Nullification(v))
}
2 => {
let v = Finalization::arbitrary(u)?;
Ok(Self::Finalization(v))
}
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Artifact<S: Scheme, D: Digest> {
Notarize(Notarize<S, D>),
Notarization(Notarization<S, D>),
Certification(Round, bool),
Nullify(Nullify<S>),
Nullification(Nullification<S>),
Finalize(Finalize<S, D>),
Finalization(Finalization<S, D>),
}
impl<S: Scheme, D: Digest> Write for Artifact<S, D> {
fn write(&self, writer: &mut impl BufMut) {
match self {
Self::Notarize(v) => {
0u8.write(writer);
v.write(writer);
}
Self::Notarization(v) => {
1u8.write(writer);
v.write(writer);
}
Self::Certification(r, b) => {
2u8.write(writer);
r.write(writer);
b.write(writer);
}
Self::Nullify(v) => {
3u8.write(writer);
v.write(writer);
}
Self::Nullification(v) => {
4u8.write(writer);
v.write(writer);
}
Self::Finalize(v) => {
5u8.write(writer);
v.write(writer);
}
Self::Finalization(v) => {
6u8.write(writer);
v.write(writer);
}
}
}
}
impl<S: Scheme, D: Digest> EncodeSize for Artifact<S, D> {
fn encode_size(&self) -> usize {
1 + match self {
Self::Notarize(v) => v.encode_size(),
Self::Notarization(v) => v.encode_size(),
Self::Certification(r, b) => r.encode_size() + b.encode_size(),
Self::Nullify(v) => v.encode_size(),
Self::Nullification(v) => v.encode_size(),
Self::Finalize(v) => v.encode_size(),
Self::Finalization(v) => v.encode_size(),
}
}
}
impl<S: Scheme, D: Digest> Read for Artifact<S, D> {
type Cfg = <S::Certificate as Read>::Cfg;
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let tag = <u8>::read(reader)?;
match tag {
0 => {
let v = Notarize::read(reader)?;
Ok(Self::Notarize(v))
}
1 => {
let v = Notarization::read_cfg(reader, cfg)?;
Ok(Self::Notarization(v))
}
2 => {
let r = Round::read(reader)?;
let b = bool::read(reader)?;
Ok(Self::Certification(r, b))
}
3 => {
let v = Nullify::read(reader)?;
Ok(Self::Nullify(v))
}
4 => {
let v = Nullification::read_cfg(reader, cfg)?;
Ok(Self::Nullification(v))
}
5 => {
let v = Finalize::read(reader)?;
Ok(Self::Finalize(v))
}
6 => {
let v = Finalization::read_cfg(reader, cfg)?;
Ok(Self::Finalization(v))
}
_ => Err(Error::Invalid(
"consensus::simplex::Artifact",
"Invalid type",
)),
}
}
}
impl<S: Scheme, D: Digest> Epochable for Artifact<S, D> {
fn epoch(&self) -> Epoch {
match self {
Self::Notarize(v) => v.epoch(),
Self::Notarization(v) => v.epoch(),
Self::Certification(r, _) => r.epoch(),
Self::Nullify(v) => v.epoch(),
Self::Nullification(v) => v.epoch(),
Self::Finalize(v) => v.epoch(),
Self::Finalization(v) => v.epoch(),
}
}
}
impl<S: Scheme, D: Digest> Viewable for Artifact<S, D> {
fn view(&self) -> View {
match self {
Self::Notarize(v) => v.view(),
Self::Notarization(v) => v.view(),
Self::Certification(r, _) => r.view(),
Self::Nullify(v) => v.view(),
Self::Nullification(v) => v.view(),
Self::Finalize(v) => v.view(),
Self::Finalization(v) => v.view(),
}
}
}
impl<S: Scheme, D: Digest> From<Vote<S, D>> for Artifact<S, D> {
fn from(vote: Vote<S, D>) -> Self {
match vote {
Vote::Notarize(v) => Self::Notarize(v),
Vote::Nullify(v) => Self::Nullify(v),
Vote::Finalize(v) => Self::Finalize(v),
}
}
}
impl<S: Scheme, D: Digest> From<Certificate<S, D>> for Artifact<S, D> {
fn from(cert: Certificate<S, D>) -> Self {
match cert {
Certificate::Notarization(v) => Self::Notarization(v),
Certificate::Nullification(v) => Self::Nullification(v),
Certificate::Finalization(v) => Self::Finalization(v),
}
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Artifact<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let tag = u.int_in_range(0..=6)?;
match tag {
0 => {
let v = Notarize::arbitrary(u)?;
Ok(Self::Notarize(v))
}
1 => {
let v = Notarization::arbitrary(u)?;
Ok(Self::Notarization(v))
}
2 => {
let r = Round::arbitrary(u)?;
let b = bool::arbitrary(u)?;
Ok(Self::Certification(r, b))
}
3 => {
let v = Nullify::arbitrary(u)?;
Ok(Self::Nullify(v))
}
4 => {
let v = Nullification::arbitrary(u)?;
Ok(Self::Nullification(v))
}
5 => {
let v = Finalize::arbitrary(u)?;
Ok(Self::Finalize(v))
}
6 => {
let v = Finalization::arbitrary(u)?;
Ok(Self::Finalization(v))
}
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Proposal<D: Digest> {
pub round: Round,
pub parent: View,
pub payload: D,
}
impl<D: Digest> Proposal<D> {
pub const fn new(round: Round, parent: View, payload: D) -> Self {
Self {
round,
parent,
payload,
}
}
}
impl<D: Digest> Write for Proposal<D> {
fn write(&self, writer: &mut impl BufMut) {
self.round.write(writer);
self.parent.write(writer);
self.payload.write(writer)
}
}
impl<D: Digest> Read for Proposal<D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let round = Round::read(reader)?;
let parent = View::read(reader)?;
let payload = D::read(reader)?;
Ok(Self {
round,
parent,
payload,
})
}
}
impl<D: Digest> EncodeSize for Proposal<D> {
fn encode_size(&self) -> usize {
self.round.encode_size() + self.parent.encode_size() + self.payload.encode_size()
}
}
impl<D: Digest> Epochable for Proposal<D> {
fn epoch(&self) -> Epoch {
self.round.epoch()
}
}
impl<D: Digest> Viewable for Proposal<D> {
fn view(&self) -> View {
self.round.view()
}
}
#[cfg(feature = "arbitrary")]
impl<D: Digest> arbitrary::Arbitrary<'_> for Proposal<D>
where
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let round = Round::arbitrary(u)?;
let parent = View::arbitrary(u)?;
let payload = D::arbitrary(u)?;
Ok(Self {
round,
parent,
payload,
})
}
}
#[derive(Clone, Debug)]
pub struct Notarize<S: Scheme, D: Digest> {
pub proposal: Proposal<D>,
pub attestation: Attestation<S>,
}
impl<S: Scheme, D: Digest> Notarize<S, D> {
pub fn sign(scheme: &S, proposal: Proposal<D>) -> Option<Self>
where
S: scheme::Scheme<D>,
{
let attestation = scheme.sign::<D>(Subject::Notarize {
proposal: &proposal,
})?;
Some(Self {
proposal,
attestation,
})
}
pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
{
scheme.verify_attestation::<_, D>(
rng,
Subject::Notarize {
proposal: &self.proposal,
},
&self.attestation,
strategy,
)
}
pub const fn round(&self) -> Round {
self.proposal.round
}
}
impl<S: Scheme, D: Digest> PartialEq for Notarize<S, D> {
fn eq(&self, other: &Self) -> bool {
self.proposal == other.proposal && self.attestation == other.attestation
}
}
impl<S: Scheme, D: Digest> Eq for Notarize<S, D> {}
impl<S: Scheme, D: Digest> Hash for Notarize<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.proposal.hash(state);
self.attestation.hash(state);
}
}
impl<S: Scheme, D: Digest> Write for Notarize<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.proposal.write(writer);
self.attestation.write(writer);
}
}
impl<S: Scheme, D: Digest> EncodeSize for Notarize<S, D> {
fn encode_size(&self) -> usize {
self.proposal.encode_size() + self.attestation.encode_size()
}
}
impl<S: Scheme, D: Digest> Read for Notarize<S, D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let proposal = Proposal::read(reader)?;
let attestation = Attestation::read(reader)?;
Ok(Self {
proposal,
attestation,
})
}
}
impl<S: Scheme, D: Digest> Attributable for Notarize<S, D> {
fn signer(&self) -> Participant {
self.attestation.signer
}
}
impl<S: Scheme, D: Digest> Epochable for Notarize<S, D> {
fn epoch(&self) -> Epoch {
self.proposal.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for Notarize<S, D> {
fn view(&self) -> View {
self.proposal.view()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Notarize<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let proposal = Proposal::arbitrary(u)?;
let attestation = Attestation::arbitrary(u)?;
Ok(Self {
proposal,
attestation,
})
}
}
pub fn verify_certificates<'a, R, S, D>(
rng: &mut R,
scheme: &S,
certificates: &[(Subject<'a, D>, &'a S::Certificate)],
strategy: &impl Strategy,
) -> Vec<bool>
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
D: Digest,
{
scheme.verify_certificates_bisect::<_, D, N3f1>(rng, certificates, strategy)
}
#[derive(Clone, Debug)]
pub struct Notarization<S: Scheme, D: Digest> {
pub proposal: Proposal<D>,
pub certificate: S::Certificate,
}
impl<S: Scheme, D: Digest> Notarization<S, D> {
pub fn from_notarizes<'a, I>(scheme: &S, notarizes: I, strategy: &impl Strategy) -> Option<Self>
where
I: IntoIterator<Item = &'a Notarize<S, D>>,
I::IntoIter: Send,
{
let mut iter = notarizes.into_iter().peekable();
let proposal = iter.peek()?.proposal.clone();
let certificate =
scheme.assemble::<_, N3f1>(iter.map(|n| n.attestation.clone()), strategy)?;
Some(Self {
proposal,
certificate,
})
}
pub fn verify<R: CryptoRngCore>(
&self,
rng: &mut R,
scheme: &S,
strategy: &impl Strategy,
) -> bool
where
S: scheme::Scheme<D>,
{
scheme.verify_certificate::<_, D, N3f1>(
rng,
Subject::Notarize {
proposal: &self.proposal,
},
&self.certificate,
strategy,
)
}
pub const fn round(&self) -> Round {
self.proposal.round
}
}
impl<S: Scheme, D: Digest> PartialEq for Notarization<S, D> {
fn eq(&self, other: &Self) -> bool {
self.proposal == other.proposal && self.certificate == other.certificate
}
}
impl<S: Scheme, D: Digest> Eq for Notarization<S, D> {}
impl<S: Scheme, D: Digest> Hash for Notarization<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.proposal.hash(state);
self.certificate.hash(state);
}
}
impl<S: Scheme, D: Digest> Write for Notarization<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.proposal.write(writer);
self.certificate.write(writer);
}
}
impl<S: Scheme, D: Digest> EncodeSize for Notarization<S, D> {
fn encode_size(&self) -> usize {
self.proposal.encode_size() + self.certificate.encode_size()
}
}
impl<S: Scheme, D: Digest> Read for Notarization<S, D> {
type Cfg = <S::Certificate as Read>::Cfg;
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let proposal = Proposal::read(reader)?;
let certificate = S::Certificate::read_cfg(reader, cfg)?;
Ok(Self {
proposal,
certificate,
})
}
}
impl<S: Scheme, D: Digest> Epochable for Notarization<S, D> {
fn epoch(&self) -> Epoch {
self.proposal.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for Notarization<S, D> {
fn view(&self) -> View {
self.proposal.view()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Notarization<S, D>
where
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let proposal = Proposal::arbitrary(u)?;
let certificate = S::Certificate::arbitrary(u)?;
Ok(Self {
proposal,
certificate,
})
}
}
#[derive(Clone, Debug)]
pub struct Nullify<S: Scheme> {
pub round: Round,
pub attestation: Attestation<S>,
}
impl<S: Scheme> PartialEq for Nullify<S> {
fn eq(&self, other: &Self) -> bool {
self.round == other.round && self.attestation == other.attestation
}
}
impl<S: Scheme> Eq for Nullify<S> {}
impl<S: Scheme> Hash for Nullify<S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.round.hash(state);
self.attestation.hash(state);
}
}
impl<S: Scheme> Nullify<S> {
pub fn sign<D: Digest>(scheme: &S, round: Round) -> Option<Self>
where
S: scheme::Scheme<D>,
{
let attestation = scheme.sign::<D>(Subject::Nullify { round })?;
Some(Self { round, attestation })
}
pub fn verify<R, D: Digest>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
{
scheme.verify_attestation::<_, D>(
rng,
Subject::Nullify { round: self.round },
&self.attestation,
strategy,
)
}
pub const fn round(&self) -> Round {
self.round
}
}
impl<S: Scheme> Write for Nullify<S> {
fn write(&self, writer: &mut impl BufMut) {
self.round.write(writer);
self.attestation.write(writer);
}
}
impl<S: Scheme> EncodeSize for Nullify<S> {
fn encode_size(&self) -> usize {
self.round.encode_size() + self.attestation.encode_size()
}
}
impl<S: Scheme> Read for Nullify<S> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let round = Round::read(reader)?;
let attestation = Attestation::read(reader)?;
Ok(Self { round, attestation })
}
}
impl<S: Scheme> Attributable for Nullify<S> {
fn signer(&self) -> Participant {
self.attestation.signer
}
}
impl<S: Scheme> Epochable for Nullify<S> {
fn epoch(&self) -> Epoch {
self.round.epoch()
}
}
impl<S: Scheme> Viewable for Nullify<S> {
fn view(&self) -> View {
self.round.view()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme> arbitrary::Arbitrary<'_> for Nullify<S>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let round = Round::arbitrary(u)?;
let attestation = Attestation::arbitrary(u)?;
Ok(Self { round, attestation })
}
}
#[derive(Clone, Debug)]
pub struct Nullification<S: Scheme> {
pub round: Round,
pub certificate: S::Certificate,
}
impl<S: Scheme> Nullification<S> {
pub fn from_nullifies<'a, I>(scheme: &S, nullifies: I, strategy: &impl Strategy) -> Option<Self>
where
I: IntoIterator<Item = &'a Nullify<S>>,
I::IntoIter: Send,
{
let mut iter = nullifies.into_iter().peekable();
let round = iter.peek()?.round;
let certificate =
scheme.assemble::<_, N3f1>(iter.map(|n| n.attestation.clone()), strategy)?;
Some(Self { round, certificate })
}
pub fn verify<R: CryptoRngCore, D: Digest>(
&self,
rng: &mut R,
scheme: &S,
strategy: &impl Strategy,
) -> bool
where
S: scheme::Scheme<D>,
{
scheme.verify_certificate::<_, D, N3f1>(
rng,
Subject::Nullify { round: self.round },
&self.certificate,
strategy,
)
}
pub const fn round(&self) -> Round {
self.round
}
}
impl<S: Scheme> PartialEq for Nullification<S> {
fn eq(&self, other: &Self) -> bool {
self.round == other.round && self.certificate == other.certificate
}
}
impl<S: Scheme> Eq for Nullification<S> {}
impl<S: Scheme> Hash for Nullification<S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.round.hash(state);
self.certificate.hash(state);
}
}
impl<S: Scheme> Write for Nullification<S> {
fn write(&self, writer: &mut impl BufMut) {
self.round.write(writer);
self.certificate.write(writer);
}
}
impl<S: Scheme> EncodeSize for Nullification<S> {
fn encode_size(&self) -> usize {
self.round.encode_size() + self.certificate.encode_size()
}
}
impl<S: Scheme> Read for Nullification<S> {
type Cfg = <S::Certificate as Read>::Cfg;
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let round = Round::read(reader)?;
let certificate = S::Certificate::read_cfg(reader, cfg)?;
Ok(Self { round, certificate })
}
}
impl<S: Scheme> Epochable for Nullification<S> {
fn epoch(&self) -> Epoch {
self.round.epoch()
}
}
impl<S: Scheme> Viewable for Nullification<S> {
fn view(&self) -> View {
self.round.view()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme> arbitrary::Arbitrary<'_> for Nullification<S>
where
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let round = Round::arbitrary(u)?;
let certificate = S::Certificate::arbitrary(u)?;
Ok(Self { round, certificate })
}
}
#[derive(Clone, Debug)]
pub struct Finalize<S: Scheme, D: Digest> {
pub proposal: Proposal<D>,
pub attestation: Attestation<S>,
}
impl<S: Scheme, D: Digest> Finalize<S, D> {
pub fn sign(scheme: &S, proposal: Proposal<D>) -> Option<Self>
where
S: scheme::Scheme<D>,
{
let attestation = scheme.sign::<D>(Subject::Finalize {
proposal: &proposal,
})?;
Some(Self {
proposal,
attestation,
})
}
pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
{
scheme.verify_attestation::<_, D>(
rng,
Subject::Finalize {
proposal: &self.proposal,
},
&self.attestation,
strategy,
)
}
pub const fn round(&self) -> Round {
self.proposal.round
}
}
impl<S: Scheme, D: Digest> PartialEq for Finalize<S, D> {
fn eq(&self, other: &Self) -> bool {
self.proposal == other.proposal && self.attestation == other.attestation
}
}
impl<S: Scheme, D: Digest> Eq for Finalize<S, D> {}
impl<S: Scheme, D: Digest> Hash for Finalize<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.proposal.hash(state);
self.attestation.hash(state);
}
}
impl<S: Scheme, D: Digest> Write for Finalize<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.proposal.write(writer);
self.attestation.write(writer);
}
}
impl<S: Scheme, D: Digest> EncodeSize for Finalize<S, D> {
fn encode_size(&self) -> usize {
self.proposal.encode_size() + self.attestation.encode_size()
}
}
impl<S: Scheme, D: Digest> Read for Finalize<S, D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let proposal = Proposal::read(reader)?;
let attestation = Attestation::read(reader)?;
Ok(Self {
proposal,
attestation,
})
}
}
impl<S: Scheme, D: Digest> Attributable for Finalize<S, D> {
fn signer(&self) -> Participant {
self.attestation.signer
}
}
impl<S: Scheme, D: Digest> Epochable for Finalize<S, D> {
fn epoch(&self) -> Epoch {
self.proposal.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for Finalize<S, D> {
fn view(&self) -> View {
self.proposal.view()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Finalize<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let proposal = Proposal::arbitrary(u)?;
let attestation = Attestation::arbitrary(u)?;
Ok(Self {
proposal,
attestation,
})
}
}
#[derive(Clone, Debug)]
pub struct Finalization<S: Scheme, D: Digest> {
pub proposal: Proposal<D>,
pub certificate: S::Certificate,
}
impl<S: Scheme, D: Digest> Finalization<S, D> {
pub fn from_finalizes<'a, I>(scheme: &S, finalizes: I, strategy: &impl Strategy) -> Option<Self>
where
I: IntoIterator<Item = &'a Finalize<S, D>>,
I::IntoIter: Send,
{
let mut iter = finalizes.into_iter().peekable();
let proposal = iter.peek()?.proposal.clone();
let certificate =
scheme.assemble::<_, N3f1>(iter.map(|f| f.attestation.clone()), strategy)?;
Some(Self {
proposal,
certificate,
})
}
pub fn verify<R: CryptoRngCore>(
&self,
rng: &mut R,
scheme: &S,
strategy: &impl Strategy,
) -> bool
where
S: scheme::Scheme<D>,
{
scheme.verify_certificate::<_, D, N3f1>(
rng,
Subject::Finalize {
proposal: &self.proposal,
},
&self.certificate,
strategy,
)
}
pub const fn round(&self) -> Round {
self.proposal.round
}
}
impl<S: Scheme, D: Digest> PartialEq for Finalization<S, D> {
fn eq(&self, other: &Self) -> bool {
self.proposal == other.proposal && self.certificate == other.certificate
}
}
impl<S: Scheme, D: Digest> Eq for Finalization<S, D> {}
impl<S: Scheme, D: Digest> Hash for Finalization<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.proposal.hash(state);
self.certificate.hash(state);
}
}
impl<S: Scheme, D: Digest> Write for Finalization<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.proposal.write(writer);
self.certificate.write(writer);
}
}
impl<S: Scheme, D: Digest> EncodeSize for Finalization<S, D> {
fn encode_size(&self) -> usize {
self.proposal.encode_size() + self.certificate.encode_size()
}
}
impl<S: Scheme, D: Digest> Read for Finalization<S, D> {
type Cfg = <S::Certificate as Read>::Cfg;
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let proposal = Proposal::read(reader)?;
let certificate = S::Certificate::read_cfg(reader, cfg)?;
Ok(Self {
proposal,
certificate,
})
}
}
impl<S: Scheme, D: Digest> Epochable for Finalization<S, D> {
fn epoch(&self) -> Epoch {
self.proposal.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for Finalization<S, D> {
fn view(&self) -> View {
self.proposal.view()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Finalization<S, D>
where
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let proposal = Proposal::arbitrary(u)?;
let certificate = S::Certificate::arbitrary(u)?;
Ok(Self {
proposal,
certificate,
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Backfiller<S: Scheme, D: Digest> {
Request(Request),
Response(Response<S, D>),
}
impl<S: Scheme, D: Digest> Write for Backfiller<S, D> {
fn write(&self, writer: &mut impl BufMut) {
match self {
Self::Request(request) => {
0u8.write(writer);
request.write(writer);
}
Self::Response(response) => {
1u8.write(writer);
response.write(writer);
}
}
}
}
impl<S: Scheme, D: Digest> EncodeSize for Backfiller<S, D> {
fn encode_size(&self) -> usize {
1 + match self {
Self::Request(v) => v.encode_size(),
Self::Response(v) => v.encode_size(),
}
}
}
impl<S: Scheme, D: Digest> Read for Backfiller<S, D> {
type Cfg = (usize, <S::Certificate as Read>::Cfg);
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let tag = <u8>::read(reader)?;
match tag {
0 => {
let (max_len, _) = cfg;
let v = Request::read_cfg(reader, max_len)?;
Ok(Self::Request(v))
}
1 => {
let v = Response::<S, D>::read_cfg(reader, cfg)?;
Ok(Self::Response(v))
}
_ => Err(Error::Invalid(
"consensus::simplex::Backfiller",
"Invalid type",
)),
}
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Backfiller<S, D>
where
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let tag = u.int_in_range(0..=1)?;
match tag {
0 => {
let v = Request::arbitrary(u)?;
Ok(Self::Request(v))
}
1 => {
let v = Response::<S, D>::arbitrary(u)?;
Ok(Self::Response(v))
}
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Request {
pub id: u64,
pub notarizations: Vec<View>,
pub nullifications: Vec<View>,
}
impl Request {
pub const fn new(id: u64, notarizations: Vec<View>, nullifications: Vec<View>) -> Self {
Self {
id,
notarizations,
nullifications,
}
}
}
impl Write for Request {
fn write(&self, writer: &mut impl BufMut) {
UInt(self.id).write(writer);
self.notarizations.write(writer);
self.nullifications.write(writer);
}
}
impl EncodeSize for Request {
fn encode_size(&self) -> usize {
UInt(self.id).encode_size()
+ self.notarizations.encode_size()
+ self.nullifications.encode_size()
}
}
impl Read for Request {
type Cfg = usize;
fn read_cfg(reader: &mut impl Buf, max_len: &usize) -> Result<Self, Error> {
let id = UInt::read(reader)?.into();
let mut views = HashSet::new();
let notarizations = Vec::<View>::read_range(reader, ..=*max_len)?;
for view in notarizations.iter() {
if !views.insert(view) {
return Err(Error::Invalid(
"consensus::simplex::Request",
"Duplicate notarization",
));
}
}
let remaining = max_len - notarizations.len();
views.clear();
let nullifications = Vec::<View>::read_range(reader, ..=remaining)?;
for view in nullifications.iter() {
if !views.insert(view) {
return Err(Error::Invalid(
"consensus::simplex::Request",
"Duplicate nullification",
));
}
}
Ok(Self {
id,
notarizations,
nullifications,
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Response<S: Scheme, D: Digest> {
pub id: u64,
pub notarizations: Vec<Notarization<S, D>>,
pub nullifications: Vec<Nullification<S>>,
}
impl<S: Scheme, D: Digest> Response<S, D> {
pub const fn new(
id: u64,
notarizations: Vec<Notarization<S, D>>,
nullifications: Vec<Nullification<S>>,
) -> Self {
Self {
id,
notarizations,
nullifications,
}
}
pub fn verify<R: CryptoRngCore>(
&self,
rng: &mut R,
scheme: &S,
strategy: &impl Strategy,
) -> bool
where
S: scheme::Scheme<D>,
{
if self.notarizations.is_empty() && self.nullifications.is_empty() {
return true;
}
let notarizations = self.notarizations.iter().map(|notarization| {
let context = Subject::Notarize {
proposal: ¬arization.proposal,
};
(context, ¬arization.certificate)
});
let nullifications = self.nullifications.iter().map(|nullification| {
let context = Subject::Nullify {
round: nullification.round,
};
(context, &nullification.certificate)
});
scheme.verify_certificates::<_, D, _, N3f1>(
rng,
notarizations.chain(nullifications),
strategy,
)
}
}
impl<S: Scheme, D: Digest> Write for Response<S, D> {
fn write(&self, writer: &mut impl BufMut) {
UInt(self.id).write(writer);
self.notarizations.write(writer);
self.nullifications.write(writer);
}
}
impl<S: Scheme, D: Digest> EncodeSize for Response<S, D> {
fn encode_size(&self) -> usize {
UInt(self.id).encode_size()
+ self.notarizations.encode_size()
+ self.nullifications.encode_size()
}
}
impl<S: Scheme, D: Digest> Read for Response<S, D> {
type Cfg = (usize, <S::Certificate as Read>::Cfg);
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let (max_len, certificate_cfg) = cfg;
let id = UInt::read(reader)?.into();
let mut views = HashSet::new();
let notarizations = Vec::<Notarization<S, D>>::read_cfg(
reader,
&((..=*max_len).into(), certificate_cfg.clone()),
)?;
for notarization in notarizations.iter() {
if !views.insert(notarization.view()) {
return Err(Error::Invalid(
"consensus::simplex::Response",
"Duplicate notarization",
));
}
}
let remaining = max_len - notarizations.len();
views.clear();
let nullifications = Vec::<Nullification<S>>::read_cfg(
reader,
&((..=remaining).into(), certificate_cfg.clone()),
)?;
for nullification in nullifications.iter() {
if !views.insert(nullification.view()) {
return Err(Error::Invalid(
"consensus::simplex::Response",
"Duplicate nullification",
));
}
}
Ok(Self {
id,
notarizations,
nullifications,
})
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Response<S, D>
where
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let id = u.arbitrary()?;
let notarizations = u.arbitrary()?;
let nullifications = u.arbitrary()?;
Ok(Self {
id,
notarizations,
nullifications,
})
}
}
#[derive(Clone, Debug)]
pub enum Activity<S: Scheme, D: Digest> {
Notarize(Notarize<S, D>),
Notarization(Notarization<S, D>),
Certification(Notarization<S, D>),
Nullify(Nullify<S>),
Nullification(Nullification<S>),
Finalize(Finalize<S, D>),
Finalization(Finalization<S, D>),
ConflictingNotarize(ConflictingNotarize<S, D>),
ConflictingFinalize(ConflictingFinalize<S, D>),
NullifyFinalize(NullifyFinalize<S, D>),
}
impl<S: Scheme, D: Digest> PartialEq for Activity<S, D> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Notarize(a), Self::Notarize(b)) => a == b,
(Self::Notarization(a), Self::Notarization(b)) => a == b,
(Self::Certification(a), Self::Certification(b)) => a == b,
(Self::Nullify(a), Self::Nullify(b)) => a == b,
(Self::Nullification(a), Self::Nullification(b)) => a == b,
(Self::Finalize(a), Self::Finalize(b)) => a == b,
(Self::Finalization(a), Self::Finalization(b)) => a == b,
(Self::ConflictingNotarize(a), Self::ConflictingNotarize(b)) => a == b,
(Self::ConflictingFinalize(a), Self::ConflictingFinalize(b)) => a == b,
(Self::NullifyFinalize(a), Self::NullifyFinalize(b)) => a == b,
_ => false,
}
}
}
impl<S: Scheme, D: Digest> Eq for Activity<S, D> {}
impl<S: Scheme, D: Digest> Hash for Activity<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Notarize(v) => {
0u8.hash(state);
v.hash(state);
}
Self::Notarization(v) => {
1u8.hash(state);
v.hash(state);
}
Self::Certification(v) => {
2u8.hash(state);
v.hash(state);
}
Self::Nullify(v) => {
3u8.hash(state);
v.hash(state);
}
Self::Nullification(v) => {
4u8.hash(state);
v.hash(state);
}
Self::Finalize(v) => {
5u8.hash(state);
v.hash(state);
}
Self::Finalization(v) => {
6u8.hash(state);
v.hash(state);
}
Self::ConflictingNotarize(v) => {
7u8.hash(state);
v.hash(state);
}
Self::ConflictingFinalize(v) => {
8u8.hash(state);
v.hash(state);
}
Self::NullifyFinalize(v) => {
9u8.hash(state);
v.hash(state);
}
}
}
}
impl<S: Scheme, D: Digest> Activity<S, D> {
pub const fn verified(&self) -> bool {
match self {
Self::Notarize(_) => false,
Self::Notarization(_) => true,
Self::Certification(_) => false,
Self::Nullify(_) => false,
Self::Nullification(_) => true,
Self::Finalize(_) => false,
Self::Finalization(_) => true,
Self::ConflictingNotarize(_) => false,
Self::ConflictingFinalize(_) => false,
Self::NullifyFinalize(_) => false,
}
}
pub fn verify<R: CryptoRngCore>(
&self,
rng: &mut R,
scheme: &S,
strategy: &impl Strategy,
) -> bool
where
S: scheme::Scheme<D>,
{
match self {
Self::Notarize(n) => n.verify(rng, scheme, strategy),
Self::Notarization(n) => n.verify(rng, scheme, strategy),
Self::Certification(n) => n.verify(rng, scheme, strategy),
Self::Nullify(n) => n.verify(rng, scheme, strategy),
Self::Nullification(n) => n.verify(rng, scheme, strategy),
Self::Finalize(f) => f.verify(rng, scheme, strategy),
Self::Finalization(f) => f.verify(rng, scheme, strategy),
Self::ConflictingNotarize(c) => c.verify(rng, scheme, strategy),
Self::ConflictingFinalize(c) => c.verify(rng, scheme, strategy),
Self::NullifyFinalize(c) => c.verify(rng, scheme, strategy),
}
}
}
impl<S: Scheme, D: Digest> Write for Activity<S, D> {
fn write(&self, writer: &mut impl BufMut) {
match self {
Self::Notarize(v) => {
0u8.write(writer);
v.write(writer);
}
Self::Notarization(v) => {
1u8.write(writer);
v.write(writer);
}
Self::Certification(v) => {
2u8.write(writer);
v.write(writer);
}
Self::Nullify(v) => {
3u8.write(writer);
v.write(writer);
}
Self::Nullification(v) => {
4u8.write(writer);
v.write(writer);
}
Self::Finalize(v) => {
5u8.write(writer);
v.write(writer);
}
Self::Finalization(v) => {
6u8.write(writer);
v.write(writer);
}
Self::ConflictingNotarize(v) => {
7u8.write(writer);
v.write(writer);
}
Self::ConflictingFinalize(v) => {
8u8.write(writer);
v.write(writer);
}
Self::NullifyFinalize(v) => {
9u8.write(writer);
v.write(writer);
}
}
}
}
impl<S: Scheme, D: Digest> EncodeSize for Activity<S, D> {
fn encode_size(&self) -> usize {
1 + match self {
Self::Notarize(v) => v.encode_size(),
Self::Notarization(v) => v.encode_size(),
Self::Certification(v) => v.encode_size(),
Self::Nullify(v) => v.encode_size(),
Self::Nullification(v) => v.encode_size(),
Self::Finalize(v) => v.encode_size(),
Self::Finalization(v) => v.encode_size(),
Self::ConflictingNotarize(v) => v.encode_size(),
Self::ConflictingFinalize(v) => v.encode_size(),
Self::NullifyFinalize(v) => v.encode_size(),
}
}
}
impl<S: Scheme, D: Digest> Read for Activity<S, D> {
type Cfg = <S::Certificate as Read>::Cfg;
fn read_cfg(reader: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let tag = <u8>::read(reader)?;
match tag {
0 => {
let v = Notarize::<S, D>::read(reader)?;
Ok(Self::Notarize(v))
}
1 => {
let v = Notarization::<S, D>::read_cfg(reader, cfg)?;
Ok(Self::Notarization(v))
}
2 => {
let v = Notarization::<S, D>::read_cfg(reader, cfg)?;
Ok(Self::Certification(v))
}
3 => {
let v = Nullify::<S>::read(reader)?;
Ok(Self::Nullify(v))
}
4 => {
let v = Nullification::<S>::read_cfg(reader, cfg)?;
Ok(Self::Nullification(v))
}
5 => {
let v = Finalize::<S, D>::read(reader)?;
Ok(Self::Finalize(v))
}
6 => {
let v = Finalization::<S, D>::read_cfg(reader, cfg)?;
Ok(Self::Finalization(v))
}
7 => {
let v = ConflictingNotarize::<S, D>::read(reader)?;
Ok(Self::ConflictingNotarize(v))
}
8 => {
let v = ConflictingFinalize::<S, D>::read(reader)?;
Ok(Self::ConflictingFinalize(v))
}
9 => {
let v = NullifyFinalize::<S, D>::read(reader)?;
Ok(Self::NullifyFinalize(v))
}
_ => Err(Error::Invalid(
"consensus::simplex::Activity",
"Invalid type",
)),
}
}
}
impl<S: Scheme, D: Digest> Epochable for Activity<S, D> {
fn epoch(&self) -> Epoch {
match self {
Self::Notarize(v) => v.epoch(),
Self::Notarization(v) => v.epoch(),
Self::Certification(v) => v.epoch(),
Self::Nullify(v) => v.epoch(),
Self::Nullification(v) => v.epoch(),
Self::Finalize(v) => v.epoch(),
Self::Finalization(v) => v.epoch(),
Self::ConflictingNotarize(v) => v.epoch(),
Self::ConflictingFinalize(v) => v.epoch(),
Self::NullifyFinalize(v) => v.epoch(),
}
}
}
impl<S: Scheme, D: Digest> Viewable for Activity<S, D> {
fn view(&self) -> View {
match self {
Self::Notarize(v) => v.view(),
Self::Notarization(v) => v.view(),
Self::Certification(v) => v.view(),
Self::Nullify(v) => v.view(),
Self::Nullification(v) => v.view(),
Self::Finalize(v) => v.view(),
Self::Finalization(v) => v.view(),
Self::ConflictingNotarize(v) => v.view(),
Self::ConflictingFinalize(v) => v.view(),
Self::NullifyFinalize(v) => v.view(),
}
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for Activity<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
S::Certificate: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let tag = u.int_in_range(0..=9)?;
match tag {
0 => {
let v = Notarize::<S, D>::arbitrary(u)?;
Ok(Self::Notarize(v))
}
1 => {
let v = Notarization::<S, D>::arbitrary(u)?;
Ok(Self::Notarization(v))
}
2 => {
let v = Notarization::<S, D>::arbitrary(u)?;
Ok(Self::Certification(v))
}
3 => {
let v = Nullify::<S>::arbitrary(u)?;
Ok(Self::Nullify(v))
}
4 => {
let v = Nullification::<S>::arbitrary(u)?;
Ok(Self::Nullification(v))
}
5 => {
let v = Finalize::<S, D>::arbitrary(u)?;
Ok(Self::Finalize(v))
}
6 => {
let v = Finalization::<S, D>::arbitrary(u)?;
Ok(Self::Finalization(v))
}
7 => {
let v = ConflictingNotarize::<S, D>::arbitrary(u)?;
Ok(Self::ConflictingNotarize(v))
}
8 => {
let v = ConflictingFinalize::<S, D>::arbitrary(u)?;
Ok(Self::ConflictingFinalize(v))
}
9 => {
let v = NullifyFinalize::<S, D>::arbitrary(u)?;
Ok(Self::NullifyFinalize(v))
}
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug)]
pub struct ConflictingNotarize<S: Scheme, D: Digest> {
notarize_1: Notarize<S, D>,
notarize_2: Notarize<S, D>,
}
impl<S: Scheme, D: Digest> PartialEq for ConflictingNotarize<S, D> {
fn eq(&self, other: &Self) -> bool {
self.notarize_1 == other.notarize_1 && self.notarize_2 == other.notarize_2
}
}
impl<S: Scheme, D: Digest> Eq for ConflictingNotarize<S, D> {}
impl<S: Scheme, D: Digest> Hash for ConflictingNotarize<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.notarize_1.hash(state);
self.notarize_2.hash(state);
}
}
impl<S: Scheme, D: Digest> ConflictingNotarize<S, D> {
pub fn new(notarize_1: Notarize<S, D>, notarize_2: Notarize<S, D>) -> Self {
assert_eq!(notarize_1.round(), notarize_2.round());
assert_eq!(notarize_1.signer(), notarize_2.signer());
assert_ne!(
notarize_1.proposal, notarize_2.proposal,
"proposals must differ to constitute conflicting evidence"
);
Self {
notarize_1,
notarize_2,
}
}
pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
{
self.notarize_1.verify(rng, scheme, strategy)
&& self.notarize_2.verify(rng, scheme, strategy)
}
}
impl<S: Scheme, D: Digest> Attributable for ConflictingNotarize<S, D> {
fn signer(&self) -> Participant {
self.notarize_1.signer()
}
}
impl<S: Scheme, D: Digest> Epochable for ConflictingNotarize<S, D> {
fn epoch(&self) -> Epoch {
self.notarize_1.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for ConflictingNotarize<S, D> {
fn view(&self) -> View {
self.notarize_1.view()
}
}
impl<S: Scheme, D: Digest> Write for ConflictingNotarize<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.notarize_1.write(writer);
self.notarize_2.write(writer);
}
}
impl<S: Scheme, D: Digest> Read for ConflictingNotarize<S, D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let notarize_1 = Notarize::read(reader)?;
let notarize_2 = Notarize::read(reader)?;
if notarize_1.signer() != notarize_2.signer()
|| notarize_1.round() != notarize_2.round()
|| notarize_1.proposal == notarize_2.proposal
{
return Err(Error::Invalid(
"consensus::simplex::ConflictingNotarize",
"invalid conflicting notarize",
));
}
Ok(Self {
notarize_1,
notarize_2,
})
}
}
impl<S: Scheme, D: Digest> EncodeSize for ConflictingNotarize<S, D> {
fn encode_size(&self) -> usize {
self.notarize_1.encode_size() + self.notarize_2.encode_size()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for ConflictingNotarize<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let notarize_1 = Notarize::arbitrary(u)?;
let notarize_2 = Notarize::arbitrary(u)?;
Ok(Self {
notarize_1,
notarize_2,
})
}
}
#[derive(Clone, Debug)]
pub struct ConflictingFinalize<S: Scheme, D: Digest> {
finalize_1: Finalize<S, D>,
finalize_2: Finalize<S, D>,
}
impl<S: Scheme, D: Digest> PartialEq for ConflictingFinalize<S, D> {
fn eq(&self, other: &Self) -> bool {
self.finalize_1 == other.finalize_1 && self.finalize_2 == other.finalize_2
}
}
impl<S: Scheme, D: Digest> Eq for ConflictingFinalize<S, D> {}
impl<S: Scheme, D: Digest> Hash for ConflictingFinalize<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.finalize_1.hash(state);
self.finalize_2.hash(state);
}
}
impl<S: Scheme, D: Digest> ConflictingFinalize<S, D> {
pub fn new(finalize_1: Finalize<S, D>, finalize_2: Finalize<S, D>) -> Self {
assert_eq!(finalize_1.round(), finalize_2.round());
assert_eq!(finalize_1.signer(), finalize_2.signer());
assert_ne!(
finalize_1.proposal, finalize_2.proposal,
"proposals must differ to constitute conflicting evidence"
);
Self {
finalize_1,
finalize_2,
}
}
pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
{
self.finalize_1.verify(rng, scheme, strategy)
&& self.finalize_2.verify(rng, scheme, strategy)
}
}
impl<S: Scheme, D: Digest> Attributable for ConflictingFinalize<S, D> {
fn signer(&self) -> Participant {
self.finalize_1.signer()
}
}
impl<S: Scheme, D: Digest> Epochable for ConflictingFinalize<S, D> {
fn epoch(&self) -> Epoch {
self.finalize_1.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for ConflictingFinalize<S, D> {
fn view(&self) -> View {
self.finalize_1.view()
}
}
impl<S: Scheme, D: Digest> Write for ConflictingFinalize<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.finalize_1.write(writer);
self.finalize_2.write(writer);
}
}
impl<S: Scheme, D: Digest> Read for ConflictingFinalize<S, D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let finalize_1 = Finalize::read(reader)?;
let finalize_2 = Finalize::read(reader)?;
if finalize_1.signer() != finalize_2.signer()
|| finalize_1.round() != finalize_2.round()
|| finalize_1.proposal == finalize_2.proposal
{
return Err(Error::Invalid(
"consensus::simplex::ConflictingFinalize",
"invalid conflicting finalize",
));
}
Ok(Self {
finalize_1,
finalize_2,
})
}
}
impl<S: Scheme, D: Digest> EncodeSize for ConflictingFinalize<S, D> {
fn encode_size(&self) -> usize {
self.finalize_1.encode_size() + self.finalize_2.encode_size()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for ConflictingFinalize<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let finalize_1 = Finalize::arbitrary(u)?;
let finalize_2 = Finalize::arbitrary(u)?;
Ok(Self {
finalize_1,
finalize_2,
})
}
}
#[derive(Clone, Debug)]
pub struct NullifyFinalize<S: Scheme, D: Digest> {
nullify: Nullify<S>,
finalize: Finalize<S, D>,
}
impl<S: Scheme, D: Digest> PartialEq for NullifyFinalize<S, D> {
fn eq(&self, other: &Self) -> bool {
self.nullify == other.nullify && self.finalize == other.finalize
}
}
impl<S: Scheme, D: Digest> Eq for NullifyFinalize<S, D> {}
impl<S: Scheme, D: Digest> Hash for NullifyFinalize<S, D> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.nullify.hash(state);
self.finalize.hash(state);
}
}
impl<S: Scheme, D: Digest> NullifyFinalize<S, D> {
pub fn new(nullify: Nullify<S>, finalize: Finalize<S, D>) -> Self {
assert_eq!(nullify.round, finalize.round());
assert_eq!(nullify.signer(), finalize.signer());
Self { nullify, finalize }
}
pub fn verify<R>(&self, rng: &mut R, scheme: &S, strategy: &impl Strategy) -> bool
where
R: CryptoRngCore,
S: scheme::Scheme<D>,
{
self.nullify.verify(rng, scheme, strategy) && self.finalize.verify(rng, scheme, strategy)
}
}
impl<S: Scheme, D: Digest> Attributable for NullifyFinalize<S, D> {
fn signer(&self) -> Participant {
self.nullify.signer()
}
}
impl<S: Scheme, D: Digest> Epochable for NullifyFinalize<S, D> {
fn epoch(&self) -> Epoch {
self.nullify.epoch()
}
}
impl<S: Scheme, D: Digest> Viewable for NullifyFinalize<S, D> {
fn view(&self) -> View {
self.nullify.view()
}
}
impl<S: Scheme, D: Digest> Write for NullifyFinalize<S, D> {
fn write(&self, writer: &mut impl BufMut) {
self.nullify.write(writer);
self.finalize.write(writer);
}
}
impl<S: Scheme, D: Digest> Read for NullifyFinalize<S, D> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let nullify = Nullify::read(reader)?;
let finalize = Finalize::read(reader)?;
if nullify.signer() != finalize.signer() || nullify.round != finalize.round() {
return Err(Error::Invalid(
"consensus::simplex::NullifyFinalize",
"mismatched signatures",
));
}
Ok(Self { nullify, finalize })
}
}
impl<S: Scheme, D: Digest> EncodeSize for NullifyFinalize<S, D> {
fn encode_size(&self) -> usize {
self.nullify.encode_size() + self.finalize.encode_size()
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme, D: Digest> arbitrary::Arbitrary<'_> for NullifyFinalize<S, D>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
D: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let nullify = Nullify::arbitrary(u)?;
let finalize = Finalize::arbitrary(u)?;
Ok(Self { nullify, finalize })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::simplex::{
quorum,
scheme::{
bls12381_multisig,
bls12381_threshold::{
standard as bls12381_threshold_std, vrf as bls12381_threshold_vrf,
},
ed25519, secp256r1, Scheme,
},
};
use bytes::Bytes;
use commonware_codec::{Decode, DecodeExt, Encode};
use commonware_cryptography::{
bls12381::primitives::variant::{MinPk, MinSig},
certificate::mocks::Fixture,
sha256::Digest as Sha256,
};
use commonware_parallel::Sequential;
use commonware_utils::{test_rng, Faults, N3f1};
use rand::{rngs::StdRng, SeedableRng};
const NAMESPACE: &[u8] = b"test";
fn sample_digest(v: u8) -> Sha256 {
Sha256::from([v; 32]) }
fn setup_seeded<S, F>(n: u32, seed: u64, fixture: F) -> Fixture<S>
where
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
setup_seeded_ns(n, seed, NAMESPACE, fixture)
}
fn setup_seeded_ns<S, F>(n: u32, seed: u64, namespace: &[u8], fixture: F) -> Fixture<S>
where
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = StdRng::seed_from_u64(seed);
fixture(&mut rng, namespace, n)
}
#[test]
fn test_proposal_encode_decode() {
let proposal = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let encoded = proposal.encode();
let decoded = Proposal::<Sha256>::decode(encoded).unwrap();
assert_eq!(proposal, decoded);
}
fn notarize_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
let encoded = notarize.encode();
let decoded = Notarize::decode(encoded).unwrap();
assert_eq!(notarize, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_notarize_encode_decode() {
notarize_encode_decode(ed25519::fixture);
notarize_encode_decode(secp256r1::fixture);
notarize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
notarize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
notarize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn notarization_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let proposal = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let notarizes: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
.collect();
let notarization =
Notarization::from_notarizes(&fixture.schemes[0], ¬arizes, &Sequential).unwrap();
let encoded = notarization.encode();
let cfg = fixture.schemes[0].certificate_codec_config();
let decoded = Notarization::decode_cfg(encoded, &cfg).unwrap();
assert_eq!(notarization, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_notarization_encode_decode() {
notarization_encode_decode(ed25519::fixture);
notarization_encode_decode(secp256r1::fixture);
notarization_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
notarization_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
notarization_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarization_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarization_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
notarization_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn nullify_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(0), View::new(10));
let nullify = Nullify::sign::<Sha256>(&fixture.schemes[0], round).unwrap();
let encoded = nullify.encode();
let decoded = Nullify::decode(encoded).unwrap();
assert_eq!(nullify, decoded);
assert!(decoded.verify::<_, Sha256>(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_nullify_encode_decode() {
nullify_encode_decode(ed25519::fixture);
nullify_encode_decode(secp256r1::fixture);
nullify_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
nullify_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
nullify_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
nullify_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
nullify_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
nullify_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn nullification_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(333), View::new(10));
let nullifies: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Nullify::sign::<Sha256>(scheme, round).unwrap())
.collect();
let nullification =
Nullification::from_nullifies(&fixture.schemes[0], &nullifies, &Sequential).unwrap();
let encoded = nullification.encode();
let cfg = fixture.schemes[0].certificate_codec_config();
let decoded = Nullification::decode_cfg(encoded, &cfg).unwrap();
assert_eq!(nullification, decoded);
assert!(decoded.verify::<_, Sha256>(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_nullification_encode_decode() {
nullification_encode_decode(ed25519::fixture);
nullification_encode_decode(secp256r1::fixture);
nullification_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
nullification_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
nullification_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
nullification_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
nullification_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
nullification_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn finalize_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
let encoded = finalize.encode();
let decoded = Finalize::decode(encoded).unwrap();
assert_eq!(finalize, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_finalize_encode_decode() {
finalize_encode_decode(ed25519::fixture);
finalize_encode_decode(secp256r1::fixture);
finalize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
finalize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
finalize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
finalize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn finalization_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let finalizes: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
.collect();
let finalization =
Finalization::from_finalizes(&fixture.schemes[0], &finalizes, &Sequential).unwrap();
let encoded = finalization.encode();
let cfg = fixture.schemes[0].certificate_codec_config();
let decoded = Finalization::decode_cfg(encoded, &cfg).unwrap();
assert_eq!(finalization, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_finalization_encode_decode() {
finalization_encode_decode(ed25519::fixture);
finalization_encode_decode(secp256r1::fixture);
finalization_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
finalization_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
finalization_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
finalization_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
finalization_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
finalization_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn backfiller_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let cfg = fixture.schemes[0].certificate_codec_config();
let request = Request::new(
1,
vec![View::new(10), View::new(11)],
vec![View::new(12), View::new(13)],
);
let encoded_request = Backfiller::<S, Sha256>::Request(request.clone()).encode();
let decoded_request =
Backfiller::<S, Sha256>::decode_cfg(encoded_request, &(usize::MAX, cfg.clone()))
.unwrap();
assert!(matches!(decoded_request, Backfiller::Request(r) if r == request));
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let notarizes: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
.collect();
let notarization =
Notarization::from_notarizes(&fixture.schemes[0], ¬arizes, &Sequential).unwrap();
let nullifies: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Nullify::sign::<Sha256>(scheme, round).unwrap())
.collect();
let nullification =
Nullification::from_nullifies(&fixture.schemes[0], &nullifies, &Sequential).unwrap();
let response = Response::<S, Sha256>::new(1, vec![notarization], vec![nullification]);
let encoded_response = Backfiller::<S, Sha256>::Response(response.clone()).encode();
let decoded_response =
Backfiller::<S, Sha256>::decode_cfg(encoded_response, &(usize::MAX, cfg)).unwrap();
assert!(matches!(decoded_response, Backfiller::Response(r) if r.id == response.id));
}
#[test]
fn test_backfiller_encode_decode() {
backfiller_encode_decode(ed25519::fixture);
backfiller_encode_decode(secp256r1::fixture);
backfiller_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
backfiller_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
backfiller_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
backfiller_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
backfiller_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
backfiller_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
#[test]
fn test_request_encode_decode() {
let request = Request::new(
1,
vec![View::new(10), View::new(11)],
vec![View::new(12), View::new(13)],
);
let encoded = request.encode();
let decoded = Request::decode_cfg(encoded, &usize::MAX).unwrap();
assert_eq!(request, decoded);
}
fn response_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let notarizes: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
.collect();
let notarization =
Notarization::from_notarizes(&fixture.schemes[0], ¬arizes, &Sequential).unwrap();
let nullifies: Vec<_> = fixture
.schemes
.iter()
.map(|scheme| Nullify::sign::<Sha256>(scheme, round).unwrap())
.collect();
let nullification =
Nullification::from_nullifies(&fixture.schemes[0], &nullifies, &Sequential).unwrap();
let response = Response::<S, Sha256>::new(1, vec![notarization], vec![nullification]);
let cfg = fixture.schemes[0].certificate_codec_config();
let mut decoded =
Response::<S, Sha256>::decode_cfg(response.encode(), &(usize::MAX, cfg)).unwrap();
assert_eq!(response.id, decoded.id);
assert_eq!(response.notarizations.len(), decoded.notarizations.len());
assert_eq!(response.nullifications.len(), decoded.nullifications.len());
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
decoded.nullifications[0].round = Round::new(
decoded.nullifications[0].round.epoch(),
decoded.nullifications[0].round.view().next(),
);
assert!(!decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_response_encode_decode() {
response_encode_decode(ed25519::fixture);
response_encode_decode(secp256r1::fixture);
response_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
response_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
response_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
response_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
response_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
response_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn conflicting_notarize_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let proposal1 = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let proposal2 = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(2),
);
let notarize1 = Notarize::sign(&fixture.schemes[0], proposal1).unwrap();
let notarize2 = Notarize::sign(&fixture.schemes[0], proposal2).unwrap();
let conflicting = ConflictingNotarize::new(notarize1, notarize2);
let encoded = conflicting.encode();
let decoded = ConflictingNotarize::<S, Sha256>::decode(encoded).unwrap();
assert_eq!(conflicting, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_conflicting_notarize_encode_decode() {
conflicting_notarize_encode_decode(ed25519::fixture);
conflicting_notarize_encode_decode(secp256r1::fixture);
conflicting_notarize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
conflicting_notarize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
conflicting_notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
conflicting_notarize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
conflicting_notarize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
conflicting_notarize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn conflicting_finalize_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let proposal1 = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let proposal2 = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(2),
);
let finalize1 = Finalize::sign(&fixture.schemes[0], proposal1).unwrap();
let finalize2 = Finalize::sign(&fixture.schemes[0], proposal2).unwrap();
let conflicting = ConflictingFinalize::new(finalize1, finalize2);
let encoded = conflicting.encode();
let decoded = ConflictingFinalize::<S, Sha256>::decode(encoded).unwrap();
assert_eq!(conflicting, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_conflicting_finalize_encode_decode() {
conflicting_finalize_encode_decode(ed25519::fixture);
conflicting_finalize_encode_decode(secp256r1::fixture);
conflicting_finalize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
conflicting_finalize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
conflicting_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
conflicting_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
conflicting_finalize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
conflicting_finalize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn nullify_finalize_encode_decode<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let nullify = Nullify::sign::<Sha256>(&fixture.schemes[0], round).unwrap();
let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
let conflict = NullifyFinalize::new(nullify, finalize);
let encoded = conflict.encode();
let decoded = NullifyFinalize::<S, Sha256>::decode(encoded).unwrap();
assert_eq!(conflict, decoded);
assert!(decoded.verify(&mut rng, &fixture.schemes[0], &Sequential));
}
#[test]
fn test_nullify_finalize_encode_decode() {
nullify_finalize_encode_decode(ed25519::fixture);
nullify_finalize_encode_decode(secp256r1::fixture);
nullify_finalize_encode_decode(bls12381_multisig::fixture::<MinPk, _>);
nullify_finalize_encode_decode(bls12381_multisig::fixture::<MinSig, _>);
nullify_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinPk, _>);
nullify_finalize_encode_decode(bls12381_threshold_vrf::fixture::<MinSig, _>);
nullify_finalize_encode_decode(bls12381_threshold_std::fixture::<MinPk, _>);
nullify_finalize_encode_decode(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn notarize_verify_wrong_namespace<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = setup_seeded_ns(5, 0, NAMESPACE, &f);
let wrong_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(1));
let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
assert!(notarize.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!notarize.verify(&mut rng, &wrong_fixture.schemes[0], &Sequential));
}
#[test]
fn test_notarize_verify_wrong_namespace() {
notarize_verify_wrong_namespace(ed25519::fixture);
notarize_verify_wrong_namespace(secp256r1::fixture);
notarize_verify_wrong_namespace(bls12381_multisig::fixture::<MinPk, _>);
notarize_verify_wrong_namespace(bls12381_multisig::fixture::<MinSig, _>);
notarize_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarize_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarize_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinPk, _>);
notarize_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn notarize_verify_wrong_scheme<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = setup_seeded(5, 0, &f);
let wrong_fixture = setup_seeded(5, 1, &f);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(2));
let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
assert!(notarize.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!notarize.verify(&mut rng, &wrong_fixture.verifier, &Sequential));
}
#[test]
fn test_notarize_verify_wrong_scheme() {
notarize_verify_wrong_scheme(ed25519::fixture);
notarize_verify_wrong_scheme(secp256r1::fixture);
notarize_verify_wrong_scheme(bls12381_multisig::fixture::<MinPk, _>);
notarize_verify_wrong_scheme(bls12381_multisig::fixture::<MinSig, _>);
notarize_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarize_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarize_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinPk, _>);
notarize_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn notarization_verify_wrong_scheme<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = setup_seeded(5, 0, &f);
let wrong_fixture = setup_seeded(5, 1, &f);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(3));
let quorum = N3f1::quorum(fixture.schemes.len()) as usize;
let notarizes: Vec<_> = fixture
.schemes
.iter()
.take(quorum)
.map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
.collect();
let notarization =
Notarization::from_notarizes(&fixture.schemes[0], ¬arizes, &Sequential)
.expect("quorum notarization");
assert!(notarization.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!notarization.verify(&mut rng, &wrong_fixture.verifier, &Sequential));
}
#[test]
fn test_notarization_verify_wrong_scheme() {
notarization_verify_wrong_scheme(ed25519::fixture);
notarization_verify_wrong_scheme(secp256r1::fixture);
notarization_verify_wrong_scheme(bls12381_multisig::fixture::<MinPk, _>);
notarization_verify_wrong_scheme(bls12381_multisig::fixture::<MinSig, _>);
notarization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinPk, _>);
notarization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn notarization_verify_wrong_namespace<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let fixture = setup_seeded_ns(5, 0, NAMESPACE, &f);
let wrong_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
let mut rng = test_rng();
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(4));
let quorum = N3f1::quorum(fixture.schemes.len()) as usize;
let notarizes: Vec<_> = fixture
.schemes
.iter()
.take(quorum)
.map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
.collect();
let notarization =
Notarization::from_notarizes(&fixture.schemes[0], ¬arizes, &Sequential)
.expect("quorum notarization");
assert!(notarization.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!notarization.verify(&mut rng, &wrong_fixture.schemes[0], &Sequential));
}
#[test]
fn test_notarization_verify_wrong_namespace() {
notarization_verify_wrong_namespace(ed25519::fixture);
notarization_verify_wrong_namespace(secp256r1::fixture);
notarization_verify_wrong_namespace(bls12381_multisig::fixture::<MinPk, _>);
notarization_verify_wrong_namespace(bls12381_multisig::fixture::<MinSig, _>);
notarization_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarization_verify_wrong_namespace(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarization_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinPk, _>);
notarization_verify_wrong_namespace(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn notarization_recover_insufficient_signatures<S, F>(fixture: F)
where
S: Scheme<Sha256>,
F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = fixture(&mut rng, NAMESPACE, 5);
let quorum_size = quorum(fixture.schemes.len() as u32) as usize;
assert!(quorum_size > 1, "test requires quorum larger than one");
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(5));
let notarizes: Vec<_> = fixture
.schemes
.iter()
.take(quorum_size - 1)
.map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
.collect();
assert!(
Notarization::from_notarizes(&fixture.schemes[0], ¬arizes, &Sequential).is_none(),
"insufficient votes should not form a notarization"
);
}
#[test]
fn test_notarization_recover_insufficient_signatures() {
notarization_recover_insufficient_signatures(ed25519::fixture);
notarization_recover_insufficient_signatures(secp256r1::fixture);
notarization_recover_insufficient_signatures(bls12381_multisig::fixture::<MinPk, _>);
notarization_recover_insufficient_signatures(bls12381_multisig::fixture::<MinSig, _>);
notarization_recover_insufficient_signatures(bls12381_threshold_vrf::fixture::<MinPk, _>);
notarization_recover_insufficient_signatures(bls12381_threshold_vrf::fixture::<MinSig, _>);
notarization_recover_insufficient_signatures(bls12381_threshold_std::fixture::<MinPk, _>);
notarization_recover_insufficient_signatures(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn conflicting_notarize_detection<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = setup_seeded(5, 0, &f);
let wrong_ns_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
let wrong_scheme_fixture = setup_seeded(5, 1, &f);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal1 = Proposal::new(round, View::new(5), sample_digest(6));
let proposal2 = Proposal::new(round, View::new(5), sample_digest(7));
let notarize1 = Notarize::sign(&fixture.schemes[0], proposal1).unwrap();
let notarize2 = Notarize::sign(&fixture.schemes[0], proposal2).unwrap();
let conflict = ConflictingNotarize::new(notarize1, notarize2);
assert!(conflict.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!conflict.verify(&mut rng, &wrong_ns_fixture.schemes[0], &Sequential));
assert!(!conflict.verify(&mut rng, &wrong_scheme_fixture.verifier, &Sequential));
}
#[test]
fn test_conflicting_notarize_detection() {
conflicting_notarize_detection(ed25519::fixture);
conflicting_notarize_detection(secp256r1::fixture);
conflicting_notarize_detection(bls12381_multisig::fixture::<MinPk, _>);
conflicting_notarize_detection(bls12381_multisig::fixture::<MinSig, _>);
conflicting_notarize_detection(bls12381_threshold_vrf::fixture::<MinPk, _>);
conflicting_notarize_detection(bls12381_threshold_vrf::fixture::<MinSig, _>);
conflicting_notarize_detection(bls12381_threshold_std::fixture::<MinPk, _>);
conflicting_notarize_detection(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn nullify_finalize_detection<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = setup_seeded(5, 0, &f);
let wrong_ns_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f);
let wrong_scheme_fixture = setup_seeded(5, 1, &f);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(8));
let nullify = Nullify::sign::<Sha256>(&fixture.schemes[0], round).unwrap();
let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
let conflict = NullifyFinalize::new(nullify, finalize);
assert!(conflict.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!conflict.verify(&mut rng, &wrong_ns_fixture.schemes[0], &Sequential));
assert!(!conflict.verify(&mut rng, &wrong_scheme_fixture.verifier, &Sequential));
}
#[test]
fn test_nullify_finalize_detection() {
nullify_finalize_detection(ed25519::fixture);
nullify_finalize_detection(secp256r1::fixture);
nullify_finalize_detection(bls12381_multisig::fixture::<MinPk, _>);
nullify_finalize_detection(bls12381_multisig::fixture::<MinSig, _>);
nullify_finalize_detection(bls12381_threshold_vrf::fixture::<MinPk, _>);
nullify_finalize_detection(bls12381_threshold_vrf::fixture::<MinSig, _>);
nullify_finalize_detection(bls12381_threshold_std::fixture::<MinPk, _>);
nullify_finalize_detection(bls12381_threshold_std::fixture::<MinSig, _>);
}
fn finalization_verify_wrong_scheme<S, F>(f: F)
where
S: Scheme<Sha256>,
F: Fn(&mut StdRng, &[u8], u32) -> Fixture<S>,
{
let mut rng = test_rng();
let fixture = setup_seeded(5, 0, &f);
let wrong_fixture = setup_seeded(5, 1, &f);
let round = Round::new(Epoch::new(0), View::new(10));
let proposal = Proposal::new(round, View::new(5), sample_digest(9));
let quorum = N3f1::quorum(fixture.schemes.len()) as usize;
let finalizes: Vec<_> = fixture
.schemes
.iter()
.take(quorum)
.map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
.collect();
let finalization =
Finalization::from_finalizes(&fixture.schemes[0], &finalizes, &Sequential)
.expect("quorum finalization");
assert!(finalization.verify(&mut rng, &fixture.schemes[0], &Sequential));
assert!(!finalization.verify(&mut rng, &wrong_fixture.verifier, &Sequential));
}
#[test]
fn test_finalization_wrong_scheme() {
finalization_verify_wrong_scheme(ed25519::fixture);
finalization_verify_wrong_scheme(secp256r1::fixture);
finalization_verify_wrong_scheme(bls12381_multisig::fixture::<MinPk, _>);
finalization_verify_wrong_scheme(bls12381_multisig::fixture::<MinSig, _>);
finalization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinPk, _>);
finalization_verify_wrong_scheme(bls12381_threshold_vrf::fixture::<MinSig, _>);
finalization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinPk, _>);
finalization_verify_wrong_scheme(bls12381_threshold_std::fixture::<MinSig, _>);
}
struct MockAttributable(Participant);
impl Attributable for MockAttributable {
fn signer(&self) -> Participant {
self.0
}
}
#[test]
fn test_attributable_map() {
let mut map = AttributableMap::new(5);
assert_eq!(map.len(), 0);
assert!(map.is_empty());
for i in 0..5 {
assert!(map.get(Participant::new(i)).is_none());
}
assert!(map.insert(MockAttributable(Participant::new(3))));
assert_eq!(map.len(), 1);
assert!(!map.is_empty());
let mut iter = map.iter();
assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(3)));
assert!(iter.next().is_none());
drop(iter);
assert!(
matches!(map.get(Participant::new(3)), Some(a) if a.signer() == Participant::new(3))
);
assert!(map.insert(MockAttributable(Participant::new(1))));
assert_eq!(map.len(), 2);
assert!(!map.is_empty());
let mut iter = map.iter();
assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(1)));
assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(3)));
assert!(iter.next().is_none());
drop(iter);
assert!(
matches!(map.get(Participant::new(1)), Some(a) if a.signer() == Participant::new(1))
);
assert!(
matches!(map.get(Participant::new(3)), Some(a) if a.signer() == Participant::new(3))
);
assert!(map.get(Participant::new(0)).is_none());
assert!(map.get(Participant::new(2)).is_none());
assert!(map.get(Participant::new(4)).is_none());
assert!(!map.insert(MockAttributable(Participant::new(3))));
assert_eq!(map.len(), 2);
assert!(!map.is_empty());
let mut iter = map.iter();
assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(1)));
assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(3)));
assert!(iter.next().is_none());
drop(iter);
assert!(!map.insert(MockAttributable(Participant::new(5))));
assert!(!map.insert(MockAttributable(Participant::new(100))));
assert_eq!(map.len(), 2);
map.clear();
assert_eq!(map.len(), 0);
assert!(map.is_empty());
assert!(map.iter().next().is_none());
assert!(map.insert(MockAttributable(Participant::new(2))));
assert_eq!(map.len(), 1);
let mut iter = map.iter();
assert!(matches!(iter.next(), Some(a) if a.signer() == Participant::new(2)));
assert!(iter.next().is_none());
}
#[test]
#[should_panic(expected = "proposals must differ")]
fn issue_2944_regression_conflicting_notarize_new() {
let mut rng = test_rng();
let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
let proposal = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
let _ = ConflictingNotarize::new(notarize.clone(), notarize);
}
#[test]
fn issue_2944_regression_conflicting_notarize_decode() {
let mut rng = test_rng();
let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
let proposal = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap();
let mut buf = Vec::new();
notarize.write(&mut buf);
notarize.write(&mut buf);
let result = ConflictingNotarize::<ed25519::Scheme, Sha256>::decode(Bytes::from(buf));
assert!(result.is_err());
}
#[test]
#[should_panic(expected = "proposals must differ")]
fn issue_2944_regression_conflicting_finalize_new() {
let mut rng = test_rng();
let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
let proposal = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
let _ = ConflictingFinalize::new(finalize.clone(), finalize);
}
#[test]
fn issue_2944_regression_conflicting_finalize_decode() {
let mut rng = test_rng();
let fixture = ed25519::fixture(&mut rng, NAMESPACE, 1);
let proposal = Proposal::new(
Round::new(Epoch::new(0), View::new(10)),
View::new(5),
sample_digest(1),
);
let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap();
let mut buf = Vec::new();
finalize.write(&mut buf);
finalize.write(&mut buf);
let result = ConflictingFinalize::<ed25519::Scheme, Sha256>::decode(Bytes::from(buf));
assert!(result.is_err());
}
#[cfg(feature = "arbitrary")]
mod conformance {
use super::*;
use crate::simplex::scheme::bls12381_threshold::vrf as bls12381_threshold_vrf;
use commonware_codec::conformance::CodecConformance;
use commonware_cryptography::{ed25519::PublicKey, sha256::Digest as Sha256Digest};
type Scheme = bls12381_threshold_vrf::Scheme<PublicKey, MinSig>;
commonware_conformance::conformance_tests! {
CodecConformance<Vote<Scheme, Sha256Digest>>,
CodecConformance<Certificate<Scheme, Sha256Digest>>,
CodecConformance<Artifact<Scheme, Sha256Digest>>,
CodecConformance<Proposal<Sha256Digest>>,
CodecConformance<Notarize<Scheme, Sha256Digest>>,
CodecConformance<Notarization<Scheme, Sha256Digest>>,
CodecConformance<Nullify<Scheme>>,
CodecConformance<Nullification<Scheme>>,
CodecConformance<Finalize<Scheme, Sha256Digest>>,
CodecConformance<Finalization<Scheme, Sha256Digest>>,
CodecConformance<Backfiller<Scheme, Sha256Digest>>,
CodecConformance<Request>,
CodecConformance<Response<Scheme, Sha256Digest>>,
CodecConformance<Activity<Scheme, Sha256Digest>>,
CodecConformance<ConflictingNotarize<Scheme, Sha256Digest>>,
CodecConformance<ConflictingFinalize<Scheme, Sha256Digest>>,
CodecConformance<NullifyFinalize<Scheme, Sha256Digest>>,
CodecConformance<Context<Sha256Digest, PublicKey>>
}
}
}