Validator on-boarding docs (#656)

* Add first draft of validator onboarding

* Update docs

* Add documentation link to main README

* Continue docs development

* Update book readme

* Update docs

* Allow vc to run without testnet subcommand

* Small change to onboarding docs

* Tidy CLI help messages

* Update docs

* Add check to val client see if beacon node is synced

* Add notifier service to validator client

* Re-order onboarding steps

* Update deposit contract address

* Update testnet dir

* Add note about public eth1 node

* Set default eth1 endpoint to sigp

* Fix broken test

* Try fix eth1 cache locking

* Be more specific about eth1 endpoint

* Increase gas limit for deposit

* Fix default deposit amount
This commit is contained in:
Paul Hauner
2019-12-09 22:42:36 +11:00
committed by GitHub
parent f1edca30ff
commit 3c6c06a505
19 changed files with 548 additions and 57 deletions

View File

@@ -1,10 +1,10 @@
use crate::validator_store::ValidatorStore;
use environment::RuntimeContext;
use exit_future::Signal;
use futures::{Future, IntoFuture, Stream};
use futures::{future, Future, IntoFuture, Stream};
use parking_lot::RwLock;
use remote_beacon_node::RemoteBeaconNode;
use slog::{crit, error, info, trace, warn};
use slog::{crit, debug, error, info, trace, warn};
use slot_clock::SlotClock;
use std::collections::HashMap;
use std::convert::TryInto;
@@ -74,6 +74,34 @@ pub struct DutiesStore {
}
impl DutiesStore {
/// Returns the total number of validators that should propose in the given epoch.
fn proposer_count(&self, epoch: Epoch) -> usize {
self.store
.read()
.iter()
.filter(|(_validator_pubkey, validator_map)| {
validator_map
.get(&epoch)
.map(|duties| !duties.block_proposal_slots.is_empty())
.unwrap_or_else(|| false)
})
.count()
}
/// Returns the total number of validators that should attest in the given epoch.
fn attester_count(&self, epoch: Epoch) -> usize {
self.store
.read()
.iter()
.filter(|(_validator_pubkey, validator_map)| {
validator_map
.get(&epoch)
.map(|duties| duties.attestation_slot.is_some())
.unwrap_or_else(|| false)
})
.count()
}
fn block_producers(&self, slot: Slot, slots_per_epoch: u64) -> Vec<PublicKey> {
self.store
.read()
@@ -219,7 +247,7 @@ impl<T: SlotClock + 'static, E: EthSpec> DutiesServiceBuilder<T, E> {
pub struct Inner<T, E: EthSpec> {
store: Arc<DutiesStore>,
validator_store: ValidatorStore<T, E>,
slot_clock: T,
pub(crate) slot_clock: T,
beacon_node: RemoteBeaconNode<E>,
context: RuntimeContext<E>,
}
@@ -249,6 +277,21 @@ impl<T, E: EthSpec> Deref for DutiesService<T, E> {
}
impl<T: SlotClock + 'static, E: EthSpec> DutiesService<T, E> {
/// Returns the total number of validators known to the duties service.
pub fn total_validator_count(&self) -> usize {
self.validator_store.num_voting_validators()
}
/// Returns the total number of validators that should propose in the given epoch.
pub fn proposer_count(&self, epoch: Epoch) -> usize {
self.store.proposer_count(epoch)
}
/// Returns the total number of validators that should attest in the given epoch.
pub fn attester_count(&self, epoch: Epoch) -> usize {
self.store.attester_count(epoch)
}
/// Returns the pubkeys of the validators which are assigned to propose in the given slot.
///
/// In normal cases, there should be 0 or 1 validators returned. In extreme cases (i.e., deep forking)
@@ -313,6 +356,7 @@ impl<T: SlotClock + 'static, E: EthSpec> DutiesService<T, E> {
let service_1 = self.clone();
let service_2 = self.clone();
let service_3 = self.clone();
let service_4 = self.clone();
let log_1 = self.context.log.clone();
let log_2 = self.context.log.clone();
@@ -342,24 +386,56 @@ impl<T: SlotClock + 'static, E: EthSpec> DutiesService<T, E> {
})
.and_then(move |epoch| {
let log = service_2.context.log.clone();
service_2.update_epoch(epoch).then(move |result| {
if let Err(e) = result {
error!(
log,
"Failed to get current epoch duties";
"http_error" => format!("{:?}", e)
);
}
let log = service_3.context.log.clone();
service_3.update_epoch(epoch + 1).map_err(move |e| {
service_2
.beacon_node
.http
.beacon()
.get_head()
.map(move |head| (epoch, head.slot.epoch(E::slots_per_epoch())))
.map_err(move |e| {
error!(
log,
"Failed to contact beacon node";
"error" => format!("{:?}", e)
)
})
})
.and_then(move |(current_epoch, beacon_head_epoch)| {
let log = service_3.context.log.clone();
let future: Box<dyn Future<Item = (), Error = ()> + Send> =
if beacon_head_epoch + 1 < current_epoch {
error!(
log,
"Failed to get next epoch duties";
"http_error" => format!("{:?}", e)
"Beacon node is not synced";
"node_head_epoch" => format!("{}", beacon_head_epoch),
"current_epoch" => format!("{}", current_epoch),
);
})
})
Box::new(future::ok(()))
} else {
Box::new(service_3.update_epoch(current_epoch).then(move |result| {
if let Err(e) = result {
error!(
log,
"Failed to get current epoch duties";
"http_error" => format!("{:?}", e)
);
}
let log = service_4.context.log.clone();
service_4.update_epoch(current_epoch + 1).map_err(move |e| {
error!(
log,
"Failed to get next epoch duties";
"http_error" => format!("{:?}", e)
);
})
}))
};
future
})
.map(|_| ())
}
@@ -394,7 +470,7 @@ impl<T: SlotClock + 'static, E: EthSpec> DutiesService<T, E> {
.insert(epoch, duties.clone(), E::slots_per_epoch())
{
InsertOutcome::NewValidator => {
info!(
debug!(
log,
"First duty assignment for validator";
"proposal_slots" => format!("{:?}", &duties.block_proposal_slots),