From b981807282ab6e91e529c86cfed879349114e564 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 3 Jul 2024 17:41:44 +0200 Subject: [PATCH 1/2] feat: refactor config and add ChainConfig --- bolt-sidecar/bin/sidecar.rs | 2 +- bolt-sidecar/src/builder/mod.rs | 4 +- bolt-sidecar/src/builder/signature.rs | 16 +- bolt-sidecar/src/config/chain.rs | 162 ++++++++++++++++++ bolt-sidecar/src/{config.rs => config/mod.rs} | 132 ++------------ bolt-sidecar/src/config/signing.rs | 16 ++ bolt-sidecar/src/lib.rs | 2 +- 7 files changed, 206 insertions(+), 128 deletions(-) create mode 100644 bolt-sidecar/src/config/chain.rs rename bolt-sidecar/src/{config.rs => config/mod.rs} (63%) create mode 100644 bolt-sidecar/src/config/signing.rs diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 81ad9bb5..98dccb28 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -38,7 +38,7 @@ async fn main() -> eyre::Result<()> { let mut consensus_state = ConsensusState::new( &config.beacon_api_url, &config.validator_indexes, - config.commitment_deadline, + config.chain.commitment_deadline(), ); // TODO: this can be replaced with ethereum_consensus::clock::from_system_time() diff --git a/bolt-sidecar/src/builder/mod.rs b/bolt-sidecar/src/builder/mod.rs index eef913b1..dfd46292 100644 --- a/bolt-sidecar/src/builder/mod.rs +++ b/bolt-sidecar/src/builder/mod.rs @@ -13,7 +13,7 @@ use crate::{ primitives::{ BuilderBid, GetPayloadResponse, PayloadAndBid, PayloadAndBlobs, SignedBuilderBid, }, - Chain, Config, + ChainConfig, Config, }; /// Basic block template handler that can keep track of @@ -73,7 +73,7 @@ pub struct LocalBuilder { secret_key: SecretKey, /// Chain configuration /// (necessary for signing messages with the correct domain) - chain: Chain, + chain: ChainConfig, /// Async fallback payload builder to generate valid payloads with /// the engine API's `engine_newPayloadV3` response error. fallback_builder: FallbackPayloadBuilder, diff --git a/bolt-sidecar/src/builder/signature.rs b/bolt-sidecar/src/builder/signature.rs index 1b808df7..b34f0ed6 100644 --- a/bolt-sidecar/src/builder/signature.rs +++ b/bolt-sidecar/src/builder/signature.rs @@ -7,7 +7,7 @@ use ethereum_consensus::ssz::prelude::{HashTreeRoot, MerkleizationError}; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; -use crate::Chain; +use crate::ChainConfig; /// Sign a SSZ object a BLS secret key, using the Application Builder domain /// for signing arbitrary builder-api messages in the out-of-protocol specifications. @@ -15,7 +15,7 @@ use crate::Chain; /// Fun Note: we use a `blst` secret key to sign a message, and produce an `alloy` signature, /// which is then converted to an `ethereum-consensus` signature. pub fn sign_builder_message( - chain: &Chain, + chain: &ChainConfig, sk: &SecretKey, msg: &T, ) -> Result { @@ -33,7 +33,7 @@ pub fn sign_builder_message( /// Verify a SSZ object signed with a BLS public key, using the Application Builder domain /// for signing arbitrary builder-api messages in the out-of-protocol specifications. pub fn verify_signed_builder_message( - chain: &Chain, + chain: &ChainConfig, pubkey: &PublicKey, msg: &T, signature: &BlsSignature, @@ -115,29 +115,29 @@ pub fn compute_builder_domain( #[cfg(test)] mod tests { - use crate::{builder::signature::compute_builder_domain, config::Chain}; + use crate::{builder::signature::compute_builder_domain, ChainConfig}; #[test] fn test_compute_builder_domain() { - let mainnet = Chain::Mainnet; + let mainnet = ChainConfig::mainnet(); assert_eq!( compute_builder_domain(mainnet.fork_version(), None), mainnet.builder_domain() ); - let holesky = Chain::Holesky; + let holesky = ChainConfig::holesky(); assert_eq!( compute_builder_domain(holesky.fork_version(), None), holesky.builder_domain() ); - let kurtosis = Chain::Kurtosis; + let kurtosis = ChainConfig::kurtosis(0, 0); assert_eq!( compute_builder_domain(kurtosis.fork_version(), None), kurtosis.builder_domain() ); - let helder = Chain::Helder; + let helder = ChainConfig::helder(); assert_eq!( compute_builder_domain(helder.fork_version(), None), helder.builder_domain() diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs new file mode 100644 index 00000000..4b6beded --- /dev/null +++ b/bolt-sidecar/src/config/chain.rs @@ -0,0 +1,162 @@ +use std::time::Duration; + +use alloy_primitives::b256; +use clap::{Args, ValueEnum}; + +/// Default commitment deadline duration. +/// +/// The sidecar will stop accepting new commitments for the next block +/// after this deadline has passed. This is to ensure that builders and +/// relays have enough time to build valid payloads. +pub const DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS: u64 = 8_000; + +/// Default slot time duration in seconds. +pub const DEFAULT_SLOT_TIME_IN_SECONDS: u64 = 12; + +/// Builder domain for signing messages on Ethereum Mainnet. +const BUILDER_DOMAIN_MAINNET: [u8; 32] = + b256!("00000001f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a9").0; + +/// Builder domain for signing messages on Holesky. +const BUILDER_DOMAIN_HOLESKY: [u8; 32] = + b256!("000000015b83a23759c560b2d0c64576e1dcfc34ea94c4988f3e0d9f77f05387").0; + +/// Builder domain for signing messages on stock Kurtosis devnets. +const BUILDER_DOMAIN_KURTOSIS: [u8; 32] = + b256!("000000010b41be4cdb34d183dddca5398337626dcdcfaf1720c1202d3b95f84e").0; + +/// Builder domain for signing messages on Helder. +const BUILDER_DOMAIN_HELDER: [u8; 32] = + b256!("0000000194c41af484fff7964969e0bdd922f82dff0f4be87a60d0664cc9d1ff").0; + +/// Configuration for the chain the sidecar is running on. +/// This allows to customize the slot time for custom Kurtosis devnets. +#[derive(Debug, Clone, Args)] +pub struct ChainConfig { + /// Chain on which the sidecar is running + #[clap(short = 'C', long, default_value = "mainnet")] + chain: Chain, + /// The deadline in the slot at which the sidecar will stop accepting + /// new commitments for the next block (parsed as milliseconds). + #[clap(short = 'd', long, default_value_t = DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS)] + commitment_deadline: u64, + /// The slot time duration in seconds. If provided, + /// it overrides the default for the selected [Chain]. + #[clap(short = 's', long, default_value_t = DEFAULT_SLOT_TIME_IN_SECONDS)] + slot_time_in_seconds: u64, +} + +impl Default for ChainConfig { + fn default() -> Self { + Self { + chain: Chain::Mainnet, + commitment_deadline: DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS, + slot_time_in_seconds: DEFAULT_SLOT_TIME_IN_SECONDS, + } + } +} + +/// Supported chains for the sidecar +#[derive(Debug, Clone, ValueEnum)] +#[clap(rename_all = "kebab_case")] +#[allow(missing_docs)] +pub enum Chain { + Mainnet, + Holesky, + Helder, + Kurtosis, +} + +impl ChainConfig { + /// Get the chain ID for the given chain. + pub fn chain_id(&self) -> u64 { + match self.chain { + Chain::Mainnet => 1, + Chain::Holesky => 17000, + Chain::Helder => 7014190335, + Chain::Kurtosis => 3151908, + } + } + + /// Get the chain name for the given chain. + pub fn name(&self) -> &'static str { + match self.chain { + Chain::Mainnet => "mainnet", + Chain::Holesky => "holesky", + Chain::Helder => "helder", + Chain::Kurtosis => "kurtosis", + } + } + + /// Get the slot time for the given chain in seconds. + pub fn slot_time(&self) -> u64 { + match self.chain { + Chain::Mainnet => DEFAULT_SLOT_TIME_IN_SECONDS, + Chain::Holesky => DEFAULT_SLOT_TIME_IN_SECONDS, + Chain::Helder => DEFAULT_SLOT_TIME_IN_SECONDS, + Chain::Kurtosis => self.slot_time_in_seconds, + } + } + + /// Get the domain for signing messages on the given chain. + pub fn builder_domain(&self) -> [u8; 32] { + match self.chain { + Chain::Mainnet => BUILDER_DOMAIN_MAINNET, + Chain::Holesky => BUILDER_DOMAIN_HOLESKY, + Chain::Helder => BUILDER_DOMAIN_HELDER, + Chain::Kurtosis => BUILDER_DOMAIN_KURTOSIS, + } + } + + /// Get the fork version for the given chain. + pub fn fork_version(&self) -> [u8; 4] { + match self.chain { + Chain::Mainnet => [0u8; 4], + Chain::Holesky => [1, 1, 112, 0], + Chain::Helder => [16, 0, 0, 0], + Chain::Kurtosis => [16, 0, 0, 56], + } + } + + /// Get the commitment deadline duration for the given chain. + pub fn commitment_deadline(&self) -> Duration { + match self.chain { + Chain::Mainnet => Duration::from_millis(DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS), + Chain::Holesky => Duration::from_millis(DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS), + Chain::Helder => Duration::from_millis(DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS), + Chain::Kurtosis => Duration::from_millis(self.commitment_deadline), + } + } +} + +#[cfg(test)] +impl ChainConfig { + pub fn mainnet() -> Self { + Self { + chain: Chain::Mainnet, + ..Default::default() + } + } + + pub fn holesky() -> Self { + Self { + chain: Chain::Holesky, + ..Default::default() + } + } + + pub fn helder() -> Self { + Self { + chain: Chain::Helder, + ..Default::default() + } + } + + pub fn kurtosis(slot_time_in_seconds: u64, commitment_deadline: u64) -> Self { + Self { + chain: Chain::Kurtosis, + slot_time_in_seconds, + commitment_deadline, + } + } +} diff --git a/bolt-sidecar/src/config.rs b/bolt-sidecar/src/config/mod.rs similarity index 63% rename from bolt-sidecar/src/config.rs rename to bolt-sidecar/src/config/mod.rs index 510a0b0e..88557e8b 100644 --- a/bolt-sidecar/src/config.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -1,17 +1,14 @@ -use std::time::Duration; - -use alloy_primitives::{b256, Address}; +use alloy_primitives::Address; use blst::min_pk::SecretKey; -use clap::{ArgGroup, Args, Parser, ValueEnum}; +use clap::Parser; use crate::crypto::bls::random_bls_secret; -/// Default commitment deadline duration. -/// -/// The sidecar will stop accepting new commitments for the next block -/// after this deadline has passed. This is to ensure that builders and -/// relays have enough time to build valid payloads. -pub const DEFAULT_COMMITMENT_DEADLINE: Duration = Duration::from_secs(8); +pub mod chain; +pub use chain::ChainConfig; + +pub mod signing; +pub use signing::SigningOpts; /// Default port for the JSON-RPC server exposed by the sidecar. pub const DEFAULT_RPC_PORT: u16 = 8000; @@ -46,7 +43,6 @@ pub struct Opts { /// Validator indexes #[clap(short = 'v', long, value_parser, num_args = 1.., value_delimiter = ',')] pub(super) validator_indexes: Vec, - /// Signing options /// The JWT secret token to authenticate calls to the engine API. /// /// It can either be a hex-encoded string or a file path to a file @@ -60,105 +56,16 @@ pub struct Opts { /// (If not provided, a random key will be used) #[clap(short = 'k', long)] pub(super) builder_private_key: Option, - /// The deadline in the slot at which the sidecar will stop accepting - /// new commitments for the next block (parsed as milliseconds) - #[clap(short = 'd', long)] - pub(super) commitment_deadline: Option, - /// Chain on which the sidecar is running - #[clap(short = 'C', long, default_value = "mainnet")] - pub(super) chain: Chain, + /// Chain config for the chain on which the sidecar is running + #[clap(flatten)] + pub(super) chain: ChainConfig, /// Commitment signing options. #[clap(flatten)] pub(super) signing: SigningOpts, } -/// Supported chains for the sidecar -#[derive(Debug, Clone, ValueEnum)] -#[clap(rename_all = "kebab_case")] -#[allow(missing_docs)] -pub enum Chain { - Mainnet, - Holesky, - Kurtosis, - Helder, -} - -impl Chain { - /// Get the chain ID for the given chain. - pub fn chain_id(&self) -> u64 { - match self { - Chain::Mainnet => 1, - Chain::Holesky => 17000, - Chain::Kurtosis => 3151908, - Chain::Helder => 7014190335, - } - } - - /// Get the chain name for the given chain. - pub fn name(&self) -> &'static str { - match self { - Chain::Mainnet => "mainnet", - Chain::Holesky => "holesky", - Chain::Kurtosis => "kurtosis", - Chain::Helder => "helder", - } - } - - /// Get the slot time for the given chain in seconds. - pub fn slot_time(&self) -> u64 { - match self { - Chain::Mainnet => 12, - Chain::Holesky => 12, - Chain::Kurtosis => 2, - Chain::Helder => 12, - } - } - - /// Get the domain for signing messages on the given chain. - pub fn builder_domain(&self) -> [u8; 32] { - match self { - Chain::Mainnet => { - b256!("00000001f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a9").0 - } - Chain::Holesky => { - b256!("000000015b83a23759c560b2d0c64576e1dcfc34ea94c4988f3e0d9f77f05387").0 - } - Chain::Kurtosis => { - b256!("000000010b41be4cdb34d183dddca5398337626dcdcfaf1720c1202d3b95f84e").0 - } - Chain::Helder => { - b256!("0000000194c41af484fff7964969e0bdd922f82dff0f4be87a60d0664cc9d1ff").0 - } - } - } - - /// Get the fork version for the given chain. - pub fn fork_version(&self) -> [u8; 4] { - match self { - Chain::Mainnet => [0u8; 4], - Chain::Holesky => [1, 1, 112, 0], - Chain::Kurtosis => [16, 0, 0, 56], - Chain::Helder => [16, 0, 0, 0], - } - } -} - -/// Command-line options for signing -#[derive(Debug, Clone, Args)] -#[clap( - group = ArgGroup::new("signing-opts").required(true) - .args(&["private_key", "commit_boost_url"]) -)] -pub struct SigningOpts { - /// Private key to use for signing preconfirmation requests - #[clap(short = 'k', long)] - pub(super) private_key: Option, - /// URL for the commit-boost sidecar - #[clap(short = 'C', long, conflicts_with("private_key"))] - pub(super) commit_boost_url: Option, -} - -/// Configuration options for the sidecar +/// Configuration options for the sidecar. These are parsed from +/// command-line options in the form of [`Opts`]. #[derive(Debug, Clone)] pub struct Config { /// Port to listen on for incoming JSON-RPC requests @@ -187,11 +94,8 @@ pub struct Config { pub validator_indexes: Vec, /// Local bulider private key pub builder_private_key: SecretKey, - /// The deadline in the slot at which the sidecar will stop accepting - /// new commitments for the next block - pub commitment_deadline: Duration, /// The chain on which the sidecar is running - pub chain: Chain, + pub chain: ChainConfig, } impl Default for Config { @@ -210,8 +114,7 @@ impl Default for Config { builder_private_key: random_bls_secret(), limits: Limits::default(), validator_indexes: Vec::new(), - commitment_deadline: DEFAULT_COMMITMENT_DEADLINE, - chain: Chain::Mainnet, + chain: ChainConfig::default(), } } } @@ -282,10 +185,6 @@ impl TryFrom for Config { opts.jwt_hex }; - if let Some(deadline_ms) = opts.commitment_deadline { - config.commitment_deadline = Duration::from_millis(deadline_ms); - } - // Validate the JWT secret if config.jwt_hex.len() != 64 { eyre::bail!("JWT secret must be a 32 byte hex string"); @@ -298,10 +197,11 @@ impl TryFrom for Config { config.execution_api_url = opts.execution_api_url.trim_end_matches('/').to_string(); config.beacon_api_url = opts.beacon_api_url.trim_end_matches('/').to_string(); config.mevboost_url = opts.mevboost_url.trim_end_matches('/').to_string(); - config.chain = opts.chain; config.validator_indexes = opts.validator_indexes; + config.chain = opts.chain; + Ok(config) } } diff --git a/bolt-sidecar/src/config/signing.rs b/bolt-sidecar/src/config/signing.rs new file mode 100644 index 00000000..a3f70570 --- /dev/null +++ b/bolt-sidecar/src/config/signing.rs @@ -0,0 +1,16 @@ +use clap::{ArgGroup, Args}; + +/// Command-line options for signing +#[derive(Debug, Clone, Args)] +#[clap( + group = ArgGroup::new("signing-opts").required(true) + .args(&["private_key", "commit_boost_url"]) +)] +pub struct SigningOpts { + /// Private key to use for signing preconfirmation requests + #[clap(short = 'k', long)] + pub(super) private_key: Option, + /// URL for the commit-boost sidecar + #[clap(short = 'C', long, conflicts_with("private_key"))] + pub(super) commit_boost_url: Option, +} diff --git a/bolt-sidecar/src/lib.rs b/bolt-sidecar/src/lib.rs index 16bcec7f..d768e840 100644 --- a/bolt-sidecar/src/lib.rs +++ b/bolt-sidecar/src/lib.rs @@ -25,7 +25,7 @@ pub use builder::LocalBuilder; /// Configuration and command-line argument parsing mod config; -pub use config::{Chain, Config, Opts}; +pub use config::{ChainConfig, Config, Opts}; /// Crypto utilities, including BLS and ECDSA pub mod crypto; From 35cd0c63c8d6c09e6bbf244f4290bdcbc446ddf6 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 3 Jul 2024 17:45:06 +0200 Subject: [PATCH 2/2] chore: minor changes --- bolt-sidecar/src/config/chain.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 4b6beded..a58cd559 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -90,12 +90,7 @@ impl ChainConfig { /// Get the slot time for the given chain in seconds. pub fn slot_time(&self) -> u64 { - match self.chain { - Chain::Mainnet => DEFAULT_SLOT_TIME_IN_SECONDS, - Chain::Holesky => DEFAULT_SLOT_TIME_IN_SECONDS, - Chain::Helder => DEFAULT_SLOT_TIME_IN_SECONDS, - Chain::Kurtosis => self.slot_time_in_seconds, - } + self.slot_time_in_seconds } /// Get the domain for signing messages on the given chain. @@ -120,12 +115,7 @@ impl ChainConfig { /// Get the commitment deadline duration for the given chain. pub fn commitment_deadline(&self) -> Duration { - match self.chain { - Chain::Mainnet => Duration::from_millis(DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS), - Chain::Holesky => Duration::from_millis(DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS), - Chain::Helder => Duration::from_millis(DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS), - Chain::Kurtosis => Duration::from_millis(self.commitment_deadline), - } + Duration::from_millis(self.commitment_deadline) } }