Add attester to beacon chain test harness

This commit is contained in:
Paul Hauner 2019-01-28 15:50:42 +11:00
parent e1698102e0
commit 5bbffcb053
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
24 changed files with 311 additions and 166 deletions

View File

@ -9,12 +9,14 @@ pub struct AttestationAggregator {
store: HashMap<Vec<u8>, Attestation>,
}
#[derive(Debug, PartialEq)]
pub enum ProcessOutcome {
AggregationNotRequired,
Aggregated,
NewAttestationCreated,
}
#[derive(Debug, PartialEq)]
pub enum ProcessError {
BadValidatorIndex,
BadSignature,

View File

@ -0,0 +1,47 @@
use super::{BeaconChain, ClientDB, SlotClock};
pub use crate::attestation_aggregator::{ProcessError as AggregatorError, ProcessOutcome};
use crate::canonical_head::Error as HeadError;
use types::{AttestationData, Signature};
#[derive(Debug, PartialEq)]
pub enum Error {
PresentSlotUnknown,
AggregatorError(AggregatorError),
HeadError(HeadError),
}
impl<T, U> BeaconChain<T, U>
where
T: ClientDB,
U: SlotClock,
{
pub fn process_free_attestation(
&self,
attestation_data: &AttestationData,
signature: &Signature,
validator_index: u64,
) -> Result<ProcessOutcome, Error> {
let present_slot = self
.present_slot()
.ok_or_else(|| Error::PresentSlotUnknown)?;
let state = self.state(present_slot)?;
self.attestation_aggregator
.write()
.expect("Aggregator unlock failed.")
.process_free_attestation(&state, attestation_data, signature, validator_index)
.map_err(|e| e.into())
}
}
impl From<AggregatorError> for Error {
fn from(e: AggregatorError) -> Error {
Error::AggregatorError(e)
}
}
impl From<HeadError> for Error {
fn from(e: HeadError) -> Error {
Error::HeadError(e)
}
}

View File

@ -3,11 +3,6 @@ use types::{AttestationData, Hash256};
#[derive(Debug, PartialEq)]
pub enum Error {
/*
DBError(String),
StateTransitionError(TransitionError),
PresentSlotIsNone,
*/
SlotTooOld,
PresentSlotUnknown,
StateError,

View File

@ -2,6 +2,7 @@ use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock};
use std::sync::RwLockReadGuard;
use types::{beacon_state::SlotProcessingError, BeaconBlock, BeaconState, Hash256};
#[derive(Debug, PartialEq)]
pub enum Error {
PastSlot,
UnableToDetermineProducer,

View File

@ -52,4 +52,11 @@ where
.beacon_block
.slot
}
pub fn validator_attestion_slot_and_shard(&self, validator_index: usize) -> Option<(u64, u64)> {
let present_slot = self.present_slot()?;
let state = self.state(present_slot).ok()?;
Some(state.attestation_slot_and_shard_for_validator(validator_index, &self.spec))
}
}

View File

