Skip to main content

commonware_consensus/marshal/coding/
variant.rs

1use crate::{
2    marshal::{
3        ancestry::BlockProvider,
4        coding::{
5            shards,
6            types::{CodedBlock, CodedBlockCfg, StoredCodedBlock},
7        },
8        core::{Buffer, CommitmentFallback, Mailbox, Variant},
9    },
10    simplex::types::Context,
11    types::{coding::Commitment, Round},
12    CertifiableBlock,
13};
14use commonware_codec::Read;
15use commonware_coding::Scheme as CodingScheme;
16use commonware_cryptography::{certificate::Scheme, Committable, Digestible, Hasher, PublicKey};
17use commonware_p2p::Recipients;
18use commonware_utils::channel::oneshot;
19use std::future::Future;
20
21/// The coding variant of Marshal, which uses erasure coding for block dissemination.
22///
23/// This variant distributes blocks as erasure-coded shards, allowing reconstruction
24/// from a subset of shards. This reduces bandwidth requirements for block propagation.
25#[derive(Default, Clone, Copy)]
26pub struct Coding<B, C, H, P>(std::marker::PhantomData<(B, C, H, P)>)
27where
28    B: CertifiableBlock<Context = Context<Commitment, P>>,
29    C: CodingScheme,
30    H: Hasher,
31    P: PublicKey;
32
33impl<B, C, H, P> Variant for Coding<B, C, H, P>
34where
35    B: CertifiableBlock<Context = Context<Commitment, P>>,
36    C: CodingScheme,
37    H: Hasher,
38    P: PublicKey,
39{
40    type ApplicationBlock = B;
41    type Block = CodedBlock<B, C, H>;
42    type StoredBlock = StoredCodedBlock<B, C, H>;
43    type Commitment = Commitment;
44
45    fn commitment(block: &Self::Block) -> Self::Commitment {
46        // Commitment is deterministic from the coded block contents.
47        block.commitment()
48    }
49
50    fn commitment_to_inner(commitment: Self::Commitment) -> <Self::Block as Digestible>::Digest {
51        // The inner digest is embedded in the coding commitment.
52        commitment.block()
53    }
54
55    fn parent_commitment(block: &Self::Block) -> Self::Commitment {
56        // Parent commitment is embedded in the consensus context.
57        block.context().parent.1
58    }
59
60    fn block_cfg(
61        block_cfg: &<Self::ApplicationBlock as Read>::Cfg,
62        expected: Self::Commitment,
63    ) -> <Self::Block as Read>::Cfg {
64        CodedBlockCfg {
65            inner: block_cfg.clone(),
66            expected,
67        }
68    }
69
70    fn into_inner(block: Self::Block) -> Self::ApplicationBlock {
71        block.into_inner()
72    }
73}
74
75impl<B, C, H, P> Buffer<Coding<B, C, H, P>> for shards::Mailbox<B, C, H, P>
76where
77    B: CertifiableBlock<Context = Context<Commitment, P>>,
78    C: CodingScheme,
79    H: Hasher,
80    P: PublicKey,
81{
82    type PublicKey = P;
83
84    async fn find_by_digest(
85        &self,
86        digest: <CodedBlock<B, C, H> as Digestible>::Digest,
87    ) -> Option<CodedBlock<B, C, H>> {
88        self.get_by_digest(digest).await
89    }
90
91    async fn find_by_commitment(&self, commitment: Commitment) -> Option<CodedBlock<B, C, H>> {
92        self.get(commitment).await
93    }
94
95    fn subscribe_by_digest(
96        &self,
97        digest: <CodedBlock<B, C, H> as Digestible>::Digest,
98    ) -> Option<oneshot::Receiver<CodedBlock<B, C, H>>> {
99        Some(self.subscribe_by_digest(digest))
100    }
101
102    fn subscribe_by_commitment(
103        &self,
104        commitment: Commitment,
105    ) -> Option<oneshot::Receiver<CodedBlock<B, C, H>>> {
106        Some(self.subscribe(commitment))
107    }
108
109    fn finalized(&self, commitment: Commitment) {
110        self.prune(commitment);
111    }
112
113    fn send(&self, round: Round, block: CodedBlock<B, C, H>, _recipients: Recipients<P>) {
114        // Targeted forwarding is not supported by the coding variant.
115        self.proposed(round, block);
116    }
117}
118
119impl<S, B, C, H, P> BlockProvider for Mailbox<S, Coding<B, C, H, P>>
120where
121    S: Scheme,
122    B: CertifiableBlock<Context = Context<Commitment, P>>,
123    C: CodingScheme,
124    H: Hasher,
125    P: PublicKey,
126{
127    type Block = B;
128
129    fn subscribe_parent(
130        &self,
131        block: &Self::Block,
132    ) -> impl Future<Output = Option<Self::Block>> + Send + 'static {
133        let receiver = block.height().previous().map(|parent_height| {
134            self.subscribe_by_commitment(
135                block.context().parent.1,
136                CommitmentFallback::FetchByCommitment {
137                    height: parent_height,
138                },
139            )
140        });
141        async move {
142            let receiver = receiver?;
143            receiver
144                .await
145                .ok()
146                .map(<Coding<B, C, H, P> as Variant>::into_inner)
147        }
148    }
149}