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

feat(sidecar): constraints client #212

Merged
merged 15 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bolt-sidecar/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485
BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400
BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451
BOLT_SIDECAR_MEVBOOST_URL=http://localhost:19550
BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550
BOLT_SIDECAR_CB_SIGNER_URL=http://localhost:19551

# server ports
BOLT_SIDECAR_PORT=8000
BOLT_SIDECAR_MEVBOOST_PROXY_PORT=18551
BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551

# commitment limits
BOLT_SIDECAR_MAX_COMMITMENTS=128
Expand Down
24 changes: 12 additions & 12 deletions bolt-sidecar/Cargo.lock

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

4 changes: 2 additions & 2 deletions bolt-sidecar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ metrics-exporter-prometheus = { version = "0.15.3", features = [
] }

# commit-boost
commit-boost = { git = "https://github.com/Commit-Boost/commit-boost-client" }
cb-common = { git = "https://github.com/Commit-Boost/commit-boost-client" }
commit-boost = { git = "https://github.com/Commit-Boost/commit-boost-client", rev = "45ce8f1"}
cb-common = { git = "https://github.com/Commit-Boost/commit-boost-client", rev = "45ce8f1" }

[dev-dependencies]
alloy-node-bindings = "0.2.0"
Expand Down
4 changes: 2 additions & 2 deletions bolt-sidecar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The sidecar is responsible for:
3. Implementing pricing strategies
4. Building a block template & simulation
5. Communicating constraints to the downstream PBS pipeline
6. Verifying any incoming builder bids from mev-boost
6. Verifying any incoming builder bids from constraints client
7. Dealing with PBS failures by falling back to the local template

### Local Block Template
Expand All @@ -38,4 +38,4 @@ in case a fallback block is required.

## Running

- We require Anvil to be installed in the $PATH for running tests
- We require Anvil to be installed in the $PATH for running tests
22 changes: 11 additions & 11 deletions bolt-sidecar/src/api/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use super::spec::{
STATUS_PATH,
};
use crate::{
client::mevboost::MevBoostClient,
client::constraints_client::ConstraintsClient,
primitives::{GetPayloadResponse, PayloadFetcher, SignedBuilderBid},
telemetry::ApiMetrics,
};
Expand Down Expand Up @@ -63,15 +63,15 @@ where
Self { proxy_target, local_payload: Mutex::new(None), payload_fetcher }
}

/// Gets the status. Just forwards the request to mev-boost and returns the status.
/// Gets the status. Just forwards the request to constraints client and returns the status.
pub async fn status(State(server): State<Arc<BuilderProxyServer<T, P>>>) -> StatusCode {
let start = std::time::Instant::now();
debug!("Received status request");

let status = match server.proxy_target.status().await {
Ok(status) => status,
Err(error) => {
error!(%error, "Failed to get status from mev-boost");
error!(%error, "Failed to get status from constraints client");
StatusCode::INTERNAL_SERVER_ERROR
}
};
Expand All @@ -82,7 +82,7 @@ where
status
}

/// Registers the validators. Just forwards the request to mev-boost
/// Registers the validators. Just forwards the request to constraints client
/// and returns the status.
///
/// TODO: intercept this to register Bolt validators on-chain as well.
Expand All @@ -96,7 +96,7 @@ where
}

/// Gets the header. NOTE: converts this request to a get_header_with_proofs
/// request to the modified mev-boost.
/// request to the modified constraints client.
///
/// In case of a builder or relay failure, we return the locally built block header
/// and store the actual payload so we can return it later.
Expand Down Expand Up @@ -203,11 +203,11 @@ where
.await
.map(Json)
.map_err(|e| {
error!(elapsed = ?start.elapsed(), error = %e, "Failed to get payload from mev-boost");
error!(elapsed = ?start.elapsed(), error = %e, "Failed to get payload from constraints client");
e
})?;

info!(elapsed = ?start.elapsed(), "Returning payload from mev-boost");
info!(elapsed = ?start.elapsed(), "Returning payload from constraints client");
ApiMetrics::increment_remote_blocks_proposed();

