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