Skip to content

Commit

Permalink
Merge pull request #161 from chainbound/feat/sidecar/commitments-api
Browse files Browse the repository at this point in the history
Implement new commitments-API
  • Loading branch information
thedevbirb authored Jul 26, 2024
2 parents f9b1700 + 5b3dd93 commit ab94c12
Show file tree
Hide file tree
Showing 19 changed files with 1,050 additions and 635 deletions.
23 changes: 23 additions & 0 deletions bolt-sidecar/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion bolt-sidecar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ default-run = "bolt-sidecar"
clap = { version = "4.5.4", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
axum = { version = "0.7", features = ["macros"] }
axum-extra = "0.9.3"
warp = "0.3.7"
futures = "0.3"

Expand All @@ -19,7 +20,12 @@ tree_hash_derive = "0.5"
secp256k1 = { version = "0.29.0", features = ["rand"] }

# alloy
alloy = { version = "0.2.0", features = ["full", "provider-trace-api", "rpc-types-beacon", "rpc-types-engine"] }
alloy = { version = "0.2.0", features = [
"full",
"provider-trace-api",
"rpc-types-beacon",
"rpc-types-engine",
] }

# alloy-rpc-types = { git = "https://github.com/chainbound/alloy", branch = "fix/account-override-serialize" }

Expand Down
47 changes: 32 additions & 15 deletions bolt-sidecar/bin/sidecar.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use std::time::Duration;

use alloy::rpc::types::beacon::events::HeadEvent;
use alloy::{rpc::types::beacon::events::HeadEvent, signers};
use tokio::sync::mpsc;

use bolt_sidecar::{
commitments::{
server::{CommitmentsApiServer, Event},
spec,
},
crypto::{bls::Signer, SignableBLS, SignerBLS},
json_rpc::api::{ApiError, ApiEvent},
primitives::{
CommitmentRequest, ConstraintsMessage, FetchPayloadRequest, LocalPayloadFetcher,
SignedConstraints,
},
start_builder_proxy_server, start_rpc_server,
start_builder_proxy_server,
state::{ConsensusState, ExecutionState, HeadTracker, StateClient},
BeaconClient, BuilderProxyConfig, Config, ConstraintsApi, LocalBuilder, MevBoostClient,
};
Expand All @@ -27,14 +30,21 @@ async fn main() -> eyre::Result<()> {
// probably it's cleanest to have the Config parser initialize a generic Signer
let signer = Signer::new(config.private_key.clone().unwrap());

// TODO: support external signers
let commitment_signer = signers::local::PrivateKeySigner::random();

let state_client = StateClient::new(config.execution_api_url.clone());
let mut execution_state = ExecutionState::new(state_client, config.limits).await?;

let mevboost_client = MevBoostClient::new(config.mevboost_url.clone());
let beacon_client = BeaconClient::new(config.beacon_api_url.clone());

let (api_events, mut api_events_rx) = mpsc::channel(1024);
let shutdown_tx = start_rpc_server(&config, api_events).await?;

let mut api_server = CommitmentsApiServer::new(format!("0.0.0.0:{}", config.rpc_port));

api_server.run(api_events).await?;

let mut consensus_state = ConsensusState::new(
beacon_client.clone(),
config.validator_indexes.clone(),
Expand Down Expand Up @@ -65,44 +75,52 @@ async fn main() -> eyre::Result<()> {
// TODO: parallelize this
loop {
tokio::select! {
Some(ApiEvent { request, response_tx }) = api_events_rx.recv() => {
Some(Event { mut request, response }) = api_events_rx.recv() => {
let start = std::time::Instant::now();

let validator_index = match consensus_state.validate_request(&request) {
Ok(index) => index,
Err(e) => {
tracing::error!(err = ?e, "Failed to validate request");
let _ = response_tx.send(Err(ApiError::Custom(e.to_string())));
let _ = response.send(Err(spec::Error::ValidationFailed(e.to_string())));
continue;
}
};

if let Err(e) = execution_state.validate_commitment_request(&request).await {
if let Err(e) = execution_state.validate_commitment_request(&mut request).await {
tracing::error!(err = ?e, "Failed to commit request");
let _ = response_tx.send(Err(ApiError::Custom(e.to_string())));
let _ = response.send(Err(spec::Error::ValidationFailed(e.to_string())));
continue;
};

// TODO: match when we have more request types
let CommitmentRequest::Inclusion(request) = request;
let CommitmentRequest::Inclusion(ref req) = request;
tracing::info!(
elapsed = ?start.elapsed(),
tx_hash = %request.tx.hash(),
digest = ?req.digest(),
"Validation against execution state passed"
);

// TODO: review all this `clone` usage

// parse the request into constraints and sign them with the sidecar signer
let slot = request.slot;
let message = ConstraintsMessage::build(validator_index, request);
let slot = req.slot;
let message = ConstraintsMessage::build(validator_index, req.clone());
let signature = signer.sign(&message.digest())?.to_string();
let signed_constraints = SignedConstraints { message, signature };

execution_state.add_constraint(slot, signed_constraints.clone());

let res = serde_json::to_value(signed_constraints).map_err(Into::into);
let _ = response_tx.send(res).ok();
// Create a commitment by signing the request with the commitment signer
match request.commit_and_sign(&commitment_signer).await {
Ok(commitment) => {
let _ = response.send(Ok(commitment));
},
Err(e) => {
tracing::error!(err = ?e, "Failed to sign commitment");
let _ = response.send(Err(spec::Error::Internal));
}
}
},
Ok(HeadEvent { slot, .. }) = head_tracker.next_head() => {
tracing::info!(slot, "Received new head event");
Expand Down Expand Up @@ -163,7 +181,6 @@ async fn main() -> eyre::Result<()> {
},
Ok(_) = tokio::signal::ctrl_c() => {
tracing::info!("Received SIGINT, shutting down...");
shutdown_tx.send(()).await.ok();
break;
}
}
Expand Down
52 changes: 52 additions & 0 deletions bolt-sidecar/src/api/commitments/jsonrpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use serde::{Deserialize, Serialize};

#[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<serde_json::Value>,
/// The parameters object.
pub params: Vec<serde_json::Value>,
}

#[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_json::Value>,
#[serde(skip_serializing_if = "serde_json::Value::is_null", default)]
pub result: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonError>,
}

impl Default for JsonResponse {
fn default() -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: None,
result: serde_json::Value::Null,
error: None,
}
}
}

impl JsonResponse {
pub fn from_error(code: i32, message: String) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: None,
result: serde_json::Value::Null,
error: Some(JsonError { code, message }),
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonError {
pub code: i32,
pub message: String,
}
6 changes: 6 additions & 0 deletions bolt-sidecar/src/api/commitments/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// 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;
Loading

0 comments on commit ab94c12

Please sign in to comment.