(&body_bytes)
- .map_err(|e| {
- error!(error = %e, "Failed to parse signed blinded block");
- e
- })?;
-
- // If we have a locally built payload, it means we signed a local header.
- // Return it and clear the cache.
- if let Some(local_payload) = server.local_payload.lock().take() {
- check_locally_built_payload_integrity(&signed_blinded_block, &local_payload)?;
-
- debug!("Valid local block found, returning: {local_payload:?}");
- return Ok(Json(local_payload));
- }
-
- // TODO: how do we deal with failures here? What if we submit the signed blinded block but
- // don't get a response? should we ignore the error or proceed with a local block
- // (highly risky -> equivocation risk)
- let payload = server
- .proxy_target
- .get_payload(signed_blinded_block)
- .await
- .map(Json)
- .map_err(|e| {
- error!(elapsed = ?start.elapsed(), error = %e, "Failed to get payload from mev-boost");
- e
- })?;
-
- debug!(elapsed = ?start.elapsed(), "Returning payload");
-
- Ok(payload)
- }
-}
-
-/// Configuration for the builder proxy.
-#[derive(Debug, Clone)]
-pub struct BuilderProxyConfig {
- /// The URL of the target mev-boost server.
- pub mevboost_url: Url,
- /// The port on which the builder proxy should listen.
- pub server_port: u16,
-}
-
-/// Start the builder proxy with the given payload fetcher and configuration.
-pub async fn start_builder_proxy_server(
- payload_fetcher: P,
- config: BuilderProxyConfig,
-) -> eyre::Result<()>
-where
- P: PayloadFetcher + Send + Sync + 'static,
-{
- info!(
- port = config.server_port,
- target = config.mevboost_url.to_string(),
- "Starting builder proxy..."
- );
-
- let mev_boost = MevBoostClient::new(config.mevboost_url);
- let server = Arc::new(BuilderProxyServer::new(mev_boost, payload_fetcher));
-
- let router = Router::new()
- .route("/", get(index))
- .route(STATUS_PATH, get(BuilderProxyServer::status))
- .route(REGISTER_VALIDATORS_PATH, post(BuilderProxyServer::register_validators))
- .route(GET_HEADER_PATH, get(BuilderProxyServer::get_header))
- .route(GET_PAYLOAD_PATH, post(BuilderProxyServer::get_payload))
- .with_state(server);
-
- let addr = format!("0.0.0.0:{}", config.server_port);
- let listener = TcpListener::bind(addr).await?;
- axum::serve(listener, router).await?;
-
- Ok(())
-}
-
-async fn index() -> Html<&'static str> {
- Html("Hello")
-}
-
-#[derive(Error, Debug, Clone)]
-pub enum LocalPayloadIntegrityError {
- #[error(
- "Locally built payload does not match signed header.
- {field_name} mismatch: expected {expected}, have {have}"
- )]
- FieldMismatch { field_name: String, expected: String, have: String },
-}
-
-/// Helper macro to compare fields of the signed header and the local block.
-macro_rules! assert_payload_fields_eq {
- ($expected:expr, $have:expr, $field_name:ident) => {
- if $expected != $have {
- error!(
- field_name = stringify!($field_name),
- expected = %$expected,
- have = %$have,
- "Local block does not match signed header"
- );
- return Err(LocalPayloadIntegrityError::FieldMismatch {
- field_name: stringify!($field_name).to_string(),
- expected: $expected.to_string(),
- have: $have.to_string(),
- });
- }
- };
-}
-
-/// Perform some integrity checks on the locally built payload.
-/// This is to ensure that the beacon node will accept the header that was signed
-/// when we submit the full payload.
-#[inline]
-fn check_locally_built_payload_integrity(
- signed_blinded_block: &SignedBlindedBeaconBlock,
- local_payload: &GetPayloadResponse,
-) -> Result<(), LocalPayloadIntegrityError> {
- let header_signed_by_cl = &signed_blinded_block.message.body.execution_payload_header;
- let local_execution_payload = local_payload.execution_payload();
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.block_hash,
- local_execution_payload.block_hash(),
- BlockHash
- );
-
- assert_payload_fields_eq!(
- header_signed_by_cl.block_number,
- local_execution_payload.block_number(),
- BlockNumber
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.state_root,
- local_execution_payload.state_root(),
- StateRoot
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.receipts_root,
- local_execution_payload.receipts_root(),
- ReceiptsRoot
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.prev_randao,
- local_execution_payload.prev_randao(),
- PrevRandao
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.gas_limit,
- &local_execution_payload.gas_limit(),
- GasLimit
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.gas_used,
- &local_execution_payload.gas_used(),
- GasUsed
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.timestamp,
- &local_execution_payload.timestamp(),
- Timestamp
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.extra_data,
- local_execution_payload.extra_data(),
- ExtraData
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.base_fee_per_gas,
- local_execution_payload.base_fee_per_gas(),
- BaseFeePerGas
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.parent_hash,
- local_execution_payload.parent_hash(),
- ParentHash
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.fee_recipient,
- local_execution_payload.fee_recipient(),
- FeeRecipient
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.logs_bloom,
- local_execution_payload.logs_bloom(),
- LogsBloom
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.blob_gas_used,
- &local_execution_payload.blob_gas_used().unwrap_or_default(),
- BlobGasUsed
- );
-
- assert_payload_fields_eq!(
- &header_signed_by_cl.excess_blob_gas,
- &local_execution_payload.excess_blob_gas().unwrap_or_default(),
- ExcessBlobGas
- );
-
- // TODO: Sanity check: recalculate transactions and withdrawals roots
- // and assert them against the header
-
- // TODO: Sanity check: verify the validator signature
- // signed_blinded_block.verify_signature()?;
-
- Ok(())
-}
diff --git a/bolt-sidecar/src/api/commitments/jsonrpc.rs b/bolt-sidecar/src/api/commitments/jsonrpc.rs
deleted file mode 100644
index e15461c46..000000000
--- a/bolt-sidecar/src/api/commitments/jsonrpc.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use serde::{Deserialize, Serialize};
-use serde_json::Value;
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct JsonPayload {
- /// The JSON-RPC version string. MUST be "2.0".
- pub jsonrpc: String,
- /// The method string.
- pub method: String,
- /// Optional ID.
- pub id: Option,
- /// The parameters object.
- pub params: Vec,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct JsonResponse {
- pub jsonrpc: String,
- /// Optional ID. Must be serialized as `null` if not present.
- pub id: Option,
- #[serde(skip_serializing_if = "Value::is_null", default)]
- pub result: Value,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub error: Option,
-}
-
-impl Default for JsonResponse {
- fn default() -> Self {
- Self { jsonrpc: "2.0".to_string(), id: None, result: Value::Null, error: None }
- }
-}
-
-impl JsonResponse {
- pub fn from_error(code: i32, message: String) -> Self {
- Self {
- jsonrpc: "2.0".to_string(),
- id: None,
- result: Value::Null,
- error: Some(JsonError { code, message }),
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct JsonError {
- pub code: i32,
- pub message: String,
-}
diff --git a/bolt-sidecar/src/api/commitments/mod.rs b/bolt-sidecar/src/api/commitments/mod.rs
deleted file mode 100644
index a139aaeb2..000000000
--- a/bolt-sidecar/src/api/commitments/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-/// JSON-RPC helper types and functions.
-mod jsonrpc;
-/// The commitments-API JSON-RPC server implementation.
-pub mod server;
-/// The commitments-API specification and errors.
-pub mod spec;
diff --git a/bolt-sidecar/src/api/commitments/server.rs b/bolt-sidecar/src/api/commitments/server.rs
deleted file mode 100644
index 3894cfe69..000000000
--- a/bolt-sidecar/src/api/commitments/server.rs
+++ /dev/null
@@ -1,377 +0,0 @@
-use std::{
- collections::HashSet,
- fmt,
- future::Future,
- net::{SocketAddr, ToSocketAddrs},
- pin::Pin,
- str::FromStr,
- sync::Arc,
-};
-
-use alloy::primitives::{Address, Signature};
-use axum::{extract::State, http::HeaderMap, routing::post, Json, Router};
-use axum_extra::extract::WithRejection;
-use serde_json::Value;
-use tokio::{
- net::TcpListener,
- sync::{mpsc, oneshot},
-};
-use tracing::{debug, error, info, instrument};
-
-use crate::{
- common::CARGO_PKG_VERSION,
- primitives::{
- commitment::{InclusionCommitment, SignedCommitment},
- CommitmentRequest, InclusionRequest,
- },
-};
-
-use super::{
- jsonrpc::{JsonPayload, JsonResponse},
- spec::{
- CommitmentsApi, Error, RejectionError, GET_VERSION_METHOD, REQUEST_INCLUSION_METHOD,
- SIGNATURE_HEADER,
- },
-};
-
-/// Event type emitted by the commitments API.
-#[derive(Debug)]
-pub struct Event {
- /// The request to process.
- pub request: CommitmentRequest,
- /// The response channel.
- pub response: oneshot::Sender>,
-}
-
-/// The inner commitments-API handler that implements the [CommitmentsApi] spec.
-/// Should be wrapped by a [CommitmentsApiServer] JSON-RPC server to handle requests.
-#[derive(Debug)]
-pub struct CommitmentsApiInner {
- /// Event notification channel
- events: mpsc::Sender,
- /// Optional whitelist of ECDSA public keys
- #[allow(unused)]
- whitelist: Option>,
-}
-
-impl CommitmentsApiInner {
- /// Create a new API server with an optional whitelist of ECDSA public keys.
- pub fn new(events: mpsc::Sender) -> Self {
- Self { events, whitelist: None }
- }
-}
-
-#[async_trait::async_trait]
-impl CommitmentsApi for CommitmentsApiInner {
- async fn request_inclusion(
- &self,
- inclusion_request: InclusionRequest,
- ) -> Result {
- let (response_tx, response_rx) = oneshot::channel();
-
- let event = Event {
- request: CommitmentRequest::Inclusion(inclusion_request),
- response: response_tx,
- };
-
- self.events.send(event).await.unwrap();
-
- response_rx.await.map_err(|_| Error::Internal)?.map(|c| c.into())
- }
-}
-
-/// The outer commitments-API JSON-RPC server that wraps the [CommitmentsApiInner] handler.
-pub struct CommitmentsApiServer {
- /// The address to bind the server to. This will be updated
- /// with the actual address after the server is started.
- addr: SocketAddr,
- /// The shutdown signal.
- signal: Option + Send>>>,
-}
-
-impl fmt::Debug for CommitmentsApiServer {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("CommitmentsApiServer").field("addr", &self.addr).finish()
- }
-}
-
-impl CommitmentsApiServer {
- /// Creates the server with the given address and default shutdown signal (CTRL+C).
- pub fn new(addr: A) -> Self {
- Self {
- addr: addr.to_socket_addrs().unwrap().next().unwrap(),
- signal: Some(Box::pin(async {
- let _ = tokio::signal::ctrl_c().await;
- })),
- }
- }
-
- /// Creates the server with the given address and shutdown signal.
- pub fn with_shutdown(self, addr: A, signal: S) -> Self
- where
- A: ToSocketAddrs,
- S: Future