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::errors::{BeaconChainError as Error, BlockProductionError};
use db::{
@ -88,7 +87,6 @@ pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock, F: ForkChoice> {
pub block_store: Arc<BeaconBlockStore<T>>,
pub state_store: Arc<BeaconStateStore<T>>,
pub slot_clock: U,
pub attestation_aggregator: RwLock<AttestationAggregator>,
pub op_pool: OperationPool,
canonical_head: RwLock<CheckPoint>,
finalized_head: RwLock<CheckPoint>,
@ -131,7 +129,6 @@ where
genesis_state.clone(),
state_root,
));
let attestation_aggregator = RwLock::new(AttestationAggregator::new());
genesis_state.build_epoch_cache(RelativeEpoch::Previous, &spec)?;
genesis_state.build_epoch_cache(RelativeEpoch::Current, &spec)?;
@ -142,7 +139,6 @@ where
block_store,
state_store,
slot_clock,
attestation_aggregator,
op_pool: OperationPool::new(),
state: RwLock::new(genesis_state),
finalized_head,
@ -477,7 +473,7 @@ where
}
/// 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);
let source_epoch = self.state.read().current_justified_epoch;
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.
///
/// 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 slot_clock::TestingSlotClock;
use ssz::TreeHash;
use std::collections::HashSet;
use std::iter::FromIterator;
use std::sync::Arc;
use types::{test_utils::TestingBeaconStateBuilder, *};
@ -137,51 +135,64 @@ impl BeaconChainHarness {
slot
}
/// Gather the `FreeAttestation`s from the valiators.
///
/// 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> {
pub fn gather_attesations(&mut self) -> Vec<Attestation> {
let present_slot = self.beacon_chain.present_slot();
let state = self.beacon_chain.state.read();
let attesting_validators = self
.beacon_chain
.state
.read()
let mut attestations = vec![];
for committee in state
.get_crosslink_committees_at_slot(present_slot, &self.spec)
.unwrap()
.iter()
.fold(vec![], |mut acc, c| {
acc.append(&mut c.committee.clone());
acc
});
let attesting_validators: HashSet<usize> =
HashSet::from_iter(attesting_validators.iter().cloned());
{
for &validator in &committee.committee {
let duties = state
.get_attestation_duties(validator, &self.spec)
.unwrap()
.expect("Attesting validators by definition have duties");
let free_attestations: Vec<FreeAttestation> = self
.validators
.par_iter_mut()
.enumerate()
.filter_map(|(i, validator)| {
if attesting_validators.contains(&i) {
// Advance the validator slot.
validator.set_slot(present_slot);
// Obtain `AttestationData` from the beacon chain.
let data = self
.beacon_chain
.produce_attestation_data(duties.shard)
.unwrap();
// Prompt the validator to produce an attestation (if required).
validator.produce_free_attestation().ok()
} else {
None
}
})
.collect();
// Produce an aggregate signature with a single signature.
let aggregate_signature = {
let message = AttestationDataAndCustodyBit {
data: data.clone(),
custody_bit: false,
}
.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!(
"Gathered {} FreeAttestations for slot {}.",
free_attestations.len(),
present_slot
);
let mut agg_sig = AggregateSignature::new();
agg_sig.add(&sig);
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.
@ -200,7 +211,9 @@ impl BeaconChainHarness {
// Ensure the validators slot clock is accurate.
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.
@ -219,20 +232,23 @@ impl BeaconChainHarness {
};
debug!("...block processed by BeaconChain.");
debug!("Producing free attestations...");
debug!("Producing 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| {
self.beacon_chain
.process_free_attestation(free_attestation.clone())
.unwrap();
});
attestations
.par_iter()
.enumerate()
.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
}

View File

@ -55,7 +55,7 @@ impl<T: ClientDB, U: SlotClock, F: ForkChoice> AttesterBeaconNode for DirectBeac
_slot: Slot,
shard: u64,
) -> 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)),
Err(e) => Err(NodeError::RemoteFailure(format!("{:?}", e))),
}

View File

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

View File

@ -19,7 +19,7 @@ pub fn generate_deterministic_keypairs(validator_count: usize) -> Vec<Keypair> {
.collect::<Vec<usize>>()
.par_iter()
.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 pk = PublicKey::from_secret_key(&sk);
Keypair { sk, pk }