Skip to main content

espresso_types/v0/v0_1/
block.rs

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