Implement block producer for validator client
This commit is contained in:
parent
f8201edddd
commit
eea772de3e
@ -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.
|
||||
|
@ -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" }
|
||||
|
@ -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>;
|
||||
}
|
@ -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>;
|
||||
}
|
@ -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 {
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -1,3 +0,0 @@
|
||||
pub mod config;
|
||||
|
||||
pub use crate::config::Config;
|
@ -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};
|
||||
|
@ -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};
|
||||
|
7
validator_client/src/signer.rs
Normal file
7
validator_client/src/signer.rs
Normal 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>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user