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