From 90cafdf70cac2516c5e4a00f27a8b23bfde4a916 Mon Sep 17 00:00:00 2001 From: fbrv Date: Thu, 1 Aug 2024 17:11:24 +0100 Subject: [PATCH] error handling (#45) * better error handling --------- Co-authored-by: fbrv --- Cargo.toml | 1 + bin/Cargo.toml | 2 + bin/commit_boost.rs | 4 +- bin/default_pbs.rs | 8 ++- bin/signer_module.rs | 9 ++-- crates/cli/src/docker_cmd.rs | 8 +-- crates/cli/src/docker_init.rs | 7 +-- crates/common/src/commit/client.rs | 10 ++-- crates/common/src/commit/request.rs | 5 +- crates/common/src/config.rs | 80 +++++++++++++++-------------- crates/common/src/error.rs | 58 +++++++++++++++++++++ crates/common/src/lib.rs | 1 + crates/common/src/loader.rs | 26 +++++----- crates/common/src/pbs/types.rs | 16 +++--- crates/common/src/signature.rs | 11 ++-- crates/common/src/signer.rs | 12 +++-- crates/metrics/src/provider.rs | 11 ++-- crates/pbs/src/error.rs | 3 +- crates/pbs/src/service.rs | 5 +- crates/signer/src/manager.rs | 4 +- crates/signer/src/service.rs | 10 ++-- examples/custom_boost.rs | 2 + examples/da_commit/Cargo.toml | 1 + examples/da_commit/src/main.rs | 14 ++--- tests/src/mock_validator.rs | 4 +- tests/src/utils.rs | 3 +- tests/tests/config.rs | 8 +-- tests/tests/pbs_integration.rs | 48 +++++++++-------- 28 files changed, 233 insertions(+), 138 deletions(-) create mode 100644 crates/common/src/error.rs diff --git a/Cargo.toml b/Cargo.toml index c3d5ca5..908dcf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ bollard = "0.16.1" # misc clap = { version = "4.5.4", features = ["derive", "env"] } thiserror = "1.0.61" +color-eyre = "0.6.3" eyre = "0.6.12" url = "2.5.0" uuid = { version = "1.8.0", features = ["v4", "fast-rng", "serde"] } diff --git a/bin/Cargo.toml b/bin/Cargo.toml index fad9ff0..e4d727d 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml @@ -28,6 +28,8 @@ tracing-subscriber.workspace = true # misc clap.workspace = true +eyre.workspace = true +color-eyre.workspace = true [[bin]] name = "commit-boost" diff --git a/bin/commit_boost.rs b/bin/commit_boost.rs index ed55869..e2ac2be 100644 --- a/bin/commit_boost.rs +++ b/bin/commit_boost.rs @@ -2,7 +2,8 @@ use clap::Parser; /// Main entry point of the Commit Boost CLI #[tokio::main] -async fn main() { +async fn main() -> eyre::Result<()> { + color_eyre::install()?; // set default backtrace unless provided if std::env::var_os("RUST_BACKTRACE").is_none() { std::env::set_var("RUST_BACKTRACE", "1"); @@ -14,4 +15,5 @@ async fn main() { eprintln!("Error: {err}"); std::process::exit(1) }; + Ok(()) } diff --git a/bin/default_pbs.rs b/bin/default_pbs.rs index a03b792..0e5bf8a 100644 --- a/bin/default_pbs.rs +++ b/bin/default_pbs.rs @@ -1,8 +1,11 @@ use cb_common::{config::load_pbs_config, utils::initialize_tracing_log}; use cb_pbs::{DefaultBuilderApi, PbsService, PbsState}; +use eyre::Result; #[tokio::main] -async fn main() { +async fn main() -> Result<()> { + color_eyre::install()?; + // set default backtrace unless provided if std::env::var_os("RUST_BACKTRACE").is_none() { std::env::set_var("RUST_BACKTRACE", "1"); @@ -14,6 +17,7 @@ async fn main() { let pbs_config = load_pbs_config().expect("failed to load pbs config"); let state = PbsState::<()>::new(pbs_config); - PbsService::init_metrics(); + PbsService::init_metrics()?; PbsService::run::<(), DefaultBuilderApi>(state).await; + Ok(()) } diff --git a/bin/signer_module.rs b/bin/signer_module.rs index c848544..c1c9ea7 100644 --- a/bin/signer_module.rs +++ b/bin/signer_module.rs @@ -1,8 +1,11 @@ use cb_common::{config::StartSignerConfig, utils::initialize_tracing_log}; use cb_signer::service::SigningService; +use eyre::Result; #[tokio::main] -async fn main() { +async fn main() -> Result<()> { + color_eyre::install()?; + // set default backtrace unless provided if std::env::var_os("RUST_BACKTRACE").is_none() { std::env::set_var("RUST_BACKTRACE", "1"); @@ -10,6 +13,6 @@ async fn main() { initialize_tracing_log(); - let config = StartSignerConfig::load_from_env(); - SigningService::run(config).await; + let config = StartSignerConfig::load_from_env()?; + SigningService::run(config).await } diff --git a/crates/cli/src/docker_cmd.rs b/crates/cli/src/docker_cmd.rs index 1fdcd36..6c68642 100644 --- a/crates/cli/src/docker_cmd.rs +++ b/crates/cli/src/docker_cmd.rs @@ -1,6 +1,8 @@ use std::process::{Command, Stdio}; -pub fn handle_docker_start(compose_path: String, env_path: String) -> eyre::Result<()> { +use eyre::Result; + +pub fn handle_docker_start(compose_path: String, env_path: String) -> Result<()> { println!("Starting Commit-Boost with compose file: {}", compose_path); // load env file @@ -24,7 +26,7 @@ pub fn handle_docker_start(compose_path: String, env_path: String) -> eyre::Resu Ok(()) } -pub fn handle_docker_stop(compose_path: String, env_path: String) -> eyre::Result<()> { +pub fn handle_docker_stop(compose_path: String, env_path: String) -> Result<()> { println!("Stopping Commit-Boost with compose file: {}", compose_path); // load env file @@ -46,7 +48,7 @@ pub fn handle_docker_stop(compose_path: String, env_path: String) -> eyre::Resul } // TODO: we shouldnt use docker logs -pub fn handle_docker_logs(compose_path: String) -> eyre::Result<()> { +pub fn handle_docker_logs(compose_path: String) -> Result<()> { println!("Querying Commit-Boost with compose file: {}", compose_path); // TODO: if permission denied, print warning to run as sudo diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index 7cfa0a7..fc9636f 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -13,6 +13,7 @@ use docker_compose_types::{ Compose, DependsOnOptions, Environment, LoggingParameters, MapOrEmpty, NetworkSettings, Networks, Ports, Service, Services, SingleValue, Volumes, }; +use eyre::Result; use indexmap::IndexMap; use serde::Serialize; @@ -28,10 +29,10 @@ const SIGNER_NETWORK: &str = "signer_network"; // TODO: do more validation for paths, images, etc #[allow(unused_assignments)] -pub fn handle_docker_init(config_path: String, output_dir: String) -> eyre::Result<()> { +pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> { println!("Initializing Commit-Boost with config file: {}", config_path); - let cb_config = CommitBoostConfig::from_file(&config_path); + let cb_config = CommitBoostConfig::from_file(&config_path)?; let mut services = IndexMap::new(); @@ -158,7 +159,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> eyre::Resu }; // write jwts to env - let jwts_json = serde_json::to_string(&jwts).unwrap().clone(); + let jwts_json = serde_json::to_string(&jwts)?.clone(); envs.insert(JWTS_ENV.into(), format!("{jwts_json:?}")); let signer_service = Service { diff --git a/crates/common/src/commit/client.rs b/crates/common/src/commit/client.rs index b452fa4..3ead74c 100644 --- a/crates/common/src/commit/client.rs +++ b/crates/common/src/commit/client.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use alloy::rpc::types::beacon::{BlsPublicKey, BlsSignature}; +use eyre::WrapErr; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; use serde::{Deserialize, Serialize}; @@ -27,21 +28,20 @@ pub struct SignerClient { impl SignerClient { /// Create a new SignerClient - pub fn new(signer_server_address: String, jwt: &str) -> Self { + pub fn new(signer_server_address: String, jwt: &str) -> eyre::Result { let url = format!("http://{}", signer_server_address); let mut headers = HeaderMap::new(); let mut auth_value = - HeaderValue::from_str(&format!("Bearer {}", jwt)).expect("invalid jwt"); + HeaderValue::from_str(&format!("Bearer {}", jwt)).wrap_err("invalid jwt")?; auth_value.set_sensitive(true); headers.insert(AUTHORIZATION, auth_value); let client = reqwest::Client::builder() .timeout(DEFAULT_REQUEST_TIMEOUT) .default_headers(headers) - .build() - .unwrap(); + .build()?; - Self { url: url.into(), client } + Ok(Self { url: url.into(), client }) } /// Request a list of validator pubkeys for which signatures can be diff --git a/crates/common/src/commit/request.rs b/crates/common/src/commit/request.rs index d29eef6..121b5d4 100644 --- a/crates/common/src/commit/request.rs +++ b/crates/common/src/commit/request.rs @@ -1,11 +1,10 @@ use alloy::rpc::types::beacon::{BlsPublicKey, BlsSignature}; -use blst::BLST_ERROR; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; -use crate::{signature::verify_signed_builder_message, types::Chain}; +use crate::{error::BlstErrorWrapper, signature::verify_signed_builder_message, types::Chain}; // TODO: might need to adapt the SignedProxyDelegation so that it goes through // web3 signer @@ -23,7 +22,7 @@ pub struct SignedProxyDelegation { } impl SignedProxyDelegation { - pub fn validate(&self, chain: Chain) -> Result<(), BLST_ERROR> { + pub fn validate(&self, chain: Chain) -> Result<(), BlstErrorWrapper> { verify_signed_builder_message( chain, &self.message.delegator, diff --git a/crates/common/src/config.rs b/crates/common/src/config.rs index 9ebde10..24c54f4 100644 --- a/crates/common/src/config.rs +++ b/crates/common/src/config.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, sync::Arc}; -use eyre::{eyre, ContextCompat}; +use eyre::{eyre, ContextCompat, Result, WrapErr}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ @@ -43,29 +43,29 @@ pub struct CommitBoostConfig { pub metrics: MetricsConfig, } -fn load_from_file(path: &str) -> T { - let config_file = std::fs::read_to_string(path) - .unwrap_or_else(|_| panic!("Unable to find config file: '{}'", path)); - toml::from_str(&config_file).unwrap() +fn load_from_file(path: &str) -> Result { + let config_file = + std::fs::read_to_string(path).wrap_err(format!("Unable to find config file: {path}"))?; + toml::from_str(&config_file).wrap_err("could not deserialize toml from string") } -fn load_file_from_env(env: &str) -> T { - let path = std::env::var(env).unwrap_or_else(|_| panic!("{env} is not set")); +fn load_file_from_env(env: &str) -> Result { + let path = std::env::var(env).wrap_err(format!("{env} is not set"))?; load_from_file(&path) } /// Loads a map of module id -> jwt token from a json env -fn load_jwts() -> HashMap { - let jwts = std::env::var(JWTS_ENV).unwrap_or_else(|_| panic!("{JWTS_ENV} is not set")); - serde_json::from_str(&jwts).unwrap_or_else(|_| panic!("Failed to parse jwts: {jwts}")) +fn load_jwts() -> Result> { + let jwts = std::env::var(JWTS_ENV).wrap_err(format!("{JWTS_ENV} is not set"))?; + serde_json::from_str(&jwts).wrap_err("could not deserialize json from string") } impl CommitBoostConfig { - pub fn from_file(path: &str) -> Self { + pub fn from_file(path: &str) -> Result { load_from_file(path) } - pub fn from_env_path() -> Self { + pub fn from_env_path() -> Result { load_file_from_env(CB_CONFIG_ENV) } } @@ -92,18 +92,18 @@ pub struct StartSignerConfig { } impl StartSignerConfig { - pub fn load_from_env() -> Self { - let config = CommitBoostConfig::from_env_path(); + pub fn load_from_env() -> Result { + let config = CommitBoostConfig::from_env_path()?; - let jwts = load_jwts(); - let server_port = load_env_var_infallible(SIGNER_SERVER_ENV).parse().unwrap(); + let jwts = load_jwts()?; + let server_port = load_env_var(SIGNER_SERVER_ENV)?.parse()?; - StartSignerConfig { + Ok(StartSignerConfig { chain: config.chain, loader: config.signer.expect("Signer config is missing").loader, server_port, jwts, - } + }) } } @@ -121,9 +121,9 @@ pub struct ModuleMetricsConfig { } impl ModuleMetricsConfig { - pub fn load_from_env() -> Self { - let server_port = load_env_var_infallible(METRICS_SERVER_ENV).parse().unwrap(); - ModuleMetricsConfig { server_port } + pub fn load_from_env() -> Result { + let server_port = load_env_var(METRICS_SERVER_ENV)?.parse()?; + Ok(ModuleMetricsConfig { server_port }) } } @@ -163,9 +163,10 @@ fn default_pbs() -> String { } /// Loads the default pbs config, i.e. with no signer client or custom data -pub fn load_pbs_config() -> eyre::Result> { - let config = CommitBoostConfig::from_env_path(); - let relay_clients = config.relays.into_iter().map(RelayClient::new).collect(); +pub fn load_pbs_config() -> Result> { + let config = CommitBoostConfig::from_env_path()?; + let relay_clients = + config.relays.into_iter().map(RelayClient::new).collect::>>()?; Ok(PbsModuleConfig { chain: config.chain, @@ -177,7 +178,7 @@ pub fn load_pbs_config() -> eyre::Result> { } /// Loads a custom pbs config, i.e. with signer client and/or custom data -pub fn load_pbs_custom_config() -> eyre::Result> { +pub fn load_pbs_custom_config() -> Result> { #[derive(Debug, Deserialize)] struct CustomPbsConfig { #[serde(flatten)] @@ -194,17 +195,19 @@ pub fn load_pbs_custom_config() -> eyre::Result = load_file_from_env(CB_CONFIG_ENV); - let relay_clients = cb_config.relays.into_iter().map(RelayClient::new).collect(); + let cb_config: StubConfig = load_file_from_env(CB_CONFIG_ENV)?; + let relay_clients = + cb_config.relays.into_iter().map(RelayClient::new).collect::>>()?; let signer_client = if cb_config.pbs.static_config.with_signer { // if custom pbs requires a signer client, load jwt - let module_jwt = load_env_var_infallible(MODULE_JWT_ENV); - let signer_server_address = load_env_var_infallible(SIGNER_SERVER_ENV); + let module_jwt = load_env_var(MODULE_JWT_ENV)?; + let signer_server_address = load_env_var(SIGNER_SERVER_ENV)?; Some(SignerClient::new(signer_server_address, &module_jwt)) } else { None - }; + } + .transpose()?; Ok(PbsModuleConfig { chain: cb_config.chain, @@ -242,10 +245,10 @@ pub struct StartModuleConfig { /// - [CB_CONFIG_ENV] - the path to the config file /// - [MODULE_JWT_ENV] - the jwt token for the module // TODO: add metrics url here -pub fn load_module_config() -> eyre::Result> { - let module_id = load_env_var_infallible(MODULE_ID_ENV); - let module_jwt = load_env_var_infallible(MODULE_JWT_ENV); - let signer_server_address = load_env_var_infallible(SIGNER_SERVER_ENV); +pub fn load_module_config() -> Result> { + let module_id = load_env_var(MODULE_ID_ENV)?; + let module_jwt = load_env_var(MODULE_JWT_ENV)?; + let signer_server_address = load_env_var(SIGNER_SERVER_ENV)?; #[derive(Debug, Deserialize)] struct ThisModuleConfig { @@ -269,7 +272,7 @@ pub fn load_module_config() -> eyre::Result = load_file_from_env(CB_CONFIG_ENV); + let cb_config: StubConfig = load_file_from_env(CB_CONFIG_ENV)?; // find all matching modules config let matches: Vec> = cb_config @@ -286,7 +289,7 @@ pub fn load_module_config() -> eyre::Result() -> eyre::Result String { - std::env::var(env).unwrap_or_else(|_| panic!("{env} is not set")) +pub fn load_env_var(env: &str) -> Result { + std::env::var(env).wrap_err("{env} is not set") } diff --git a/crates/common/src/error.rs b/crates/common/src/error.rs new file mode 100644 index 0000000..d34949c --- /dev/null +++ b/crates/common/src/error.rs @@ -0,0 +1,58 @@ +use std::fmt::{Display, Formatter}; + +use blst::BLST_ERROR; +use thiserror::Error; +#[derive(Debug, Error, PartialEq, Eq)] +pub enum BlstErrorWrapper { + BlstSuccess(BLST_ERROR), + BlstBadEncoding(BLST_ERROR), + BlstPointNotOnCurve(BLST_ERROR), + BlstPointNotInGroup(BLST_ERROR), + BlstAggrTypeMismatch(BLST_ERROR), + BlstVerifyFail(BLST_ERROR), + BlstPkIsInfinity(BLST_ERROR), + BlstBadScalar(BLST_ERROR), +} + +impl Display for BlstErrorWrapper { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + BlstErrorWrapper::BlstSuccess(_) => write!(f, "BLST_SUCCESS"), + BlstErrorWrapper::BlstBadEncoding(_) => write!(f, "BLST_BAD_ENCODING"), + BlstErrorWrapper::BlstPointNotOnCurve(_) => write!(f, "BLST_POINT_NOT_ON_CURVE"), + BlstErrorWrapper::BlstPointNotInGroup(_) => write!(f, "BLST_POINT_NOT_IN_GROUP"), + BlstErrorWrapper::BlstAggrTypeMismatch(_) => write!(f, "BLST_AGGR_TYPE_MISMATCH"), + BlstErrorWrapper::BlstVerifyFail(_) => write!(f, "BLST_VERIFY_FAIL"), + BlstErrorWrapper::BlstPkIsInfinity(_) => write!(f, "BLST_PK_IS_INFINITY"), + BlstErrorWrapper::BlstBadScalar(_) => write!(f, "BLST_BAD_SCALAR"), + } + } +} +impl From for BlstErrorWrapper { + fn from(value: BLST_ERROR) -> Self { + match value { + BLST_ERROR::BLST_SUCCESS => BlstErrorWrapper::BlstSuccess(BLST_ERROR::BLST_SUCCESS), + BLST_ERROR::BLST_BAD_ENCODING => { + BlstErrorWrapper::BlstBadEncoding(BLST_ERROR::BLST_BAD_ENCODING) + } + BLST_ERROR::BLST_POINT_NOT_ON_CURVE => { + BlstErrorWrapper::BlstPointNotOnCurve(BLST_ERROR::BLST_POINT_NOT_ON_CURVE) + } + BLST_ERROR::BLST_POINT_NOT_IN_GROUP => { + BlstErrorWrapper::BlstPointNotInGroup(BLST_ERROR::BLST_POINT_NOT_IN_GROUP) + } + BLST_ERROR::BLST_AGGR_TYPE_MISMATCH => { + BlstErrorWrapper::BlstAggrTypeMismatch(BLST_ERROR::BLST_AGGR_TYPE_MISMATCH) + } + BLST_ERROR::BLST_VERIFY_FAIL => { + BlstErrorWrapper::BlstVerifyFail(BLST_ERROR::BLST_VERIFY_FAIL) + } + BLST_ERROR::BLST_PK_IS_INFINITY => { + BlstErrorWrapper::BlstPkIsInfinity(BLST_ERROR::BLST_PK_IS_INFINITY) + } + BLST_ERROR::BLST_BAD_SCALAR => { + BlstErrorWrapper::BlstBadScalar(BLST_ERROR::BLST_BAD_SCALAR) + } + } + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index b67562e..33a44ab 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -3,6 +3,7 @@ use std::time::Duration; pub mod commit; pub mod config; pub mod constants; +pub mod error; pub mod loader; pub mod pbs; pub mod signature; diff --git a/crates/common/src/loader.rs b/crates/common/src/loader.rs index 3f32632..cf5d453 100644 --- a/crates/common/src/loader.rs +++ b/crates/common/src/loader.rs @@ -6,9 +6,7 @@ use eyre::eyre; use serde::{de, Deserialize, Deserializer, Serialize}; use crate::{ - config::{ - load_env_var_infallible, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV, - }, + config::{load_env_var, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV}, signer::Signer, }; @@ -26,30 +24,32 @@ pub enum SignerLoader { } impl SignerLoader { - pub fn load_keys(self) -> Vec { + pub fn load_keys(self) -> eyre::Result> { // TODO: add flag to support also native loader self.load_from_env() } - pub fn load_from_env(self) -> Vec { - match self { + pub fn load_from_env(self) -> eyre::Result> { + Ok(match self { SignerLoader::File { .. } => { - let path = load_env_var_infallible(SIGNER_KEYS_ENV); + let path = load_env_var(SIGNER_KEYS_ENV)?; let file = std::fs::read_to_string(path) .unwrap_or_else(|_| panic!("Unable to find keys file")); - let keys: Vec = serde_json::from_str(&file).unwrap(); + let keys: Vec = serde_json::from_str(&file)?; - keys.into_iter().map(|k| Signer::new_from_bytes(&k.secret_key)).collect() + keys.into_iter() + .map(|k| Signer::new_from_bytes(&k.secret_key)) + .collect::>>()? } SignerLoader::ValidatorsDir { .. } => { // TODO: hacky way to load for now, we should support reading the // definitions.yml file - let keys_path = load_env_var_infallible(SIGNER_DIR_KEYS_ENV); - let secrets_path = load_env_var_infallible(SIGNER_DIR_SECRETS_ENV); + let keys_path = load_env_var(SIGNER_DIR_KEYS_ENV)?; + let secrets_path = load_env_var(SIGNER_DIR_SECRETS_ENV)?; load_secrets_and_keys(keys_path, secrets_path).expect("failed to load signers") } - } + }) } } @@ -103,7 +103,7 @@ fn load_one(ks_path: String, pw_path: String) -> eyre::Result { let password = fs::read(pw_path)?; let key = keystore.decrypt_keypair(&password).map_err(|_| eyre!("failed decrypting keypair"))?; - Ok(Signer::new_from_bytes(key.sk.serialize().as_bytes())) + Signer::new_from_bytes(key.sk.serialize().as_bytes()) } #[cfg(test)] diff --git a/crates/common/src/pbs/types.rs b/crates/common/src/pbs/types.rs index f770afd..eade572 100644 --- a/crates/common/src/pbs/types.rs +++ b/crates/common/src/pbs/types.rs @@ -4,6 +4,7 @@ use alloy::{ primitives::{hex::FromHex, B256}, rpc::types::beacon::BlsPublicKey, }; +use eyre::WrapErr; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use serde::{Deserialize, Serialize}; use url::Url; @@ -59,17 +60,15 @@ pub struct RelayClient { } impl RelayClient { - pub fn new(config: RelayConfig) -> Self { + pub fn new(config: RelayConfig) -> eyre::Result { let mut headers = HeaderMap::new(); headers.insert(HEADER_VERSION_KEY, HeaderValue::from_static(HEAVER_VERSION_VALUE)); if let Some(custom_headers) = &config.headers { for (key, value) in custom_headers { headers.insert( - HeaderName::from_str(key) - .unwrap_or_else(|_| panic!("{key} is an invalid header name")), - HeaderValue::from_str(value) - .unwrap_or_else(|_| panic!("{key} has an invalid header value")), + HeaderName::from_str(key).wrap_err("{key} is an invalid header name")?, + HeaderValue::from_str(value).wrap_err("{key} has an invalid header value")?, ); } } @@ -77,14 +76,13 @@ impl RelayClient { let client = reqwest::Client::builder() .default_headers(headers) .timeout(DEFAULT_REQUEST_TIMEOUT) - .build() - .expect("failed to build relay client"); + .build()?; - Self { + Ok(Self { id: Arc::new(config.id.clone().unwrap_or(config.entry.id.clone())), client, config: Arc::new(config), - } + }) } pub fn pubkey(&self) -> BlsPublicKey { diff --git a/crates/common/src/signature.rs b/crates/common/src/signature.rs index b7e9c95..65a7438 100644 --- a/crates/common/src/signature.rs +++ b/crates/common/src/signature.rs @@ -10,22 +10,23 @@ use tree_hash_derive::TreeHash; use crate::{ constants::{APPLICATION_BUILDER_DOMAIN, GENESIS_VALIDATORS_ROOT}, + error::BlstErrorWrapper, types::Chain, utils::{alloy_pubkey_to_blst, alloy_sig_to_blst}, }; -pub fn random_secret() -> SecretKey { +pub fn random_secret() -> eyre::Result { let mut rng = rand::thread_rng(); let mut ikm = [0u8; 32]; rng.fill_bytes(&mut ikm); - SecretKey::key_gen(&ikm, &[]).unwrap() + Ok(SecretKey::key_gen(&ikm, &[]).map_err(BlstErrorWrapper::from)?) } pub fn verify_signature( pubkey: &BlsPublicKey, msg: &[u8], signature: &BlsSignature, -) -> Result<(), blst::BLST_ERROR> { +) -> Result<(), BlstErrorWrapper> { let pubkey: PublicKey = alloy_pubkey_to_blst(pubkey)?; let signature: Signature = alloy_sig_to_blst(signature)?; @@ -33,7 +34,7 @@ pub fn verify_signature( if res == BLST_ERROR::BLST_SUCCESS { Ok(()) } else { - Err(res) + Err(res.into()) } } @@ -78,7 +79,7 @@ pub fn verify_signed_builder_message( pubkey: &BlsPublicKey, msg: &T, signature: &BlsSignature, -) -> Result<(), BLST_ERROR> { +) -> Result<(), BlstErrorWrapper> { let domain = chain.builder_domain(); let signing_root = compute_signing_root(msg.tree_hash_root().0, domain); diff --git a/crates/common/src/signer.rs b/crates/common/src/signer.rs index 16b4722..8062005 100644 --- a/crates/common/src/signer.rs +++ b/crates/common/src/signer.rs @@ -1,8 +1,10 @@ use alloy::rpc::types::beacon::{BlsPublicKey, BlsSignature}; use blst::min_pk::SecretKey; +use eyre::Result; use tree_hash::TreeHash; use crate::{ + error::BlstErrorWrapper, signature::{random_secret, sign_builder_message}, types::Chain, utils::blst_pubkey_to_alloy, @@ -14,13 +16,13 @@ pub enum Signer { } impl Signer { - pub fn new_random() -> Self { - Signer::Local(random_secret()) + pub fn new_random() -> Result { + Ok(Signer::Local(random_secret()?)) } - pub fn new_from_bytes(bytes: &[u8]) -> Self { - let secret_key = SecretKey::from_bytes(bytes).unwrap(); - Self::Local(secret_key) + pub fn new_from_bytes(bytes: &[u8]) -> Result { + let secret_key = SecretKey::from_bytes(bytes).map_err(BlstErrorWrapper::from)?; + Ok(Self::Local(secret_key)) } pub fn pubkey(&self) -> BlsPublicKey { diff --git a/crates/metrics/src/provider.rs b/crates/metrics/src/provider.rs index 5765641..ea9d9c4 100644 --- a/crates/metrics/src/provider.rs +++ b/crates/metrics/src/provider.rs @@ -22,18 +22,19 @@ impl MetricsProvider { MetricsProvider { config, registry } } - pub fn from_registry(registry: Registry) -> Self { - let config = ModuleMetricsConfig::load_from_env(); - MetricsProvider { config, registry } + pub fn from_registry(registry: Registry) -> eyre::Result { + let config = ModuleMetricsConfig::load_from_env()?; + Ok(MetricsProvider { config, registry }) } - pub fn load_and_run(registry: Registry) { - let provider = MetricsProvider::from_registry(registry); + pub fn load_and_run(registry: Registry) -> eyre::Result<()> { + let provider = MetricsProvider::from_registry(registry)?; tokio::spawn(async move { if let Err(err) = provider.run().await { error!("Metrics server error: {:?}", err); } }); + Ok(()) } pub async fn run(self) -> eyre::Result<()> { diff --git a/crates/pbs/src/error.rs b/crates/pbs/src/error.rs index 7921667..92d86b7 100644 --- a/crates/pbs/src/error.rs +++ b/crates/pbs/src/error.rs @@ -3,6 +3,7 @@ use alloy::{ rpc::types::beacon::BlsPublicKey, }; use axum::{http::StatusCode, response::IntoResponse}; +use cb_common::error::BlstErrorWrapper; use thiserror::Error; #[derive(Debug)] @@ -88,5 +89,5 @@ pub enum ValidationError { EmptyTxRoot, #[error("failed signature verification: {0:?}")] - Sigverify(blst::BLST_ERROR), + Sigverify(#[from] BlstErrorWrapper), } diff --git a/crates/pbs/src/service.rs b/crates/pbs/src/service.rs index 7ca965c..3c0bba1 100644 --- a/crates/pbs/src/service.rs +++ b/crates/pbs/src/service.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; use cb_metrics::provider::MetricsProvider; +use eyre::Result; use prometheus::core::Collector; use tokio::net::TcpListener; use tracing::{error, info}; @@ -38,8 +39,8 @@ impl PbsService { PBS_METRICS_REGISTRY.register(c).expect("failed to register metric"); } - pub fn init_metrics() { - MetricsProvider::load_and_run(PBS_METRICS_REGISTRY.clone()); + pub fn init_metrics() -> Result<()> { + MetricsProvider::load_and_run(PBS_METRICS_REGISTRY.clone()) } // TODO: before starting, send a sanity check to relay diff --git a/crates/signer/src/manager.rs b/crates/signer/src/manager.rs index 49910bb..5bf053d 100644 --- a/crates/signer/src/manager.rs +++ b/crates/signer/src/manager.rs @@ -45,8 +45,8 @@ impl SigningManager { pub async fn create_proxy( &mut self, delegator: BlsPublicKey, - ) -> Result { - let signer = Signer::new_random(); + ) -> eyre::Result { + let signer = Signer::new_random()?; let message = ProxyDelegation { delegator, proxy: signer.pubkey() }; let signature = self.sign_consensus(&delegator, &message.tree_hash_root().0).await?; diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 9a4a368..ed0e85c 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -16,6 +16,7 @@ use cb_common::{ }, config::StartSignerConfig, }; +use eyre::WrapErr; use headers::{authorization::Bearer, Authorization}; use tokio::net::TcpListener; use tracing::{debug, error, info, warn}; @@ -36,10 +37,10 @@ struct SigningState { } impl SigningService { - pub async fn run(config: StartSignerConfig) { + pub async fn run(config: StartSignerConfig) -> eyre::Result<()> { if config.jwts.is_empty() { warn!("Signing service was started but no module is registered. Exiting"); - return; + return Ok(()); } else { info!(modules =? config.jwts.keys(), port =? config.server_port, "Starting signing service"); } @@ -47,7 +48,7 @@ impl SigningService { let mut manager = SigningManager::new(config.chain); // TODO: load proxy keys, or pass already loaded? - for signer in config.loader.load_keys() { + for signer in config.loader.load_keys()? { manager.add_consensus_signer(signer); } @@ -59,11 +60,12 @@ impl SigningService { .with_state(state); let address = SocketAddr::from(([0, 0, 0, 0], config.server_port)); - let listener = TcpListener::bind(address).await.expect("failed tcp binding"); + let listener = TcpListener::bind(address).await.wrap_err("failed tcp binding")?; if let Err(err) = axum::serve(listener, app).await { error!(?err, "Signing server exited") } + Ok(()) } } diff --git a/examples/custom_boost.rs b/examples/custom_boost.rs index 9bd0b40..d2587fd 100644 --- a/examples/custom_boost.rs +++ b/examples/custom_boost.rs @@ -56,6 +56,8 @@ async fn handle_stats(State(state): State>) -> Respo #[tokio::main] async fn main() { + + color_eyre::install()?; initialize_tracing_log(); let (chain, config) = load_pbs_config(); diff --git a/examples/da_commit/Cargo.toml b/examples/da_commit/Cargo.toml index a9388bb..9ad0fe2 100644 --- a/examples/da_commit/Cargo.toml +++ b/examples/da_commit/Cargo.toml @@ -25,4 +25,5 @@ prometheus.workspace = true # misc eyre.workspace = true +color-eyre.workspace = true lazy_static.workspace = true diff --git a/examples/da_commit/src/main.rs b/examples/da_commit/src/main.rs index f42b012..10dda09 100644 --- a/examples/da_commit/src/main.rs +++ b/examples/da_commit/src/main.rs @@ -2,7 +2,7 @@ use std::time::Duration; use alloy::rpc::types::beacon::{BlsPublicKey, BlsSignature}; use commit_boost::prelude::*; -use eyre::OptionExt; +use eyre::{OptionExt, Result}; use lazy_static::lazy_static; use prometheus::{IntCounter, Registry}; use serde::Deserialize; @@ -37,7 +37,7 @@ struct ExtraConfig { } impl DaCommitService { - pub async fn run(self) -> eyre::Result<()> { + pub async fn run(self) -> Result<()> { // the config has the signer_client already setup, we can use it to interact // with the Signer API let pubkeys = self.config.signer_client.get_pubkeys().await?; @@ -55,7 +55,7 @@ impl DaCommitService { } } - pub async fn send_request(&self, data: u64, pubkey: BlsPublicKey) -> eyre::Result<()> { + pub async fn send_request(&self, data: u64, pubkey: BlsPublicKey) -> Result<()> { let datagram = Datagram { data }; let request = SignRequest::builder(&self.config.id, pubkey).with_msg(&datagram); let signature = self.config.signer_client.request_signature(&request).await?; @@ -69,13 +69,14 @@ impl DaCommitService { } #[tokio::main] -async fn main() { +async fn main() -> Result<()> { + color_eyre::install()?; initialize_tracing_log(); // Remember to register all your metrics before starting the process - MY_CUSTOM_REGISTRY.register(Box::new(SIG_RECEIVED_COUNTER.clone())).unwrap(); + MY_CUSTOM_REGISTRY.register(Box::new(SIG_RECEIVED_COUNTER.clone()))?; // Spin up a server that exposes the /metrics endpoint to Prometheus - MetricsProvider::load_and_run(MY_CUSTOM_REGISTRY.clone()); + MetricsProvider::load_and_run(MY_CUSTOM_REGISTRY.clone())?; match load_module_config::() { Ok(config) => { @@ -95,6 +96,7 @@ async fn main() { error!(?err, "Failed to load module config"); } } + Ok(()) } fn pretty_print_sig(sig: BlsSignature) -> String { diff --git a/tests/src/mock_validator.rs b/tests/src/mock_validator.rs index 67d332d..9a4ae41 100644 --- a/tests/src/mock_validator.rs +++ b/tests/src/mock_validator.rs @@ -13,8 +13,8 @@ pub struct MockValidator { } impl MockValidator { - pub fn new(port: u16) -> Self { - Self { comm_boost: generate_mock_relay(port, BlsPublicKey::default()) } + pub fn new(port: u16) -> eyre::Result { + Ok(Self { comm_boost: generate_mock_relay(port, BlsPublicKey::default())? }) } pub async fn do_get_header(&self) -> Result<(), Error> { diff --git a/tests/src/utils.rs b/tests/src/utils.rs index b1975ba..08307c8 100644 --- a/tests/src/utils.rs +++ b/tests/src/utils.rs @@ -2,6 +2,7 @@ use std::sync::Once; use alloy::rpc::types::beacon::BlsPublicKey; use cb_common::pbs::{RelayClient, RelayConfig, RelayEntry}; +use eyre::Result; pub fn get_local_address(port: u16) -> String { format!("http://0.0.0.0:{port}") @@ -14,7 +15,7 @@ pub fn setup_test_env() { }); } -pub fn generate_mock_relay(port: u16, pubkey: BlsPublicKey) -> RelayClient { +pub fn generate_mock_relay(port: u16, pubkey: BlsPublicKey) -> Result { let entry = RelayEntry { id: format!("mock_{port}"), pubkey, url: get_local_address(port) }; let config = RelayConfig { entry, ..RelayConfig::default() }; RelayClient::new(config) diff --git a/tests/tests/config.rs b/tests/tests/config.rs index 090ff10..b7775f4 100644 --- a/tests/tests/config.rs +++ b/tests/tests/config.rs @@ -1,10 +1,12 @@ use cb_common::{config::CommitBoostConfig, types::Chain}; +use eyre::Result; #[tokio::test] -async fn test_load_config() { - let config = CommitBoostConfig::from_file("../config.example.toml"); +async fn test_load_config() -> Result<()> { + let config = CommitBoostConfig::from_file("../config.example.toml")?; assert_eq!(config.chain, Chain::Holesky); - assert!(config.relays[0].headers.is_some()) + assert!(config.relays[0].headers.is_some()); // TODO: add more + Ok(()) } diff --git a/tests/tests/pbs_integration.rs b/tests/tests/pbs_integration.rs index 72e4873..a3d089e 100644 --- a/tests/tests/pbs_integration.rs +++ b/tests/tests/pbs_integration.rs @@ -13,17 +13,19 @@ use cb_tests::{ mock_validator::MockValidator, utils::{generate_mock_relay, setup_test_env}, }; +use eyre::Result; use tokio::net::TcpListener; use tracing::info; -async fn start_mock_relay_service(state: Arc, port: u16) { +async fn start_mock_relay_service(state: Arc, port: u16) -> Result<()> { let app = mock_relay_app_router(state); - let socket = SocketAddr::new("0.0.0.0".parse().unwrap(), port); - let listener = TcpListener::bind(socket).await.unwrap(); + let socket = SocketAddr::new("0.0.0.0".parse()?, port); + let listener = TcpListener::bind(socket).await?; info!("Starting mock relay on {socket:?}"); - axum::serve(listener, app).await.unwrap(); + axum::serve(listener, app).await?; + Ok(()) } fn get_pbs_static_config(port: u16) -> PbsConfig { @@ -54,14 +56,14 @@ fn to_pbs_config( } #[tokio::test] -async fn test_get_header() { +async fn test_get_header() -> Result<()> { setup_test_env(); - let signer = Signer::new_random(); + let signer = Signer::new_random()?; let chain = Chain::Holesky; let port = 3000; - let mock_relay = generate_mock_relay(port + 1, signer.pubkey()); + let mock_relay = generate_mock_relay(port + 1, signer.pubkey())?; let mock_state = Arc::new(MockRelayState::new(chain, signer, 0)); tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); @@ -72,25 +74,26 @@ async fn test_get_header() { // leave some time to start servers tokio::time::sleep(Duration::from_millis(100)).await; - let mock_validator = MockValidator::new(port); + let mock_validator = MockValidator::new(port)?; info!("Sending get header"); let res = mock_validator.do_get_header().await; assert!(res.is_ok()); assert_eq!(mock_state.received_get_header(), 1); + Ok(()) } #[tokio::test] -async fn test_get_status() { +async fn test_get_status() -> Result<()> { setup_test_env(); - let signer = Signer::new_random(); + let signer = Signer::new_random()?; let chain = Chain::Holesky; let port = 3100; let relays = vec![ - generate_mock_relay(port + 1, signer.pubkey()), - generate_mock_relay(port + 2, signer.pubkey()), + generate_mock_relay(port + 1, signer.pubkey())?, + generate_mock_relay(port + 2, signer.pubkey())?, ]; let mock_state = Arc::new(MockRelayState::new(chain, signer, 0)); tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); @@ -103,23 +106,24 @@ async fn test_get_status() { // leave some time to start servers tokio::time::sleep(Duration::from_millis(100)).await; - let mock_validator = MockValidator::new(port); + let mock_validator = MockValidator::new(port)?; info!("Sending get status"); let res = mock_validator.do_get_status().await; assert!(res.is_ok()); assert_eq!(mock_state.received_get_status(), 2); + Ok(()) } #[tokio::test] -async fn test_register_validators() { +async fn test_register_validators() -> Result<()> { setup_test_env(); - let signer = Signer::new_random(); + let signer = Signer::new_random()?; let chain = Chain::Holesky; let port = 3300; - let relays = vec![generate_mock_relay(port + 1, signer.pubkey())]; + let relays = vec![generate_mock_relay(port + 1, signer.pubkey())?]; let mock_state = Arc::new(MockRelayState::new(chain, signer, 0)); tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); @@ -130,23 +134,24 @@ async fn test_register_validators() { // leave some time to start servers tokio::time::sleep(Duration::from_millis(100)).await; - let mock_validator = MockValidator::new(port); + let mock_validator = MockValidator::new(port)?; info!("Sending register validator"); let res = mock_validator.do_register_validator().await; assert!(res.is_ok()); assert_eq!(mock_state.received_register_validator(), 1); + Ok(()) } #[tokio::test] -async fn test_submit_block() { +async fn test_submit_block() -> Result<()> { setup_test_env(); - let signer = Signer::new_random(); + let signer = Signer::new_random()?; let chain = Chain::Holesky; let port = 3400; - let relays = vec![generate_mock_relay(port + 1, signer.pubkey())]; + let relays = vec![generate_mock_relay(port + 1, signer.pubkey())?]; let mock_state = Arc::new(MockRelayState::new(chain, signer, 0)); tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); @@ -157,10 +162,11 @@ async fn test_submit_block() { // leave some time to start servers tokio::time::sleep(Duration::from_millis(100)).await; - let mock_validator = MockValidator::new(port); + let mock_validator = MockValidator::new(port)?; info!("Sending submit block"); let res = mock_validator.do_submit_block().await; assert!(res.is_ok()); assert_eq!(mock_state.received_submit_block(), 1); + Ok(()) }