From 5477a9ded57efe779c95f9651e5dd14ac76187b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 10 Jan 2025 18:08:40 -0300 Subject: [PATCH 01/23] Update signer config via API endpoint --- crates/common/src/commit/constants.rs | 1 + crates/signer/src/service.rs | 31 ++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/crates/common/src/commit/constants.rs b/crates/common/src/commit/constants.rs index 3335833a..7c9f948c 100644 --- a/crates/common/src/commit/constants.rs +++ b/crates/common/src/commit/constants.rs @@ -2,3 +2,4 @@ pub const GET_PUBKEYS_PATH: &str = "/signer/v1/get_pubkeys"; pub const REQUEST_SIGNATURE_PATH: &str = "/signer/v1/request_signature"; pub const GENERATE_PROXY_KEY_PATH: &str = "/signer/v1/generate_proxy_key"; pub const STATUS_PATH: &str = "/status"; +pub const RELOAD_PATH: &str = "/reload"; diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 228158e9..38a33bef 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -13,7 +13,8 @@ use bimap::BiHashMap; use cb_common::{ commit::{ constants::{ - GENERATE_PROXY_KEY_PATH, GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH, STATUS_PATH, + GENERATE_PROXY_KEY_PATH, GET_PUBKEYS_PATH, RELOAD_PATH, REQUEST_SIGNATURE_PATH, + STATUS_PATH, }, request::{ EncryptionScheme, GenerateProxyRequest, GetPubkeysResponse, SignConsensusRequest, @@ -83,6 +84,7 @@ impl SigningService { .route(REQUEST_SIGNATURE_PATH, post(handle_request_signature)) .route(GET_PUBKEYS_PATH, get(handle_get_pubkeys)) .route(GENERATE_PROXY_KEY_PATH, post(handle_generate_proxy)) + .route(RELOAD_PATH, post(handle_reload)) .with_state(state.clone()) .route_layer(middleware::from_fn_with_state(state.clone(), jwt_auth)) .route_layer(middleware::from_fn(log_request)); @@ -220,3 +222,30 @@ async fn handle_generate_proxy( Ok(response) } + +async fn handle_reload( + State(state): State, +) -> Result { + let config = StartSignerConfig::load_from_env() + .map_err(|err| SignerModuleError::Internal(err.to_string()))?; + + let proxy_store = if let Some(store) = config.store { + Some(store.init_from_env().map_err(|err| SignerModuleError::Internal(err.to_string()))?) + } else { + warn!("Proxy store not configured. Proxies keys and delegations will not be persisted"); + None + }; + + let mut new_manager = SigningManager::new(config.chain, proxy_store) + .map_err(|err| SignerModuleError::Internal(err.to_string()))?; + + for signer in + config.loader.load_keys().map_err(|err| SignerModuleError::Internal(err.to_string()))? + { + new_manager.add_consensus_signer(signer); + } + + *state.manager.write().await = new_manager; + + Ok((StatusCode::OK, "OK")) +} From fb60b2407049a681c89e019a4b6e336eaca7b96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 10 Jan 2025 19:01:32 -0300 Subject: [PATCH 02/23] Update config for PBS module --- bin/pbs.rs | 4 +-- bin/src/lib.rs | 2 +- crates/common/src/pbs/constants.rs | 1 + crates/pbs/src/api.rs | 10 +++---- crates/pbs/src/error.rs | 9 ++++--- crates/pbs/src/lib.rs | 2 +- crates/pbs/src/mev_boost/get_header.rs | 4 +-- .../pbs/src/mev_boost/register_validator.rs | 4 +-- crates/pbs/src/mev_boost/status.rs | 4 +-- crates/pbs/src/mev_boost/submit_block.rs | 4 +-- crates/pbs/src/routes/get_header.rs | 1 + crates/pbs/src/routes/mod.rs | 1 + crates/pbs/src/routes/register_validator.rs | 1 + crates/pbs/src/routes/reload.rs | 24 +++++++++++++++++ crates/pbs/src/routes/router.rs | 11 +++++--- crates/pbs/src/routes/status.rs | 2 ++ crates/pbs/src/routes/submit_block.rs | 1 + crates/pbs/src/service.rs | 6 ++--- crates/pbs/src/state.rs | 27 +++++++++++++++---- examples/status_api/src/main.rs | 12 ++++++--- tests/tests/pbs_integration.rs | 14 +++++----- 21 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 crates/pbs/src/routes/reload.rs diff --git a/bin/pbs.rs b/bin/pbs.rs index 1514cf10..295f14a1 100644 --- a/bin/pbs.rs +++ b/bin/pbs.rs @@ -2,7 +2,7 @@ use cb_common::{ config::load_pbs_config, utils::{initialize_pbs_tracing_log, wait_for_signal}, }; -use cb_pbs::{DefaultBuilderApi, PbsService, PbsState}; +use cb_pbs::{DefaultBuilderApi, InnerPbsState, PbsService}; use eyre::Result; use tracing::{error, info}; @@ -18,7 +18,7 @@ async fn main() -> Result<()> { let pbs_config = load_pbs_config().await?; - let state = PbsState::new(pbs_config); + let state: InnerPbsState = InnerPbsState::new(pbs_config); PbsService::init_metrics()?; let server = PbsService::run::<_, DefaultBuilderApi>(state); diff --git a/bin/src/lib.rs b/bin/src/lib.rs index e4f566b2..0f54e531 100644 --- a/bin/src/lib.rs +++ b/bin/src/lib.rs @@ -19,7 +19,7 @@ pub mod prelude { pub use cb_metrics::provider::MetricsProvider; pub use cb_pbs::{ get_header, get_status, register_validator, submit_block, BuilderApi, BuilderApiState, - DefaultBuilderApi, PbsService, PbsState, + DefaultBuilderApi, InnerPbsState, PbsService, PbsState, }; // The TreeHash derive macro requires tree_hash as import pub mod tree_hash { diff --git a/crates/common/src/pbs/constants.rs b/crates/common/src/pbs/constants.rs index 07f9faf4..52c571ed 100644 --- a/crates/common/src/pbs/constants.rs +++ b/crates/common/src/pbs/constants.rs @@ -6,6 +6,7 @@ pub const GET_HEADER_PATH: &str = "/header/:slot/:parent_hash/:pubkey"; pub const GET_STATUS_PATH: &str = "/status"; pub const REGISTER_VALIDATOR_PATH: &str = "/validators"; pub const SUBMIT_BLOCK_PATH: &str = "/blinded_blocks"; +pub const RELOAD_PATH: &str = "/reload"; // https://ethereum.github.io/builder-specs/#/Builder diff --git a/crates/pbs/src/api.rs b/crates/pbs/src/api.rs index 217e438a..b6fa90af 100644 --- a/crates/pbs/src/api.rs +++ b/crates/pbs/src/api.rs @@ -7,7 +7,7 @@ use cb_common::pbs::{ use crate::{ mev_boost, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, InnerPbsState, PbsState}, }; #[async_trait] @@ -21,13 +21,13 @@ pub trait BuilderApi: 'static { async fn get_header( params: GetHeaderParams, req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result> { mev_boost::get_header(params, req_headers, state).await } /// https://ethereum.github.io/builder-specs/#/Builder/status - async fn get_status(req_headers: HeaderMap, state: PbsState) -> eyre::Result<()> { + async fn get_status(req_headers: HeaderMap, state: InnerPbsState) -> eyre::Result<()> { mev_boost::get_status(req_headers, state).await } @@ -35,7 +35,7 @@ pub trait BuilderApi: 'static { async fn submit_block( signed_blinded_block: SignedBlindedBeaconBlock, req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result { mev_boost::submit_block(signed_blinded_block, req_headers, state).await } @@ -44,7 +44,7 @@ pub trait BuilderApi: 'static { async fn register_validator( registrations: Vec, req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result<()> { mev_boost::register_validator(registrations, req_headers, state).await } diff --git a/crates/pbs/src/error.rs b/crates/pbs/src/error.rs index 74fc7004..d1da66d0 100644 --- a/crates/pbs/src/error.rs +++ b/crates/pbs/src/error.rs @@ -5,6 +5,7 @@ use axum::{http::StatusCode, response::IntoResponse}; pub enum PbsClientError { NoResponse, NoPayload, + Internal(String), } impl PbsClientError { @@ -12,15 +13,17 @@ impl PbsClientError { match self { PbsClientError::NoResponse => StatusCode::BAD_GATEWAY, PbsClientError::NoPayload => StatusCode::BAD_GATEWAY, + PbsClientError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } impl IntoResponse for PbsClientError { fn into_response(self) -> axum::response::Response { - let msg = match self { - PbsClientError::NoResponse => "no response from relays", - PbsClientError::NoPayload => "no payload from relays", + let msg = match &self { + PbsClientError::NoResponse => "no response from relays".to_string(), + PbsClientError::NoPayload => "no payload from relays".to_string(), + PbsClientError::Internal(msg) => msg.clone(), }; (self.status_code(), msg).into_response() diff --git a/crates/pbs/src/lib.rs b/crates/pbs/src/lib.rs index efe76d4e..5b6a70bf 100644 --- a/crates/pbs/src/lib.rs +++ b/crates/pbs/src/lib.rs @@ -12,4 +12,4 @@ pub use api::*; pub use constants::*; pub use mev_boost::*; pub use service::PbsService; -pub use state::{BuilderApiState, PbsState}; +pub use state::{BuilderApiState, InnerPbsState, PbsState}; diff --git a/crates/pbs/src/mev_boost/get_header.rs b/crates/pbs/src/mev_boost/get_header.rs index f875a4e1..718a8fca 100644 --- a/crates/pbs/src/mev_boost/get_header.rs +++ b/crates/pbs/src/mev_boost/get_header.rs @@ -32,7 +32,7 @@ use crate::{ GET_HEADER_ENDPOINT_TAG, MAX_SIZE_GET_HEADER, TIMEOUT_ERROR_CODE, TIMEOUT_ERROR_CODE_STR, }, metrics::{RELAY_HEADER_VALUE, RELAY_LAST_SLOT, RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, InnerPbsState}, utils::{check_gas_limit, read_chunked_body_with_max}, }; @@ -41,7 +41,7 @@ use crate::{ pub async fn get_header( params: GetHeaderParams, req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result> { let parent_block = Arc::new(RwLock::new(None)); if state.extra_validation_enabled() { diff --git a/crates/pbs/src/mev_boost/register_validator.rs b/crates/pbs/src/mev_boost/register_validator.rs index 6a745319..bdffb705 100644 --- a/crates/pbs/src/mev_boost/register_validator.rs +++ b/crates/pbs/src/mev_boost/register_validator.rs @@ -15,7 +15,7 @@ use url::Url; use crate::{ constants::{MAX_SIZE_DEFAULT, REGISTER_VALIDATOR_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR}, metrics::{RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, InnerPbsState}, utils::read_chunked_body_with_max, }; @@ -24,7 +24,7 @@ use crate::{ pub async fn register_validator( registrations: Vec, req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result<()> { // prepare headers let mut send_headers = HeaderMap::new(); diff --git a/crates/pbs/src/mev_boost/status.rs b/crates/pbs/src/mev_boost/status.rs index 7d9d67d2..6f005c4c 100644 --- a/crates/pbs/src/mev_boost/status.rs +++ b/crates/pbs/src/mev_boost/status.rs @@ -12,7 +12,7 @@ use tracing::{debug, error}; use crate::{ constants::{MAX_SIZE_DEFAULT, STATUS_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR}, metrics::{RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, InnerPbsState}, utils::read_chunked_body_with_max, }; @@ -21,7 +21,7 @@ use crate::{ /// relay returns 200 pub async fn get_status( req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result<()> { // If no relay check, return early if !state.config.pbs_config.relay_check { diff --git a/crates/pbs/src/mev_boost/submit_block.rs b/crates/pbs/src/mev_boost/submit_block.rs index 0d03816b..c1653c12 100644 --- a/crates/pbs/src/mev_boost/submit_block.rs +++ b/crates/pbs/src/mev_boost/submit_block.rs @@ -17,7 +17,7 @@ use url::Url; use crate::{ constants::{MAX_SIZE_SUBMIT_BLOCK, SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR}, metrics::{RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, InnerPbsState}, utils::read_chunked_body_with_max, }; @@ -25,7 +25,7 @@ use crate::{ pub async fn submit_block( signed_blinded_block: SignedBlindedBeaconBlock, req_headers: HeaderMap, - state: PbsState, + state: InnerPbsState, ) -> eyre::Result { // prepare headers let mut send_headers = HeaderMap::new(); diff --git a/crates/pbs/src/routes/get_header.rs b/crates/pbs/src/routes/get_header.rs index 5d0b3bd6..f2fc1f0a 100644 --- a/crates/pbs/src/routes/get_header.rs +++ b/crates/pbs/src/routes/get_header.rs @@ -26,6 +26,7 @@ pub async fn handle_get_header>( req_headers: HeaderMap, Path(params): Path, ) -> Result { + let state = state.inner.write().await; state.publish_event(BuilderEvent::GetHeaderRequest(params)); let ua = get_user_agent(&req_headers); diff --git a/crates/pbs/src/routes/mod.rs b/crates/pbs/src/routes/mod.rs index 265b7da0..33a07500 100644 --- a/crates/pbs/src/routes/mod.rs +++ b/crates/pbs/src/routes/mod.rs @@ -1,5 +1,6 @@ mod get_header; mod register_validator; +mod reload; mod router; mod status; mod submit_block; diff --git a/crates/pbs/src/routes/register_validator.rs b/crates/pbs/src/routes/register_validator.rs index fd73837b..856d6b0f 100644 --- a/crates/pbs/src/routes/register_validator.rs +++ b/crates/pbs/src/routes/register_validator.rs @@ -25,6 +25,7 @@ pub async fn handle_register_validator>( req_headers: HeaderMap, Json(registrations): Json>, ) -> Result { + let state = state.inner.write().await; trace!(?registrations); state.publish_event(BuilderEvent::RegisterValidatorRequest(registrations.clone())); diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs new file mode 100644 index 00000000..affd0236 --- /dev/null +++ b/crates/pbs/src/routes/reload.rs @@ -0,0 +1,24 @@ +use axum::{extract::State, response::IntoResponse}; +use cb_common::config::load_pbs_config; +use reqwest::StatusCode; +use uuid::Uuid; + +use crate::{ + error::PbsClientError, + state::{BuilderApiState, PbsState}, + InnerPbsState, +}; + +#[tracing::instrument(skip_all, name = "reload", fields(req_id = %Uuid::new_v4()))] +pub async fn handle_reload( + State(state): State>, +) -> Result { + let pbs_config = load_pbs_config() + .await + .map_err(|err| PbsClientError::Internal(format!("Cannot parse new config: {err}")))?; + let new_state = InnerPbsState::new(pbs_config).with_data(state.inner.read().await.data.clone()); + + *state.inner.write().await = new_state; + + Ok(StatusCode::OK) +} diff --git a/crates/pbs/src/routes/router.rs b/crates/pbs/src/routes/router.rs index f6ae4202..1df6be85 100644 --- a/crates/pbs/src/routes/router.rs +++ b/crates/pbs/src/routes/router.rs @@ -3,10 +3,14 @@ use axum::{ Router, }; use cb_common::pbs::{ - BUILDER_API_PATH, GET_HEADER_PATH, GET_STATUS_PATH, REGISTER_VALIDATOR_PATH, SUBMIT_BLOCK_PATH, + BUILDER_API_PATH, GET_HEADER_PATH, GET_STATUS_PATH, REGISTER_VALIDATOR_PATH, RELOAD_PATH, + SUBMIT_BLOCK_PATH, }; -use super::{handle_get_header, handle_get_status, handle_register_validator, handle_submit_block}; +use super::{ + handle_get_header, handle_get_status, handle_register_validator, handle_submit_block, + reload::handle_reload, +}; use crate::{ api::BuilderApi, state::{BuilderApiState, PbsState}, @@ -17,7 +21,8 @@ pub fn create_app_router>(state: PbsState)) .route(GET_STATUS_PATH, get(handle_get_status::)) .route(REGISTER_VALIDATOR_PATH, post(handle_register_validator::)) - .route(SUBMIT_BLOCK_PATH, post(handle_submit_block::)); + .route(SUBMIT_BLOCK_PATH, post(handle_submit_block::)) + .route(RELOAD_PATH, post(handle_reload::)); let builder_api = Router::new().nest(BUILDER_API_PATH, builder_routes); diff --git a/crates/pbs/src/routes/status.rs b/crates/pbs/src/routes/status.rs index 2a33e4a8..6e891b9c 100644 --- a/crates/pbs/src/routes/status.rs +++ b/crates/pbs/src/routes/status.rs @@ -17,6 +17,8 @@ pub async fn handle_get_status>( req_headers: HeaderMap, State(state): State>, ) -> Result { + let state = state.inner.read().await; + state.publish_event(BuilderEvent::GetStatusEvent); let ua = get_user_agent(&req_headers); diff --git a/crates/pbs/src/routes/submit_block.rs b/crates/pbs/src/routes/submit_block.rs index 5ae64c5a..bedfd1a3 100644 --- a/crates/pbs/src/routes/submit_block.rs +++ b/crates/pbs/src/routes/submit_block.rs @@ -21,6 +21,7 @@ pub async fn handle_submit_block>( req_headers: HeaderMap, Json(signed_blinded_block): Json, ) -> Result { + let state = state.inner.write().await; trace!(?signed_blinded_block); state.publish_event(BuilderEvent::SubmitBlockRequest(Box::new(signed_blinded_block.clone()))); diff --git a/crates/pbs/src/service.rs b/crates/pbs/src/service.rs index 2533573b..2f5633e9 100644 --- a/crates/pbs/src/service.rs +++ b/crates/pbs/src/service.rs @@ -15,19 +15,19 @@ use crate::{ api::BuilderApi, metrics::PBS_METRICS_REGISTRY, routes::create_app_router, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, InnerPbsState, PbsState}, }; pub struct PbsService; impl PbsService { - pub async fn run>(state: PbsState) -> Result<()> { + pub async fn run>(state: InnerPbsState) -> Result<()> { let addr = state.config.endpoint; let events_subs = state.config.event_publisher.as_ref().map(|e| e.n_subscribers()).unwrap_or_default(); info!(version = COMMIT_BOOST_VERSION, ?addr, events_subs, chain =? state.config.chain, "starting PBS service"); - let app = create_app_router::(state); + let app = create_app_router::(PbsState::new(state)); let listener = TcpListener::bind(addr).await?; let task = diff --git a/crates/pbs/src/state.rs b/crates/pbs/src/state.rs index 6b3f15c9..bf25a120 100644 --- a/crates/pbs/src/state.rs +++ b/crates/pbs/src/state.rs @@ -1,34 +1,51 @@ +use std::sync::Arc; + use alloy::rpc::types::beacon::BlsPublicKey; use cb_common::{ config::{PbsConfig, PbsModuleConfig}, pbs::{BuilderEvent, RelayClient}, }; +use tokio::sync::RwLock; pub trait BuilderApiState: Clone + Sync + Send + 'static {} impl BuilderApiState for () {} +#[derive(Clone)] +pub struct PbsState { + pub inner: Arc>>, +} + +impl PbsState +where + S: BuilderApiState, +{ + pub fn new(state: InnerPbsState) -> Self { + Self { inner: Arc::new(RwLock::new(state)) } + } +} + /// Config for the Pbs module. It can be extended by adding extra data to the /// state for modules that need it // TODO: consider remove state from the PBS module altogether #[derive(Clone)] -pub struct PbsState { +pub struct InnerPbsState { /// Config data for the Pbs service pub config: PbsModuleConfig, /// Opaque extra data for library use pub data: S, } -impl PbsState<()> { +impl InnerPbsState<()> { pub fn new(config: PbsModuleConfig) -> Self { Self { config, data: () } } - pub fn with_data(self, data: S) -> PbsState { - PbsState { data, config: self.config } + pub fn with_data(self, data: S) -> InnerPbsState { + InnerPbsState { data, config: self.config } } } -impl PbsState +impl InnerPbsState where S: BuilderApiState, { diff --git a/examples/status_api/src/main.rs b/examples/status_api/src/main.rs index 40bed730..b78b4a8b 100644 --- a/examples/status_api/src/main.rs +++ b/examples/status_api/src/main.rs @@ -58,7 +58,10 @@ struct MyBuilderApi; #[async_trait] impl BuilderApi for MyBuilderApi { - async fn get_status(req_headers: HeaderMap, state: PbsState) -> Result<()> { + async fn get_status( + req_headers: HeaderMap, + state: InnerPbsState, + ) -> Result<()> { state.data.inc(); info!("THIS IS A CUSTOM LOG"); CHECK_RECEIVED_COUNTER.inc(); @@ -73,7 +76,10 @@ impl BuilderApi for MyBuilderApi { } async fn handle_check(State(state): State>) -> Response { - (StatusCode::OK, format!("Received {count} status requests!", count = state.data.get())) + ( + StatusCode::OK, + format!("Received {count} status requests!", count = state.inner.write().await.data.get()), + ) .into_response() } @@ -85,7 +91,7 @@ async fn main() -> Result<()> { let _guard = initialize_pbs_tracing_log()?; let custom_state = MyBuilderState::from_config(extra); - let state = PbsState::new(pbs_config).with_data(custom_state); + let state = InnerPbsState::new(pbs_config).with_data(custom_state); PbsService::register_metric(Box::new(CHECK_RECEIVED_COUNTER.clone())); PbsService::init_metrics()?; diff --git a/tests/tests/pbs_integration.rs b/tests/tests/pbs_integration.rs index cc8f5d27..81eceac7 100644 --- a/tests/tests/pbs_integration.rs +++ b/tests/tests/pbs_integration.rs @@ -14,7 +14,7 @@ use cb_common::{ types::Chain, utils::blst_pubkey_to_alloy, }; -use cb_pbs::{DefaultBuilderApi, PbsService, PbsState}; +use cb_pbs::{DefaultBuilderApi, InnerPbsState, PbsService}; use cb_tests::{ mock_relay::{start_mock_relay_service, MockRelayState}, mock_validator::MockValidator, @@ -68,7 +68,7 @@ async fn test_get_header() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), vec![mock_relay]); - let state = PbsState::new(config); + let state = InnerPbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -99,7 +99,7 @@ async fn test_get_status() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 2)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = PbsState::new(config); + let state = InnerPbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -128,7 +128,7 @@ async fn test_register_validators() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = PbsState::new(config); + let state = InnerPbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -157,7 +157,7 @@ async fn test_submit_block() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = PbsState::new(config); + let state = InnerPbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -186,7 +186,7 @@ async fn test_submit_block_too_large() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = PbsState::new(config); + let state = InnerPbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -233,7 +233,7 @@ async fn test_mux() -> Result<()> { config.muxes = Some(HashMap::from([(validator_pubkey, mux)])); - let state = PbsState::new(config); + let state = InnerPbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers From cf20b435b0f3e2b991a22ac20111456b7374c960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 15:35:00 -0300 Subject: [PATCH 03/23] Add reload endpoint to API --- bin/pbs.rs | 2 +- crates/common/src/pbs/event.rs | 2 ++ crates/pbs/src/api.rs | 4 +++ crates/pbs/src/constants.rs | 1 + crates/pbs/src/mev_boost/mod.rs | 2 ++ crates/pbs/src/mev_boost/reload.rs | 12 +++++++++ crates/pbs/src/routes/reload.rs | 41 ++++++++++++++++++++++-------- crates/pbs/src/routes/router.rs | 2 +- crates/signer/src/service.rs | 4 +++ 9 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 crates/pbs/src/mev_boost/reload.rs diff --git a/bin/pbs.rs b/bin/pbs.rs index 295f14a1..db6545bd 100644 --- a/bin/pbs.rs +++ b/bin/pbs.rs @@ -18,7 +18,7 @@ async fn main() -> Result<()> { let pbs_config = load_pbs_config().await?; - let state: InnerPbsState = InnerPbsState::new(pbs_config); + let state = InnerPbsState::new(pbs_config); PbsService::init_metrics()?; let server = PbsService::run::<_, DefaultBuilderApi>(state); diff --git a/crates/common/src/pbs/event.rs b/crates/common/src/pbs/event.rs index f7954907..6441543e 100644 --- a/crates/common/src/pbs/event.rs +++ b/crates/common/src/pbs/event.rs @@ -37,6 +37,8 @@ pub enum BuilderEvent { }, RegisterValidatorRequest(Vec), RegisterValidatorResponse, + ReloadEvent, + ReloadResponse, } #[derive(Debug, Clone)] diff --git a/crates/pbs/src/api.rs b/crates/pbs/src/api.rs index b6fa90af..91894f03 100644 --- a/crates/pbs/src/api.rs +++ b/crates/pbs/src/api.rs @@ -48,6 +48,10 @@ pub trait BuilderApi: 'static { ) -> eyre::Result<()> { mev_boost::register_validator(registrations, req_headers, state).await } + + async fn reload(state: PbsState) -> eyre::Result<()> { + mev_boost::reload(state).await + } } pub struct DefaultBuilderApi; diff --git a/crates/pbs/src/constants.rs b/crates/pbs/src/constants.rs index 54156d1b..8dc3bd86 100644 --- a/crates/pbs/src/constants.rs +++ b/crates/pbs/src/constants.rs @@ -2,6 +2,7 @@ pub const STATUS_ENDPOINT_TAG: &str = "status"; pub const REGISTER_VALIDATOR_ENDPOINT_TAG: &str = "register_validator"; pub const SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG: &str = "submit_blinded_block"; pub const GET_HEADER_ENDPOINT_TAG: &str = "get_header"; +pub const RELOAD_ENDPOINT_TAG: &str = "reload"; /// For metrics recorded when a request times out pub const TIMEOUT_ERROR_CODE: u16 = 555; diff --git a/crates/pbs/src/mev_boost/mod.rs b/crates/pbs/src/mev_boost/mod.rs index e80ee2b8..a41b79db 100644 --- a/crates/pbs/src/mev_boost/mod.rs +++ b/crates/pbs/src/mev_boost/mod.rs @@ -1,9 +1,11 @@ mod get_header; mod register_validator; +mod reload; mod status; mod submit_block; pub use get_header::get_header; pub use register_validator::register_validator; +pub use reload::reload; pub use status::get_status; pub use submit_block::submit_block; diff --git a/crates/pbs/src/mev_boost/reload.rs b/crates/pbs/src/mev_boost/reload.rs new file mode 100644 index 00000000..0764a2fc --- /dev/null +++ b/crates/pbs/src/mev_boost/reload.rs @@ -0,0 +1,12 @@ +use cb_common::config::load_pbs_config; + +use crate::{BuilderApiState, InnerPbsState, PbsState}; + +pub async fn reload(state: PbsState) -> eyre::Result<()> { + let pbs_config = load_pbs_config().await?; + let new_state = InnerPbsState::new(pbs_config).with_data(state.inner.read().await.data.clone()); + + *state.inner.write().await = new_state; + + Ok(()) +} diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs index affd0236..e4da838e 100644 --- a/crates/pbs/src/routes/reload.rs +++ b/crates/pbs/src/routes/reload.rs @@ -1,24 +1,45 @@ -use axum::{extract::State, response::IntoResponse}; -use cb_common::config::load_pbs_config; +use axum::{extract::State, http::HeaderMap, response::IntoResponse}; +use cb_common::{pbs::BuilderEvent, utils::get_user_agent}; use reqwest::StatusCode; +use tracing::{error, info}; use uuid::Uuid; use crate::{ error::PbsClientError, + metrics::BEACON_NODE_STATUS, state::{BuilderApiState, PbsState}, - InnerPbsState, + BuilderApi, RELOAD_ENDPOINT_TAG, }; #[tracing::instrument(skip_all, name = "reload", fields(req_id = %Uuid::new_v4()))] -pub async fn handle_reload( +pub async fn handle_reload>( + req_headers: HeaderMap, State(state): State>, ) -> Result { - let pbs_config = load_pbs_config() - .await - .map_err(|err| PbsClientError::Internal(format!("Cannot parse new config: {err}")))?; - let new_state = InnerPbsState::new(pbs_config).with_data(state.inner.read().await.data.clone()); + let inner_state = state.inner.read().await.clone(); - *state.inner.write().await = new_state; + inner_state.publish_event(BuilderEvent::ReloadEvent); - Ok(StatusCode::OK) + let ua = get_user_agent(&req_headers); + + info!(ua, relay_check = inner_state.config.pbs_config.relay_check); + + match A::reload(state.clone()).await { + Ok(_) => { + state.inner.read().await.publish_event(BuilderEvent::ReloadResponse); + info!("config reload successful"); + + BEACON_NODE_STATUS.with_label_values(&["200", RELOAD_ENDPOINT_TAG]).inc(); + Ok((StatusCode::OK, "OK")) + } + Err(err) => { + error!(%err, "config reload failed"); + + let err = PbsClientError::Internal("Config reload failed".to_string()); + BEACON_NODE_STATUS + .with_label_values(&[err.status_code().as_str(), RELOAD_ENDPOINT_TAG]) + .inc(); + Err(err) + } + } } diff --git a/crates/pbs/src/routes/router.rs b/crates/pbs/src/routes/router.rs index 1df6be85..5fc192d8 100644 --- a/crates/pbs/src/routes/router.rs +++ b/crates/pbs/src/routes/router.rs @@ -22,7 +22,7 @@ pub fn create_app_router>(state: PbsState)) .route(REGISTER_VALIDATOR_PATH, post(handle_register_validator::)) .route(SUBMIT_BLOCK_PATH, post(handle_submit_block::)) - .route(RELOAD_PATH, post(handle_reload::)); + .route(RELOAD_PATH, post(handle_reload::)); let builder_api = Router::new().nest(BUILDER_API_PATH, builder_routes); diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 38a33bef..32aefd22 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -226,6 +226,10 @@ async fn handle_generate_proxy( async fn handle_reload( State(state): State, ) -> Result { + let req_id = Uuid::new_v4(); + + debug!(event = "reload", ?req_id, "New request"); + let config = StartSignerConfig::load_from_env() .map_err(|err| SignerModuleError::Internal(err.to_string()))?; From 1162b49b32d34a2bd98817514c613a80bc225f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 15:36:26 -0300 Subject: [PATCH 04/23] Take read lock instead of write --- crates/pbs/src/routes/get_header.rs | 3 ++- crates/pbs/src/routes/register_validator.rs | 3 ++- crates/pbs/src/routes/submit_block.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/pbs/src/routes/get_header.rs b/crates/pbs/src/routes/get_header.rs index f2fc1f0a..b67cb78a 100644 --- a/crates/pbs/src/routes/get_header.rs +++ b/crates/pbs/src/routes/get_header.rs @@ -26,7 +26,8 @@ pub async fn handle_get_header>( req_headers: HeaderMap, Path(params): Path, ) -> Result { - let state = state.inner.write().await; + let state = state.inner.read().await; + state.publish_event(BuilderEvent::GetHeaderRequest(params)); let ua = get_user_agent(&req_headers); diff --git a/crates/pbs/src/routes/register_validator.rs b/crates/pbs/src/routes/register_validator.rs index 856d6b0f..750e7ae5 100644 --- a/crates/pbs/src/routes/register_validator.rs +++ b/crates/pbs/src/routes/register_validator.rs @@ -25,7 +25,8 @@ pub async fn handle_register_validator>( req_headers: HeaderMap, Json(registrations): Json>, ) -> Result { - let state = state.inner.write().await; + let state = state.inner.read().await; + trace!(?registrations); state.publish_event(BuilderEvent::RegisterValidatorRequest(registrations.clone())); diff --git a/crates/pbs/src/routes/submit_block.rs b/crates/pbs/src/routes/submit_block.rs index bedfd1a3..1a2318bb 100644 --- a/crates/pbs/src/routes/submit_block.rs +++ b/crates/pbs/src/routes/submit_block.rs @@ -21,7 +21,8 @@ pub async fn handle_submit_block>( req_headers: HeaderMap, Json(signed_blinded_block): Json, ) -> Result { - let state = state.inner.write().await; + let state = state.inner.read().await; + trace!(?signed_blinded_block); state.publish_event(BuilderEvent::SubmitBlockRequest(Box::new(signed_blinded_block.clone()))); From 97a2266f3bd5ed6dd772e86b6f5a90c2206ac25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 15:37:34 -0300 Subject: [PATCH 05/23] Add custom reload to status_api example --- examples/status_api/src/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/status_api/src/main.rs b/examples/status_api/src/main.rs index b78b4a8b..3b49bd79 100644 --- a/examples/status_api/src/main.rs +++ b/examples/status_api/src/main.rs @@ -68,6 +68,16 @@ impl BuilderApi for MyBuilderApi { get_status(req_headers, state).await } + async fn reload(state: PbsState) -> Result<()> { + let (pbs_config, extra_config) = load_pbs_custom_config::().await?; + let mut data = state.inner.read().await.data.clone(); + data.inc_amount = extra_config.inc_amount; + + *state.inner.write().await = InnerPbsState::new(pbs_config).with_data(data); + + Ok(()) + } + fn extra_routes() -> Option>> { let mut router = Router::new(); router = router.route("/check", get(handle_check)); From 4e08196fafb51f3c3f4138f132172332df08b318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 15:40:47 -0300 Subject: [PATCH 06/23] Add function doc --- crates/pbs/src/mev_boost/reload.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pbs/src/mev_boost/reload.rs b/crates/pbs/src/mev_boost/reload.rs index 0764a2fc..74f795f8 100644 --- a/crates/pbs/src/mev_boost/reload.rs +++ b/crates/pbs/src/mev_boost/reload.rs @@ -2,6 +2,8 @@ use cb_common::config::load_pbs_config; use crate::{BuilderApiState, InnerPbsState, PbsState}; +/// Reload the PBS state with the latest configuration in the config file +/// Returns 200 if successful or 500 if failed pub async fn reload(state: PbsState) -> eyre::Result<()> { let pbs_config = load_pbs_config().await?; let new_state = InnerPbsState::new(pbs_config).with_data(state.inner.read().await.data.clone()); From 797bc8779f3365ea54c3b3aeb8b11a950d760816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 15:54:07 -0300 Subject: [PATCH 07/23] Add warning when updating PBS host and port --- crates/pbs/src/mev_boost/reload.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/pbs/src/mev_boost/reload.rs b/crates/pbs/src/mev_boost/reload.rs index 74f795f8..9068ac8f 100644 --- a/crates/pbs/src/mev_boost/reload.rs +++ b/crates/pbs/src/mev_boost/reload.rs @@ -1,12 +1,29 @@ use cb_common::config::load_pbs_config; +use tracing::warn; use crate::{BuilderApiState, InnerPbsState, PbsState}; /// Reload the PBS state with the latest configuration in the config file /// Returns 200 if successful or 500 if failed pub async fn reload(state: PbsState) -> eyre::Result<()> { + let prev_state = state.inner.read().await; + let pbs_config = load_pbs_config().await?; - let new_state = InnerPbsState::new(pbs_config).with_data(state.inner.read().await.data.clone()); + let new_state = InnerPbsState::new(pbs_config).with_data(prev_state.data.clone()); + + if prev_state.config.pbs_config.host != new_state.config.pbs_config.host { + warn!( + "Host change for PBS module require a full restart. Old: {}, New: {}", + prev_state.config.pbs_config.host, new_state.config.pbs_config.host + ); + } + + if prev_state.config.pbs_config.port != new_state.config.pbs_config.port { + warn!( + "Port change for PBS module require a full restart. Old: {}, New: {}", + prev_state.config.pbs_config.port, new_state.config.pbs_config.port + ); + } *state.inner.write().await = new_state; From a2ae3e588e9961165fd4eeaabd2af64f48eb3356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 16:28:05 -0300 Subject: [PATCH 08/23] Improve signer reload logging --- crates/signer/src/service.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 32aefd22..e64b60d1 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -230,21 +230,40 @@ async fn handle_reload( debug!(event = "reload", ?req_id, "New request"); - let config = StartSignerConfig::load_from_env() - .map_err(|err| SignerModuleError::Internal(err.to_string()))?; + let config = match StartSignerConfig::load_from_env() { + Ok(config) => config, + Err(err) => { + error!(event = "reload", ?req_id, error = ?err, "Failed to reload config"); + return Err(SignerModuleError::Internal("failed to reload config".to_string())); + } + }; let proxy_store = if let Some(store) = config.store { - Some(store.init_from_env().map_err(|err| SignerModuleError::Internal(err.to_string()))?) + let store = store.init_from_env(); + match store { + Ok(store) => Some(store), + Err(err) => { + error!(event = "reload", ?req_id, error = ?err, "Failed to reload proxy store"); + return Err(SignerModuleError::Internal("failed to reload config".to_string())); + } + } } else { warn!("Proxy store not configured. Proxies keys and delegations will not be persisted"); None }; - let mut new_manager = SigningManager::new(config.chain, proxy_store) - .map_err(|err| SignerModuleError::Internal(err.to_string()))?; + let mut new_manager = match SigningManager::new(config.chain, proxy_store) { + Ok(manager) => manager, + Err(err) => { + error!(event = "reload", ?req_id, error = ?err, "Failed to reload manager"); + return Err(SignerModuleError::Internal("failed to reload config".to_string())); + } + }; - for signer in - config.loader.load_keys().map_err(|err| SignerModuleError::Internal(err.to_string()))? + for signer in config + .loader + .load_keys() + .map_err(|_| SignerModuleError::Internal("failed to reload config".to_string()))? { new_manager.add_consensus_signer(signer); } From e4d066087a4ad65cffadee9c8be1fcda01ce1d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 13 Jan 2025 17:22:16 -0300 Subject: [PATCH 09/23] Update OpenAPI docs --- api/signer-api.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/api/signer-api.yml b/api/signer-api.yml index 757b9d6c..5668c61d 100644 --- a/api/signer-api.yml +++ b/api/signer-api.yml @@ -254,6 +254,38 @@ paths: type: string example: "Internal error" + /signer/v1/reload: + post: + summary: Reload the signer with the latest configuration in the config file + tags: + - Signer + security: + - BearerAuth: [] + responses: + "200": + description: Success + content: + text/plain: + schema: + type: string + example: "OK" + "500": + description: Internal error + content: + application/json: + schema: + type: object + required: + - code + - message + properties: + code: + type: number + example: 500 + message: + type: string + example: "Internal error" + components: securitySchemes: BearerAuth: From 4d7b60949102f1bf9f1b64b1831e4ce45072d3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 10:14:23 -0300 Subject: [PATCH 10/23] Refactor error --- crates/pbs/src/error.rs | 6 +++--- crates/pbs/src/routes/reload.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/pbs/src/error.rs b/crates/pbs/src/error.rs index d1da66d0..590c03d4 100644 --- a/crates/pbs/src/error.rs +++ b/crates/pbs/src/error.rs @@ -5,7 +5,7 @@ use axum::{http::StatusCode, response::IntoResponse}; pub enum PbsClientError { NoResponse, NoPayload, - Internal(String), + Internal, } impl PbsClientError { @@ -13,7 +13,7 @@ impl PbsClientError { match self { PbsClientError::NoResponse => StatusCode::BAD_GATEWAY, PbsClientError::NoPayload => StatusCode::BAD_GATEWAY, - PbsClientError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + PbsClientError::Internal => StatusCode::INTERNAL_SERVER_ERROR, } } } @@ -23,7 +23,7 @@ impl IntoResponse for PbsClientError { let msg = match &self { PbsClientError::NoResponse => "no response from relays".to_string(), PbsClientError::NoPayload => "no payload from relays".to_string(), - PbsClientError::Internal(msg) => msg.clone(), + PbsClientError::Internal => "internal server error".to_string(), }; (self.status_code(), msg).into_response() diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs index e4da838e..54b62f1d 100644 --- a/crates/pbs/src/routes/reload.rs +++ b/crates/pbs/src/routes/reload.rs @@ -35,7 +35,7 @@ pub async fn handle_reload>( Err(err) => { error!(%err, "config reload failed"); - let err = PbsClientError::Internal("Config reload failed".to_string()); + let err = PbsClientError::Internal; BEACON_NODE_STATUS .with_label_values(&[err.status_code().as_str(), RELOAD_ENDPOINT_TAG]) .inc(); From 92a04decb207cdf00ca3e93df0e04baf65daf921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 10:46:01 -0300 Subject: [PATCH 11/23] Refactor PbsState --- bin/pbs.rs | 4 ++-- bin/src/lib.rs | 2 +- crates/pbs/src/api.rs | 14 +++++------ crates/pbs/src/lib.rs | 2 +- crates/pbs/src/mev_boost/get_header.rs | 4 ++-- .../pbs/src/mev_boost/register_validator.rs | 4 ++-- crates/pbs/src/mev_boost/reload.rs | 10 ++++---- crates/pbs/src/mev_boost/status.rs | 4 ++-- crates/pbs/src/mev_boost/submit_block.rs | 4 ++-- crates/pbs/src/routes/get_header.rs | 6 ++--- crates/pbs/src/routes/register_validator.rs | 6 ++--- crates/pbs/src/routes/reload.rs | 8 +++---- crates/pbs/src/routes/router.rs | 4 ++-- crates/pbs/src/routes/status.rs | 6 ++--- crates/pbs/src/routes/submit_block.rs | 6 ++--- crates/pbs/src/service.rs | 8 +++---- crates/pbs/src/state.rs | 24 +++++-------------- examples/status_api/src/main.rs | 19 +++++++-------- tests/tests/pbs_integration.rs | 14 +++++------ 19 files changed, 67 insertions(+), 82 deletions(-) diff --git a/bin/pbs.rs b/bin/pbs.rs index db6545bd..1514cf10 100644 --- a/bin/pbs.rs +++ b/bin/pbs.rs @@ -2,7 +2,7 @@ use cb_common::{ config::load_pbs_config, utils::{initialize_pbs_tracing_log, wait_for_signal}, }; -use cb_pbs::{DefaultBuilderApi, InnerPbsState, PbsService}; +use cb_pbs::{DefaultBuilderApi, PbsService, PbsState}; use eyre::Result; use tracing::{error, info}; @@ -18,7 +18,7 @@ async fn main() -> Result<()> { let pbs_config = load_pbs_config().await?; - let state = InnerPbsState::new(pbs_config); + let state = PbsState::new(pbs_config); PbsService::init_metrics()?; let server = PbsService::run::<_, DefaultBuilderApi>(state); diff --git a/bin/src/lib.rs b/bin/src/lib.rs index 0f54e531..68c1502c 100644 --- a/bin/src/lib.rs +++ b/bin/src/lib.rs @@ -19,7 +19,7 @@ pub mod prelude { pub use cb_metrics::provider::MetricsProvider; pub use cb_pbs::{ get_header, get_status, register_validator, submit_block, BuilderApi, BuilderApiState, - DefaultBuilderApi, InnerPbsState, PbsService, PbsState, + DefaultBuilderApi, PbsService, PbsState, PbsStateGuard, }; // The TreeHash derive macro requires tree_hash as import pub mod tree_hash { diff --git a/crates/pbs/src/api.rs b/crates/pbs/src/api.rs index 91894f03..1e48a2a9 100644 --- a/crates/pbs/src/api.rs +++ b/crates/pbs/src/api.rs @@ -7,13 +7,13 @@ use cb_common::pbs::{ use crate::{ mev_boost, - state::{BuilderApiState, InnerPbsState, PbsState}, + state::{BuilderApiState, PbsState, PbsStateGuard}, }; #[async_trait] pub trait BuilderApi: 'static { /// Use to extend the BuilderApi - fn extra_routes() -> Option>> { + fn extra_routes() -> Option>> { None } @@ -21,13 +21,13 @@ pub trait BuilderApi: 'static { async fn get_header( params: GetHeaderParams, req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result> { mev_boost::get_header(params, req_headers, state).await } /// https://ethereum.github.io/builder-specs/#/Builder/status - async fn get_status(req_headers: HeaderMap, state: InnerPbsState) -> eyre::Result<()> { + async fn get_status(req_headers: HeaderMap, state: PbsState) -> eyre::Result<()> { mev_boost::get_status(req_headers, state).await } @@ -35,7 +35,7 @@ pub trait BuilderApi: 'static { async fn submit_block( signed_blinded_block: SignedBlindedBeaconBlock, req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result { mev_boost::submit_block(signed_blinded_block, req_headers, state).await } @@ -44,12 +44,12 @@ pub trait BuilderApi: 'static { async fn register_validator( registrations: Vec, req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result<()> { mev_boost::register_validator(registrations, req_headers, state).await } - async fn reload(state: PbsState) -> eyre::Result<()> { + async fn reload(state: PbsStateGuard) -> eyre::Result<()> { mev_boost::reload(state).await } } diff --git a/crates/pbs/src/lib.rs b/crates/pbs/src/lib.rs index 5b6a70bf..8b4afdcf 100644 --- a/crates/pbs/src/lib.rs +++ b/crates/pbs/src/lib.rs @@ -12,4 +12,4 @@ pub use api::*; pub use constants::*; pub use mev_boost::*; pub use service::PbsService; -pub use state::{BuilderApiState, InnerPbsState, PbsState}; +pub use state::{BuilderApiState, PbsState, PbsStateGuard}; diff --git a/crates/pbs/src/mev_boost/get_header.rs b/crates/pbs/src/mev_boost/get_header.rs index 718a8fca..f875a4e1 100644 --- a/crates/pbs/src/mev_boost/get_header.rs +++ b/crates/pbs/src/mev_boost/get_header.rs @@ -32,7 +32,7 @@ use crate::{ GET_HEADER_ENDPOINT_TAG, MAX_SIZE_GET_HEADER, TIMEOUT_ERROR_CODE, TIMEOUT_ERROR_CODE_STR, }, metrics::{RELAY_HEADER_VALUE, RELAY_LAST_SLOT, RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, InnerPbsState}, + state::{BuilderApiState, PbsState}, utils::{check_gas_limit, read_chunked_body_with_max}, }; @@ -41,7 +41,7 @@ use crate::{ pub async fn get_header( params: GetHeaderParams, req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result> { let parent_block = Arc::new(RwLock::new(None)); if state.extra_validation_enabled() { diff --git a/crates/pbs/src/mev_boost/register_validator.rs b/crates/pbs/src/mev_boost/register_validator.rs index bdffb705..6a745319 100644 --- a/crates/pbs/src/mev_boost/register_validator.rs +++ b/crates/pbs/src/mev_boost/register_validator.rs @@ -15,7 +15,7 @@ use url::Url; use crate::{ constants::{MAX_SIZE_DEFAULT, REGISTER_VALIDATOR_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR}, metrics::{RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, InnerPbsState}, + state::{BuilderApiState, PbsState}, utils::read_chunked_body_with_max, }; @@ -24,7 +24,7 @@ use crate::{ pub async fn register_validator( registrations: Vec, req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result<()> { // prepare headers let mut send_headers = HeaderMap::new(); diff --git a/crates/pbs/src/mev_boost/reload.rs b/crates/pbs/src/mev_boost/reload.rs index 9068ac8f..9154344f 100644 --- a/crates/pbs/src/mev_boost/reload.rs +++ b/crates/pbs/src/mev_boost/reload.rs @@ -1,15 +1,15 @@ use cb_common::config::load_pbs_config; use tracing::warn; -use crate::{BuilderApiState, InnerPbsState, PbsState}; +use crate::{BuilderApiState, PbsState, PbsStateGuard}; /// Reload the PBS state with the latest configuration in the config file /// Returns 200 if successful or 500 if failed -pub async fn reload(state: PbsState) -> eyre::Result<()> { - let prev_state = state.inner.read().await; +pub async fn reload(state: PbsStateGuard) -> eyre::Result<()> { + let prev_state = state.read().await; let pbs_config = load_pbs_config().await?; - let new_state = InnerPbsState::new(pbs_config).with_data(prev_state.data.clone()); + let new_state = PbsState::new(pbs_config).with_data(prev_state.data.clone()); if prev_state.config.pbs_config.host != new_state.config.pbs_config.host { warn!( @@ -25,7 +25,7 @@ pub async fn reload(state: PbsState) -> eyre::Result<()> ); } - *state.inner.write().await = new_state; + *state.write().await = new_state; Ok(()) } diff --git a/crates/pbs/src/mev_boost/status.rs b/crates/pbs/src/mev_boost/status.rs index 6f005c4c..7d9d67d2 100644 --- a/crates/pbs/src/mev_boost/status.rs +++ b/crates/pbs/src/mev_boost/status.rs @@ -12,7 +12,7 @@ use tracing::{debug, error}; use crate::{ constants::{MAX_SIZE_DEFAULT, STATUS_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR}, metrics::{RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, InnerPbsState}, + state::{BuilderApiState, PbsState}, utils::read_chunked_body_with_max, }; @@ -21,7 +21,7 @@ use crate::{ /// relay returns 200 pub async fn get_status( req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result<()> { // If no relay check, return early if !state.config.pbs_config.relay_check { diff --git a/crates/pbs/src/mev_boost/submit_block.rs b/crates/pbs/src/mev_boost/submit_block.rs index c1653c12..0d03816b 100644 --- a/crates/pbs/src/mev_boost/submit_block.rs +++ b/crates/pbs/src/mev_boost/submit_block.rs @@ -17,7 +17,7 @@ use url::Url; use crate::{ constants::{MAX_SIZE_SUBMIT_BLOCK, SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG, TIMEOUT_ERROR_CODE_STR}, metrics::{RELAY_LATENCY, RELAY_STATUS_CODE}, - state::{BuilderApiState, InnerPbsState}, + state::{BuilderApiState, PbsState}, utils::read_chunked_body_with_max, }; @@ -25,7 +25,7 @@ use crate::{ pub async fn submit_block( signed_blinded_block: SignedBlindedBeaconBlock, req_headers: HeaderMap, - state: InnerPbsState, + state: PbsState, ) -> eyre::Result { // prepare headers let mut send_headers = HeaderMap::new(); diff --git a/crates/pbs/src/routes/get_header.rs b/crates/pbs/src/routes/get_header.rs index b67cb78a..2b55acb1 100644 --- a/crates/pbs/src/routes/get_header.rs +++ b/crates/pbs/src/routes/get_header.rs @@ -17,16 +17,16 @@ use crate::{ constants::GET_HEADER_ENDPOINT_TAG, error::PbsClientError, metrics::BEACON_NODE_STATUS, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, PbsStateGuard}, }; #[tracing::instrument(skip_all, name = "get_header", fields(req_id = %Uuid::new_v4(), slot = params.slot))] pub async fn handle_get_header>( - State(state): State>, + State(state): State>, req_headers: HeaderMap, Path(params): Path, ) -> Result { - let state = state.inner.read().await; + let state = state.read().await; state.publish_event(BuilderEvent::GetHeaderRequest(params)); diff --git a/crates/pbs/src/routes/register_validator.rs b/crates/pbs/src/routes/register_validator.rs index 750e7ae5..7a058913 100644 --- a/crates/pbs/src/routes/register_validator.rs +++ b/crates/pbs/src/routes/register_validator.rs @@ -16,16 +16,16 @@ use crate::{ constants::REGISTER_VALIDATOR_ENDPOINT_TAG, error::PbsClientError, metrics::BEACON_NODE_STATUS, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, PbsStateGuard}, }; #[tracing::instrument(skip_all, name = "register_validators", fields(req_id = %Uuid::new_v4()))] pub async fn handle_register_validator>( - State(state): State>, + State(state): State>, req_headers: HeaderMap, Json(registrations): Json>, ) -> Result { - let state = state.inner.read().await; + let state = state.read().await; trace!(?registrations); state.publish_event(BuilderEvent::RegisterValidatorRequest(registrations.clone())); diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs index 54b62f1d..8f1a44f4 100644 --- a/crates/pbs/src/routes/reload.rs +++ b/crates/pbs/src/routes/reload.rs @@ -7,16 +7,16 @@ use uuid::Uuid; use crate::{ error::PbsClientError, metrics::BEACON_NODE_STATUS, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, PbsStateGuard}, BuilderApi, RELOAD_ENDPOINT_TAG, }; #[tracing::instrument(skip_all, name = "reload", fields(req_id = %Uuid::new_v4()))] pub async fn handle_reload>( req_headers: HeaderMap, - State(state): State>, + State(state): State>, ) -> Result { - let inner_state = state.inner.read().await.clone(); + let inner_state = state.read().await.clone(); inner_state.publish_event(BuilderEvent::ReloadEvent); @@ -26,7 +26,7 @@ pub async fn handle_reload>( match A::reload(state.clone()).await { Ok(_) => { - state.inner.read().await.publish_event(BuilderEvent::ReloadResponse); + state.read().await.publish_event(BuilderEvent::ReloadResponse); info!("config reload successful"); BEACON_NODE_STATUS.with_label_values(&["200", RELOAD_ENDPOINT_TAG]).inc(); diff --git a/crates/pbs/src/routes/router.rs b/crates/pbs/src/routes/router.rs index 5fc192d8..fd99a2bd 100644 --- a/crates/pbs/src/routes/router.rs +++ b/crates/pbs/src/routes/router.rs @@ -13,10 +13,10 @@ use super::{ }; use crate::{ api::BuilderApi, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, PbsStateGuard}, }; -pub fn create_app_router>(state: PbsState) -> Router { +pub fn create_app_router>(state: PbsStateGuard) -> Router { let builder_routes = Router::new() .route(GET_HEADER_PATH, get(handle_get_header::)) .route(GET_STATUS_PATH, get(handle_get_status::)) diff --git a/crates/pbs/src/routes/status.rs b/crates/pbs/src/routes/status.rs index 6e891b9c..57e724c5 100644 --- a/crates/pbs/src/routes/status.rs +++ b/crates/pbs/src/routes/status.rs @@ -9,15 +9,15 @@ use crate::{ constants::STATUS_ENDPOINT_TAG, error::PbsClientError, metrics::BEACON_NODE_STATUS, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, PbsStateGuard}, }; #[tracing::instrument(skip_all, name = "status", fields(req_id = %Uuid::new_v4()))] pub async fn handle_get_status>( req_headers: HeaderMap, - State(state): State>, + State(state): State>, ) -> Result { - let state = state.inner.read().await; + let state = state.read().await; state.publish_event(BuilderEvent::GetStatusEvent); diff --git a/crates/pbs/src/routes/submit_block.rs b/crates/pbs/src/routes/submit_block.rs index 1a2318bb..3d9a1795 100644 --- a/crates/pbs/src/routes/submit_block.rs +++ b/crates/pbs/src/routes/submit_block.rs @@ -12,16 +12,16 @@ use crate::{ constants::SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG, error::PbsClientError, metrics::BEACON_NODE_STATUS, - state::{BuilderApiState, PbsState}, + state::{BuilderApiState, PbsStateGuard}, }; #[tracing::instrument(skip_all, name = "submit_blinded_block", fields(req_id = %Uuid::new_v4(), slot = signed_blinded_block.message.slot))] pub async fn handle_submit_block>( - State(state): State>, + State(state): State>, req_headers: HeaderMap, Json(signed_blinded_block): Json, ) -> Result { - let state = state.inner.read().await; + let state = state.read().await; trace!(?signed_blinded_block); state.publish_event(BuilderEvent::SubmitBlockRequest(Box::new(signed_blinded_block.clone()))); diff --git a/crates/pbs/src/service.rs b/crates/pbs/src/service.rs index 2f5633e9..46c60254 100644 --- a/crates/pbs/src/service.rs +++ b/crates/pbs/src/service.rs @@ -7,7 +7,7 @@ use cb_common::{ use cb_metrics::provider::MetricsProvider; use eyre::{bail, Context, Result}; use prometheus::core::Collector; -use tokio::net::TcpListener; +use tokio::{net::TcpListener, sync::RwLock}; use tracing::info; use url::Url; @@ -15,19 +15,19 @@ use crate::{ api::BuilderApi, metrics::PBS_METRICS_REGISTRY, routes::create_app_router, - state::{BuilderApiState, InnerPbsState, PbsState}, + state::{BuilderApiState, PbsState}, }; pub struct PbsService; impl PbsService { - pub async fn run>(state: InnerPbsState) -> Result<()> { + pub async fn run>(state: PbsState) -> Result<()> { let addr = state.config.endpoint; let events_subs = state.config.event_publisher.as_ref().map(|e| e.n_subscribers()).unwrap_or_default(); info!(version = COMMIT_BOOST_VERSION, ?addr, events_subs, chain =? state.config.chain, "starting PBS service"); - let app = create_app_router::(PbsState::new(state)); + let app = create_app_router::(RwLock::new(state).into()); let listener = TcpListener::bind(addr).await?; let task = diff --git a/crates/pbs/src/state.rs b/crates/pbs/src/state.rs index bf25a120..95319867 100644 --- a/crates/pbs/src/state.rs +++ b/crates/pbs/src/state.rs @@ -10,42 +10,30 @@ use tokio::sync::RwLock; pub trait BuilderApiState: Clone + Sync + Send + 'static {} impl BuilderApiState for () {} -#[derive(Clone)] -pub struct PbsState { - pub inner: Arc>>, -} - -impl PbsState -where - S: BuilderApiState, -{ - pub fn new(state: InnerPbsState) -> Self { - Self { inner: Arc::new(RwLock::new(state)) } - } -} +pub type PbsStateGuard = Arc>>; /// Config for the Pbs module. It can be extended by adding extra data to the /// state for modules that need it // TODO: consider remove state from the PBS module altogether #[derive(Clone)] -pub struct InnerPbsState { +pub struct PbsState { /// Config data for the Pbs service pub config: PbsModuleConfig, /// Opaque extra data for library use pub data: S, } -impl InnerPbsState<()> { +impl PbsState<()> { pub fn new(config: PbsModuleConfig) -> Self { Self { config, data: () } } - pub fn with_data(self, data: S) -> InnerPbsState { - InnerPbsState { data, config: self.config } + pub fn with_data(self, data: S) -> PbsState { + PbsState { data, config: self.config } } } -impl InnerPbsState +impl PbsState where S: BuilderApiState, { diff --git a/examples/status_api/src/main.rs b/examples/status_api/src/main.rs index 3b49bd79..12b495fb 100644 --- a/examples/status_api/src/main.rs +++ b/examples/status_api/src/main.rs @@ -58,37 +58,34 @@ struct MyBuilderApi; #[async_trait] impl BuilderApi for MyBuilderApi { - async fn get_status( - req_headers: HeaderMap, - state: InnerPbsState, - ) -> Result<()> { + async fn get_status(req_headers: HeaderMap, state: PbsState) -> Result<()> { state.data.inc(); info!("THIS IS A CUSTOM LOG"); CHECK_RECEIVED_COUNTER.inc(); get_status(req_headers, state).await } - async fn reload(state: PbsState) -> Result<()> { + async fn reload(state: PbsStateGuard) -> Result<()> { let (pbs_config, extra_config) = load_pbs_custom_config::().await?; - let mut data = state.inner.read().await.data.clone(); + let mut data = state.read().await.data.clone(); data.inc_amount = extra_config.inc_amount; - *state.inner.write().await = InnerPbsState::new(pbs_config).with_data(data); + *state.write().await = PbsState::new(pbs_config).with_data(data); Ok(()) } - fn extra_routes() -> Option>> { + fn extra_routes() -> Option>> { let mut router = Router::new(); router = router.route("/check", get(handle_check)); Some(router) } } -async fn handle_check(State(state): State>) -> Response { +async fn handle_check(State(state): State>) -> Response { ( StatusCode::OK, - format!("Received {count} status requests!", count = state.inner.write().await.data.get()), + format!("Received {count} status requests!", count = state.read().await.data.get()), ) .into_response() } @@ -101,7 +98,7 @@ async fn main() -> Result<()> { let _guard = initialize_pbs_tracing_log()?; let custom_state = MyBuilderState::from_config(extra); - let state = InnerPbsState::new(pbs_config).with_data(custom_state); + let state = PbsState::new(pbs_config).with_data(custom_state); PbsService::register_metric(Box::new(CHECK_RECEIVED_COUNTER.clone())); PbsService::init_metrics()?; diff --git a/tests/tests/pbs_integration.rs b/tests/tests/pbs_integration.rs index 81eceac7..cc8f5d27 100644 --- a/tests/tests/pbs_integration.rs +++ b/tests/tests/pbs_integration.rs @@ -14,7 +14,7 @@ use cb_common::{ types::Chain, utils::blst_pubkey_to_alloy, }; -use cb_pbs::{DefaultBuilderApi, InnerPbsState, PbsService}; +use cb_pbs::{DefaultBuilderApi, PbsService, PbsState}; use cb_tests::{ mock_relay::{start_mock_relay_service, MockRelayState}, mock_validator::MockValidator, @@ -68,7 +68,7 @@ async fn test_get_header() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), vec![mock_relay]); - let state = InnerPbsState::new(config); + let state = PbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -99,7 +99,7 @@ async fn test_get_status() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 2)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = InnerPbsState::new(config); + let state = PbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -128,7 +128,7 @@ async fn test_register_validators() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = InnerPbsState::new(config); + let state = PbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -157,7 +157,7 @@ async fn test_submit_block() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = InnerPbsState::new(config); + let state = PbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -186,7 +186,7 @@ async fn test_submit_block_too_large() -> Result<()> { tokio::spawn(start_mock_relay_service(mock_state.clone(), port + 1)); let config = to_pbs_config(chain, get_pbs_static_config(port), relays); - let state = InnerPbsState::new(config); + let state = PbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers @@ -233,7 +233,7 @@ async fn test_mux() -> Result<()> { config.muxes = Some(HashMap::from([(validator_pubkey, mux)])); - let state = InnerPbsState::new(config); + let state = PbsState::new(config); tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); // leave some time to start servers From 9f37297bbe09e2cf325c10c357b7c10586331c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 11:38:25 -0300 Subject: [PATCH 12/23] Fix deadlock --- crates/pbs/src/mev_boost/reload.rs | 4 ++-- crates/pbs/src/routes/reload.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/pbs/src/mev_boost/reload.rs b/crates/pbs/src/mev_boost/reload.rs index 9154344f..f8c8cb44 100644 --- a/crates/pbs/src/mev_boost/reload.rs +++ b/crates/pbs/src/mev_boost/reload.rs @@ -6,10 +6,10 @@ use crate::{BuilderApiState, PbsState, PbsStateGuard}; /// Reload the PBS state with the latest configuration in the config file /// Returns 200 if successful or 500 if failed pub async fn reload(state: PbsStateGuard) -> eyre::Result<()> { - let prev_state = state.read().await; + let prev_state = state.read().await.clone(); let pbs_config = load_pbs_config().await?; - let new_state = PbsState::new(pbs_config).with_data(prev_state.data.clone()); + let new_state = PbsState::new(pbs_config).with_data(prev_state.data); if prev_state.config.pbs_config.host != new_state.config.pbs_config.host { warn!( diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs index 8f1a44f4..7eb53c93 100644 --- a/crates/pbs/src/routes/reload.rs +++ b/crates/pbs/src/routes/reload.rs @@ -16,13 +16,13 @@ pub async fn handle_reload>( req_headers: HeaderMap, State(state): State>, ) -> Result { - let inner_state = state.read().await.clone(); + let prev_state = state.read().await.clone(); - inner_state.publish_event(BuilderEvent::ReloadEvent); + prev_state.publish_event(BuilderEvent::ReloadEvent); let ua = get_user_agent(&req_headers); - info!(ua, relay_check = inner_state.config.pbs_config.relay_check); + info!(ua, relay_check = prev_state.config.pbs_config.relay_check); match A::reload(state.clone()).await { Ok(_) => { From 0ec779ef7615a80326e8bd61759244b7b391a8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 12:29:26 -0300 Subject: [PATCH 13/23] Remove authentication from signer reload --- crates/signer/src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index e64b60d1..0c6948bb 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -84,9 +84,9 @@ impl SigningService { .route(REQUEST_SIGNATURE_PATH, post(handle_request_signature)) .route(GET_PUBKEYS_PATH, get(handle_get_pubkeys)) .route(GENERATE_PROXY_KEY_PATH, post(handle_generate_proxy)) + .route_layer(middleware::from_fn_with_state(state.clone(), jwt_auth)) .route(RELOAD_PATH, post(handle_reload)) .with_state(state.clone()) - .route_layer(middleware::from_fn_with_state(state.clone(), jwt_auth)) .route_layer(middleware::from_fn(log_request)); let status_router = axum::Router::new().route(STATUS_PATH, get(handle_status)); From bb08dd130746ea37f27ba0aa6b66904a49bf3cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 12:42:26 -0300 Subject: [PATCH 14/23] Refactor signer --- crates/signer/src/service.rs | 61 +++++++++++++++--------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 0c6948bb..d7965516 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -26,7 +26,7 @@ use cb_common::{ types::{Jwt, ModuleId}, }; use cb_metrics::provider::MetricsProvider; -use eyre::{Context, Result}; +use eyre::{bail, Context}; use headers::{authorization::Bearer, Authorization}; use tokio::{net::TcpListener, sync::RwLock}; use tracing::{debug, error, info, warn}; @@ -57,18 +57,14 @@ impl SigningService { return Ok(()); } - let proxy_store = if let Some(store) = config.store { - Some(store.init_from_env()?) - } else { - warn!("Proxy store not configured. Proxies keys and delegations will not be persisted"); - None + let manager = match start_manager(&config) { + Ok(manager) => manager, + Err(err) => { + error!(error = ?err, "Failed to start signing manager"); + bail!(err); + } }; - let mut manager = SigningManager::new(config.chain, proxy_store)?; - - for signer in config.loader.load_keys()? { - manager.add_consensus_signer(signer); - } let module_ids: Vec = config.jwts.left_values().cloned().map(Into::into).collect(); let loaded_consensus = manager.consensus_pubkeys().len(); @@ -98,7 +94,7 @@ impl SigningService { .wrap_err("signer server exited") } - fn init_metrics() -> Result<()> { + fn init_metrics() -> eyre::Result<()> { MetricsProvider::load_and_run(SIGNER_METRICS_REGISTRY.clone()) } } @@ -238,21 +234,7 @@ async fn handle_reload( } }; - let proxy_store = if let Some(store) = config.store { - let store = store.init_from_env(); - match store { - Ok(store) => Some(store), - Err(err) => { - error!(event = "reload", ?req_id, error = ?err, "Failed to reload proxy store"); - return Err(SignerModuleError::Internal("failed to reload config".to_string())); - } - } - } else { - warn!("Proxy store not configured. Proxies keys and delegations will not be persisted"); - None - }; - - let mut new_manager = match SigningManager::new(config.chain, proxy_store) { + let new_manager = match start_manager(&config) { Ok(manager) => manager, Err(err) => { error!(event = "reload", ?req_id, error = ?err, "Failed to reload manager"); @@ -260,15 +242,24 @@ async fn handle_reload( } }; - for signer in config - .loader - .load_keys() - .map_err(|_| SignerModuleError::Internal("failed to reload config".to_string()))? - { - new_manager.add_consensus_signer(signer); - } - *state.manager.write().await = new_manager; Ok((StatusCode::OK, "OK")) } + +fn start_manager(config: &StartSignerConfig) -> eyre::Result { + let proxy_store = if let Some(store) = config.store.clone() { + Some(store.init_from_env()?) + } else { + warn!("Proxy store not configured. Proxies keys and delegations will not be persisted"); + None + }; + + let mut manager = SigningManager::new(config.chain, proxy_store)?; + + for signer in config.loader.clone().load_keys()? { + manager.add_consensus_signer(signer); + } + + Ok(manager) +} From cafc7a7354611ddc7fb9de60e2dbb3568f142145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 12:42:46 -0300 Subject: [PATCH 15/23] Add status endpoint to OpenAPI --- api/signer-api.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/api/signer-api.yml b/api/signer-api.yml index 5668c61d..3ad8623f 100644 --- a/api/signer-api.yml +++ b/api/signer-api.yml @@ -5,6 +5,7 @@ info: description: API that allows commit modules to request generic signatures from validators tags: - name: Signer + - name: Management paths: /signer/v1/get_pubkeys: get: @@ -254,13 +255,24 @@ paths: type: string example: "Internal error" - /signer/v1/reload: + /status: + get: + summary: Get the status of the Signer API module + tags: + - Management + responses: + "200": + description: Success + content: + text/plain: + schema: + type: string + example: "OK" + /reload: post: summary: Reload the signer with the latest configuration in the config file tags: - - Signer - security: - - BearerAuth: [] + - Management responses: "200": description: Success From acd4b326c95116c050821ecc8bf41cb9cde35dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 13:18:28 -0300 Subject: [PATCH 16/23] Add documentation --- docs/docs/get_started/configuration.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/docs/get_started/configuration.md b/docs/docs/get_started/configuration.md index 36462642..adde64a6 100644 --- a/docs/docs/get_started/configuration.md +++ b/docs/docs/get_started/configuration.md @@ -311,3 +311,18 @@ This approach could also work if you have a multi-beacon-node setup, where some ### Notes - It's up to you to decide which relays will be connected via Commit-Boost (`[[relays]]` section in the `toml` config) and which via Vouch (additional entries in the `relays` field). Remember that any rate-limit will be shared across the two sidecars, if running on the same machine. - You may occasionally see a `timeout` error during registrations, especially if you're running a large number of validators in the same instance. This can resolve itself as registrations will be cleared later in the epoch when relays are less busy processing other registrations. Alternatively you can also adjust the `builderclient.timeout` option in `.vouch.yml`. + +## Hot Reload + +Commit-Boost supports hot-reloading the configuration file. This means that you can modify the `cb-config.toml` file and apply the changes without needing to restart the modules. To do this, you need to send a `POST` request to the `/reload` endpoint on each module you want to reload the configuration. In the case the module is running in a Docker container without the port exposed (like the signer), you can use the following command: + +```bash +docker compose -f cb.docker-compose.yml exec cb_signer curl -X POST http://localhost:20000/reload +``` + +### Notes + +- The hot reload feature is available for PBS modules (both default and custom) and signer module. +- Changes related to listening hosts and ports will not been applied, as it requires the server to be restarted. +- If running in Docker containers, changes in `volumes` will not be applied, as it requires the container to be recreated. Be careful if changing a path to a local file as it may not be accessible from the container. +- Custom PBS modules may override the default behaviour of the hot reload feature to parse extra configuration fields. Check the [examples](https://github.com/Commit-Boost/commit-boost-client/blob/main/examples/status_api/src/main.rs) for more details. From 2481a9571ed885b8942fc9a2ea621e0e7002634f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 14 Jan 2025 13:20:47 -0300 Subject: [PATCH 17/23] Change reload endpoint in PBS --- crates/pbs/src/routes/router.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pbs/src/routes/router.rs b/crates/pbs/src/routes/router.rs index fd99a2bd..54659c5c 100644 --- a/crates/pbs/src/routes/router.rs +++ b/crates/pbs/src/routes/router.rs @@ -21,10 +21,10 @@ pub fn create_app_router>(state: PbsStateGu .route(GET_HEADER_PATH, get(handle_get_header::)) .route(GET_STATUS_PATH, get(handle_get_status::)) .route(REGISTER_VALIDATOR_PATH, post(handle_register_validator::)) - .route(SUBMIT_BLOCK_PATH, post(handle_submit_block::)) - .route(RELOAD_PATH, post(handle_reload::)); + .route(SUBMIT_BLOCK_PATH, post(handle_submit_block::)); + let reload_router = Router::new().route(RELOAD_PATH, post(handle_reload::)); - let builder_api = Router::new().nest(BUILDER_API_PATH, builder_routes); + let builder_api = Router::new().nest(BUILDER_API_PATH, builder_routes).merge(reload_router); let app = if let Some(extra_routes) = A::extra_routes() { builder_api.merge(extra_routes) From d3339f021763a91a0b307a4279a8c0731023b140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Wed, 22 Jan 2025 15:38:52 -0300 Subject: [PATCH 18/23] Release locks earlier --- crates/pbs/src/routes/get_header.rs | 2 +- crates/pbs/src/routes/register_validator.rs | 2 +- crates/pbs/src/routes/status.rs | 2 +- crates/pbs/src/routes/submit_block.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/pbs/src/routes/get_header.rs b/crates/pbs/src/routes/get_header.rs index 2b55acb1..a635cd4b 100644 --- a/crates/pbs/src/routes/get_header.rs +++ b/crates/pbs/src/routes/get_header.rs @@ -26,7 +26,7 @@ pub async fn handle_get_header>( req_headers: HeaderMap, Path(params): Path, ) -> Result { - let state = state.read().await; + let state = state.read().await.clone(); state.publish_event(BuilderEvent::GetHeaderRequest(params)); diff --git a/crates/pbs/src/routes/register_validator.rs b/crates/pbs/src/routes/register_validator.rs index 7a058913..00ca8566 100644 --- a/crates/pbs/src/routes/register_validator.rs +++ b/crates/pbs/src/routes/register_validator.rs @@ -25,7 +25,7 @@ pub async fn handle_register_validator>( req_headers: HeaderMap, Json(registrations): Json>, ) -> Result { - let state = state.read().await; + let state = state.read().await.clone(); trace!(?registrations); state.publish_event(BuilderEvent::RegisterValidatorRequest(registrations.clone())); diff --git a/crates/pbs/src/routes/status.rs b/crates/pbs/src/routes/status.rs index 57e724c5..642d2d3f 100644 --- a/crates/pbs/src/routes/status.rs +++ b/crates/pbs/src/routes/status.rs @@ -17,7 +17,7 @@ pub async fn handle_get_status>( req_headers: HeaderMap, State(state): State>, ) -> Result { - let state = state.read().await; + let state = state.read().await.clone(); state.publish_event(BuilderEvent::GetStatusEvent); diff --git a/crates/pbs/src/routes/submit_block.rs b/crates/pbs/src/routes/submit_block.rs index 3d9a1795..56ad3d41 100644 --- a/crates/pbs/src/routes/submit_block.rs +++ b/crates/pbs/src/routes/submit_block.rs @@ -21,7 +21,7 @@ pub async fn handle_submit_block>( req_headers: HeaderMap, Json(signed_blinded_block): Json, ) -> Result { - let state = state.read().await; + let state = state.read().await.clone(); trace!(?signed_blinded_block); state.publish_event(BuilderEvent::SubmitBlockRequest(Box::new(signed_blinded_block.clone()))); From be8e8a57681896d254609c21d7fce5a1f1b1863a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Wed, 22 Jan 2025 16:15:35 -0300 Subject: [PATCH 19/23] Move write new status to wrapper --- crates/pbs/src/api.rs | 2 +- crates/pbs/src/mev_boost/reload.rs | 20 ++++++++------------ crates/pbs/src/routes/reload.rs | 8 +++++--- examples/status_api/src/main.rs | 8 +++----- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/crates/pbs/src/api.rs b/crates/pbs/src/api.rs index 1e48a2a9..8a76a423 100644 --- a/crates/pbs/src/api.rs +++ b/crates/pbs/src/api.rs @@ -49,7 +49,7 @@ pub trait BuilderApi: 'static { mev_boost::register_validator(registrations, req_headers, state).await } - async fn reload(state: PbsStateGuard) -> eyre::Result<()> { + async fn reload(state: PbsState) -> eyre::Result> { mev_boost::reload(state).await } } diff --git a/crates/pbs/src/mev_boost/reload.rs b/crates/pbs/src/mev_boost/reload.rs index f8c8cb44..0a0555b6 100644 --- a/crates/pbs/src/mev_boost/reload.rs +++ b/crates/pbs/src/mev_boost/reload.rs @@ -1,31 +1,27 @@ use cb_common::config::load_pbs_config; use tracing::warn; -use crate::{BuilderApiState, PbsState, PbsStateGuard}; +use crate::{BuilderApiState, PbsState}; /// Reload the PBS state with the latest configuration in the config file /// Returns 200 if successful or 500 if failed -pub async fn reload(state: PbsStateGuard) -> eyre::Result<()> { - let prev_state = state.read().await.clone(); - +pub async fn reload(state: PbsState) -> eyre::Result> { let pbs_config = load_pbs_config().await?; - let new_state = PbsState::new(pbs_config).with_data(prev_state.data); + let new_state = PbsState::new(pbs_config).with_data(state.data); - if prev_state.config.pbs_config.host != new_state.config.pbs_config.host { + if state.config.pbs_config.host != new_state.config.pbs_config.host { warn!( "Host change for PBS module require a full restart. Old: {}, New: {}", - prev_state.config.pbs_config.host, new_state.config.pbs_config.host + state.config.pbs_config.host, new_state.config.pbs_config.host ); } - if prev_state.config.pbs_config.port != new_state.config.pbs_config.port { + if state.config.pbs_config.port != new_state.config.pbs_config.port { warn!( "Port change for PBS module require a full restart. Old: {}, New: {}", - prev_state.config.pbs_config.port, new_state.config.pbs_config.port + state.config.pbs_config.port, new_state.config.pbs_config.port ); } - *state.write().await = new_state; - - Ok(()) + Ok(new_state) } diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs index 7eb53c93..daf2a7d7 100644 --- a/crates/pbs/src/routes/reload.rs +++ b/crates/pbs/src/routes/reload.rs @@ -24,11 +24,13 @@ pub async fn handle_reload>( info!(ua, relay_check = prev_state.config.pbs_config.relay_check); - match A::reload(state.clone()).await { - Ok(_) => { - state.read().await.publish_event(BuilderEvent::ReloadResponse); + match A::reload(prev_state.clone()).await { + Ok(new_state) => { + prev_state.publish_event(BuilderEvent::ReloadResponse); info!("config reload successful"); + *state.write().await = new_state; + BEACON_NODE_STATUS.with_label_values(&["200", RELOAD_ENDPOINT_TAG]).inc(); Ok((StatusCode::OK, "OK")) } diff --git a/examples/status_api/src/main.rs b/examples/status_api/src/main.rs index 1cd30564..5d8d7b6d 100644 --- a/examples/status_api/src/main.rs +++ b/examples/status_api/src/main.rs @@ -65,14 +65,12 @@ impl BuilderApi for MyBuilderApi { get_status(req_headers, state).await } - async fn reload(state: PbsStateGuard) -> Result<()> { + async fn reload(state: PbsState) -> Result> { let (pbs_config, extra_config) = load_pbs_custom_config::().await?; - let mut data = state.read().await.data.clone(); + let mut data = state.data.clone(); data.inc_amount = extra_config.inc_amount; - *state.write().await = PbsState::new(pbs_config).with_data(data); - - Ok(()) + Ok(PbsState::new(pbs_config).with_data(data)) } fn extra_routes() -> Option>> { From f340bafcf45d3fcf0e2cd35d7f55b055d495c2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Wed, 22 Jan 2025 16:41:48 -0300 Subject: [PATCH 20/23] Replace tokio RwLock with parking_lot --- crates/pbs/src/routes/get_header.rs | 2 +- crates/pbs/src/routes/register_validator.rs | 2 +- crates/pbs/src/routes/reload.rs | 4 ++-- crates/pbs/src/routes/status.rs | 2 +- crates/pbs/src/routes/submit_block.rs | 2 +- crates/pbs/src/service.rs | 3 ++- crates/pbs/src/state.rs | 2 +- examples/status_api/src/main.rs | 5 +---- 8 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/pbs/src/routes/get_header.rs b/crates/pbs/src/routes/get_header.rs index a635cd4b..919bad11 100644 --- a/crates/pbs/src/routes/get_header.rs +++ b/crates/pbs/src/routes/get_header.rs @@ -26,7 +26,7 @@ pub async fn handle_get_header>( req_headers: HeaderMap, Path(params): Path, ) -> Result { - let state = state.read().await.clone(); + let state = state.read().clone(); state.publish_event(BuilderEvent::GetHeaderRequest(params)); diff --git a/crates/pbs/src/routes/register_validator.rs b/crates/pbs/src/routes/register_validator.rs index 00ca8566..4566016f 100644 --- a/crates/pbs/src/routes/register_validator.rs +++ b/crates/pbs/src/routes/register_validator.rs @@ -25,7 +25,7 @@ pub async fn handle_register_validator>( req_headers: HeaderMap, Json(registrations): Json>, ) -> Result { - let state = state.read().await.clone(); + let state = state.read().clone(); trace!(?registrations); state.publish_event(BuilderEvent::RegisterValidatorRequest(registrations.clone())); diff --git a/crates/pbs/src/routes/reload.rs b/crates/pbs/src/routes/reload.rs index daf2a7d7..9b984d3f 100644 --- a/crates/pbs/src/routes/reload.rs +++ b/crates/pbs/src/routes/reload.rs @@ -16,7 +16,7 @@ pub async fn handle_reload>( req_headers: HeaderMap, State(state): State>, ) -> Result { - let prev_state = state.read().await.clone(); + let prev_state = state.read().clone(); prev_state.publish_event(BuilderEvent::ReloadEvent); @@ -29,7 +29,7 @@ pub async fn handle_reload>( prev_state.publish_event(BuilderEvent::ReloadResponse); info!("config reload successful"); - *state.write().await = new_state; + *state.write() = new_state; BEACON_NODE_STATUS.with_label_values(&["200", RELOAD_ENDPOINT_TAG]).inc(); Ok((StatusCode::OK, "OK")) diff --git a/crates/pbs/src/routes/status.rs b/crates/pbs/src/routes/status.rs index 642d2d3f..b4262d1f 100644 --- a/crates/pbs/src/routes/status.rs +++ b/crates/pbs/src/routes/status.rs @@ -17,7 +17,7 @@ pub async fn handle_get_status>( req_headers: HeaderMap, State(state): State>, ) -> Result { - let state = state.read().await.clone(); + let state = state.read().clone(); state.publish_event(BuilderEvent::GetStatusEvent); diff --git a/crates/pbs/src/routes/submit_block.rs b/crates/pbs/src/routes/submit_block.rs index 56ad3d41..c9d206a1 100644 --- a/crates/pbs/src/routes/submit_block.rs +++ b/crates/pbs/src/routes/submit_block.rs @@ -21,7 +21,7 @@ pub async fn handle_submit_block>( req_headers: HeaderMap, Json(signed_blinded_block): Json, ) -> Result { - let state = state.read().await.clone(); + let state = state.read().clone(); trace!(?signed_blinded_block); state.publish_event(BuilderEvent::SubmitBlockRequest(Box::new(signed_blinded_block.clone()))); diff --git a/crates/pbs/src/service.rs b/crates/pbs/src/service.rs index 6f098d43..6b6fd677 100644 --- a/crates/pbs/src/service.rs +++ b/crates/pbs/src/service.rs @@ -7,8 +7,9 @@ use cb_common::{ }; use cb_metrics::provider::MetricsProvider; use eyre::{bail, Context, Result}; +use parking_lot::RwLock; use prometheus::core::Collector; -use tokio::{net::TcpListener, sync::RwLock}; +use tokio::net::TcpListener; use tracing::info; use url::Url; diff --git a/crates/pbs/src/state.rs b/crates/pbs/src/state.rs index 95319867..d2bb2eb9 100644 --- a/crates/pbs/src/state.rs +++ b/crates/pbs/src/state.rs @@ -5,7 +5,7 @@ use cb_common::{ config::{PbsConfig, PbsModuleConfig}, pbs::{BuilderEvent, RelayClient}, }; -use tokio::sync::RwLock; +use parking_lot::RwLock; pub trait BuilderApiState: Clone + Sync + Send + 'static {} impl BuilderApiState for () {} diff --git a/examples/status_api/src/main.rs b/examples/status_api/src/main.rs index 5d8d7b6d..973348bc 100644 --- a/examples/status_api/src/main.rs +++ b/examples/status_api/src/main.rs @@ -81,10 +81,7 @@ impl BuilderApi for MyBuilderApi { } async fn handle_check(State(state): State>) -> Response { - ( - StatusCode::OK, - format!("Received {count} status requests!", count = state.read().await.data.get()), - ) + (StatusCode::OK, format!("Received {count} status requests!", count = state.read().data.get())) .into_response() } From 617327048c2957b9427d036a29188e5d597ec8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Wed, 22 Jan 2025 16:44:17 -0300 Subject: [PATCH 21/23] Update docs --- docs/docs/get_started/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/get_started/configuration.md b/docs/docs/get_started/configuration.md index adde64a6..0f78b4d6 100644 --- a/docs/docs/get_started/configuration.md +++ b/docs/docs/get_started/configuration.md @@ -326,3 +326,4 @@ docker compose -f cb.docker-compose.yml exec cb_signer curl -X POST http://local - Changes related to listening hosts and ports will not been applied, as it requires the server to be restarted. - If running in Docker containers, changes in `volumes` will not be applied, as it requires the container to be recreated. Be careful if changing a path to a local file as it may not be accessible from the container. - Custom PBS modules may override the default behaviour of the hot reload feature to parse extra configuration fields. Check the [examples](https://github.com/Commit-Boost/commit-boost-client/blob/main/examples/status_api/src/main.rs) for more details. +- In case the reload fails (most likely because of some misconfigured option), the server will return a 500 error and the previous configuration will be kept. From 6e956352a6c7e02bd4fbc9da9908205e47f39ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Wed, 22 Jan 2025 17:03:08 -0300 Subject: [PATCH 22/23] Fix error logging --- crates/signer/src/service.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index ef122e5c..f8544137 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -26,7 +26,7 @@ use cb_common::{ types::{Chain, Jwt, ModuleId}, }; use cb_metrics::provider::MetricsProvider; -use eyre::{bail, Context}; +use eyre::Context; use headers::{authorization::Bearer, Authorization}; use tokio::{net::TcpListener, sync::RwLock}; use tracing::{debug, error, info, warn}; @@ -57,13 +57,8 @@ impl SigningService { return Ok(()); } - let manager = match start_manager(&config) { - Ok(manager) => manager, - Err(err) => { - error!(error = ?err, "Failed to start signing manager"); - bail!(err); - } - }; + let manager = start_manager(&config) + .map_err(|err| eyre::eyre!("failed to start signing manager {err}"))?; let module_ids: Vec = config.jwts.left_values().cloned().map(Into::into).collect(); From 53d504447be8a6bc8cd19d888c677e1e98d3d032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Wed, 22 Jan 2025 17:13:01 -0300 Subject: [PATCH 23/23] Remove /reload endpoint from OpenAPI --- api/signer-api.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/api/signer-api.yml b/api/signer-api.yml index 3ad8623f..888b64fb 100644 --- a/api/signer-api.yml +++ b/api/signer-api.yml @@ -268,35 +268,6 @@ paths: schema: type: string example: "OK" - /reload: - post: - summary: Reload the signer with the latest configuration in the config file - tags: - - Management - responses: - "200": - description: Success - content: - text/plain: - schema: - type: string - example: "OK" - "500": - description: Internal error - content: - application/json: - schema: - type: object - required: - - code - - message - properties: - code: - type: number - example: 500 - message: - type: string - example: "Internal error" components: securitySchemes: