Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5477a9d
Update signer config via API endpoint
ManuelBilbao Jan 10, 2025
fb60b24
Update config for PBS module
ManuelBilbao Jan 10, 2025
cf20b43
Add reload endpoint to API
ManuelBilbao Jan 13, 2025
1162b49
Take read lock instead of write
ManuelBilbao Jan 13, 2025
97a2266
Add custom reload to status_api example
ManuelBilbao Jan 13, 2025
4e08196
Add function doc
ManuelBilbao Jan 13, 2025
797bc87
Add warning when updating PBS host and port
ManuelBilbao Jan 13, 2025
a2ae3e5
Improve signer reload logging
ManuelBilbao Jan 13, 2025
e4d0660
Update OpenAPI docs
ManuelBilbao Jan 13, 2025
4d7b609
Refactor error
ManuelBilbao Jan 14, 2025
92a04de
Refactor PbsState
ManuelBilbao Jan 14, 2025
9f37297
Fix deadlock
ManuelBilbao Jan 14, 2025
0ec779e
Remove authentication from signer reload
ManuelBilbao Jan 14, 2025
bb08dd1
Refactor signer
ManuelBilbao Jan 14, 2025
cafc7a7
Add status endpoint to OpenAPI
ManuelBilbao Jan 14, 2025
acd4b32
Add documentation
ManuelBilbao Jan 14, 2025
2481a95
Change reload endpoint in PBS
ManuelBilbao Jan 14, 2025
7f3a904
Merge branch 'main' into mb/hot_reloading
ManuelBilbao Jan 14, 2025
afd6edb
Merge branch 'main' into mb/hot_reloading
ManuelBilbao Jan 15, 2025
9dbdd60
Merge branch 'main' into mb/hot_reloading
ManuelBilbao Jan 22, 2025
d3339f0
Release locks earlier
ManuelBilbao Jan 22, 2025
be8e8a5
Move write new status to wrapper
ManuelBilbao Jan 22, 2025
f340baf
Replace tokio RwLock with parking_lot
ManuelBilbao Jan 22, 2025
6173270
Update docs
ManuelBilbao Jan 22, 2025
6e95635
Fix error logging
ManuelBilbao Jan 22, 2025
53d5044
Remove /reload endpoint from OpenAPI
ManuelBilbao Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions api/signer-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -254,6 +255,20 @@ paths:
type: string
example: "Internal error"

/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"

components:
securitySchemes:
BearerAuth:
Expand Down
2 changes: 1 addition & 1 deletion bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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, PbsService, PbsState, PbsStateGuard,
};
// The TreeHash derive macro requires tree_hash as import
pub mod tree_hash {
Expand Down
1 change: 1 addition & 0 deletions crates/common/src/commit/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
1 change: 1 addition & 0 deletions crates/common/src/pbs/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions crates/common/src/pbs/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum BuilderEvent {
},
RegisterValidatorRequest(Vec<ValidatorRegistration>),
RegisterValidatorResponse,
ReloadEvent,
ReloadResponse,
}

#[derive(Debug, Clone)]
Expand Down
8 changes: 6 additions & 2 deletions crates/pbs/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use cb_common::pbs::{

use crate::{
mev_boost,
state::{BuilderApiState, PbsState},
state::{BuilderApiState, PbsState, PbsStateGuard},
};

#[async_trait]
pub trait BuilderApi<S: BuilderApiState>: 'static {
/// Use to extend the BuilderApi
fn extra_routes() -> Option<Router<PbsState<S>>> {
fn extra_routes() -> Option<Router<PbsStateGuard<S>>> {
None
}

Expand Down Expand Up @@ -48,6 +48,10 @@ pub trait BuilderApi<S: BuilderApiState>: 'static {
) -> eyre::Result<()> {
mev_boost::register_validator(registrations, req_headers, state).await
}

async fn reload(state: PbsState<S>) -> eyre::Result<PbsState<S>> {
mev_boost::reload(state).await
}
}

pub struct DefaultBuilderApi;
Expand Down
1 change: 1 addition & 0 deletions crates/pbs/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 6 additions & 3 deletions crates/pbs/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,25 @@ use axum::{http::StatusCode, response::IntoResponse};
pub enum PbsClientError {
NoResponse,
NoPayload,
Internal,
}

