diff --git a/beacon_node/http_api/src/block_id.rs b/beacon_node/http_api/src/block_id.rs index 5c785fe651..fd45e14faa 100644 --- a/beacon_node/http_api/src/block_id.rs +++ b/beacon_node/http_api/src/block_id.rs @@ -4,7 +4,7 @@ use eth2::types::BlockId as CoreBlockId; use std::fmt; use std::str::FromStr; use std::sync::Arc; -use types::{Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot}; +use types::{BlobsSidecar, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot}; /// Wraps `eth2::types::BlockId` and provides a simple way to obtain a block or root for a given /// `BlockId`. @@ -211,6 +211,22 @@ impl BlockId { } } } + + /// Return the `BlobsSidecar` identified by `self`. + pub async fn blobs_sidecar( + &self, + chain: &BeaconChain, + ) -> Result<(Arc>), warp::Rejection> { + let root = self.root(chain)?.0; + match chain.store.get_blobs(&root) { + Ok(Some(blob)) => Ok((Arc::new(blob))), + Ok(None) => Err(warp_utils::reject::custom_not_found(format!( + "Blob with block root {} is not in the store", + root + ))), + Err(e) => Err(warp_utils::reject::beacon_chain_error(e.into())), + } + } } impl FromStr for BlockId { diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index e5336ef6ad..e8ec77f87b 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3324,6 +3324,41 @@ pub fn serve( ))) }); + // GET lighthouse/beacon/blobs_sidecars/{block_id} + let get_lighthouse_blobs_sidecars = warp::path("lighthouse") + .and(warp::path("beacon")) + .and(warp::path("blobs_sidecars")) + .and(block_id_or_err) + .and(warp::path::end()) + .and(chain_filter.clone()) + .and(warp::header::optional::("accept")) + .and_then( + |block_id: BlockId, + chain: Arc>, + accept_header: Option| { + async move { + let blobs_sidecar = block_id.blobs_sidecar(&chain).await?; + + match accept_header { + Some(api_types::Accept::Ssz) => Response::builder() + .status(200) + .header("Content-Type", "application/octet-stream") + .body(blobs_sidecar.as_ssz_bytes().into()) + .map_err(|e| { + warp_utils::reject::custom_server_error(format!( + "failed to create response: {}", + e + )) + }), + _ => Ok(warp::reply::json(&api_types::GenericResponse::from( + blobs_sidecar, + )) + .into_response()), + } + } + }, + ); + let get_events = eth_v1 .and(warp::path("events")) .and(warp::path::end()) @@ -3459,6 +3494,7 @@ pub fn serve( .or(get_lighthouse_attestation_performance.boxed()) .or(get_lighthouse_block_packing_efficiency.boxed()) .or(get_lighthouse_merge_readiness.boxed()) + .or(get_lighthouse_blobs_sidecars.boxed()) .or(get_events.boxed()), ) .boxed() diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index fcfff7284a..f08c4eb30e 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -680,6 +680,19 @@ impl BeaconNodeHttpClient { Ok(path) } + /// Path for `lighthouse/beacon/blobs_sidecars/{block_id}` + pub fn get_blobs_sidecar_path(&self, block_id: BlockId) -> Result { + let mut path = self.server.full.clone(); + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("lighthouse") + .push("beacon") + .push("blobs_sidecars") + .push(&block_id.to_string()); + Ok(path) + } + /// Path for `v1/beacon/blinded_blocks/{block_id}` pub fn get_beacon_blinded_blocks_path(&self, block_id: BlockId) -> Result { let mut path = self.eth_path(V1)?; @@ -735,6 +748,23 @@ impl BeaconNodeHttpClient { })) } + /// `GET lighthouse/beacon/blobs_sidecars/{block_id}` + /// + /// Returns `Ok(None)` on a 404 error. + pub async fn get_blobs_sidecar( + &self, + block_id: BlockId, + ) -> Result>>, Error> { + let path = self.get_blobs_sidecar_path(block_id)?; + let response = match self.get_response(path, |b| b).await.optional()? { + Some(res) => res, + None => return Ok(None), + }; + + let GenericResponse { data } = response.json().await?; + Ok(Some(GenericResponse { data })) + } + /// `GET v1/beacon/blinded_blocks/{block_id}` /// /// Returns `Ok(None)` on a 404 error. diff --git a/lcli/src/parse_ssz.rs b/lcli/src/parse_ssz.rs index fff6f7de58..0e87e330b1 100644 --- a/lcli/src/parse_ssz.rs +++ b/lcli/src/parse_ssz.rs @@ -63,6 +63,7 @@ pub fn run_parse_ssz(matches: &ArgMatches) -> Result<(), String> { "state_merge" => decode_and_print::>(&bytes, format)?, "state_capella" => decode_and_print::>(&bytes, format)?, "state_eip4844" => decode_and_print::>(&bytes, format)?, + "blobs_sidecar" => decode_and_print::>(&bytes, format)?, other => return Err(format!("Unknown type: {}", other)), };