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}