Skip to content

Commit 39cc015

Browse files
committed
Add erasure coded block envelope wrapper
1 parent dbaa491 commit 39cc015

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

consensus/src/marshal/envelope.rs

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
//! An envelope type for an erasure coded [Block].
2+
3+
use crate::Block;
4+
use commonware_codec::{EncodeSize, Read, Write};
5+
use commonware_coding::reed_solomon;
6+
use commonware_cryptography::{Committable, Digestible, Hasher};
7+
8+
/// An envelope type for an erasure coded [Block].
9+
#[derive(Debug, Clone)]
10+
pub struct CodedBlock<B, H>
11+
where
12+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
13+
H: Hasher,
14+
{
15+
/// The inner block type.
16+
inner: B,
17+
/// The erasure coding configuration.
18+
config: (u16, u16),
19+
/// The erasure coding commitment.
20+
commitment: H::Digest,
21+
}
22+
23+
impl<B, H> CodedBlock<B, H>
24+
where
25+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
26+
H: Hasher,
27+
{
28+
/// Erasure codes the block to create the commitment.
29+
fn commit(inner: &B, config: (u16, u16)) -> H::Digest {
30+
let mut buf = Vec::with_capacity(config.encode_size() + inner.encode_size());
31+
inner.write(&mut buf);
32+
config.write(&mut buf);
33+
34+
let (commitment, _) =
35+
reed_solomon::encode::<H>(config.0, config.1, buf).expect("failed to commit to block");
36+
commitment
37+
}
38+
39+
/// Create a new [CodedBlock] from a [Block] and a configuration.
40+
pub fn new(inner: B, config: (u16, u16)) -> Self {
41+
let commitment = Self::commit(&inner, config);
42+
Self {
43+
inner,
44+
config,
45+
commitment,
46+
}
47+
}
48+
49+
/// Returns a reference to the inner [Block].
50+
pub fn inner(&self) -> &B {
51+
&self.inner
52+
}
53+
}
54+
55+
impl<B, H> Write for CodedBlock<B, H>
56+
where
57+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
58+
H: Hasher,
59+
{
60+
fn write(&self, buf: &mut impl bytes::BufMut) {
61+
self.inner.write(buf);
62+
self.config.write(buf);
63+
}
64+
}
65+
66+
impl<B, H> Read for CodedBlock<B, H>
67+
where
68+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
69+
H: Hasher,
70+
{
71+
type Cfg = B::Cfg;
72+
73+
fn read_cfg(
74+
buf: &mut impl bytes::Buf,
75+
cfg: &Self::Cfg,
76+
) -> Result<Self, commonware_codec::Error> {
77+
let inner = B::read_cfg(buf, cfg)?;
78+
let config = <(u16, u16)>::read_cfg(buf, &((), ()))?;
79+
let commitment = Self::commit(&inner, config);
80+
81+
Ok(Self {
82+
inner,
83+
config,
84+
commitment,
85+
})
86+
}
87+
}
88+
89+
impl<B, H> EncodeSize for CodedBlock<B, H>
90+
where
91+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
92+
H: Hasher,
93+
{
94+
fn encode_size(&self) -> usize {
95+
self.inner.encode_size() + self.config.encode_size()
96+
}
97+
}
98+
99+
impl<B, H> Digestible for CodedBlock<B, H>
100+
where
101+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
102+
H: Hasher,
103+
{
104+
type Digest = B::Digest;
105+
106+
fn digest(&self) -> Self::Digest {
107+
self.inner.digest()
108+
}
109+
}
110+
111+
impl<B, H> Committable for CodedBlock<B, H>
112+
where
113+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
114+
H: Hasher,
115+
{
116+
type Commitment = B::Commitment;
117+
118+
fn commitment(&self) -> Self::Commitment {
119+
self.commitment
120+
}
121+
}
122+
123+
impl<B, H> Block for CodedBlock<B, H>
124+
where
125+
B: Block<Digest = H::Digest, Commitment = H::Digest>,
126+
H: Hasher,
127+
{
128+
fn height(&self) -> u64 {
129+
self.inner.height()
130+
}
131+
132+
fn parent(&self) -> Self::Commitment {
133+
self.inner.parent()
134+
}
135+
}
136+
137+
impl<B, H> PartialEq for CodedBlock<B, H>
138+
where
139+
B: Block<Digest = H::Digest, Commitment = H::Digest> + PartialEq,
140+
H: Hasher,
141+
{
142+
fn eq(&self, other: &Self) -> bool {
143+
self.inner == other.inner
144+
&& self.config == other.config
145+
&& self.commitment == other.commitment
146+
}
147+
}
148+
149+
impl<B, H> Eq for CodedBlock<B, H>
150+
where
151+
B: Block<Digest = H::Digest, Commitment = H::Digest> + PartialEq,
152+
H: Hasher,
153+
{
154+
}
155+
156+
#[cfg(test)]
157+
mod test {
158+
use crate::marshal::{envelope::CodedBlock, mocks::block::Block};
159+
use commonware_codec::{Decode, Encode};
160+
use commonware_cryptography::{sha256::Digest as Sha256Digest, Hasher, Sha256};
161+
162+
#[test]
163+
fn test_codec_roundtrip() {
164+
const MOCK_BLOCK_DATA: &[u8] = b"commonware bit twiddling club";
165+
const CONFIG: (u16, u16) = (4, 2);
166+
167+
let inner = Block::new::<Sha256>(Sha256::hash(MOCK_BLOCK_DATA), 0xFE, 0xFF);
168+
let block = CodedBlock::<_, Sha256>::new(inner, CONFIG);
169+
170+
let encoded = block.encode().to_vec();
171+
let decoded =
172+
CodedBlock::<Block<Sha256Digest>, Sha256>::decode_cfg(&mut encoded.as_ref(), &())
173+
.unwrap();
174+
175+
assert_eq!(block, decoded);
176+
}
177+
}

consensus/src/marshal/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub use finalizer::Finalizer;
6363
pub mod ingress;
6464
pub use ingress::mailbox::Mailbox;
6565
pub mod resolver;
66+
pub mod envelope;
6667

6768
#[cfg(test)]
6869
pub mod mocks;

0 commit comments

Comments
 (0)