Skip to main content

commonware_consensus/simplex/
config.rs

1use super::{
2    elector::Config as Elector,
3    types::{Activity, Context, Finalization},
4};
5use crate::{
6    types::{Epoch, ViewDelta},
7    CertifiableAutomaton, Epochable, Relay, Reporter, Viewable,
8};
9use commonware_cryptography::{certificate::Scheme, Digest};
10use commonware_p2p::Blocker;
11use commonware_parallel::Strategy;
12use commonware_runtime::buffer::paged::CacheRef;
13use rand_core::CryptoRngCore;
14use std::{num::NonZeroUsize, time::Duration};
15
16/// Controls whether and how the engine proactively forwards certified blocks
17/// when entering the next view.
18///
19/// Forwarding is a best-effort liveness aid: when enabled, the batcher
20/// broadcasts only after we locally certify a proposal and enter the next
21/// view, avoiding sends for proposals that never pass certification.
22#[derive(Debug, Clone, Copy)]
23pub enum ForwardingPolicy {
24    /// Do nothing when a certified proposal becomes eligible for forwarding.
25    Disabled,
26    /// Forward the block to all participants that did not vote for the proposal.
27    ///
28    /// To only send to the leader of the newly entered view, see [ForwardingPolicy::SilentLeader].
29    SilentVoters,
30    /// Forward the block to the leader of the newly entered view if they did not
31    /// vote for the proposal.
32    ///
33    /// To forward to all participants that did not vote for the proposal, see [ForwardingPolicy::SilentVoters].
34    SilentLeader,
35}
36
37impl ForwardingPolicy {
38    /// Returns true if the policy is enabled.
39    pub const fn is_enabled(&self) -> bool {
40        !matches!(self, Self::Disabled)
41    }
42}
43
44/// The certified root from which a Simplex instance starts.
45#[derive(Clone, Debug)]
46pub enum Floor<S: Scheme, D: Digest> {
47    /// Start from the epoch genesis payload at view 0.
48    Genesis(D),
49    /// Start from an already-finalized proposal.
50    Finalized(Finalization<S, D>),
51}
52
53impl<S: Scheme, D: Digest> Floor<S, D> {
54    fn assert<Rng>(&self, epoch: Epoch, rng: &mut Rng, scheme: &S, strategy: &impl Strategy)
55    where
56        Rng: CryptoRngCore,
57        S: super::scheme::Scheme<D>,
58    {
59        if let Self::Finalized(finalization) = self {
60            assert_eq!(
61                finalization.epoch(),
62                epoch,
63                "floor finalization must be in the configured epoch"
64            );
65            assert!(
66                !finalization.view().is_zero(),
67                "use Floor::Genesis for the genesis view"
68            );
69            assert!(
70                finalization.verify(rng, scheme, strategy),
71                "floor finalization must verify"
72            );
73        }
74    }
75}
76
77/// Configuration for the consensus engine.
78pub struct Config<S, L, B, D, A, R, F, T>
79where
80    S: Scheme,
81    L: Elector<S>,
82    B: Blocker<PublicKey = S::PublicKey>,
83    D: Digest,
84    A: CertifiableAutomaton<Context = Context<D, S::PublicKey>>,
85    R: Relay,
86    F: Reporter<Activity = Activity<S, D>>,
87    T: Strategy,
88{
89    /// Signing scheme for the consensus engine.
90    ///
91    /// Consensus messages can be signed with a cryptosystem that differs from the static
92    /// participant identity keys exposed in `participants`. For example, we can authenticate peers
93    /// on the network with [commonware_cryptography::ed25519] keys while signing votes with shares distributed
94    /// via [commonware_cryptography::bls12381::dkg] (which change each epoch). The scheme implementation is
95    /// responsible for reusing the exact participant ordering carried by `participants` so that signer indices
96    /// remain stable across both key spaces; if the order diverges, validators will reject votes as coming from
97    /// the wrong validator.
98    pub scheme: S,
99
100    /// Leader election configuration.
101    ///
102    /// Determines how leaders are selected for each view. Built-in options include
103    /// [`RoundRobin`](super::elector::RoundRobin) for deterministic rotation and
104    /// [`Random`](super::elector::Random) for unpredictable selection using BLS
105    /// threshold signatures.
106    pub elector: L,
107
108    /// Blocker for the network.
109    ///
110    /// Blocking is handled by [commonware_p2p].
111    pub blocker: B,
112
113    /// Automaton for the consensus engine.
114    pub automaton: A,
115
116    /// Relay for the consensus engine.
117    pub relay: R,
118
119    /// Reporter for the consensus engine.
120    ///
121    /// All activity is exported for downstream applications that benefit from total observability,
122    /// consider wrapping with [`crate::simplex::scheme::reporter::AttributableReporter`] to
123    /// automatically filter and verify activities based on scheme attributability.
124    pub reporter: F,
125
126    /// Strategy for parallel operations.
127    pub strategy: T,
128
129    /// Partition for the consensus engine.
130    pub partition: String,
131
132    /// Maximum number of messages to buffer on channels inside the consensus
133    /// engine before blocking.
134    pub mailbox_size: NonZeroUsize,
135
136    /// Epoch for the consensus engine. Each running engine should have a unique epoch.
137    pub epoch: Epoch,
138
139    /// Certified root for the consensus engine.
140    pub floor: Floor<S, D>,
141
142    /// Number of bytes to buffer when replaying during startup.
143    pub replay_buffer: NonZeroUsize,
144
145    /// The size of the write buffer to use for each blob in the journal.
146    pub write_buffer: NonZeroUsize,
147
148    /// Page cache for the journal.
149    pub page_cache: CacheRef,
150
151    /// Amount of time to wait for a leader to propose a payload
152    /// in a view.
153    pub leader_timeout: Duration,
154
155    /// Amount of time to wait for certification progress in a view
156    /// before attempting to skip the view.
157    pub certification_timeout: Duration,
158
159    /// Amount of time to wait before retrying a nullify broadcast if
160    /// stuck in a view.
161    pub timeout_retry: Duration,
162
163    /// Number of views behind finalized tip to track
164    /// and persist activity derived from validator messages.
165    pub activity_timeout: ViewDelta,
166
167    /// Move to nullify immediately if the selected leader has been inactive
168    /// for this many recent known views (we ignore views we don't have data for).
169    ///
170    /// This number should be less than or equal to `activity_timeout` (how
171    /// many views we are tracking below the finalized tip).
172    pub skip_timeout: ViewDelta,
173
174    /// Timeout to wait for a peer to respond to a request.
175    pub fetch_timeout: Duration,
176
177    /// Number of concurrent requests to make at once.
178    pub fetch_concurrent: NonZeroUsize,
179
180    /// Policy for proactively forwarding certified blocks when entering the
181    /// next view.
182    pub forwarding: ForwardingPolicy,
183}
184
185impl<
186        S: Scheme,
187        L: Elector<S>,
188        B: Blocker<PublicKey = S::PublicKey>,
189        D: Digest,
190        A: CertifiableAutomaton<Context = Context<D, S::PublicKey>>,
191        R: Relay,
192        F: Reporter<Activity = Activity<S, D>>,
193        T: Strategy,
194    > Config<S, L, B, D, A, R, F, T>
195{
196    /// Assert enforces that all configuration values are valid.
197    ///
198    /// The RNG is used to verify finalized floor certificates.
199    pub fn assert<Rng>(&self, rng: &mut Rng)
200    where
201        Rng: CryptoRngCore,
202        S: super::scheme::Scheme<D>,
203    {
204        assert!(
205            !self.scheme.participants().is_empty(),
206            "there must be at least one participant"
207        );
208        assert!(
209            self.leader_timeout > Duration::default(),
210            "leader timeout must be greater than zero"
211        );
212        assert!(
213            self.certification_timeout > Duration::default(),
214            "certification timeout must be greater than zero"
215        );
216        assert!(
217            self.leader_timeout <= self.certification_timeout,
218            "leader timeout must be less than or equal to certification timeout"
219        );
220        assert!(
221            self.timeout_retry > Duration::default(),
222            "timeout retry broadcast must be greater than zero"
223        );
224        assert!(
225            !self.activity_timeout.is_zero(),
226            "activity timeout must be greater than zero"
227        );
228        assert!(
229            !self.skip_timeout.is_zero(),
230            "skip timeout must be greater than zero"
231        );
232        assert!(
233            self.skip_timeout <= self.activity_timeout,
234            "skip timeout must be less than or equal to activity timeout"
235        );
236        assert!(
237            self.fetch_timeout > Duration::default(),
238            "fetch timeout must be greater than zero"
239        );
240        self.floor
241            .assert(self.epoch, rng, &self.scheme, &self.strategy);
242    }
243}