Implement block producer for validator client

This commit is contained in:
Age Manning 2019-03-29 16:33:27 +11:00
parent f8201edddd
commit eea772de3e
No known key found for this signature in database
GPG Key ID: 05EED64B79E06A93
10 changed files with 92 additions and 78 deletions

View File

@ -19,7 +19,9 @@ service BeaconNodeService {
/// Service that handles block production
service BeaconBlockService {
// Requests a block to be signed from the beacon node.
rpc ProduceBeaconBlock(ProduceBeaconBlockRequest) returns (ProduceBeaconBlockResponse);
// Responds to the node the signed block to be published.
rpc PublishBeaconBlock(PublishBeaconBlockRequest) returns (PublishBeaconBlockResponse);
}
@ -64,6 +66,7 @@ message Empty {}
// Validator requests an unsigned proposal.
message ProduceBeaconBlockRequest {
uint64 slot = 1;
bytes randao_reveal = 2;
}
// Beacon node returns an unsigned proposal.

View File

@ -8,11 +8,6 @@ edition = "2018"
name = "validator_client"
path = "src/main.rs"
[lib]
name = "validator_client"
path = "src/lib.rs"
[dependencies]
block_proposer = { path = "../eth2/block_proposer" }
attester = { path = "../eth2/attester" }

View File

@ -1,23 +0,0 @@
#[Derive(Debug, PartialEq, Clone)]
pub enum BeaconNodeError {
RemoteFailure(String),
DecodeFailure,
}
/// Defines the methods required to produce and publish blocks on a Beacon Node. Abstracts the
/// actual beacon node.
pub trait BeaconNode: 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(
&self,
slot: Slot,
randao_reveal: &Signature,
) -> Result<Option<BeaconBlock>, BeaconNodeError>;
/// Request that the node publishes a block.
///
/// Returns `true` if the publish was sucessful.
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError>;
}

View File

@ -0,0 +1,33 @@
use types::{BeaconBlock, Signature, Slot};
#[derive(Debug, PartialEq, Clone)]
pub enum BeaconBlockNodeError {
RemoteFailure(String),
DecodeFailure,
}
#[derive(Debug, PartialEq, Clone)]
pub enum PublishOutcome {
ValidBlock,
InvalidBlock(String),
}
/// Defines the methods required to produce and publish blocks on a Beacon Node. Abstracts the
/// actual beacon node.
pub trait BeaconBlockNode: 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(
&self,
slot: Slot,
randao_reveal: &Signature,
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError>;
/// Request that the node publishes a block.
///
/// Returns `true` if the publish was successful.
fn publish_beacon_block(
&self,
block: BeaconBlock,
) -> Result<PublishOutcome, BeaconBlockNodeError>;
}

View File

