espresso_types/v0/v0_1/block.rs
1use serde::{Deserialize, Serialize};
2use std::iter::Peekable;
3
4use derive_more::Display;
5use std::ops::Range;
6use thiserror::Error;
7
8use hotshot_types::vid::advz::{LargeRangeProofType, SmallRangeProofType};
9
10use std::default::Default;
11
12/// Proof of correctness for namespace payload bytes in a block.
13#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
14pub struct ADVZNsProof {
15 pub(crate) ns_index: NsIndex,
16 pub(crate) ns_payload: NsPayloadOwned,
17 pub(crate) ns_proof: Option<LargeRangeProofType>, // `None` if ns_payload is empty
18}
19
20/// Byte lengths for the different items that could appear in a namespace table.
21pub const NUM_NSS_BYTE_LEN: usize = 4;
22pub const NS_OFFSET_BYTE_LEN: usize = 4;
23
24// TODO prefer [`NS_ID_BYTE_LEN`] set to `8` because [`NamespaceId`] is a `u64`
25// but we need to maintain serialization compatibility.
26// https://github.com/EspressoSystems/espresso-sequencer/issues/1574
27pub const NS_ID_BYTE_LEN: usize = 4;
28
29/// Raw binary data for a namespace table.
30///
31/// Any sequence of bytes is a valid [`NsTable`].
32///
33/// # Binary format of a namespace table
34///
35/// Byte lengths for the different items that could appear in a namespace table
36/// are specified in local private constants [`NUM_NSS_BYTE_LEN`],
37/// [`NS_OFFSET_BYTE_LEN`], [`NS_ID_BYTE_LEN`].
38///
39/// ## Number of entries in the namespace table
40///
41/// The first [`NUM_NSS_BYTE_LEN`] bytes of the namespace table indicate the
42/// number `n` of entries in the table as a little-endian unsigned integer. If
43/// the entire table length is smaller than [`NUM_NSS_BYTE_LEN`] then the
44/// missing bytes are zero-padded.
45///
46/// The bytes in the namespace table beyond the first [`NUM_NSS_BYTE_LEN`] bytes
47/// encode table entries. Each entry consumes exactly [`NS_ID_BYTE_LEN`] `+`
48/// [`NS_OFFSET_BYTE_LEN`] bytes.
49///
50/// The number `n` could be anything, including a number much larger than the
51/// number of entries that could fit in the namespace table. As such, the actual
52/// number of entries in the table is defined as the minimum of `n` and the
53/// maximum number of whole entries that could fit in the table.
54///
55/// See [`Self::in_bounds`] for clarification.
56///
57/// ## Namespace table entry
58///
59/// ### Namespace ID
60///
61/// The first [`NS_ID_BYTE_LEN`] bytes of each table entry indicate the
62/// [`NamespaceId`] for this namespace. Any table entry whose [`NamespaceId`] is
63/// a duplicate of a previous entry is ignored. A correct count of the number of
64/// *unique* (non-ignored) entries is given by `NsTable::iter().count()`.
65///
66/// ### Namespace offset
67///
68/// The next [`NS_OFFSET_BYTE_LEN`] bytes of each table entry indicate the
69/// end-index of a namespace in the block payload bytes
70/// [`Payload`](super::payload::Payload). This end-index is a little-endian
71/// unsigned integer.
72///
73/// # How to deduce a namespace's byte range
74///
75/// In order to extract the payload bytes of a single namespace `N` from the
76/// block payload one needs both the start- and end-indices for `N`.
77///
78/// See [`Self::ns_range`] for clarification. What follows is a description of
79/// what's implemented in [`Self::ns_range`].
80///
81/// If `N` occupies the `i`th entry in the namespace table for `i>0` then the
82/// start-index for `N` is defined as the end-index of the `(i-1)`th entry in
83/// the table.
84///
85/// Even if the `(i-1)`the entry would otherwise be ignored (due to a duplicate
86/// [`NamespaceId`] or any other reason), that entry's end-index still defines
87/// the start-index of `N`. This rule guarantees that both start- and
88/// end-indices for any namespace `N` can be read from a constant-size byte
89/// range in the namespace table, and it eliminates the need to traverse an
90/// unbounded number of previous entries of the namespace table looking for a
91/// previous non-ignored entry.
92///
93/// The start-index of the 0th entry in the table is implicitly defined to be
94/// `0`.
95///
96/// The start- and end-indices `(declared_start, declared_end)` declared in the
97/// namespace table could be anything. As such, the actual start- and
98/// end-indices `(start, end)` are defined so as to ensure that the byte range
99/// is well-defined and in-bounds for the block payload:
100/// ```ignore
101/// end = min(declared_end, block_payload_byte_length)
102/// start = min(declared_start, end)
103/// ```
104///
105/// In a "honestly-prepared" namespace table the end-index of the final
106/// namespace equals the byte length of the block payload. (Otherwise the block
107/// payload might have bytes that are not included in any namespace.)
108///
109/// It is possible that a namespace table could indicate two distinct namespaces
110/// whose byte ranges overlap, though no "honestly-prepared" namespace table
111/// would do this.
112///
113/// TODO prefer [`NsTable`] to be a newtype like this
114/// ```ignore
115/// #[repr(transparent)]
116/// #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
117/// #[serde(transparent)]
118/// pub struct NsTable(#[serde(with = "base64_bytes")] Vec<u8>);
119/// ```
120/// but we need to maintain serialization compatibility.
121/// <https://github.com/EspressoSystems/espresso-sequencer/issues/1575>
122#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
123// Boilerplate: `#[serde(remote = "Self")]` needed to check invariants on
124// deserialization. See
125// https://github.com/serde-rs/serde/issues/1220#issuecomment-382589140
126#[serde(remote = "Self")]
127pub struct NsTable {
128 #[serde(with = "base64_bytes")]
129 pub(crate) bytes: Vec<u8>,
130}
131
132/// Return type for [`NsTable::validate`].
133#[derive(Error, Debug, Display, Eq, PartialEq)]
134pub enum NsTableValidationError {
135 InvalidByteLen,
136 NonIncreasingEntries,
137 DuplicateNamespaceId,
138 InvalidHeader, // TODO this variant obsolete after https://github.com/EspressoSystems/espresso-sequencer/issues/1604
139 InvalidFinalOffset, // TODO this variant obsolete after https://github.com/EspressoSystems/espresso-sequencer/issues/1604
140 ExpectNonemptyNsTable,
141}
142
143pub struct NsTableBuilder {
144 pub(crate) bytes: Vec<u8>,
145 pub(crate) num_entries: usize,
146}
147
148/// Index for an entry in a ns table.
149#[derive(Clone, Debug, Eq, Hash, PartialEq)]
150pub struct NsIndex(pub(crate) usize);
151
152/// Number of entries in a namespace table.
153pub struct NumNss(pub(crate) usize);
154
155/// Return type for [`Payload::ns_iter`].
156pub struct NsIter(pub(crate) Range<usize>);
157
158/// Raw payload data for an entire block.
159///
160/// A block consists of two sequences of arbitrary bytes:
161/// - `ns_table`: namespace table
162/// - `ns_payloads`: namespace payloads
163///
164/// Any sequence of bytes is a valid `ns_table`. Any sequence of bytes is a
165/// valid `ns_payloads`. The contents of `ns_table` determine how to interpret
166/// `ns_payload`.
167///
168/// # Namespace table
169///
170/// See [`NsTable`] for the format of a namespace table.
171///
172/// # Namespace payloads
173///
174/// A concatenation of payload bytes for multiple individual namespaces.
175/// Namespace boundaries are dictated by `ns_table`. See [`NsPayload`] for the
176/// format of a namespace payload.
177#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
178pub struct Payload {
179 // Concatenated payload bytes for each namespace
180 //
181 // TODO want to rename thisfield to `ns_payloads`, but can't due to
182 // serialization compatibility.
183 #[serde(with = "base64_bytes")]
184 pub(crate) raw_payload: Vec<u8>,
185
186 pub(crate) ns_table: NsTable,
187}
188
189/// Byte length of a block payload, which includes all namespaces but *not* the
190/// namespace table.
191#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)]
192pub struct PayloadByteLen(pub(crate) usize);
193
194#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
195pub struct Index {
196 pub(crate) ns_index: NsIndex,
197 pub(crate) tx_index: TxIndex,
198}
199
200/// Cartesian product of [`NsIter`], [`TxIter`].
201pub struct Iter<'a> {
202 pub(crate) ns_iter: Peekable<NsIter>,
203 pub(crate) tx_iter: Option<TxIter>,
204 pub(crate) block: &'a Payload,
205}
206
207/// Index range for a namespace payload inside a block payload.
208#[derive(Clone, Debug, Eq, Hash, PartialEq)]
209pub struct NsPayloadRange(pub(crate) Range<usize>);
210
211/// Raw binary data for a single namespace's payload.
212///
213/// Any sequence of bytes is a valid [`NsPayload`].
214///
215/// See module-level documentation [`types`](super::types) for a full
216/// specification of the binary format of a namespace.
217pub struct NsPayload(pub(crate) [u8]);
218
219#[repr(transparent)]
220#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
221#[serde(transparent)]
222pub struct NsPayloadOwned(#[serde(with = "base64_bytes")] pub(crate) Vec<u8>);
223
224/// Proof of correctness for transaction bytes in a block.
225#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
226pub struct TxProof {
227 // Naming conventions for this struct's fields:
228 // - `payload_x`: bytes from the payload
229 // - `payload_proof_x`: a proof of those bytes from the payload
230 pub(crate) tx_index: TxIndex,
231
232 // Number of txs declared in the tx table
233 pub(crate) payload_num_txs: NumTxsUnchecked,
234 pub(crate) payload_proof_num_txs: SmallRangeProofType,
235
236 // Tx table entries for this tx
237 pub(crate) payload_tx_table_entries: TxTableEntries,
238 pub(crate) payload_proof_tx_table_entries: SmallRangeProofType,
239
240 // This tx's payload bytes.
241 // `None` if this tx has zero length.
242 pub(crate) payload_proof_tx: Option<SmallRangeProofType>,
243}
244
245/// Byte lengths for the different items that could appear in a tx table.
246pub const NUM_TXS_BYTE_LEN: usize = 4;
247pub const TX_OFFSET_BYTE_LEN: usize = 4;
248
249/// Number of txs in a namespace.
250///
251/// Like [`NumTxsUnchecked`] but checked against a [`NsPayloadByteLen`].
252pub struct NumTxs(pub(crate) usize);
253
254/// Byte length of a namespace payload.
255pub struct NsPayloadByteLen(pub(crate) usize);
256
257/// The part of a tx table that declares the number of txs in the payload.
258///
259/// "Unchecked" because this quantity might exceed the number of tx table
260/// entries that could fit into the namespace that contains it.
261///
262/// Use [`NumTxs`] for the actual number of txs in this namespace.
263#[derive(Clone, Debug, Eq, PartialEq)]
264pub struct NumTxsUnchecked(pub(crate) usize);
265
266/// Byte range for the part of a tx table that declares the number of txs in the
267/// payload.
268pub struct NumTxsRange(pub(crate) Range<usize>);
269
270/// Entries from a tx table in a namespace for use in a transaction proof.
271///
272/// Contains either one or two entries according to whether it was derived from
273/// the first transaction in the namespace.
274#[derive(Clone, Debug, Eq, PartialEq)]
275pub struct TxTableEntries {
276 pub(crate) cur: usize,
277 pub(crate) prev: Option<usize>, // `None` if derived from the first transaction
278}
279
280/// Byte range for entries from a tx table for use in a transaction proof.
281///
282/// This range covers either one or two entries from a tx table according to
283/// whether it was derived from the first transaction in the namespace.
284pub struct TxTableEntriesRange(pub(crate) Range<usize>);
285
286/// A transaction's payload data.
287pub struct TxPayload<'a>(pub(crate) &'a [u8]);
288
289/// Byte range for a transaction's payload data.
290pub struct TxPayloadRange(pub(crate) Range<usize>);
291
292/// Index for an entry in a tx table.
293#[derive(Clone, Debug, Eq, Hash, PartialEq)]
294pub struct TxIndex(pub(crate) usize);
295
296pub struct TxIter(pub(crate) Range<usize>);
297
298/// Build an individual namespace payload one transaction at a time.
299///
300/// Use [`Self::append_tx`] to add each transaction. Use [`Self::into_bytes`]
301/// when you're done. The returned bytes include a well-formed tx table and all
302/// tx payloads.
303#[derive(Default)]
304pub struct NsPayloadBuilder {
305 pub(crate) tx_table_entries: Vec<u8>,
306 pub(crate) tx_bodies: Vec<u8>,
307}