Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new commitments-API #161

Merged
merged 14 commits into from
Jul 26, 2024
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
49 changes: 34 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,22 @@ 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));
merklefruit marked this conversation as resolved.
Show resolved Hide resolved

api_server.run(api_events).await?;

// let shutdown_tx = start_rpc_server(&config, api_events).await?;
mempirate marked this conversation as resolved.
Show resolved Hide resolved
let mut consensus_state = ConsensusState::new(
beacon_client.clone(),
config.validator_indexes.clone(),
Expand Down Expand Up @@ -65,44 +76,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));
merklefruit marked this conversation as resolved.
Show resolved Hide resolved
}
}
},
Ok(HeadEvent { slot, .. }) = head_tracker.next_head() => {
tracing::info!(slot, "Received new head event");
Expand Down Expand Up @@ -163,7 +182,7 @@ async fn main() -> eyre::Result<()> {
},
Ok(_) = tokio::signal::ctrl_c() => {
tracing::info!("Received SIGINT, shutting down...");
shutdown_tx.send(()).await.ok();
// shutdown_tx.send(()).await.ok();
mempirate marked this conversation as resolved.
Show resolved Hide resolved
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<String>,
/// 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<String>,
#[serde(skip_serializing_if = "serde_json::Value::is_null")]
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,
}
3 changes: 3 additions & 0 deletions bolt-sidecar/src/api/commitments/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod jsonrpc;
pub mod server;
pub mod spec;
Loading
Loading