Skip to main content

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 are used to track the progress of the consensus engine.
59    pub trait Block: Heightable + Codec + Digestible + Send + Sync + 'static {
60        /// Get the parent block's digest.
61        fn parent(&self) -> Self::Digest;
62    }
63
64    /// CertifiableBlock extends [Block] with consensus context information.
65    ///
66    /// This trait is required for blocks used with deferred verification in [CertifiableAutomaton].
67    /// It allows the verification context to be derived directly from the block when a validator
68    /// needs to participate in certification but never verified the block locally (necessary for liveness).
69    ///
70    /// The [`Digestible`] implementation for a [`CertifiableBlock`] must commit to the block's
71    /// embedded consensus context. In other words, changing [`CertifiableBlock::context`] for a
72    /// block must also change [`Digestible::digest`].
73    pub trait CertifiableBlock: Block {
74        /// The consensus context type stored in this block.
75        type Context: Clone + Encode;
76
77        /// Get the consensus context that was used when this block was proposed.
78        fn context(&self) -> Self::Context;
79    }
80});
81stability_scope!(BETA, cfg(not(target_arch = "wasm32")) {
82    use commonware_actor::Feedback;
83    use commonware_cryptography::{Digest, PublicKey};
84    use commonware_utils::channel::{fallible::OneshotExt, mpsc, oneshot};
85    use std::future::Future;
86
87    pub mod marshal;
88
89    mod reporter;
90    pub use reporter::*;
91
92    /// Histogram buckets for measuring consensus latency.
93    const LATENCY: [f64; 36] = [
94        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,
95        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,
96        0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
97    ];
98
99    /// Automaton is the interface responsible for driving the consensus forward by proposing new payloads
100    /// and verifying payloads proposed by other participants.
101    pub trait Automaton: Clone + Send + 'static {
102        /// Context is metadata provided by the consensus engine associated with a given payload.
103        ///
104        /// This often includes things like the proposer, view number, the height, or the epoch.
105        type Context;
106
107        /// Hash of an arbitrary payload.
108        type Digest: Digest;
109
110        /// Generate a new payload for the given context.
111        ///
112        /// If it is possible to generate a payload, the Digest should be returned over the provided
113        /// channel. If it is not possible to generate a payload, the channel can be dropped. If construction
114        /// takes too long, the consensus engine may drop the provided proposal.
115        ///
116        /// Returning a payload from `propose` commits the local proposer to verifying
117        /// the same `(context, payload)`.
118        ///
119        /// For [`CertifiableAutomaton`] implementations, returning a payload from
120        /// `propose` also commits the local proposer to certifying that same
121        /// `(round, payload)` if it later becomes notarized.
122        fn propose(
123            &mut self,
124            context: Self::Context,
125        ) -> impl Future<Output = oneshot::Receiver<Self::Digest>> + Send;
126
127        /// Verify the payload is valid.
128        ///
129        /// This request is single-shot for the given `(context, payload)`. Once the returned
130        /// channel resolves or closes, consensus treats verification as concluded and will not
131        /// retry the same request. After a restart, however, consensus may request verification
132        /// for the same `(context, payload)` again if the result was not durably recorded before
133        /// shutdown.
134        ///
135        /// Implementations should therefore keep the request pending while the verdict may still
136        /// change. Return `false` only when the payload is permanently invalid for this context.
137        /// For example, temporary conditions such as time skew, missing dependencies, or data
138        /// that may arrive later should not conclude verification with `false`.
139        ///
140        /// Closing the channel is also terminal for this request and should be reserved for cases
141        /// where verification cannot ever produce a verdict anymore (for example, shutdown), not
142        /// for temporary inability to decide.
143        fn verify(
144            &mut self,
145            context: Self::Context,
146            payload: Self::Digest,
147        ) -> impl Future<Output = oneshot::Receiver<bool>> + Send;
148    }
149
150    /// CertifiableAutomaton extends [Automaton] with the ability to certify payloads before finalization.
151    ///
152    /// This trait is required by consensus implementations (like Simplex) that support a certification
153    /// phase between notarization and finalization. Applications that do not need custom certification
154    /// logic can use the default implementation which always certifies.
155    pub trait CertifiableAutomaton: Automaton {
156        /// Determine whether a verified payload is safe to commit.
157        ///
158        /// The round parameter identifies which consensus round is being certified, allowing
159        /// applications to associate certification with the correct verification context. The
160        /// same payload may appear in multiple rounds, so implementations must key any state
161        /// on `(round, payload)` rather than `payload` alone.
162        ///
163        /// Like [`Automaton::verify`], payloads produced by [`Automaton::propose`] are certifiable-by-construction.
164        /// Also like [`Automaton::verify`], certification is single-shot for the given
165        /// `(round, payload)`. Once the returned channel resolves or closes, consensus treats
166        /// certification as concluded and will not retry the same request. After a restart,
167        /// however, consensus may request certification for the same `(round, payload)` again
168        /// if the result was not durably recorded before shutdown.
169        ///
170        /// Implementations should therefore keep the request pending while the verdict may still
171        /// change. Return `false` only when the payload is permanently uncertifiable for that
172        /// round. Temporary conditions such as waiting for more data should not conclude
173        /// certification with `false`.
174        ///
175        /// Closing the channel is also terminal for this request and should be reserved for cases
176        /// where certification can no longer produce a verdict (for example, shutdown), not for temporary
177        /// inability to decide.
178        ///
179        /// # Determinism Requirement
180        ///
181        /// The decision returned by `certify` must be deterministic and consistent across
182        /// all honest participants to ensure liveness.
183        fn certify(
184            &mut self,
185            _round: Round,
186            _payload: Self::Digest,
187        ) -> impl Future<Output = oneshot::Receiver<bool>> + Send {
188            #[allow(clippy::async_yields_async)]
189            async move {
190                let (sender, receiver) = oneshot::channel();
191                sender.send_lossy(true);
192                receiver
193            }
194        }
195    }
196
197    /// Relay is the interface responsible for broadcasting payloads to the network.
198    ///
199    /// The consensus engine is only aware of a payload's digest, not its contents. It is up
200    /// to the relay to efficiently broadcast the full payload to other participants.
201    pub trait Relay: Clone + Send + 'static {
202        /// Hash of an arbitrary payload.
203        type Digest: Digest;
204
205        /// Identity key of a network participant.
206        type PublicKey: PublicKey;
207
208        /// Directive for how a payload should be broadcast.
209        ///
210        /// Consensus mechanisms that need broadcast control (e.g. distinguishing
211        /// initial broadcast from rebroadcasts) define a custom enum here. Mechanisms that
212        /// treat every broadcast identically can set this to `()`.
213        type Plan: Send;
214
215        /// Broadcast a payload according to the given plan.
216        fn broadcast(&mut self, payload: Self::Digest, plan: Self::Plan) -> Feedback;
217    }
218
219    /// Reporter is the interface responsible for reporting activity to some external actor.
220    pub trait Reporter: Clone + Send + 'static {
221        /// Activity is specified by the underlying consensus implementation and can be interpreted if desired.
222        ///
223        /// Examples of activity would be "vote", "finalize", or "fault". Various consensus implementations may
224        /// want to reward (or penalize) participation in different ways and in different places. For example,
225        /// validators could be required to send multiple types of messages (i.e. vote and finalize) and rewarding
226        /// both equally may better align incentives with desired behavior.
227        type Activity;
228
229        /// Report some activity observed by the consensus implementation.
230        fn report(&mut self, activity: Self::Activity) -> Feedback;
231    }
232
233    /// Monitor is the interface an external actor can use to observe the progress of a consensus implementation.
234    ///
235    /// Monitor is used to implement mechanisms that share the same set of active participants as consensus and/or
236    /// perform some activity that requires some synchronization with the progress of consensus.
237    ///
238    /// Monitor can be implemented using [crate::Reporter] to avoid introducing complexity
239    /// into any particular consensus implementation.
240    pub trait Monitor: Clone + Send + 'static {
241        /// Index is the type used to indicate the in-progress consensus decision.
242        type Index;
243
244        /// Create a channel that will receive updates when the latest index (also provided) changes.
245        fn subscribe(
246            &mut self,
247        ) -> impl Future<Output = (Self::Index, mpsc::Receiver<Self::Index>)> + Send;
248    }
249});
250stability_scope!(ALPHA {
251    pub mod aggregation;
252    pub mod ordered_broadcast;
253});
254stability_scope!(ALPHA, cfg(not(target_arch = "wasm32")) {
255    use crate::marshal::ancestry::Ancestry;
256    use commonware_cryptography::certificate::Scheme;
257    use commonware_runtime::{Clock, Metrics, Spawner};
258    use rand_core::Rng;
259
260    /// Application is a minimal interface for standard implementations that operate over a stream
261    /// of epoched blocks.
262    pub trait Application<E>: Clone + Send + 'static
263    where
264        E: Rng + Spawner + Metrics + Clock,
265    {
266        /// The signing scheme used by the application.
267        type SigningScheme: Scheme;
268
269        /// Context is metadata provided by the consensus engine associated with a given payload.
270        ///
271        /// This often includes things like the proposer, view number, the height, or the epoch.
272        type Context: Epochable;
273
274        /// The block type produced by the application's builder.
275        type Block: Block;
276
277        /// Build a new block on top of the provided parent ancestry. If the build job fails,
278        /// or the proposer's slot should be skipped, the implementor should return [None].
279        ///
280        /// This future may be cancelled before it completes. Implementations must be
281        /// cancellation-safe.
282        fn propose(
283            &mut self,
284            context: (E, Self::Context),
285            ancestry: impl Ancestry<Self::Block>,
286        ) -> impl Future<Output = Option<Self::Block>> + Send;
287
288        /// Verify a block produced by the application's proposer, relative to its ancestry.
289        ///
290        /// This future should not resolve until the implementation can produce a stable verdict.
291        /// Return `false` only when the block is permanently invalid for the supplied context and
292        /// ancestry. If validity may still change as additional information becomes available,
293        /// continue waiting instead of returning `false`.
294        ///
295        /// In other words, to abstain from voting, do not resolve this future yet. Keep it
296        /// pending until the implementation can either prove the block valid, prove it invalid,
297        /// or the consensus engine cancels the request. Abstaining is not represented by a
298        /// special return value.
299        ///
300        /// This future may be cancelled before it completes. Implementations must be
301        /// cancellation-safe.
302        fn verify(
303            &mut self,
304            context: (E, Self::Context),
305            ancestry: impl Ancestry<Self::Block>,
306        ) -> impl Future<Output = bool> + Send;
307    }
308});