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}