Improve crosslink reward processing

This commit is contained in:
Paul Hauner 2019-03-14 12:17:43 +11:00
parent 6f919e6f7d
commit a319144835
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
3 changed files with 60 additions and 92 deletions

View File

@ -227,11 +227,11 @@ fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSp
&format!("{}/epoch_processing", desc),
Benchmark::new("process_rewards_and_penalties", move |b| {
b.iter_batched(
|| state_clone.clone(),
|mut state| {
|| (state_clone.clone(), attesters.clone()),
|(mut state, mut attesters)| {
process_rewards_and_penalities(
&mut state,
&attesters,
&mut attesters,
previous_total_balance,
&winning_root_for_shards,
&spec_clone,

View File

@ -1,11 +1,9 @@
use attesters::Attesters;
use errors::EpochProcessingError as Error;
use fnv::FnvHashSet;
use integer_sqrt::IntegerSquareRoot;
use rayon::prelude::*;
use ssz::TreeHash;
use std::collections::HashMap;
use std::iter::FromIterator;
use types::{validator_registry::get_active_validator_indices, *};
use winning_root::{winning_root, WinningRoot};
@ -44,7 +42,7 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result
spec,
);
let attesters = calculate_attester_sets(&state, &active_validator_indices, spec)?;
let mut attesters = calculate_attester_sets(&state, &active_validator_indices, spec)?;
process_eth1_data(state, spec);
@ -63,7 +61,7 @@ pub fn per_epoch_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result
// Rewards and Penalities
process_rewards_and_penalities(
state,
&attesters,
&mut attesters,
previous_total_balance,
&winning_root_for_shards,
spec,
@ -286,21 +284,13 @@ pub fn process_crosslinks(
/// Spec v0.4.0
pub fn process_rewards_and_penalities(
state: &mut BeaconState,
attesters: &Attesters,
attesters: &mut Attesters,
previous_total_balance: u64,
winning_root_for_shards: &WinningRootHashSet,
spec: &ChainSpec,
) -> Result<(), Error> {
let next_epoch = state.next_epoch(spec);
/*
let previous_epoch_attestations: Vec<&PendingAttestation> = state
.latest_attestations
.par_iter()
.filter(|a| a.data.slot.epoch(spec.slots_per_epoch) == state.previous_epoch(spec))
.collect();
*/
let base_reward_quotient = previous_total_balance.integer_sqrt() / spec.base_reward_quotient;
if base_reward_quotient == 0 {
@ -310,29 +300,7 @@ pub fn process_rewards_and_penalities(
return Err(Error::PreviousTotalBalanceIsZero);
}
/*
// Map is ValidatorIndex -> ProposerIndex
let mut inclusion_slots: FnvHashMap<usize, (Slot, usize)> = FnvHashMap::default();
for a in &previous_epoch_attestations {
let participants =
state.get_attestation_participants(&a.data, &a.aggregation_bitfield, spec)?;
let inclusion_distance = (a.inclusion_slot - a.data.slot).as_u64();
for participant in participants {
if let Some((existing_distance, _)) = inclusion_slots.get(&participant) {
if *existing_distance <= inclusion_distance {
continue;
}
}
let proposer_index = state
.get_beacon_proposer_index(a.data.slot, spec)
.map_err(|_| Error::UnableToDetermineProducer)?;
inclusion_slots.insert(
participant,
(Slot::from(inclusion_distance), proposer_index),
);
}
}
*/
attesters.process_winning_roots(state, winning_root_for_shards, spec)?;
// Justification and finalization
@ -345,10 +313,9 @@ pub fn process_rewards_and_penalities(
.map(|(index, &balance)| {
let mut balance = balance;
let status = &attesters.statuses[index];
if epochs_since_finality <= 4 {
let base_reward = state.base_reward(index, base_reward_quotient, spec);
if epochs_since_finality <= 4 {
// Expected FFG source
if status.is_previous_epoch {
safe_add_assign!(
@ -406,6 +373,17 @@ pub fn process_rewards_and_penalities(
}
}
// Crosslinks
if let Some(ref info) = status.winning_root_info {
safe_add_assign!(
balance,
base_reward * info.total_attesting_balance / info.total_committee_balance
);
} else {
safe_sub_assign!(balance, base_reward);
}
balance
})
.collect();
@ -431,57 +409,6 @@ pub fn process_rewards_and_penalities(
}
}
//Crosslinks
for slot in state.previous_epoch(spec).slot_iter(spec.slots_per_epoch) {
// Clone removes the borrow which becomes an issue when mutating `state.balances`.
let crosslink_committees_at_slot =
state.get_crosslink_committees_at_slot(slot, spec)?.clone();
for (crosslink_committee, shard) in crosslink_committees_at_slot {
let shard = shard as u64;
// Note: I'm a little uncertain of the logic here -- I am waiting for spec v0.5.0 to
// clear it up.
//
// What happens here is:
//
// - If there was some crosslink root elected by the super-majority of this committee,
// then we reward all who voted for that root and penalize all that did not.
// - However, if there _was not_ some super-majority-voted crosslink root, then penalize
// all the validators.
//
// I'm not quite sure that the second case (no super-majority crosslink) is correct.
if let Some(winning_root) = winning_root_for_shards.get(&shard) {
// Hash set de-dedups and (hopefully) offers a speed improvement from faster
// lookups.
let attesting_validator_indices: FnvHashSet<usize> =
FnvHashSet::from_iter(winning_root.attesting_validator_indices.iter().cloned());
for &index in &crosslink_committee {
let base_reward = state.base_reward(index, base_reward_quotient, spec);
let total_balance = state.get_total_balance(&crosslink_committee, spec);
if attesting_validator_indices.contains(&index) {
safe_add_assign!(
state.validator_balances[index],
base_reward * winning_root.total_attesting_balance / total_balance
);
} else {
safe_sub_assign!(state.validator_balances[index], base_reward);
}
}
} else {
for &index in &crosslink_committee {
let base_reward = state.base_reward(index, base_reward_quotient, spec);
safe_sub_assign!(state.validator_balances[index], base_reward);
}
}
}
}
Ok(())
}

View File

@ -1,3 +1,4 @@
use super::WinningRootHashSet;
use types::*;
macro_rules! set_self_if_other_is_true {
@ -6,6 +7,12 @@ macro_rules! set_self_if_other_is_true {
};
}
#[derive(Default, Clone)]
pub struct WinningRootInfo {
pub total_committee_balance: u64,
pub total_attesting_balance: u64,
}
#[derive(Clone)]
pub struct InclusionInfo {
pub slot: Slot,
@ -44,6 +51,7 @@ pub struct AttesterStatus {
pub is_previous_epoch_head: bool,
pub inclusion_info: InclusionInfo,
pub winning_root_info: Option<WinningRootInfo>,
}
impl AttesterStatus {
@ -70,6 +78,7 @@ pub struct TotalBalances {
pub previous_epoch_head: u64,
}
#[derive(Clone)]
pub struct Attesters {
pub statuses: Vec<AttesterStatus>,
pub balances: TotalBalances,
@ -147,6 +156,38 @@ impl Attesters {
Ok(())
}
pub fn process_winning_roots(
&mut self,
state: &BeaconState,
winning_roots: &WinningRootHashSet,
spec: &ChainSpec,
) -> Result<(), BeaconStateError> {
// Loop through each slot in the previous epoch.
for slot in state.previous_epoch(spec).slot_iter(spec.slots_per_epoch) {
let crosslink_committees_at_slot =
state.get_crosslink_committees_at_slot(slot, spec)?;
// Loop through each committee in the slot.
for (crosslink_committee, shard) in crosslink_committees_at_slot {
// If there was some winning crosslink root for the committee's shard.
if let Some(winning_root) = winning_roots.get(&shard) {
let total_committee_balance =
state.get_total_balance(&crosslink_committee, spec);
for &validator_index in &winning_root.attesting_validator_indices {
// Take note of the balance information for the winning root, it will be
// used later to calculate rewards for that validator.
self.statuses[validator_index].winning_root_info = Some(WinningRootInfo {
total_committee_balance,
total_attesting_balance: winning_root.total_attesting_balance,
})
}
}
}
}
Ok(())
}
}
fn inclusion_distance(a: &PendingAttestation) -> Slot {