@ -1,10 +1,8 @@
pub mod test_utils;
mod traits;
use slot_clock::SlotClock;
use super::beacon_block_node::{BeaconBlockNode, BeaconBlockNodeError};
use crate::signer::Signer;
use ssz::{SignedRoot, TreeHash};
use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Domain, Slot};
use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot};
#[derive(Debug, PartialEq)]
pub enum Error {
@ -13,11 +11,11 @@ pub enum Error {
EpochMapPoisoned,
SlotClockPoisoned,
EpochLengthIsZero,
BeaconNodeError(BeaconNodeError),
BeaconBlockNodeError(BeaconBlockNodeError),
}
#[derive(Debug, PartialEq)]
pub enum BlockProducerEvent {
pub enum ValidatorEvent {
/// A new block was produced.
BlockProduced(Slot),
/// A block was not produced as it would have been slashable.
@ -32,21 +30,20 @@ pub enum BlockProducerEvent {
/// 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<B: BeaconNode, S: Signer> {
pub struct BlockProducer<B: BeaconBlockNode, S: Signer> {
/// The current fork.
pub fork: Fork,
/// The current slot to produce a block for.
pub slot: Slot,
/// The current epoch.
pub epoch: Epoch,
pub spec: Arc<ChainSpec>,
/// The beacon node to connect to.
pub beacon_node: Arc<B>,
/// The signer to sign the block.
pub signer: Arc<S>,
}
impl<B: BeaconNode, S: Signer> BlockProducer<B, S> {
impl<B: BeaconBlockNode, S: Signer> BlockProducer<B, S> {
/// Produce a block at some slot.
///
/// Assumes that a block is required at this slot (does not check the duties).
@ -57,43 +54,38 @@ impl<B: BeaconNode, S: Signer> BlockProducer<B, S> {
///
/// The slash-protection code is not yet implemented. There is zero protection against
/// slashing.
fn produce_block(&mut self) -> Result<BlockProducerEvent, Error> {
fn produce_block(&mut self) -> Result<ValidatorEvent, Error> {
let epoch = self.slot.epoch(self.spec.slots_per_epoch);
let randao_reveal = {
// TODO: add domain, etc to this message. Also ensure result matches `into_to_bytes32`.
let message = slot.epoch(self.spec.slots_per_epoch).hash_tree_root();
match self.signer.sign_randao_reveal(
let message = epoch.hash_tree_root();
let randao_reveal = match self.signer.sign_randao_reveal(
&message,
self.spec
.get_domain(slot.epoch(self.spec.slots_per_epoch), Domain::Randao, &fork),
self.spec.get_domain(epoch, Domain::Randao, &self.fork),
) {
None => return Ok(PollOutcome::SignerRejection(slot)),
None => return Ok(ValidatorEvent::SignerRejection(self.slot)),
Some(signature) => signature,
}
};
randao_reveal
};
if let Some(block) = self
.beacon_node
.produce_beacon_block(slot, &randao_reveal)?
.produce_beacon_block(self.slot, &randao_reveal)?
{
if self.safe_to_produce(&block) {
let domain = self.spec.get_domain(
slot.epoch(self.spec.slots_per_epoch),
Domain::BeaconBlock,
&fork,
);
let domain = self.spec.get_domain(epoch, Domain::BeaconBlock, &self.fork);
if let Some(block) = self.sign_block(block, domain) {
self.beacon_node.publish_beacon_block(block)?;
Ok(PollOutcome::BlockProduced(slot))
Ok(ValidatorEvent::BlockProduced(self.slot))
} else {
Ok(PollOutcome::SignerRejection(slot))
Ok(ValidatorEvent::SignerRejection(self.slot))
}
} else {
Ok(PollOutcome::SlashableBlockNotProduced(slot))
Ok(ValidatorEvent::SlashableBlockNotProduced(self.slot))
}
} else {
Ok(PollOutcome::BeaconNodeUnableToProduceBlock(slot))
Ok(ValidatorEvent::BeaconNodeUnableToProduceBlock(self.slot))
}
}
@ -138,13 +130,12 @@ impl<B: BeaconNode, S: Signer> BlockProducer<B, S> {
}
}
impl From<BeaconNodeError> for Error {
fn from(e: BeaconNodeError) -> Error {
Error::BeaconNodeError(e)
impl From<BeaconBlockNodeError> for Error {
fn from(e: BeaconBlockNodeError) -> Error {
Error::BeaconBlockNodeError(e)
}
}
/* Old tests - Re-work for new logic
#[cfg(test)]
mod tests {
@ -225,3 +216,4 @@ mod tests {
);
}
}
*/

View File

@ -1,3 +1,7 @@
mod beacon_block_node;
mod block_producer;
use self::beacon_block_node::*;
use protos::services::{
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
};
@ -9,16 +13,16 @@ use types::{BeaconBlock, Signature, Slot};
/// A newtype designed to wrap the gRPC-generated service so the `BeaconNode` trait may be
/// implemented upon it.
pub struct BeaconBlockGrpcClient {
inner: Arc<BeaconBlockServiceClient>,
client: Arc<BeaconBlockServiceClient>,
}
impl BeaconBlockGrpcClient {
pub fn new(client: Arc<BeaconBlockServiceClient>) -> Self {
Self { inner: client }
Self { client }
}
}
impl BeaconNode for BeaconBlockGrpcClient {
impl BeaconBlockNode 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
@ -26,23 +30,26 @@ impl BeaconNode for BeaconBlockGrpcClient {
fn produce_beacon_block(
&self,
slot: Slot,
// TODO: use randao_reveal, when proto APIs have been updated.
_randao_reveal: &Signature,
) -> Result<Option<BeaconBlock>, BeaconNodeError> {
randao_reveal: &Signature,
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError> {
// request a beacon block from the node
let mut req = ProduceBeaconBlockRequest::new();
req.set_slot(slot.as_u64());
req.set_randao_reveal(ssz_encode(randao_reveal));
//TODO: Determine if we want an explicit timeout
let reply = self
.client
.produce_beacon_block(&req)
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
.map_err(|err| BeaconBlockNodeError::RemoteFailure(format!("{:?}", err)))?;
// format the reply
if reply.has_block() {
let block = reply.get_block();
let ssz = block.get_ssz();
let (block, _i) =
BeaconBlock::ssz_decode(&ssz, 0).map_err(|_| BeaconNodeError::DecodeFailure)?;
let (block, _i) = BeaconBlock::ssz_decode(&ssz, 0)
.map_err(|_| BeaconBlockNodeError::DecodeFailure)?;
Ok(Some(block))
} else {
@ -54,12 +61,14 @@ impl BeaconNode for BeaconBlockGrpcClient {
///
/// 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(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError> {
fn publish_beacon_block(
&self,
block: BeaconBlock,
) -> Result<PublishOutcome, BeaconBlockNodeError> {
let mut req = PublishBeaconBlockRequest::new();
let ssz = ssz_encode(&block);
// TODO: this conversion is incomplete; fix it.
let mut grpc_block = GrpcBeaconBlock::new();
grpc_block.set_ssz(ssz);
@ -68,7 +77,7 @@ impl BeaconNode for BeaconBlockGrpcClient {
let reply = self
.client
.publish_beacon_block(&req)
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
.map_err(|err| BeaconBlockNodeError::RemoteFailure(format!("{:?}", err)))?;
if reply.get_success() {
Ok(PublishOutcome::ValidBlock)

View File

@ -1,3 +0,0 @@
pub mod config;
pub use crate::config::Config;

View File

@ -4,6 +4,7 @@ mod config;
mod duties;
pub mod error;
mod service;
mod signer;
use crate::config::Config as ValidatorClientConfig;
use clap::{App, Arg};

View File

@ -9,7 +9,7 @@
/// data from the beacon node and performs the signing before publishing the block to the beacon
/// node.
use crate::attester_service::{AttestationGrpcClient, AttesterService};
use crate::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
use crate::block_producer_service::BeaconBlockGrpcClient;
use crate::config::Config as ValidatorConfig;
use crate::duties::UpdateOutcome;
use crate::duties::{DutiesManager, EpochDutiesMap};

View File

@ -0,0 +1,7 @@
use types::Signature;
/// Signs message using an internally-maintained private key.
pub trait Signer {
fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option<Signature>;
fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option<Signature>;
}