Ok(payload)
Expand All @@ -217,8 +217,8 @@ where
/// Configuration for the builder proxy.
#[derive(Debug, Clone)]
pub struct BuilderProxyConfig {
/// The URL of the target mev-boost server.
pub mevboost_url: Url,
/// The URL of the target constraints client server.
pub constraints_url: Url,
/// The port on which the builder proxy should listen.
pub server_port: u16,
}
Expand All @@ -233,11 +233,11 @@ where
{
info!(
port = config.server_port,
target = config.mevboost_url.to_string(),
target = config.constraints_url.to_string(),
"Starting builder proxy..."
);

let mev_boost = MevBoostClient::new(config.mevboost_url);
let mev_boost = ConstraintsClient::new(config.constraints_url);
let server = Arc::new(BuilderProxyServer::new(mev_boost, payload_fetcher));

let router = Router::new()
Expand Down
31 changes: 27 additions & 4 deletions bolt-sidecar/src/api/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use ethereum_consensus::{
};
use serde::{Deserialize, Serialize, Serializer};

use crate::primitives::{BatchedSignedConstraints, GetPayloadResponse, SignedBuilderBid};
use crate::primitives::{
BatchedSignedConstraints, GetPayloadResponse, SignedBuilderBid, SignedDelegation,
SignedRevocation,
};

use super::builder::GetHeaderParams;

Expand All @@ -22,7 +25,11 @@ pub const GET_HEADER_PATH: &str = "/eth/v1/builder/header/:slot/:parent_hash/:pu
/// The path to the builder API get payload endpoint.
pub const GET_PAYLOAD_PATH: &str = "/eth/v1/builder/blinded_blocks";
/// The path to the constraints API submit constraints endpoint.
pub const CONSTRAINTS_PATH: &str = "/eth/v1/builder/constraints";
pub const SUBMIT_CONSTRAINTS_PATH: &str = "/constraints/v1/builder/constraints";
/// The path to the constraints API delegate endpoint.
pub const DELEGATE_PATH: &str = "/constraints/v1/builder/delegate";
/// The path to the constraints API revoke endpoint.
pub const REVOKE_PATH: &str = "/constraints/v1/builder/revoke";

/// A response object for errors.
#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -52,6 +59,10 @@ pub enum BuilderApiError {
FailedGettingPayload(ErrorResponse),
#[error("Failed submitting constraints: {0:?}")]
FailedSubmittingConstraints(ErrorResponse),
#[error("Failed to delegate constraint submission rights: {0:?}")]
FailedDelegating(ErrorResponse),
#[error("Failed to revoke constraint submission rights: {0:?}")]
FailedRevoking(ErrorResponse),
#[error("Failed to fetch local payload for slot {0}")]
FailedToFetchLocalPayload(u64),
#[error("Axum error: {0:?}")]
Expand Down Expand Up @@ -85,6 +96,12 @@ impl IntoResponse for BuilderApiError {
BuilderApiError::FailedSubmittingConstraints(error) => {
(StatusCode::from_u16(error.code).unwrap(), Json(error)).into_response()
}
BuilderApiError::FailedDelegating(error) => {
(StatusCode::from_u16(error.code).unwrap(), Json(error)).into_response()
}
BuilderApiError::FailedRevoking(error) => {
(StatusCode::from_u16(error.code).unwrap(), Json(error)).into_response()
}
BuilderApiError::AxumError(err) => {
(StatusCode::BAD_REQUEST, err.to_string()).into_response()
}
Expand Down Expand Up @@ -142,15 +159,21 @@ pub trait BuilderApi {
#[async_trait::async_trait]
/// Implements the constraints API as defines in <https://chainbound.github.io/bolt-docs/api/builder-api>
pub trait ConstraintsApi: BuilderApi {
/// Implements: <https://chainbound.github.io/bolt-docs/api/builder-api#ethv1builderconstraints>
/// Implements: <https://chainbound.github.io/bolt-docs/api/builder#constraints>
async fn submit_constraints(
&self,
constraints: &BatchedSignedConstraints,
) -> Result<(), BuilderApiError>;

/// Implements: <https://chainbound.github.io/bolt-docs/api/builder-api#ethv1builderheader_with_proofsslotparent_hashpubkey>
/// Implements: <https://chainbound.github.io/bolt-docs/api/builder#get_header_with_proofs>
async fn get_header_with_proofs(
&self,
params: GetHeaderParams,
) -> Result<VersionedValue<SignedBuilderBid>, BuilderApiError>;

/// Implements: <https://chainbound.github.io/bolt-docs/api/builder#delegate>
async fn delegate(&self, signed_data: SignedDelegation) -> Result<(), BuilderApiError>;

/// Implements: <https://chainbound.github.io/bolt-docs/api/builder#revoke>
async fn revoke(&self, signed_data: SignedRevocation) -> Result<(), BuilderApiError>;
}
2 changes: 1 addition & 1 deletion bolt-sidecar/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl LocalBuilder {
// the current head of the chain
let block = self.fallback_builder.build_fallback_payload(slot, &transactions).await?;

// NOTE: we use a big value for the bid to ensure it gets chosen by mev-boost.
// NOTE: we use a big value for the bid to ensure it gets chosen by constraints client.
// the client has no way to actually verify this, and we don't need to trust
// an external relay as this block is self-built, so the fake bid value is fine.
//
Expand Down
44 changes: 13 additions & 31 deletions bolt-sidecar/src/builder/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ use tracing::warn;

use crate::{
common::max_transaction_cost,
primitives::{
constraint::Constraint, AccountState, FullTransaction, SignedConstraints, TransactionExt,
},
primitives::{AccountState, FullTransaction, SignedConstraints, TransactionExt},
};

/// A block template that serves as a fallback block, but is also used
Expand Down Expand Up @@ -47,10 +45,7 @@ impl BlockTemplate {
/// Returns the cloned list of transactions from the constraints.
#[inline]
pub fn transactions(&self) -> Vec<FullTransaction> {
self.signed_constraints_list
.iter()
.flat_map(|sc| sc.message.constraints.iter().map(|c| c.transaction.clone()))
.collect()
self.signed_constraints_list.iter().flat_map(|sc| sc.message.constraints.clone()).collect()
}

/// Converts the list of signed constraints into a list of signed transactions. Use this when
Expand All @@ -60,10 +55,7 @@ impl BlockTemplate {
self.signed_constraints_list
.iter()
.flat_map(|sc| {
sc.message
.constraints
.iter()
.map(|c| c.transaction.clone().into_inner().into_transaction())
sc.message.constraints.iter().map(|c| c.clone().into_inner().into_transaction())
})
.collect()
}
Expand All @@ -76,7 +68,7 @@ impl BlockTemplate {
self.signed_constraints_list
.iter()
.flat_map(|sc| sc.message.constraints.iter())
.filter_map(|c| c.transaction.blob_sidecar())
.filter_map(|c| c.blob_sidecar())
.fold(
(Vec::new(), Vec::new(), Vec::new()),
|(mut commitments, mut proofs, mut blobs), bs| {
Expand Down Expand Up @@ -108,7 +100,7 @@ impl BlockTemplate {
#[inline]
pub fn committed_gas(&self) -> u64 {
self.signed_constraints_list.iter().fold(0, |acc, sc| {
acc + sc.message.constraints.iter().fold(0, |acc, c| acc + c.transaction.gas_limit())
acc + sc.message.constraints.iter().fold(0, |acc, c| acc + c.gas_limit())
})
}

Expand All @@ -117,11 +109,7 @@ impl BlockTemplate {
pub fn blob_count(&self) -> usize {
self.signed_constraints_list.iter().fold(0, |mut acc, sc| {
acc += sc.message.constraints.iter().fold(0, |acc, c| {
acc + c
.transaction
.as_eip4844()
.map(|tx| tx.blob_versioned_hashes.len())
.unwrap_or(0)
acc + c.as_eip4844().map(|tx| tx.blob_versioned_hashes.len()).unwrap_or(0)
});

acc
Expand All @@ -131,7 +119,7 @@ impl BlockTemplate {
/// Adds a list of constraints to the block template and updates the state diff.
pub fn add_constraints(&mut self, constraints: SignedConstraints) {
for constraint in constraints.message.constraints.iter() {
let max_cost = max_transaction_cost(&constraint.transaction);
let max_cost = max_transaction_cost(&constraint);
self.state_diff
.diffs
.entry(constraint.sender())
Expand All @@ -150,13 +138,10 @@ impl BlockTemplate {
let constraints = self.signed_constraints_list.remove(index);

for constraint in constraints.message.constraints.iter() {
self.state_diff
.diffs
.entry(constraint.transaction.sender().expect("Recovered sender"))
.and_modify(|(nonce, balance)| {
*nonce = nonce.saturating_sub(1);
*balance -= max_transaction_cost(&constraint.transaction);
});
self.state_diff.diffs.entry(constraint.sender()).and_modify(|(nonce, balance)| {
*nonce = nonce.saturating_sub(1);
*balance -= max_transaction_cost(&constraint);
});
}
}

Expand All @@ -166,7 +151,7 @@ impl BlockTemplate {

// The preconfirmations made by such address, and the indexes of the signed constraints
// in which they appear
let constraints_with_address: Vec<(usize, Vec<&Constraint>)> = self
let constraints_with_address: Vec<(usize, Vec<&FullTransaction>)> = self
.signed_constraints_list
.iter()
.enumerate()
Expand All @@ -181,10 +166,7 @@ impl BlockTemplate {
.iter()
.flat_map(|c| c.1.clone())
.fold((U256::ZERO, u64::MAX), |(total_cost, min_nonce), c| {
(
total_cost + max_transaction_cost(&c.transaction),
min_nonce.min(c.transaction.nonce()),
)
(total_cost + max_transaction_cost(&c), min_nonce.min(c.nonce()))
});

if state.balance < max_total_cost || state.transaction_count > min_nonce {
Expand Down
Loading
Loading