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 that handles block production
|
||||||
service BeaconBlockService {
|
service BeaconBlockService {
|
||||||
|
// Requests a block to be signed from the beacon node.
|
||||||
rpc ProduceBeaconBlock(ProduceBeaconBlockRequest) returns (ProduceBeaconBlockResponse);
|
rpc ProduceBeaconBlock(ProduceBeaconBlockRequest) returns (ProduceBeaconBlockResponse);
|
||||||
|
// Responds to the node the signed block to be published.
|
||||||
rpc PublishBeaconBlock(PublishBeaconBlockRequest) returns (PublishBeaconBlockResponse);
|
rpc PublishBeaconBlock(PublishBeaconBlockRequest) returns (PublishBeaconBlockResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +66,7 @@ message Empty {}
|
|||||||
// Validator requests an unsigned proposal.
|
// Validator requests an unsigned proposal.
|
||||||
message ProduceBeaconBlockRequest {
|
message ProduceBeaconBlockRequest {
|
||||||
uint64 slot = 1;
|
uint64 slot = 1;
|
||||||
|
bytes randao_reveal = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Beacon node returns an unsigned proposal.
|
// Beacon node returns an unsigned proposal.
|
||||||
|
@ -8,11 +8,6 @@ edition = "2018"
|
|||||||
name = "validator_client"
|
name = "validator_client"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "validator_client"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
block_proposer = { path = "../eth2/block_proposer" }
|
block_proposer = { path = "../eth2/block_proposer" }
|
||||||
attester = { path = "../eth2/attester" }
|
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;
|
use super::beacon_block_node::{BeaconBlockNode, BeaconBlockNodeError};
|
||||||
mod traits;
|
use crate::signer::Signer;
|
||||||
|
|
||||||
use slot_clock::SlotClock;
|
|
||||||
use ssz::{SignedRoot, TreeHash};
|
use ssz::{SignedRoot, TreeHash};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::{BeaconBlock, ChainSpec, Domain, Slot};
|
use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -13,11 +11,11 @@ pub enum Error {
|
|||||||
EpochMapPoisoned,
|
EpochMapPoisoned,
|
||||||
SlotClockPoisoned,
|
SlotClockPoisoned,
|
||||||
EpochLengthIsZero,
|
EpochLengthIsZero,
|
||||||
BeaconNodeError(BeaconNodeError),
|
BeaconBlockNodeError(BeaconBlockNodeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum BlockProducerEvent {
|
pub enum ValidatorEvent {
|
||||||
/// A new block was produced.
|
/// A new block was produced.
|
||||||
BlockProduced(Slot),
|
BlockProduced(Slot),
|
||||||
/// A block was not produced as it would have been slashable.
|
/// 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
|
/// This struct contains the logic for requesting and signing beacon blocks for a validator. The
|
||||||
/// validator can abstractly sign via the Signer trait object.
|
/// 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.
|
/// The current fork.
|
||||||
pub fork: Fork,
|
pub fork: Fork,
|
||||||
/// The current slot to produce a block for.
|
/// The current slot to produce a block for.
|
||||||
pub slot: Slot,
|
pub slot: Slot,
|
||||||
/// The current epoch.
|
/// The current epoch.
|
||||||
pub epoch: Epoch,
|
pub spec: Arc<ChainSpec>,
|
||||||
/// The beacon node to connect to.
|
/// The beacon node to connect to.
|
||||||
pub beacon_node: Arc<B>,
|
pub beacon_node: Arc<B>,
|
||||||
/// The signer to sign the block.
|
/// The signer to sign the block.
|
||||||
pub signer: Arc<S>,
|
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.
|
/// Produce a block at some slot.
|
||||||
///
|
///
|
||||||
/// Assumes that a block is required at this slot (does not check the duties).
|
/// 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
|
/// The slash-protection code is not yet implemented. There is zero protection against
|
||||||
/// slashing.
|
/// 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 = {
|
let randao_reveal = {
|
||||||
// TODO: add domain, etc to this message. Also ensure result matches `into_to_bytes32`.
|
let message = epoch.hash_tree_root();
|
||||||
let message = slot.epoch(self.spec.slots_per_epoch).hash_tree_root();
|
let randao_reveal = match self.signer.sign_randao_reveal(
|
||||||
|
|
||||||
match self.signer.sign_randao_reveal(
|
|
||||||
&message,
|
&message,
|
||||||
self.spec
|
self.spec.get_domain(epoch, Domain::Randao, &self.fork),
|
||||||
.get_domain(slot.epoch(self.spec.slots_per_epoch), Domain::Randao, &fork),
|
|
||||||
) {
|
) {
|
||||||
None => return Ok(PollOutcome::SignerRejection(slot)),
|
None => return Ok(ValidatorEvent::SignerRejection(self.slot)),
|
||||||
Some(signature) => signature,
|
Some(signature) => signature,
|
||||||
}
|
};
|
||||||
|
randao_reveal
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(block) = self
|
if let Some(block) = self
|
||||||
.beacon_node
|
.beacon_node
|
||||||
.produce_beacon_block(slot, &randao_reveal)?
|
.produce_beacon_block(self.slot, &randao_reveal)?
|
||||||
{
|
{
|
||||||
if self.safe_to_produce(&block) {
|
if self.safe_to_produce(&block) {
|
||||||
let domain = self.spec.get_domain(
|
let domain = self.spec.get_domain(epoch, Domain::BeaconBlock, &self.fork);
|
||||||
slot.epoch(self.spec.slots_per_epoch),
|
|
||||||
Domain::BeaconBlock,
|
|
||||||
&fork,
|
|
||||||
);
|
|
||||||
if let Some(block) = self.sign_block(block, domain) {
|
if let Some(block) = self.sign_block(block, domain) {
|
||||||
self.beacon_node.publish_beacon_block(block)?;
|
self.beacon_node.publish_beacon_block(block)?;
|
||||||
Ok(PollOutcome::BlockProduced(slot))
|
Ok(ValidatorEvent::BlockProduced(self.slot))
|
||||||
} else {
|
} else {
|
||||||
Ok(PollOutcome::SignerRejection(slot))
|
Ok(ValidatorEvent::SignerRejection(self.slot))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(PollOutcome::SlashableBlockNotProduced(slot))
|
Ok(ValidatorEvent::SlashableBlockNotProduced(self.slot))
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
impl From<BeaconBlockNodeError> for Error {
|
||||||
fn from(e: BeaconNodeError) -> Error {
|
fn from(e: BeaconBlockNodeError) -> Error {
|
||||||
Error::BeaconNodeError(e)
|
Error::BeaconBlockNodeError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Old tests - Re-work for new logic
|
/* Old tests - Re-work for new logic
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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::{
|
use protos::services::{
|
||||||
BeaconBlock as GrpcBeaconBlock, ProduceBeaconBlockRequest, PublishBeaconBlockRequest,
|
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
|
/// A newtype designed to wrap the gRPC-generated service so the `BeaconNode` trait may be
|
||||||
/// implemented upon it.
|
/// implemented upon it.
|
||||||
pub struct BeaconBlockGrpcClient {
|
pub struct BeaconBlockGrpcClient {
|
||||||
inner: Arc<BeaconBlockServiceClient>,
|
client: Arc<BeaconBlockServiceClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BeaconBlockGrpcClient {
|
impl BeaconBlockGrpcClient {
|
||||||
pub fn new(client: Arc<BeaconBlockServiceClient>) -> Self {
|
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.
|
/// 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
|
/// 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(
|
fn produce_beacon_block(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
// TODO: use randao_reveal, when proto APIs have been updated.
|
randao_reveal: &Signature,
|
||||||
_randao_reveal: &Signature,
|
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError> {
|
||||||
) -> Result<Option<BeaconBlock>, BeaconNodeError> {
|
// request a beacon block from the node
|
||||||
let mut req = ProduceBeaconBlockRequest::new();
|
let mut req = ProduceBeaconBlockRequest::new();
|
||||||
req.set_slot(slot.as_u64());
|
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
|
let reply = self
|
||||||
.client
|
.client
|
||||||
.produce_beacon_block(&req)
|
.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() {
|
if reply.has_block() {
|
||||||
let block = reply.get_block();
|
let block = reply.get_block();
|
||||||
let ssz = block.get_ssz();
|
let ssz = block.get_ssz();
|
||||||
|
|
||||||
let (block, _i) =
|
let (block, _i) = BeaconBlock::ssz_decode(&ssz, 0)
|
||||||
BeaconBlock::ssz_decode(&ssz, 0).map_err(|_| BeaconNodeError::DecodeFailure)?;
|
.map_err(|_| BeaconBlockNodeError::DecodeFailure)?;
|
||||||
|
|
||||||
Ok(Some(block))
|
Ok(Some(block))
|
||||||
} else {
|
} 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
|
/// Generally, this will be called after a `produce_beacon_block` call with a block that has
|
||||||
/// been completed (signed) by the validator client.
|
/// 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 mut req = PublishBeaconBlockRequest::new();
|
||||||
|
|
||||||
let ssz = ssz_encode(&block);
|
let ssz = ssz_encode(&block);
|
||||||
|
|
||||||
// TODO: this conversion is incomplete; fix it.
|
|
||||||
let mut grpc_block = GrpcBeaconBlock::new();
|
let mut grpc_block = GrpcBeaconBlock::new();
|
||||||
grpc_block.set_ssz(ssz);
|
grpc_block.set_ssz(ssz);
|
||||||
|
|
||||||
@ -68,7 +77,7 @@ impl BeaconNode for BeaconBlockGrpcClient {
|
|||||||
let reply = self
|
let reply = self
|
||||||
.client
|
.client
|
||||||
.publish_beacon_block(&req)
|
.publish_beacon_block(&req)
|
||||||
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
|
.map_err(|err| BeaconBlockNodeError::RemoteFailure(format!("{:?}", err)))?;
|
||||||
|
|
||||||
if reply.get_success() {
|
if reply.get_success() {
|
||||||
Ok(PublishOutcome::ValidBlock)
|
Ok(PublishOutcome::ValidBlock)
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
pub mod config;
|
|
||||||
|
|
||||||
pub use crate::config::Config;
|
|
@ -4,6 +4,7 @@ mod config;
|
|||||||
mod duties;
|
mod duties;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod service;
|
mod service;
|
||||||
|
mod signer;
|
||||||
|
|
||||||
use crate::config::Config as ValidatorClientConfig;
|
use crate::config::Config as ValidatorClientConfig;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
/// data from the beacon node and performs the signing before publishing the block to the beacon
|
/// data from the beacon node and performs the signing before publishing the block to the beacon
|
||||||
/// node.
|
/// node.
|
||||||
use crate::attester_service::{AttestationGrpcClient, AttesterService};
|
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::config::Config as ValidatorConfig;
|
||||||
use crate::duties::UpdateOutcome;
|
use crate::duties::UpdateOutcome;
|
||||||
use crate::duties::{DutiesManager, EpochDutiesMap};
|
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