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#[derive(Clone, Debug)]
18pub struct PartialSidecar {
19 blobs: Vec<Blob>,
21 fe: usize,
23}
24
25impl Default for PartialSidecar {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl PartialSidecar {
32 pub fn new() -> Self {
37 Self::with_capacity(2)
38 }
39
40 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 #[allow(clippy::missing_const_for_fn)]
50 pub fn blobs(&self) -> &[Blob] {
51 &self.blobs
52 }
53
54 fn free_fe(&self) -> usize {
56 self.blobs.len() * FIELD_ELEMENTS_PER_BLOB as usize - self.fe
57 }
58
59 pub const fn len(&self) -> usize {
64 self.fe * 32
65 }
66
67 pub const fn is_empty(&self) -> bool {
69 self.fe == 0
70 }
71
72 fn push_empty_blob(&mut self) {
74 self.blobs.push(Blob::new([0u8; BYTES_PER_BLOB]));
75 }
76
77 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 const fn fe_in_current_blob(&self) -> usize {
86 self.fe % FIELD_ELEMENTS_PER_BLOB as usize
87 }
88
89 const fn first_unused_fe_index_in_current_blob(&self) -> usize {
91 self.fe_in_current_blob()
92 }
93
94 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 fn fe_at_mut(&mut self, index: usize) -> &mut [u8] {
103 &mut self.current_blob_mut()[index * 32..(index + 1) * 32]
104 }
105
106 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 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 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
132pub trait SidecarCoder {
146 fn required_fe(&self, data: &[u8]) -> usize;
149
150 fn code(&mut self, builder: &mut PartialSidecar, data: &[u8]);
152
153 fn finish(self, builder: &mut PartialSidecar);
157
158 fn decode_all(&mut self, blobs: &[Blob]) -> Option<Vec<Vec<u8>>>;
160}
161
162#[derive(Clone, Copy, Debug, Default)]
187#[non_exhaustive]
188pub struct SimpleCoder;
189
190impl SimpleCoder {
191 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 num_bytes == 0 {
204 return Ok(None);
205 }
206
207 const MAX_ALLOCATION_SIZE: usize = 2_097_152; 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 builder.ingest_partial_fe(&(data.len() as u64).to_be_bytes());
236
237 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 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#[derive(Clone, Debug)]
285pub struct SidecarBuilder<T = SimpleCoder> {
286 inner: PartialSidecar,
288 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 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 pub fn new() -> Self {
319 T::default().into()
320 }
321
322 pub fn from_slice(data: &[u8]) -> Self {
325 Self::from_coder_and_data(T::default(), data)
326 }
327
328 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 pub fn from_coder_and_capacity(coder: T, capacity: usize) -> Self {
339 Self { inner: PartialSidecar::with_capacity(capacity), coder }
340 }
341
342 pub const fn len(&self) -> usize {
347 self.inner.len()
348 }
349
350 pub const fn is_empty(&self) -> bool {
352 self.inner.is_empty()
353 }
354
355 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 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 #[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 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 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 #[cfg(feature = "kzg")]
400 pub fn build(self) -> Result<BlobTransactionSidecar, c_kzg::Error> {
401 self.build_with_settings(EnvKzgSettings::Default.get())
402 }
403
404 pub fn take(self) -> Vec<Blob> {
406 self.inner.blobs
407 }
408
409 #[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 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 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 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 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 builder.ingest(b"hello");
523 assert_eq!(builder.len(), expected_fe * 32 + 64);
524 }
525}