commonware_consensus/lib.rs
1//! Order opaque messages in a Byzantine environment.
2//!
3//! # Status
4//!
5//! Stability varies by primitive. See [README](https://github.com/commonwarexyz/monorepo#stability) for details.
6
7#![doc(
8 html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
9 html_favicon_url = "https://commonware.xyz/favicon.ico"
10)]
11
12use commonware_macros::stability_scope;
13
14stability_scope!(BETA {
15 use commonware_codec::{Codec, Encode};
16 use commonware_cryptography::Digestible;
17
18 pub mod simplex;
19
20 pub mod types;
21 use types::{Epoch, Height, Round, View};
22
23 /// Epochable is a trait that provides access to the epoch number.
24 /// Any consensus message or object that is associated with a specific epoch should implement this.
25 pub trait Epochable {
26 /// Returns the epoch associated with this object.
27 fn epoch(&self) -> Epoch;
28 }
29
30 /// Heightable is a trait that provides access to the height.
31 /// Any consensus message or object that is associated with a specific height should implement this.
32 pub trait Heightable {
33 /// Returns the height associated with this object.
34 fn height(&self) -> Height;
35 }
36
37 /// Viewable is a trait that provides access to the view (round) number.
38 /// Any consensus message or object that is associated with a specific view should implement this.
39 pub trait Viewable {
40 /// Returns the view associated with this object.
41 fn view(&self) -> View;
42 }
43
44 /// Roundable is a trait that provides access to the [`Round`] number.
45 /// Any consensus message or object that implements [`Epochable`] and [`Viewable`] automatically
46 /// implements this trait.
47 pub trait Roundable: Epochable + Viewable {
48 /// Returns the round associated with this object, derived from its epoch and view.
49 fn round(&self) -> Round {
50 Round::new(self.epoch(), self.view())
51 }
52 }
53
54 impl<T: Epochable + Viewable> Roundable for T {}
55
56 /// Block is the interface for a block in the blockchain.
57 ///
58 /// Blocks must use a canonical encoding: every byte sequence `bytes` accepted by the decoder
59 /// must satisfy `encode(decode(bytes)) == bytes`. Decoders must reject alternate encodings of
60 /// the same block.
61 pub trait Block: Heightable + Codec + Digestible + Send + Sync + 'static {
62 /// Get the parent block's digest.
63 fn parent(&self) -> Self::Digest;
64 }
65
66 /// CertifiableBlock extends [Block] with consensus context information.
67 ///
68 /// This trait is required for blocks used with deferred verification in [CertifiableAutomaton].
69 /// It allows the verification context to be derived directly from the block when a validator
70 /// needs to participate in certification but never verified the block locally (necessary for liveness).
71 ///
72 /// The [`Digestible`] implementation for a [`CertifiableBlock`] must commit to the block's
73 /// embedded consensus context. In other words, changing [`CertifiableBlock::context`] for a
74 /// block must also change [`Digestible::digest`].
75 pub trait CertifiableBlock: Block {
76 /// The consensus context type stored in this block.
77 type Context: Clone + Encode;
78
79 /// Get the consensus context that was used when this block was proposed.
80 fn context(&self) -> Self::Context;
81 }
82});
83stability_scope!(BETA, cfg(not(target_arch = "wasm32")) {
84 use commonware_actor::Feedback;
85 use commonware_cryptography::{Digest, PublicKey};
86 use commonware_utils::channel::{fallible::OneshotExt, mpsc, oneshot};
87 use std::future::Future;
88
89 pub mod marshal;
90
91 mod reporter;
92 pub use reporter::*;
93
94 /// Histogram buckets for measuring consensus latency.
95 const LATENCY: [f64; 36] = [
96 0.05, 0.1, 0.125, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26,
97 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.45,
98 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
99 ];
100
101 /// Automaton is the interface responsible for driving the consensus forward by proposing new payloads
102 /// and verifying payloads proposed by other participants.
103 pub trait Automaton: Clone + Send + 'static {
104 /// Context is metadata provided by the consensus engine associated with a given payload.
105 ///
106 /// This often includes things like the proposer, view number, the height, or the epoch.
107 type Context;
108
109 /// Hash of an arbitrary payload.
110 type Digest: Digest;
111
112 /// Generate a new payload for the given context.
113 ///
114 /// If it is possible to generate a payload, the Digest should be returned over the provided
115 /// channel. If it is not possible to generate a payload, the channel can be dropped. If construction
116 /// takes too long, the consensus engine may drop the provided proposal.
117 ///
118 /// Returning a payload from `propose` commits the local proposer to verifying
119 /// the same `(context, payload)`.
120 ///
121 /// For [`CertifiableAutomaton`] implementations, returning a payload from
122 /// `propose` also commits the local proposer to certifying that same
123 /// `(round, payload)` if it later becomes notarized.
124 ///
125 /// Consensus may request a payload for a future context before earlier
126 /// contexts complete. Honor any dependencies supplied in the context
127 /// rather than rebuilding them from current local state. If consensus
128 /// later abandons a dependency, it also abandons the proposal.
129 ///
130 /// Closing the response declines this request, which consensus may
131 /// treat as final for the context. Keep the response pending when
132 /// temporary unavailability should not abandon the context.
133 fn propose(
134 &mut self,
135 context: Self::Context,
136 ) -> impl Future<Output = oneshot::Receiver<Self::Digest>> + Send;
137
138 /// Verify the payload is valid.
139 ///
140 /// This request is single-shot for the given `(context, payload)`. Once the returned
141 /// channel resolves or closes, consensus treats verification as concluded and will not
142 /// retry the same request. After a restart, however, consensus may request verification
143 /// for the same `(context, payload)` again if the result was not durably recorded before
144 /// shutdown.
145 ///
146 /// Implementations should therefore keep the request pending while the verdict may still
147 /// change. Return `false` only when the payload is permanently invalid for this context.
148 /// For example, temporary conditions such as time skew, missing dependencies, or data
149 /// that may arrive later should not conclude verification with `false`.
150 ///
151 /// Closing the channel is also terminal for this request and should be reserved for cases
152 /// where verification cannot ever produce a verdict anymore (for example, shutdown), not
153 /// for temporary inability to decide.
154 ///
155 /// The future-context requirement on [`Self::propose`] applies here
156 /// too: the context's dependencies may not be resolvable locally yet.
157 fn verify(
158 &mut self,
159 context: Self::Context,
160 payload: Self::Digest,
161 ) -> impl Future<Output = oneshot::Receiver<bool>> + Send;
162 }
163
164 /// CertifiableAutomaton extends [Automaton] with the ability to certify payloads before finalization.
165 ///
166 /// This trait is required by consensus implementations (like Simplex) that support a certification
167 /// phase between notarization and finalization. Applications that do not need custom certification
168 /// logic can use the default implementation which always certifies.
169 pub trait CertifiableAutomaton: Automaton {
170 /// Determine whether a verified payload is safe to commit.
171 ///
172 /// The round parameter identifies which consensus round is being certified, allowing
173 /// applications to associate certification with the correct verification context. The
174 /// same payload may appear in multiple rounds, so implementations must key any state
175 /// on `(round, payload)` rather than `payload` alone.
176 ///
177 /// Like [`Automaton::verify`], payloads produced by [`Automaton::propose`] are certifiable-by-construction.
178 /// Also like [`Automaton::verify`], certification is single-shot for the given
179 /// `(round, payload)`. Once the returned channel resolves or closes, consensus treats
180 /// certification as concluded and will not retry the same request. After a restart,
181 /// however, consensus may request certification for the same `(round, payload)` again
182 /// if the result was not durably recorded before shutdown.
183 ///
184 /// Implementations should therefore keep the request pending while the verdict may still
185 /// change. Return `false` only when the payload is permanently uncertifiable for that
186 /// round. Temporary conditions such as waiting for more data should not conclude
187 /// certification with `false`.
188 ///
189 /// Closing the channel is also terminal for this request and should be reserved for cases
190 /// where certification can no longer produce a verdict (for example, shutdown), not for temporary
191 /// inability to decide.
192 ///
193 /// # Determinism Requirement
194 ///
195 /// The decision returned by `certify` must be deterministic and consistent across
196 /// all honest participants to ensure liveness.
197 fn certify(
198 &mut self,
199 _round: Round,
200 _payload: Self::Digest,
201 ) -> impl Future<Output = oneshot::Receiver<bool>> + Send {
202 #[allow(clippy::async_yields_async)]
203 async move {
204 let (sender, receiver) = oneshot::channel();
205 sender.send_lossy(true);
206 receiver
207 }
208 }
209 }
210
211 /// Relay is the interface responsible for broadcasting payloads to the network.
212 ///
213 /// The consensus engine is only aware of a payload's digest, not its contents. It is up
214 /// to the relay to efficiently broadcast the full payload to other participants.
215 pub trait Relay: Clone + Send + 'static {
216 /// Hash of an arbitrary payload.
217 type Digest: Digest;
218
219 /// Identity key of a network participant.
220 type PublicKey: PublicKey;
221
222 /// Directive for how a payload should be broadcast.
223 ///
224 /// Consensus mechanisms that need broadcast control (e.g. distinguishing
225 /// initial broadcast from rebroadcasts) define a custom enum here. Mechanisms that
226 /// treat every broadcast identically can set this to `()`.
227 type Plan: Send;
228
229 /// Broadcast a payload according to the given plan.
230 fn broadcast(&mut self, payload: Self::Digest, plan: Self::Plan) -> Feedback;
231 }
232
233 /// Reporter is the interface responsible for reporting activity to some external actor.
234 pub trait Reporter: Clone + Send + 'static {
235 /// Activity is specified by the underlying consensus implementation and can be interpreted if desired.
236 ///
237 /// Examples of activity would be "vote", "finalize", or "fault". Various consensus implementations may
238 /// want to reward (or penalize) participation in different ways and in different places. For example,
239 /// validators could be required to send multiple types of messages (i.e. vote and finalize) and rewarding
240 /// both equally may better align incentives with desired behavior.
241 type Activity;
242
243 /// Report some activity observed by the consensus implementation.
244 fn report(&mut self, activity: Self::Activity) -> Feedback;
245 }
246
247 /// Monitor is the interface an external actor can use to observe the progress of a consensus implementation.
248 ///
249 /// Monitor is used to implement mechanisms that share the same set of active participants as consensus and/or
250 /// perform some activity that requires some synchronization with the progress of consensus.
251 ///
252 /// Monitor can be implemented using [crate::Reporter] to avoid introducing complexity
253 /// into any particular consensus implementation.
254 pub trait Monitor: Clone + Send + 'static {
255 /// Index is the type used to indicate the in-progress consensus decision.
256 type Index;
257
258 /// Create a channel that will receive updates when the latest index (also provided) changes.
259 fn subscribe(
260 &mut self,
261 ) -> impl Future<Output = (Self::Index, mpsc::Receiver<Self::Index>)> + Send;
262 }
263});
264stability_scope!(ALPHA {
265 pub mod aggregation;
266});
267stability_scope!(ALPHA, cfg(not(target_arch = "wasm32")) {
268 use crate::marshal::ancestry::Ancestry;
269 use commonware_cryptography::certificate::Scheme;
270 use commonware_runtime::{Clock, Metrics, Spawner};
271 use rand_core::Rng;
272
273 /// Application is a minimal interface for standard implementations that operate over a stream
274 /// of epoched blocks.
275 pub trait Application<E>: Clone + Send + 'static
276 where
277 E: Rng + Spawner + Metrics + Clock,
278 {
279 /// The signing scheme used by the application.
280 type SigningScheme: Scheme;
281
282 /// Context is metadata provided by the consensus engine associated with a given payload.
283 ///
284 /// This often includes things like the proposer, view number, the height, or the epoch.
285 type Context: Epochable;
286
287 /// The block type produced by the application's builder.
288 type Block: Block;
289
290 /// Per-proposal input handed to [`propose`](Self::propose). Applications
291 /// that need no input set this to `()`.
292 type Input: Send;
293
294 /// Build a new block on top of the provided parent ancestry. If the build job fails,
295 /// or the proposer's slot should be skipped, the implementor should return [None].
296 ///
297 /// `input` is the per-proposal input for this build.
298 ///
299 /// This future may be cancelled before it completes. Implementations must be
300 /// cancellation-safe.
301 fn propose(
302 &mut self,
303 context: (E, Self::Context),
304 ancestry: impl Ancestry<Self::Block>,
305 input: Self::Input,
306 ) -> impl Future<Output = Option<Self::Block>> + Send;
307
308 /// Verify a block produced by the application's proposer, relative to its ancestry.
309 ///
310 /// This future should not resolve until the implementation can produce a stable verdict.
311 /// Return `false` only when the block is permanently invalid for the supplied context and
312 /// ancestry. If validity may still change as additional information becomes available,
313 /// continue waiting instead of returning `false`.
314 ///
315 /// In other words, to abstain from voting, do not resolve this future yet. Keep it
316 /// pending until the implementation can either prove the block valid, prove it invalid,
317 /// or the consensus engine cancels the request. Abstaining is not represented by a
318 /// special return value.
319 ///
320 /// This future may be cancelled before it completes. Implementations must be
321 /// cancellation-safe.
322 fn verify(
323 &mut self,
324 context: (E, Self::Context),
325 ancestry: impl Ancestry<Self::Block>,
326 ) -> impl Future<Output = bool> + Send;
327 }
328});