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): load delegations on startup and send them upon registration #292

Merged
merged 10 commits into from
Oct 15, 2024
1 change: 1 addition & 0 deletions bolt-sidecar/Cargo.lock

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

2 changes: 1 addition & 1 deletion bolt-sidecar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ thiserror = "1.0"
rand = "0.8.5"
dotenvy = "0.15.7"
regex = "1.10.5"
# backtrace = "0.3.74"
toml = "0.5"

# tracing
tracing = "0.1.40"
Expand Down
20 changes: 20 additions & 0 deletions bolt-sidecar/Config.toml
merklefruit marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ports
port = 8000
metrics_port = 3300

# node urls
execution_api_url = "http://localhost:8545"
beacon_api_url = "http://localhost:5052"
engine_api_url = "http://localhost:8551"

# constraints options
constraints_url = "http://localhost:3030"
constraints_proxy_port = 18551

# chain options
chain = "kurtosis"
slot_time = 2

# signing options
private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe"
delegations_path = "./delegations.json"
13 changes: 8 additions & 5 deletions bolt-sidecar/bin/sidecar.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver};
use clap::Parser;
use eyre::{bail, Result};
use tracing::info;

use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver};

#[tokio::main]
async fn main() -> Result<()> {
let opts = Opts::parse();
let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") {
Opts::parse_from_toml(config_path.as_str())?
} else {
Opts::parse()
};

