commonware_consensus/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
//! Order opaque messages in a Byzantine environment.
//!
//! # Status
//!
//! `commonware-consensus` is **ALPHA** software and is not yet recommended for production use. Developers should
//! expect breaking changes and occasional instability.
use bytes::Bytes;
use commonware_cryptography::{Digest, PublicKey};
use futures::channel::oneshot;
use std::future::Future;
pub mod simplex;
/// Automaton is the interface responsible for driving the consensus forward by proposing new payloads
/// and verifying payloads proposed by other participants.
pub trait Automaton: Clone + Send + 'static {
/// Context is metadata provided by the consensus engine to associated with a given payload.
///
/// This often includes things like the proposer, view number, the height, or the epoch.
type Context;
/// Payload used to initialize the consensus engine.
fn genesis(&mut self) -> impl Future<Output = Digest> + Send;
/// Generate a new payload for the given context.
///
/// If it is possible to generate a payload, the Digest should be returned over the provided
/// channel. If it is not possible to generate a payload, the channel can be dropped. If construction
/// takes too long, the consensus engine may drop the provided proposal.
fn propose(
&mut self,
context: Self::Context,
) -> impl Future<Output = oneshot::Receiver<Digest>> + Send;
/// Verify the payload is valid.
///
/// If it is possible to verify the payload, a boolean should be returned indicating whether
/// the payload is valid. If it is not possible to verify the payload, the channel can be dropped.
fn verify(
&mut self,
context: Self::Context,
payload: Digest,
) -> impl Future<Output = oneshot::Receiver<bool>> + Send;
}
/// Relay is the interface responsible for broadcasting payloads to the network.
///
/// The consensus engine is only aware of a payload's digest, not its contents. It is up
/// to the relay to efficiently broadcast the full payload to other participants.
pub trait Relay: Clone + Send + 'static {
/// Called once consensus begins working towards a proposal provided by `Automaton` (i.e.
/// it isn't dropped).
///
/// Other participants may not begin voting on a proposal until they have the full contents,
/// so timely delivery often yields better performance.
fn broadcast(&mut self, payload: Digest) -> impl Future<Output = ()> + Send;
}
/// Proof is a blob that attests to some data.
pub type Proof = Bytes;
/// Committer is the interface responsible for handling notifications of payload status.
pub trait Committer: Clone + Send + 'static {
/// Event that a payload has made some progress towards finalization but is not yet finalized.
///
/// This is often used to provide an early ("best guess") confirmation to users.
fn prepared(&mut self, proof: Proof, payload: Digest) -> impl Future<Output = ()> + Send;
/// Event indicating the container has been finalized.
fn finalized(&mut self, proof: Proof, payload: Digest) -> impl Future<Output = ()> + Send;
}
/// Activity is specified by the underlying consensus implementation and can be interpreted if desired.
///
/// Examples of activity would be "vote", "finalize", or "fault". Various consensus implementations may
/// want to reward (or penalize) participation in different ways and in different places. For example,
/// validators could be required to send multiple types of messages (i.e. vote and finalize) and rewarding
/// both equally may better align incentives with desired behavior.
pub type Activity = u8;
/// Supervisor is the interface responsible for managing which participants are active at a given time.
///
/// ## Synchronization
///
/// It is up to the user to ensure changes in this list are synchronized across nodes in the network
/// at a given `Index`. If care is not taken to do this, consensus could halt (as different participants
/// may have a different view of who is active at a given time).
///
/// The simplest way to avoid this complexity is to use a consensus implementation that reaches finalization
/// on application data before transitioning to a new `Index` (i.e. [Tendermint](https://arxiv.org/abs/1807.04938)).
///
/// Implementations that do not work this way (like `simplex`) must introduce some synchrony bound for changes
/// (where it is assumed all participants have finalized some previous set change by some point) or "sync points"
/// (i.e. epochs) where participants agree that some finalization occurred at some point in the past.
pub trait Supervisor: Clone + Send + 'static {
/// Index is the type used to indicate the in-progress consensus decision.
type Index;
/// Seed is a consensus artifact to use as randomness for leader selection.
type Seed;
/// Return the leader at a given index for the provided seed.
fn leader(&self, index: Self::Index, seed: Self::Seed) -> Option<PublicKey>;
/// Get the **sorted** participants for the given view. This is called when entering a new view before
/// listening for proposals or votes. If nothing is returned, the view will not be entered.
fn participants(&self, index: Self::Index) -> Option<&Vec<PublicKey>>;
// Indicate whether some candidate is a participant at the given view.
fn is_participant(&self, index: Self::Index, candidate: &PublicKey) -> Option<u32>;
/// Report some activity observed by the consensus implementation.
fn report(&self, activity: Activity, proof: Proof) -> impl Future<Output = ()> + Send;
}