validator client: start http api before genesis (#4714)

## Issue Addressed

On a new network a user might require importing validators before waiting until genesis has occurred.

## Proposed Changes

Starts the validator client http api before waiting for genesis 

## Additional Info

cc @antondlr
This commit is contained in:
João Oliveira 2023-09-15 10:08:30 +00:00
parent b88e57c989
commit d386a07b0c
3 changed files with 51 additions and 50 deletions

View File

@ -8,6 +8,7 @@ use env_logger::{Builder, Env};
use environment::{EnvironmentBuilder, LoggerConfig}; use environment::{EnvironmentBuilder, LoggerConfig};
use eth2_network_config::{Eth2NetworkConfig, DEFAULT_HARDCODED_NETWORK, HARDCODED_NET_NAMES}; use eth2_network_config::{Eth2NetworkConfig, DEFAULT_HARDCODED_NETWORK, HARDCODED_NET_NAMES};
use ethereum_hashing::have_sha_extensions; use ethereum_hashing::have_sha_extensions;
use futures::TryFutureExt;
use lighthouse_version::VERSION; use lighthouse_version::VERSION;
use malloc_utils::configure_memory_allocator; use malloc_utils::configure_memory_allocator;
use slog::{crit, info, warn}; use slog::{crit, info, warn};
@ -659,8 +660,8 @@ fn run<E: EthSpec>(
executor.clone().spawn( executor.clone().spawn(
async move { async move {
if let Err(e) = ProductionValidatorClient::new(context, config) if let Err(e) = ProductionValidatorClient::new(context, config)
.and_then(|mut vc| async move { vc.start_service().await })
.await .await
.and_then(|mut vc| vc.start_service())
{ {
crit!(log, "Failed to start validator client"; "reason" => e); crit!(log, "Failed to start validator client"; "reason" => e);
// Ignore the error since it always occurs during normal operation when // Ignore the error since it always occurs during normal operation when

View File

@ -220,14 +220,13 @@ impl<E: EthSpec> LocalValidatorClient<E> {
config.validator_dir = files.validator_dir.path().into(); config.validator_dir = files.validator_dir.path().into();
config.secrets_dir = files.secrets_dir.path().into(); config.secrets_dir = files.secrets_dir.path().into();
ProductionValidatorClient::new(context, config) let mut client = ProductionValidatorClient::new(context, config).await?;
client
.start_service()
.await .await
.map(move |mut client| { .expect("should start validator services");
client Ok(Self { client, files })
.start_service()
.expect("should start validator services");
Self { client, files }
})
} }
} }

View File

@ -98,6 +98,8 @@ pub struct ProductionValidatorClient<T: EthSpec> {
slot_clock: SystemTimeSlotClock, slot_clock: SystemTimeSlotClock,
http_api_listen_addr: Option<SocketAddr>, http_api_listen_addr: Option<SocketAddr>,
config: Config, config: Config,
beacon_nodes: Arc<BeaconNodeFallback<SystemTimeSlotClock, T>>,
genesis_time: u64,
} }
impl<T: EthSpec> ProductionValidatorClient<T> { impl<T: EthSpec> ProductionValidatorClient<T> {
@ -501,12 +503,6 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
context.service_context("sync_committee".into()), context.service_context("sync_committee".into()),
); );
// Wait until genesis has occurred.
//
// It seems most sensible to move this into the `start_service` function, but I'm caution
// of making too many changes this close to genesis (<1 week).
wait_for_genesis(&beacon_nodes, genesis_time, &context).await?;
Ok(Self { Ok(Self {
context, context,
duties_service, duties_service,
@ -519,10 +515,12 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
config, config,
slot_clock, slot_clock,
http_api_listen_addr: None, http_api_listen_addr: None,
genesis_time,
beacon_nodes,
}) })
} }
pub fn start_service(&mut self) -> Result<(), String> { pub async fn start_service(&mut self) -> Result<(), String> {
// We use `SLOTS_PER_EPOCH` as the capacity of the block notification channel, because // We use `SLOTS_PER_EPOCH` as the capacity of the block notification channel, because
// we don't expect notifications to be delayed by more than a single slot, let alone a // we don't expect notifications to be delayed by more than a single slot, let alone a
// whole epoch! // whole epoch!
@ -530,6 +528,44 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
let (block_service_tx, block_service_rx) = mpsc::channel(channel_capacity); let (block_service_tx, block_service_rx) = mpsc::channel(channel_capacity);
let log = self.context.log(); let log = self.context.log();
let api_secret = ApiSecret::create_or_open(&self.config.validator_dir)?;
self.http_api_listen_addr = if self.config.http_api.enabled {
let ctx = Arc::new(http_api::Context {
task_executor: self.context.executor.clone(),
api_secret,
validator_store: Some(self.validator_store.clone()),
validator_dir: Some(self.config.validator_dir.clone()),
secrets_dir: Some(self.config.secrets_dir.clone()),
graffiti_file: self.config.graffiti_file.clone(),
graffiti_flag: self.config.graffiti,
spec: self.context.eth2_config.spec.clone(),
config: self.config.http_api.clone(),
sse_logging_components: self.context.sse_logging_components.clone(),
slot_clock: self.slot_clock.clone(),
log: log.clone(),
_phantom: PhantomData,
});
let exit = self.context.executor.exit();
let (listen_addr, server) = http_api::serve(ctx, exit)
.map_err(|e| format!("Unable to start HTTP API server: {:?}", e))?;
self.context
.clone()
.executor
.spawn_without_exit(server, "http-api");
Some(listen_addr)
} else {
info!(log, "HTTP API server is disabled");
None
};
// Wait until genesis has occurred.
wait_for_genesis(&self.beacon_nodes, self.genesis_time, &self.context).await?;
duties_service::start_update_service(self.duties_service.clone(), block_service_tx); duties_service::start_update_service(self.duties_service.clone(), block_service_tx);
self.block_service self.block_service
@ -568,41 +604,6 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
spawn_notifier(self).map_err(|e| format!("Failed to start notifier: {}", e))?; spawn_notifier(self).map_err(|e| format!("Failed to start notifier: {}", e))?;
let api_secret = ApiSecret::create_or_open(&self.config.validator_dir)?;
self.http_api_listen_addr = if self.config.http_api.enabled {
let ctx = Arc::new(http_api::Context {
task_executor: self.context.executor.clone(),
api_secret,
validator_store: Some(self.validator_store.clone()),
validator_dir: Some(self.config.validator_dir.clone()),
secrets_dir: Some(self.config.secrets_dir.clone()),
graffiti_file: self.config.graffiti_file.clone(),
graffiti_flag: self.config.graffiti,
spec: self.context.eth2_config.spec.clone(),
config: self.config.http_api.clone(),
sse_logging_components: self.context.sse_logging_components.clone(),
slot_clock: self.slot_clock.clone(),
log: log.clone(),
_phantom: PhantomData,
});
let exit = self.context.executor.exit();
let (listen_addr, server) = http_api::serve(ctx, exit)
.map_err(|e| format!("Unable to start HTTP API server: {:?}", e))?;
self.context
.clone()
.executor
.spawn_without_exit(server, "http-api");
Some(listen_addr)
} else {
info!(log, "HTTP API server is disabled");
None
};
if self.config.enable_latency_measurement_service { if self.config.enable_latency_measurement_service {
latency::start_latency_service( latency::start_latency_service(
self.context.clone(), self.context.clone(),