alloy_eips/eip4844/
builder.rs1use crate::eip4844::Blob;
2#[cfg(feature = "kzg")]
3use c_kzg::{KzgCommitment, KzgProof};
4
5use crate::eip4844::{
6 utils::WholeFe, BYTES_PER_BLOB, FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENT_BYTES_USIZE,
7};
8use alloc::vec::Vec;
9
10#[cfg(feature = "kzg")]
11use crate::eip4844::env_settings::EnvKzgSettings;
12#[cfg(any(feature = "kzg", feature = "arbitrary"))]
13use crate::eip4844::BlobTransactionSidecar;
14#[cfg(feature = "kzg")]
15use crate::eip4844::Bytes48;
16use core::cmp;
17
18#[derive(Clone, Debug)]
22pub struct PartialSidecar {
23 blobs: Vec<Blob>,
25 fe: usize,
27}
28
29impl Default for PartialSidecar {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl PartialSidecar {
36 pub fn new() -> Self {
41 Self::with_capacity(2)
42 }
43
44 pub fn with_capacity(capacity: usize) -> Self {
47 let mut blobs = Vec::with_capacity(capacity);
48 blobs.push(Blob::new([0u8; BYTES_PER_BLOB]));
49 Self { blobs, fe: 0 }
50 }
51
52 pub fn blobs(&self) -> &[Blob] {
54 &self.blobs
55 }
56
57 fn free_fe(&self) -> usize {
59 self.blobs.len() * FIELD_ELEMENTS_PER_BLOB as usize - self.fe
60 }
61
62 pub const fn len(&self) -> usize {
67 self.fe * 32
68 }
69
70 pub const fn is_empty(&self) -> bool {
72 self.fe == 0
73 }
74
75 fn push_empty_blob(&mut self) {
77 self.blobs.push(Blob::new([0u8; BYTES_PER_BLOB]));
78 }
79
80 pub fn alloc_fes(&mut self, required_fe: usize) {
82 while self.free_fe() < required_fe {
83 self.push_empty_blob()
84 }
85 }
86
87 const fn fe_in_current_blob(&self) -> usize {
89 self.fe % FIELD_ELEMENTS_PER_BLOB as usize
90 }
91
92 const fn first_unused_fe_index_in_current_blob(&self) -> usize {
94 self.fe_in_current_blob()
95 }
96
97 fn current_blob_mut(&mut self) -> &mut Blob {
99 let last_unused_blob_index = self.fe / FIELD_ELEMENTS_PER_BLOB as usize;
100 self.blobs.get_mut(last_unused_blob_index).expect("never empty")
101 }
102
103 fn fe_at_mut(&mut self, index: usize) -> &mut [u8] {
106 &mut self.current_blob_mut()[index * 32..(index + 1) * 32]
107 }
108
109 fn next_unused_fe_mut(&mut self) -> &mut [u8] {
111 self.fe_at_mut(self.first_unused_fe_index_in_current_blob())
112 }
113
114 pub fn ingest_valid_fe(&mut self, data: WholeFe<'_>) {
116 self.alloc_fes(1);
117 self.next_unused_fe_mut().copy_from_slice(data.as_ref());
118 self.fe += 1;
119 }
120
121 pub fn ingest_partial_fe(&mut self, data: &[u8]) {
128 self.alloc_fes(1);
129 let fe = self.next_unused_fe_mut();
130 fe[1..1 + data.len()].copy_from_slice(data);
131 self.fe += 1;
132 }
133}
134
135pub trait SidecarCoder {
149 fn required_fe(&self, data: &[u8]) -> usize;
152
153 fn code(&mut self, builder: &mut PartialSidecar, data: &[u8]);
155
156 fn finish(self, builder: &mut PartialSidecar);
160
161 fn decode_all(&mut self, blobs: &[Blob]) -> Option<Vec<Vec<u8>>>;
163}
164
165#[derive(Clone, Copy, Debug, Default)]
190#[non_exhaustive]
191pub struct SimpleCoder;
192
193impl SimpleCoder {
194 fn decode_one<'a>(mut fes: impl Iterator<Item = WholeFe<'a>>) -> Result<Option<Vec<u8>>, ()> {
200 let Some(first) = fes.next() else {
201 return Ok(None);
202 };
203 let mut num_bytes = u64::from_be_bytes(first.as_ref()[1..9].try_into().unwrap()) as usize;
204
205 if num_bytes == 0 {
207 return Ok(None);
208 }
209
210 const MAX_ALLOCATION_SIZE: usize = 2_097_152; if num_bytes > MAX_ALLOCATION_SIZE {
213 return Err(());
214 }
215
216 let mut res = Vec::with_capacity(num_bytes);
217 while num_bytes > 0 {
218 let to_copy = cmp::min(31, num_bytes);
219 let fe = fes.next().ok_or(())?;
220 res.extend_from_slice(&fe.as_ref()[1..1 + to_copy]);
221 num_bytes -= to_copy;
222 }
223 Ok(Some(res))
224 }
225}
226
227impl SidecarCoder for SimpleCoder {
228 fn required_fe(&self, data: &[u8]) -> usize {
229 data.len().div_ceil(31) + 1
230 }
231
232 fn code(&mut self, builder: &mut PartialSidecar, mut data: &[u8]) {
233 if data.is_empty() {
234 return;
235 }
236
237 builder.ingest_partial_fe(&(data.len() as u64).to_be_bytes());
239
240 while !data.is_empty() {
242 let (left, right) = data.split_at(cmp::min(31, data.len()));
243 builder.ingest_partial_fe(left);
244 data = right
245 }
246 }
247
248 fn finish(self, _builder: &mut PartialSidecar) {}
250
251 fn decode_all(&mut self, blobs: &[Blob]) -> Option<Vec<Vec<u8>>> {
252 if blobs.is_empty() {
253 return None;
254 }
255
256 if blobs
257 .iter()
258 .flat_map(|blob| blob.chunks(FIELD_ELEMENT_BYTES_USIZE).map(WholeFe::new))
259 .any(|fe| fe.is_none())
260 {
261 return None;
262 }
263
264 let mut fes = blobs
265 .iter()
266 .flat_map(|blob| blob.chunks(FIELD_ELEMENT_BYTES_USIZE).map(WholeFe::new_unchecked));
267
268 let mut res = Vec::new();
269 loop {
270 match Self::decode_one(&mut fes) {
271 Ok(Some(data)) => res.push(data),
272 Ok(None) => break,
273 Err(()) => return None,
274 }
275 }
276 Some(res)
277 }
278}
279
280#[derive(Clone, Debug)]
288pub struct SidecarBuilder<T = SimpleCoder> {
289 inner: PartialSidecar,
291 coder: T,
293}
294
295impl<T> Default for SidecarBuilder<T>
296where
297 T: Default + SidecarCoder,
298{
299 fn default() -> Self {
300 Self::new()
301 }
302}
303
304#[cfg(feature = "arbitrary")]
305impl<'a, T: arbitrary::Arbitrary<'a> + Clone> SidecarBuilder<T> {
306 pub fn build_arbitrary(&self) -> BlobTransactionSidecar {
308 <BlobTransactionSidecar as arbitrary::Arbitrary>::arbitrary(
309 &mut arbitrary::Unstructured::new(&[]),
310 )
311 .unwrap()
312 }
313}
314
315impl<T: SidecarCoder + Default> SidecarBuilder<T> {
316 pub fn new() -> Self {
322 T::default().into()
323 }
324
325 pub fn from_slice(data: &[u8]) -> Self {
328 Self::from_coder_and_data(T::default(), data)
329 }
330
331 pub fn with_capacity(capacity: usize) -> Self {
334 Self::from_coder_and_capacity(T::default(), capacity)
335 }
336}
337
338impl<T: SidecarCoder> SidecarBuilder<T> {
339 pub fn from_coder_and_capacity(coder: T, capacity: usize) -> Self {
342 Self { inner: PartialSidecar::with_capacity(capacity), coder }
343 }
344
345 pub const fn len(&self) -> usize {
350 self.inner.len()
351 }
352
353 pub const fn is_empty(&self) -> bool {
355 self.inner.is_empty()
356 }
357
358 pub fn from_coder_and_data(coder: T, data: &[u8]) -> Self {
360 let required_fe = coder.required_fe(data);
361 let mut this = Self::from_coder_and_capacity(
362 coder,
363 required_fe.div_ceil(FIELD_ELEMENTS_PER_BLOB as usize),
364 );
365 this.ingest(data);
366 this
367 }
368
369 pub fn ingest(&mut self, data: &[u8]) {
371 self.inner.alloc_fes(self.coder.required_fe(data));
372 self.coder.code(&mut self.inner, data);
373 }
374
375 #[cfg(feature = "kzg")]
377 pub fn build_with_settings(
378 self,
379 settings: &c_kzg::KzgSettings,
380 ) -> Result<BlobTransactionSidecar, c_kzg::Error> {
381 let mut commitments = Vec::with_capacity(self.inner.blobs.len());
382 let mut proofs = Vec::with_capacity(self.inner.blobs.len());
383 for blob in &self.inner.blobs {
384 let blob = unsafe { core::mem::transmute::<&Blob, &c_kzg::Blob>(blob) };
386 let commitment = KzgCommitment::blob_to_kzg_commitment(blob, settings)?;
387 let proof = KzgProof::compute_blob_kzg_proof(blob, &commitment.to_bytes(), settings)?;
388
389 unsafe {
391 commitments
392 .push(core::mem::transmute::<c_kzg::Bytes48, Bytes48>(commitment.to_bytes()));
393 proofs.push(core::mem::transmute::<c_kzg::Bytes48, Bytes48>(proof.to_bytes()));
394 }
395 }
396
397 Ok(BlobTransactionSidecar::new(self.inner.blobs, commitments, proofs))
398 }
399
400 #[cfg(feature = "kzg")]
403 pub fn build(self) -> Result<BlobTransactionSidecar, c_kzg::Error> {
404 self.build_with_settings(EnvKzgSettings::Default.get())
405 }
406
407 pub fn take(self) -> Vec<Blob> {
409 self.inner.blobs
410 }
411}
412
413impl<T: SidecarCoder> From<T> for SidecarBuilder<T> {
414 fn from(coder: T) -> Self {
421 Self::from_coder_and_capacity(coder, 1)
422 }
423}
424
425impl<T, R> FromIterator<R> for SidecarBuilder<T>
426where
427 T: SidecarCoder + Default,
428 R: AsRef<[u8]>,
429{
430 fn from_iter<I: IntoIterator<Item = R>>(iter: I) -> Self {
431 let mut this = Self::new();
432 for data in iter {
433 this.ingest(data.as_ref());
434 }
435 this
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442 use crate::eip4844::USABLE_BYTES_PER_BLOB;
443
444 #[test]
445 fn ingestion_strategy() {
446 let mut builder = PartialSidecar::new();
447 let data = &[
448 vec![1u8; 32],
449 vec![2u8; 372],
450 vec![3u8; 17],
451 vec![4u8; 5],
452 vec![5u8; 126_945],
453 vec![6u8; 2 * 126_945],
454 ];
455
456 data.iter().for_each(|data| SimpleCoder.code(&mut builder, data.as_slice()));
457
458 let decoded = SimpleCoder.decode_all(builder.blobs()).unwrap();
459 assert_eq!(decoded, data);
460 }
461
462 #[test]
463 fn big_ingestion_strategy() {
464 let data = vec![1u8; 126_945];
465 let builder = SidecarBuilder::<SimpleCoder>::from_slice(&data);
466
467 let blobs = builder.take();
468 let decoded = SimpleCoder.decode_all(&blobs).unwrap().concat();
469
470 assert_eq!(decoded, data);
471 }
472
473 #[test]
474 fn decode_all_rejects_invalid_data() {
475 assert_eq!(SimpleCoder.decode_all(&[]), None);
476 assert_eq!(SimpleCoder.decode_all(&[Blob::new([0xffu8; BYTES_PER_BLOB])]), None);
477 }
478
479 #[test]
480 fn it_ingests() {
481 let data = [
483 vec![1u8; 32],
484 vec![2u8; 372],
485 vec![3u8; 17],
486 vec![4u8; 5],
487 vec![5u8; USABLE_BYTES_PER_BLOB + 2],
488 ];
489
490 let mut builder = data.iter().collect::<SidecarBuilder<SimpleCoder>>();
491
492 let expected_fe = data.iter().map(|d| SimpleCoder.required_fe(d)).sum::<usize>();
493 assert_eq!(builder.len(), expected_fe * 32);
494
495 builder.ingest(b"hello");
497 assert_eq!(builder.len(), expected_fe * 32 + 64);
498 }
499}