Initial implementation of AttestationProducer

This commit is contained in:
Age Manning 2019-03-30 17:06:43 +11:00
parent 25d1ddfbb0
commit c107ebf9aa
No known key found for this signature in database
GPG Key ID: 05EED64B79E06A93
5 changed files with 222 additions and 63 deletions

View File

@ -0,0 +1,22 @@
//TODO: generalise these enums to the crate
use super::block_producer::{BeaconNodeError, PublishOutcome};
/// Defines the methods required to produce and publish attestations on a Beacon Node. Abstracts the
/// actual beacon node.
pub trait BeaconNodeAttestation: Send + Sync {
/// Request that the node produces the required attestation data.
///
fn produce_attestation_data(
&self,
slot: Slot,
shard: u64,
) -> Result<AttestationData, BeaconNodeError>;
/// Request that the node publishes a attestation.
///
/// Returns `true` if the publish was successful.
fn publish_attestation(
&self,
attestation: Attestation,
) -> Result<PublishOutcome, BeaconNodeError>;
}

View File

@ -0,0 +1,45 @@
use protos::services_grpc::AttestationServiceClient;
use std::sync::Arc;
use attester::{BeaconNode, BeaconNodeError, PublishOutcome};
use protos::services::ProduceAttestationDataRequest;
use types::{Attestation, AttestationData, Slot};
pub struct AttestationGrpcClient {
client: Arc<AttestationServiceClient>,
}
impl AttestationGrpcClient {
pub fn new(client: Arc<AttestationServiceClient>) -> Self {
Self { client }
}
}
/*
impl BeaconNode for AttestationGrpcClient {
fn produce_attestation_data(
&self,
slot: Slot,
shard: u64,
) -> Result<Option<AttestationData>, BeaconNodeError> {
let mut req = ProduceAttestationDataRequest::new();
req.set_slot(slot.as_u64());
req.set_shard(shard);
let reply = self
.client
.produce_attestation_data(&req)
.map_err(|err| BeaconNodeError::RemoteFailure(format!("{:?}", err)))?;
// TODO: return correct Attestation
Err(BeaconNodeError::DecodeFailure)
}
fn publish_attestation(
&self,
attestation: Attestation,
) -> Result<PublishOutcome, BeaconNodeError> {
// TODO: return correct PublishOutcome
Err(BeaconNodeError::DecodeFailure)
}
}
*/

View File

