mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-08 09:16:00 +00:00
Prune old validator client files
This commit is contained in:
@@ -1,23 +0,0 @@
|
|||||||
//TODO: generalise these enums to the crate
|
|
||||||
use crate::block_producer::{BeaconNodeError, PublishOutcome};
|
|
||||||
use types::{Attestation, AttestationData, CommitteeIndex, EthSpec, Slot};
|
|
||||||
|
|
||||||
/// Defines the methods required to produce and publish attestations on a Beacon Node. Abstracts the
|
|
||||||
/// actual beacon node.
|
|
||||||
pub trait BeaconNodeAttestation: Send + Sync {
|
|
||||||
/// Request that the node produces the required attestation data.
|
|
||||||
///
|
|
||||||
fn produce_attestation_data(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
index: CommitteeIndex,
|
|
||||||
) -> Result<AttestationData, BeaconNodeError>;
|
|
||||||
|
|
||||||
/// Request that the node publishes a attestation.
|
|
||||||
///
|
|
||||||
/// Returns `true` if the publish was successful.
|
|
||||||
fn publish_attestation<T: EthSpec>(
|
|
||||||
&self,
|
|
||||||
attestation: Attestation<T>,
|
|
||||||
) -> Result<PublishOutcome, BeaconNodeError>;
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
use super::beacon_node_attestation::BeaconNodeAttestation;
|
|
||||||
use crate::block_producer::{BeaconNodeError, PublishOutcome};
|
|
||||||
use protos::services_grpc::AttestationServiceClient;
|
|
||||||
use ssz::{Decode, Encode};
|
|
||||||
|
|
||||||
use protos::services::{
|
|
||||||
Attestation as GrpcAttestation, ProduceAttestationDataRequest, PublishAttestationRequest,
|
|
||||||
};
|
|
||||||
use types::{Attestation, AttestationData, CommitteeIndex, EthSpec, Slot};
|
|
||||||
|
|
||||||
impl BeaconNodeAttestation for AttestationServiceClient {
|
|
||||||
fn produce_attestation_data(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
index: CommitteeIndex,
|
|
||||||
) -> Result<AttestationData, BeaconNodeError> {
|
|
||||||
let mut req = ProduceAttestationDataRequest::new();
|
|
||||||
req.set_slot(slot.as_u64());
|
|
||||||
req.set_shard(index);
|
|
||||||
|
|
||||||
let reply = self
|
|
||||||
.produce_attestation_data(&req)
|
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
let attestation_data =
|
|
||||||
AttestationData::from_ssz_bytes(reply.get_attestation_data().get_ssz())
|
|
||||||
.map_err(|_| BeaconNodeError::DecodeFailure)?;
|
|
||||||
Ok(attestation_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn publish_attestation<T: EthSpec>(
|
|
||||||
&self,
|
|
||||||
attestation: Attestation<T>,
|
|
||||||
) -> Result<PublishOutcome, BeaconNodeError> {
|
|
||||||
let mut req = PublishAttestationRequest::new();
|
|
||||||
|
|
||||||
let ssz = attestation.as_ssz_bytes();
|
|
||||||
|
|
||||||
let mut grpc_attestation = GrpcAttestation::new();
|
|
||||||
grpc_attestation.set_ssz(ssz);
|
|
||||||
|
|
||||||
req.set_attestation(grpc_attestation);
|
|
||||||
|
|
||||||
let reply = self
|
|
||||||
.publish_attestation(&req)
|
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
if reply.get_success() {
|
|
||||||
Ok(PublishOutcome::Valid)
|
|
||||||
} else {
|
|
||||||
// TODO: distinguish between different errors
|
|
||||||
Ok(PublishOutcome::InvalidAttestation(
|
|
||||||
"Publish failed".to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
mod beacon_node_attestation;
|
|
||||||
mod grpc;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use types::{ChainSpec, Domain, EthSpec, Fork};
|
|
||||||
//TODO: Move these higher up in the crate
|
|
||||||
use super::block_producer::{BeaconNodeError, PublishOutcome, ValidatorEvent};
|
|
||||||
use crate::signer::Signer;
|
|
||||||
use beacon_node_attestation::BeaconNodeAttestation;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use slog::{error, info, warn};
|
|
||||||
use tree_hash::TreeHash;
|
|
||||||
use types::{AggregateSignature, Attestation, AttestationData, AttestationDuty, BitList};
|
|
||||||
|
|
||||||
//TODO: Group these errors at a crate level
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
BeaconNodeError(BeaconNodeError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BeaconNodeError> for Error {
|
|
||||||
fn from(e: BeaconNodeError) -> Error {
|
|
||||||
Error::BeaconNodeError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This struct contains the logic for requesting and signing beacon attestations for a validator. The
|
|
||||||
/// validator can abstractly sign via the Signer trait object.
|
|
||||||
pub struct AttestationProducer<'a, B: BeaconNodeAttestation, S: Signer, E: EthSpec> {
|
|
||||||
/// The current fork.
|
|
||||||
pub fork: Fork,
|
|
||||||
/// The attestation duty to perform.
|
|
||||||
pub duty: AttestationDuty,
|
|
||||||
/// The current epoch.
|
|
||||||
pub spec: Arc<ChainSpec>,
|
|
||||||
/// The beacon node to connect to.
|
|
||||||
pub beacon_node: Arc<B>,
|
|
||||||
/// The signer to sign the block.
|
|
||||||
pub signer: &'a S,
|
|
||||||
/// Used for calculating epoch.
|
|
||||||
pub slots_per_epoch: u64,
|
|
||||||
/// Mere vessel for E.
|
|
||||||
pub _phantom: PhantomData<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, B: BeaconNodeAttestation, S: Signer, E: EthSpec> AttestationProducer<'a, B, S, E> {
|
|
||||||
/// Handle outputs and results from attestation production.
|
|
||||||
pub fn handle_produce_attestation(&mut self, log: slog::Logger) {
|
|
||||||
match self.produce_attestation() {
|
|
||||||
Ok(ValidatorEvent::AttestationProduced(slot)) => info!(
|
|
||||||
log,
|
|
||||||
"Attestation produced";
|
|
||||||
"validator" => format!("{}", self.signer),
|
|
||||||
"slot" => slot,
|
|
||||||
),
|
|
||||||
Err(e) => error!(log, "Attestation production error"; "Error" => format!("{:?}", e)),
|
|
||||||
Ok(ValidatorEvent::SignerRejection(_slot)) => {
|
|
||||||
error!(log, "Attestation production error"; "Error" => "Signer could not sign the attestation".to_string())
|
|
||||||
}
|
|
||||||
Ok(ValidatorEvent::IndexedAttestationNotProduced(_slot)) => {
|
|
||||||
error!(log, "Attestation production error"; "Error" => "Rejected the attestation as it could have been slashed".to_string())
|
|
||||||
}
|
|
||||||
Ok(ValidatorEvent::PublishAttestationFailed) => {
|
|
||||||
error!(log, "Attestation production error"; "Error" => "Beacon node was unable to publish an attestation".to_string())
|
|
||||||
}
|
|
||||||
Ok(ValidatorEvent::InvalidAttestation) => {
|
|
||||||
error!(log, "Attestation production error"; "Error" => "The signed attestation was invalid".to_string())
|
|
||||||
}
|
|
||||||
Ok(v) => {
|
|
||||||
warn!(log, "Unknown result for attestation production"; "Error" => format!("{:?}",v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce an attestation, sign it and send it back
|
|
||||||
///
|
|
||||||
/// Assumes that an attestation is required at this slot (does not check the duties).
|
|
||||||
///
|
|
||||||
/// Ensures the message is not slashable.
|
|
||||||
///
|
|
||||||
/// !!! UNSAFE !!!
|
|
||||||
///
|
|
||||||
/// The slash-protection code is not yet implemented. There is zero protection against
|
|
||||||
/// slashing.
|
|
||||||
pub fn produce_attestation(&mut self) -> Result<ValidatorEvent, Error> {
|
|
||||||
let epoch = self.duty.slot.epoch(self.slots_per_epoch);
|
|
||||||
|
|
||||||
let attestation = self
|
|
||||||
.beacon_node
|
|
||||||
.produce_attestation_data(self.duty.slot, self.duty.index)?;
|
|
||||||
if self.safe_to_produce(&attestation) {
|
|
||||||
let domain = self
|
|
||||||
.spec
|
|
||||||
.get_domain(epoch, Domain::BeaconAttester, &self.fork);
|
|
||||||
if let Some(attestation) = self.sign_attestation(attestation, self.duty, domain) {
|
|
||||||
match self.beacon_node.publish_attestation(attestation) {
|
|
||||||
Ok(PublishOutcome::InvalidAttestation(_string)) => {
|
|
||||||
Ok(ValidatorEvent::InvalidAttestation)
|
|
||||||
}
|
|
||||||
Ok(PublishOutcome::Valid) => {
|
|
||||||
Ok(ValidatorEvent::AttestationProduced(self.duty.slot))
|
|
||||||
}
|
|
||||||
Err(_) | Ok(_) => Ok(ValidatorEvent::PublishAttestationFailed),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(ValidatorEvent::SignerRejection(self.duty.slot))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(ValidatorEvent::IndexedAttestationNotProduced(
|
|
||||||
self.duty.slot,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes an attestation, returning the attestation signed by the validators private key.
|
|
||||||
///
|
|
||||||
/// Important: this function will not check to ensure the attestation is not slashable. This must be
|
|
||||||
/// done upstream.
|
|
||||||
fn sign_attestation(
|
|
||||||
&mut self,
|
|
||||||
attestation: AttestationData,
|
|
||||||
duties: AttestationDuty,
|
|
||||||
domain: u64,
|
|
||||||
) -> Option<Attestation<E>> {
|
|
||||||
self.store_produce(&attestation);
|
|
||||||
|
|
||||||
// build the aggregate signature
|
|
||||||
let aggregate_signature = {
|
|
||||||
let message = attestation.tree_hash_root();
|
|
||||||
|
|
||||||
let sig = self.signer.sign_message(&message, domain)?;
|
|
||||||
|
|
||||||
let mut agg_sig = AggregateSignature::new();
|
|
||||||
agg_sig.add(&sig);
|
|
||||||
agg_sig
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut aggregation_bits = BitList::with_capacity(duties.committee_len).ok()?;
|
|
||||||
aggregation_bits.set(duties.committee_position, true).ok()?;
|
|
||||||
|
|
||||||
Some(Attestation {
|
|
||||||
aggregation_bits,
|
|
||||||
data: attestation,
|
|
||||||
signature: aggregate_signature,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if signing an attestation is safe (non-slashable).
|
|
||||||
///
|
|
||||||
/// !!! UNSAFE !!!
|
|
||||||
///
|
|
||||||
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
|
|
||||||
fn safe_to_produce(&self, _attestation: &AttestationData) -> bool {
|
|
||||||
//TODO: Implement slash protection
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Record that an attestation was produced so that slashable votes may not be made in the future.
|
|
||||||
///
|
|
||||||
/// !!! UNSAFE !!!
|
|
||||||
///
|
|
||||||
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
|
|
||||||
fn store_produce(&mut self, _attestation: &AttestationData) {
|
|
||||||
// TODO: Implement slash protection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
use types::{BeaconBlock, EthSpec, Signature, Slot};
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum BeaconNodeError {
|
|
||||||
RemoteFailure(String),
|
|
||||||
DecodeFailure,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum PublishOutcome {
|
|
||||||
Valid,
|
|
||||||
InvalidBlock(String),
|
|
||||||
InvalidAttestation(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the methods required to produce and publish blocks on a Beacon Node. Abstracts the
|
|
||||||
/// actual beacon node.
|
|
||||||
pub trait BeaconNodeBlock: Send + Sync {
|
|
||||||
/// Request that the node produces a block.
|
|
||||||
///
|
|
||||||
/// Returns Ok(None) if the Beacon Node is unable to produce at the given slot.
|
|
||||||
fn produce_beacon_block<T: EthSpec>(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
randao_reveal: &Signature,
|
|
||||||
) -> Result<Option<BeaconBlock<T>>, BeaconNodeError>;
|
|
||||||
|
|
||||||
/// Request that the node publishes a block.
|
|
||||||
///
|
|
||||||
/// Returns `true` if the publish was successful.
|
|
||||||
fn publish_beacon_block<T: EthSpec>(
|
|
||||||
&self,
|
|
||||||
block: BeaconBlock<T>,
|
|
||||||
) -> Result<PublishOutcome, BeaconNodeError>;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
use super::beacon_node_block::*;
|
|
||||||
use protos::services::{
|
|
||||||
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
|
|
||||||
};
|
|
||||||
use protos::services_grpc::BeaconBlockServiceClient;
|
|
||||||
use ssz::{Decode, Encode};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use types::{BeaconBlock, EthSpec, Signature, Slot};
|
|
||||||
|
|
||||||
//TODO: Remove this new type. Do not need to wrap
|
|
||||||
/// A newtype designed to wrap the gRPC-generated service so the `BeaconNode` trait may be
|
|
||||||
/// implemented upon it.
|
|
||||||
pub struct BeaconBlockGrpcClient {
|
|
||||||
client: Arc<BeaconBlockServiceClient>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconBlockGrpcClient {
|
|
||||||
pub fn new(client: Arc<BeaconBlockServiceClient>) -> Self {
|
|
||||||
Self { client }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconNodeBlock for BeaconBlockGrpcClient {
|
|
||||||
/// Request a Beacon Node (BN) to produce a new block at the supplied slot.
|
|
||||||
///
|
|
||||||
/// Returns `None` if it is not possible to produce at the supplied slot. For example, if the
|
|
||||||
/// BN is unable to find a parent block.
|
|
||||||
fn produce_beacon_block<T: EthSpec>(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
randao_reveal: &Signature,
|
|
||||||
) -> Result<Option<BeaconBlock<T>>, BeaconNodeError> {
|
|
||||||
// request a beacon block from the node
|
|
||||||
let mut req = ProduceBeaconBlockRequest::new();
|
|
||||||
req.set_slot(slot.as_u64());
|
|
||||||
req.set_randao_reveal(randao_reveal.as_ssz_bytes());
|
|
||||||
|
|
||||||
//TODO: Determine if we want an explicit timeout
|
|
||||||
let reply = self
|
|
||||||
.client
|
|
||||||
.produce_beacon_block(&req)
|
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
// format the reply
|
|
||||||
if reply.has_block() {
|
|
||||||
let block = reply.get_block();
|
|
||||||
let ssz = block.get_ssz();
|
|
||||||
|
|
||||||
let block =
|
|
||||||
BeaconBlock::from_ssz_bytes(&ssz).map_err(|_| BeaconNodeError::DecodeFailure)?;
|
|
||||||
|
|
||||||
Ok(Some(block))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request a Beacon Node (BN) to publish a block.
|
|
||||||
///
|
|
||||||
/// Generally, this will be called after a `produce_beacon_block` call with a block that has
|
|
||||||
/// been completed (signed) by the validator client.
|
|
||||||
fn publish_beacon_block<T: EthSpec>(
|
|
||||||
&self,
|
|
||||||
block: BeaconBlock<T>,
|
|
||||||
) -> Result<PublishOutcome, BeaconNodeError> {
|
|
||||||
let mut req = PublishBeaconBlockRequest::new();
|
|
||||||
|
|
||||||
let ssz = block.as_ssz_bytes();
|
|
||||||
|
|
||||||
let mut grpc_block = GrpcBeaconBlock::new();
|
|
||||||
grpc_block.set_ssz(ssz);
|
|
||||||
|
|
||||||
req.set_block(grpc_block);
|
|
||||||
|
|
||||||
let reply = self
|
|
||||||
.client
|
|
||||||
.publish_beacon_block(&req)
|
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
if reply.get_success() {
|
|
||||||
Ok(PublishOutcome::Valid)
|
|
||||||
} else {
|
|
||||||
// TODO: distinguish between different errors
|
|
||||||
Ok(PublishOutcome::InvalidBlock("Publish failed".to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
mod beacon_node_block;
|
|
||||||
mod grpc;
|
|
||||||
|
|
||||||
use self::beacon_node_block::BeaconNodeBlock;
|
|
||||||
pub use self::beacon_node_block::{BeaconNodeError, PublishOutcome};
|
|
||||||
pub use self::grpc::BeaconBlockGrpcClient;
|
|
||||||
use crate::signer::Signer;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use slog::{error, info, trace, warn};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tree_hash::{SignedRoot, TreeHash};
|
|
||||||
use types::{BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Slot};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
BeaconNodeError(BeaconNodeError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ValidatorEvent {
|
|
||||||
/// A new block was produced.
|
|
||||||
BlockProduced(Slot),
|
|
||||||
/// A new attestation was produced.
|
|
||||||
AttestationProduced(Slot),
|
|
||||||
/// A block was not produced as it would have been slashable.
|
|
||||||
SlashableBlockNotProduced(Slot),
|
|
||||||
/// An attestation was not produced as it would have been slashable.
|
|
||||||
IndexedAttestationNotProduced(Slot),
|
|
||||||
/// The Beacon Node was unable to produce a block at that slot.
|
|
||||||
BeaconNodeUnableToProduceBlock(Slot),
|
|
||||||
/// The signer failed to sign the message.
|
|
||||||
SignerRejection(Slot),
|
|
||||||
/// Publishing an attestation failed.
|
|
||||||
PublishAttestationFailed,
|
|
||||||
/// Beacon node rejected the attestation.
|
|
||||||
InvalidAttestation,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This struct contains the logic for requesting and signing beacon blocks for a validator. The
|
|
||||||
/// validator can abstractly sign via the Signer trait object.
|
|
||||||
pub struct BlockProducer<'a, B: BeaconNodeBlock, S: Signer, E: EthSpec> {
|
|
||||||
/// The current fork.
|
|
||||||
pub fork: Fork,
|
|
||||||
/// The current slot to produce a block for.
|
|
||||||
pub slot: Slot,
|
|
||||||
/// The current epoch.
|
|
||||||
pub spec: Arc<ChainSpec>,
|
|
||||||
/// The beacon node to connect to.
|
|
||||||
pub beacon_node: Arc<B>,
|
|
||||||
/// The signer to sign the block.
|
|
||||||
pub signer: &'a S,
|
|
||||||
/// Used for calculating epoch.
|
|
||||||
pub slots_per_epoch: u64,
|
|
||||||
/// Mere vessel for E.
|
|
||||||
pub _phantom: PhantomData<E>,
|
|
||||||
/// The logger, for logging
|
|
||||||
pub log: slog::Logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, B: BeaconNodeBlock, S: Signer, E: EthSpec> BlockProducer<'a, B, S, E> {
|
|
||||||
/// Handle outputs and results from block production.
|
|
||||||
pub fn handle_produce_block(&mut self) {
|
|
||||||
match self.produce_block() {
|
|
||||||
Ok(ValidatorEvent::BlockProduced(slot)) => info!(
|
|
||||||
self.log,
|
|
||||||
"Block produced";
|
|
||||||
"validator" => format!("{}", self.signer),
|
|
||||||
"slot" => slot,
|
|
||||||
),
|
|
||||||
Err(e) => error!(self.log, "Block production error"; "Error" => format!("{:?}", e)),
|
|
||||||
Ok(ValidatorEvent::SignerRejection(_slot)) => {
|
|
||||||
error!(self.log, "Block production error"; "Error" => "Signer Could not sign the block".to_string())
|
|
||||||
}
|
|
||||||
Ok(ValidatorEvent::SlashableBlockNotProduced(_slot)) => {
|
|
||||||
error!(self.log, "Block production error"; "Error" => "Rejected the block as it could have been slashed".to_string())
|
|
||||||
}
|
|
||||||
Ok(ValidatorEvent::BeaconNodeUnableToProduceBlock(_slot)) => {
|
|
||||||
error!(self.log, "Block production error"; "Error" => "Beacon node was unable to produce a block".to_string())
|
|
||||||
}
|
|
||||||
Ok(v) => {
|
|
||||||
warn!(self.log, "Unknown result for block production"; "Error" => format!("{:?}",v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce a block at some slot.
|
|
||||||
///
|
|
||||||
/// Assumes that a block is required at this slot (does not check the duties).
|
|
||||||
///
|
|
||||||
/// Ensures the message is not slashable.
|
|
||||||
///
|
|
||||||
/// !!! UNSAFE !!!
|
|
||||||
///
|
|
||||||
/// The slash-protection code is not yet implemented. There is zero protection against
|
|
||||||
/// slashing.
|
|
||||||
pub fn produce_block(&mut self) -> Result<ValidatorEvent, Error> {
|
|
||||||
let epoch = self.slot.epoch(self.slots_per_epoch);
|
|
||||||
trace!(self.log, "Producing block"; "epoch" => epoch);
|
|
||||||
|
|
||||||
let message = epoch.tree_hash_root();
|
|
||||||
let randao_reveal = match self.signer.sign_message(
|
|
||||||
&message,
|
|
||||||
self.spec.get_domain(epoch, Domain::Randao, &self.fork),
|
|
||||||
) {
|
|
||||||
None => {
|
|
||||||
warn!(self.log, "Signing rejected"; "message" => format!("{:?}", message));
|
|
||||||
return Ok(ValidatorEvent::SignerRejection(self.slot));
|
|
||||||
}
|
|
||||||
Some(signature) => signature,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(block) = self
|
|
||||||
.beacon_node
|
|
||||||
.produce_beacon_block(self.slot, &randao_reveal)?
|
|
||||||
{
|
|
||||||
if self.safe_to_produce(&block) {
|
|
||||||
let slot = block.slot;
|
|
||||||
let domain = self
|
|
||||||
.spec
|
|
||||||
.get_domain(epoch, Domain::BeaconProposer, &self.fork);
|
|
||||||
if let Some(block) = self.sign_block(block, domain) {
|
|
||||||
self.beacon_node.publish_beacon_block(block)?;
|
|
||||||
Ok(ValidatorEvent::BlockProduced(slot))
|
|
||||||
} else {
|
|
||||||
Ok(ValidatorEvent::SignerRejection(self.slot))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(ValidatorEvent::SlashableBlockNotProduced(self.slot))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(ValidatorEvent::BeaconNodeUnableToProduceBlock(self.slot))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes a block, returning that block signed by the validators private key.
|
|
||||||
///
|
|
||||||
/// Important: this function will not check to ensure the block is not slashable. This must be
|
|
||||||
/// done upstream.
|
|
||||||
fn sign_block(&mut self, mut block: BeaconBlock<E>, domain: u64) -> Option<BeaconBlock<E>> {
|
|
||||||
self.store_produce(&block);
|
|
||||||
|
|
||||||
match self.signer.sign_message(&block.signed_root()[..], domain) {
|
|
||||||
None => None,
|
|
||||||
Some(signature) => {
|
|
||||||
block.signature = signature;
|
|
||||||
Some(block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if signing a block is safe (non-slashable).
|
|
||||||
///
|
|
||||||
/// !!! UNSAFE !!!
|
|
||||||
///
|
|
||||||
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
|
|
||||||
fn safe_to_produce(&self, _block: &BeaconBlock<E>) -> bool {
|
|
||||||
// TODO: ensure the producer doesn't produce slashable blocks.
|
|
||||||
// https://github.com/sigp/lighthouse/issues/160
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Record that a block was produced so that slashable votes may not be made in the future.
|
|
||||||
///
|
|
||||||
/// !!! UNSAFE !!!
|
|
||||||
///
|
|
||||||
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
|
|
||||||
fn store_produce(&mut self, _block: &BeaconBlock<E>) {
|
|
||||||
// TODO: record this block production to prevent future slashings.
|
|
||||||
// https://github.com/sigp/lighthouse/issues/160
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BeaconNodeError> for Error {
|
|
||||||
fn from(e: BeaconNodeError) -> Error {
|
|
||||||
Error::BeaconNodeError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Old tests - Re-work for new logic
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::test_utils::{EpochMap, LocalSigner, SimulatedBeaconNode};
|
|
||||||
use super::*;
|
|
||||||
use slot_clock::TestingSlotClock;
|
|
||||||
use types::{
|
|
||||||
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
|
||||||
Keypair,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: implement more thorough testing.
|
|
||||||
// https://github.com/sigp/lighthouse/issues/160
|
|
||||||
//
|
|
||||||
// These tests should serve as a good example for future tests.
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn polling() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
|
||||||
|
|
||||||
let spec = Arc::new(ChainSpec::mainnet());
|
|
||||||
let slot_clock = Arc::new(TestingSlotClock::new(0));
|
|
||||||
let beacon_node = Arc::new(SimulatedBeaconNode::default());
|
|
||||||
let signer = Arc::new(LocalSigner::new(Keypair::random()));
|
|
||||||
|
|
||||||
let mut epoch_map = EpochMap::new(T::slots_per_epoch());
|
|
||||||
let produce_slot = Slot::new(100);
|
|
||||||
let produce_epoch = produce_slot.epoch(T::slots_per_epoch());
|
|
||||||
epoch_map.map.insert(produce_epoch, produce_slot);
|
|
||||||
let epoch_map = Arc::new(epoch_map);
|
|
||||||
|
|
||||||
let mut block_proposer = BlockProducer::new(
|
|
||||||
spec.clone(),
|
|
||||||
epoch_map.clone(),
|
|
||||||
slot_clock.clone(),
|
|
||||||
beacon_node.clone(),
|
|
||||||
signer.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Configure responses from the BeaconNode.
|
|
||||||
beacon_node.set_next_produce_result(Ok(Some(BeaconBlock::random_for_test(&mut rng))));
|
|
||||||
beacon_node.set_next_publish_result(Ok(PublishOutcome::ValidBlock));
|
|
||||||
|
|
||||||
// One slot before production slot...
|
|
||||||
slot_clock.set_slot(produce_slot.as_u64() - 1);
|
|
||||||
assert_eq!(
|
|
||||||
block_proposer.poll(),
|
|
||||||
Ok(PollOutcome::BlockProductionNotRequired(produce_slot - 1))
|
|
||||||
);
|
|
||||||
|
|
||||||
// On the produce slot...
|
|
||||||
slot_clock.set_slot(produce_slot.as_u64());
|
|
||||||
assert_eq!(
|
|
||||||
block_proposer.poll(),
|
|
||||||
Ok(PollOutcome::BlockProduced(produce_slot.into()))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Trying the same produce slot again...
|
|
||||||
slot_clock.set_slot(produce_slot.as_u64());
|
|
||||||
assert_eq!(
|
|
||||||
block_proposer.poll(),
|
|
||||||
Ok(PollOutcome::SlotAlreadyProcessed(produce_slot))
|
|
||||||
);
|
|
||||||
|
|
||||||
// One slot after the produce slot...
|
|
||||||
slot_clock.set_slot(produce_slot.as_u64() + 1);
|
|
||||||
assert_eq!(
|
|
||||||
block_proposer.poll(),
|
|
||||||
Ok(PollOutcome::BlockProductionNotRequired(produce_slot + 1))
|
|
||||||
);
|
|
||||||
|
|
||||||
// In an epoch without known duties...
|
|
||||||
let slot = (produce_epoch.as_u64() + 1) * T::slots_per_epoch();
|
|
||||||
slot_clock.set_slot(slot);
|
|
||||||
assert_eq!(
|
|
||||||
block_proposer.poll(),
|
|
||||||
Ok(PollOutcome::ProducerDutiesUnknown(Slot::new(slot)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
use super::EpochDuties;
|
|
||||||
use types::{Epoch, PublicKey};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum BeaconNodeDutiesError {
|
|
||||||
RemoteFailure(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the methods required to obtain a validators shuffling from a Beacon Node.
|
|
||||||
pub trait BeaconNodeDuties: Send + Sync {
|
|
||||||
/// Gets the duties for all validators.
|
|
||||||
///
|
|
||||||
/// Returns a vector of EpochDuties for each validator public key. The entry will be None for
|
|
||||||
/// validators that are not activated.
|
|
||||||
fn request_duties(
|
|
||||||
&self,
|
|
||||||
epoch: Epoch,
|
|
||||||
pub_keys: &[PublicKey],
|
|
||||||
) -> Result<EpochDuties, BeaconNodeDutiesError>;
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use types::{AttestationDuty, Epoch, PublicKey, Slot};
|
|
||||||
|
|
||||||
/// When work needs to be performed by a validator, this type is given back to the main service
|
|
||||||
/// which indicates all the information that required to process the work.
|
|
||||||
///
|
|
||||||
/// Note: This is calculated per slot, so a validator knows which slot is related to this struct.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct WorkInfo {
|
|
||||||
/// Validator needs to produce a block.
|
|
||||||
pub produce_block: bool,
|
|
||||||
/// Validator needs to produce an attestation. This supplies the required attestation data.
|
|
||||||
pub attestation_duty: Option<AttestationDuty>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The information required for a validator to propose and attest during some epoch.
|
|
||||||
///
|
|
||||||
/// Generally obtained from a Beacon Node, this information contains the validators canonical index
|
|
||||||
/// (their sequence in the global validator induction process) and the "shuffling" for that index
|
|
||||||
/// for some epoch.
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
|
||||||
pub struct EpochDuty {
|
|
||||||
pub block_production_slot: Option<Slot>,
|
|
||||||
pub attestation_duty: AttestationDuty,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EpochDuty {
|
|
||||||
/// Returns `WorkInfo` if work needs to be done in the supplied `slot`
|
|
||||||
pub fn is_work_slot(&self, slot: Slot) -> Option<WorkInfo> {
|
|
||||||
// if validator is required to produce a slot return true
|
|
||||||
let produce_block = match self.block_production_slot {
|
|
||||||
Some(s) if s == slot => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// if the validator is required to attest to a index, create the data
|
|
||||||
let mut attestation_duty = None;
|
|
||||||
if self.attestation_duty.slot == slot {
|
|
||||||
attestation_duty = Some(self.attestation_duty)
|
|
||||||
}
|
|
||||||
|
|
||||||
if produce_block | attestation_duty.is_some() {
|
|
||||||
return Some(WorkInfo {
|
|
||||||
produce_block,
|
|
||||||
attestation_duty,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for EpochDuty {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let mut display_block = String::from("None");
|
|
||||||
if let Some(block_slot) = self.block_production_slot {
|
|
||||||
display_block = block_slot.to_string();
|
|
||||||
}
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"produce block slot: {}, attestation slot: {}, attestation index: {}",
|
|
||||||
display_block, self.attestation_duty.slot, self.attestation_duty.index
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maps a list of keypairs (many validators) to an EpochDuty.
|
|
||||||
pub type EpochDuties = HashMap<PublicKey, Option<EpochDuty>>;
|
|
||||||
|
|
||||||
pub enum EpochDutiesMapError {
|
|
||||||
UnknownEpoch,
|
|
||||||
UnknownValidator,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maps an `epoch` to some `EpochDuties` for a single validator.
|
|
||||||
pub struct EpochDutiesMap {
|
|
||||||
pub slots_per_epoch: u64,
|
|
||||||
pub map: HashMap<Epoch, EpochDuties>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EpochDutiesMap {
|
|
||||||
pub fn new(slots_per_epoch: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
slots_per_epoch,
|
|
||||||
map: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expose the hashmap methods
|
|
||||||
impl Deref for EpochDutiesMap {
|
|
||||||
type Target = HashMap<Epoch, EpochDuties>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.map
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DerefMut for EpochDutiesMap {
|
|
||||||
fn deref_mut(&mut self) -> &mut HashMap<Epoch, EpochDuties> {
|
|
||||||
&mut self.map
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EpochDutiesMap {
|
|
||||||
/// Checks if the validator has work to do.
|
|
||||||
pub fn is_work_slot(
|
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
signer: &PublicKey,
|
|
||||||
) -> Result<Option<WorkInfo>, EpochDutiesMapError> {
|
|
||||||
let epoch = slot.epoch(self.slots_per_epoch);
|
|
||||||
|
|
||||||
let epoch_duties = self
|
|
||||||
.map
|
|
||||||
.get(&epoch)
|
|
||||||
.ok_or_else(|| EpochDutiesMapError::UnknownEpoch)?;
|
|
||||||
if let Some(epoch_duty) = epoch_duties.get(signer) {
|
|
||||||
if let Some(duty) = epoch_duty {
|
|
||||||
// Retrieves the duty for a validator at a given slot
|
|
||||||
Ok(duty.is_work_slot(slot))
|
|
||||||
} else {
|
|
||||||
// the validator isn't active
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// validator isn't known
|
|
||||||
Err(EpochDutiesMapError::UnknownValidator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add tests.
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
use super::beacon_node_duties::{BeaconNodeDuties, BeaconNodeDutiesError};
|
|
||||||
use super::epoch_duties::{EpochDuties, EpochDuty};
|
|
||||||
// to use if we manually specify a timeout
|
|
||||||
//use grpcio::CallOption;
|
|
||||||
use protos::services::{GetDutiesRequest, Validators};
|
|
||||||
use protos::services_grpc::ValidatorServiceClient;
|
|
||||||
use ssz::ssz_encode;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
// use std::time::Duration;
|
|
||||||
use types::{AttestationDuty, Epoch, PublicKey, Slot};
|
|
||||||
|
|
||||||
impl BeaconNodeDuties for ValidatorServiceClient {
|
|
||||||
/// Requests all duties (block signing and committee attesting) from the Beacon Node (BN).
|
|
||||||
fn request_duties(
|
|
||||||
&self,
|
|
||||||
epoch: Epoch,
|
|
||||||
pub_keys: &[PublicKey],
|
|
||||||
) -> Result<EpochDuties, BeaconNodeDutiesError> {
|
|
||||||
// Get the required duties from all validators
|
|
||||||
// build the request
|
|
||||||
let mut req = GetDutiesRequest::new();
|
|
||||||
req.set_epoch(epoch.as_u64());
|
|
||||||
let mut validators = Validators::new();
|
|
||||||
validators.set_public_keys(pub_keys.iter().map(|v| ssz_encode(v)).collect());
|
|
||||||
req.set_validators(validators);
|
|
||||||
|
|
||||||
// set a timeout for requests
|
|
||||||
// let call_opt = CallOption::default().timeout(Duration::from_secs(2));
|
|
||||||
|
|
||||||
// send the request, get the duties reply
|
|
||||||
let reply = self
|
|
||||||
.get_validator_duties(&req)
|
|
||||||
.map_err(|err| BeaconNodeDutiesError::RemoteFailure(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
let mut epoch_duties: HashMap<PublicKey, Option<EpochDuty>> = HashMap::new();
|
|
||||||
for (index, validator_duty) in reply.get_active_validators().iter().enumerate() {
|
|
||||||
if !validator_duty.has_duty() {
|
|
||||||
// validator is inactive
|
|
||||||
epoch_duties.insert(pub_keys[index].clone(), None);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// active validator
|
|
||||||
let active_duty = validator_duty.get_duty();
|
|
||||||
let block_production_slot = {
|
|
||||||
if active_duty.has_block_production_slot() {
|
|
||||||
Some(Slot::from(active_duty.get_block_production_slot()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let attestation_duty = AttestationDuty {
|
|
||||||
slot: Slot::from(active_duty.get_attestation_slot()),
|
|
||||||
index: active_duty.get_attestation_shard(),
|
|
||||||
committee_position: active_duty.get_committee_index() as usize,
|
|
||||||
committee_len: active_duty.get_committee_len() as usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
let epoch_duty = EpochDuty {
|
|
||||||
block_production_slot,
|
|
||||||
attestation_duty,
|
|
||||||
};
|
|
||||||
epoch_duties.insert(pub_keys[index].clone(), Some(epoch_duty));
|
|
||||||
}
|
|
||||||
Ok(epoch_duties)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
mod beacon_node_duties;
|
|
||||||
mod epoch_duties;
|
|
||||||
mod grpc;
|
|
||||||
// TODO: reintroduce tests
|
|
||||||
//#[cfg(test)]
|
|
||||||
//mod test_node;
|
|
||||||
|
|
||||||
pub use self::beacon_node_duties::{BeaconNodeDuties, BeaconNodeDutiesError};
|
|
||||||
use self::epoch_duties::{EpochDuties, EpochDutiesMapError};
|
|
||||||
pub use self::epoch_duties::{EpochDutiesMap, WorkInfo};
|
|
||||||
use super::signer::Signer;
|
|
||||||
use futures::Async;
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use slog::{debug, error, info};
|
|
||||||
use std::fmt::Display;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use types::{Epoch, PublicKey, Slot};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum UpdateOutcome {
|
|
||||||
/// The `EpochDuties` were not updated during this poll.
|
|
||||||
NoChange(Epoch),
|
|
||||||
/// The `EpochDuties` for the `epoch` were previously unknown, but obtained in the poll.
|
|
||||||
NewDuties(Epoch, EpochDuties),
|
|
||||||
/// New `EpochDuties` were obtained, different to those which were previously known. This is
|
|
||||||
/// likely to be the result of chain re-organisation.
|
|
||||||
DutiesChanged(Epoch, EpochDuties),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
DutiesMapPoisoned,
|
|
||||||
BeaconNodeDutiesError(BeaconNodeDutiesError),
|
|
||||||
UnknownEpoch,
|
|
||||||
UnknownValidator,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A polling state machine which ensures the latest `EpochDuties` are obtained from the Beacon
|
|
||||||
/// Node.
|
|
||||||
///
|
|
||||||
/// This keeps track of all validator keys and required voting slots.
|
|
||||||
pub struct DutiesManager<U: BeaconNodeDuties, S: Signer> {
|
|
||||||
pub duties_map: RwLock<EpochDutiesMap>,
|
|
||||||
/// A list of all signer objects known to the validator service.
|
|
||||||
pub signers: Arc<Vec<S>>,
|
|
||||||
pub beacon_node: Arc<U>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U: BeaconNodeDuties, S: Signer + Display> DutiesManager<U, S> {
|
|
||||||
/// Check the Beacon Node for `EpochDuties`.
|
|
||||||
///
|
|
||||||
/// be a wall-clock (e.g., system time, remote server time, etc.).
|
|
||||||
fn update(&self, epoch: Epoch) -> Result<UpdateOutcome, Error> {
|
|
||||||
let public_keys: Vec<PublicKey> = self.signers.iter().map(Signer::to_public).collect();
|
|
||||||
let duties = self.beacon_node.request_duties(epoch, &public_keys)?;
|
|
||||||
{
|
|
||||||
// If these duties were known, check to see if they're updates or identical.
|
|
||||||
if let Some(known_duties) = self.duties_map.read().get(&epoch) {
|
|
||||||
if *known_duties == duties {
|
|
||||||
return Ok(UpdateOutcome::NoChange(epoch));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.duties_map.read().contains_key(&epoch) {
|
|
||||||
//TODO: Remove clone by removing duties from outcome
|
|
||||||
self.duties_map.write().insert(epoch, duties.clone());
|
|
||||||
return Ok(UpdateOutcome::NewDuties(epoch, duties));
|
|
||||||
}
|
|
||||||
// duties have changed
|
|
||||||
//TODO: Duties could be large here. Remove from display and avoid the clone.
|
|
||||||
self.duties_map.write().insert(epoch, duties.clone());
|
|
||||||
Ok(UpdateOutcome::DutiesChanged(epoch, duties))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A future wrapping around `update()`. This will perform logic based upon the update
|
|
||||||
/// process and complete once the update has completed.
|
|
||||||
pub fn run_update(&self, epoch: Epoch, log: slog::Logger) -> Result<Async<()>, ()> {
|
|
||||||
match self.update(epoch) {
|
|
||||||
Err(error) => error!(log, "Epoch duties poll error"; "error" => format!("{:?}", error)),
|
|
||||||
Ok(UpdateOutcome::NoChange(epoch)) => {
|
|
||||||
debug!(log, "No change in duties"; "epoch" => epoch)
|
|
||||||
}
|
|
||||||
Ok(UpdateOutcome::DutiesChanged(epoch, duties)) => {
|
|
||||||
info!(log, "Duties changed (potential re-org)"; "epoch" => epoch, "duties" => format!("{:?}", duties))
|
|
||||||
}
|
|
||||||
Ok(UpdateOutcome::NewDuties(epoch, duties)) => {
|
|
||||||
info!(log, "New duties obtained"; "epoch" => epoch);
|
|
||||||
print_duties(&log, duties);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(Async::Ready(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a list of (index, WorkInfo) indicating all the validators that have work to perform
|
|
||||||
/// this slot.
|
|
||||||
pub fn get_current_work(&self, slot: Slot) -> Option<Vec<(usize, WorkInfo)>> {
|
|
||||||
let mut current_work: Vec<(usize, WorkInfo)> = Vec::new();
|
|
||||||
|
|
||||||
// if the map is poisoned, return None
|
|
||||||
let duties = self.duties_map.read();
|
|
||||||
|
|
||||||
for (index, validator_signer) in self.signers.iter().enumerate() {
|
|
||||||
match duties.is_work_slot(slot, &validator_signer.to_public()) {
|
|
||||||
Ok(Some(work_type)) => current_work.push((index, work_type)),
|
|
||||||
Ok(None) => {} // No work for this validator
|
|
||||||
//TODO: This should really log an error, as we shouldn't end up with an err here.
|
|
||||||
Err(_) => {} // Unknown epoch or validator, no work
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if current_work.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(current_work)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Use error_chain to handle errors
|
|
||||||
impl From<BeaconNodeDutiesError> for Error {
|
|
||||||
fn from(e: BeaconNodeDutiesError) -> Error {
|
|
||||||
Error::BeaconNodeDutiesError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Use error_chain to handle errors
|
|
||||||
impl<T> From<std::sync::PoisonError<T>> for Error {
|
|
||||||
fn from(_e: std::sync::PoisonError<T>) -> Error {
|
|
||||||
Error::DutiesMapPoisoned
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<EpochDutiesMapError> for Error {
|
|
||||||
fn from(e: EpochDutiesMapError) -> Error {
|
|
||||||
match e {
|
|
||||||
EpochDutiesMapError::UnknownEpoch => Error::UnknownEpoch,
|
|
||||||
EpochDutiesMapError::UnknownValidator => Error::UnknownValidator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_duties(log: &slog::Logger, duties: EpochDuties) {
|
|
||||||
for (pk, duty) in duties.iter() {
|
|
||||||
if let Some(display_duty) = duty {
|
|
||||||
info!(log, "Validator: {}",pk; "Duty" => format!("{}",display_duty));
|
|
||||||
} else {
|
|
||||||
info!(log, "Validator: {}",pk; "Duty" => "None");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Modify tests for new Duties Manager form
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::test_node::TestBeaconNode;
|
|
||||||
use super::*;
|
|
||||||
use bls::Keypair;
|
|
||||||
use slot_clock::TestingSlotClock;
|
|
||||||
use types::Slot;
|
|
||||||
|
|
||||||
// TODO: implement more thorough testing.
|
|
||||||
// https://github.com/sigp/lighthouse/issues/160
|
|
||||||
//
|
|
||||||
// These tests should serve as a good example for future tests.
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn polling() {
|
|
||||||
let spec = Arc::new(ChainSpec::mainnet());
|
|
||||||
let duties_map = Arc::new(EpochDutiesMap::new(T::slots_per_epoch()));
|
|
||||||
let keypair = Keypair::random();
|
|
||||||
let slot_clock = Arc::new(TestingSlotClock::new(0));
|
|
||||||
let beacon_node = Arc::new(TestBeaconNode::default());
|
|
||||||
|
|
||||||
let manager = DutiesManager {
|
|
||||||
spec: spec.clone(),
|
|
||||||
pubkey: keypair.pk.clone(),
|
|
||||||
duties_map: duties_map.clone(),
|
|
||||||
slot_clock: slot_clock.clone(),
|
|
||||||
beacon_node: beacon_node.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configure response from the BeaconNode.
|
|
||||||
let duties = EpochDuties {
|
|
||||||
validator_index: 0,
|
|
||||||
block_production_slot: Some(Slot::new(10)),
|
|
||||||
};
|
|
||||||
beacon_node.set_next_shuffling_result(Ok(Some(duties)));
|
|
||||||
|
|
||||||
// Get the duties for the first time...
|
|
||||||
assert_eq!(
|
|
||||||
manager.poll(),
|
|
||||||
Ok(PollOutcome::NewDuties(Epoch::new(0), duties))
|
|
||||||
);
|
|
||||||
// Get the same duties again...
|
|
||||||
assert_eq!(manager.poll(), Ok(PollOutcome::NoChange(Epoch::new(0))));
|
|
||||||
|
|
||||||
// Return new duties.
|
|
||||||
let duties = EpochDuties {
|
|
||||||
validator_index: 0,
|
|
||||||
block_production_slot: Some(Slot::new(11)),
|
|
||||||
};
|
|
||||||
beacon_node.set_next_shuffling_result(Ok(Some(duties)));
|
|
||||||
assert_eq!(
|
|
||||||
manager.poll(),
|
|
||||||
Ok(PollOutcome::DutiesChanged(Epoch::new(0), duties))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Return no duties.
|
|
||||||
beacon_node.set_next_shuffling_result(Ok(None));
|
|
||||||
assert_eq!(
|
|
||||||
manager.poll(),
|
|
||||||
Ok(PollOutcome::UnknownValidatorOrEpoch(Epoch::new(0)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use super::traits::{BeaconNode, BeaconNodeError};
|
|
||||||
use super::EpochDuties;
|
|
||||||
use bls::PublicKey;
|
|
||||||
use std::sync::RwLock;
|
|
||||||
use types::Epoch;
|
|
||||||
|
|
||||||
type ShufflingResult = Result<Option<EpochDuties>, BeaconNodeError>;
|
|
||||||
|
|
||||||
/// A test-only struct used to simulate a Beacon Node.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct TestBeaconNode {
|
|
||||||
pub request_shuffling_input: RwLock<Option<(Epoch, PublicKey)>>,
|
|
||||||
pub request_shuffling_result: RwLock<Option<ShufflingResult>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestBeaconNode {
|
|
||||||
/// Set the result to be returned when `request_shuffling` is called.
|
|
||||||
pub fn set_next_shuffling_result(&self, result: ShufflingResult) {
|
|
||||||
*self.request_shuffling_result.write().unwrap() = Some(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconNode for TestBeaconNode {
|
|
||||||
/// Returns the value specified by the `set_next_shuffling_result`.
|
|
||||||
fn request_shuffling(&self, epoch: Epoch, public_key: &PublicKey) -> ShufflingResult {
|
|
||||||
*self.request_shuffling_input.write().unwrap() = Some((epoch, public_key.clone()));
|
|
||||||
match *self.request_shuffling_result.read().unwrap() {
|
|
||||||
Some(ref r) => r.clone(),
|
|
||||||
None => panic!("TestBeaconNode: produce_result == None"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
use error_chain::error_chain;
|
|
||||||
|
|
||||||
error_chain! {
|
|
||||||
links { }
|
|
||||||
|
|
||||||
errors {
|
|
||||||
SystemTimeError(t: String ) {
|
|
||||||
description("Error reading system time"),
|
|
||||||
display("SystemTimeError: '{}'", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,7 @@ mod block_service;
|
|||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
mod duties_service;
|
mod duties_service;
|
||||||
mod error;
|
|
||||||
mod fork_service;
|
mod fork_service;
|
||||||
mod signer;
|
|
||||||
mod validator_store;
|
mod validator_store;
|
||||||
|
|
||||||
pub mod validator_directory;
|
pub mod validator_directory;
|
||||||
|
|||||||
@@ -1,364 +0,0 @@
|
|||||||
/// The Validator Client service.
|
|
||||||
///
|
|
||||||
/// Connects to a beacon node and negotiates the correct chain id.
|
|
||||||
///
|
|
||||||
/// Once connected, the service loads known validators keypairs from disk. Every slot,
|
|
||||||
/// the service pings the beacon node, asking for new duties for each of the validators.
|
|
||||||
///
|
|
||||||
/// When a validator needs to either produce a block or sign an attestation, it requests the
|
|
||||||
/// data from the beacon node and performs the signing before publishing the block to the beacon
|
|
||||||
/// node.
|
|
||||||
use crate::attestation_producer::AttestationProducer;
|
|
||||||
use crate::block_producer::{BeaconBlockGrpcClient, BlockProducer};
|
|
||||||
use crate::config::Config as ValidatorConfig;
|
|
||||||
use crate::duties::{BeaconNodeDuties, DutiesManager, EpochDutiesMap};
|
|
||||||
use crate::error as error_chain;
|
|
||||||
use crate::signer::Signer;
|
|
||||||
use eth2_config::Eth2Config;
|
|
||||||
use grpcio::{ChannelBuilder, EnvBuilder};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use protos::services::Empty;
|
|
||||||
use protos::services_grpc::{
|
|
||||||
AttestationServiceClient, BeaconBlockServiceClient, BeaconNodeServiceClient,
|
|
||||||
ValidatorServiceClient,
|
|
||||||
};
|
|
||||||
use slog::{crit, error, info, trace, warn};
|
|
||||||
use slot_clock::{SlotClock, SystemTimeSlotClock};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
|
||||||
use types::{ChainSpec, Epoch, EthSpec, Fork, Keypair, Slot};
|
|
||||||
|
|
||||||
/// The validator service. This is the main thread that executes and maintains validator
|
|
||||||
/// duties.
|
|
||||||
//TODO: Generalize the BeaconNode types to use testing
|
|
||||||
pub struct Service<B: BeaconNodeDuties + 'static, S: Signer + 'static, E: EthSpec> {
|
|
||||||
/// The node's current fork version we are processing on.
|
|
||||||
fork: Fork,
|
|
||||||
/// The slot clock for this service.
|
|
||||||
pub slot_clock: SystemTimeSlotClock,
|
|
||||||
/// The slot that is currently, or was previously processed by the service.
|
|
||||||
current_slot: RwLock<Option<Slot>>,
|
|
||||||
slots_per_epoch: u64,
|
|
||||||
/// The chain specification for this clients instance.
|
|
||||||
pub spec: Arc<ChainSpec>,
|
|
||||||
/// The duties manager which maintains the state of when to perform actions.
|
|
||||||
duties_manager: Arc<DutiesManager<B, S>>,
|
|
||||||
// GRPC Clients
|
|
||||||
/// The beacon block GRPC client.
|
|
||||||
beacon_block_client: Arc<BeaconBlockGrpcClient>,
|
|
||||||
/// The attester GRPC client.
|
|
||||||
attestation_client: Arc<AttestationServiceClient>,
|
|
||||||
/// The validator client logger.
|
|
||||||
log: slog::Logger,
|
|
||||||
_phantom: PhantomData<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: EthSpec> Service<ValidatorServiceClient, Keypair, E> {
|
|
||||||
/// Initial connection to the beacon node to determine its properties.
|
|
||||||
///
|
|
||||||
/// This tries to connect to a beacon node. Once connected, it initialised the gRPC clients
|
|
||||||
/// and returns an instance of the service.
|
|
||||||
pub fn initialize_service(
|
|
||||||
client_config: ValidatorConfig,
|
|
||||||
eth2_config: Eth2Config,
|
|
||||||
log: slog::Logger,
|
|
||||||
) -> error_chain::Result<Service<ValidatorServiceClient, Keypair, E>> {
|
|
||||||
let server_url = format!(
|
|
||||||
"{}:{}",
|
|
||||||
client_config.server, client_config.server_grpc_port
|
|
||||||
);
|
|
||||||
|
|
||||||
let env = Arc::new(EnvBuilder::new().build());
|
|
||||||
// Beacon node gRPC beacon node endpoints.
|
|
||||||
let beacon_node_client = {
|
|
||||||
let ch = ChannelBuilder::new(env.clone()).connect(&server_url);
|
|
||||||
BeaconNodeServiceClient::new(ch)
|
|
||||||
};
|
|
||||||
|
|
||||||
// retrieve node information and validate the beacon node
|
|
||||||
let node_info = loop {
|
|
||||||
match beacon_node_client.info(&Empty::new()) {
|
|
||||||
Err(e) => {
|
|
||||||
let retry_seconds = 5;
|
|
||||||
warn!(
|
|
||||||
log,
|
|
||||||
"Could not connect to beacon node";
|
|
||||||
"error" => format!("{:?}", e),
|
|
||||||
"retry_in" => format!("{} seconds", retry_seconds),
|
|
||||||
);
|
|
||||||
std::thread::sleep(Duration::from_secs(retry_seconds));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Ok(info) => {
|
|
||||||
// verify the node's network id
|
|
||||||
if eth2_config.spec.network_id != info.network_id as u8 {
|
|
||||||
error!(
|
|
||||||
log,
|
|
||||||
"Beacon Node's genesis time is in the future. No work to do.\n Exiting"
|
|
||||||
);
|
|
||||||
return Err(format!("Beacon node has the wrong chain id. Expected chain id: {}, node's chain id: {}", eth2_config.spec.network_id, info.network_id).into());
|
|
||||||
}
|
|
||||||
break info;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// build requisite objects to form Self
|
|
||||||
let genesis_time = node_info.get_genesis_time();
|
|
||||||
let genesis_slot = Slot::from(node_info.get_genesis_slot());
|
|
||||||
|
|
||||||
info!(
|
|
||||||
log,
|
|
||||||
"Beacon node connected";
|
|
||||||
"version" => node_info.version.clone(),
|
|
||||||
"network_id" => node_info.network_id,
|
|
||||||
"genesis_time" => genesis_time
|
|
||||||
);
|
|
||||||
|
|
||||||
let proto_fork = node_info.get_fork();
|
|
||||||
let mut previous_version: [u8; 4] = [0; 4];
|
|
||||||
let mut current_version: [u8; 4] = [0; 4];
|
|
||||||
previous_version.copy_from_slice(&proto_fork.get_previous_version()[..4]);
|
|
||||||
current_version.copy_from_slice(&proto_fork.get_current_version()[..4]);
|
|
||||||
let fork = Fork {
|
|
||||||
previous_version,
|
|
||||||
current_version,
|
|
||||||
epoch: Epoch::from(proto_fork.get_epoch()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// initialize the RPC clients
|
|
||||||
|
|
||||||
// Beacon node gRPC beacon block endpoints.
|
|
||||||
let beacon_block_client = {
|
|
||||||
let ch = ChannelBuilder::new(env.clone()).connect(&server_url);
|
|
||||||
let beacon_block_service_client = Arc::new(BeaconBlockServiceClient::new(ch));
|
|
||||||
// a wrapper around the service client to implement the beacon block node trait
|
|
||||||
Arc::new(BeaconBlockGrpcClient::new(beacon_block_service_client))
|
|
||||||
};
|
|
||||||
|
|
||||||
// Beacon node gRPC validator endpoints.
|
|
||||||
let validator_client = {
|
|
||||||
let ch = ChannelBuilder::new(env.clone()).connect(&server_url);
|
|
||||||
Arc::new(ValidatorServiceClient::new(ch))
|
|
||||||
};
|
|
||||||
|
|
||||||
//Beacon node gRPC attester endpoints.
|
|
||||||
let attestation_client = {
|
|
||||||
let ch = ChannelBuilder::new(env.clone()).connect(&server_url);
|
|
||||||
Arc::new(AttestationServiceClient::new(ch))
|
|
||||||
};
|
|
||||||
|
|
||||||
// build the validator slot clock
|
|
||||||
let slot_clock = SystemTimeSlotClock::new(
|
|
||||||
genesis_slot,
|
|
||||||
Duration::from_secs(genesis_time),
|
|
||||||
Duration::from_millis(eth2_config.spec.milliseconds_per_slot),
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Generate the duties manager */
|
|
||||||
|
|
||||||
// Load generated keypairs
|
|
||||||
let keypairs = Arc::new(client_config.fetch_keys(&log)?);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
log,
|
|
||||||
"Keypairs loaded";
|
|
||||||
"local_validator_count" => keypairs.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let slots_per_epoch = E::slots_per_epoch();
|
|
||||||
|
|
||||||
// Builds a mapping of Epoch -> Map(PublicKey, EpochDuty)
|
|
||||||
// where EpochDuty contains slot numbers and attestation data that each validator needs to
|
|
||||||
// produce work on.
|
|
||||||
let duties_map = RwLock::new(EpochDutiesMap::new(slots_per_epoch));
|
|
||||||
|
|
||||||
// builds a manager which maintains the list of current duties for all known validators
|
|
||||||
// and can check when a validator needs to perform a task.
|
|
||||||
let duties_manager = Arc::new(DutiesManager {
|
|
||||||
duties_map,
|
|
||||||
// these are abstract objects capable of signing
|
|
||||||
signers: keypairs,
|
|
||||||
beacon_node: validator_client,
|
|
||||||
});
|
|
||||||
|
|
||||||
let spec = Arc::new(eth2_config.spec);
|
|
||||||
|
|
||||||
Ok(Service {
|
|
||||||
fork,
|
|
||||||
slot_clock,
|
|
||||||
current_slot: RwLock::new(None),
|
|
||||||
slots_per_epoch,
|
|
||||||
spec,
|
|
||||||
duties_manager,
|
|
||||||
beacon_block_client,
|
|
||||||
attestation_client,
|
|
||||||
log,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: BeaconNodeDuties + 'static, S: Signer + 'static, E: EthSpec> Service<B, S, E> {
|
|
||||||
/// The execution logic that runs every slot.
|
|
||||||
// Errors are logged to output, and core execution continues unless fatal errors occur.
|
|
||||||
pub fn per_slot_execution(&self) -> error_chain::Result<()> {
|
|
||||||
/* get the new current slot and epoch */
|
|
||||||
self.update_current_slot()?;
|
|
||||||
|
|
||||||
/* check for new duties */
|
|
||||||
self.check_for_duties();
|
|
||||||
|
|
||||||
/* process any required duties for validators */
|
|
||||||
self.process_duties();
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
self.log,
|
|
||||||
"Per slot execution finished";
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the known current slot and epoch.
|
|
||||||
fn update_current_slot(&self) -> error_chain::Result<()> {
|
|
||||||
let wall_clock_slot = self
|
|
||||||
.slot_clock
|
|
||||||
.now()
|
|
||||||
.ok_or_else::<error_chain::Error, _>(|| {
|
|
||||||
"Genesis is not in the past. Exiting.".into()
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let wall_clock_epoch = wall_clock_slot.epoch(self.slots_per_epoch);
|
|
||||||
let mut current_slot = self.current_slot.write();
|
|
||||||
|
|
||||||
// this is a non-fatal error. If the slot clock repeats, the node could
|
|
||||||
// have been slow to process the previous slot and is now duplicating tasks.
|
|
||||||
// We ignore duplicated but raise a critical error.
|
|
||||||
if let Some(current_slot) = *current_slot {
|
|
||||||
if wall_clock_slot <= current_slot {
|
|
||||||
crit!(
|
|
||||||
self.log,
|
|
||||||
"The validator tried to duplicate a slot. Likely missed the previous slot"
|
|
||||||
);
|
|
||||||
return Err("Duplicate slot".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*current_slot = Some(wall_clock_slot);
|
|
||||||
info!(self.log, "Processing"; "slot" => wall_clock_slot.as_u64(), "epoch" => wall_clock_epoch.as_u64());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For all known validator keypairs, update any known duties from the beacon node.
|
|
||||||
fn check_for_duties(&self) {
|
|
||||||
let cloned_manager = self.duties_manager.clone();
|
|
||||||
let cloned_log = self.log.clone();
|
|
||||||
let current_epoch = self
|
|
||||||
.current_slot
|
|
||||||
.read()
|
|
||||||
.expect("The current slot must be updated before checking for duties")
|
|
||||||
.epoch(self.slots_per_epoch);
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
self.log,
|
|
||||||
"Checking for duties";
|
|
||||||
"epoch" => current_epoch
|
|
||||||
);
|
|
||||||
|
|
||||||
// spawn a new thread separate to the runtime
|
|
||||||
// TODO: Handle thread termination/timeout
|
|
||||||
// TODO: Add duties thread back in, with channel to process duties in duty change.
|
|
||||||
// leave sequential for now.
|
|
||||||
//std::thread::spawn(move || {
|
|
||||||
// the return value is a future which returns ready.
|
|
||||||
// built to be compatible with the tokio runtime.
|
|
||||||
let _empty = cloned_manager.run_update(current_epoch, cloned_log.clone());
|
|
||||||
//});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If there are any duties to process, spawn a separate thread and perform required actions.
|
|
||||||
fn process_duties(&self) {
|
|
||||||
if let Some(work) = self.duties_manager.get_current_work(
|
|
||||||
self.current_slot
|
|
||||||
.read()
|
|
||||||
.expect("The current slot must be updated before processing duties"),
|
|
||||||
) {
|
|
||||||
trace!(
|
|
||||||
self.log,
|
|
||||||
"Processing duties";
|
|
||||||
"work_items" => work.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
for (signer_index, work_type) in work {
|
|
||||||
if work_type.produce_block {
|
|
||||||
// we need to produce a block
|
|
||||||
// spawns a thread to produce a beacon block
|
|
||||||
let signers = self.duties_manager.signers.clone(); // this is an arc
|
|
||||||
let fork = self.fork.clone();
|
|
||||||
let slot = self
|
|
||||||
.current_slot
|
|
||||||
.read()
|
|
||||||
.expect("The current slot must be updated before processing duties");
|
|
||||||
let spec = self.spec.clone();
|
|
||||||
let beacon_node = self.beacon_block_client.clone();
|
|
||||||
let log = self.log.clone();
|
|
||||||
let slots_per_epoch = self.slots_per_epoch;
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
info!(
|
|
||||||
log,
|
|
||||||
"Producing a block";
|
|
||||||
"validator"=> format!("{}", signers[signer_index]),
|
|
||||||
"slot"=> slot
|
|
||||||
);
|
|
||||||
let signer = &signers[signer_index];
|
|
||||||
let mut block_producer = BlockProducer {
|
|
||||||
fork,
|
|
||||||
slot,
|
|
||||||
spec,
|
|
||||||
beacon_node,
|
|
||||||
signer,
|
|
||||||
slots_per_epoch,
|
|
||||||
_phantom: PhantomData::<E>,
|
|
||||||
log,
|
|
||||||
};
|
|
||||||
block_producer.handle_produce_block();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if work_type.attestation_duty.is_some() {
|
|
||||||
// we need to produce an attestation
|
|
||||||
// spawns a thread to produce and sign an attestation
|
|
||||||
let slot = self
|
|
||||||
.current_slot
|
|
||||||
.read()
|
|
||||||
.expect("The current slot must be updated before processing duties");
|
|
||||||
let signers = self.duties_manager.signers.clone(); // this is an arc
|
|
||||||
let fork = self.fork.clone();
|
|
||||||
let spec = self.spec.clone();
|
|
||||||
let beacon_node = self.attestation_client.clone();
|
|
||||||
let log = self.log.clone();
|
|
||||||
let slots_per_epoch = self.slots_per_epoch;
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
info!(
|
|
||||||
log,
|
|
||||||
"Producing an attestation";
|
|
||||||
"validator"=> format!("{}", signers[signer_index]),
|
|
||||||
"slot"=> slot
|
|
||||||
);
|
|
||||||
let signer = &signers[signer_index];
|
|
||||||
let mut attestation_producer = AttestationProducer {
|
|
||||||
fork,
|
|
||||||
duty: work_type.attestation_duty.expect("Should never be none"),
|
|
||||||
spec,
|
|
||||||
beacon_node,
|
|
||||||
signer,
|
|
||||||
slots_per_epoch,
|
|
||||||
_phantom: PhantomData::<E>,
|
|
||||||
};
|
|
||||||
attestation_producer.handle_produce_attestation(log);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
use types::{Keypair, PublicKey, Signature};
|
|
||||||
|
|
||||||
/// Signs message using an internally-maintained private key.
|
|
||||||
pub trait Signer: Display + Send + Sync + Clone {
|
|
||||||
fn sign_message(&self, message: &[u8], domain: u64) -> Option<Signature>;
|
|
||||||
/// Returns a public key for the signer object.
|
|
||||||
fn to_public(&self) -> PublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Implements Display and Signer for Keypair */
|
|
||||||
|
|
||||||
impl Signer for Keypair {
|
|
||||||
fn to_public(&self) -> PublicKey {
|
|
||||||
self.pk.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign_message(&self, message: &[u8], domain: u64) -> Option<Signature> {
|
|
||||||
Some(Signature::new(message, domain, &self.sk))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user