Implement /eth/v1/beacon/blobs endpoint (#8103)

* #8085


  


Co-Authored-By: Tan Chee Keong <tanck@sigmaprime.io>

Co-Authored-By: chonghe <44791194+chong-he@users.noreply.github.com>
This commit is contained in:
chonghe
2025-10-09 13:01:30 +08:00
committed by GitHub
parent 8e382ceed9
commit 3110ca325b
10 changed files with 346 additions and 41 deletions

View File

@@ -1336,7 +1336,7 @@ impl BeaconNodeHttpClient {
}
/// Path for `v1/beacon/blob_sidecars/{block_id}`
pub fn get_blobs_path(&self, block_id: BlockId) -> Result<Url, Error> {
pub fn get_blob_sidecars_path(&self, block_id: BlockId) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1346,6 +1346,17 @@ impl BeaconNodeHttpClient {
Ok(path)
}
/// Path for `v1/beacon/blobs/{blob_id}`
pub fn get_blobs_path(&self, block_id: BlockId) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("blobs")
.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<Url, Error> {
let mut path = self.eth_path(V1)?;
@@ -1374,13 +1385,13 @@ impl BeaconNodeHttpClient {
/// `GET v1/beacon/blob_sidecars/{block_id}`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_blobs<E: EthSpec>(
pub async fn get_blob_sidecars<E: EthSpec>(
&self,
block_id: BlockId,
indices: Option<&[u64]>,
spec: &ChainSpec,
) -> Result<Option<ExecutionOptimisticFinalizedBeaconResponse<BlobSidecarList<E>>>, Error> {
let mut path = self.get_blobs_path(block_id)?;
let mut path = self.get_blob_sidecars_path(block_id)?;
if let Some(indices) = indices {
let indices_string = indices
.iter()
@@ -1400,6 +1411,31 @@ impl BeaconNodeHttpClient {
.map(|opt| opt.map(BeaconResponse::ForkVersioned))
}
/// `GET v1/beacon/blobs/{block_id}`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_blobs<E: EthSpec>(
&self,
block_id: BlockId,
versioned_hashes: Option<&[Hash256]>,
) -> Result<Option<ExecutionOptimisticFinalizedBeaconResponse<Vec<BlobWrapper<E>>>>, Error>
{
let mut path = self.get_blobs_path(block_id)?;
if let Some(hashes) = versioned_hashes {
let hashes_string = hashes
.iter()
.map(|hash| hash.to_string())
.collect::<Vec<_>>()
.join(",");
path.query_pairs_mut()
.append_pair("versioned_hashes", &hashes_string);
}
self.get_opt(path)
.await
.map(|opt| opt.map(BeaconResponse::Unversioned))
}
/// `GET v1/beacon/blinded_blocks/{block_id}`
///
/// Returns `Ok(None)` on a 404 error.

View File

@@ -716,6 +716,13 @@ pub struct BlobIndicesQuery {
pub indices: Option<Vec<u64>>,
}
#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BlobsVersionedHashesQuery {
#[serde(default, deserialize_with = "option_query_vec")]
pub versioned_hashes: Option<Vec<Hash256>>,
}
#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataColumnIndicesQuery {
@@ -2317,6 +2324,14 @@ pub struct StandardAttestationRewards {
pub total_rewards: Vec<TotalAttestationRewards>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
#[serde(bound = "E: EthSpec")]
#[serde(transparent)]
pub struct BlobWrapper<E: EthSpec> {
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
pub blob: Blob<E>,
}
#[cfg(test)]
mod test {
use std::fmt::Debug;