mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 21:34:46 +00:00
Update VC and BN APIs for naive aggregation (#950)
* Refactor `Attestation` production * Add constant * Start refactor for aggregation * Return early when no attesting validators * Refactor into individual functions * Tidy, add comments * Add first draft of NaiveAggregationPool * Further progress on naive aggregation pool * Fix compile errors in VC * Change locking logic for naive pool * Introduce AttesationType * Add pruning, comments * Add MAX_ATTESTATIONS_PER_SLOT restriction * Add pruning based on slot * Update BN for new aggregation fns * Fix test compile errors * Fix failing rest_api test * Move SignedAggregateAndProof into own file * Update docs, fix warning * Tidy some formatting in validator API * Remove T::default_spec from signing * Fix failing rest test * Tidy * Add test, fix bug * Improve naive pool tests * Add max attestations test * Revert changes to the op_pool * Refactor timer
This commit is contained in:
@@ -8,6 +8,7 @@ use crate::events::{EventHandler, EventKind};
|
||||
use crate::fork_choice::{Error as ForkChoiceError, ForkChoice};
|
||||
use crate::head_tracker::HeadTracker;
|
||||
use crate::metrics;
|
||||
use crate::naive_aggregation_pool::{Error as NaiveAggregationError, NaiveAggregationPool};
|
||||
use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||
use crate::shuffling_cache::ShufflingCache;
|
||||
use crate::snapshot_cache::SnapshotCache;
|
||||
@@ -62,6 +63,23 @@ pub const OP_POOL_DB_KEY: [u8; 32] = [0; 32];
|
||||
pub const ETH1_CACHE_DB_KEY: [u8; 32] = [0; 32];
|
||||
pub const FORK_CHOICE_DB_KEY: [u8; 32] = [0; 32];
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationType {
|
||||
/// An attestation with a single-signature that has been published in accordance with the naive
|
||||
/// aggregation strategy.
|
||||
///
|
||||
/// These attestations may have come from a `committee_index{subnet_id}_beacon_attestation`
|
||||
/// gossip subnet or they have have come directly from a validator attached to our API.
|
||||
///
|
||||
/// If `should_store == true`, the attestation will be added to the `NaiveAggregationPool`.
|
||||
Unaggregated { should_store: bool },
|
||||
/// An attestation with one more more signatures that has passed through the aggregation phase
|
||||
/// of the naive aggregation scheme.
|
||||
///
|
||||
/// These attestations must have come from the `beacon_aggregate_and_proof` gossip subnet.
|
||||
Aggregated,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationProcessingOutcome {
|
||||
Processed,
|
||||
@@ -142,6 +160,12 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
/// Stores all operations (e.g., `Attestation`, `Deposit`, etc) that are candidates for
|
||||
/// inclusion in a block.
|
||||
pub op_pool: OperationPool<T::EthSpec>,
|
||||
/// A pool of attestations dedicated to the "naive aggregation strategy" defined in the eth2
|
||||
/// specs.
|
||||
///
|
||||
/// This pool accepts `Attestation` objects that only have one aggregation bit set and provides
|
||||
/// a method to get an aggregated `Attestation` for some `AttestationData`.
|
||||
pub naive_aggregation_pool: NaiveAggregationPool<T::EthSpec>,
|
||||
/// Provides information from the Ethereum 1 (PoW) chain.
|
||||
pub eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec, T::Store>>,
|
||||
/// Stores a "snapshot" of the chain at the time the head-of-the-chain block was received.
|
||||
@@ -676,27 +700,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce an aggregate attestation that has been collected for this slot and committee.
|
||||
// TODO: Check and optimize
|
||||
pub fn return_aggregate_attestation(
|
||||
/// Returns an aggregated `Attestation`, if any, that has a matching `attestation.data`.
|
||||
///
|
||||
/// The attestation will be obtained from `self.naive_aggregation_pool`.
|
||||
pub fn get_aggregated_attestation(
|
||||
&self,
|
||||
slot: Slot,
|
||||
index: CommitteeIndex,
|
||||
) -> Result<Attestation<T::EthSpec>, Error> {
|
||||
let epoch = |slot: Slot| slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
let head_state = &self.head()?.beacon_state;
|
||||
|
||||
let state = if epoch(slot) == epoch(head_state.slot) {
|
||||
self.head()?.beacon_state
|
||||
} else {
|
||||
// The block proposer shuffling is not affected by the state roots, so we don't need to
|
||||
// calculate them.
|
||||
self.state_at_slot(slot, StateSkipConfig::WithoutStateRoots)?
|
||||
};
|
||||
|
||||
self.op_pool
|
||||
.get_raw_aggregated_attestations(&slot, &index, &state, &self.spec)
|
||||
.map_err(Error::from)
|
||||
data: &AttestationData,
|
||||
) -> Result<Option<Attestation<T::EthSpec>>, Error> {
|
||||
self.naive_aggregation_pool.get(data).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Produce a raw unsigned `Attestation` that is valid for the given `slot` and `index`.
|
||||
@@ -824,12 +835,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn process_attestation(
|
||||
&self,
|
||||
attestation: Attestation<T::EthSpec>,
|
||||
store_raw: Option<bool>,
|
||||
attestation_type: AttestationType,
|
||||
) -> Result<AttestationProcessingOutcome, Error> {
|
||||
metrics::inc_counter(&metrics::ATTESTATION_PROCESSING_REQUESTS);
|
||||
let timer = metrics::start_timer(&metrics::ATTESTATION_PROCESSING_TIMES);
|
||||
|
||||
let outcome = self.process_attestation_internal(attestation.clone(), store_raw);
|
||||
let outcome = self.process_attestation_internal(attestation.clone(), attestation_type);
|
||||
|
||||
match &outcome {
|
||||
Ok(outcome) => match outcome {
|
||||
@@ -883,7 +894,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn process_attestation_internal(
|
||||
&self,
|
||||
attestation: Attestation<T::EthSpec>,
|
||||
store_raw: Option<bool>,
|
||||
attestation_type: AttestationType,
|
||||
) -> Result<AttestationProcessingOutcome, Error> {
|
||||
let initial_validation_timer =
|
||||
metrics::start_timer(&metrics::ATTESTATION_PROCESSING_INITIAL_VALIDATION_TIMES);
|
||||
@@ -1120,20 +1131,61 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// subnet without a validator responsible for aggregating it, we don't store it in the
|
||||
// op pool.
|
||||
if self.eth1_chain.is_some() {
|
||||
if let Some(is_raw) = store_raw {
|
||||
if is_raw {
|
||||
// This is a raw un-aggregated attestation received from a subnet with a
|
||||
// connected validator required to aggregate and publish these attestations
|
||||
self.op_pool
|
||||
.insert_raw_attestation(attestation, &fork, &self.spec)?;
|
||||
} else {
|
||||
// This an aggregate attestation received from the aggregate attestation
|
||||
// channel
|
||||
self.op_pool.insert_aggregate_attestation(
|
||||
attestation,
|
||||
&fork,
|
||||
&self.spec,
|
||||
)?;
|
||||
match attestation_type {
|
||||
AttestationType::Unaggregated { should_store } if should_store => {
|
||||
match self.naive_aggregation_pool.insert(&attestation) {
|
||||
Ok(outcome) => trace!(
|
||||
self.log,
|
||||
"Stored unaggregated attestation";
|
||||
"outcome" => format!("{:?}", outcome),
|
||||
"index" => attestation.data.index,
|
||||
"slot" => attestation.data.slot.as_u64(),
|
||||
),
|
||||
Err(NaiveAggregationError::SlotTooLow {
|
||||
slot,
|
||||
lowest_permissible_slot,
|
||||
}) => {
|
||||
trace!(
|
||||
self.log,
|
||||
"Refused to store unaggregated attestation";
|
||||
"lowest_permissible_slot" => lowest_permissible_slot.as_u64(),
|
||||
"slot" => slot.as_u64(),
|
||||
);
|
||||
}
|
||||
Err(e) => error!(
|
||||
self.log,
|
||||
"Failed to store unaggregated attestation";
|
||||
"error" => format!("{:?}", e),
|
||||
"index" => attestation.data.index,
|
||||
"slot" => attestation.data.slot.as_u64(),
|
||||
),
|
||||
}
|
||||
}
|
||||
AttestationType::Unaggregated { .. } => trace!(
|
||||
self.log,
|
||||
"Did not store unaggregated attestation";
|
||||
"index" => attestation.data.index,
|
||||
"slot" => attestation.data.slot.as_u64(),
|
||||
),
|
||||
AttestationType::Aggregated => {
|
||||
let index = attestation.data.index;
|
||||
let slot = attestation.data.slot;
|
||||
|
||||
match self
|
||||
.op_pool
|
||||
.insert_attestation(attestation, &fork, &self.spec)
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!(
|
||||
self.log,
|
||||
"Failed to add attestation to op pool";
|
||||
"error" => format!("{:?}", e),
|
||||
"index" => index,
|
||||
"slot" => slot.as_u64(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1181,7 +1233,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
if let Ok(Some(pubkey)) =
|
||||
self.validator_pubkey(aggregate_and_proof.aggregator_index as usize)
|
||||
{
|
||||
if !signed_aggregate_and_proof.is_valid(&pubkey, &state.fork) {
|
||||
if !signed_aggregate_and_proof.is_valid(&pubkey, &state.fork, &self.spec) {
|
||||
Err(AttestationDropReason::AggregatorSignatureInvalid)
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -1915,18 +1967,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn per_slot_task(&self) {
|
||||
trace!(self.log, "Running beacon chain per slot tasks");
|
||||
if let Some(slot) = self.slot_clock.now() {
|
||||
self.op_pool.prune_committee_attestations(&slot)
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the timer on every epoch.
|
||||
///
|
||||
/// Performs epoch-based pruning.
|
||||
pub fn per_epoch_task(&self) {
|
||||
trace!(self.log, "Running beacon chain per epoch tasks");
|
||||
if let Some(slot) = self.slot_clock.now() {
|
||||
let current_epoch = slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
self.op_pool.prune_attestations(¤t_epoch);
|
||||
self.naive_aggregation_pool.prune(slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user