From 0bdb4821eabfde4df273fb35c6c0d78cb4eea9f6 Mon Sep 17 00:00:00 2001 From: Mac L Date: Tue, 1 Aug 2023 13:38:22 +1000 Subject: [PATCH] Add http api endpoint --- validator_client/src/beacon_node_fallback.rs | 10 ++--- validator_client/src/beacon_node_health.rs | 11 +++--- validator_client/src/block_service.rs | 8 ++-- validator_client/src/http_api/mod.rs | 40 +++++++++++++++++++- validator_client/src/lib.rs | 1 + 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/validator_client/src/beacon_node_fallback.rs b/validator_client/src/beacon_node_fallback.rs index 759148060ab..4e18246dad9 100644 --- a/validator_client/src/beacon_node_fallback.rs +++ b/validator_client/src/beacon_node_fallback.rs @@ -132,7 +132,7 @@ impl fmt::Display for Errors { } /// Reasons why a candidate might not be ready. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Deserialize, Serialize)] pub enum CandidateError { Uninitialized, Offline, @@ -151,9 +151,9 @@ pub struct CandidateInfo { /// for a query. #[derive(Debug)] pub struct CandidateBeaconNode { - id: usize, - beacon_node: BeaconNodeHttpClient, - health: PLRwLock>, + pub id: usize, + pub beacon_node: BeaconNodeHttpClient, + pub health: PLRwLock>, _phantom: PhantomData, } @@ -324,7 +324,7 @@ impl CandidateBeaconNode { /// identical query. #[derive(Clone, Debug)] pub struct BeaconNodeFallback { - candidates: Arc>>>, + pub candidates: Arc>>>, disable_run_on_all: bool, distance_tiers: BeaconNodeSyncDistanceTiers, slot_clock: Option, diff --git a/validator_client/src/beacon_node_health.rs b/validator_client/src/beacon_node_health.rs index f1a6c7bbec0..ed91d3a2c58 100644 --- a/validator_client/src/beacon_node_health.rs +++ b/validator_client/src/beacon_node_health.rs @@ -1,4 +1,5 @@ use crate::beacon_node_fallback::Config; +use serde_derive::{Deserialize, Serialize}; use slot_clock::SlotClock; use std::cmp::Ordering; use std::fmt::{Debug, Display, Formatter}; @@ -20,7 +21,7 @@ type HealthTier = u8; type SyncDistance = Slot; /// Helpful enum which is used when pattern matching to determine health tier. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum SyncDistanceTier { Synced, Small, @@ -95,19 +96,19 @@ impl Default for BeaconNodeSyncDistanceTiers { /// Execution Node health metrics. /// /// Currently only considers `el_offline`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum ExecutionEngineHealth { Healthy, Unhealthy, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum IsOptimistic { Yes, No, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct BeaconNodeHealthTier { pub tier: HealthTier, pub sync_distance: SyncDistance, @@ -158,7 +159,7 @@ impl BeaconNodeHealthTier { } /// Beacon Node Health metrics. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct BeaconNodeHealth { // The ID of the Beacon Node. This should correspond with its position in the `--beacon-nodes` // list. Note that the ID field is used to tie-break nodes with the same health so that nodes diff --git a/validator_client/src/block_service.rs b/validator_client/src/block_service.rs index b0dfd17ed34..97a15796022 100644 --- a/validator_client/src/block_service.rs +++ b/validator_client/src/block_service.rs @@ -44,8 +44,8 @@ impl From> for BlockError { pub struct BlockServiceBuilder { validator_store: Option>>, slot_clock: Option>, - beacon_nodes: Option>>, - proposer_nodes: Option>>, + pub beacon_nodes: Option>>, + pub proposer_nodes: Option>>, context: Option>, graffiti: Option, graffiti_file: Option, @@ -187,8 +187,8 @@ impl ProposerFallback { pub struct Inner { validator_store: Arc>, slot_clock: Arc, - beacon_nodes: Arc>, - proposer_nodes: Option>>, + pub beacon_nodes: Arc>, + pub proposer_nodes: Option>>, context: RuntimeContext, graffiti: Option, graffiti_file: Option, diff --git a/validator_client/src/http_api/mod.rs b/validator_client/src/http_api/mod.rs index f08c8da1bd9..b69509ffb8b 100644 --- a/validator_client/src/http_api/mod.rs +++ b/validator_client/src/http_api/mod.rs @@ -5,8 +5,10 @@ mod keystores; mod remotekeys; mod tests; +use crate::beacon_node_fallback::CandidateError; +use crate::beacon_node_health::BeaconNodeHealth; use crate::http_api::create_signed_voluntary_exit::create_signed_voluntary_exit; -use crate::{determine_graffiti, GraffitiFile, ValidatorStore}; +use crate::{determine_graffiti, BlockService, GraffitiFile, ValidatorStore}; use account_utils::{ mnemonic_from_phrase, validator_definitions::{SigningDefinition, ValidatorDefinition, Web3SignerDefinition}, @@ -69,6 +71,7 @@ impl From for Error { pub struct Context { pub task_executor: TaskExecutor, pub api_secret: ApiSecret, + pub block_service: Option>, pub validator_store: Option>>, pub validator_dir: Option, pub graffiti_file: Option, @@ -162,6 +165,17 @@ pub fn serve( let signer = ctx.api_secret.signer(); let signer = warp::any().map(move || signer.clone()); + let inner_block_service = ctx.block_service.clone(); + let block_service_filter = warp::any() + .map(move || inner_block_service.clone()) + .and_then(|block_service: Option<_>| async move { + block_service.ok_or_else(|| { + warp_utils::reject::custom_not_found( + "block service is not initialized.".to_string(), + ) + }) + }); + let inner_validator_store = ctx.validator_store.clone(); let validator_store_filter = warp::any() .map(move || inner_validator_store.clone()) @@ -388,6 +402,29 @@ pub fn serve( }, ); + // GET lighthouse/ui/fallback_health + let get_lighthouse_ui_fallback_health = warp::path("lighthouse") + .and(warp::path("ui")) + .and(warp::path("fallback_health")) + .and(warp::path::end()) + .and(signer.clone()) + .and(block_service_filter.clone()) + .and_then(|signer, block_filter: BlockService| async move { + let mut result: HashMap> = + HashMap::new(); + for node in &*block_filter.beacon_nodes.candidates.read().await { + result.insert(node.beacon_node.to_string(), *node.health.read()); + } + if let Some(proposer_nodes) = &block_filter.proposer_nodes { + for node in &*proposer_nodes.candidates.read().await { + result.insert(node.beacon_node.to_string(), *node.health.read()); + } + } + + blocking_signed_json_task(signer, move || Ok(api_types::GenericResponse::from(result))) + .await + }); + // POST lighthouse/validators/ let post_validators = warp::path("lighthouse") .and(warp::path("validators")) @@ -1099,6 +1136,7 @@ pub fn serve( .or(get_lighthouse_validators_pubkey) .or(get_lighthouse_ui_health) .or(get_lighthouse_ui_graffiti) + .or(get_lighthouse_ui_fallback_health) .or(get_fee_recipient) .or(get_gas_limit) .or(get_std_keystores) diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index a29f05638bf..9dbab31a0af 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -574,6 +574,7 @@ impl ProductionValidatorClient { let ctx = Arc::new(http_api::Context { task_executor: self.context.executor.clone(), api_secret, + block_service: Some(self.block_service.clone()), validator_store: Some(self.validator_store.clone()), validator_dir: Some(self.config.validator_dir.clone()), graffiti_file: self.config.graffiti_file.clone(),