Skip to main content

commonware_consensus/marshal/standard/
variant.rs

1//! Standard variant implementation for Marshal.
2//!
3//! The standard variant broadcasts complete blocks to all peers. Each validator
4//! receives the full block directly from the proposer or via gossip.
5
6use crate::{
7    marshal::{
8        ancestry::BlockProvider,
9        core::{Buffer, CommitmentFallback, Mailbox, Variant},
10    },
11    simplex::scheme::Scheme as SimplexScheme,
12    types::Round,
13    Block,
14};
15use commonware_broadcast::buffered;
16use commonware_codec::Read;
17use commonware_cryptography::{certificate::Scheme, Digestible, PublicKey};
18use commonware_p2p::Recipients;
19use commonware_utils::channel::oneshot;
20use std::{future::Future, sync::Arc};
21
22/// The standard variant of Marshal, which broadcasts complete blocks.
23///
24/// This variant sends the entire block to all peers.
25#[derive(Default, Clone, Copy)]
26pub struct Standard<B: Block>(std::marker::PhantomData<B>);
27
28impl<B> Variant for Standard<B>
29where
30    B: Block,
31{
32    type ApplicationBlock = B;
33    type Block = B;
34    type StoredBlock = B;
35    type Commitment = <B as Digestible>::Digest;
36
37    fn commitment(block: &Self::Block) -> Self::Commitment {
38        // Standard variant commitment is exactly the block digest.
39        block.digest()
40    }
41
42    fn stored_commitment(block: &Self::StoredBlock) -> Self::Commitment {
43        block.digest()
44    }
45
46    fn commitment_to_inner(commitment: Self::Commitment) -> <Self::Block as Digestible>::Digest {
47        // Trivial left-inverse: digest == commitment in this variant.
48        commitment
49    }
50
51    fn parent_commitment(block: &Self::Block) -> Self::Commitment {
52        // In standard mode, commitments are digests, so parent commitment is parent digest.
53        block.parent()
54    }
55
56    fn check_payload<S>(_scheme: &S, _payload: Self::Commitment) -> bool
57    where
58        S: SimplexScheme<Self::Commitment>,
59    {
60        true
61    }
62
63    fn block_cfg(
64        block_cfg: &<Self::ApplicationBlock as Read>::Cfg,
65        _expected: Self::Commitment,
66    ) -> <Self::Block as Read>::Cfg {
67        block_cfg.clone()
68    }
69
70    fn into_inner(block: Self::Block) -> Self::ApplicationBlock {
71        block
72    }
73
74    fn into_inner_shared(block: Arc<Self::Block>) -> Arc<Self::ApplicationBlock> {
75        block
76    }
77
78    fn owned_into_inner_shared(block: Self::Block) -> Arc<Self::ApplicationBlock> {
79        Arc::new(block)
80    }
81
82    fn from_application_block(
83        block: Self::ApplicationBlock,
84        _payload: Self::Commitment,
85    ) -> Self::Block {
86        block
87    }
88}
89
90impl<B, K> Buffer<Standard<B>> for buffered::Mailbox<K, B>
91where
92    B: Block,
93    K: PublicKey,
94{
95    type PublicKey = K;
96
97    async fn find_by_digest(&self, digest: B::Digest) -> Option<Arc<B>> {
98        self.get(digest).await
99    }
100
101    async fn find_by_commitment(&self, commitment: B::Digest) -> Option<Arc<B>> {
102        self.find_by_digest(commitment).await
103    }
104
105    fn subscribe_by_digest(&self, digest: B::Digest) -> Option<oneshot::Receiver<Arc<B>>> {
106        Some(self.subscribe(digest))
107    }
108
109    fn subscribe_by_commitment(&self, commitment: B::Digest) -> Option<oneshot::Receiver<Arc<B>>> {
110        self.subscribe_by_digest(commitment)
111    }
112
113    fn finalized(&self, _commitment: B::Digest) {
114        // No cleanup needed in standard mode - the buffer handles its own pruning
115    }
116
117    fn send(&self, _round: Round, block: Arc<B>, recipients: Recipients<K>) {
118        self.broadcast_shared(recipients, block);
119    }
120}
121
122impl<S, B> BlockProvider for Mailbox<S, Standard<B>>
123where
124    S: Scheme,
125    B: Block,
126{
127    type Block = B;
128
129    fn subscribe_parent(
130        &self,
131        block: &Self::Block,
132    ) -> impl Future<Output = Option<Arc<Self::Block>>> + Send + 'static {
133        let receiver = block.height().previous().map(|parent_height| {
134            self.subscribe_by_commitment(
135                block.parent(),
136                CommitmentFallback::FetchByCommitment {
137                    height: parent_height,
138                },
139            )
140        });
141        async move { receiver?.await.ok() }
142    }
143}