diff --git a/protos/src/services.proto b/protos/src/services.proto index dd82855a1..e5095f386 100644 --- a/protos/src/services.proto +++ b/protos/src/services.proto @@ -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. diff --git a/validator_client/Cargo.toml b/validator_client/Cargo.toml index 570e06d74..209ebf25e 100644 --- a/validator_client/Cargo.toml +++ b/validator_client/Cargo.toml @@ -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" } diff --git a/validator_client/src/beacon_block_node.rs b/validator_client/src/beacon_block_node.rs deleted file mode 100644 index bc85ef194..000000000 --- a/validator_client/src/beacon_block_node.rs +++ /dev/null @@ -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, BeaconNodeError>; - - /// Request that the node publishes a block. - /// - /// Returns `true` if the publish was sucessful. - fn publish_beacon_block(&self, block: BeaconBlock) -> Result; -} diff --git a/validator_client/src/block_producer_service/beacon_block_node.rs b/validator_client/src/block_producer_service/beacon_block_node.rs new file mode 100644 index 000000000..5a581a4a7 --- /dev/null +++ b/validator_client/src/block_producer_service/beacon_block_node.rs @@ -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, BeaconBlockNodeError>; + + /// Request that the node publishes a block. + /// + /// Returns `true` if the publish was successful. + fn publish_beacon_block( + &self, + block: BeaconBlock, + ) -> Result; +} diff --git a/validator_client/src/block_producer_service/block_producer.rs b/validator_client/src/block_producer_service/block_producer.rs index 187f2b649..e71e6cd4b 100644 --- a/validator_client/src/block_producer_service/block_producer.rs +++ b/validator_client/src/block_producer_service/block_producer.rs @@ -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 { +pub struct BlockProducer { /// 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, /// The beacon node to connect to. pub beacon_node: Arc, /// The signer to sign the block. pub signer: Arc, } -impl BlockProducer { - +impl BlockProducer { /// 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 BlockProducer { /// /// The slash-protection code is not yet implemented. There is zero protection against /// slashing. - fn produce_block(&mut self) -> Result { + fn produce_block(&mut self) -> Result { + 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 BlockProducer { } } -impl From for Error { - fn from(e: BeaconNodeError) -> Error { - Error::BeaconNodeError(e) +impl From 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 { ); } } +*/ diff --git a/validator_client/src/block_producer_service/mod.rs b/validator_client/src/block_producer_service/mod.rs index 8a8cce613..fe34f627d 100644 --- a/validator_client/src/block_producer_service/mod.rs +++ b/validator_client/src/block_producer_service/mod.rs @@ -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, + client: Arc, } impl BeaconBlockGrpcClient { pub fn new(client: Arc) -> 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, BeaconNodeError> { + randao_reveal: &Signature, + ) -> Result, 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 { + fn publish_beacon_block( + &self, + block: BeaconBlock, + ) -> Result { 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) diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs deleted file mode 100644 index 470a070e8..000000000 --- a/validator_client/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod config; - -pub use crate::config::Config; diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index d044030fe..0b11ed0d0 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -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}; diff --git a/validator_client/src/service.rs b/validator_client/src/service.rs index 64ed7cb03..9c0f5d23c 100644 --- a/validator_client/src/service.rs +++ b/validator_client/src/service.rs @@ -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}; diff --git a/validator_client/src/signer.rs b/validator_client/src/signer.rs new file mode 100644 index 000000000..85bf35b16 --- /dev/null +++ b/validator_client/src/signer.rs @@ -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; + fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option; +}