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:
Paul Hauner
2020-10-02 09:42:19 +00:00
parent 1d278aaa83
commit 6ea3bc5e52
43 changed files with 2882 additions and 172 deletions

View File

@@ -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(())
}
}