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};
75use std::sync::Arc;
76
77mod config;
78pub use config::{Config, Start};
79
80pub mod ancestry;
81pub mod core;
82pub mod resolver;
83pub mod standard;
84pub mod store;
85
86#[cfg(all(test, feature = "arbitrary"))]
87mod conformance;
88
89commonware_macros::stability_scope!(ALPHA {
90 pub(crate) mod application;
91 pub mod coding;
92});
93
94#[cfg(test)]
95pub mod mocks;
96
97/// An identifier for a block request.
98pub enum Identifier<D: Digest> {
99 /// The height of the block to retrieve.
100 Height(Height),
101 /// The digest of the block to retrieve.
102 Digest(D),
103 /// The highest finalized block. It may be the case that marshal does not have some of the
104 /// blocks below this height.
105 Latest,
106}
107
108// Allows using u64 directly for convenience.
109impl<D: Digest> From<Height> for Identifier<D> {
110 fn from(src: Height) -> Self {
111 Self::Height(src)
112 }
113}
114
115// Allows using &Digest directly for convenience.
116impl<D: Digest> From<&D> for Identifier<D> {
117 fn from(src: &D) -> Self {
118 Self::Digest(*src)
119 }
120}
121
122// Allows using archive identifiers directly for convenience.
123impl<D: Digest> From<archive::Identifier<'_, D>> for Identifier<D> {
124 fn from(src: archive::Identifier<'_, D>) -> Self {
125 match src {
126 archive::Identifier::Index(index) => Self::Height(Height::new(index)),
127 archive::Identifier::Key(key) => Self::Digest(*key),
128 }
129 }
130}
131
132/// An update reported to the application, either a new finalized tip or a finalized block.
133///
134/// Finalized tips are reported as soon as known, whether or not we hold all blocks up to that height.
135/// Finalized blocks are reported to the application in monotonically increasing order (no gaps permitted).
136#[derive(Clone, Debug)]
137pub enum Update<B: Block, A: Acknowledgement = Exact> {
138 /// A new finalized tip and the finalization round.
139 Tip(Round, Height, B::Digest),
140 /// A new finalized block and an [Acknowledgement] for the application to signal once processed.
141 ///
142 /// To ensure all blocks are delivered at least once, marshal waits to mark a block as delivered
143 /// until the application explicitly acknowledges the update. If the [Acknowledgement] is dropped before
144 /// handling, marshal will exit (assuming the application is shutting down).
145 ///
146 /// Cloning the update shares the immutable block, so applications can fan it out without requiring
147 /// block clones. Marshal only considers the block delivered once every acknowledgement is handled.
148 ///
149 /// Marshal only emits a block after it has durably persisted the said block. This ensures applications
150 /// that make stateful changes based on a block in other locations can access the same block on restart (often
151 /// some logic on startup attempts on infallible read on the last processed block).
152 Block(Arc<B>, A),
153}