alloy_eips/eip4844/
builder.rs

1use crate::eip4844::{
2    utils::WholeFe, Blob, BYTES_PER_BLOB, FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENT_BYTES_USIZE,
3};
4use alloc::vec::Vec;
5
6#[cfg(any(feature = "kzg", feature = "arbitrary"))]
7use crate::eip4844::BlobTransactionSidecar;
8#[cfg(feature = "kzg")]
9use crate::eip4844::Bytes48;
10#[cfg(feature = "kzg")]
11use crate::{eip4844::env_settings::EnvKzgSettings, eip7594::BlobTransactionSidecarEip7594};
12use core::cmp;
13
14/// A builder for creating a [`BlobTransactionSidecar`].
15///
16/// [`BlobTransactionSidecar`]: crate::eip4844::BlobTransactionSidecar
17#[derive(Clone, Debug)]
18pub struct PartialSidecar {
19    /// The blobs in the sidecar.
20    blobs: Vec<Blob>,
21    /// The number of field elements that we have ingested, total.
22    fe: usize,
23}
24
25impl Default for PartialSidecar {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl PartialSidecar {
32    /// Create a new builder, and push an empty blob to it. This is the default
33    /// constructor, and allocates space for 2 blobs (256 KiB). If you want to
34    /// preallocate a specific number of blobs, use
35    /// [`PartialSidecar::with_capacity`].
36    pub fn new() -> Self {
37        Self::with_capacity(2)
38    }
39
40    /// Create a new builder, preallocating room for `capacity` blobs, and push
41    /// an empty blob to it.
42    pub fn with_capacity(capacity: usize) -> Self {
43        let mut blobs = Vec::with_capacity(capacity);
44        blobs.push(Blob::new([0u8; BYTES_PER_BLOB]));
45        Self { blobs, fe: 0 }
46    }
47
48    /// Get a reference to the blobs currently in the builder.
49    #[allow(clippy::missing_const_for_fn)]
50    pub fn blobs(&self) -> &[Blob] {
51        &self.blobs
52    }
53
54    /// Get the number of unused field elements that have been allocated
55    fn free_fe(&self) -> usize {
56        self.blobs.len() * FIELD_ELEMENTS_PER_BLOB as usize - self.fe
57    }
58
59    /// Calculate the length of used field elements IN BYTES in the builder.
60    ///
61    /// This is always strictly greater than the number of bytes that have been
62    /// ingested.
63    pub const fn len(&self) -> usize {
64        self.fe * 32
65    }
66
67    /// Check if the builder is empty.
68    pub const fn is_empty(&self) -> bool {
69        self.fe == 0
70    }
71
72    /// Push an empty blob to the builder.
73    fn push_empty_blob(&mut self) {
74        self.blobs.push(Blob::new([0u8; BYTES_PER_BLOB]));
75    }
76
77    /// Allocate enough space for the required number of new field elements.
78    pub fn alloc_fes(&mut self, required_fe: usize) {
79        while self.free_fe() < required_fe {
80            self.push_empty_blob()
81        }
82    }
83
84    /// Get the number of used field elements in the current blob.
85    const fn fe_in_current_blob(&self) -> usize {
86        self.fe % FIELD_ELEMENTS_PER_BLOB as usize
87    }
88
89    /// Get the index of the first unused field element in the current blob.
90    const fn first_unused_fe_index_in_current_blob(&self) -> usize {
91        self.fe_in_current_blob()
92    }
93
94    /// Get a mutable reference to the current blob.
95    fn current_blob_mut(&mut self) -> &mut Blob {
96        let last_unused_blob_index = self.fe / FIELD_ELEMENTS_PER_BLOB as usize;
97        self.blobs.get_mut(last_unused_blob_index).expect("never empty")
98    }
99
100    /// Get a mutable reference to the field element at the given index, in
101    /// the current blob.
102    fn fe_at_mut(&mut self, index: usize) -> &mut [u8] {
103        &mut self.current_blob_mut()[index * 32..(index + 1) * 32]
104    }
105
106    /// Get a mutable reference to the next unused field element.
107    fn next_unused_fe_mut(&mut self) -> &mut [u8] {
108        self.fe_at_mut(self.first_unused_fe_index_in_current_blob())
109    }
110
111    /// Ingest a field element into the current blobs.
112    pub fn ingest_valid_fe(&mut self, data: WholeFe<'_>) {
113        self.alloc_fes(1);
114        self.next_unused_fe_mut().copy_from_slice(data.as_ref());
115        self.fe += 1;
116    }
117
118    /// Ingest a partial FE into the current blobs.
119    ///
120    /// # Panics
121    ///
122    /// If the data is >=32 bytes. Or if there are not enough free FEs to
123    /// encode the data.
124    pub fn ingest_partial_fe(&mut self, data: &[u8]) {
125        self.alloc_fes(1);
126        let fe = self.next_unused_fe_mut();
127        fe[1..1 + data.len()].copy_from_slice(data);
128        self.fe += 1;
129    }
130}
131
132/// A strategy for coding and decoding data into sidecars.
133///
134/// Coder instances are responsible for encoding and decoding data into and from the sidecar. They
135/// are called by the [`SidecarBuilder`] during the [`ingest`], [`take`], and (if `c_kzg` feature
136/// enabled) `build` methods.
137///
138/// This trait allows different downstream users to use different bit-packing
139/// strategies. For example, a simple coder might only use the last 31 bytes of
140/// each blob, while a more complex coder might use a more sophisticated
141/// strategy to pack data into the low 6 bits of the top byte.
142///
143/// [`ingest`]: SidecarBuilder::ingest
144/// [`take`]: SidecarBuilder::take
145pub trait SidecarCoder {
146    /// Calculate the number of field elements required to store the given
147    /// data.
148    fn required_fe(&self, data: &[u8]) -> usize;
149
150    /// Code a slice of data into the builder.
151    fn code(&mut self, builder: &mut PartialSidecar, data: &[u8]);
152
153    /// Finish the sidecar, and commit to the data. This method should empty
154    /// any buffer or scratch space in the coder, and is called by
155    /// [`SidecarBuilder`]'s `take` and `build` methods.
156    fn finish(self, builder: &mut PartialSidecar);
157
158    /// Decode all slices of data from the blobs.
159    fn decode_all(&mut self, blobs: &[Blob]) -> Option<Vec<Vec<u8>>>;
160}
161
162/// Simple coder that only uses the last 31 bytes of each blob. This is the
163/// default coder for the [`SidecarBuilder`].
164///
165/// # Note
166///
167/// Because this coder sacrifices around 3% of total sidecar space, we do not
168/// recommend its use in production. It is provided for convenience and
169/// non-prod environments.
170///
171/// # Behavior
172///
173/// This coder encodes data as follows:
174/// - The first byte of every 32-byte word is empty.
175/// - Data is pre-pended with a 64-bit big-endian length prefix, which is right padded with zeros to
176///   form a complete word.
177/// - The rest of the data is packed into the remaining 31 bytes of each word.
178/// - If the data is not a multiple of 31 bytes, the last word is right-padded with zeros.
179///
180/// This means that the following regions cannot be used to store data, and are
181/// considered "wasted":
182///
183/// - The first byte of every 32-byte word.
184/// - The right padding on the header word containing the data length.
185/// - Any right padding on the last word for each piece of data.
186#[derive(Clone, Copy, Debug, Default)]
187#[non_exhaustive]
188pub struct SimpleCoder;
189
190impl SimpleCoder {
191    /// Decode an some bytes from an iterator of valid FEs.
192    ///
193    /// Returns `Ok(Some(data))` if there is some data.
194    /// Returns `Ok(None)` if there is no data (empty iterator, length prefix is 0).
195    /// Returns `Err(())` if there is an error.
196    fn decode_one<'a>(mut fes: impl Iterator<Item = WholeFe<'a>>) -> Result<Option<Vec<u8>>, ()> {
197        let Some(first) = fes.next() else {
198            return Ok(None);
199        };
200        let mut num_bytes = u64::from_be_bytes(first.as_ref()[1..9].try_into().unwrap()) as usize;
201
202        // if no more bytes is 0, we're done
203        if num_bytes == 0 {
204            return Ok(None);
205        }
206
207        // if there are too many bytes
208        const MAX_ALLOCATION_SIZE: usize = 2_097_152; //2 MiB
209        if num_bytes > MAX_ALLOCATION_SIZE {
210            return Err(());
211        }
212
213        let mut res = Vec::with_capacity(num_bytes);
214        while num_bytes > 0 {
215            let to_copy = cmp::min(31, num_bytes);
216            let fe = fes.next().ok_or(())?;
217            res.extend_from_slice(&fe.as_ref()[1..1 + to_copy]);
218            num_bytes -= to_copy;
219        }
220        Ok(Some(res))
221    }
222}
223
224impl SidecarCoder for SimpleCoder {
225    fn required_fe(&self, data: &[u8]) -> usize {
226        data.len().div_ceil(31) + 1
227    }
228
229    fn code(&mut self, builder: &mut PartialSidecar, mut data: &[u8]) {
230        if data.is_empty() {
231            return;
232        }
233
234        // first FE is the number of following bytes
235        builder.ingest_partial_fe(&(data.len() as u64).to_be_bytes());
236
237        // ingest the rest of the data
238        while !data.is_empty() {
239            let (left, right) = data.split_at(cmp::min(31, data.len()));
240            builder.ingest_partial_fe(left);
241            data = right
242        }
243    }
244
245    /// No-op
246    fn finish(self, _builder: &mut PartialSidecar) {}
247
248    fn decode_all(&mut self, blobs: &[Blob]) -> Option<Vec<Vec<u8>>> {
249        if blobs.is_empty() {
250            return None;
251        }
252
253        if blobs
254            .iter()
255            .flat_map(|blob| blob.chunks(FIELD_ELEMENT_BYTES_USIZE).map(WholeFe::new))
256            .any(|fe| fe.is_none())
257        {
258            return None;
259        }
260
261        let mut fes = blobs
262            .iter()
263            .flat_map(|blob| blob.chunks(FIELD_ELEMENT_BYTES_USIZE).map(WholeFe::new_unchecked));
264
265        let mut res = Vec::new();
266        loop {
267            match Self::decode_one(&mut fes) {
268                Ok(Some(data)) => res.push(data),
269                Ok(None) => break,
270                Err(()) => return None,
271            }
272        }
273        Some(res)
274    }
275}
276
277/// Build a [`BlobTransactionSidecar`] from an arbitrary amount of data.
278///
279/// This is useful for creating a sidecar from a large amount of data,
280/// which is then split into blobs. It delays KZG commitments and proofs
281/// until all data is ready.
282///
283/// [`BlobTransactionSidecar`]: crate::eip4844::BlobTransactionSidecar
284#[derive(Clone, Debug)]
285pub struct SidecarBuilder<T = SimpleCoder> {
286    /// The blob array we will code data into
287    inner: PartialSidecar,
288    /// The coder to use for ingesting and decoding data.
289    coder: T,
290}
291
292impl<T> Default for SidecarBuilder<T>
293where
294    T: Default + SidecarCoder,
295{
296    fn default() -> Self {
297        Self::new()
298    }
299}
300
301#[cfg(feature = "arbitrary")]
302impl<'a, T: arbitrary::Arbitrary<'a> + Clone> SidecarBuilder<T> {
303    /// Builds an arbitrary realization for BlobTransactionSidecar.
304    pub fn build_arbitrary(&self) -> BlobTransactionSidecar {
305        <BlobTransactionSidecar as arbitrary::Arbitrary>::arbitrary(
306            &mut arbitrary::Unstructured::new(&[]),
307        )
308        .unwrap()
309    }
310}
311
312impl<T: SidecarCoder + Default> SidecarBuilder<T> {
313    /// Instantiate a new builder and new coder instance.
314    ///
315    /// By default, this allocates space for 2 blobs (256 KiB). If you want to
316    /// preallocate a specific number of blobs, use
317    /// [`SidecarBuilder::with_capacity`].
318    pub fn new() -> Self {
319        T::default().into()
320    }
321
322    /// Create a new builder from a slice of data by calling
323    /// [`SidecarBuilder::from_coder_and_data`]
324    pub fn from_slice(data: &[u8]) -> Self {
325        Self::from_coder_and_data(T::default(), data)
326    }
327
328    /// Create a new builder with a pre-allocated capacity. This capacity is
329    /// measured in blobs, each of which is 128 KiB.
330    pub fn with_capacity(capacity: usize) -> Self {
331        Self::from_coder_and_capacity(T::default(), capacity)
332    }
333}
334
335impl<T: SidecarCoder> SidecarBuilder<T> {
336    /// Instantiate a new builder with the provided coder and capacity. This
337    /// capacity is measured in blobs, each of which is 128 KiB.
338    pub fn from_coder_and_capacity(coder: T, capacity: usize) -> Self {
339        Self { inner: PartialSidecar::with_capacity(capacity), coder }
340    }
341
342    /// Calculate the length of bytes used by field elements in the builder.
343    ///
344    /// This is always strictly greater than the number of bytes that have been
345    /// ingested.
346    pub const fn len(&self) -> usize {
347        self.inner.len()
348    }
349
350    /// Check if the builder is empty.
351    pub const fn is_empty(&self) -> bool {
352        self.inner.is_empty()
353    }
354
355    /// Create a new builder from a slice of data.
356    pub fn from_coder_and_data(coder: T, data: &[u8]) -> Self {
357        let required_fe = coder.required_fe(data);
358        let mut this = Self::from_coder_and_capacity(
359            coder,
360            required_fe.div_ceil(FIELD_ELEMENTS_PER_BLOB as usize),
361        );
362        this.ingest(data);
363        this
364    }
365
366    /// Ingest a slice of data into the builder.
367    pub fn ingest(&mut self, data: &[u8]) {
368        self.inner.alloc_fes(self.coder.required_fe(data));
369        self.coder.code(&mut self.inner, data);
370    }
371
372    /// Build the sidecar from the data with the provided settings.
373    #[cfg(feature = "kzg")]
374    pub fn build_with_settings(
375        self,
376        settings: &c_kzg::KzgSettings,
377    ) -> Result<BlobTransactionSidecar, c_kzg::Error> {
378        let mut commitments = Vec::with_capacity(self.inner.blobs.len());
379        let mut proofs = Vec::with_capacity(self.inner.blobs.len());
380        for blob in &self.inner.blobs {
381            // SAFETY: same size
382            let blob = unsafe { core::mem::transmute::<&Blob, &c_kzg::Blob>(blob) };
383            let commitment = settings.blob_to_kzg_commitment(blob)?;
384            let proof = settings.compute_blob_kzg_proof(blob, &commitment.to_bytes())?;
385
386            // SAFETY: same size
387            unsafe {
388                commitments
389                    .push(core::mem::transmute::<c_kzg::Bytes48, Bytes48>(commitment.to_bytes()));
390                proofs.push(core::mem::transmute::<c_kzg::Bytes48, Bytes48>(proof.to_bytes()));
391            }
392        }
393
394        Ok(BlobTransactionSidecar::new(self.inner.blobs, commitments, proofs))
395    }
396
397    /// Build the sidecar from the data, with default (Ethereum Mainnet)
398    /// settings.
399    #[cfg(feature = "kzg")]
400    pub fn build(self) -> Result<BlobTransactionSidecar, c_kzg::Error> {
401        self.build_with_settings(EnvKzgSettings::Default.get())
402    }
403
404    /// Take the blobs from the builder, without committing them to a KZG proof.
405    pub fn take(self) -> Vec<Blob> {
406        self.inner.blobs
407    }
408
409    /// Build the sidecar for eip-7594 from the data with the provided settings.
410    #[cfg(feature = "kzg")]
411    pub fn builder_7594_sidecar(
412        self,
413        settings: &c_kzg::KzgSettings,
414    ) -> Result<BlobTransactionSidecarEip7594, c_kzg::Error> {
415        let mut commitments = Vec::with_capacity(self.inner.blobs.len());
416        let mut proofs = Vec::with_capacity(self.inner.blobs.len());
417        for blob in &self.inner.blobs {
418            // SAFETY: same size
419            let blob = unsafe { core::mem::transmute::<&Blob, &c_kzg::Blob>(blob) };
420            let commitment = settings.blob_to_kzg_commitment(blob)?;
421            let (_cells, kzg_proofs) = settings.compute_cells_and_kzg_proofs(blob)?;
422
423            // SAFETY: same size
424            unsafe {
425                commitments
426                    .push(core::mem::transmute::<c_kzg::Bytes48, Bytes48>(commitment.to_bytes()));
427                for kzg_proof in kzg_proofs.iter() {
428                    proofs.push(core::mem::transmute::<c_kzg::Bytes48, Bytes48>(
429                        kzg_proof.to_bytes(),
430                    ));
431                }
432            }
433        }
434
435        Ok(BlobTransactionSidecarEip7594::new(self.inner.blobs, commitments, proofs))
436    }
437}
438
439impl<T: SidecarCoder> From<T> for SidecarBuilder<T> {
440    /// Instantiate a new builder with the provided coder.
441    ///
442    /// This is equivalent to calling
443    /// [`SidecarBuilder::from_coder_and_capacity`] with a capacity of 1.
444    /// If you want to preallocate a specific number of blobs, use
445    /// [`SidecarBuilder::from_coder_and_capacity`].
446    fn from(coder: T) -> Self {
447        Self::from_coder_and_capacity(coder, 1)
448    }
449}
450
451impl<T, R> FromIterator<R> for SidecarBuilder<T>
452where
453    T: SidecarCoder + Default,
454    R: AsRef<[u8]>,
455{
456    fn from_iter<I: IntoIterator<Item = R>>(iter: I) -> Self {
457        let mut this = Self::new();
458        for data in iter {
459            this.ingest(data.as_ref());
460        }
461        this
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use super::*;
468    use crate::eip4844::USABLE_BYTES_PER_BLOB;
469
470    #[test]
471    fn ingestion_strategy() {
472        let mut builder = PartialSidecar::new();
473        let data = &[
474            vec![1u8; 32],
475            vec![2u8; 372],
476            vec![3u8; 17],
477            vec![4u8; 5],
478            vec![5u8; 126_945],
479            vec![6u8; 2 * 126_945],
480        ];
481
482        data.iter().for_each(|data| SimpleCoder.code(&mut builder, data.as_slice()));
483
484        let decoded = SimpleCoder.decode_all(builder.blobs()).unwrap();
485        assert_eq!(decoded, data);
486    }
487
488    #[test]
489    fn big_ingestion_strategy() {
490        let data = vec![1u8; 126_945];
491        let builder = SidecarBuilder::<SimpleCoder>::from_slice(&data);
492
493        let blobs = builder.take();
494        let decoded = SimpleCoder.decode_all(&blobs).unwrap().concat();
495
496        assert_eq!(decoded, data);
497    }
498
499    #[test]
500    fn decode_all_rejects_invalid_data() {
501        assert_eq!(SimpleCoder.decode_all(&[]), None);
502        assert_eq!(SimpleCoder.decode_all(&[Blob::new([0xffu8; BYTES_PER_BLOB])]), None);
503    }
504
505    #[test]
506    fn it_ingests() {
507        // test ingesting a lot of data.
508        let data = [
509            vec![1u8; 32],
510            vec![2u8; 372],
511            vec![3u8; 17],
512            vec![4u8; 5],
513            vec![5u8; USABLE_BYTES_PER_BLOB + 2],
514        ];
515
516        let mut builder = data.iter().collect::<SidecarBuilder<SimpleCoder>>();
517
518        let expected_fe = data.iter().map(|d| SimpleCoder.required_fe(d)).sum::<usize>();
519        assert_eq!(builder.len(), expected_fe * 32);
520
521        // consume 2 more
522        builder.ingest(b"hello");
523        assert_eq!(builder.len(), expected_fe * 32 + 64);
524    }
525}