impl PbsClientError {
pub fn status_code(&self) -> StatusCode {
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 => "internal server error".to_string(),
};

(self.status_code(), msg).into_response()
Expand Down
2 changes: 1 addition & 1 deletion crates/pbs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, PbsState, PbsStateGuard};
2 changes: 2 additions & 0 deletions crates/pbs/src/mev_boost/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
27 changes: 27 additions & 0 deletions crates/pbs/src/mev_boost/reload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use cb_common::config::load_pbs_config;
use tracing::warn;

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<S: BuilderApiState>(state: PbsState<S>) -> eyre::Result<PbsState<S>> {
let pbs_config = load_pbs_config().await?;
let new_state = PbsState::new(pbs_config).with_data(state.data);

if state.config.pbs_config.host != new_state.config.pbs_config.host {
warn!(
"Host change for PBS module require a full restart. Old: {}, New: {}",
state.config.pbs_config.host, new_state.config.pbs_config.host
);
}

if state.config.pbs_config.port != new_state.config.pbs_config.port {
warn!(
"Port change for PBS module require a full restart. Old: {}, New: {}",
state.config.pbs_config.port, new_state.config.pbs_config.port
);
}

Ok(new_state)
}
6 changes: 4 additions & 2 deletions crates/pbs/src/routes/get_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ 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<S: BuilderApiState, A: BuilderApi<S>>(
State(state): State<PbsState<S>>,
State(state): State<PbsStateGuard<S>>,
req_headers: HeaderMap,
Path(params): Path<GetHeaderParams>,
) -> Result<impl IntoResponse, PbsClientError> {
let state = state.read().clone();

state.publish_event(BuilderEvent::GetHeaderRequest(params));

let ua = get_user_agent(&req_headers);
Expand Down
1 change: 1 addition & 0 deletions crates/pbs/src/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod get_header;
mod register_validator;
mod reload;
mod router;
mod status;
mod submit_block;
Expand Down
6 changes: 4 additions & 2 deletions crates/pbs/src/routes/register_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ 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<S: BuilderApiState, A: BuilderApi<S>>(
State(state): State<PbsState<S>>,
State(state): State<PbsStateGuard<S>>,
req_headers: HeaderMap,
Json(registrations): Json<Vec<ValidatorRegistration>>,
) -> Result<impl IntoResponse, PbsClientError> {
let state = state.read().clone();

trace!(?registrations);
state.publish_event(BuilderEvent::RegisterValidatorRequest(registrations.clone()));

Expand Down
47 changes: 47 additions & 0 deletions crates/pbs/src/routes/reload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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, PbsStateGuard},
BuilderApi, RELOAD_ENDPOINT_TAG,
};

#[tracing::instrument(skip_all, name = "reload", fields(req_id = %Uuid::new_v4()))]
pub async fn handle_reload<S: BuilderApiState, A: BuilderApi<S>>(
req_headers: HeaderMap,
State(state): State<PbsStateGuard<S>>,
) -> Result<impl IntoResponse, PbsClientError> {
let prev_state = state.read().clone();

prev_state.publish_event(BuilderEvent::ReloadEvent);

let ua = get_user_agent(&req_headers);

info!(ua, relay_check = prev_state.config.pbs_config.relay_check);

match A::reload(prev_state.clone()).await {
Ok(new_state) => {
prev_state.publish_event(BuilderEvent::ReloadResponse);
info!("config reload successful");

*state.write() = new_state;

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;
BEACON_NODE_STATUS
.with_label_values(&[err.status_code().as_str(), RELOAD_ENDPOINT_TAG])
.inc();
Err(err)
}
}
}
15 changes: 10 additions & 5 deletions crates/pbs/src/routes/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@ 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},
state::{BuilderApiState, PbsStateGuard},
};

pub fn create_app_router<S: BuilderApiState, A: BuilderApi<S>>(state: PbsState<S>) -> Router {
pub fn create_app_router<S: BuilderApiState, A: BuilderApi<S>>(state: PbsStateGuard<S>) -> Router {
let builder_routes = Router::new()
.route(GET_HEADER_PATH, get(handle_get_header::<S, A>))
.route(GET_STATUS_PATH, get(handle_get_status::<S, A>))
.route(REGISTER_VALIDATOR_PATH, post(handle_register_validator::<S, A>))
.route(SUBMIT_BLOCK_PATH, post(handle_submit_block::<S, A>));
let reload_router = Router::new().route(RELOAD_PATH, post(handle_reload::<S, A>));

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)
Expand Down
6 changes: 4 additions & 2 deletions crates/pbs/src/routes/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ 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<S: BuilderApiState, A: BuilderApi<S>>(
req_headers: HeaderMap,
State(state): State<PbsState<S>>,
State(state): State<PbsStateGuard<S>>,
) -> Result<impl IntoResponse, PbsClientError> {
let state = state.read().clone();

state.publish_event(BuilderEvent::GetStatusEvent);

let ua = get_user_agent(&req_headers);
Expand Down
6 changes: 4 additions & 2 deletions crates/pbs/src/routes/submit_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ 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<S: BuilderApiState, A: BuilderApi<S>>(
State(state): State<PbsState<S>>,
State(state): State<PbsStateGuard<S>>,
req_headers: HeaderMap,
Json(signed_blinded_block): Json<SignedBlindedBeaconBlock>,
) -> Result<impl IntoResponse, PbsClientError> {
let state = state.read().clone();

trace!(?signed_blinded_block);
state.publish_event(BuilderEvent::SubmitBlockRequest(Box::new(signed_blinded_block.clone())));

Expand Down
3 changes: 2 additions & 1 deletion crates/pbs/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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;
use tracing::info;
Expand All @@ -28,7 +29,7 @@ impl PbsService {
state.config.event_publisher.as_ref().map(|e| e.n_subscribers()).unwrap_or_default();
info!(version = COMMIT_BOOST_VERSION, commit = COMMIT_BOOST_COMMIT, ?addr, events_subs, chain =? state.config.chain, "starting PBS service");

let app = create_app_router::<S, A>(state);
let app = create_app_router::<S, A>(RwLock::new(state).into());
let listener = TcpListener::bind(addr).await?;

let task =
Expand Down
5 changes: 5 additions & 0 deletions crates/pbs/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use std::sync::Arc;

use alloy::rpc::types::beacon::BlsPublicKey;
use cb_common::{
config::{PbsConfig, PbsModuleConfig},
pbs::{BuilderEvent, RelayClient},
};
use parking_lot::RwLock;

pub trait BuilderApiState: Clone + Sync + Send + 'static {}
impl BuilderApiState for () {}

pub type PbsStateGuard<S> = Arc<RwLock<PbsState<S>>>;

/// 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
Expand Down
Loading
Loading