Build validator client AttestationProducer
This commit is contained in:
parent
a952acb86f
commit
145cabc427
@ -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.
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user