mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Implement VC API (#1657)
## Issue Addressed
NA
## Proposed Changes
- Implements a HTTP API for the validator client.
- Creates EIP-2335 keystores with an empty `description` field, instead of a missing `description` field. Adds option to set name.
- Be more graceful with setups without any validators (yet)
- Remove an error log when there are no validators.
- Create the `validator` dir if it doesn't exist.
- Allow building a `ValidatorDir` without a withdrawal keystore (required for the API method where we only post a voting keystore).
- Add optional `description` field to `validator_definitions.yml`
## TODO
- [x] Signature header, as per https://github.com/sigp/lighthouse/issues/1269#issuecomment-649879855
- [x] Return validator descriptions
- [x] Return deposit data
- [x] Respect the mnemonic offset
- [x] Check that mnemonic can derive returned keys
- [x] Be strict about non-localhost
- [x] Allow graceful start without any validators (+ create validator dir)
- [x] Docs final pass
- [x] Swap to EIP-2335 description field.
- [x] Fix Zerioze TODO in VC api types.
- [x] Zeroize secp256k1 key
## Endpoints
- [x] `GET /lighthouse/version`
- [x] `GET /lighthouse/health`
- [x] `GET /lighthouse/validators`
- [x] `POST /lighthouse/validators/hd`
- [x] `POST /lighthouse/validators/keystore`
- [x] `PATCH /lighthouse/validators/:validator_pubkey`
- [ ] ~~`POST /lighthouse/validators/:validator_pubkey/exit/:epoch`~~ Future works
## Additional Info
TBC
This commit is contained in:
@@ -10,6 +10,8 @@ mod notifier;
|
||||
mod validator_duty;
|
||||
mod validator_store;
|
||||
|
||||
pub mod http_api;
|
||||
|
||||
pub use cli::cli_app;
|
||||
pub use config::Config;
|
||||
|
||||
@@ -22,11 +24,14 @@ use environment::RuntimeContext;
|
||||
use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient, StatusCode, Url};
|
||||
use fork_service::{ForkService, ForkServiceBuilder};
|
||||
use futures::channel::mpsc;
|
||||
use http_api::ApiSecret;
|
||||
use initialized_validators::InitializedValidators;
|
||||
use notifier::spawn_notifier;
|
||||
use slog::{error, info, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use slot_clock::SystemTimeSlotClock;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tokio::time::{delay_for, Duration};
|
||||
@@ -42,9 +47,11 @@ const HTTP_TIMEOUT: Duration = Duration::from_secs(12);
|
||||
pub struct ProductionValidatorClient<T: EthSpec> {
|
||||
context: RuntimeContext<T>,
|
||||
duties_service: DutiesService<SystemTimeSlotClock, T>,
|
||||
fork_service: ForkService<SystemTimeSlotClock, T>,
|
||||
fork_service: ForkService<SystemTimeSlotClock>,
|
||||
block_service: BlockService<SystemTimeSlotClock, T>,
|
||||
attestation_service: AttestationService<SystemTimeSlotClock, T>,
|
||||
validator_store: ValidatorStore<SystemTimeSlotClock, T>,
|
||||
http_api_listen_addr: Option<SocketAddr>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
@@ -55,7 +62,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
context: RuntimeContext<T>,
|
||||
cli_args: &ArgMatches<'_>,
|
||||
) -> Result<Self, String> {
|
||||
let config = Config::from_cli(&cli_args)
|
||||
let config = Config::from_cli(&cli_args, context.log())
|
||||
.map_err(|e| format!("Unable to initialize config: {}", e))?;
|
||||
Self::new(context, config).await
|
||||
}
|
||||
@@ -68,7 +75,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
info!(
|
||||
log,
|
||||
"Starting validator client";
|
||||
"beacon_node" => &config.http_server,
|
||||
"beacon_node" => &config.beacon_node,
|
||||
"validator_dir" => format!("{:?}", config.validator_dir),
|
||||
);
|
||||
|
||||
@@ -106,7 +113,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
);
|
||||
|
||||
let beacon_node_url: Url = config
|
||||
.http_server
|
||||
.beacon_node
|
||||
.parse()
|
||||
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?;
|
||||
let beacon_node_http_client = ClientBuilder::new()
|
||||
@@ -144,7 +151,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
let fork_service = ForkServiceBuilder::new()
|
||||
.slot_clock(slot_clock.clone())
|
||||
.beacon_node(beacon_node.clone())
|
||||
.runtime_context(context.service_context("fork".into()))
|
||||
.log(log.clone())
|
||||
.build()?;
|
||||
|
||||
let validator_store: ValidatorStore<SystemTimeSlotClock, T> = ValidatorStore::new(
|
||||
@@ -183,7 +190,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
let attestation_service = AttestationServiceBuilder::new()
|
||||
.duties_service(duties_service.clone())
|
||||
.slot_clock(slot_clock)
|
||||
.validator_store(validator_store)
|
||||
.validator_store(validator_store.clone())
|
||||
.beacon_node(beacon_node)
|
||||
.runtime_context(context.service_context("attestation".into()))
|
||||
.build()?;
|
||||
@@ -194,7 +201,9 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
fork_service,
|
||||
block_service,
|
||||
attestation_service,
|
||||
validator_store,
|
||||
config,
|
||||
http_api_listen_addr: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -204,6 +213,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
// whole epoch!
|
||||
let channel_capacity = T::slots_per_epoch() as usize;
|
||||
let (block_service_tx, block_service_rx) = mpsc::channel(channel_capacity);
|
||||
let log = self.context.log();
|
||||
|
||||
self.duties_service
|
||||
.clone()
|
||||
@@ -215,7 +225,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
|
||||
self.fork_service
|
||||
.clone()
|
||||
.start_update_service(&self.context.eth2_config.spec)
|
||||
.start_update_service(&self.context)
|
||||
.map_err(|e| format!("Unable to start fork service: {}", e))?;
|
||||
|
||||
self.block_service
|
||||
@@ -230,6 +240,35 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
|
||||
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<http_api::Context<SystemTimeSlotClock, T>> = Arc::new(http_api::Context {
|
||||
api_secret,
|
||||
validator_store: Some(self.validator_store.clone()),
|
||||
validator_dir: Some(self.config.validator_dir.clone()),
|
||||
spec: self.context.eth2_config.spec.clone(),
|
||||
config: self.config.http_api.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(async move { server.await }, "http-api");
|
||||
|
||||
Some(listen_addr)
|
||||
} else {
|
||||
info!(log, "HTTP API server is disabled");
|
||||
None
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user