Skip to main content

alloy_rpc_types_beacon/
sidecar.rs

1use crate::header::Header;
2use alloy_eips::eip4844::{
3    deserialize_blob, deserialize_blobs, Blob, BlobTransactionSidecar, Bytes48,
4};
5use alloy_primitives::B256;
6use serde::{Deserialize, Serialize};
7use serde_with::{serde_as, DisplayFromStr};
8use std::vec::IntoIter;
9
10/// Bundle of blobs for a given block.
11///
12/// See [`/eth/v1/beacon/blob_sidecars/{block_id}`](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlobSidecars).
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, derive_more::IntoIterator)]
14pub struct BeaconBlobBundle {
15    /// Vec of individual blob data
16    pub data: Vec<BlobData>,
17}
18
19impl BeaconBlobBundle {
20    /// Creates a new [`BeaconBlobBundle`] from a given vector of [`BlobData`].
21    pub const fn new(data: Vec<BlobData>) -> Self {
22        Self { data }
23    }
24
25    /// Returns the number of blobs in the bundle.
26    pub const fn len(&self) -> usize {
27        self.data.len()
28    }
29
30    /// Returns if the bundle is empty.
31    pub const fn is_empty(&self) -> bool {
32        self.data.is_empty()
33    }
34
35    /// Returns the blob with the given index.
36    pub fn get_blob(&self, index: u64) -> Option<&BlobData> {
37        self.data.iter().find(|blob| blob.index == index)
38    }
39}
40
41/// Response from `eth/v1/beacon/blobs`.
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, derive_more::IntoIterator)]
43pub struct GetBlobsResponse {
44    /// True if the response references an unverified execution payload. Optimistic information may
45    /// be invalidated at a later time. If the field is not present, assume the False value.
46    #[serde(default)]
47    pub execution_optimistic: bool,
48    /// True if the response references the finalized history of the chain, as determined by fork
49    /// choice. If the field is not present, additional calls are necessary to compare the epoch of
50    /// the requested information with the finalized checkpoint.
51    #[serde(default)]
52    pub finalized: bool,
53    /// Vec of individual blobs
54    #[serde(deserialize_with = "deserialize_blobs")]
55    #[into_iterator]
56    pub data: Vec<Blob>,
57}
58
59/// Intermediate type for BlobTransactionSidecar matching
60#[derive(Debug, Clone)]
61pub struct SidecarIterator {
62    /// The internal iterator over [`BlobData`].
63    pub iter: IntoIter<BlobData>,
64}
65
66impl Iterator for SidecarIterator {
67    type Item = BlobData;
68
69    fn next(&mut self) -> Option<Self::Item> {
70        self.iter.next()
71    }
72}
73
74impl SidecarIterator {
75    /// Creates a new [`SidecarIterator`] from a given [`BeaconBlobBundle`].
76    pub fn new(bundle: BeaconBlobBundle) -> Self {
77        Self { iter: bundle.into_iter() }
78    }
79
80    /// Returns a BlobTransactionSidecar of len num_hashes.
81    pub fn next_sidecar(&mut self, num_hashes: usize) -> Option<BlobTransactionSidecar> {
82        let mut blobs = Vec::with_capacity(num_hashes);
83        let mut commitments = Vec::with_capacity(num_hashes);
84        let mut proofs = Vec::with_capacity(num_hashes);
85        for _ in 0..num_hashes {
86            let next = self.next()?;
87            blobs.push(*next.blob);
88            commitments.push(next.kzg_commitment);
89            proofs.push(next.kzg_proof);
90        }
91        Some(BlobTransactionSidecar { blobs, commitments, proofs })
92    }
93}
94
95/// Individual Blob data that belongs to a 4844 transaction.
96#[serde_as]
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98pub struct BlobData {
99    /// Blob index
100    #[serde_as(as = "DisplayFromStr")]
101    pub index: u64,
102    #[serde(deserialize_with = "deserialize_blob")]
103    /// Blob data
104    pub blob: Box<Blob>,
105    /// The blob's commitment
106    pub kzg_commitment: Bytes48,
107    /// The blob's proof
108    pub kzg_proof: Bytes48,
109    /// The block header containing the blob
110    pub signed_block_header: Header,
111    /// The blob's inclusion proofs
112    pub kzg_commitment_inclusion_proof: Vec<B256>,
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use similar_asserts::assert_eq;
119
120    /// Should deserialize json containing 6 blobs
121    #[test]
122    fn serde_sidecar_bundle() {
123        let s = include_str!("examples/sidecar.json");
124        let resp: BeaconBlobBundle = serde_json::from_str(s).unwrap();
125        let json: serde_json::Value = serde_json::from_str(s).unwrap();
126        assert_eq!(json, serde_json::to_value(resp.clone()).unwrap());
127        assert_eq!(6, resp.data.len());
128    }
129}