Skip to main content

exoware_simplex/
types.rs

1use bytes::{Buf, BufMut, Bytes, BytesMut};
2use commonware_codec::{Decode, EncodeSize, Error, Read, Write};
3use commonware_consensus::{simplex::types, Block};
4use commonware_cryptography::{certificate, Digest};
5
6const HEADER_LENGTH_BYTES: usize = 4;
7
8fn write_block_data<B>(header: &B, body: &[u8], buf: &mut impl BufMut)
9where
10    B: Write + EncodeSize,
11{
12    let header_len =
13        u32::try_from(header.encode_size()).expect("header block encoding exceeds u32 length");
14    buf.put_u32(header_len);
15    header.write(buf);
16    buf.put_slice(body);
17}
18
19pub fn encode_block_data<B>(header: &B, body: &[u8]) -> Bytes
20where
21    B: Write + EncodeSize,
22{
23    let mut buf = BytesMut::with_capacity(HEADER_LENGTH_BYTES + header.encode_size() + body.len());
24    write_block_data(header, body, &mut buf);
25    buf.freeze()
26}
27
28/// Simplex header bytes plus arbitrary body bytes that ride with them.
29///
30/// The header portion is the only part covered by the certificate digest.
31/// The body portion can carry transactions or other non-certified data that
32/// should be fetched and streamed with the header bytes.
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct BlockData<B> {
35    pub header: B,
36    pub body: Bytes,
37}
38
39impl<B> BlockData<B> {
40    pub fn new(header: B) -> Self {
41        Self {
42            header,
43            body: Bytes::new(),
44        }
45    }
46
47    pub fn with_body(header: B, body: impl Into<Bytes>) -> Self {
48        Self {
49            header,
50            body: body.into(),
51        }
52    }
53}
54
55impl<B> Write for BlockData<B>
56where
57    B: Write + EncodeSize,
58{
59    fn write(&self, buf: &mut impl BufMut) {
60        write_block_data(&self.header, &self.body, buf);
61    }
62}
63
64impl<B> Read for BlockData<B>
65where
66    B: Read,
67{
68    type Cfg = <B as Read>::Cfg;
69
70    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
71        if buf.remaining() < HEADER_LENGTH_BYTES {
72            return Err(Error::Invalid(
73                "exoware_simplex::BlockData",
74                "missing header length",
75            ));
76        }
77        let header_len = buf.get_u32() as usize;
78        if buf.remaining() < header_len {
79            return Err(Error::Invalid(
80                "exoware_simplex::BlockData",
81                "header length exceeds remaining bytes",
82            ));
83        }
84        let header = B::decode_cfg(buf.copy_to_bytes(header_len), cfg)?;
85        let body = buf.copy_to_bytes(buf.remaining());
86        Ok(Self { header, body })
87    }
88}
89
90impl<B> EncodeSize for BlockData<B>
91where
92    B: EncodeSize,
93{
94    fn encode_size(&self) -> usize {
95        HEADER_LENGTH_BYTES + self.header.encode_size() + self.body.len()
96    }
97}
98
99/// A Simplex notarization plus the header bytes for its payload digest.
100#[derive(Clone, Debug)]
101pub struct Notarized<B, S: certificate::Scheme, D: Digest> {
102    pub proof: types::Notarization<S, D>,
103    pub header: B,
104}
105
106impl<B, S, D> PartialEq for Notarized<B, S, D>
107where
108    B: PartialEq,
109    S: certificate::Scheme,
110    D: Digest,
111{
112    fn eq(&self, other: &Self) -> bool {
113        self.proof == other.proof && self.header == other.header
114    }
115}
116
117impl<B, S, D> Eq for Notarized<B, S, D>
118where
119    B: Eq,
120    S: certificate::Scheme,
121    D: Digest,
122{
123}
124
125impl<B, S, D> Notarized<B, S, D>
126where
127    B: Block<Digest = D>,
128    S: certificate::Scheme,
129    D: Digest,
130{
131    pub fn new(proof: types::Notarization<S, D>, header: B) -> Result<Self, Error> {
132        if proof.proposal.payload != header.digest() {
133            return Err(Error::Invalid(
134                "exoware_simplex::Notarized",
135                "proof payload does not match header digest",
136            ));
137        }
138        Ok(Self { proof, header })
139    }
140}
141
142impl<B, S, D> Write for Notarized<B, S, D>
143where
144    B: Block<Digest = D>,
145    S: certificate::Scheme,
146    D: Digest,
147{
148    fn write(&self, buf: &mut impl BufMut) {
149        self.proof.write(buf);
150        self.header.write(buf);
151    }
152}
153
154impl<B, S, D> Read for Notarized<B, S, D>
155where
156    B: Block<Digest = D>,
157    S: certificate::Scheme,
158    D: Digest,
159    <S::Certificate as Read>::Cfg: Clone,
160{
161    type Cfg = (<S::Certificate as Read>::Cfg, <B as Read>::Cfg);
162
163    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
164        let (proof, header) = <(types::Notarization<S, D>, B) as Read>::read_cfg(buf, cfg)?;
165        Self::new(proof, header)
166    }
167}
168
169impl<B, S, D> EncodeSize for Notarized<B, S, D>
170where
171    B: Block<Digest = D>,
172    S: certificate::Scheme,
173    D: Digest,
174{
175    fn encode_size(&self) -> usize {
176        self.proof.encode_size() + self.header.encode_size()
177    }
178}
179
180/// A Simplex finalization plus the header bytes for its payload digest.
181#[derive(Clone, Debug)]
182pub struct Finalized<B, S: certificate::Scheme, D: Digest> {
183    pub proof: types::Finalization<S, D>,
184    pub header: B,
185}
186
187impl<B, S, D> PartialEq for Finalized<B, S, D>
188where
189    B: PartialEq,
190    S: certificate::Scheme,
191    D: Digest,
192{
193    fn eq(&self, other: &Self) -> bool {
194        self.proof == other.proof && self.header == other.header
195    }
196}
197
198impl<B, S, D> Eq for Finalized<B, S, D>
199where
200    B: Eq,
201    S: certificate::Scheme,
202    D: Digest,
203{
204}
205
206impl<B, S, D> Finalized<B, S, D>
207where
208    B: Block<Digest = D>,
209    S: certificate::Scheme,
210    D: Digest,
211{
212    pub fn new(proof: types::Finalization<S, D>, header: B) -> Result<Self, Error> {
213        if proof.proposal.payload != header.digest() {
214            return Err(Error::Invalid(
215                "exoware_simplex::Finalized",
216                "proof payload does not match header digest",
217            ));
218        }
219        Ok(Self { proof, header })
220    }
221}
222
223impl<B, S, D> Write for Finalized<B, S, D>
224where
225    B: Block<Digest = D>,
226    S: certificate::Scheme,
227    D: Digest,
228{
229    fn write(&self, buf: &mut impl BufMut) {
230        self.proof.write(buf);
231        self.header.write(buf);
232    }
233}
234
235impl<B, S, D> Read for Finalized<B, S, D>
236where
237    B: Block<Digest = D>,
238    S: certificate::Scheme,
239    D: Digest,
240    <S::Certificate as Read>::Cfg: Clone,
241{
242    type Cfg = (<S::Certificate as Read>::Cfg, <B as Read>::Cfg);
243
244    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
245        let (proof, header) = <(types::Finalization<S, D>, B) as Read>::read_cfg(buf, cfg)?;
246        Self::new(proof, header)
247    }
248}
249
250impl<B, S, D> EncodeSize for Finalized<B, S, D>
251where
252    B: Block<Digest = D>,
253    S: certificate::Scheme,
254    D: Digest,
255{
256    fn encode_size(&self) -> usize {
257        self.proof.encode_size() + self.header.encode_size()
258    }
259}
260
261#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
262pub struct UploadSummary {
263    pub headers: usize,
264    pub blocks: usize,
265    pub notarizations: usize,
266    pub finalizations: usize,
267    pub finalized_height_indexes: usize,
268}
269
270#[derive(Clone, Copy, Debug, PartialEq, Eq)]
271pub struct UploadReceipt {
272    pub store_sequence_number: u64,
273    pub summary: UploadSummary,
274}