Add AttesterCache for attestation production (#2478)

## Issue Addressed

- Resolves #2169

## Proposed Changes

Adds the `AttesterCache` to allow validators to produce attestations for older slots. Presently, some arbitrary restrictions can force validators to receive an error when attesting to a slot earlier than the present one. This can cause attestation misses when there is excessive load on the validator client or time sync issues between the VC and BN.

## Additional Info

NA
This commit is contained in:
Paul Hauner
2021-07-29 04:38:26 +00:00
parent 1d4f90e2eb
commit 8efd9fc324
9 changed files with 663 additions and 58 deletions

View File

@@ -21,7 +21,10 @@ use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
pub use self::committee_cache::CommitteeCache;
pub use self::committee_cache::{
compute_committee_index_in_epoch, compute_committee_range_in_epoch, epoch_committee_count,
CommitteeCache,
};
pub use clone_config::CloneConfig;
pub use eth_spec::*;
pub use iter::BlockRootsIter;
@@ -1310,10 +1313,22 @@ impl<T: EthSpec> BeaconState<T> {
let epoch = relative_epoch.into_epoch(self.current_epoch());
let i = Self::committee_cache_index(relative_epoch);
*self.committee_cache_at_index_mut(i)? = CommitteeCache::initialized(&self, epoch, spec)?;
*self.committee_cache_at_index_mut(i)? = self.initialize_committee_cache(epoch, spec)?;
Ok(())
}
/// Initializes a new committee cache for the given `epoch`, regardless of whether one already
/// exists. Returns the committee cache without attaching it to `self`.
///
/// To build a cache and store it on `self`, use `Self::build_committee_cache`.
pub fn initialize_committee_cache(
&self,
epoch: Epoch,
spec: &ChainSpec,
) -> Result<CommitteeCache, Error> {
CommitteeCache::initialized(&self, epoch, spec)
}
/// Advances the cache for this state into the next epoch.
///
/// This should be used if the `slot` of this state is advanced beyond an epoch boundary.

View File

@@ -121,8 +121,12 @@ impl CommitteeCache {
return None;
}
let committee_index =
(slot.as_u64() % self.slots_per_epoch) * self.committees_per_slot + index;
let committee_index = compute_committee_index_in_epoch(
slot,
self.slots_per_epoch as usize,
self.committees_per_slot as usize,
index as usize,
);
let committee = self.compute_committee(committee_index as usize)?;
Some(BeaconCommittee {
@@ -219,7 +223,10 @@ impl CommitteeCache {
///
/// Spec v0.12.1
pub fn epoch_committee_count(&self) -> usize {
self.committees_per_slot as usize * self.slots_per_epoch as usize
epoch_committee_count(
self.committees_per_slot as usize,
self.slots_per_epoch as usize,
)
}
/// Returns the number of committees per slot for this cache's epoch.
@@ -242,16 +249,7 @@ impl CommitteeCache {
///
/// Spec v0.12.1
fn compute_committee_range(&self, index: usize) -> Option<Range<usize>> {
let count = self.epoch_committee_count();
if count == 0 || index >= count {
return None;
}
let num_validators = self.shuffling.len();
let start = (num_validators * index) / count;
let end = (num_validators * (index + 1)) / count;
Some(start..end)
compute_committee_range_in_epoch(self.epoch_committee_count(), index, self.shuffling.len())
}
/// Returns the index of some validator in `self.shuffling`.
@@ -264,6 +262,44 @@ impl CommitteeCache {
}
}
/// Computes the position of the given `committee_index` with respect to all committees in the
/// epoch.
///
/// The return result may be used to provide input to the `compute_committee_range_in_epoch`
/// function.
pub fn compute_committee_index_in_epoch(
slot: Slot,
slots_per_epoch: usize,
committees_per_slot: usize,
committee_index: usize,
) -> usize {
(slot.as_usize() % slots_per_epoch) * committees_per_slot + committee_index
}
/// Computes the range for slicing the shuffled indices to determine the members of a committee.
///
/// The `index_in_epoch` parameter can be computed computed using
/// `compute_committee_index_in_epoch`.
pub fn compute_committee_range_in_epoch(
epoch_committee_count: usize,
index_in_epoch: usize,
shuffling_len: usize,
) -> Option<Range<usize>> {
if epoch_committee_count == 0 || index_in_epoch >= epoch_committee_count {
return None;
}
let start = (shuffling_len * index_in_epoch) / epoch_committee_count;
let end = (shuffling_len * (index_in_epoch + 1)) / epoch_committee_count;
Some(start..end)
}
/// Returns the total number of committees in an epoch.
pub fn epoch_committee_count(committees_per_slot: usize, slots_per_epoch: usize) -> usize {
committees_per_slot * slots_per_epoch
}
/// Returns a list of all `validators` indices where the validator is active at the given
/// `epoch`.
///