let metrics_port =
if !opts.telemetry.disable_metrics { Some(opts.telemetry.metrics_port) } else { None };
if let Err(err) = init_telemetry_stack(metrics_port) {
if let Err(err) = init_telemetry_stack(opts.telemetry.metrics_port()) {
bail!("Failed to initialize telemetry stack: {:?}", err)
}

Expand Down
4 changes: 2 additions & 2 deletions bolt-sidecar/src/api/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ pub trait ConstraintsApi: BuilderApi {
) -> Result<VersionedValue<SignedBuilderBid>, BuilderApiError>;

/// Implements: <https://chainbound.github.io/bolt-docs/api/builder#delegate>
async fn delegate(&self, signed_data: SignedDelegation) -> Result<(), BuilderApiError>;
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>;
async fn revoke(&self, signed_data: &[SignedRevocation]) -> Result<(), BuilderApiError>;
}
34 changes: 29 additions & 5 deletions bolt-sidecar/src/client/constraints_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use axum::http::StatusCode;
use beacon_api_client::VersionedValue;
use ethereum_consensus::{
builder::SignedValidatorRegistration, deneb::mainnet::SignedBlindedBeaconBlock, Fork,
builder::SignedValidatorRegistration, crypto::PublicKey as BlsPublicKey,
deneb::mainnet::SignedBlindedBeaconBlock, Fork,
};
use reqwest::Url;
use tracing::error;
Expand All @@ -30,6 +31,7 @@ use crate::{
pub struct ConstraintsClient {
url: Url,
client: reqwest::Client,
delegations: Vec<SignedDelegation>,
}

impl ConstraintsClient {
Expand All @@ -38,9 +40,24 @@ impl ConstraintsClient {
Self {
url: url.into(),
client: reqwest::ClientBuilder::new().user_agent("bolt-sidecar").build().unwrap(),
delegations: Vec::new(),
}
}

/// Adds a list of delegations to the client.
pub fn add_delegations(&mut self, delegations: Vec<SignedDelegation>) {
self.delegations.extend(delegations);
}

/// Finds all delegations for the given public key.
pub fn find_delegatees(&self, pubkey: &BlsPublicKey) -> Vec<BlsPublicKey> {
self.delegations
.iter()
.filter(|d| d.message.delegatee_pubkey == *pubkey)
.map(|d| d.message.delegatee_pubkey.clone())
.collect::<Vec<_>>()
}

fn endpoint(&self, path: &str) -> Url {
self.url.join(path).unwrap_or_else(|e| {
error!(err = ?e, "Failed to join path: {} with url: {}", path, self.url);
Expand Down Expand Up @@ -80,6 +97,13 @@ impl BuilderApi for ConstraintsClient {
return Err(BuilderApiError::FailedRegisteringValidators(error));
}

// If there are any delegations, propagate them to the relay
if self.delegations.is_empty() {
return Ok(());
} else if let Err(err) = self.delegate(&self.delegations).await {
error!(?err, "Failed to propagate delegations during validator registration");
}

Ok(())
}

Expand Down Expand Up @@ -190,12 +214,12 @@ impl ConstraintsApi for ConstraintsClient {
Ok(header)
}

async fn delegate(&self, signed_data: SignedDelegation) -> Result<(), BuilderApiError> {
async fn delegate(&self, signed_data: &[SignedDelegation]) -> Result<(), BuilderApiError> {
let response = self
.client
.post(self.endpoint(DELEGATE_PATH))
.header("content-type", "application/json")
.body(serde_json::to_string(&signed_data)?)
.body(serde_json::to_string(signed_data)?)
.send()
.await?;

Expand All @@ -207,12 +231,12 @@ impl ConstraintsApi for ConstraintsClient {
Ok(())
}

async fn revoke(&self, signed_data: SignedRevocation) -> Result<(), BuilderApiError> {
async fn revoke(&self, signed_data: &[SignedRevocation]) -> Result<(), BuilderApiError> {
let response = self
.client
.post(self.endpoint(REVOKE_PATH))
.header("content-type", "application/json")
.body(serde_json::to_string(&signed_data)?)
.body(serde_json::to_string(signed_data)?)
.send()
.await?;

Expand Down
4 changes: 2 additions & 2 deletions bolt-sidecar/src/client/test_util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ impl ConstraintsApi for MockConstraintsClient {
Ok(bid)
}

async fn delegate(&self, signed_data: SignedDelegation) -> Result<(), BuilderApiError> {
async fn delegate(&self, signed_data: &[SignedDelegation]) -> Result<(), BuilderApiError> {
Err(BuilderApiError::Generic(
"MockConstraintsClient does not support delegating".to_string(),
))
}

async fn revoke(&self, signed_data: SignedRevocation) -> Result<(), BuilderApiError> {
async fn revoke(&self, signed_data: &[SignedRevocation]) -> Result<(), BuilderApiError> {
Err(BuilderApiError::Generic("MockConstraintsClient does not support revoking".to_string()))
}
}
Expand Down
21 changes: 21 additions & 0 deletions bolt-sidecar/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use alloy::primitives::U256;
use blst::min_pk::SecretKey;
use rand::{Rng, RngCore};
use reth_primitives::PooledTransactionsElement;
use serde::{Deserialize, Deserializer};

use crate::{
primitives::{AccountState, TransactionExt},
Expand Down Expand Up @@ -107,6 +108,16 @@ impl BlsSecretKeyWrapper {
}
}

impl<'de> Deserialize<'de> for BlsSecretKeyWrapper {
fn deserialize<D>(deserializer: D) -> Result<BlsSecretKeyWrapper, D::Error>
where
D: Deserializer<'de>,
{
let sk = String::deserialize(deserializer)?;
Ok(BlsSecretKeyWrapper::from(sk.as_str()))
}
}

impl From<&str> for BlsSecretKeyWrapper {
fn from(sk: &str) -> Self {
let hex_sk = sk.strip_prefix("0x").unwrap_or(sk);
Expand Down Expand Up @@ -158,6 +169,16 @@ impl From<&str> for JwtSecretConfig {
}
}

impl<'de> Deserialize<'de> for JwtSecretConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let jwt = String::deserialize(deserializer)?;
Ok(Self::from(jwt.as_str()))
}
}

impl Deref for JwtSecretConfig {
type Target = str;
fn deref(&self) -> &Self::Target {
Expand Down
6 changes: 3 additions & 3 deletions bolt-sidecar/src/config/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::time::Duration;

use clap::{Args, ValueEnum};
use ethereum_consensus::deneb::{compute_fork_data_root, Root};
use serde::Deserialize;

/// Default commitment deadline duration.
///
Expand All @@ -21,7 +22,7 @@ pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67];

/// Configuration for the chain the sidecar is running on.
/// This allows to customize the slot time for custom Kurtosis devnets.
#[derive(Debug, Clone, Copy, Args)]
#[derive(Debug, Clone, Copy, Args, Deserialize)]
pub struct ChainConfig {
/// Chain on which the sidecar is running
#[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value = "mainnet")]
Expand Down Expand Up @@ -55,9 +56,8 @@ impl Default for ChainConfig {
}

/// Supported chains for the sidecar
#[derive(Debug, Clone, Copy, ValueEnum)]
#[derive(Debug, Clone, Copy, ValueEnum, Deserialize)]
#[clap(rename_all = "kebab_case")]
#[allow(missing_docs)]
pub enum Chain {
Mainnet,
Holesky,
Expand Down
27 changes: 19 additions & 8 deletions bolt-sidecar/src/config/limits.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
use clap::Parser;
use std::num::NonZero;

use clap::Parser;
use serde::Deserialize;

// Default limit values
pub const DEFAULT_MAX_COMMITMENTS: usize = 128;
pub const DEFAULT_MAX_COMMITTED_GAS: u64 = 10_000_000;
pub const DEFAULT_MIN_PRIORITY_FEE: u128 = 1_000_000_000; // 1 Gwei

/// Limits for the sidecar.
#[derive(Debug, Parser, Clone, Copy)]
#[derive(Debug, Parser, Clone, Copy, Deserialize)]
pub struct LimitsOpts {
/// Max number of commitments to accept per block
#[clap(long, env = "BOLT_SIDECAR_MAX_COMMITMENTS",
default_value_t = LimitsOpts::default().max_commitments_per_slot)]
#[clap(
long,
env = "BOLT_SIDECAR_MAX_COMMITMENTS",
default_value_t = LimitsOpts::default().max_commitments_per_slot
)]
pub max_commitments_per_slot: NonZero<usize>,
/// Max committed gas per slot
#[clap(long, env = "BOLT_SIDECAR_MAX_COMMITTED_GAS",
default_value_t = LimitsOpts::default().max_committed_gas_per_slot)]
#[clap(
long,
env = "BOLT_SIDECAR_MAX_COMMITTED_GAS",
default_value_t = LimitsOpts::default().max_committed_gas_per_slot
)]
pub max_committed_gas_per_slot: NonZero<u64>,
/// Min priority fee to accept for a commitment
#[clap(long, env = "BOLT_SIDECAR_MIN_PRIORITY_FEE",
default_value_t = LimitsOpts::default().min_priority_fee)]
#[clap(
long,
env = "BOLT_SIDECAR_MIN_PRIORITY_FEE",
default_value_t = LimitsOpts::default().min_priority_fee
)]
pub min_priority_fee: NonZero<u128>,
}

Expand Down
Loading