@ -1,4 +1,5 @@
mod attestation_aggregation;
mod attestation_aggregator;
pub mod attestation_processing;
mod attestation_production;
mod attestation_targets;
mod block_graph;
@ -14,7 +15,7 @@ mod state_transition;
use self::attestation_targets::AttestationTargets;
use self::block_graph::BlockGraph;
use attestation_aggregation::AttestationAggregator;
use attestation_aggregator::AttestationAggregator;
use db::{
stores::{BeaconBlockStore, BeaconStateStore},
ClientDB, DBError,

View File

@ -12,11 +12,13 @@ harness = false
criterion = "0.2"
[dependencies]
attester = { path = "../../../eth2/attester" }
beacon_chain = { path = "../../beacon_chain" }
block_producer = { path = "../../../eth2/block_producer" }
bls = { path = "../../../eth2/utils/bls" }
boolean-bitfield = { path = "../../../eth2/utils/boolean-bitfield" }
db = { path = "../../db" }
parking_lot = "0.7"
failure = "0.1"
failure_derive = "0.1"
genesis = { path = "../../../eth2/genesis" }

View File

@ -1,71 +0,0 @@
use beacon_chain::block_processing::{Error as ProcessingError, Outcome as ProcessingOutcome};
use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain};
use block_producer::{
BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, PublishOutcome,
};
use db::ClientDB;
use slot_clock::SlotClock;
use std::sync::Arc;
use types::{BeaconBlock, PublicKey, Signature};
pub struct DirectBeaconNode<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
}
impl<T: ClientDB, U: SlotClock> DirectBeaconNode<T, U> {
pub fn new(beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
Self { beacon_chain }
}
}
impl<T: ClientDB, U: SlotClock> BeaconBlockNode for DirectBeaconNode<T, U>
where
BlockProductionError: From<<U>::Error>,
ProcessingError: From<<U as SlotClock>::Error>,
{
fn proposer_nonce(&self, pubkey: &PublicKey) -> Result<u64, BeaconBlockNodeError> {
let validator_index = self
.beacon_chain
.validator_index(pubkey)
.ok_or_else(|| BeaconBlockNodeError::RemoteFailure("pubkey unknown.".to_string()))?;
self.beacon_chain
.proposer_slots(validator_index)
.ok_or_else(|| {
BeaconBlockNodeError::RemoteFailure("validator_index unknown.".to_string())
})
}
fn produce_beacon_block(
&self,
slot: u64,
randao_reveal: &Signature,
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError>
where {
let (block, _state) = self
.beacon_chain
.produce_block(randao_reveal.clone())
.map_err(|e| BeaconBlockNodeError::RemoteFailure(format!("{:?}", e)))?;
if block.slot == slot {
Ok(Some(block))
} else {
Err(BeaconBlockNodeError::RemoteFailure(
"Unable to produce at non-current slot.".to_string(),
))
}
}
fn publish_beacon_block(
&self,
block: BeaconBlock,
) -> Result<PublishOutcome, BeaconBlockNodeError> {
match self.beacon_chain.process_block(block) {
Ok(ProcessingOutcome::ValidBlock(_)) => Ok(PublishOutcome::ValidBlock),
Ok(ProcessingOutcome::InvalidBlock(reason)) => {
Ok(PublishOutcome::InvalidBlock(format!("{:?}", reason)))
}
Err(error) => Err(BeaconBlockNodeError::RemoteFailure(format!("{:?}", error))),
}
}
}

View File

@ -1,38 +0,0 @@
use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain};
use block_producer::{DutiesReader, DutiesReaderError};
use db::ClientDB;
use slot_clock::SlotClock;
use std::sync::Arc;
use types::PublicKey;
pub struct DirectDuties<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
pubkey: PublicKey,
}
impl<T: ClientDB, U: SlotClock> DirectDuties<T, U> {
pub fn new(pubkey: PublicKey, beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
Self {
beacon_chain,
pubkey,
}
}
}
impl<T: ClientDB, U: SlotClock> DutiesReader for DirectDuties<T, U>
where
BlockProductionError: From<<U>::Error>,
{
fn is_block_production_slot(&self, slot: u64) -> Result<bool, DutiesReaderError> {
let validator_index = self
.beacon_chain
.validator_index(&self.pubkey)
.ok_or_else(|| DutiesReaderError::UnknownValidator)?;
match self.beacon_chain.block_proposer(slot) {
Some(proposer) if proposer == validator_index => Ok(true),
Some(_) => Ok(false),
None => Err(DutiesReaderError::UnknownEpoch),
}
}
}

View File

@ -1,11 +1,5 @@
mod beacon_chain_harness;
mod benching_beacon_node;
mod direct_beacon_node;
mod direct_duties;
mod harness;
mod validator;
pub use self::beacon_chain_harness::BeaconChainHarness;
pub use self::benching_beacon_node::BenchingBeaconNode;
pub use self::direct_beacon_node::DirectBeaconNode;
pub use self::direct_duties::DirectDuties;
pub use self::harness::BeaconChainHarness;
pub use self::validator::TestValidator;

View File

