diff --git a/crates/blobber/src/blobs/fetch.rs b/crates/blobber/src/blobs/fetch.rs index d6e20c9..d2540ff 100644 --- a/crates/blobber/src/blobs/fetch.rs +++ b/crates/blobber/src/blobs/fetch.rs @@ -1,10 +1,10 @@ -use crate::{BlobFetcherBuilder, FetchError, FetchResult, utils::extract_blobs_from_bundle}; +use crate::{BlobFetcherBuilder, FetchError, FetchResult}; use alloy::{ consensus::{Blob, BlobTransactionSidecar}, eips::eip7594::{BlobTransactionSidecarEip7594, BlobTransactionSidecarVariant}, primitives::{B256, TxHash}, }; -use reth::{rpc::types::beacon::sidecar::BeaconBlobBundle, transaction_pool::TransactionPool}; +use reth::{rpc::types::beacon::sidecar::GetBlobsResponse, transaction_pool::TransactionPool}; use std::{ops::Deref, sync::Arc}; use tokio::select; use tracing::instrument; @@ -218,15 +218,24 @@ where return Err(FetchError::ConsensusClientUrlNotSet); }; - let url = url - .join(&format!("/eth/v1/beacon/blob_sidecars/{slot}")) - .map_err(FetchError::UrlParse)?; + let mut url = + url.join(&format!("/eth/v1/beacon/blobs/{slot}")).map_err(FetchError::UrlParse)?; + + url.query_pairs_mut() + .extend_pairs(versioned_hashes.iter().map(|hash| ("versioned_hash", hash.to_string()))); let response = self.client.get(url).header("accept", "application/json").send().await?; - let response: BeaconBlobBundle = response.json().await?; + let response: GetBlobsResponse = response.json().await?; + + debug_assert!( + response.data.len() == versioned_hashes.len(), + "Expected {} blobs, got {}", + versioned_hashes.len(), + response.data.len() + ); - extract_blobs_from_bundle(response, versioned_hashes) + Ok(Arc::new(response.data).into()) } } diff --git a/crates/blobber/src/coder/trait.rs b/crates/blobber/src/coder/trait.rs index 3a303f2..51652bc 100644 --- a/crates/blobber/src/coder/trait.rs +++ b/crates/blobber/src/coder/trait.rs @@ -51,7 +51,7 @@ where #[cfg(test)] mod test { use super::*; - use crate::{Blobs, utils::tests::PYLON_BLOB_RESPONSE}; + use crate::{Blobs, test::PYLON_BLOB_RESPONSE}; use alloy::{ consensus::{Blob, BlobTransactionSidecar, Bytes48, SimpleCoder}, primitives::{Address, B256, U256, b256}, diff --git a/crates/blobber/src/lib.rs b/crates/blobber/src/lib.rs index 4d4b7bd..ef6dcca 100644 --- a/crates/blobber/src/lib.rs +++ b/crates/blobber/src/lib.rs @@ -26,11 +26,12 @@ pub use error::{BlobberError, BlobberResult}; mod shim; pub use shim::ExtractableChainShim; -pub(crate) mod utils; - #[cfg(test)] mod test { - use crate::utils::tests::BLOBSCAN_BLOB_RESPONSE; + pub(crate) const BLOBSCAN_BLOB_RESPONSE: &str = + include_str!("../../../tests/artifacts/blob.json"); + pub(crate) const PYLON_BLOB_RESPONSE: &str = + include_str!("../../../tests/artifacts/pylon_blob.json"); use foundry_blob_explorers::TransactionDetails; // Sanity check on dependency compatibility. diff --git a/crates/blobber/src/utils.rs b/crates/blobber/src/utils.rs deleted file mode 100644 index 71d27be..0000000 --- a/crates/blobber/src/utils.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::{Blobs, FetchResult}; -use alloy::{ - eips::{eip4844::kzg_to_versioned_hash, eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA}, - primitives::B256, -}; -use reth::rpc::types::beacon::sidecar::BeaconBlobBundle; -use smallvec::SmallVec; - -/// Extracts the blobs from the [`BeaconBlobBundle`], and returns the blobs that match the versioned hashes in the transaction. -/// This also dedups any duplicate blobs if a builder lands the same blob multiple times in a block. -pub(crate) fn extract_blobs_from_bundle( - bundle: BeaconBlobBundle, - versioned_hashes: &[B256], -) -> FetchResult { - let mut blobs = vec![]; - // NB: There can be, at most, 9 blobs per block from Pectra forwards. We'll never need more space than this, unless blob capacity is increased again or made dynamic. - let mut seen_versioned_hashes: SmallVec<[B256; MAX_BLOBS_PER_BLOCK_ELECTRA as usize]> = - SmallVec::new(); - - for item in bundle.data.iter() { - let versioned_hash = kzg_to_versioned_hash(item.kzg_commitment.as_ref()); - - if versioned_hashes.contains(&versioned_hash) - && !seen_versioned_hashes.contains(&versioned_hash) - { - blobs.push(*item.blob); - seen_versioned_hashes.push(versioned_hash); - } - } - - Ok(blobs.into()) -} - -#[cfg(test)] -pub(crate) mod tests { - - use super::*; - use alloy::{ - consensus::{BlobTransactionSidecar, SidecarCoder, SimpleCoder, Transaction, TxEnvelope}, - eips::eip7594::BlobTransactionSidecarVariant, - }; - use signet_types::primitives::TransactionSigned; - use std::sync::Arc; - - pub(crate) const BLOBSCAN_BLOB_RESPONSE: &str = - include_str!("../../../tests/artifacts/blob.json"); - /// Blob from Slot 2277733, corresponding to block 277722 on Pecorino host. - pub(crate) const CL_BLOB_RESPONSE: &str = include_str!("../../../tests/artifacts/cl_blob.json"); - /// EIP4844 blob tx with hash 0x73d1c682fae85c761528a0a7ec22fac613b25ede87b80f0ac052107f3444324f, - /// corresponding to blob sent to block 277722 on Pecorino host. - pub(crate) const CL_BLOB_TX: &str = include_str!("../../../tests/artifacts/cl_blob_tx.json"); - /// Blob sidecar from Pylon, corresponding to block 277722 on Pecorino host. - pub(crate) const PYLON_BLOB_RESPONSE: &str = - include_str!("../../../tests/artifacts/pylon_blob.json"); - - #[test] - fn test_process_blob_extraction() { - let bundle: BeaconBlobBundle = serde_json::from_str(CL_BLOB_RESPONSE).unwrap(); - let tx: TxEnvelope = serde_json::from_str::(CL_BLOB_TX).unwrap(); - let tx: TransactionSigned = tx.into(); - - let versioned_hashes = tx.blob_versioned_hashes().unwrap().to_owned(); - - // Extract the blobs from the CL beacon blob bundle. - let cl_blobs = extract_blobs_from_bundle(bundle, &versioned_hashes).unwrap(); - assert_eq!(cl_blobs.len(), 1); - - // Now, process the pylon blobs which come in a [`BlobTransactionSidecar`]. - // NB: this should be changes to `BlobTransactionSidecarVariant` in the - // future. After https://github.com/alloy-rs/alloy/pull/2713 - // The json is definitely a `BlobTransactionSidecar`, so we can - // deserialize it directly and it doesn't really matter much. - let sidecar: BlobTransactionSidecar = - serde_json::from_str::(PYLON_BLOB_RESPONSE).unwrap(); - let pylon_blobs: Blobs = Arc::::new(sidecar.into()).into(); - - // Make sure that both blob sources have the same blobs after being processed. - assert_eq!(cl_blobs.len(), pylon_blobs.len()); - assert_eq!(cl_blobs.as_slice(), pylon_blobs.as_slice()); - - // Make sure both can be decoded - let cl_decoded = SimpleCoder::default().decode_all(cl_blobs.as_ref()).unwrap(); - let pylon_decoded = SimpleCoder::default().decode_all(pylon_blobs.as_ref()).unwrap(); - assert_eq!(cl_decoded.len(), pylon_decoded.len()); - assert_eq!(cl_decoded, pylon_decoded); - } -}