From de50b359aa7c6d7a079aeef9b37aef59fdf82c72 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:43:20 +0200 Subject: [PATCH 1/9] feat: add commitment signing key --- bolt-sidecar/.gitignore | 3 +- bolt-sidecar/bin/sidecar.rs | 4 +- bolt-sidecar/src/common.rs | 40 ++++++++++++++++++- .../{signing.rs => constraint_signing.rs} | 22 +++++----- bolt-sidecar/src/config/mod.rs | 20 +++++++--- bolt-sidecar/src/driver.rs | 27 +++++++------ bolt-sidecar/src/signer/keystore.rs | 2 +- bolt-sidecar/src/signer/local.rs | 2 +- justfile | 16 ++++---- scripts/bolt_sidecar.service | 12 ------ scripts/bolt_sidecar_helder.service | 12 ++++++ scripts/bolt_sidecar_holesky.service | 12 ++++++ scripts/deploy_bolt_sidecar.sh | 27 ++++++++----- 13 files changed, 135 insertions(+), 64 deletions(-) rename bolt-sidecar/src/config/{signing.rs => constraint_signing.rs} (74%) delete mode 100644 scripts/bolt_sidecar.service create mode 100644 scripts/bolt_sidecar_helder.service create mode 100644 scripts/bolt_sidecar_holesky.service diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 8f74f83d..55509a80 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -1,3 +1,4 @@ target/ .env -.env.dev +.env.* +!.env.example diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 8beff9ba..c7fbf60c 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -18,14 +18,14 @@ async fn main() -> Result<()> { info!(chain = opts.chain.name(), "Starting Bolt sidecar"); - if opts.signing.private_key.is_some() { + if opts.constraint_signing.constraint_private_key.is_some() { match SidecarDriver::with_local_signer(&opts).await { Ok(driver) => driver.run_forever().await, Err(err) => { bail!("Failed to initialize the sidecar driver with local signer: {:?}", err) } } - } else if opts.signing.commit_boost_jwt_hex.is_some() { + } else if opts.constraint_signing.commit_boost_signer_url.is_some() { match SidecarDriver::with_commit_boost_signer(&opts).await { Ok(driver) => driver.run_forever().await, Err(err) => { diff --git a/bolt-sidecar/src/common.rs b/bolt-sidecar/src/common.rs index b8d6e80c..2fabb383 100644 --- a/bolt-sidecar/src/common.rs +++ b/bolt-sidecar/src/common.rs @@ -5,7 +5,7 @@ use std::{ path::Path, }; -use alloy::primitives::U256; +use alloy::{primitives::U256, signers::k256::ecdsa::SigningKey}; use blst::min_pk::SecretKey; use rand::{Rng, RngCore}; use reth_primitives::PooledTransactionsElement; @@ -139,6 +139,44 @@ impl fmt::Display for BlsSecretKeyWrapper { } } +#[derive(Clone, Debug)] +pub struct EcdsaSecretKeyWrapper(pub SigningKey); + +impl EcdsaSecretKeyWrapper { + /// Generate a new random ECDSA secret key. + #[allow(dead_code)] + pub fn random() -> Self { + Self(SigningKey::random(&mut rand::thread_rng())) + } +} + +impl<'de> Deserialize<'de> for EcdsaSecretKeyWrapper { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let sk = String::deserialize(deserializer)?; + Ok(EcdsaSecretKeyWrapper::from(sk.as_str())) + } +} + +impl From<&str> for EcdsaSecretKeyWrapper { + fn from(sk: &str) -> Self { + let hex_sk = sk.strip_prefix("0x").unwrap_or(sk); + let bytes = hex::decode(hex_sk).expect("valid hex"); + let sk = SigningKey::from_slice(&bytes).expect("valid sk"); + EcdsaSecretKeyWrapper(sk) + } +} + +impl Deref for EcdsaSecretKeyWrapper { + type Target = SigningKey; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[derive(Debug, Clone)] pub struct JwtSecretConfig(pub String); diff --git a/bolt-sidecar/src/config/signing.rs b/bolt-sidecar/src/config/constraint_signing.rs similarity index 74% rename from bolt-sidecar/src/config/signing.rs rename to bolt-sidecar/src/config/constraint_signing.rs index b542fa7f..2a8649f0 100644 --- a/bolt-sidecar/src/config/signing.rs +++ b/bolt-sidecar/src/config/constraint_signing.rs @@ -6,21 +6,21 @@ use serde::Deserialize; use crate::common::{BlsSecretKeyWrapper, JwtSecretConfig}; -/// Command-line options for signing +/// Command-line options for signing constraint messages #[derive(Args, Deserialize)] #[clap( group = ArgGroup::new("signing-opts").required(true) - .args(&["private_key", "commit_boost_address", "keystore_password", "keystore_secrets_path"]) + .args(&["constraint_private_key", "commit_boost_signer_url", "keystore_password", "keystore_secrets_path"]) )] -pub struct SigningOpts { - /// Private key to use for signing preconfirmation requests - #[clap(long, env = "BOLT_SIDECAR_PRIVATE_KEY")] - pub private_key: Option, +pub struct ConstraintSigningOpts { + /// Private key to use for signing constraint messages + #[clap(long, env = "BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY")] + pub constraint_private_key: Option, /// Socket address for the commit-boost sidecar #[clap(long, env = "BOLT_SIDECAR_CB_SIGNER_URL", requires("commit_boost_jwt_hex"))] - pub commit_boost_address: Option, + pub commit_boost_signer_url: Option, /// JWT in hexadecimal format for authenticating with the commit-boost service - #[clap(long, env = "BOLT_SIDECAR_CB_JWT_HEX", requires("commit_boost_address"))] + #[clap(long, env = "BOLT_SIDECAR_CB_JWT_HEX", requires("commit_boost_signer_url"))] pub commit_boost_jwt_hex: Option, /// The password for the ERC-2335 keystore. /// Reference: https://eips.ethereum.org/EIPS/eip-2335 @@ -39,11 +39,11 @@ pub struct SigningOpts { } // Implement Debug manually to hide the keystore_password field -impl fmt::Debug for SigningOpts { +impl fmt::Debug for ConstraintSigningOpts { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SigningOpts") - .field("private_key", &self.private_key) - .field("commit_boost_address", &self.commit_boost_address) + .field("constraint_private_key", &"********") // Hides the actual private key + .field("commit_boost_url", &self.commit_boost_signer_url) .field("commit_boost_jwt_hex", &self.commit_boost_jwt_hex) .field("keystore_password", &"********") // Hides the actual password .field("keystore_path", &self.keystore_path) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 93dfa069..f6b38f42 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -12,8 +12,8 @@ pub use validator_indexes::ValidatorIndexes; pub mod chain; pub use chain::ChainConfig; -pub mod signing; -pub use signing::SigningOpts; +pub mod constraint_signing; +pub use constraint_signing::ConstraintSigningOpts; pub mod telemetry; use telemetry::TelemetryOpts; @@ -21,7 +21,7 @@ use telemetry::TelemetryOpts; pub mod limits; use limits::LimitsOpts; -use crate::common::{BlsSecretKeyWrapper, JwtSecretConfig}; +use crate::common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}; /// Default port for the JSON-RPC server exposed by the sidecar. pub const DEFAULT_RPC_PORT: u16 = 8000; @@ -70,18 +70,22 @@ pub struct Opts { /// Secret BLS key to sign fallback payloads with (If not provided, a random key will be used) #[clap(long, env = "BOLT_SIDECAR_BUILDER_PRIVATE_KEY", default_value_t = BlsSecretKeyWrapper::random())] pub builder_private_key: BlsSecretKeyWrapper, + /// Secret ECDSA key to sign commitment messages with + #[clap(long, env = "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY")] + pub commitment_private_key: EcdsaSecretKeyWrapper, /// Operating limits for the sidecar #[clap(flatten)] pub limits: LimitsOpts, /// Chain config for the chain on which the sidecar is running #[clap(flatten)] pub chain: ChainConfig, - /// Commitment signing options. + /// Constraint signing options #[clap(flatten)] - pub signing: SigningOpts, + pub constraint_signing: ConstraintSigningOpts, /// Telemetry options #[clap(flatten)] pub telemetry: TelemetryOpts, + /// Additional unrecognized arguments. Useful for CI and testing /// to avoid issues on potential extra flags provided (e.g. "--exact" from cargo nextest). #[cfg(test)] @@ -105,6 +109,12 @@ impl Opts { mod tests { use super::*; + #[test] + fn test_validate_cli_flags() { + use clap::CommandFactory; + Opts::command().debug_assert(); + } + #[test] fn test_parse_url() { let url = "http://0.0.0.0:3030"; diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 8a1714c9..44fa366f 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -90,13 +90,17 @@ impl SidecarDriver { // Constraints are signed with a BLS private key let constraint_signer = SignerBLS::Local(LocalSigner::new( - opts.signing.private_key.clone().expect("local signer").0, + opts.constraint_signing + .constraint_private_key + .clone() + .expect("local constraint signing key") + .0, opts.chain, )); // Commitment responses are signed with a regular Ethereum wallet private key. // This is now generated randomly because slashing is not yet implemented. - let commitment_signer = PrivateKeySigner::random(); + let commitment_signer = PrivateKeySigner::from_slice("".as_bytes().to_vec().as_slice())?; Self::from_components(opts, constraint_signer, commitment_signer, state_client).await } @@ -108,18 +112,16 @@ impl SidecarDriver { // The default state client simply uses the execution API URL to fetch state updates. let state_client = StateClient::new(opts.execution_api_url.clone()); - let signing_opts = &opts.signing; - - let keystore = if let Some(psw) = signing_opts.keystore_password.as_ref() { + let keystore = if let Some(psw) = opts.constraint_signing.keystore_password.as_ref() { KeystoreSigner::from_password( - signing_opts.keystore_path.as_ref().expect("keystore path"), + opts.constraint_signing.keystore_path.as_ref().expect("keystore path"), psw.as_ref(), opts.chain, )? } else { KeystoreSigner::from_secrets_directory( - signing_opts.keystore_path.as_ref().expect("keystore path"), - signing_opts.keystore_secrets_path.as_ref().expect("keystore secrets path"), + opts.constraint_signing.keystore_path.as_ref().expect("keystore path"), + opts.constraint_signing.keystore_secrets_path.as_ref().expect("keystore secrets"), opts.chain, )? }; @@ -128,7 +130,8 @@ impl SidecarDriver { // Commitment responses are signed with a regular Ethereum wallet private key. // This is now generated randomly because slashing is not yet implemented. - let commitment_signer = PrivateKeySigner::random(); + let commitment_key = opts.commitment_private_key.0.clone(); + let commitment_signer = PrivateKeySigner::from_signing_key(commitment_key); Self::from_components(opts, keystore_signer, commitment_signer, state_client).await } @@ -141,8 +144,8 @@ impl SidecarDriver { let state_client = StateClient::new(opts.execution_api_url.clone()); let commit_boost_signer = CommitBoostSigner::new( - opts.signing.commit_boost_address.expect("CommitBoost URL").to_string(), - &opts.signing.commit_boost_jwt_hex.clone().expect("CommitBoost JWT"), + opts.constraint_signing.commit_boost_signer_url.expect("CommitBoost URL").to_string(), + &opts.constraint_signing.commit_boost_jwt_hex.clone().expect("CommitBoost JWT"), )?; let cb_bls_signer = SignerBLS::CommitBoost(commit_boost_signer.clone()); @@ -198,7 +201,7 @@ impl SidecarDriver { let mut constraints_client = ConstraintsClient::new(opts.constraints_url.clone()); // read the delegaitons from disk if they exist and add them to the constraints client - if let Some(delegations_file_path) = opts.signing.delegations_path.as_ref() { + if let Some(delegations_file_path) = opts.constraint_signing.delegations_path.as_ref() { let delegations = read_signed_delegations_from_file(delegations_file_path)?; constraints_client.add_delegations(delegations); } diff --git a/bolt-sidecar/src/signer/keystore.rs b/bolt-sidecar/src/signer/keystore.rs index fa8cfe7a..8487a1ce 100644 --- a/bolt-sidecar/src/signer/keystore.rs +++ b/bolt-sidecar/src/signer/keystore.rs @@ -141,7 +141,7 @@ impl KeystoreSigner { impl Debug for KeystoreSigner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Signer") + f.debug_struct("KeystoreSigner") .field( "pubkeys", &self.keypairs.iter().map(|kp| kp.pk.as_hex_string()).collect::>(), diff --git a/bolt-sidecar/src/signer/local.rs b/bolt-sidecar/src/signer/local.rs index 2ce97059..f7481b9d 100644 --- a/bolt-sidecar/src/signer/local.rs +++ b/bolt-sidecar/src/signer/local.rs @@ -28,7 +28,7 @@ pub struct LocalSigner { impl Debug for LocalSigner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Signer") + f.debug_struct("LocalSigner") .field("pubkey", &self.pubkey()) .field("chain", &self.chain.name()) .finish() diff --git a/justfile b/justfile index 54827ebc..79757179 100644 --- a/justfile +++ b/justfile @@ -172,20 +172,20 @@ _build-bolt-boost: cd bolt-boost && docker build -t ghcr.io/chainbound/bolt-boost:0.1.0 . --load # deploy the bolt sidecar to the dev server -deploy-sidecar-dev: - chmod +x ./scripts/deploy_bolt_sidecar.sh && ./scripts/deploy_bolt_sidecar.sh +deploy-sidecar-dev chain: + chmod +x ./scripts/deploy_bolt_sidecar.sh && ./scripts/deploy_bolt_sidecar.sh {{chain}} # Check the status of the sidecar service on the dev server -status-sidecar-dev: - ssh shared@remotebeast "sudo systemctl status bolt_sidecar" | less +status-sidecar-dev chain: + ssh shared@remotebeast "sudo systemctl status bolt_sidecar{{chain}}" | less # Tail the logs of the service on the dev server -logs-sidecar-dev: - ssh shared@remotebeast "journalctl -qu bolt_sidecar -f" +logs-sidecar-dev chain: + ssh shared@remotebeast "journalctl -qu bolt_sidecar_{{chain}} -f" # Stop the service on the dev server -stop-sidecar-dev: - ssh shared@remotebeast "sudo systemctl stop bolt_sidecar" +stop-sidecar-dev chain: + ssh shared@remotebeast "sudo systemctl stop bolt_sidecar_{{chain}}" # build and push the docker images to the github container registry with the provided tag diff --git a/scripts/bolt_sidecar.service b/scripts/bolt_sidecar.service deleted file mode 100644 index c8b1dedc..00000000 --- a/scripts/bolt_sidecar.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=Bolt Sidecar Development Service -After=network.target - -[Service] -User=shared -ExecStart=/usr/local/bin/bolt-sidecar -Restart=on-failure -EnvironmentFile=/home/shared/bolt_sidecar/.env.dev - -[Install] -WantedBy=multi-user.target diff --git a/scripts/bolt_sidecar_helder.service b/scripts/bolt_sidecar_helder.service new file mode 100644 index 00000000..1048f32b --- /dev/null +++ b/scripts/bolt_sidecar_helder.service @@ -0,0 +1,12 @@ +[Unit] +Description=Bolt Sidecar Development Service for Helder +After=network.target + +[Service] +User=shared +ExecStart=/usr/local/bin/bolt-sidecar-helder +Restart=on-failure +EnvironmentFile=/home/shared/helder/bolt_sidecar/.env.helder.dev + +[Install] +WantedBy=multi-user.target diff --git a/scripts/bolt_sidecar_holesky.service b/scripts/bolt_sidecar_holesky.service new file mode 100644 index 00000000..eedbceac --- /dev/null +++ b/scripts/bolt_sidecar_holesky.service @@ -0,0 +1,12 @@ +[Unit] +Description=Bolt Sidecar Development Service for Holesky +After=network.target + +[Service] +User=shared +ExecStart=/usr/local/bin/bolt-sidecar-holesky +Restart=on-failure +EnvironmentFile=/home/shared/holesky/bolt_sidecar/.env.holesky.dev + +[Install] +WantedBy=multi-user.target diff --git a/scripts/deploy_bolt_sidecar.sh b/scripts/deploy_bolt_sidecar.sh index 27f7105e..7baca2cd 100755 --- a/scripts/deploy_bolt_sidecar.sh +++ b/scripts/deploy_bolt_sidecar.sh @@ -4,21 +4,28 @@ # our remote dev server. Requirements: # - Access to Chainbound's Tailnet dev server "remotebeast" # - A .env.dev file in the bolt_sidecar directory, filled with the necessary vars +# and configured with the right chain configuration set -e -# check if ".env.dev" exists. if not, exit with error -test -f ./bolt-sidecar/.env.dev || (echo "No .env.dev file found. Exiting." && exit 1) +# check that the first argument is one of the supported chains: "helder" & "holesky" +if [ "$1" != "helder" ] && [ "$1" != "holesky" ]; then + echo "Invalid chain argument. Supported chains: helder, holesky" + exit 1 +fi + +# check if ".env.$1.dev" exists. if not, exit with error +test -f "./bolt-sidecar/.env.$1.dev" || (echo "No .env.$1.dev file found. Exiting." && exit 1) # copy the files to the remote dev server -rsync -av --exclude target --exclude .git ./bolt-sidecar/ shared@remotebeast:/home/shared/bolt_sidecar -rsync -av ./scripts/bolt_sidecar.service shared@remotebeast:/home/shared/bolt_sidecar/bolt_sidecar.service +rsync -av --exclude target --exclude .git ./bolt-sidecar/ shared@remotebeast:/home/shared/$1/bolt_sidecar +rsync -av ./scripts/bolt_sidecar_$1.service shared@remotebeast:/home/shared/$1/bolt_sidecar/bolt_sidecar_$1.service # build the project on the remote dev server -ssh shared@remotebeast "cd ~/bolt_sidecar && CC=clang ~/.cargo/bin/cargo build --release" -ssh shared@remotebeast "mv ~/bolt_sidecar/target/release/bolt-sidecar /usr/local/bin/bolt-sidecar || true" -ssh shared@remotebeast "cp -f ~/bolt_sidecar/bolt_sidecar.service /etc/systemd/system/bolt_sidecar.service" -ssh shared@remotebeast "sudo systemctl daemon-reload && sudo systemctl enable bolt_sidecar" -ssh shared@remotebeast "sudo systemctl restart bolt_sidecar" +ssh shared@remotebeast "cd ~/$1/bolt_sidecar && CC=clang ~/.cargo/bin/cargo build --release" +ssh shared@remotebeast "mv ~/$1/bolt_sidecar/target/release/bolt-sidecar /usr/local/bin/bolt-sidecar-$1 || true" +ssh shared@remotebeast "cp -f ~/$1/bolt_sidecar/bolt_sidecar_$1.service /etc/systemd/system/bolt_sidecar_$1.service" +ssh shared@remotebeast "sudo systemctl daemon-reload && sudo systemctl enable bolt_sidecar_$1" +ssh shared@remotebeast "sudo systemctl restart bolt_sidecar_$1" -echo "Deployed bolt_sidecar successfully" +echo "Deployed bolt_sidecar_$1 successfully" From d81c4e01895c3a70e8a12d54e8b5eb1bd98e6a3d Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:48:31 +0200 Subject: [PATCH 2/9] chore: typo --- bolt-sidecar/src/config/constraint_signing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bolt-sidecar/src/config/constraint_signing.rs b/bolt-sidecar/src/config/constraint_signing.rs index 2a8649f0..708644a0 100644 --- a/bolt-sidecar/src/config/constraint_signing.rs +++ b/bolt-sidecar/src/config/constraint_signing.rs @@ -43,7 +43,7 @@ impl fmt::Debug for ConstraintSigningOpts { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SigningOpts") .field("constraint_private_key", &"********") // Hides the actual private key - .field("commit_boost_url", &self.commit_boost_signer_url) + .field("commit_boost_signer_url", &self.commit_boost_signer_url) .field("commit_boost_jwt_hex", &self.commit_boost_jwt_hex) .field("keystore_password", &"********") // Hides the actual password .field("keystore_path", &self.keystore_path) From 7088d85f4b3839ff4d077bbbd868f36fff63a65c Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:40:14 +0200 Subject: [PATCH 3/9] chore: rename vars --- bolt-sidecar/.env.example | 17 +++++++++++------ bolt-sidecar/src/builder/payload_builder.rs | 2 +- bolt-sidecar/src/config/mod.rs | 14 +++++++++----- bolt-sidecar/src/driver.rs | 4 ++-- bolt-sidecar/src/test_util.rs | 2 +- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index a7ed8591..cb234a59 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,10 +1,16 @@ - -# node + PBS URLs +# Ethereum node connections 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_ENGINE_JWT_HEX= + +# Constraint URL: should point to the constraint API sidecar. +# Usually this corresponds to `mev-boost` or `bolt-boost` BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 + +# Commit-boost specific options (optional) BOLT_SIDECAR_CB_SIGNER_URL=http://localhost:19551 +BOLT_SIDECAR_CB_JWT_HEX= # server ports BOLT_SIDECAR_PORT=8000 @@ -15,14 +21,13 @@ BOLT_SIDECAR_MAX_COMMITMENTS=128 BOLT_SIDECAR_MAX_COMMITTED_GAS=10000000 # chain configs -BOLT_SIDECAR_CHAIN=helder +BOLT_SIDECAR_CHAIN=holesky BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 BOLT_SIDECAR_SLOT_TIME=12 # sidecar security configs BOLT_SIDECAR_VALIDATOR_INDEXES= -BOLT_SIDECAR_JWT_HEX= -BOLT_SIDECAR_CB_JWT_HEX= BOLT_SIDECAR_FEE_RECIPIENT= BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -BOLT_SIDECAR_PRIVATE_KEY= +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= diff --git a/bolt-sidecar/src/builder/payload_builder.rs b/bolt-sidecar/src/builder/payload_builder.rs index 1dfe2e9f..c8338262 100644 --- a/bolt-sidecar/src/builder/payload_builder.rs +++ b/bolt-sidecar/src/builder/payload_builder.rs @@ -57,7 +57,7 @@ impl FallbackPayloadBuilder { pub fn new(config: &Opts, beacon_api_client: BeaconClient, genesis_time: u64) -> Self { let engine_hinter = EngineHinter { client: reqwest::Client::new(), - jwt_hex: config.jwt_hex.to_string(), + jwt_hex: config.engine_jwt_hex.to_string(), engine_rpc_url: config.engine_api_url.clone(), }; diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index f6b38f42..1d11f680 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -46,8 +46,12 @@ pub struct Opts { #[clap(long, env = "BOLT_SIDECAR_ENGINE_API_URL", default_value = "http://localhost:8551")] pub engine_api_url: Url, /// URL for the Constraint sidecar client to use - #[clap(long, env = "BOLT_SIDECAR_CONSTRAINTS_URL", default_value = "http://localhost:3030")] - pub constraints_url: Url, + #[clap( + long, + env = "BOLT_SIDECAR_CONSTRAINTS_API_URL", + default_value = "http://localhost:3030" + )] + pub constraints_api_url: Url, /// Constraint proxy server port to use #[clap(long, env = "BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT", default_value_t = DEFAULT_CONSTRAINTS_PROXY_PORT)] pub constraints_proxy_port: u16, @@ -62,8 +66,8 @@ pub struct Opts { /// /// It can either be a hex-encoded string or a file path to a file /// containing the hex-encoded secret. - #[clap(long, env = "BOLT_SIDECAR_JWT_HEX", default_value_t)] - pub jwt_hex: JwtSecretConfig, + #[clap(long, env = "BOLT_SIDECAR_ENGINE_JWT_HEX", default_value_t)] + pub engine_jwt_hex: JwtSecretConfig, /// The fee recipient address for fallback blocks #[clap(long, env = "BOLT_SIDECAR_FEE_RECIPIENT", default_value_t = Address::ZERO)] pub fee_recipient: Address, @@ -133,7 +137,7 @@ mod tests { assert_eq!(config.execution_api_url, Url::parse("http://localhost:8545").unwrap()); assert_eq!(config.beacon_api_url, Url::parse("http://localhost:5052").unwrap()); assert_eq!(config.engine_api_url, Url::parse("http://localhost:8551").unwrap()); - assert_eq!(config.constraints_url, Url::parse("http://localhost:3030").unwrap()); + assert_eq!(config.constraints_api_url, Url::parse("http://localhost:3030").unwrap()); assert_eq!(config.constraints_proxy_port, 18551); } } diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 44fa366f..370abd2e 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -181,7 +181,7 @@ impl SidecarDriver { let (payload_requests_tx, payload_requests_rx) = mpsc::channel(16); let builder_proxy_cfg = BuilderProxyConfig { - constraints_url: opts.constraints_url.clone(), + constraints_url: opts.constraints_api_url.clone(), server_port: opts.constraints_proxy_port, }; @@ -198,7 +198,7 @@ impl SidecarDriver { let (api_events_tx, api_events_rx) = mpsc::channel(1024); CommitmentsApiServer::new(api_addr).run(api_events_tx).await; - let mut constraints_client = ConstraintsClient::new(opts.constraints_url.clone()); + let mut constraints_client = ConstraintsClient::new(opts.constraints_api_url.clone()); // read the delegaitons from disk if they exist and add them to the constraints client if let Some(delegations_file_path) = opts.constraint_signing.delegations_path.as_ref() { diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index b4dbf40c..4a71746c 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -99,7 +99,7 @@ pub(crate) async fn get_test_config() -> Option { if let Some(url) = try_get_engine_api_url().await { opts.engine_api_url = url.parse().expect("valid URL"); } - opts.jwt_hex = JwtSecretConfig(jwt); + opts.engine_jwt_hex = JwtSecretConfig(jwt); Some(opts) } From 1dbc11af2137356002e54847106187e8bb427a0c Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:43:51 +0200 Subject: [PATCH 4/9] chore: rm default flags --- bolt-sidecar/src/config/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 1d11f680..e2168ae9 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -53,7 +53,11 @@ pub struct Opts { )] pub constraints_api_url: Url, /// Constraint proxy server port to use - #[clap(long, env = "BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT", default_value_t = DEFAULT_CONSTRAINTS_PROXY_PORT)] + #[clap( + long, + env = "BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT", + default_value_t = DEFAULT_CONSTRAINTS_PROXY_PORT + )] pub constraints_proxy_port: u16, /// Validator indexes of connected validators that the sidecar /// should accept commitments on behalf of. Accepted values: @@ -66,13 +70,13 @@ pub struct Opts { /// /// It can either be a hex-encoded string or a file path to a file /// containing the hex-encoded secret. - #[clap(long, env = "BOLT_SIDECAR_ENGINE_JWT_HEX", default_value_t)] + #[clap(long, env = "BOLT_SIDECAR_ENGINE_JWT_HEX")] pub engine_jwt_hex: JwtSecretConfig, /// The fee recipient address for fallback blocks - #[clap(long, env = "BOLT_SIDECAR_FEE_RECIPIENT", default_value_t = Address::ZERO)] + #[clap(long, env = "BOLT_SIDECAR_FEE_RECIPIENT")] pub fee_recipient: Address, /// Secret BLS key to sign fallback payloads with (If not provided, a random key will be used) - #[clap(long, env = "BOLT_SIDECAR_BUILDER_PRIVATE_KEY", default_value_t = BlsSecretKeyWrapper::random())] + #[clap(long, env = "BOLT_SIDECAR_BUILDER_PRIVATE_KEY")] pub builder_private_key: BlsSecretKeyWrapper, /// Secret ECDSA key to sign commitment messages with #[clap(long, env = "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY")] From 6e1cdc7cc71b4e948fb9be1f48541d04e21d38ac Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:47:25 +0200 Subject: [PATCH 5/9] chore: test_util fix --- bolt-sidecar/src/test_util.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index 4a71746c..8ae057a2 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -1,3 +1,5 @@ +use std::env; + use alloy::{ eips::eip2718::Encodable2718, network::{EthereumWallet, TransactionBuilder}, @@ -79,13 +81,16 @@ pub(crate) async fn try_get_beacon_api_url() -> Option<&'static str> { /// /// If any of the above values can't be found, the function will return `None`. pub(crate) async fn get_test_config() -> Option { - std::env::set_var("BOLT_SIDECAR_PRIVATE_KEY", BlsSecretKeyWrapper::random().to_string()); + env::set_var("BOLT_SIDECAR_PRIVATE_KEY", BlsSecretKeyWrapper::random().to_string()); + env::set_var("BOLT_SIDECAR_JWT_HEX", JwtSecretConfig::default().to_string()); + env::set_var("BOLT_SIDECAR_FEE_RECIPIENT", Address::ZERO.to_string()); + env::set_var("BOLT_SIDECAR_BUILDER_PRIVATE_KEY", BlsSecretKeyWrapper::random().to_string()); let _ = dotenvy::dotenv(); let mut opts = Opts::parse(); - let Some(jwt) = std::env::var("ENGINE_JWT").ok() else { + let Some(jwt) = env::var("ENGINE_JWT").ok() else { warn!("ENGINE_JWT not found in environment variables"); return None; }; From b9ff3e04c8d06b707042e98f53dad99d3304aca8 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:02:50 +0200 Subject: [PATCH 6/9] chore: fix tests --- bolt-sidecar/src/common.rs | 6 ++++++ bolt-sidecar/src/test_util.rs | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/common.rs b/bolt-sidecar/src/common.rs index 2fabb383..439816cf 100644 --- a/bolt-sidecar/src/common.rs +++ b/bolt-sidecar/src/common.rs @@ -169,6 +169,12 @@ impl From<&str> for EcdsaSecretKeyWrapper { } } +impl Display for EcdsaSecretKeyWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.to_bytes())) + } +} + impl Deref for EcdsaSecretKeyWrapper { type Target = SigningKey; diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index 8ae057a2..6eace374 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -20,7 +20,7 @@ use secp256k1::Message; use tracing::warn; use crate::{ - common::{BlsSecretKeyWrapper, JwtSecretConfig}, + common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}, crypto::{ecdsa::SignableECDSA, SignableBLS}, primitives::{ CommitmentRequest, ConstraintsMessage, DelegationMessage, FullTransaction, @@ -82,9 +82,14 @@ pub(crate) async fn try_get_beacon_api_url() -> Option<&'static str> { /// If any of the above values can't be found, the function will return `None`. pub(crate) async fn get_test_config() -> Option { env::set_var("BOLT_SIDECAR_PRIVATE_KEY", BlsSecretKeyWrapper::random().to_string()); - env::set_var("BOLT_SIDECAR_JWT_HEX", JwtSecretConfig::default().to_string()); + env::set_var("BOLT_SIDECAR_ENGINE_JWT_HEX", JwtSecretConfig::default().to_string()); env::set_var("BOLT_SIDECAR_FEE_RECIPIENT", Address::ZERO.to_string()); env::set_var("BOLT_SIDECAR_BUILDER_PRIVATE_KEY", BlsSecretKeyWrapper::random().to_string()); + env::set_var("BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY", BlsSecretKeyWrapper::random().to_string()); + env::set_var( + "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY", + EcdsaSecretKeyWrapper::random().to_string(), + ); let _ = dotenvy::dotenv(); From 6560237569cc4be92cd253d5a2e622c5ab8648cd Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:29:36 +0200 Subject: [PATCH 7/9] feat: parse test config --- bolt-sidecar/Config.example.toml | 20 +++++++++++++++++--- bolt-sidecar/src/config/mod.rs | 28 ++++++++++++---------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index 05f97692..b73e925d 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -6,15 +6,29 @@ metrics_port = 3300 execution_api_url = "http://localhost:8545" beacon_api_url = "http://localhost:5052" engine_api_url = "http://localhost:8551" +engine_jwt_hex = "0x573300d0cd9a8e253429998a3ceecf358aa4868d96772d1344a80144d3b7b593" # constraints options -constraints_url = "http://localhost:3030" +constraints_api_url = "http://localhost:3030" constraints_proxy_port = 18551 +validator_indexes = "0..64" +fee_recipient = "0x0155ef0C0fE550C297c1216585e0DE1478EA30e4" + +builder_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +commitment_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" + # chain options -chain = "kurtosis" +[chain] +chain = "Kurtosis" slot_time = 2 +commitment_deadline = 8000 + +[telemetry] +metrics_port = 3300 +disable_metrics = false # signing options -private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +[constraint_signing] +constraint_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" delegations_path = "./delegations.json" diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index e2168ae9..e72e73ae 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -1,4 +1,4 @@ -use std::{fs::File, io::Read}; +use std::fs; use alloy::primitives::Address; use clap::Parser; @@ -83,6 +83,7 @@ pub struct Opts { pub commitment_private_key: EcdsaSecretKeyWrapper, /// Operating limits for the sidecar #[clap(flatten)] + #[serde(default)] pub limits: LimitsOpts, /// Chain config for the chain on which the sidecar is running #[clap(flatten)] @@ -98,17 +99,14 @@ pub struct Opts { /// to avoid issues on potential extra flags provided (e.g. "--exact" from cargo nextest). #[cfg(test)] #[clap(allow_hyphen_values = true)] + #[serde(default)] pub extra_args: Vec, } impl Opts { /// Parse the configuration from a TOML file. pub fn parse_from_toml(file_path: &str) -> eyre::Result { - let mut file = File::open(file_path).wrap_err("Unable to open file")?; - - let mut contents = String::new(); - file.read_to_string(&mut contents).wrap_err("Unable to read file")?; - + let contents = fs::read_to_string(file_path).wrap_err("Unable to read file")?; toml::from_str(&contents).wrap_err("Error parsing the TOML file") } } @@ -134,15 +132,13 @@ mod tests { #[test] fn test_parse_config_from_toml() { - let path = env!("CARGO_MANIFEST_DIR").to_string() + "Config.toml"; - - if let Ok(config_file) = std::fs::read_to_string(path) { - let config = Opts::parse_from_toml(&config_file).expect("Failed to parse config"); - assert_eq!(config.execution_api_url, Url::parse("http://localhost:8545").unwrap()); - assert_eq!(config.beacon_api_url, Url::parse("http://localhost:5052").unwrap()); - assert_eq!(config.engine_api_url, Url::parse("http://localhost:8551").unwrap()); - assert_eq!(config.constraints_api_url, Url::parse("http://localhost:3030").unwrap()); - assert_eq!(config.constraints_proxy_port, 18551); - } + let path = env!("CARGO_MANIFEST_DIR").to_string() + "/Config.example.toml"; + + let config = Opts::parse_from_toml(&path).expect("Failed to parse config from TOML"); + assert_eq!(config.execution_api_url, Url::parse("http://localhost:8545").unwrap()); + assert_eq!(config.beacon_api_url, Url::parse("http://localhost:5052").unwrap()); + assert_eq!(config.engine_api_url, Url::parse("http://localhost:8551").unwrap()); + assert_eq!(config.constraints_api_url, Url::parse("http://localhost:3030").unwrap()); + assert_eq!(config.constraints_proxy_port, 18551); } } From 168188366355c986e915a2ef5900de41c9e84f78 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:54:59 +0200 Subject: [PATCH 8/9] feat: addressed review --- bolt-sidecar/src/config/constraint_signing.rs | 5 +++-- bolt-sidecar/src/config/mod.rs | 6 ++++-- bolt-sidecar/src/driver.rs | 7 +++---- bolt-sidecar/src/signer/commit_boost.rs | 21 +++++++++++++------ justfile | 2 +- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/bolt-sidecar/src/config/constraint_signing.rs b/bolt-sidecar/src/config/constraint_signing.rs index 708644a0..a8712dfd 100644 --- a/bolt-sidecar/src/config/constraint_signing.rs +++ b/bolt-sidecar/src/config/constraint_signing.rs @@ -1,7 +1,8 @@ -use std::{fmt, net::SocketAddr, path::PathBuf}; +use std::{fmt, path::PathBuf}; use clap::{ArgGroup, Args}; use lighthouse_account_utils::ZeroizeString; +use reqwest::Url; use serde::Deserialize; use crate::common::{BlsSecretKeyWrapper, JwtSecretConfig}; @@ -18,7 +19,7 @@ pub struct ConstraintSigningOpts { pub constraint_private_key: Option, /// Socket address for the commit-boost sidecar #[clap(long, env = "BOLT_SIDECAR_CB_SIGNER_URL", requires("commit_boost_jwt_hex"))] - pub commit_boost_signer_url: Option, + pub commit_boost_signer_url: Option, /// JWT in hexadecimal format for authenticating with the commit-boost service #[clap(long, env = "BOLT_SIDECAR_CB_JWT_HEX", requires("commit_boost_signer_url"))] pub commit_boost_jwt_hex: Option, diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index e72e73ae..af325600 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -45,14 +45,16 @@ pub struct Opts { /// Execution client Engine API URL #[clap(long, env = "BOLT_SIDECAR_ENGINE_API_URL", default_value = "http://localhost:8551")] pub engine_api_url: Url, - /// URL for the Constraint sidecar client to use + /// URL to forward the constraints produced by the Bolt sidecar to a server supporting the + /// Constraints API, such as an MEV-Boost fork. #[clap( long, env = "BOLT_SIDECAR_CONSTRAINTS_API_URL", default_value = "http://localhost:3030" )] pub constraints_api_url: Url, - /// Constraint proxy server port to use + /// The port from which the Bolt sidecar will receive Builder-API requests from the + /// Beacon client #[clap( long, env = "BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT", diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 370abd2e..2c433eed 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -99,8 +99,8 @@ impl SidecarDriver { )); // Commitment responses are signed with a regular Ethereum wallet private key. - // This is now generated randomly because slashing is not yet implemented. - let commitment_signer = PrivateKeySigner::from_slice("".as_bytes().to_vec().as_slice())?; + let commitment_key = opts.commitment_private_key.0.clone(); + let commitment_signer = PrivateKeySigner::from_signing_key(commitment_key); Self::from_components(opts, constraint_signer, commitment_signer, state_client).await } @@ -129,7 +129,6 @@ impl SidecarDriver { let keystore_signer = SignerBLS::Keystore(keystore); // Commitment responses are signed with a regular Ethereum wallet private key. - // This is now generated randomly because slashing is not yet implemented. let commitment_key = opts.commitment_private_key.0.clone(); let commitment_signer = PrivateKeySigner::from_signing_key(commitment_key); @@ -144,7 +143,7 @@ impl SidecarDriver { let state_client = StateClient::new(opts.execution_api_url.clone()); let commit_boost_signer = CommitBoostSigner::new( - opts.constraint_signing.commit_boost_signer_url.expect("CommitBoost URL").to_string(), + opts.constraint_signing.commit_boost_signer_url.clone().expect("CommitBoost URL"), &opts.constraint_signing.commit_boost_jwt_hex.clone().expect("CommitBoost JWT"), )?; diff --git a/bolt-sidecar/src/signer/commit_boost.rs b/bolt-sidecar/src/signer/commit_boost.rs index d3c81a16..9820373d 100644 --- a/bolt-sidecar/src/signer/commit_boost.rs +++ b/bolt-sidecar/src/signer/commit_boost.rs @@ -8,6 +8,7 @@ use cb_common::{ use commit_boost::prelude::SignProxyRequest; use ethereum_consensus::crypto::bls::PublicKey as BlsPublicKey; use parking_lot::RwLock; +use reqwest::Url; use thiserror::Error; use tracing::{debug, error, info}; @@ -34,15 +35,23 @@ pub enum CommitBoostError { #[error("failed to create signer client: {0}")] SignerClientError(#[from] SignerClientError), #[error("error in commit boost signer: {0}")] - Other(#[from] eyre::Report), + Other(String), } #[allow(unused)] impl CommitBoostSigner { /// Create a new [CommitBoostSigner] instance - pub fn new(signer_server_address: String, jwt: &str) -> SignerResult { - let signer_client = - SignerClient::new(signer_server_address, jwt).map_err(CommitBoostError::Other)?; + pub fn new(signer_url: Url, jwt: &str) -> SignerResult { + let Some(hostname) = signer_url.host_str() else { + return Err(CommitBoostError::Other("Invalid signer host".to_string()).into()); + }; + + let signer_server_address = format!("{}:{}", hostname, signer_url.port().unwrap_or(80)); + + let signer_client = match SignerClient::new(signer_server_address, jwt) { + Ok(client) => client, + Err(e) => return Err(CommitBoostError::Other(e.to_string()).into()), + }; let client = Self { signer_client, @@ -178,7 +187,7 @@ mod test { return Ok(()); } }; - let signer = CommitBoostSigner::new(signer_server_address, &jwt_hex).unwrap(); + let signer = CommitBoostSigner::new(signer_server_address.parse()?, &jwt_hex).unwrap(); // Generate random data for the test let mut rng = rand::thread_rng(); @@ -208,7 +217,7 @@ mod test { return Ok(()); } }; - let signer = CommitBoostSigner::new(signer_server_address, &jwt_hex).unwrap(); + let signer = CommitBoostSigner::new(signer_server_address.parse()?, &jwt_hex).unwrap(); let pubkey = signer.get_proxy_ecdsa_pubkey(); // Generate random data for the test diff --git a/justfile b/justfile index 79757179..974344ec 100644 --- a/justfile +++ b/justfile @@ -177,7 +177,7 @@ deploy-sidecar-dev chain: # Check the status of the sidecar service on the dev server status-sidecar-dev chain: - ssh shared@remotebeast "sudo systemctl status bolt_sidecar{{chain}}" | less + ssh shared@remotebeast "sudo systemctl status bolt_sidecar_{{chain}}" | less # Tail the logs of the service on the dev server logs-sidecar-dev chain: From 13de5771f2443e069b473e03b5e169161419a805 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:13:05 +0200 Subject: [PATCH 9/9] feat: addressed review --- bolt-sidecar/src/config/constraint_signing.rs | 2 +- bolt-sidecar/src/signer/commit_boost.rs | 36 +++++++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/bolt-sidecar/src/config/constraint_signing.rs b/bolt-sidecar/src/config/constraint_signing.rs index a8712dfd..50356f69 100644 --- a/bolt-sidecar/src/config/constraint_signing.rs +++ b/bolt-sidecar/src/config/constraint_signing.rs @@ -17,7 +17,7 @@ pub struct ConstraintSigningOpts { /// Private key to use for signing constraint messages #[clap(long, env = "BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY")] pub constraint_private_key: Option, - /// Socket address for the commit-boost sidecar + /// URL for the commit-boost sidecar #[clap(long, env = "BOLT_SIDECAR_CB_SIGNER_URL", requires("commit_boost_jwt_hex"))] pub commit_boost_signer_url: Option, /// JWT in hexadecimal format for authenticating with the commit-boost service diff --git a/bolt-sidecar/src/signer/commit_boost.rs b/bolt-sidecar/src/signer/commit_boost.rs index 9820373d..e15aec08 100644 --- a/bolt-sidecar/src/signer/commit_boost.rs +++ b/bolt-sidecar/src/signer/commit_boost.rs @@ -35,23 +35,15 @@ pub enum CommitBoostError { #[error("failed to create signer client: {0}")] SignerClientError(#[from] SignerClientError), #[error("error in commit boost signer: {0}")] - Other(String), + Other(eyre::Report), } #[allow(unused)] impl CommitBoostSigner { /// Create a new [CommitBoostSigner] instance pub fn new(signer_url: Url, jwt: &str) -> SignerResult { - let Some(hostname) = signer_url.host_str() else { - return Err(CommitBoostError::Other("Invalid signer host".to_string()).into()); - }; - - let signer_server_address = format!("{}:{}", hostname, signer_url.port().unwrap_or(80)); - - let signer_client = match SignerClient::new(signer_server_address, jwt) { - Ok(client) => client, - Err(e) => return Err(CommitBoostError::Other(e.to_string()).into()), - }; + let socket_addr = parse_address_from_url(signer_url).map_err(CommitBoostError::Other)?; + let signer_client = SignerClient::new(socket_addr, jwt).map_err(CommitBoostError::Other)?; let client = Self { signer_client, @@ -167,12 +159,34 @@ impl SignerECDSA for CommitBoostSigner { } } +fn parse_address_from_url(url: Url) -> eyre::Result { + let str = url.as_str(); + + // take the host out of the URL, e.g. "http://localhost:425" -> localhost:425 + // and also "remotehost:2425" -> remotehost:2425 + let without_base = url.as_str().split("://").last().unwrap_or(str); + let hostname = without_base.split(':').next().unwrap_or(without_base); + let port = without_base.split(':').last().ok_or_else(|| eyre::eyre!("No port found"))?; + let port = port.trim_end_matches('/'); + + Ok(format!("{}:{}", hostname, port)) +} + #[cfg(test)] mod test { use super::*; use rand::Rng; use tracing::warn; + #[test] + fn test_url_parse_address() { + let url = Url::parse("http://localhost:8080").unwrap(); + assert_eq!(parse_address_from_url(url).unwrap(), "localhost:8080"); + + let url = Url::parse("remotehost:2425").unwrap(); + assert_eq!(parse_address_from_url(url).unwrap(), "remotehost:2425"); + } + #[tokio::test] async fn test_bls_commit_boost_signer() -> eyre::Result<()> { let _ = dotenvy::dotenv();