@ -0,0 +1,40 @@
use super::BenchingBeaconNode;
use attester::{BeaconNode as AttesterBeaconNode, BeaconNodeError as NodeError, PublishOutcome};
use beacon_chain::block_processing::Error as ProcessingError;
use beacon_chain::block_production::Error as BlockProductionError;
use db::ClientDB;
use slot_clock::SlotClock;
use types::{AttestationData, Signature};
impl<T: ClientDB, U: SlotClock> AttesterBeaconNode for BenchingBeaconNode<T, U>
where
BlockProductionError: From<<U>::Error>,
ProcessingError: From<<U as SlotClock>::Error>,
{
fn produce_attestation_data(
&self,
slot: u64,
shard: u64,
) -> Result<Option<AttestationData>, NodeError> {
match self.beacon_chain.produce_attestation_data(slot, shard) {
Ok(attestation_data) => Ok(Some(attestation_data)),
Err(e) => Err(NodeError::RemoteFailure(format!("{:?}", e))),
}
}
fn publish_attestation_data(
&self,
attestation_data: AttestationData,
signature: Signature,
validator_index: u64,
) -> Result<PublishOutcome, NodeError> {
match self.beacon_chain.process_free_attestation(
&attestation_data,
&signature,
validator_index,
) {
Ok(_) => Ok(PublishOutcome::ValidAttestation),
Err(e) => Err(NodeError::RemoteFailure(format!("{:?}", e))),
}
}
}

View File

@ -0,0 +1,34 @@
use beacon_chain::BeaconChain;
use db::ClientDB;
use parking_lot::RwLock;
use slot_clock::SlotClock;
use std::sync::Arc;
use types::{AttestationData, BeaconBlock, Signature};
mod attester;
mod producer;
/// An attestation that hasn't been aggregated into an `Attestation`.
///
/// (attestation_data, signature, validator_index)
pub type FreeAttestation = (AttestationData, Signature, u64);
pub struct BenchingBeaconNode<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
published_blocks: RwLock<Vec<BeaconBlock>>,
published_attestations: RwLock<Vec<FreeAttestation>>,
}
impl<T: ClientDB, U: SlotClock> BenchingBeaconNode<T, U> {
pub fn new(beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
Self {
beacon_chain,
published_blocks: RwLock::new(vec![]),
published_attestations: RwLock::new(vec![]),
}
}
pub fn last_published_block(&self) -> Option<BeaconBlock> {
Some(self.published_blocks.read().last()?.clone())
}
}

View File

@ -1,37 +1,13 @@
use super::BenchingBeaconNode;
use beacon_chain::block_processing::Error as ProcessingError;
use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain};
use beacon_chain::block_production::Error as BlockProductionError;
use block_producer::{
BeaconNode as BeaconBlockNode, BeaconNodeError as BeaconBlockNodeError, PublishOutcome,
};
use db::ClientDB;
use slot_clock::SlotClock;
use std::sync::{Arc, RwLock};
use types::{BeaconBlock, PublicKey, Signature};
pub struct BenchingBeaconNode<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
published_blocks: RwLock<Vec<BeaconBlock>>,
}
impl<T: ClientDB, U: SlotClock> BenchingBeaconNode<T, U> {
pub fn new(beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
Self {
beacon_chain,
published_blocks: RwLock::new(vec![]),
}
}
pub fn last_published_block(&self) -> Option<BeaconBlock> {
Some(
self.published_blocks
.read()
.expect("Unable to unlock `published_blocks` for reading.")
.last()?
.clone(),
)
}
}
impl<T: ClientDB, U: SlotClock> BeaconBlockNode for BenchingBeaconNode<T, U>
where
BlockProductionError: From<<U>::Error>,
@ -56,8 +32,7 @@ where
&self,
slot: u64,
randao_reveal: &Signature,
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError>
where {
) -> Result<Option<BeaconBlock>, BeaconBlockNodeError> {
let (block, _state) = self
.beacon_chain
.produce_block(randao_reveal.clone())
@ -81,10 +56,7 @@ where {
&self,
block: BeaconBlock,
) -> Result<PublishOutcome, BeaconBlockNodeError> {
self.published_blocks
.write()
.expect("Unable to unlock `published_blocks` for writing.")
.push(block);
self.published_blocks.write().push(block);
Ok(PublishOutcome::ValidBlock)
}
}

View File

