From d3a6d73153eb6de71ed6eb5d2a8ad08324bf5927 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Sat, 30 Mar 2019 14:27:37 +1100 Subject: [PATCH] Implements Signer generic for validator client and epoch duties --- eth2/utils/bls/src/keypair.rs | 7 +++++ .../src/duties/beacon_node_duties.rs | 4 +-- validator_client/src/duties/epoch_duties.rs | 6 ++-- validator_client/src/duties/grpc.rs | 12 ++++---- validator_client/src/duties/mod.rs | 26 +++++++++-------- validator_client/src/main.rs | 5 ++-- validator_client/src/service.rs | 17 +++++------ validator_client/src/signer.rs | 29 +++++++++++++++++-- 8 files changed, 70 insertions(+), 36 deletions(-) diff --git a/eth2/utils/bls/src/keypair.rs b/eth2/utils/bls/src/keypair.rs index c91b13bad..2f0e794a6 100644 --- a/eth2/utils/bls/src/keypair.rs +++ b/eth2/utils/bls/src/keypair.rs @@ -1,5 +1,6 @@ use super::{PublicKey, SecretKey}; use serde_derive::{Deserialize, Serialize}; +use std::fmt; use std::hash::{Hash, Hasher}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -32,3 +33,9 @@ impl Hash for Keypair { 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) + } +} diff --git a/validator_client/src/duties/beacon_node_duties.rs b/validator_client/src/duties/beacon_node_duties.rs index b66b5f704..af1fab60b 100644 --- a/validator_client/src/duties/beacon_node_duties.rs +++ b/validator_client/src/duties/beacon_node_duties.rs @@ -1,5 +1,5 @@ use super::EpochDuties; -use types::{Epoch, Keypair}; +use types::{Epoch, PublicKey}; #[derive(Debug, PartialEq, Clone)] pub enum BeaconNodeDutiesError { @@ -15,6 +15,6 @@ pub trait BeaconNodeDuties: Send + Sync { fn request_duties( &self, epoch: Epoch, - signers: &[Keypair], + pub_keys: &[PublicKey], ) -> Result; } diff --git a/validator_client/src/duties/epoch_duties.rs b/validator_client/src/duties/epoch_duties.rs index 8e710ba9a..0a4f73f72 100644 --- a/validator_client/src/duties/epoch_duties.rs +++ b/validator_client/src/duties/epoch_duties.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::fmt; 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 /// 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. -pub type EpochDuties = HashMap>; +pub type EpochDuties = HashMap>; pub enum EpochDutiesMapError { UnknownEpoch, @@ -113,7 +113,7 @@ impl EpochDutiesMap { pub fn is_work_slot( &self, slot: Slot, - signer: &Keypair, + signer: &PublicKey, ) -> Result, EpochDutiesMapError> { let epoch = slot.epoch(self.slots_per_epoch); diff --git a/validator_client/src/duties/grpc.rs b/validator_client/src/duties/grpc.rs index d6e2e5238..66bf368a2 100644 --- a/validator_client/src/duties/grpc.rs +++ b/validator_client/src/duties/grpc.rs @@ -6,21 +6,21 @@ use protos::services_grpc::ValidatorServiceClient; use ssz::ssz_encode; use std::collections::HashMap; use std::time::Duration; -use types::{Epoch, Keypair, Slot}; +use types::{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, - signers: &[Keypair], + pub_keys: &[PublicKey], ) -> Result { // 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(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); // set a timeout for requests @@ -31,11 +31,11 @@ impl BeaconNodeDuties for ValidatorServiceClient { .get_validator_duties(&req) .map_err(|err| BeaconNodeDutiesError::RemoteFailure(format!("{:?}", err)))?; - let mut epoch_duties: HashMap> = HashMap::new(); + let mut epoch_duties: HashMap> = HashMap::new(); for (index, validator_duty) in reply.get_active_validators().iter().enumerate() { if !validator_duty.has_duty() { // validator is inactive - epoch_duties.insert(signers[index].clone(), None); + epoch_duties.insert(pub_keys[index].clone(), None); continue; } // active validator @@ -53,7 +53,7 @@ impl BeaconNodeDuties for ValidatorServiceClient { attestation_shard: active_duty.get_attestation_shard(), 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) } diff --git a/validator_client/src/duties/mod.rs b/validator_client/src/duties/mod.rs index 1019f489d..596d314ed 100644 --- a/validator_client/src/duties/mod.rs +++ b/validator_client/src/duties/mod.rs @@ -8,11 +8,13 @@ mod grpc; 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 slog::{debug, error, info}; +use std::fmt::Display; use std::sync::Arc; use std::sync::RwLock; -use types::{Epoch, Keypair, Slot}; +use types::{Epoch, PublicKey, Slot}; #[derive(Debug, PartialEq, Clone)] pub enum UpdateOutcome { @@ -38,20 +40,20 @@ pub enum Error { /// Node. /// /// This keeps track of all validator keys and required voting slots. -pub struct DutiesManager { +pub struct DutiesManager { pub duties_map: RwLock, /// 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>, + pub signers: Arc>, pub beacon_node: Arc, } -impl DutiesManager { +impl DutiesManager { /// Check the Beacon Node for `EpochDuties`. /// /// be a wall-clock (e.g., system time, remote server time, etc.). fn update(&self, epoch: Epoch) -> Result { - let duties = self.beacon_node.request_duties(epoch, &self.signers)?; + let public_keys: Vec = 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 let Some(known_duties) = self.duties_map.read()?.get(&epoch) { @@ -90,17 +92,17 @@ impl DutiesManager { 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. - pub fn get_current_work(&self, slot: Slot) -> Option> { - let mut current_work: Vec<(Keypair, WorkInfo)> = Vec::new(); + pub fn get_current_work(&self, slot: Slot) -> Option> { + let mut current_work: Vec<(usize, WorkInfo)> = Vec::new(); // if the map is poisoned, return None let duties = self.duties_map.read().ok()?; - for validator_signer in self.signers.iter() { - match duties.is_work_slot(slot, &validator_signer) { - Ok(Some(work_type)) => current_work.push((validator_signer.clone(), work_type)), + 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 diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index b3a488fe0..481766734 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -11,6 +11,7 @@ use clap::{App, Arg}; use protos::services_grpc::ValidatorServiceClient; use service::Service as ValidatorService; use slog::{error, info, o, Drain}; +use types::Keypair; fn main() { // Logging @@ -54,8 +55,8 @@ fn main() { .expect("Unable to build a configuration for the validator client."); // start the validator service. - // this specifies the GRPC type to use as the duty manager beacon node. - match ValidatorService::::start(config, log.clone()) { + // this specifies the GRPC and signer type to use as the duty manager beacon node. + match ValidatorService::::start(config, log.clone()) { Ok(_) => info!(log, "Validator client shutdown successfully."), Err(e) => error!(log, "Validator exited due to: {}", e.to_string()), } diff --git a/validator_client/src/service.rs b/validator_client/src/service.rs index d92d944cb..d6c0e6638 100644 --- a/validator_client/src/service.rs +++ b/validator_client/src/service.rs @@ -14,6 +14,7 @@ use crate::config::Config as ValidatorConfig; use crate::duties::{BeaconNodeDuties, DutiesManager, EpochDutiesMap, UpdateOutcome}; use crate::error as error_chain; use crate::error::ErrorKind; +use crate::signer::Signer; use attester::test_utils::EpochMap; use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester}; use bls::Keypair; @@ -36,14 +37,10 @@ use tokio_timer::clock::Clock; use types::test_utils::generate_deterministic_keypairs; 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 /// duties. //TODO: Generalize the BeaconNode types to use testing -pub struct Service { +pub struct Service { /// The node's current fork version we are processing on. fork: Fork, /// The slot clock for this service. @@ -53,7 +50,7 @@ pub struct Service { /// The chain specification for this clients instance. spec: Arc, /// The duties manager which maintains the state of when to perform actions. - duties_manager: Arc>, + duties_manager: Arc>, // GRPC Clients /// The beacon block GRPC client. beacon_block_client: Arc, @@ -63,7 +60,7 @@ pub struct Service { log: slog::Logger, } -impl Service { +impl Service { /// 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 @@ -71,7 +68,7 @@ impl Service { fn initialize_service( config: ValidatorConfig, log: slog::Logger, - ) -> error_chain::Result> { + ) -> error_chain::Result> { // initialise the beacon node client to check for a connection let env = Arc::new(EnvBuilder::new().build()); @@ -183,6 +180,7 @@ impl Service { // 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, }); @@ -205,7 +203,8 @@ impl Service { // TODO: Improve handling of generic BeaconNode types, to stub grpcClient pub fn start(config: ValidatorConfig, log: slog::Logger) -> error_chain::Result<()> { // connect to the node and retrieve its properties and initialize the gRPC clients - let mut service = Service::::initialize_service(config, log)?; + let mut service = + Service::::initialize_service(config, log)?; // we have connected to a node and established its parameters. Spin up the core service diff --git a/validator_client/src/signer.rs b/validator_client/src/signer.rs index 85bf35b16..49dedbb33 100644 --- a/validator_client/src/signer.rs +++ b/validator_client/src/signer.rs @@ -1,7 +1,32 @@ -use types::Signature; +use std::fmt::Display; +use types::{Keypair, PublicKey, Signature}; /// 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; fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option; + /// 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 { + Some(Signature::new(message, domain, &self.sk)) + } + + fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option { + Some(Signature::new(message, domain, &self.sk)) + } + + /* + fn sign_attestation_message(&self, message: &[u8], domain: u64) -> Option { + Some(Signature::new(message, domain, &self.sk)) + } + */ }