Build validator client AttestationProducer

This commit is contained in:
Age Manning 2019-03-30 17:56:43 +11:00
parent a952acb86f
commit 145cabc427
No known key found for this signature in database
GPG Key ID: 05EED64B79E06A93
5 changed files with 83 additions and 52 deletions

View File

@ -1,5 +1,6 @@
//TODO: generalise these enums to the crate //TODO: generalise these enums to the crate
use super::block_producer::{BeaconNodeError, PublishOutcome}; use crate::block_producer::{BeaconNodeError, PublishOutcome};
use types::{Attestation, AttestationData, Slot};
/// Defines the methods required to produce and publish attestations on a Beacon Node. Abstracts the /// Defines the methods required to produce and publish attestations on a Beacon Node. Abstracts the
/// actual beacon node. /// actual beacon node.

View File

@ -1,22 +1,38 @@
mod grpc;
mod beacon_node_attestation; mod beacon_node_attestation;
mod grpc;
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot}; use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot};
use super::block_proposer::beacon_node_block::BeaconNodeError; //TODO: Move these higher up in the crate
use super::block_producer::{BeaconNodeError, ValidatorEvent};
use crate::signer::Signer;
use beacon_node_attestation::BeaconNodeAttestation;
use slog::{error, info, warn};
use ssz::TreeHash;
use types::{
AggregateSignature, Attestation, AttestationData, AttestationDataAndCustodyBit,
AttestationDuty, Bitfield,
};
//TODO: Group these errors at a crate level
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
BeaconNodeError(BeaconNodeError), BeaconNodeError(BeaconNodeError),
} }
impl From<BeaconNodeError> for Error {
fn from(e: BeaconNodeError) -> Error {
Error::BeaconNodeError(e)
}
}
/// This struct contains the logic for requesting and signing beacon attestations for a validator. The /// This struct contains the logic for requesting and signing beacon attestations for a validator. The
/// validator can abstractly sign via the Signer trait object. /// validator can abstractly sign via the Signer trait object.
pub struct AttestationProducer<'a, B: BeaconNodeAttestation, S: Signer> { pub struct AttestationProducer<'a, B: BeaconNodeAttestation, S: Signer> {
/// The current fork. /// The current fork.
pub fork: Fork, pub fork: Fork,
/// The current slot to produce an attestation for. /// The attestation duty to perform.
pub slot: Slot, pub duty: AttestationDuty,
/// The current epoch. /// The current epoch.
pub spec: Arc<ChainSpec>, pub spec: Arc<ChainSpec>,
/// The beacon node to connect to. /// The beacon node to connect to.
@ -42,6 +58,9 @@ impl<'a, B: BeaconNodeAttestation, S: Signer> AttestationProducer<'a, B, S> {
Ok(ValidatorEvent::BeaconNodeUnableToProduceAttestation(_slot)) => { Ok(ValidatorEvent::BeaconNodeUnableToProduceAttestation(_slot)) => {
error!(log, "Attestation production error"; "Error" => format!("Beacon node was unable to produce an attestation")) error!(log, "Attestation production error"; "Error" => format!("Beacon node was unable to produce an attestation"))
} }
Ok(v) => {
warn!(log, "Unknown result for attestation production"; "Error" => format!("{:?}",v))
}
} }
} }
@ -56,25 +75,23 @@ impl<'a, B: BeaconNodeAttestation, S: Signer> AttestationProducer<'a, 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.
pub fn produce_attestation(&mut self) -> Result<ValidatorEvent, Error> { pub fn produce_attestation(&mut self) -> Result<ValidatorEvent, Error> {
let epoch = self.slot.epoch(self.spec.slots_per_epoch); let epoch = self.duty.slot.epoch(self.spec.slots_per_epoch);
if let Some(attestation) = self let attestation = self
.beacon_node .beacon_node
.produce_attestation_data(self.slot, self.shard)? .produce_attestation_data(self.duty.slot, self.duty.shard)?;
{ if self.safe_to_produce(&attestation) {
if self.safe_to_produce(&attestation) { let domain = self.spec.get_domain(epoch, Domain::Attestation, &self.fork);
let domain = self.spec.get_domain(epoch, Domain::Attestation, &self.fork); if let Some(attestation) = self.sign_attestation(attestation, self.duty, domain) {
if let Some(attestation) = self.sign_attestation(attestation, domain) { self.beacon_node.publish_attestation(attestation)?;
self.beacon_node.publish_attestation(attestation)?; Ok(ValidatorEvent::AttestationProduced(self.duty.slot))
Ok(ValidatorEvent::AttestationProduced(self.slot))
} else {
Ok(ValidatorEvent::SignerRejection(self.slot))
}
} else { } else {
Ok(ValidatorEvent::SlashableAttestationNotProduced(self.slot)) Ok(ValidatorEvent::SignerRejection(self.duty.slot))
} }
} else { } else {
Ok(ValidatorEvent::BeaconNodeUnableToProduceAttestation(self.slot)) Ok(ValidatorEvent::SlashableAttestationNotProduced(
self.duty.slot,
))
} }
} }
@ -82,33 +99,39 @@ impl<'a, B: BeaconNodeAttestation, S: Signer> AttestationProducer<'a, B, S> {
/// ///
/// Important: this function will not check to ensure the attestation is not slashable. This must be /// Important: this function will not check to ensure the attestation is not slashable. This must be
/// done upstream. /// done upstream.
fn sign_attestation(&mut self, mut attestation: Attestation, duties: AttestationDuties, domain: u64) -> Option<AggregateSignature> { fn sign_attestation(
&mut self,
mut attestation: AttestationData,
duties: AttestationDuty,
domain: u64,
) -> Option<Attestation> {
self.store_produce(&attestation); self.store_produce(&attestation);
// build the aggregate signature // build the aggregate signature
let aggregate_sig = { let aggregate_signature = {
let message = AttestationDataAndCustodyBit { let message = AttestationDataAndCustodyBit {
data: attestation.clone(), data: attestation.clone(),
custody_bit: false, custody_bit: false,
}.hash_tree_root(); }
.hash_tree_root();
let sig = self.signer.sign_message(&message, domain)?; let sig = self.signer.sign_message(&message, domain)?;
let mut agg_sig = AggregateSignature::new(); let mut agg_sig = AggregateSignature::new();
agg_sig.add(&sig); agg_sig.add(&sig);
agg_sig agg_sig
} };
let mut aggregation_bitfield = Bitfield::with_capacity(duties.comitee_size); let mut aggregation_bitfield = Bitfield::with_capacity(duties.committee_len);
let custody_bitfield = Bitfield::with_capacity(duties.committee_size); let custody_bitfield = Bitfield::with_capacity(duties.committee_len);
aggregation_bitfield.set(duties.committee_index, true); aggregation_bitfield.set(duties.committee_index, true);
Attestation { Some(Attestation {
aggregation_bitfield, aggregation_bitfield,
data, data: attestation,
custody_bitfield, custody_bitfield,
aggregate_signature, aggregate_signature,
} })
} }
/// Returns `true` if signing an attestation is safe (non-slashable). /// Returns `true` if signing an attestation is safe (non-slashable).
@ -116,8 +139,8 @@ impl<'a, B: BeaconNodeAttestation, S: Signer> AttestationProducer<'a, B, S> {
/// !!! UNSAFE !!! /// !!! UNSAFE !!!
/// ///
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY. /// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
fn safe_to_produce(&self, _block: &Attestation) -> bool { fn safe_to_produce(&self, _attestation: &AttestationData) -> bool {
//TODO: Implement slash protection //TODO: Implement slash protection
true true
} }
@ -126,7 +149,7 @@ impl<'a, B: BeaconNodeAttestation, S: Signer> AttestationProducer<'a, B, S> {
/// !!! UNSAFE !!! /// !!! UNSAFE !!!
/// ///
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY. /// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
fn store_produce(&mut self, _block: &BeaconBlock) { fn store_produce(&mut self, _attestation: &AttestationData) {
// TODO: Implement slash protection // TODO: Implement slash protection
} }
} }

View File

@ -1,10 +1,11 @@
mod beacon_node_block; mod beacon_node_block;
mod grpc; mod grpc;
use self::beacon_node_block::{BeaconNodeBlock, BeaconNodeError}; use self::beacon_node_block::BeaconNodeBlock;
pub use self::beacon_node_block::{BeaconNodeError, PublishOutcome};
pub use self::grpc::BeaconBlockGrpcClient; pub use self::grpc::BeaconBlockGrpcClient;
use crate::signer::Signer; use crate::signer::Signer;
use slog::{error, info}; use slog::{error, info, warn};
use ssz::{SignedRoot, TreeHash}; use ssz::{SignedRoot, TreeHash};
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot}; use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot};
@ -18,10 +19,16 @@ pub enum Error {
pub enum ValidatorEvent { pub enum ValidatorEvent {
/// A new block was produced. /// A new block was produced.
BlockProduced(Slot), BlockProduced(Slot),
/// A new attestation was produced.
AttestationProduced(Slot),
/// A block was not produced as it would have been slashable. /// A block was not produced as it would have been slashable.
SlashableBlockNotProduced(Slot), SlashableBlockNotProduced(Slot),
/// An attestation was not produced as it would have been slashable.
SlashableAttestationNotProduced(Slot),
/// 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(Slot), BeaconNodeUnableToProduceBlock(Slot),
/// The Beacon Node was unable to produce an attestation at that slot.
BeaconNodeUnableToProduceAttestation(Slot),
/// The signer failed to sign the message. /// The signer failed to sign the message.
SignerRejection(Slot), SignerRejection(Slot),
} }
@ -58,6 +65,9 @@ impl<'a, B: BeaconNodeBlock, S: Signer> BlockProducer<'a, B, S> {
Ok(ValidatorEvent::BeaconNodeUnableToProduceBlock(_slot)) => { Ok(ValidatorEvent::BeaconNodeUnableToProduceBlock(_slot)) => {
error!(log, "Block production error"; "Error" => format!("Beacon node was unable to produce a block")) error!(log, "Block production error"; "Error" => format!("Beacon node was unable to produce a block"))
} }
Ok(v) => {
warn!(log, "Unknown result for block production"; "Error" => format!("{:?}",v))
}
} }
} }
@ -76,7 +86,7 @@ impl<'a, B: BeaconNodeBlock, S: Signer> BlockProducer<'a, B, S> {
let randao_reveal = { let randao_reveal = {
let message = epoch.hash_tree_root(); let message = epoch.hash_tree_root();
let randao_reveal = match self.signer.sign_randao_reveal( let randao_reveal = match self.signer.sign_message(
&message, &message,
self.spec.get_domain(epoch, Domain::Randao, &self.fork), self.spec.get_domain(epoch, Domain::Randao, &self.fork),
) { ) {
@ -113,10 +123,7 @@ impl<'a, B: BeaconNodeBlock, S: Signer> BlockProducer<'a, B, S> {
fn sign_block(&mut self, mut block: BeaconBlock, domain: u64) -> Option<BeaconBlock> { fn sign_block(&mut self, mut block: BeaconBlock, domain: u64) -> Option<BeaconBlock> {
self.store_produce(&block); self.store_produce(&block);
match self match self.signer.sign_message(&block.signed_root()[..], domain) {
.signer
.sign_block_proposal(&block.signed_root()[..], domain)
{
None => None, None => None,
Some(signature) => { Some(signature) => {
block.signature = signature; block.signature = signature;

View File

@ -38,7 +38,7 @@ impl EpochDuty {
// if the validator is required to attest to a shard, create the data // if the validator is required to attest to a shard, create the data
let mut attestation_duty = None; let mut attestation_duty = None;
if self.attestation_duty.slot == slot { if self.attestation_duty.slot == slot {
attestation_duty = self.attestation_duty attestation_duty = Some(self.attestation_duty)
} }
if produce_block | attestation_duty.is_some() { if produce_block | attestation_duty.is_some() {
@ -60,7 +60,7 @@ impl fmt::Display for EpochDuty {
write!( write!(
f, f,
"produce block slot: {}, attestation slot: {}, attestation shard: {}", "produce block slot: {}, attestation slot: {}, attestation shard: {}",
display_block, self.attestation_slot, self.attestation_shard display_block, self.attestation_duty.slot, self.attestation_duty.shard
) )
} }
} }

View File

@ -6,7 +6,7 @@ use protos::services_grpc::ValidatorServiceClient;
use ssz::ssz_encode; use ssz::ssz_encode;
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
use types::{Epoch, PublicKey, Slot}; use types::{AttestationDuty, Epoch, PublicKey, Slot};
impl BeaconNodeDuties for ValidatorServiceClient { impl BeaconNodeDuties for ValidatorServiceClient {
/// Requests all duties (block signing and committee attesting) from the Beacon Node (BN). /// Requests all duties (block signing and committee attesting) from the Beacon Node (BN).
@ -49,11 +49,11 @@ impl BeaconNodeDuties for ValidatorServiceClient {
}; };
let attestation_duty = AttestationDuty { let attestation_duty = AttestationDuty {
slot: Slot::from(active_duty.get_attestation_slot()), slot: Slot::from(active_duty.get_attestation_slot()),
shard: active_duty.get_attestation_shard(), shard: active_duty.get_attestation_shard(),
committee_index: active_duty.get_committee_index(), committee_index: active_duty.get_committee_index() as usize,
comittee_size: 10, committee_len: 10,
} };
let epoch_duty = EpochDuty { let epoch_duty = EpochDuty {
block_production_slot, block_production_slot,