Implement Attestation building in test harness

This commit is contained in:
Paul Hauner 2019-03-30 16:02:09 +11:00
parent 89cc92572a
commit 397e104f9b
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
5 changed files with 69 additions and 84 deletions

View File

@ -1,4 +1,3 @@
use crate::attestation_aggregator::{AttestationAggregator, Outcome as AggregationOutcome};
use crate::checkpoint::CheckPoint; use crate::checkpoint::CheckPoint;
use crate::errors::{BeaconChainError as Error, BlockProductionError}; use crate::errors::{BeaconChainError as Error, BlockProductionError};
use db::{ use db::{
@ -88,7 +87,6 @@ pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock, F: ForkChoice> {
pub block_store: Arc<BeaconBlockStore<T>>, pub block_store: Arc<BeaconBlockStore<T>>,
pub state_store: Arc<BeaconStateStore<T>>, pub state_store: Arc<BeaconStateStore<T>>,
pub slot_clock: U, pub slot_clock: U,
pub attestation_aggregator: RwLock<AttestationAggregator>,
pub op_pool: OperationPool, pub op_pool: OperationPool,
canonical_head: RwLock<CheckPoint>, canonical_head: RwLock<CheckPoint>,
finalized_head: RwLock<CheckPoint>, finalized_head: RwLock<CheckPoint>,
@ -131,7 +129,6 @@ where
genesis_state.clone(), genesis_state.clone(),
state_root, state_root,
)); ));
let attestation_aggregator = RwLock::new(AttestationAggregator::new());
genesis_state.build_epoch_cache(RelativeEpoch::Previous, &spec)?; genesis_state.build_epoch_cache(RelativeEpoch::Previous, &spec)?;
genesis_state.build_epoch_cache(RelativeEpoch::Current, &spec)?; genesis_state.build_epoch_cache(RelativeEpoch::Current, &spec)?;
@ -142,7 +139,6 @@ where
block_store, block_store,
state_store, state_store,
slot_clock, slot_clock,
attestation_aggregator,
op_pool: OperationPool::new(), op_pool: OperationPool::new(),
state: RwLock::new(genesis_state), state: RwLock::new(genesis_state),
finalized_head, finalized_head,
@ -477,7 +473,7 @@ where
} }
/// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`.
pub fn produce_attestation(&self, shard: u64) -> Result<AttestationData, Error> { pub fn produce_attestation_data(&self, shard: u64) -> Result<AttestationData, Error> {
trace!("BeaconChain::produce_attestation: shard: {}", shard); trace!("BeaconChain::produce_attestation: shard: {}", shard);
let source_epoch = self.state.read().current_justified_epoch; let source_epoch = self.state.read().current_justified_epoch;
let source_root = *self.state.read().get_block_root( let source_root = *self.state.read().get_block_root(
@ -509,33 +505,6 @@ where
}) })
} }
/// Validate a `FreeAttestation` and either:
///
/// - Create a new `Attestation`.
/// - Aggregate it to an existing `Attestation`.
pub fn process_free_attestation(
&self,
free_attestation: FreeAttestation,
) -> Result<AggregationOutcome, Error> {
let aggregation_outcome = self
.attestation_aggregator
.write()
.process_free_attestation(&self.state.read(), &free_attestation, &self.spec)?;
// return if the attestation is invalid
if !aggregation_outcome.valid {
return Ok(aggregation_outcome);
}
// valid attestation, proceed with fork-choice logic
self.fork_choice.write().add_attestation(
free_attestation.validator_index,
&free_attestation.data.beacon_block_root,
&self.spec,
)?;
Ok(aggregation_outcome)
}
/// Accept a new attestation from the network. /// Accept a new attestation from the network.
/// ///
/// If valid, the attestation is added to the `op_pool` and aggregated with another attestation /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation

View File

