Skip to main content

commonware_consensus/simplex/scheme/
mod.rs

1//! Signing scheme implementations for `simplex`.
2//!
3//! # Attributable Schemes and Fault Evidence
4//!
5//! Signing schemes differ in whether per-validator activities can be used as evidence of either
6//! liveness or of committing a fault:
7//!
8//! - **Attributable Schemes** ([`ed25519`], [`bls12381_multisig`], [`secp256r1`]): Individual signatures can be
9//!   presented to some third party as evidence of either liveness or of committing a fault. Certificates contain signer
10//!   indices alongside individual signatures, enabling secure per-validator activity tracking and conflict detection.
11//!
12//! - **Non-Attributable schemes** ([`bls12381_threshold`]): Individual signatures cannot be presented
13//!   to some third party as evidence of either liveness or of committing a fault because they can be forged
14//!   by other players (often after some quorum of partial signatures are collected). With [`bls12381_threshold`],
15//!   possession of any `t` valid partial signatures can be used to forge a partial signature for any other player.
16//!   Because peer connections are authenticated, evidence can be used locally (as it must be sent by said participant)
17//!   but can't be used by an external observer.
18//!
19//! The [`CertificateScheme::is_attributable()`] associated function signals whether evidence can be safely
20//! exposed. For applications only interested in collecting evidence for liveness/faults, use [`reporter::AttributableReporter`]
21//! which automatically handles filtering and verification based on scheme (hiding votes/proofs that are not attributable). If
22//! full observability is desired, process all messages passed through the [`crate::Reporter`] interface.
23//!
24//! # BLS12-381 Threshold Variants
25//!
26//! The [`bls12381_threshold`] module provides two variants:
27//!
28//! - [`bls12381_threshold::standard`]: Standard threshold signatures. Certificates contain only a
29//!   vote signature recovered from partial signatures.
30//!
31//! - [`bls12381_threshold::vrf`]: Threshold VRF (Verifiable Random Function) that produces both
32//!   vote signatures and per-round seed signatures. The seed can be used for randomness (e.g.,
33//!   leader election, timelock encryption).
34//!
35//! **Security Warning for VRF Usage**: It is **not safe** to use a round's randomness to drive
36//! execution in that same round. A malicious leader can selectively distribute blocks to gain
37//! early visibility of the randomness output, then choose nullification if the outcome is
38//! unfavorable. Applications should employ a "commit-then-reveal" pattern by binding randomness
39//! requests in finalized blocks **before** the reveal occurs (e.g., `draw(view+100)`).
40
41use crate::simplex::types::Subject;
42use bytes::Bytes;
43use commonware_codec::Encode;
44use commonware_cryptography::{
45    certificate::{
46        Namespace as CertificateNamespace, Scheme as CertificateScheme,
47        Subject as CertificateSubject, Verifier,
48    },
49    Digest,
50};
51use commonware_utils::union;
52
53pub mod bls12381_multisig;
54pub mod bls12381_threshold;
55pub mod ed25519;
56commonware_macros::stability_mod!(ALPHA, pub mod secp256r1);
57
58#[cfg(not(target_arch = "wasm32"))]
59pub mod reporter;
60
61/// Pre-computed namespaces for simplex voting subjects.
62///
63/// This struct holds the pre-computed namespace bytes for each vote type.
64#[derive(Clone, Debug)]
65pub struct Namespace {
66    /// Namespace for notarize votes/certificates.
67    pub notarize: Vec<u8>,
68    /// Namespace for nullify votes/certificates.
69    pub nullify: Vec<u8>,
70    /// Namespace for finalize votes/certificates.
71    pub finalize: Vec<u8>,
72    /// Namespace for seed signatures (used by threshold schemes).
73    pub seed: Vec<u8>,
74}
75
76impl Namespace {
77    /// Creates a new SimplexNamespace from a base namespace.
78    pub fn new(namespace: &[u8]) -> Self {
79        Self {
80            notarize: notarize_namespace(namespace),
81            nullify: nullify_namespace(namespace),
82            finalize: finalize_namespace(namespace),
83            seed: seed_namespace(namespace),
84        }
85    }
86}
87
88impl CertificateNamespace for Namespace {
89    fn derive(namespace: &[u8]) -> Self {
90        Self::new(namespace)
91    }
92}
93
94impl<'a, D: Digest> CertificateSubject for Subject<'a, D> {
95    type Namespace = Namespace;
96
97    fn namespace<'b>(&self, derived: &'b Self::Namespace) -> &'b [u8] {
98        match self {
99            Self::Notarize { .. } => &derived.notarize,
100            Self::Nullify { .. } => &derived.nullify,
101            Self::Finalize { .. } => &derived.finalize,
102        }
103    }
104
105    fn message(&self) -> Bytes {
106        match self {
107            Self::Notarize { proposal } => proposal.encode(),
108            Self::Nullify { round } => round.encode(),
109            Self::Finalize { proposal } => proposal.encode(),
110        }
111    }
112}
113
114/// Marker trait for certificate verifiers compatible with `simplex`.
115///
116/// This trait binds a [`Verifier`] to the [`Subject`] subject type
117/// used by the simplex protocol. It is automatically implemented for any verifier whose subject
118/// type matches `Subject<'a, D>`.
119pub trait CertificateVerifier<D: Digest>:
120    for<'a> Verifier<Subject<'a, D> = Subject<'a, D>>
121{
122}
123
124impl<D: Digest, S> CertificateVerifier<D> for S where
125    S: for<'a> Verifier<Subject<'a, D> = Subject<'a, D>>
126{
127}
128
129/// Marker trait for signing schemes compatible with `simplex`.
130///
131/// This trait binds a [`CertificateScheme`] to the [`Subject`] subject type
132/// used by the simplex protocol. It is automatically implemented for any scheme
133/// whose subject type matches `Subject<'a, D>`.
134pub trait Scheme<D: Digest>:
135    CertificateVerifier<D> + for<'a> CertificateScheme<Subject<'a, D> = Subject<'a, D>>
136{
137}
138
139impl<D: Digest, S> Scheme<D> for S where
140    S: for<'a> CertificateScheme<Subject<'a, D> = Subject<'a, D>>
141{
142}
143
144// Constants for domain separation in signature verification
145// These are used to prevent cross-protocol attacks and message-type confusion
146const SEED_SUFFIX: &[u8] = b"_SEED";
147const NOTARIZE_SUFFIX: &[u8] = b"_NOTARIZE";
148const NULLIFY_SUFFIX: &[u8] = b"_NULLIFY";
149const FINALIZE_SUFFIX: &[u8] = b"_FINALIZE";
150
151/// Creates a namespace for seed messages by appending the SEED_SUFFIX
152/// The seed is used for leader election and randomness generation
153#[inline]
154pub(crate) fn seed_namespace(namespace: &[u8]) -> Vec<u8> {
155    union(namespace, SEED_SUFFIX)
156}
157
158/// Creates a namespace for notarize messages by appending the NOTARIZE_SUFFIX
159/// Domain separation prevents cross-protocol attacks
160#[inline]
161pub(crate) fn notarize_namespace(namespace: &[u8]) -> Vec<u8> {
162    union(namespace, NOTARIZE_SUFFIX)
163}
164
165/// Creates a namespace for nullify messages by appending the NULLIFY_SUFFIX
166/// Domain separation prevents cross-protocol attacks
167#[inline]
168pub(crate) fn nullify_namespace(namespace: &[u8]) -> Vec<u8> {
169    union(namespace, NULLIFY_SUFFIX)
170}
171
172/// Creates a namespace for finalize messages by appending the FINALIZE_SUFFIX
173/// Domain separation prevents cross-protocol attacks
174#[inline]
175pub(crate) fn finalize_namespace(namespace: &[u8]) -> Vec<u8> {
176    union(namespace, FINALIZE_SUFFIX)
177}