@ -0,0 +1,70 @@
use attester::{
DutiesReader as AttesterDutiesReader, DutiesReaderError as AttesterDutiesReaderError,
};
use beacon_chain::{block_production::Error as BlockProductionError, BeaconChain};
use block_producer::{
DutiesReader as ProducerDutiesReader, DutiesReaderError as ProducerDutiesReaderError,
};
use db::ClientDB;
use slot_clock::SlotClock;
use std::sync::Arc;
use types::PublicKey;
pub struct DirectDuties<T: ClientDB, U: SlotClock> {
beacon_chain: Arc<BeaconChain<T, U>>,
pubkey: PublicKey,
}
impl<T: ClientDB, U: SlotClock> DirectDuties<T, U> {
pub fn new(pubkey: PublicKey, beacon_chain: Arc<BeaconChain<T, U>>) -> Self {
Self {
beacon_chain,
pubkey,
}
}
}
impl<T: ClientDB, U: SlotClock> ProducerDutiesReader for DirectDuties<T, U>
where
BlockProductionError: From<<U>::Error>,
{
fn is_block_production_slot(&self, slot: u64) -> Result<bool, ProducerDutiesReaderError> {
let validator_index = self
.beacon_chain
.validator_index(&self.pubkey)
.ok_or_else(|| ProducerDutiesReaderError::UnknownValidator)?;
match self.beacon_chain.block_proposer(slot) {
Some(proposer) if proposer == validator_index => Ok(true),
Some(_) => Ok(false),
None => Err(ProducerDutiesReaderError::UnknownEpoch),
}
}
}
impl<T: ClientDB, U: SlotClock> AttesterDutiesReader for DirectDuties<T, U>
where
BlockProductionError: From<<U>::Error>,
{
fn validator_index(&self) -> Option<u64> {
match self.beacon_chain.validator_index(&self.pubkey) {
Some(index) => Some(index as u64),
None => None,
}
}
fn attestation_shard(&self, slot: u64) -> Result<Option<u64>, AttesterDutiesReaderError> {
if let Some(validator_index) = self.validator_index() {
match self
.beacon_chain
.validator_attestion_slot_and_shard(validator_index as usize)
{
Some((attest_slot, attest_shard)) if attest_slot == slot => Ok(Some(attest_shard)),
Some(_) => Ok(None),
None => Err(AttesterDutiesReaderError::UnknownEpoch),
}
} else {
Err(AttesterDutiesReaderError::UnknownValidator)
}
}
}

View File

@ -1,11 +1,18 @@
use super::{BenchingBeaconNode, DirectDuties};
use attester::Attester;
use beacon_chain::BeaconChain;
use block_producer::{test_utils::TestSigner, BlockProducer, Error as PollError};
use block_producer::{BlockProducer, Error as PollError};
use db::MemoryDB;
use signer::TestSigner;
use slot_clock::TestingSlotClock;
use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Keypair};
mod beacon_node;
mod direct_duties;
mod signer;
pub use self::beacon_node::BenchingBeaconNode;
pub use self::direct_duties::DirectDuties;
pub use block_producer::PollOutcome;
#[derive(Debug, PartialEq)]
@ -21,6 +28,12 @@ pub struct TestValidator {
DirectDuties<MemoryDB, TestingSlotClock>,
TestSigner,
>,
pub attester: Attester<
TestingSlotClock,
BenchingBeaconNode<MemoryDB, TestingSlotClock>,
DirectDuties<MemoryDB, TestingSlotClock>,
TestSigner,
>,
pub spec: Arc<ChainSpec>,
pub epoch_map: Arc<DirectDuties<MemoryDB, TestingSlotClock>>,
pub keypair: Keypair,
@ -49,8 +62,16 @@ impl TestValidator {
signer.clone(),
);
let attester = Attester::new(
epoch_map.clone(),
slot_clock.clone(),
beacon_node.clone(),
signer.clone(),
);
Self {
block_producer,
attester,
spec,
epoch_map,
keypair,

View File

@ -0,0 +1,46 @@
use attester::Signer as AttesterSigner;
use block_producer::Signer as BlockProposerSigner;
use std::sync::RwLock;
use types::{Keypair, Signature};
/// A test-only struct used to simulate a Beacon Node.
pub struct TestSigner {
keypair: Keypair,
should_sign: RwLock<bool>,
}
impl TestSigner {
/// Produce a new TestSigner with signing enabled by default.
pub fn new(keypair: Keypair) -> Self {
Self {
keypair,
should_sign: RwLock::new(true),
}
}
/// If set to `false`, the service will refuse to sign all messages. Otherwise, all messages
/// will be signed.
pub fn enable_signing(&self, enabled: bool) {
*self.should_sign.write().unwrap() = enabled;
}
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
Some(Signature::new(message, &self.keypair.sk))
}
}
impl BlockProposerSigner for TestSigner {
fn sign_block_proposal(&self, message: &[u8]) -> Option<Signature> {
self.bls_sign(message)
}
fn sign_randao_reveal(&self, message: &[u8]) -> Option<Signature> {
self.bls_sign(message)
}
}
impl AttesterSigner for TestSigner {
fn sign_attestation_message(&self, message: &[u8]) -> Option<Signature> {
self.bls_sign(message)
}
}

View File

@ -131,7 +131,7 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> Attester<T, U, V,
self.store_produce(attestation_data);
self.signer
.bls_sign(&attestation_data.signable_message(PHASE_0_CUSTODY_BIT)[..])
.sign_attestation_message(&attestation_data.signable_message(PHASE_0_CUSTODY_BIT)[..])
}
/// Returns `true` if signing some attestation_data is safe (non-slashable).

