Skip to main content

commonware_consensus/marshal/
mod.rs

1//! Ordered delivery of finalized blocks.
2//!
3//! # Architecture
4//!
5//! The core of the module is the unified [`core::Actor`]. It marshals finalized blocks into order by:
6//!
7//! - Receiving uncertified blocks from a broadcast mechanism
8//! - Receiving notarizations and finalizations from consensus
9//! - Reconstructing a total order of finalized blocks
10//! - Providing a backfill mechanism for missing blocks
11//!
12//! The actor interacts with several components:
13//! - [`crate::Reporter`]: Receives ordered, finalized blocks at-least-once
14//! - [`crate::simplex`]: Provides consensus messages
15//! - Application: Provides verified blocks
16//! - [`commonware_broadcast::buffered`]: Provides uncertified blocks (standard mode)
17//! - [`coding::shards::Engine`]: Provides erasure-coded shards (coding mode)
18//! - [`resolver`]: Provides a backfill mechanism for missing blocks
19//!
20//! # Design
21//!
22//! ## Delivery
23//!
24//! The actor will deliver a block to the reporter at-least-once. The reporter should be prepared to
25//! handle duplicate deliveries. However the blocks will be in order.
26//!
27//! ## Finalization
28//!
29//! The actor uses a view-based model to track the state of the chain. Each view corresponds
30//! to a potential block in the chain. The actor will only finalize a block (and its ancestors)
31//! if it has a corresponding finalization from consensus.
32//!
33//! _It is possible that there may exist multiple finalizations for the same block in different views. Marshal
34//! only concerns itself with verifying a valid finalization exists for a block, not that a specific finalization
35//! exists. This means different Marshals may have different finalizations for the same block persisted to disk._
36//!
37//! ## Backfill
38//!
39//! The actor provides a backfill mechanism for missing blocks. If the actor notices a gap in its
40//! knowledge of finalized blocks, it will request the missing blocks from its peers. This ensures
41//! that the actor can catch up to the rest of the network if it falls behind.
42//!
43//! ## Storage
44//!
45//! The actor uses a combination of internal and external ([`store::Certificates`], [`store::Blocks`]) storage
46//! to store blocks and finalizations. Internal storage (in-memory caches) is used for data that is only
47//! needed for a short period of time, such as unverified blocks or notarizations. External storage
48//! (archive backends) is used to persist finalized blocks and certificates indefinitely.
49//!
50//! Marshal will store all blocks after a configurable starting height (or, floor) onward.
51//! This allows for state sync from a specific height rather than from genesis. The floor
52//! is supplied as a finalization; marshal fetches the corresponding block asynchronously
53//! before dispatching application blocks above it. When updating the starting height,
54//! marshal will attempt to prune blocks in external storage that are no longer needed, if
55//! the backing [`store::Blocks`] supports pruning.
56//!
57//! _Setting a configurable starting height will prevent others from backfilling blocks below said height. This
58//! feature is only recommended for applications that support state sync (i.e., those that don't require full
59//! block history to participate in consensus)._
60//!
61//! ## Limitations and Future Work
62//!
63//! - Only works with [crate::simplex] rather than general consensus.
64//! - Assumes at-most one notarization per view, incompatible with some consensus protocols.
65//! - Uses [`broadcast::buffered`](`commonware_broadcast::buffered`) for broadcasting and receiving
66//!   uncertified blocks from the network.
67
68use crate::{
69    types::{Height, Round},
70    Block,
71};
72use commonware_cryptography::Digest;
73use commonware_storage::archive;
74use commonware_utils::{acknowledgement::Exact, Acknowledgement};
75
76mod config;
77pub use config::{Config, Start};
78
79pub mod ancestry;
80pub mod core;
81pub mod resolver;
82pub mod standard;
83pub mod store;
84
85#[cfg(all(test, feature = "arbitrary"))]
86mod conformance;
87
88commonware_macros::stability_scope!(ALPHA {
89    pub(crate) mod application;
90    pub mod coding;
91});
92
93#[cfg(test)]
94pub mod mocks;
95
96/// An identifier for a block request.
97pub enum Identifier<D: Digest> {
98    /// The height of the block to retrieve.
99    Height(Height),
100    /// The digest of the block to retrieve.
101    Digest(D),
102    /// The highest finalized block. It may be the case that marshal does not have some of the
103    /// blocks below this height.
104    Latest,
105}
106
107// Allows using u64 directly for convenience.
108impl<D: Digest> From<Height> for Identifier<D> {
109    fn from(src: Height) -> Self {
110        Self::Height(src)
111    }
112}
113
114// Allows using &Digest directly for convenience.
115impl<D: Digest> From<&D> for Identifier<D> {
116    fn from(src: &D) -> Self {
117        Self::Digest(*src)
118    }
119}
120
121// Allows using archive identifiers directly for convenience.
122impl<D: Digest> From<archive::Identifier<'_, D>> for Identifier<D> {
123    fn from(src: archive::Identifier<'_, D>) -> Self {
124        match src {
125            archive::Identifier::Index(index) => Self::Height(Height::new(index)),
126            archive::Identifier::Key(key) => Self::Digest(*key),
127        }
128    }
129}
130
131/// An update reported to the application, either a new finalized tip or a finalized block.
132///
133/// Finalized tips are reported as soon as known, whether or not we hold all blocks up to that height.
134/// Finalized blocks are reported to the application in monotonically increasing order (no gaps permitted).
135#[derive(Clone, Debug)]
136pub enum Update<B: Block, A: Acknowledgement = Exact> {
137    /// A new finalized tip and the finalization round.
138    Tip(Round, Height, B::Digest),
139    /// A new finalized block and an [Acknowledgement] for the application to signal once processed.
140    ///
141    /// To ensure all blocks are delivered at least once, marshal waits to mark a block as delivered
142    /// until the application explicitly acknowledges the update. If the [Acknowledgement] is dropped before
143    /// handling, marshal will exit (assuming the application is shutting down).
144    ///
145    /// Because the [Acknowledgement] is clonable, the application can pass [Update] to multiple consumers
146    /// (and marshal will only consider the block delivered once all consumers have acknowledged it).
147    ///
148    /// Marshal only emits a block after it has durably persisted the said block. This ensures applications
149    /// that make stateful changes based on a block in other locations can access the same block on restart (often
150    /// some logic on startup attempts on infallible read on the last processed block).
151    Block(B, A),
152}