Skip to content

Commit a335575

Browse files
authored
chore(anvil): deprecate getBlobSidecars Beacon API endpoint (#12568)
- Removed useless `BeaconResponse`, rely instead on `alloy_rpc_types_beacon` crate response structs - Move `BeaconError` in `anvil/src/server` mod, to mirror perfectly the Beacon/JSON-RPC handler/error submodules structure - updated the test accordingly
1 parent 4f6c0f6 commit a335575

File tree

7 files changed

+22
-276
lines changed

7 files changed

+22
-276
lines changed

crates/anvil/src/eth/beacon/mod.rs

Lines changed: 0 additions & 10 deletions
This file was deleted.

crates/anvil/src/eth/beacon/response.rs

Lines changed: 0 additions & 71 deletions
This file was deleted.

crates/anvil/src/eth/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
pub mod api;
2-
pub mod beacon;
32
pub mod otterscan;
43
pub mod sign;
54
pub use api::EthApi;

crates/anvil/src/eth/beacon/error.rs renamed to crates/anvil/src/server/beacon_error.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ impl BeaconError {
4545
Self::new(BeaconErrorCode::InternalError, "Internal server error")
4646
}
4747

48-
/// Helper function to create a 500 Internal Server Error with the given details
49-
pub fn internal_error_with_details(error: impl Display) -> Self {
50-
Self::new(BeaconErrorCode::InternalError, format!("Internal server error: {error}"))
48+
/// Helper function to create a 410 Gone error for deprecated endpoints
49+
pub fn deprecated_endpoint_with_hint(hint: impl Display) -> Self {
50+
Self::new(BeaconErrorCode::Gone, format!("This endpoint is deprecated. {hint}"))
5151
}
5252

5353
/// Converts to an Axum response
@@ -86,6 +86,7 @@ impl IntoResponse for BeaconError {
8686
pub enum BeaconErrorCode {
8787
BadRequest = 400,
8888
NotFound = 404,
89+
Gone = 410,
8990
InternalError = 500,
9091
}
9192

@@ -100,6 +101,7 @@ impl BeaconErrorCode {
100101
match self {
101102
Self::BadRequest => "Bad Request",
102103
Self::NotFound => "Not Found",
104+
Self::Gone => "Gone",
103105
Self::InternalError => "Internal Server Error",
104106
}
105107
}

crates/anvil/src/server/beacon_handler.rs

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
use crate::eth::{
2-
EthApi,
3-
beacon::{BeaconError, BeaconResponse},
4-
};
1+
use super::beacon_error::BeaconError;
2+
use crate::eth::EthApi;
53
use alloy_eips::BlockId;
64
use alloy_primitives::{B256, aliases::B32};
75
use alloy_rpc_types_beacon::{
86
genesis::{GenesisData, GenesisResponse},
9-
header::Header,
10-
sidecar::{BlobData, GetBlobsResponse},
7+
sidecar::GetBlobsResponse,
118
};
129
use axum::{
1310
Json,
@@ -19,46 +16,16 @@ use std::{collections::HashMap, str::FromStr as _};
1916

2017
/// Handles incoming Beacon API requests for blob sidecars
2118
///
19+
/// This endpoint is deprecated. Use `GET /eth/v1/beacon/blobs/{block_id}` instead.
20+
///
2221
/// GET /eth/v1/beacon/blob_sidecars/{block_id}
2322
pub async fn handle_get_blob_sidecars(
24-
State(api): State<EthApi>,
25-
Path(block_id): Path<String>,
26-
Query(params): Query<HashMap<String, String>>,
23+
State(_api): State<EthApi>,
24+
Path(_block_id): Path<String>,
25+
Query(_params): Query<HashMap<String, String>>,
2726
) -> Response {
28-
// Parse block_id from path parameter
29-
let Ok(block_id) = BlockId::from_str(&block_id) else {
30-
return BeaconError::invalid_block_id(block_id).into_response();
31-
};
32-
33-
// Parse indices from query parameters
34-
// Supports both comma-separated (?indices=1,2,3) and repeated parameters (?indices=1&indices=2)
35-
let indices: Vec<u64> = params
36-
.get("indices")
37-
.map(|s| s.split(',').filter_map(|idx| idx.trim().parse::<u64>().ok()).collect())
38-
.unwrap_or_default();
39-
40-
// Get the blob sidecars using existing EthApi logic
41-
match api.anvil_get_blob_sidecars_by_block_id(block_id) {
42-
Ok(Some(sidecar)) => BeaconResponse::with_flags(
43-
sidecar
44-
.into_iter()
45-
.filter(|blob_item| indices.is_empty() || indices.contains(&blob_item.index))
46-
.map(|blob_item| BlobData {
47-
index: blob_item.index,
48-
blob: blob_item.blob,
49-
kzg_commitment: blob_item.kzg_commitment,
50-
kzg_proof: blob_item.kzg_proof,
51-
signed_block_header: Header::default(), // Not available in Anvil
52-
kzg_commitment_inclusion_proof: vec![], // Not available in Anvil
53-
})
54-
.collect::<Vec<_>>(),
55-
false, // Not available in Anvil
56-
false, // Not available in Anvil
57-
)
58-
.into_response(),
59-
Ok(None) => BeaconError::block_not_found().into_response(),
60-
Err(_) => BeaconError::internal_error().into_response(),
61-
}
27+
BeaconError::deprecated_endpoint_with_hint("Use `GET /eth/v1/beacon/blobs/{block_id}` instead.")
28+
.into_response()
6229
}
6330

6431
/// Handles incoming Beacon API requests for blobs

crates/anvil/src/server/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use handler::{HttpEthRpcHandler, PubSubEthRpcHandler};
88
use std::{io, net::SocketAddr, pin::pin};
99
use tokio::net::TcpListener;
1010

11+
mod beacon_error;
1112
mod beacon_handler;
1213
pub mod error;
1314
mod handler;

crates/anvil/tests/it/beacon_api.rs

Lines changed: 6 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -12,161 +12,19 @@ use anvil::{NodeConfig, spawn};
1212
#[tokio::test(flavor = "multi_thread")]
1313
async fn test_beacon_api_get_blob_sidecars() {
1414
let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into()));
15-
let (api, handle) = spawn(node_config).await;
16-
17-
// Disable auto-mining so we can include multiple transactions in the same block
18-
api.anvil_set_auto_mine(false).await.unwrap();
19-
20-
let wallets = handle.dev_wallets().collect::<Vec<_>>();
21-
let from = wallets[0].address();
22-
let to = wallets[1].address();
23-
24-
let provider = http_provider(&handle.http_endpoint());
25-
26-
let eip1559_est = provider.estimate_eip1559_fees().await.unwrap();
27-
let gas_price = provider.get_gas_price().await.unwrap();
28-
29-
// Create multiple blob transactions to be included in the same block
30-
let blob_data =
31-
[b"Hello Beacon API - Blob 1", b"Hello Beacon API - Blob 2", b"Hello Beacon API - Blob 3"];
32-
33-
let mut pending_txs = Vec::new();
34-
35-
// Send all transactions without waiting for receipts
36-
for (i, data) in blob_data.iter().enumerate() {
37-
let sidecar: SidecarBuilder<SimpleCoder> = SidecarBuilder::from_slice(data.as_slice());
38-
let sidecar = sidecar.build().unwrap();
39-
40-
let tx = TransactionRequest::default()
41-
.with_from(from)
42-
.with_to(to)
43-
.with_nonce(i as u64)
44-
.with_max_fee_per_blob_gas(gas_price + 1)
45-
.with_max_fee_per_gas(eip1559_est.max_fee_per_gas)
46-
.with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas)
47-
.with_blob_sidecar(sidecar)
48-
.value(U256::from(100));
49-
50-
let mut tx = WithOtherFields::new(tx);
51-
tx.populate_blob_hashes();
52-
53-
let pending = provider.send_transaction(tx).await.unwrap();
54-
pending_txs.push(pending);
55-
}
56-
57-
// Mine a block to include all transactions
58-
api.evm_mine(None).await.unwrap();
59-
60-
// Get receipts for all transactions
61-
let mut receipts = Vec::new();
62-
for pending in pending_txs {
63-
let receipt = pending.get_receipt().await.unwrap();
64-
receipts.push(receipt);
65-
}
66-
67-
// Verify all transactions were included in the same block
68-
let block_number = receipts[0].block_number.unwrap();
69-
for (i, receipt) in receipts.iter().enumerate() {
70-
assert_eq!(
71-
receipt.block_number.unwrap(),
72-
block_number,
73-
"Transaction {i} was not included in block {block_number}"
74-
);
75-
}
15+
let (_api, handle) = spawn(node_config).await;
7616

7717
// Test Beacon API endpoint using HTTP client
7818
let client = reqwest::Client::new();
79-
let url = format!("{}/eth/v1/beacon/blob_sidecars/{}", handle.http_endpoint(), block_number);
19+
let url = format!("{}/eth/v1/beacon/blob_sidecars/latest", handle.http_endpoint());
8020

21+
// This endpoint is deprecated, so we expect a 410 Gone response
8122
let response = client.get(&url).send().await.unwrap();
82-
assert_eq!(response.status(), reqwest::StatusCode::OK);
83-
84-
let body: serde_json::Value = response.json().await.unwrap();
85-
86-
// Verify response structure
87-
assert!(body["data"].is_array());
88-
assert!(body["execution_optimistic"].is_boolean());
89-
assert!(body["finalized"].is_boolean());
90-
91-
// Verify we have blob data from all transactions
92-
let blobs = body["data"].as_array().unwrap();
93-
assert_eq!(blobs.len(), 3, "Expected 3 blob sidecars from 3 transactions");
94-
95-
// Verify blob structure for each blob
96-
for (i, blob) in blobs.iter().enumerate() {
97-
assert!(blob["index"].is_string(), "Blob {i} missing index");
98-
assert!(blob["blob"].is_string(), "Blob {i} missing blob data");
99-
assert!(blob["kzg_commitment"].is_string(), "Blob {i} missing kzg_commitment");
100-
assert!(blob["kzg_proof"].is_string(), "Blob {i} missing kzg_proof");
101-
}
102-
103-
// Test filtering with indices query parameter - single index
104-
let url = format!(
105-
"{}/eth/v1/beacon/blob_sidecars/{}?indices=1",
106-
handle.http_endpoint(),
107-
block_number
108-
);
109-
let response = client.get(&url).send().await.unwrap();
110-
let status = response.status();
111-
if status != reqwest::StatusCode::OK {
112-
let error_body = response.text().await.unwrap();
113-
panic!("Expected OK status, got {status}: {error_body}");
114-
}
115-
let body: serde_json::Value = response.json().await.unwrap();
116-
let filtered_blobs = body["data"].as_array().unwrap();
117-
assert_eq!(filtered_blobs.len(), 1, "Expected 1 blob sidecar when filtering by indices=1");
118-
assert_eq!(filtered_blobs[0]["index"].as_str().unwrap(), "1");
119-
120-
// Test filtering with indices query parameter - multiple indices (comma-separated)
121-
let url = format!(
122-
"{}/eth/v1/beacon/blob_sidecars/{}?indices=0,2",
123-
handle.http_endpoint(),
124-
block_number
125-
);
126-
let response = client.get(&url).send().await.unwrap();
127-
assert_eq!(response.status(), reqwest::StatusCode::OK);
128-
let body: serde_json::Value = response.json().await.unwrap();
129-
let filtered_blobs = body["data"].as_array().unwrap();
130-
assert_eq!(filtered_blobs.len(), 2, "Expected 2 blob sidecars when filtering by indices=0,2");
131-
let indices: Vec<String> =
132-
filtered_blobs.iter().map(|b| b["index"].as_str().unwrap().to_string()).collect();
133-
assert!(indices.contains(&"0".to_string()), "Expected index 0 in results");
134-
assert!(indices.contains(&"2".to_string()), "Expected index 2 in results");
135-
136-
// Test filtering with non-existent index
137-
let url = format!(
138-
"{}/eth/v1/beacon/blob_sidecars/{}?indices=99",
139-
handle.http_endpoint(),
140-
block_number
141-
);
142-
let response = client.get(&url).send().await.unwrap();
143-
assert_eq!(response.status(), reqwest::StatusCode::OK);
144-
let body: serde_json::Value = response.json().await.unwrap();
145-
let filtered_blobs = body["data"].as_array().unwrap();
14623
assert_eq!(
147-
filtered_blobs.len(),
148-
0,
149-
"Expected 0 blob sidecars when filtering by non-existent index"
24+
response.text().await.unwrap(),
25+
r#"{"code":410,"message":"This endpoint is deprecated. Use `GET /eth/v1/beacon/blobs/{block_id}` instead."}"#,
26+
"Expected deprecation message for blob_sidecars endpoint"
15027
);
151-
152-
// Test with special block identifiers
153-
let test_ids = vec!["latest", "finalized", "safe", "earliest"];
154-
for block_id in test_ids {
155-
let url = format!("{}/eth/v1/beacon/blob_sidecars/{}", handle.http_endpoint(), block_id);
156-
assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::OK);
157-
}
158-
let url = format!("{}/eth/v1/beacon/blob_sidecars/pending", handle.http_endpoint());
159-
assert_eq!(client.get(&url).send().await.unwrap().status(), reqwest::StatusCode::NOT_FOUND);
160-
161-
// Test with hex block number
162-
let url = format!("{}/eth/v1/beacon/blob_sidecars/0x{block_number:x}", handle.http_endpoint());
163-
let response = client.get(&url).send().await.unwrap();
164-
assert_eq!(response.status(), reqwest::StatusCode::OK);
165-
166-
// Test with non-existent block
167-
let url = format!("{}/eth/v1/beacon/blob_sidecars/999999", handle.http_endpoint());
168-
let response = client.get(&url).send().await.unwrap();
169-
assert_eq!(response.status(), reqwest::StatusCode::NOT_FOUND);
17028
}
17129

17230
#[tokio::test(flavor = "multi_thread")]

0 commit comments

Comments
 (0)