@ -1,59 +1,131 @@
mod grpc; mod grpc;
/* mod beacon_node_attestation;
use attester::{Attester, BeaconNode, DutiesReader, PollOutcome as AttesterPollOutcome, Signer};
use slog::{error, info, warn, Logger};
use slot_clock::SlotClock;
use std::time::Duration;
pub use self::attestation_grpc_client::AttestationGrpcClient; use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Domain, Fork, Slot};
pub struct AttesterService<U: BeaconNode, W: Signer> { #[derive(Debug, PartialEq)]
pub attester: Attester<U, W>, pub enum Error {
pub poll_interval_millis: u64, BeaconNodeError(BeaconNodeError),
pub log: Logger,
} }
/// This struct contains the logic for requesting and signing beacon attestations for a validator. The
/// validator can abstractly sign via the Signer trait object.
pub struct AttestationProducer<'a, B: BeaconNodeAttestation, S: Signer> {
/// The current fork.
pub fork: Fork,
/// The current slot to produce an attestation for.
pub slot: Slot,
/// The current epoch.
pub spec: Arc<ChainSpec>,
/// The beacon node to connect to.
pub beacon_node: Arc<B>,
/// The signer to sign the block.
pub signer: &'a S,
}
impl<U: BeaconNode, W: Signer> AttesterService<U, W> { impl<'a, B: BeaconNodeAttestation, S: Signer> AttestationProducer<'a, B, S> {
/// Run a loop which polls the Attester each `poll_interval_millis` millseconds. /// Handle outputs and results from attestation production.
pub fn handle_produce_attestation(&mut self, log: slog::Logger) {
match self.produce_attestation() {
Ok(ValidatorEvent::AttestationProduced(_slot)) => {
info!(log, "Attestation produced"; "Validator" => format!("{}", self.signer))
}
Err(e) => error!(log, "Attestation production error"; "Error" => format!("{:?}", e)),
Ok(ValidatorEvent::SignerRejection(_slot)) => {
error!(log, "Attestation production error"; "Error" => format!("Signer could not sign the attestation"))
}
Ok(ValidatorEvent::SlashableAttestationNotProduced(_slot)) => {
error!(log, "Attestation production error"; "Error" => format!("Rejected the attestation as it could have been slashed"))
}
Ok(ValidatorEvent::BeaconNodeUnableToProduceAttestation(_slot)) => {
error!(log, "Attestation production error"; "Error" => format!("Beacon node was unable to produce an attestation"))
}
}
}
/// Produce an attestation, sign it and send it back
/// ///
/// Logs the results of the polls. /// Assumes that an attestation is required at this slot (does not check the duties).
pub fn run(&mut self) { ///
loop { /// Ensures the message is not slashable.
/* We don't do the polling any more... ///
match self.attester.poll() { /// !!! UNSAFE !!!
Err(error) => { ///
error!(self.log, "Attester poll error"; "error" => format!("{:?}", error)) /// The slash-protection code is not yet implemented. There is zero protection against
/// slashing.
pub fn produce_attestation(&mut self) -> Result<ValidatorEvent, Error> {
let epoch = self.slot.epoch(self.spec.slots_per_epoch);
if let Some(attestation) = self
.beacon_node
.produce_attestation_data(self.slot, self.shard)?
{
if self.safe_to_produce(&attestation) {
let domain = self.spec.get_domain(epoch, Domain::Attestation, &self.fork);
if let Some(attestation) = self.sign_attestation(attestation, domain) {
self.beacon_node.publish_attestation(attestation)?;
Ok(ValidatorEvent::AttestationProduced(self.slot))
} else {
Ok(ValidatorEvent::SignerRejection(self.slot))
} }
Ok(AttesterPollOutcome::AttestationProduced(slot)) => { } else {
info!(self.log, "Produced Attestation"; "slot" => slot) Ok(ValidatorEvent::SlashableAttestationNotProduced(self.slot))
} }
Ok(AttesterPollOutcome::SlashableAttestationNotProduced(slot)) => { } else {
warn!(self.log, "Slashable attestation was not produced"; "slot" => slot) Ok(ValidatorEvent::BeaconNodeUnableToProduceAttestation(self.slot))
}
Ok(AttesterPollOutcome::AttestationNotRequired(slot)) => {
info!(self.log, "Attestation not required"; "slot" => slot)
}
Ok(AttesterPollOutcome::ProducerDutiesUnknown(slot)) => {
error!(self.log, "Attestation duties unknown"; "slot" => slot)
}
Ok(AttesterPollOutcome::SlotAlreadyProcessed(slot)) => {
warn!(self.log, "Attempted to re-process slot"; "slot" => slot)
}
Ok(AttesterPollOutcome::BeaconNodeUnableToProduceAttestation(slot)) => {
error!(self.log, "Beacon node unable to produce attestation"; "slot" => slot)
}
Ok(AttesterPollOutcome::SignerRejection(slot)) => {
error!(self.log, "The cryptographic signer refused to sign the attestation"; "slot" => slot)
}
Ok(AttesterPollOutcome::ValidatorIsUnknown(slot)) => {
error!(self.log, "The Beacon Node does not recognise the validator"; "slot" => slot)
}
};
*/
println!("Legacy polling still happening...");
std::thread::sleep(Duration::from_millis(self.poll_interval_millis));
} }
} }
/// Consumes an attestation, returning the attestation signed by the validators private key.
///
/// Important: this function will not check to ensure the attestation is not slashable. This must be
/// done upstream.
fn sign_attestation(&mut self, mut attestation: Attestation, duties: AttestationDuties, domain: u64) -> Option<AggregateSignature> {
self.store_produce(&attestation);
// build the aggregate signature
let aggregate_sig = {
let message = AttestationDataAndCustodyBit {
data: attestation.clone(),
custody_bit: false,
}.hash_tree_root();
let sig = self.signer.sign_message(&message, domain)?;
let mut agg_sig = AggregateSignature::new();
agg_sig.add(&sig);
agg_sig
}
let mut aggregation_bitfield = Bitfield::with_capacity(duties.comitee_size);
let custody_bitfield = Bitfield::with_capacity(duties.committee_size);
aggregation_bitfield.set(duties.committee_index, true);
Attestation {
aggregation_bitfield,
data,
custody_bitfield,
aggregate_signature,
}
}
/// Returns `true` if signing an attestation is safe (non-slashable).
///
/// !!! UNSAFE !!!
///
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
fn safe_to_produce(&self, _block: &Attestation) -> bool {
//TODO: Implement slash protection
true
}
/// Record that an attestation was produced so that slashable votes may not be made in the future.
///
/// !!! UNSAFE !!!
///
/// Important: this function is presently stubbed-out. It provides ZERO SAFETY.
fn store_produce(&mut self, _block: &BeaconBlock) {
// TODO: Implement slash protection
}
} }
*/

View File

@ -0,0 +1,31 @@
use types::{BeaconBlock, Signature, Slot};
#[derive(Debug, PartialEq, Clone)]
pub enum BeaconNodeError {
RemoteFailure(String),
DecodeFailure,
}
#[derive(Debug, PartialEq, Clone)]
pub enum PublishOutcome {
Valid,
InvalidBlock(String),
InvalidAttestation(String),
}
/// Defines the methods required to produce and publish blocks on a Beacon Node. Abstracts the
/// actual beacon node.
pub trait BeaconNodeBlock: 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 successful.
fn publish_beacon_block(&self, block: BeaconBlock) -> Result<PublishOutcome, BeaconNodeError>;
}

View File

@ -3,8 +3,7 @@ use types::{Keypair, PublicKey, Signature};
/// Signs message using an internally-maintained private key. /// Signs message using an internally-maintained private key.
pub trait Signer: Display + Send + Sync + Clone { pub trait Signer: Display + Send + Sync + Clone {
fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option<Signature>; fn sign_message(&self, message: &[u8], domain: u64) -> Option<Signature>;
fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option<Signature>;
/// Returns a public key for the signer object. /// Returns a public key for the signer object.
fn to_public(&self) -> PublicKey; fn to_public(&self) -> PublicKey;
} }
@ -16,17 +15,7 @@ impl Signer for Keypair {
self.pk.clone() self.pk.clone()
} }
fn sign_block_proposal(&self, message: &[u8], domain: u64) -> Option<Signature> { fn sign_message(&self, message: &[u8], domain: u64) -> Option<Signature> {
Some(Signature::new(message, domain, &self.sk)) Some(Signature::new(message, domain, &self.sk))
} }
fn sign_randao_reveal(&self, message: &[u8], domain: u64) -> Option<Signature> {
Some(Signature::new(message, domain, &self.sk))
}
/*
fn sign_attestation_message(&self, message: &[u8], domain: u64) -> Option<Signature> {
Some(Signature::new(message, domain, &self.sk))
}
*/
} }