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 [`certificate::Scheme::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
24use crate::simplex::types::Subject;
25use bytes::Bytes;
26use commonware_codec::Encode;
27use commonware_cryptography::{certificate, Digest};
28use commonware_utils::union;
29
30pub mod bls12381_multisig;
31pub mod bls12381_threshold;
32pub mod ed25519;
33pub mod secp256r1;
34
35#[cfg(not(target_arch = "wasm32"))]
36pub mod reporter;
37
38/// Pre-computed namespaces for simplex voting subjects.
39///
40/// This struct holds the pre-computed namespace bytes for each vote type.
41#[derive(Clone, Debug)]
42pub struct Namespace {
43    /// Namespace for notarize votes/certificates.
44    pub notarize: Vec<u8>,
45    /// Namespace for nullify votes/certificates.
46    pub nullify: Vec<u8>,
47    /// Namespace for finalize votes/certificates.
48    pub finalize: Vec<u8>,
49    /// Namespace for seed signatures (used by threshold schemes).
50    pub seed: Vec<u8>,
51}
52
53impl Namespace {
54    /// Creates a new SimplexNamespace from a base namespace.
55    pub fn new(namespace: &[u8]) -> Self {
56        Self {
57            notarize: notarize_namespace(namespace),
58            nullify: nullify_namespace(namespace),
59            finalize: finalize_namespace(namespace),
60            seed: seed_namespace(namespace),
61        }
62    }
63}
64
65impl certificate::Namespace for Namespace {
66    fn derive(namespace: &[u8]) -> Self {
67        Self::new(namespace)
68    }
69}
70
71impl<'a, D: Digest> certificate::Subject for Subject<'a, D> {
72    type Namespace = Namespace;
73
74    fn namespace<'b>(&self, derived: &'b Self::Namespace) -> &'b [u8] {
75        match self {
76            Self::Notarize { .. } => &derived.notarize,
77            Self::Nullify { .. } => &derived.nullify,
78            Self::Finalize { .. } => &derived.finalize,
79        }
80    }
81
82    fn message(&self) -> Bytes {
83        match self {
84            Self::Notarize { proposal } => proposal.encode(),
85            Self::Nullify { round } => round.encode(),
86            Self::Finalize { proposal } => proposal.encode(),
87        }
88    }
89}
90
91/// Marker trait for signing schemes compatible with `simplex`.
92///
93/// This trait binds a [`certificate::Scheme`] to the [`Subject`] subject type
94/// used by the simplex protocol. It is automatically implemented for any scheme
95/// whose subject type matches `Subject<'a, D>`.
96pub trait Scheme<D: Digest>: for<'a> certificate::Scheme<Subject<'a, D> = Subject<'a, D>> {}
97
98impl<D: Digest, S> Scheme<D> for S where
99    S: for<'a> certificate::Scheme<Subject<'a, D> = Subject<'a, D>>
100{
101}
102
103// Constants for domain separation in signature verification
104// These are used to prevent cross-protocol attacks and message-type confusion
105const SEED_SUFFIX: &[u8] = b"_SEED";
106const NOTARIZE_SUFFIX: &[u8] = b"_NOTARIZE";
107const NULLIFY_SUFFIX: &[u8] = b"_NULLIFY";
108const FINALIZE_SUFFIX: &[u8] = b"_FINALIZE";
109
110/// Creates a namespace for seed messages by appending the SEED_SUFFIX
111/// The seed is used for leader election and randomness generation
112#[inline]
113pub(crate) fn seed_namespace(namespace: &[u8]) -> Vec<u8> {
114    union(namespace, SEED_SUFFIX)
115}
116
117/// Creates a namespace for notarize messages by appending the NOTARIZE_SUFFIX
118/// Domain separation prevents cross-protocol attacks
119#[inline]
120pub(crate) fn notarize_namespace(namespace: &[u8]) -> Vec<u8> {
121    union(namespace, NOTARIZE_SUFFIX)
122}
123
124/// Creates a namespace for nullify messages by appending the NULLIFY_SUFFIX
125/// Domain separation prevents cross-protocol attacks
126#[inline]
127pub(crate) fn nullify_namespace(namespace: &[u8]) -> Vec<u8> {
128    union(namespace, NULLIFY_SUFFIX)
129}
130
131/// Creates a namespace for finalize messages by appending the FINALIZE_SUFFIX
132/// Domain separation prevents cross-protocol attacks
133#[inline]
134pub(crate) fn finalize_namespace(namespace: &[u8]) -> Vec<u8> {
135    union(namespace, FINALIZE_SUFFIX)
136}