use crate::validator_directory::ValidatorDirectory; use parking_lot::RwLock; use slog::{error, Logger}; use std::collections::HashMap; use std::fs::read_dir; use std::iter::FromIterator; use std::marker::PhantomData; use std::path::PathBuf; use std::sync::Arc; use tree_hash::{SignedRoot, TreeHash}; use types::{ Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, PublicKey, Signature, }; #[derive(Clone)] pub struct ValidatorStore { validators: Arc>>, spec: Arc, log: Logger, _phantom: PhantomData, } impl ValidatorStore { pub fn load_from_disk(base_dir: PathBuf, spec: ChainSpec, log: Logger) -> Result { let validator_iter = read_dir(&base_dir) .map_err(|e| format!("Failed to read base directory: {:?}", e))? .filter_map(|validator_dir| { let path = validator_dir.ok()?.path(); if path.is_dir() { match ValidatorDirectory::load_for_signing(path.clone()) { Ok(validator_directory) => Some(validator_directory), Err(e) => { error!( log, "Failed to load a validator directory"; "error" => e, "path" => path.to_str(), ); None } } } else { None } }) .filter_map(|validator_directory| { validator_directory .voting_keypair .clone() .map(|voting_keypair| (voting_keypair.pk, validator_directory)) }); Ok(Self { validators: Arc::new(RwLock::new(HashMap::from_iter(validator_iter))), spec: Arc::new(spec), log, _phantom: PhantomData, }) } pub fn voting_pubkeys(&self) -> Vec { self.validators .read() .iter() .map(|(pubkey, _dir)| pubkey.clone()) .collect() } pub fn num_voting_validators(&self) -> usize { self.validators.read().len() } pub fn randao_reveal( &self, validator_pubkey: &PublicKey, epoch: Epoch, fork: &Fork, ) -> Option { // TODO: check this against the slot clock to make sure it's not an early reveal? self.validators .read() .get(validator_pubkey) .and_then(|validator_dir| { validator_dir.voting_keypair.as_ref().map(|voting_keypair| { let message = epoch.tree_hash_root(); let domain = self.spec.get_domain(epoch, Domain::Randao, &fork); Signature::new(&message, domain, &voting_keypair.sk) }) }) } pub fn sign_block( &self, validator_pubkey: &PublicKey, mut block: BeaconBlock, fork: &Fork, ) -> Option> { // TODO: check for slashing. self.validators .read() .get(validator_pubkey) .and_then(|validator_dir| { validator_dir.voting_keypair.as_ref().map(|voting_keypair| { let epoch = block.slot.epoch(E::slots_per_epoch()); let message = block.signed_root(); let domain = self.spec.get_domain(epoch, Domain::BeaconProposer, &fork); block.signature = Signature::new(&message, domain, &voting_keypair.sk); block }) }) } pub fn sign_attestation( &self, validator_pubkey: &PublicKey, validator_committee_position: usize, mut attestation: Attestation, fork: &Fork, ) -> Option> { // TODO: check for slashing. self.validators .read() .get(validator_pubkey) .and_then(|validator_dir| { validator_dir .voting_keypair .as_ref() .and_then(|voting_keypair| { attestation .sign( &voting_keypair.sk, validator_committee_position, fork, &self.spec, ) .map_err(|e| { error!( self.log, "Error whilst signing attestation"; "error" => format!("{:?}", e) ) }) .map(|()| attestation) .ok() }) }) } }