Add Signer to validator client
This commit is contained in:
parent
2bcce37b3c
commit
8c0e634aa4
@ -1,11 +1,10 @@
|
|||||||
#[cfg(test)]
|
pub mod test_utils;
|
||||||
mod test_node;
|
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use spec::ChainSpec;
|
use spec::ChainSpec;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use types::BeaconBlock;
|
use types::{BeaconBlock, Hash256, ProposalSignedData};
|
||||||
|
|
||||||
pub use self::traits::{BeaconNode, BeaconNodeError, DutiesReader, DutiesReaderError, Signer};
|
pub use self::traits::{BeaconNode, BeaconNodeError, DutiesReader, DutiesReaderError, Signer};
|
||||||
|
|
||||||
@ -23,6 +22,8 @@ pub enum PollOutcome {
|
|||||||
SlotAlreadyProcessed(u64),
|
SlotAlreadyProcessed(u64),
|
||||||
/// The Beacon Node was unable to produce a block at that slot.
|
/// The Beacon Node was unable to produce a block at that slot.
|
||||||
BeaconNodeUnableToProduceBlock(u64),
|
BeaconNodeUnableToProduceBlock(u64),
|
||||||
|
/// The signer failed to sign the message.
|
||||||
|
SignerRejection(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -123,9 +124,12 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
|||||||
fn produce_block(&mut self, slot: u64) -> Result<PollOutcome, Error> {
|
fn produce_block(&mut self, slot: u64) -> Result<PollOutcome, Error> {
|
||||||
if let Some(block) = self.beacon_node.produce_beacon_block(slot)? {
|
if let Some(block) = self.beacon_node.produce_beacon_block(slot)? {
|
||||||
if self.safe_to_produce(&block) {
|
if self.safe_to_produce(&block) {
|
||||||
let block = self.sign_block(block);
|
if let Some(block) = self.sign_block(block) {
|
||||||
self.beacon_node.publish_beacon_block(block)?;
|
self.beacon_node.publish_beacon_block(block)?;
|
||||||
Ok(PollOutcome::BlockProduced(slot))
|
Ok(PollOutcome::BlockProduced(slot))
|
||||||
|
} else {
|
||||||
|
Ok(PollOutcome::SignerRejection(slot))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(PollOutcome::SlashableBlockNotProduced(slot))
|
Ok(PollOutcome::SlashableBlockNotProduced(slot))
|
||||||
}
|
}
|
||||||
@ -138,11 +142,30 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
|||||||
///
|
///
|
||||||
/// Important: this function will not check to ensure the block is not slashable. This must be
|
/// Important: this function will not check to ensure the block is not slashable. This must be
|
||||||
/// done upstream.
|
/// done upstream.
|
||||||
fn sign_block(&mut self, block: BeaconBlock) -> BeaconBlock {
|
fn sign_block(&mut self, mut block: BeaconBlock) -> Option<BeaconBlock> {
|
||||||
// TODO: sign the block
|
|
||||||
// https://github.com/sigp/lighthouse/issues/160
|
|
||||||
self.store_produce(&block);
|
self.store_produce(&block);
|
||||||
block
|
|
||||||
|
let proposal_root = {
|
||||||
|
let block_without_signature_root = {
|
||||||
|
let mut block_without_signature = block.clone();
|
||||||
|
block_without_signature.signature = self.spec.empty_signature.clone();
|
||||||
|
block_without_signature.canonical_root()
|
||||||
|
};
|
||||||
|
let proposal = ProposalSignedData {
|
||||||
|
slot: block.slot,
|
||||||
|
shard: self.spec.beacon_chain_shard_number,
|
||||||
|
block_root: block_without_signature_root,
|
||||||
|
};
|
||||||
|
hash_tree_root(&proposal)
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.signer.bls_sign(&proposal_root[..]) {
|
||||||
|
None => None,
|
||||||
|
Some(signature) => {
|
||||||
|
block.signature = signature;
|
||||||
|
Some(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if signing a block is safe (non-slashable).
|
/// Returns `true` if signing a block is safe (non-slashable).
|
||||||
@ -167,6 +190,11 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_tree_root<T>(_input: &T) -> Hash256 {
|
||||||
|
// TODO: stubbed out.
|
||||||
|
Hash256::zero()
|
||||||
|
}
|
||||||
|
|
||||||
impl From<BeaconNodeError> for Error {
|
impl From<BeaconNodeError> for Error {
|
||||||
fn from(e: BeaconNodeError) -> Error {
|
fn from(e: BeaconNodeError) -> Error {
|
||||||
Error::BeaconNodeError(e)
|
Error::BeaconNodeError(e)
|
||||||
@ -175,13 +203,12 @@ impl From<BeaconNodeError> for Error {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::test_node::TestBeaconNode;
|
use super::test_utils::{TestBeaconNode, TestEpochMap, TestSigner};
|
||||||
use super::*;
|
use super::*;
|
||||||
use slot_clock::TestingSlotClock;
|
use slot_clock::TestingSlotClock;
|
||||||
use std::collections::HashMap;
|
|
||||||
use types::{
|
use types::{
|
||||||
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
||||||
Signature,
|
Keypair,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: implement more thorough testing.
|
// TODO: implement more thorough testing.
|
||||||
@ -189,31 +216,6 @@ mod tests {
|
|||||||
//
|
//
|
||||||
// These tests should serve as a good example for future tests.
|
// These tests should serve as a good example for future tests.
|
||||||
|
|
||||||
type EpochMap = HashMap<u64, u64>;
|
|
||||||
|
|
||||||
impl DutiesReader for EpochMap {
|
|
||||||
fn is_block_production_slot(
|
|
||||||
&self,
|
|
||||||
epoch: u64,
|
|
||||||
slot: u64,
|
|
||||||
) -> Result<bool, DutiesReaderError> {
|
|
||||||
match self.get(&epoch) {
|
|
||||||
Some(s) if *s == slot => Ok(true),
|
|
||||||
Some(s) if *s != slot => Ok(false),
|
|
||||||
_ => Err(DutiesReaderError::UnknownEpoch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestSigner();
|
|
||||||
|
|
||||||
impl Signer for TestSigner {
|
|
||||||
fn bls_sign(_message: &[u8]) -> Option<Signature> {
|
|
||||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
|
||||||
Some(Signature::random_for_test(&mut rng))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn polling() {
|
pub fn polling() {
|
||||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
@ -221,9 +223,9 @@ mod tests {
|
|||||||
let spec = Arc::new(ChainSpec::foundation());
|
let spec = Arc::new(ChainSpec::foundation());
|
||||||
let slot_clock = Arc::new(RwLock::new(TestingSlotClock::new(0)));
|
let slot_clock = Arc::new(RwLock::new(TestingSlotClock::new(0)));
|
||||||
let beacon_node = Arc::new(TestBeaconNode::default());
|
let beacon_node = Arc::new(TestBeaconNode::default());
|
||||||
let signer = Arc::new(TestSigner());
|
let signer = Arc::new(TestSigner::new(Keypair::random()));
|
||||||
|
|
||||||
let mut epoch_map = EpochMap::new();
|
let mut epoch_map = TestEpochMap::new();
|
||||||
let produce_slot = 100;
|
let produce_slot = 100;
|
||||||
let produce_epoch = produce_slot / spec.epoch_length;
|
let produce_epoch = produce_slot / spec.epoch_length;
|
||||||
epoch_map.insert(produce_epoch, produce_slot);
|
epoch_map.insert(produce_epoch, produce_slot);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::traits::{BeaconNode, BeaconNodeError};
|
use crate::traits::{BeaconNode, BeaconNodeError};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use types::BeaconBlock;
|
use types::BeaconBlock;
|
||||||
|
|
14
eth2/block_producer/src/test_utils/epoch_map.rs
Normal file
14
eth2/block_producer/src/test_utils/epoch_map.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use crate::{DutiesReader, DutiesReaderError};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type TestEpochMap = HashMap<u64, u64>;
|
||||||
|
|
||||||
|
impl DutiesReader for TestEpochMap {
|
||||||
|
fn is_block_production_slot(&self, epoch: u64, slot: u64) -> Result<bool, DutiesReaderError> {
|
||||||
|
match self.get(&epoch) {
|
||||||
|
Some(s) if *s == slot => Ok(true),
|
||||||
|
Some(s) if *s != slot => Ok(false),
|
||||||
|
_ => Err(DutiesReaderError::UnknownEpoch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
eth2/block_producer/src/test_utils/mod.rs
Normal file
7
eth2/block_producer/src/test_utils/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod beacon_node;
|
||||||
|
mod epoch_map;
|
||||||
|
mod signer;
|
||||||
|
|
||||||
|
pub use self::beacon_node::TestBeaconNode;
|
||||||
|
pub use self::epoch_map::TestEpochMap;
|
||||||
|
pub use self::signer::TestSigner;
|
31
eth2/block_producer/src/test_utils/signer.rs
Normal file
31
eth2/block_producer/src/test_utils/signer.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use crate::traits::Signer;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
use types::{Keypair, Signature};
|
||||||
|
|
||||||
|
/// A test-only struct used to simulate a Beacon Node.
|
||||||
|
pub struct TestSigner {
|
||||||
|
keypair: Keypair,
|
||||||
|
should_sign: RwLock<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestSigner {
|
||||||
|
/// Produce a new TestSigner with signing enabled by default.
|
||||||
|
pub fn new(keypair: Keypair) -> Self {
|
||||||
|
Self {
|
||||||
|
keypair,
|
||||||
|
should_sign: RwLock::new(true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If set to `false`, the service will refuse to sign all messages. Otherwise, all messages
|
||||||
|
/// will be signed.
|
||||||
|
pub fn enable_signing(&self, enabled: bool) {
|
||||||
|
*self.should_sign.write().unwrap() = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signer for TestSigner {
|
||||||
|
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
|
||||||
|
Some(Signature::new(message, &self.keypair.sk))
|
||||||
|
}
|
||||||
|
}
|
@ -18,16 +18,18 @@ pub trait BeaconNode: Send + Sync {
|
|||||||
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<bool, BeaconNodeError>;
|
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<bool, BeaconNodeError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum DutiesReaderError {
|
pub enum DutiesReaderError {
|
||||||
UnknownEpoch,
|
UnknownEpoch,
|
||||||
Poisoned,
|
Poisoned,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides methods for a validator to determine their responsibilities for some slot.
|
/// Informs a validator of their duties (e.g., block production).
|
||||||
pub trait DutiesReader: Send + Sync {
|
pub trait DutiesReader: Send + Sync {
|
||||||
fn is_block_production_slot(&self, epoch: u64, slot: u64) -> Result<bool, DutiesReaderError>;
|
fn is_block_production_slot(&self, epoch: u64, slot: u64) -> Result<bool, DutiesReaderError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signs message using an internally-maintained private key.
|
||||||
pub trait Signer {
|
pub trait Signer {
|
||||||
fn bls_sign(message: &[u8]) -> Option<Signature>;
|
fn bls_sign(&self, message: &[u8]) -> Option<Signature>;
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use block_producer::{
|
use block_producer::{
|
||||||
BeaconNode, BlockProducer, DutiesReader, PollOutcome as BlockProducerPollOutcome,
|
BeaconNode, BlockProducer, DutiesReader, PollOutcome as BlockProducerPollOutcome, Signer,
|
||||||
};
|
};
|
||||||
use slog::{error, info, warn, Logger};
|
use slog::{error, info, warn, Logger};
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub struct BlockProducerService<T: SlotClock, U: BeaconNode, V: DutiesReader> {
|
pub struct BlockProducerService<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> {
|
||||||
pub block_producer: BlockProducer<T, U, V>,
|
pub block_producer: BlockProducer<T, U, V, W>,
|
||||||
pub poll_interval_millis: u64,
|
pub poll_interval_millis: u64,
|
||||||
pub log: Logger,
|
pub log: Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SlotClock, U: BeaconNode, V: DutiesReader> BlockProducerService<T, U, V> {
|
impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducerService<T, U, V, W> {
|
||||||
/// Run a loop which polls the block producer each `poll_interval_millis` millseconds.
|
/// Run a loop which polls the block producer each `poll_interval_millis` millseconds.
|
||||||
///
|
///
|
||||||
/// Logs the results of the polls.
|
/// Logs the results of the polls.
|
||||||
@ -39,6 +39,9 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader> BlockProducerService<T, U, V>
|
|||||||
Ok(BlockProducerPollOutcome::BeaconNodeUnableToProduceBlock(slot)) => {
|
Ok(BlockProducerPollOutcome::BeaconNodeUnableToProduceBlock(slot)) => {
|
||||||
error!(self.log, "Beacon node unable to produce block"; "slot" => slot)
|
error!(self.log, "Beacon node unable to produce block"; "slot" => slot)
|
||||||
}
|
}
|
||||||
|
Ok(BlockProducerPollOutcome::SignerRejection(slot)) => {
|
||||||
|
error!(self.log, "The cryptographic signer refused to sign the block"; "slot" => slot)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_millis(self.poll_interval_millis));
|
std::thread::sleep(Duration::from_millis(self.poll_interval_millis));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use self::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
|
use self::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
|
||||||
use self::duties::{DutiesManager, DutiesManagerService, EpochDutiesMap};
|
use self::duties::{DutiesManager, DutiesManagerService, EpochDutiesMap};
|
||||||
use crate::config::ClientConfig;
|
use crate::config::ClientConfig;
|
||||||
use block_producer::BlockProducer;
|
use block_producer::{test_utils::TestSigner, BlockProducer};
|
||||||
use bls::Keypair;
|
use bls::Keypair;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use grpcio::{ChannelBuilder, EnvBuilder};
|
use grpcio::{ChannelBuilder, EnvBuilder};
|
||||||
@ -139,12 +139,14 @@ fn main() {
|
|||||||
// Spawn a new thread to perform block production for the validator.
|
// Spawn a new thread to perform block production for the validator.
|
||||||
let producer_thread = {
|
let producer_thread = {
|
||||||
let spec = spec.clone();
|
let spec = spec.clone();
|
||||||
|
let signer = Arc::new(TestSigner::new(keypair.clone()));
|
||||||
let duties_map = duties_map.clone();
|
let duties_map = duties_map.clone();
|
||||||
let slot_clock = slot_clock.clone();
|
let slot_clock = slot_clock.clone();
|
||||||
let log = log.clone();
|
let log = log.clone();
|
||||||
let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone()));
|
let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone()));
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let block_producer = BlockProducer::new(spec, duties_map, slot_clock, client);
|
let block_producer =
|
||||||
|
BlockProducer::new(spec, duties_map, slot_clock, client, signer);
|
||||||
let mut block_producer_service = BlockProducerService {
|
let mut block_producer_service = BlockProducerService {
|
||||||
block_producer,
|
block_producer,
|
||||||
poll_interval_millis,
|
poll_interval_millis,
|
||||||
|
Loading…
Reference in New Issue
Block a user