View File

@ -25,7 +25,7 @@ impl TestSigner {
}
impl Signer for TestSigner {
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
fn sign_attestation_message(&self, message: &[u8]) -> Option<Signature> {
Some(Signature::new(message, &self.keypair.sk))
}
}

View File

@ -47,5 +47,5 @@ pub trait DutiesReader: Send + Sync {
/// Signs message using an internally-maintained private key.
pub trait Signer {
fn bls_sign(&self, message: &[u8]) -> Option<Signature>;
fn sign_attestation_message(&self, message: &[u8]) -> Option<Signature>;
}

View File

@ -4,7 +4,7 @@ mod traits;
use slot_clock::SlotClock;
use ssz::ssz_encode;
use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Hash256, ProposalSignedData, PublicKey};
use types::{BeaconBlock, ChainSpec, PublicKey};
pub use self::traits::{
BeaconNode, BeaconNodeError, DutiesReader, DutiesReaderError, PublishOutcome, Signer,
@ -139,7 +139,7 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
// TODO: add domain, etc to this message.
let message = ssz_encode(&producer_nonce);
match self.signer.bls_sign(&message) {
match self.signer.sign_randao_reveal(&message) {
None => return Ok(PollOutcome::SignerRejection(slot)),
Some(signature) => signature,
}
@ -171,7 +171,10 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
fn sign_block(&mut self, mut block: BeaconBlock) -> Option<BeaconBlock> {
self.store_produce(&block);
match self.signer.bls_sign(&block.proposal_root(&self.spec)[..]) {
match self
.signer
.sign_block_proposal(&block.proposal_root(&self.spec)[..])
{
None => None,
Some(signature) => {
block.signature = signature;

View File

@ -25,7 +25,11 @@ impl TestSigner {
}
impl Signer for TestSigner {
fn bls_sign(&self, message: &[u8]) -> Option<Signature> {
fn sign_block_proposal(&self, message: &[u8]) -> Option<Signature> {
Some(Signature::new(message, &self.keypair.sk))
}
fn sign_randao_reveal(&self, message: &[u8]) -> Option<Signature> {
Some(Signature::new(message, &self.keypair.sk))
}
}

View File

@ -47,5 +47,6 @@ pub trait DutiesReader: Send + Sync {
/// Signs message using an internally-maintained private key.
pub trait Signer {
fn bls_sign(&self, message: &[u8]) -> Option<Signature>;
fn sign_block_proposal(&self, message: &[u8]) -> Option<Signature>;
fn sign_randao_reveal(&self, message: &[u8]) -> Option<Signature>;
}

View File

@ -38,6 +38,20 @@ impl BeaconState {
let validator_count = self.validator_registry.len();
Some((slot as usize) % validator_count)
}
pub fn attestation_slot_and_shard_for_validator(
&self,
validator_index: usize,
spec: &ChainSpec,
) -> (u64, u64) {
// TODO: this is a stub; implement it properly.
let validator_index = validator_index as u64;
let slot = validator_index % spec.epoch_length;
let shard = validator_index % spec.shard_count;
(slot, shard)
}
}
fn merkle_root(_input: &[Hash256]) -> Hash256 {