Implements Signer generic for validator client and epoch duties
This commit is contained in:
parent
ba771282fa
commit
d3a6d73153
@ -1,5 +1,6 @@
|
|||||||
use super::{PublicKey, SecretKey};
|
use super::{PublicKey, SecretKey};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@ -32,3 +33,9 @@ impl Hash for Keypair {
|
|||||||
self.pk.as_uncompressed_bytes().hash(state)
|
self.pk.as_uncompressed_bytes().hash(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Keypair {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::EpochDuties;
|
use super::EpochDuties;
|
||||||
use types::{Epoch, Keypair};
|
use types::{Epoch, PublicKey};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum BeaconNodeDutiesError {
|
pub enum BeaconNodeDutiesError {
|
||||||
@ -15,6 +15,6 @@ pub trait BeaconNodeDuties: Send + Sync {
|
|||||||
fn request_duties(
|
fn request_duties(
|
||||||
&self,
|
&self,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
signers: &[Keypair],
|
pub_keys: &[PublicKey],
|
||||||
) -> Result<EpochDuties, BeaconNodeDutiesError>;
|
) -> Result<EpochDuties, BeaconNodeDutiesError>;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use types::{AttestationDuty, Epoch, Keypair, Slot};
|
use types::{AttestationDuty, Epoch, PublicKey, Slot};
|
||||||
|
|
||||||
/// When work needs to be performed by a validator, this type is given back to the main service
|
/// 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.
|
/// which indicates all the information that required to process the work.
|
||||||
@ -72,7 +72,7 @@ impl fmt::Display for EpochDuty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a list of keypairs (many validators) to an EpochDuty.
|
/// Maps a list of keypairs (many validators) to an EpochDuty.
|
||||||
pub type EpochDuties = HashMap<Keypair, Option<EpochDuty>>;
|
pub type EpochDuties = HashMap<PublicKey, Option<EpochDuty>>;
|
||||||
|
|
||||||
pub enum EpochDutiesMapError {
|
pub enum EpochDutiesMapError {
|
||||||
UnknownEpoch,
|
UnknownEpoch,
|
||||||
@ -113,7 +113,7 @@ impl EpochDutiesMap {
|
|||||||
pub fn is_work_slot(
|
pub fn is_work_slot(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
signer: &Keypair,
|
signer: &PublicKey,
|
||||||
) -> Result<Option<WorkInfo>, EpochDutiesMapError> {
|
) -> Result<Option<WorkInfo>, EpochDutiesMapError> {
|
||||||
let epoch = slot.epoch(self.slots_per_epoch);
|
let epoch = slot.epoch(self.slots_per_epoch);
|
||||||
|
|
||||||
|
@ -6,21 +6,21 @@ use protos::services_grpc::ValidatorServiceClient;
|
|||||||
use ssz::ssz_encode;
|
use ssz::ssz_encode;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use types::{Epoch, Keypair, Slot};
|
use types::{Epoch, PublicKey, Slot};
|
||||||
|
|
||||||
impl BeaconNodeDuties for ValidatorServiceClient {
|
impl BeaconNodeDuties for ValidatorServiceClient {
|
||||||
/// Requests all duties (block signing and committee attesting) from the Beacon Node (BN).
|
/// Requests all duties (block signing and committee attesting) from the Beacon Node (BN).
|
||||||
fn request_duties(
|
fn request_duties(
|
||||||
&self,
|
&self,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
signers: &[Keypair],
|
pub_keys: &[PublicKey],
|
||||||
) -> Result<EpochDuties, BeaconNodeDutiesError> {
|
) -> Result<EpochDuties, BeaconNodeDutiesError> {
|
||||||
// Get the required duties from all validators
|
// Get the required duties from all validators
|
||||||
// build the request
|
// build the request
|
||||||
let mut req = GetDutiesRequest::new();
|
let mut req = GetDutiesRequest::new();
|
||||||
req.set_epoch(epoch.as_u64());
|
req.set_epoch(epoch.as_u64());
|
||||||
let mut validators = Validators::new();
|
let mut validators = Validators::new();
|
||||||
validators.set_public_keys(signers.iter().map(|v| ssz_encode(&v.pk)).collect());
|
validators.set_public_keys(pub_keys.iter().map(|v| ssz_encode(v)).collect());
|
||||||
req.set_validators(validators);
|
req.set_validators(validators);
|
||||||
|
|
||||||
// set a timeout for requests
|
// set a timeout for requests
|
||||||
@ -31,11 +31,11 @@ impl BeaconNodeDuties for ValidatorServiceClient {
|
|||||||
.get_validator_duties(&req)
|
.get_validator_duties(&req)
|
||||||
.map_err(|err| BeaconNodeDutiesError::RemoteFailure(format!("{:?}", err)))?;
|
.map_err(|err| BeaconNodeDutiesError::RemoteFailure(format!("{:?}", err)))?;
|
||||||
|
|
||||||
let mut epoch_duties: HashMap<Keypair, Option<EpochDuty>> = HashMap::new();
|
let mut epoch_duties: HashMap<PublicKey, Option<EpochDuty>> = HashMap::new();
|
||||||
for (index, validator_duty) in reply.get_active_validators().iter().enumerate() {
|
for (index, validator_duty) in reply.get_active_validators().iter().enumerate() {
|
||||||
if !validator_duty.has_duty() {
|
if !validator_duty.has_duty() {
|
||||||
// validator is inactive
|
// validator is inactive
|
||||||
epoch_duties.insert(signers[index].clone(), None);
|
epoch_duties.insert(pub_keys[index].clone(), None);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// active validator
|
// active validator
|
||||||
@ -53,7 +53,7 @@ impl BeaconNodeDuties for ValidatorServiceClient {
|
|||||||
attestation_shard: active_duty.get_attestation_shard(),
|
attestation_shard: active_duty.get_attestation_shard(),
|
||||||
committee_index: active_duty.get_committee_index(),
|
committee_index: active_duty.get_committee_index(),
|
||||||
};
|
};
|
||||||
epoch_duties.insert(signers[index].clone(), Some(epoch_duty));
|
epoch_duties.insert(pub_keys[index].clone(), Some(epoch_duty));
|
||||||
}
|
}
|
||||||
Ok(epoch_duties)
|
Ok(epoch_duties)
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,13 @@ mod grpc;
|
|||||||
pub use self::beacon_node_duties::{BeaconNodeDuties, BeaconNodeDutiesError};
|
pub use self::beacon_node_duties::{BeaconNodeDuties, BeaconNodeDutiesError};
|
||||||
use self::epoch_duties::{EpochDuties, EpochDutiesMapError};
|
use self::epoch_duties::{EpochDuties, EpochDutiesMapError};
|
||||||
pub use self::epoch_duties::{EpochDutiesMap, WorkInfo};
|
pub use self::epoch_duties::{EpochDutiesMap, WorkInfo};
|
||||||
|
use super::signer::Signer;
|
||||||
use futures::Async;
|
use futures::Async;
|
||||||
use slog::{debug, error, info};
|
use slog::{debug, error, info};
|
||||||
|
use std::fmt::Display;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use types::{Epoch, Keypair, Slot};
|
use types::{Epoch, PublicKey, Slot};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum UpdateOutcome {
|
pub enum UpdateOutcome {
|
||||||
@ -38,20 +40,20 @@ pub enum Error {
|
|||||||
/// Node.
|
/// Node.
|
||||||
///
|
///
|
||||||
/// This keeps track of all validator keys and required voting slots.
|
/// This keeps track of all validator keys and required voting slots.
|
||||||
pub struct DutiesManager<U: BeaconNodeDuties> {
|
pub struct DutiesManager<U: BeaconNodeDuties, S: Signer> {
|
||||||
pub duties_map: RwLock<EpochDutiesMap>,
|
pub duties_map: RwLock<EpochDutiesMap>,
|
||||||
/// A list of all signer objects known to the validator service.
|
/// A list of all signer objects known to the validator service.
|
||||||
// TODO: Generalise the signers, so that they're not just keypairs
|
pub signers: Arc<Vec<S>>,
|
||||||
pub signers: Arc<Vec<Keypair>>,
|
|
||||||
pub beacon_node: Arc<U>,
|
pub beacon_node: Arc<U>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U: BeaconNodeDuties> DutiesManager<U> {
|
impl<U: BeaconNodeDuties, S: Signer + Display> DutiesManager<U, S> {
|
||||||
/// Check the Beacon Node for `EpochDuties`.
|
/// Check the Beacon Node for `EpochDuties`.
|
||||||
///
|
///
|
||||||
/// be a wall-clock (e.g., system time, remote server time, etc.).
|
/// be a wall-clock (e.g., system time, remote server time, etc.).
|
||||||
fn update(&self, epoch: Epoch) -> Result<UpdateOutcome, Error> {
|
fn update(&self, epoch: Epoch) -> Result<UpdateOutcome, Error> {
|
||||||
let duties = self.beacon_node.request_duties(epoch, &self.signers)?;
|
let public_keys: Vec<PublicKey> = self.signers.iter().map(|s| s.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 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 let Some(known_duties) = self.duties_map.read()?.get(&epoch) {
|
||||||
@ -90,17 +92,17 @@ impl<U: BeaconNodeDuties> DutiesManager<U> {
|
|||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of (Public, WorkInfo) indicating all the validators that have work to perform
|
/// Returns a list of (index, WorkInfo) indicating all the validators that have work to perform
|
||||||
/// this slot.
|
/// this slot.
|
||||||
pub fn get_current_work(&self, slot: Slot) -> Option<Vec<(Keypair, WorkInfo)>> {
|
pub fn get_current_work(&self, slot: Slot) -> Option<Vec<(usize, WorkInfo)>> {
|
||||||
let mut current_work: Vec<(Keypair, WorkInfo)> = Vec::new();
|
let mut current_work: Vec<(usize, WorkInfo)> = Vec::new();
|
||||||
|
|
||||||
// if the map is poisoned, return None
|
// if the map is poisoned, return None
|
||||||
let duties = self.duties_map.read().ok()?;
|
let duties = self.duties_map.read().ok()?;
|
||||||
|
|
||||||
for validator_signer in self.signers.iter() {
|
for (index, validator_signer) in self.signers.iter().enumerate() {
|
||||||
match duties.is_work_slot(slot, &validator_signer) {
|
match duties.is_work_slot(slot, &validator_signer.to_public()) {
|
||||||
Ok(Some(work_type)) => current_work.push((validator_signer.clone(), work_type)),
|
Ok(Some(work_type)) => current_work.push((index, work_type)),
|
||||||
Ok(None) => {} // No work for this validator
|
Ok(None) => {} // No work for this validator
|
||||||
//TODO: This should really log an error, as we shouldn't end up with an err here.
|
//TODO: This should really log an error, as we shouldn't end up with an err here.
|
||||||
Err(_) => {} // Unknown epoch or validator, no work
|
Err(_) => {} // Unknown epoch or validator, no work
|
||||||
|
@ -11,6 +11,7 @@ use clap::{App, Arg};
|
|||||||
use protos::services_grpc::ValidatorServiceClient;
|
use protos::services_grpc::ValidatorServiceClient;
|
||||||
use service::Service as ValidatorService;
|
use service::Service as ValidatorService;
|
||||||
use slog::{error, info, o, Drain};
|
use slog::{error, info, o, Drain};
|
||||||
|
use types::Keypair;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Logging
|
// Logging
|
||||||
@ -54,8 +55,8 @@ fn main() {
|
|||||||
.expect("Unable to build a configuration for the validator client.");
|
.expect("Unable to build a configuration for the validator client.");
|
||||||
|
|
||||||
// start the validator service.
|
// start the validator service.
|
||||||
// this specifies the GRPC type to use as the duty manager beacon node.
|
// this specifies the GRPC and signer type to use as the duty manager beacon node.
|
||||||
match ValidatorService::<ValidatorServiceClient>::start(config, log.clone()) {
|
match ValidatorService::<ValidatorServiceClient, Keypair>::start(config, log.clone()) {
|
||||||
Ok(_) => info!(log, "Validator client shutdown successfully."),
|
Ok(_) => info!(log, "Validator client shutdown successfully."),
|
||||||
Err(e) => error!(log, "Validator exited due to: {}", e.to_string()),
|
Err(e) => error!(log, "Validator exited due to: {}", e.to_string()),
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ use crate::config::Config as ValidatorConfig;
|
|||||||
use crate::duties::{BeaconNodeDuties, DutiesManager, EpochDutiesMap, UpdateOutcome};
|
use crate::duties::{BeaconNodeDuties, DutiesManager, EpochDutiesMap, UpdateOutcome};
|
||||||
use crate::error as error_chain;
|
use crate::error as error_chain;
|
||||||
use crate::error::ErrorKind;
|
use crate::error::ErrorKind;
|
||||||
|
use crate::signer::Signer;
|
||||||
use attester::test_utils::EpochMap;
|
use attester::test_utils::EpochMap;
|
||||||
use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester};
|
use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester};
|
||||||
use bls::Keypair;
|
use bls::Keypair;
|
||||||
@ -36,14 +37,10 @@ use tokio_timer::clock::Clock;
|
|||||||
use types::test_utils::generate_deterministic_keypairs;
|
use types::test_utils::generate_deterministic_keypairs;
|
||||||
use types::{ChainSpec, Epoch, Fork, Slot};
|
use types::{ChainSpec, Epoch, Fork, Slot};
|
||||||
|
|
||||||
//TODO: This service should be simplified in the future. Can be made more steamlined.
|
|
||||||
|
|
||||||
const POLL_INTERVAL_MILLIS: u64 = 100;
|
|
||||||
|
|
||||||
/// The validator service. This is the main thread that executes and maintains validator
|
/// The validator service. This is the main thread that executes and maintains validator
|
||||||
/// duties.
|
/// duties.
|
||||||
//TODO: Generalize the BeaconNode types to use testing
|
//TODO: Generalize the BeaconNode types to use testing
|
||||||
pub struct Service<B: BeaconNodeDuties + 'static> {
|
pub struct Service<B: BeaconNodeDuties + 'static, S: Signer + 'static> {
|
||||||
/// The node's current fork version we are processing on.
|
/// The node's current fork version we are processing on.
|
||||||
fork: Fork,
|
fork: Fork,
|
||||||
/// The slot clock for this service.
|
/// The slot clock for this service.
|
||||||
@ -53,7 +50,7 @@ pub struct Service<B: BeaconNodeDuties + 'static> {
|
|||||||
/// The chain specification for this clients instance.
|
/// The chain specification for this clients instance.
|
||||||
spec: Arc<ChainSpec>,
|
spec: Arc<ChainSpec>,
|
||||||
/// The duties manager which maintains the state of when to perform actions.
|
/// The duties manager which maintains the state of when to perform actions.
|
||||||
duties_manager: Arc<DutiesManager<B>>,
|
duties_manager: Arc<DutiesManager<B, S>>,
|
||||||
// GRPC Clients
|
// GRPC Clients
|
||||||
/// The beacon block GRPC client.
|
/// The beacon block GRPC client.
|
||||||
beacon_block_client: Arc<BeaconBlockGrpcClient>,
|
beacon_block_client: Arc<BeaconBlockGrpcClient>,
|
||||||
@ -63,7 +60,7 @@ pub struct Service<B: BeaconNodeDuties + 'static> {
|
|||||||
log: slog::Logger,
|
log: slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: BeaconNodeDuties + 'static> Service<B> {
|
impl<B: BeaconNodeDuties + 'static, S: Signer + 'static> Service<B, S> {
|
||||||
/// Initial connection to the beacon node to determine its properties.
|
/// 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
|
/// This tries to connect to a beacon node. Once connected, it initialised the gRPC clients
|
||||||
@ -71,7 +68,7 @@ impl<B: BeaconNodeDuties + 'static> Service<B> {
|
|||||||
fn initialize_service(
|
fn initialize_service(
|
||||||
config: ValidatorConfig,
|
config: ValidatorConfig,
|
||||||
log: slog::Logger,
|
log: slog::Logger,
|
||||||
) -> error_chain::Result<Service<ValidatorServiceClient>> {
|
) -> error_chain::Result<Service<ValidatorServiceClient, Keypair>> {
|
||||||
// initialise the beacon node client to check for a connection
|
// initialise the beacon node client to check for a connection
|
||||||
|
|
||||||
let env = Arc::new(EnvBuilder::new().build());
|
let env = Arc::new(EnvBuilder::new().build());
|
||||||
@ -183,6 +180,7 @@ impl<B: BeaconNodeDuties + 'static> Service<B> {
|
|||||||
// and can check when a validator needs to perform a task.
|
// and can check when a validator needs to perform a task.
|
||||||
let duties_manager = Arc::new(DutiesManager {
|
let duties_manager = Arc::new(DutiesManager {
|
||||||
duties_map,
|
duties_map,
|
||||||
|
// these are abstract objects capable of signing
|
||||||
signers: keypairs,
|
signers: keypairs,
|
||||||
beacon_node: validator_client,
|
beacon_node: validator_client,
|
||||||
});
|
});
|
||||||
@ -205,7 +203,8 @@ impl<B: BeaconNodeDuties + 'static> Service<B> {
|
|||||||
// TODO: Improve handling of generic BeaconNode types, to stub grpcClient
|
// TODO: Improve handling of generic BeaconNode types, to stub grpcClient
|
||||||
pub fn start(config: ValidatorConfig, log: slog::Logger) -> error_chain::Result<()> {
|
pub fn start(config: ValidatorConfig, log: slog::Logger) -> error_chain::Result<()> {
|
||||||
// connect to the node and retrieve its properties and initialize the gRPC clients
|
// connect to the node and retrieve its properties and initialize the gRPC clients
|
||||||
let mut service = Service::<ValidatorServiceClient>::initialize_service(config, log)?;
|
let mut service =
|
||||||
|
Service::<ValidatorServiceClient, Keypair>::initialize_service(config, log)?;
|
||||||
|
|
||||||
// we have connected to a node and established its parameters. Spin up the core service
|
// we have connected to a node and established its parameters. Spin up the core service
|
||||||
|
|
||||||
|
@ -1,7 +1,32 @@
|
|||||||
use types::Signature;
|
use std::fmt::Display;
|
||||||
|
use types::{Keypair, PublicKey, Signature};
|
||||||
|
|
||||||
/// Signs message using an internally-maintained private key.
|
/// Signs message using an internally-maintained private key.
|
||||||
pub trait Signer {
|
pub trait Signer: Display + Send + Sync {
|
||||||
fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option<Signature>;
|
fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option<Signature>;
|
||||||
fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option<Signature>;
|
fn sign_randao_reveal(&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_block_proposal(&self, message: &[u8], domain: u64) -> Option<Signature> {
|
||||||
|
Some(Signature::new(message, domain, &self.sk))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option<Signature> {
|
||||||
|
Some(Signature::new(message, domain, &self.sk))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn sign_attestation_message(&self, message: &[u8], domain: u64) -> Option<Signature> {
|
||||||
|
Some(Signature::new(message, domain, &self.sk))
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user