@ -10,8 +10,6 @@ use log::debug;
use rayon::prelude::*; use rayon::prelude::*;
use slot_clock::TestingSlotClock; use slot_clock::TestingSlotClock;
use ssz::TreeHash; use ssz::TreeHash;
use std::collections::HashSet;
use std::iter::FromIterator;
use std::sync::Arc; use std::sync::Arc;
use types::{test_utils::TestingBeaconStateBuilder, *}; use types::{test_utils::TestingBeaconStateBuilder, *};
@ -137,51 +135,64 @@ impl BeaconChainHarness {
slot slot
} }
/// Gather the `FreeAttestation`s from the valiators. pub fn gather_attesations(&mut self) -> Vec<Attestation> {
///
/// Note: validators will only produce attestations _once per slot_. So, if you call this twice
/// you'll only get attestations on the first run.
pub fn gather_free_attesations(&mut self) -> Vec<FreeAttestation> {
let present_slot = self.beacon_chain.present_slot(); let present_slot = self.beacon_chain.present_slot();
let state = self.beacon_chain.state.read();
let attesting_validators = self let mut attestations = vec![];
.beacon_chain
.state for committee in state
.read()
.get_crosslink_committees_at_slot(present_slot, &self.spec) .get_crosslink_committees_at_slot(present_slot, &self.spec)
.unwrap() .unwrap()
.iter() {
.fold(vec![], |mut acc, c| { for &validator in &committee.committee {
acc.append(&mut c.committee.clone()); let duties = state
acc .get_attestation_duties(validator, &self.spec)
}); .unwrap()
let attesting_validators: HashSet<usize> = .expect("Attesting validators by definition have duties");
HashSet::from_iter(attesting_validators.iter().cloned());
let free_attestations: Vec<FreeAttestation> = self // Obtain `AttestationData` from the beacon chain.
.validators let data = self
.par_iter_mut() .beacon_chain
.enumerate() .produce_attestation_data(duties.shard)
.filter_map(|(i, validator)| { .unwrap();
if attesting_validators.contains(&i) {
// Advance the validator slot.
validator.set_slot(present_slot);
// Prompt the validator to produce an attestation (if required). // Produce an aggregate signature with a single signature.
validator.produce_free_attestation().ok() let aggregate_signature = {
} else { let message = AttestationDataAndCustodyBit {
None data: data.clone(),
} custody_bit: false,
}) }
.collect(); .hash_tree_root();
let domain = self.spec.get_domain(
state.slot.epoch(self.spec.slots_per_epoch),
Domain::Attestation,
&state.fork,
);
let sig =
Signature::new(&message, domain, &self.validators[validator].keypair.sk);
debug!( let mut agg_sig = AggregateSignature::new();
"Gathered {} FreeAttestations for slot {}.", agg_sig.add(&sig);
free_attestations.len(),
present_slot
);
free_attestations agg_sig
};
let mut aggregation_bitfield = Bitfield::with_capacity(committee.committee.len());
let custody_bitfield = Bitfield::with_capacity(committee.committee.len());
aggregation_bitfield.set(duties.committee_index, true);
attestations.push(Attestation {
aggregation_bitfield,
data,
custody_bitfield,
aggregate_signature,
})
}
}
attestations
} }
/// Get the block from the proposer for the slot. /// Get the block from the proposer for the slot.
@ -200,7 +211,9 @@ impl BeaconChainHarness {
// Ensure the validators slot clock is accurate. // Ensure the validators slot clock is accurate.
self.validators[proposer].set_slot(present_slot); self.validators[proposer].set_slot(present_slot);
self.validators[proposer].produce_block().unwrap() let block = self.validators[proposer].produce_block().unwrap();
block
} }
/// Advances the chain with a BeaconBlock and attestations from all validators. /// Advances the chain with a BeaconBlock and attestations from all validators.
@ -219,20 +232,23 @@ impl BeaconChainHarness {
}; };
debug!("...block processed by BeaconChain."); debug!("...block processed by BeaconChain.");
debug!("Producing free attestations..."); debug!("Producing attestations...");
// Produce new attestations. // Produce new attestations.
let free_attestations = self.gather_free_attesations(); let attestations = self.gather_attesations();
debug!("Processing free attestations..."); debug!("Processing {} attestations...", attestations.len());
free_attestations.par_iter().for_each(|free_attestation| { attestations
self.beacon_chain .par_iter()
.process_free_attestation(free_attestation.clone()) .enumerate()
.unwrap(); .for_each(|(i, attestation)| {
}); self.beacon_chain
.process_attestation(attestation.clone())
.expect(&format!("Attestation {} invalid: {:?}", i, attestation));
});
debug!("Free attestations processed."); debug!("Attestations processed.");
block block
} }

View File

@ -55,7 +55,7 @@ impl<T: ClientDB, U: SlotClock, F: ForkChoice> AttesterBeaconNode for DirectBeac
_slot: Slot, _slot: Slot,
shard: u64, shard: u64,
) -> Result<Option<AttestationData>, NodeError> { ) -> Result<Option<AttestationData>, NodeError> {
match self.beacon_chain.produce_attestation(shard) { match self.beacon_chain.produce_attestation_data(shard) {
Ok(attestation_data) => Ok(Some(attestation_data)), Ok(attestation_data) => Ok(Some(attestation_data)),
Err(e) => Err(NodeError::RemoteFailure(format!("{:?}", e))), Err(e) => Err(NodeError::RemoteFailure(format!("{:?}", e))),
} }

View File

@ -1,7 +1,7 @@
use crate::*; use crate::*;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize)]
pub struct AttestationDuty { pub struct AttestationDuty {
pub slot: Slot, pub slot: Slot,
pub shard: Shard, pub shard: Shard,

View File

@ -19,7 +19,7 @@ pub fn generate_deterministic_keypairs(validator_count: usize) -> Vec<Keypair> {
.collect::<Vec<usize>>() .collect::<Vec<usize>>()
.par_iter() .par_iter()
.map(|&i| { .map(|&i| {
let secret = int_to_bytes48(i as u64 + 1); let secret = int_to_bytes48(i as u64 + 1000);
let sk = SecretKey::from_bytes(&secret).unwrap(); let sk = SecretKey::from_bytes(&secret).unwrap();
let pk = PublicKey::from_secret_key(&sk); let pk = PublicKey::from_secret_key(&sk);
Keypair { sk, pk } Keypair { sk, pk }