mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-01 03:33:47 +00:00
Electra: Remaining Consensus Data Structures (#5712)
* Attestation superstruct changes for EIP 7549 (#5644)
* update
* experiment
* superstruct changes
* revert
* superstruct changes
* fix tests
* indexed attestation
* indexed attestation superstruct
* updated TODOs
* `superstruct` the `AttesterSlashing` (#5636)
* `superstruct` Attester Fork Variants
* Push a little further
* Deal with Encode / Decode of AttesterSlashing
* not so sure about this..
* Stop Encode/Decode Bounds from Propagating Out
* Tons of Changes..
* More Conversions to AttestationRef
* Add AsReference trait (#15)
* Add AsReference trait
* Fix some snafus
* Got it Compiling! :D
* Got Tests Building
* Get beacon chain tests compiling
---------
Co-authored-by: Michael Sproul <micsproul@gmail.com>
* Merge remote-tracking branch 'upstream/unstable' into electra_attestation_changes
* Make EF Tests Fork-Agnostic (#5713)
* Finish EF Test Fork Agnostic (#5714)
* Superstruct `AggregateAndProof` (#5715)
* Upgrade `superstruct` to `0.8.0`
* superstruct `AggregateAndProof`
* Merge remote-tracking branch 'sigp/unstable' into electra_attestation_changes
* cargo fmt
* Merge pull request #5726 from realbigsean/electra_attestation_changes
Merge unstable into Electra attestation changes
* EIP7549 `get_attestation_indices` (#5657)
* get attesting indices electra impl
* fmt
* get tests to pass
* fmt
* fix some beacon chain tests
* fmt
* fix slasher test
* fmt got me again
* fix more tests
* fix tests
* Some small changes (#5739)
* cargo fmt (#5740)
* Sketch op pool changes
* fix get attesting indices (#5742)
* fix get attesting indices
* better errors
* fix compile
* only get committee index once
* Ef test fixes (#5753)
* attestation related ef test fixes
* delete commented out stuff
* Fix Aggregation Pool for Electra (#5754)
* Fix Aggregation Pool for Electra
* Remove Outdated Interface
* fix ssz (#5755)
* Get `electra_op_pool` up to date (#5756)
* fix get attesting indices (#5742)
* fix get attesting indices
* better errors
* fix compile
* only get committee index once
* Ef test fixes (#5753)
* attestation related ef test fixes
* delete commented out stuff
* Fix Aggregation Pool for Electra (#5754)
* Fix Aggregation Pool for Electra
* Remove Outdated Interface
* fix ssz (#5755)
---------
Co-authored-by: realbigsean <sean@sigmaprime.io>
* Revert "Get `electra_op_pool` up to date (#5756)" (#5757)
This reverts commit ab9e58aa3d.
* Merge branch 'electra_attestation_changes' of https://github.com/sigp/lighthouse into electra_op_pool
* Compute on chain aggregate impl (#5752)
* add compute_on_chain_agg impl to op pool changes
* fmt
* get op pool tests to pass
* update the naive agg pool interface (#5760)
* Fix bugs in cross-committee aggregation
* Add comment to max cover optimisation
* Fix assert
* Merge pull request #5749 from sigp/electra_op_pool
Optimise Electra op pool aggregation
* update committee offset
* Fix Electra Fork Choice Tests (#5764)
* Subscribe to the correct subnets for electra attestations (#5782)
* subscribe to the correct att subnets for electra
* subscribe to the correct att subnets for electra
* cargo fmt
* fix slashing handling
* Merge remote-tracking branch 'upstream/unstable'
* Send unagg attestation based on fork
* Publish all aggregates
* just one more check bro plz..
* Merge pull request #5832 from ethDreamer/electra_attestation_changes_merge_unstable
Merge `unstable` into `electra_attestation_changes`
* Merge pull request #5835 from realbigsean/fix-validator-logic
Fix validator logic
* Merge pull request #5816 from realbigsean/electra-attestation-slashing-handling
Electra slashing handling
* Electra attestation changes rm decode impl (#5856)
* Remove Crappy Decode impl for Attestation
* Remove Inefficient Attestation Decode impl
* Implement Schema Upgrade / Downgrade
* Update beacon_node/beacon_chain/src/schema_change/migration_schema_v20.rs
Co-authored-by: Michael Sproul <micsproul@gmail.com>
---------
Co-authored-by: Michael Sproul <micsproul@gmail.com>
* Fix failing attestation tests and misc electra attestation cleanup (#5810)
* - get attestation related beacon chain tests to pass
- observed attestations are now keyed off of data + committee index
- rename op pool attestationref to compactattestationref
- remove unwraps in agg pool and use options instead
- cherry pick some changes from ef-tests-electra
* cargo fmt
* fix failing test
* Revert dockerfile changes
* make committee_index return option
* function args shouldnt be a ref to attestation ref
* fmt
* fix dup imports
---------
Co-authored-by: realbigsean <seananderson33@GMAIL.com>
* fix some todos (#5817)
* Merge branch 'unstable' of https://github.com/sigp/lighthouse into electra_attestation_changes
* add consolidations to merkle calc for inclusion proof
* Remove Duplicate KZG Commitment Merkle Proof Code (#5874)
* Remove Duplicate KZG Commitment Merkle Proof Code
* s/tree_lists/fields/
* Merge branch 'unstable' of https://github.com/sigp/lighthouse into electra_attestation_changes
* fix compile
* Fix slasher tests (#5906)
* Fix electra tests
* Add electra attestations to double vote tests
* Update superstruct to 0.8
* Merge remote-tracking branch 'origin/unstable' into electra_attestation_changes
* Small cleanup in slasher tests
* Clean up Electra observed aggregates (#5929)
* Use consistent key in observed_attestations
* Remove unwraps from observed aggregates
* Merge branch 'unstable' of https://github.com/sigp/lighthouse into electra_attestation_changes
* De-dup attestation constructor logic
* Remove unwraps in Attestation construction
* Dedup match_attestation_data
* Remove outdated TODO
* Use ForkName Ord in fork-choice tests
* Use ForkName Ord in BeaconBlockBody
* Make to_electra not fallible
* Remove TestRandom impl for IndexedAttestation
* Remove IndexedAttestation faulty Decode impl
* Drop TestRandom impl
* Add PendingAttestationInElectra
* Indexed att on disk (#35)
* indexed att on disk
* fix lints
* Update slasher/src/migrate.rs
Co-authored-by: ethDreamer <37123614+ethDreamer@users.noreply.github.com>
---------
Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com>
Co-authored-by: ethDreamer <37123614+ethDreamer@users.noreply.github.com>
* add electra fork enabled fn to ForkName impl (#36)
* add electra fork enabled fn to ForkName impl
* remove inadvertent file
* Update common/eth2/src/types.rs
Co-authored-by: ethDreamer <37123614+ethDreamer@users.noreply.github.com>
* Dedup attestation constructor logic in attester cache
* Use if let Ok for committee_bits
* Dedup Attestation constructor code
* Diff reduction in tests
* Fix beacon_chain tests
* Diff reduction
* Use Ord for ForkName in pubsub
* Resolve into_attestation_and_indices todo
* Remove stale TODO
* Fix beacon_chain tests
* Test spec invariant
* Use electra_enabled in pubsub
* Remove get_indexed_attestation_from_signed_aggregate
* Use ok_or instead of if let else
* committees are sorted
* remove dup method `get_indexed_attestation_from_committees`
* Merge pull request #5940 from dapplion/electra_attestation_changes_lionreview
Electra attestations #5712 review
* update default persisted op pool deserialization
* ensure aggregate and proof uses serde untagged on ref
* Fork aware ssz static attestation tests
* Electra attestation changes from Lions review (#5971)
* dedup/cleanup and remove unneeded hashset use
* remove irrelevant TODOs
* Merge branch 'unstable' of https://github.com/sigp/lighthouse into electra_attestation_changes
* Electra attestation changes sean review (#5972)
* instantiate empty bitlist in unreachable code
* clean up error conversion
* fork enabled bool cleanup
* remove a couple todos
* return bools instead of options in `aggregate` and use the result
* delete commented out code
* use map macros in simple transformations
* remove signers_disjoint_from
* get ef tests compiling
* get ef tests compiling
* update intentionally excluded files
* Avoid changing slasher schema for Electra
* Delete slasher schema v4
* Fix clippy
* Fix compilation of beacon_chain tests
* Update database.rs
* Add electra lightclient types
* Update slasher/src/database.rs
* fix imports
* Merge pull request #5980 from dapplion/electra-lightclient
Add electra lightclient types
* Merge pull request #5975 from michaelsproul/electra-slasher-no-migration
Avoid changing slasher schema for Electra
* Update beacon_node/beacon_chain/src/attestation_verification.rs
* Update beacon_node/beacon_chain/src/attestation_verification.rs
This commit is contained in:
@@ -35,17 +35,23 @@
|
||||
mod batch;
|
||||
|
||||
use crate::{
|
||||
beacon_chain::VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT, metrics,
|
||||
observed_aggregates::ObserveOutcome, observed_attesters::Error as ObservedAttestersError,
|
||||
beacon_chain::VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT,
|
||||
metrics,
|
||||
observed_aggregates::{ObserveOutcome, ObservedAttestationKey},
|
||||
observed_attesters::Error as ObservedAttestersError,
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||
};
|
||||
use bls::verify_signature_sets;
|
||||
use itertools::Itertools;
|
||||
use proto_array::Block as ProtoBlock;
|
||||
use slog::debug;
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::{
|
||||
common::get_indexed_attestation,
|
||||
per_block_processing::errors::AttestationValidationError,
|
||||
common::{
|
||||
attesting_indices_base,
|
||||
attesting_indices_electra::{self, get_committee_indices},
|
||||
},
|
||||
per_block_processing::errors::{AttestationValidationError, BlockOperationError},
|
||||
signature_sets::{
|
||||
indexed_attestation_signature_set_from_pubkeys,
|
||||
signed_aggregate_selection_proof_signature_set, signed_aggregate_signature_set,
|
||||
@@ -55,8 +61,9 @@ use std::borrow::Cow;
|
||||
use strum::AsRefStr;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
Attestation, BeaconCommittee, ChainSpec, CommitteeIndex, Epoch, EthSpec, ForkName, Hash256,
|
||||
IndexedAttestation, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
|
||||
Attestation, AttestationRef, BeaconCommittee, BeaconStateError::NoCommitteeFound, ChainSpec,
|
||||
CommitteeIndex, Epoch, EthSpec, Hash256, IndexedAttestation, SelectionProof,
|
||||
SignedAggregateAndProof, Slot, SubnetId,
|
||||
};
|
||||
|
||||
pub use batch::{batch_verify_aggregated_attestations, batch_verify_unaggregated_attestations};
|
||||
@@ -137,6 +144,12 @@ pub enum Error {
|
||||
///
|
||||
/// The peer has sent an invalid message.
|
||||
ValidatorIndexTooHigh(usize),
|
||||
/// The validator index is not set to zero after Electra.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// The peer has sent an invalid message.
|
||||
CommitteeIndexNonZero(usize),
|
||||
/// The `attestation.data.beacon_block_root` block is unknown.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
@@ -185,6 +198,12 @@ pub enum Error {
|
||||
///
|
||||
/// The peer has sent an invalid message.
|
||||
NotExactlyOneAggregationBitSet(usize),
|
||||
/// The attestation doesn't have only one aggregation bit set.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// The peer has sent an invalid message.
|
||||
NotExactlyOneCommitteeBitSet(usize),
|
||||
/// We have already observed an attestation for the `validator_index` and refuse to process
|
||||
/// another.
|
||||
///
|
||||
@@ -248,7 +267,7 @@ pub enum Error {
|
||||
|
||||
impl From<BeaconChainError> for Error {
|
||||
fn from(e: BeaconChainError) -> Self {
|
||||
Error::BeaconChainError(e)
|
||||
Self::BeaconChainError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,10 +282,11 @@ enum CheckAttestationSignature {
|
||||
/// `IndexedAttestation` can be derived.
|
||||
///
|
||||
/// These attestations have *not* undergone signature verification.
|
||||
/// The `observed_attestation_key_root` is the hashed value of an `ObservedAttestationKey`.
|
||||
struct IndexedAggregatedAttestation<'a, T: BeaconChainTypes> {
|
||||
signed_aggregate: &'a SignedAggregateAndProof<T::EthSpec>,
|
||||
indexed_attestation: IndexedAttestation<T::EthSpec>,
|
||||
attestation_data_root: Hash256,
|
||||
observed_attestation_key_root: Hash256,
|
||||
}
|
||||
|
||||
/// Wraps a `Attestation` that has been verified up until the point that an `IndexedAttestation` can
|
||||
@@ -274,7 +294,7 @@ struct IndexedAggregatedAttestation<'a, T: BeaconChainTypes> {
|
||||
///
|
||||
/// These attestations have *not* undergone signature verification.
|
||||
struct IndexedUnaggregatedAttestation<'a, T: BeaconChainTypes> {
|
||||
attestation: &'a Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<'a, T::EthSpec>,
|
||||
indexed_attestation: IndexedAttestation<T::EthSpec>,
|
||||
subnet_id: SubnetId,
|
||||
validator_index: u64,
|
||||
@@ -295,7 +315,7 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
|
||||
/// Wraps an `Attestation` that has been fully verified for propagation on the gossip network.
|
||||
pub struct VerifiedUnaggregatedAttestation<'a, T: BeaconChainTypes> {
|
||||
attestation: &'a Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<'a, T::EthSpec>,
|
||||
indexed_attestation: IndexedAttestation<T::EthSpec>,
|
||||
subnet_id: SubnetId,
|
||||
}
|
||||
@@ -322,20 +342,20 @@ impl<'a, T: BeaconChainTypes> Clone for IndexedUnaggregatedAttestation<'a, T> {
|
||||
/// A helper trait implemented on wrapper types that can be progressed to a state where they can be
|
||||
/// verified for application to fork choice.
|
||||
pub trait VerifiedAttestation<T: BeaconChainTypes>: Sized {
|
||||
fn attestation(&self) -> &Attestation<T::EthSpec>;
|
||||
fn attestation(&self) -> AttestationRef<T::EthSpec>;
|
||||
|
||||
fn indexed_attestation(&self) -> &IndexedAttestation<T::EthSpec>;
|
||||
|
||||
// Inefficient default implementation. This is overridden for gossip verified attestations.
|
||||
fn into_attestation_and_indices(self) -> (Attestation<T::EthSpec>, Vec<u64>) {
|
||||
let attestation = self.attestation().clone();
|
||||
let attesting_indices = self.indexed_attestation().attesting_indices.clone().into();
|
||||
let attestation = self.attestation().clone_as_attestation();
|
||||
let attesting_indices = self.indexed_attestation().attesting_indices_to_vec();
|
||||
(attestation, attesting_indices)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: BeaconChainTypes> VerifiedAttestation<T> for VerifiedAggregatedAttestation<'a, T> {
|
||||
fn attestation(&self) -> &Attestation<T::EthSpec> {
|
||||
fn attestation(&self) -> AttestationRef<T::EthSpec> {
|
||||
self.attestation()
|
||||
}
|
||||
|
||||
@@ -345,7 +365,7 @@ impl<'a, T: BeaconChainTypes> VerifiedAttestation<T> for VerifiedAggregatedAttes
|
||||
}
|
||||
|
||||
impl<'a, T: BeaconChainTypes> VerifiedAttestation<T> for VerifiedUnaggregatedAttestation<'a, T> {
|
||||
fn attestation(&self) -> &Attestation<T::EthSpec> {
|
||||
fn attestation(&self) -> AttestationRef<T::EthSpec> {
|
||||
self.attestation
|
||||
}
|
||||
|
||||
@@ -357,7 +377,7 @@ impl<'a, T: BeaconChainTypes> VerifiedAttestation<T> for VerifiedUnaggregatedAtt
|
||||
/// Information about invalid attestations which might still be slashable despite being invalid.
|
||||
pub enum AttestationSlashInfo<'a, T: BeaconChainTypes, TErr> {
|
||||
/// The attestation is invalid, but its signature wasn't checked.
|
||||
SignatureNotChecked(&'a Attestation<T::EthSpec>, TErr),
|
||||
SignatureNotChecked(AttestationRef<'a, T::EthSpec>, TErr),
|
||||
/// As for `SignatureNotChecked`, but we know the `IndexedAttestation`.
|
||||
SignatureNotCheckedIndexed(IndexedAttestation<T::EthSpec>, TErr),
|
||||
/// The attestation's signature is invalid, so it will never be slashable.
|
||||
@@ -382,7 +402,7 @@ fn process_slash_info<T: BeaconChainTypes>(
|
||||
let (indexed_attestation, check_signature, err) = match slash_info {
|
||||
SignatureNotChecked(attestation, err) => {
|
||||
if let Error::UnknownHeadBlock { .. } = err {
|
||||
if attestation.data.beacon_block_root == attestation.data.target.root {
|
||||
if attestation.data().beacon_block_root == attestation.data().target.root {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -451,7 +471,7 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
signed_aggregate: &SignedAggregateAndProof<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Hash256, Error> {
|
||||
let attestation = &signed_aggregate.message.aggregate;
|
||||
let attestation = signed_aggregate.message().aggregate();
|
||||
|
||||
// Ensure attestation is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (within a
|
||||
// MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance).
|
||||
@@ -460,30 +480,39 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
verify_propagation_slot_range(&chain.slot_clock, attestation, &chain.spec)?;
|
||||
|
||||
// Check the attestation's epoch matches its target.
|
||||
if attestation.data.slot.epoch(T::EthSpec::slots_per_epoch())
|
||||
!= attestation.data.target.epoch
|
||||
if attestation.data().slot.epoch(T::EthSpec::slots_per_epoch())
|
||||
!= attestation.data().target.epoch
|
||||
{
|
||||
return Err(Error::InvalidTargetEpoch {
|
||||
slot: attestation.data.slot,
|
||||
epoch: attestation.data.target.epoch,
|
||||
slot: attestation.data().slot,
|
||||
epoch: attestation.data().target.epoch,
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure the valid aggregated attestation has not already been seen locally.
|
||||
let attestation_data = &attestation.data;
|
||||
let attestation_data_root = attestation_data.tree_hash_root();
|
||||
let observed_attestation_key_root = ObservedAttestationKey {
|
||||
committee_index: attestation
|
||||
.committee_index()
|
||||
.ok_or(Error::NotExactlyOneCommitteeBitSet(0))?,
|
||||
attestation_data: attestation.data().clone(),
|
||||
}
|
||||
.tree_hash_root();
|
||||
|
||||
// [New in Electra:EIP7549]
|
||||
verify_committee_index(attestation)?;
|
||||
|
||||
if chain
|
||||
.observed_attestations
|
||||
.write()
|
||||
.is_known_subset(attestation, attestation_data_root)
|
||||
.is_known_subset(attestation, observed_attestation_key_root)
|
||||
.map_err(|e| Error::BeaconChainError(e.into()))?
|
||||
{
|
||||
metrics::inc_counter(&metrics::AGGREGATED_ATTESTATION_SUBSETS);
|
||||
return Err(Error::AttestationSupersetKnown(attestation_data_root));
|
||||
return Err(Error::AttestationSupersetKnown(
|
||||
observed_attestation_key_root,
|
||||
));
|
||||
}
|
||||
|
||||
let aggregator_index = signed_aggregate.message.aggregator_index;
|
||||
let aggregator_index = signed_aggregate.message().aggregator_index();
|
||||
|
||||
// Ensure there has been no other observed aggregate for the given `aggregator_index`.
|
||||
//
|
||||
@@ -491,7 +520,7 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
match chain
|
||||
.observed_aggregators
|
||||
.read()
|
||||
.validator_has_been_observed(attestation.data.target.epoch, aggregator_index as usize)
|
||||
.validator_has_been_observed(attestation.data().target.epoch, aggregator_index as usize)
|
||||
{
|
||||
Ok(true) => Err(Error::AggregatorAlreadyKnown(aggregator_index)),
|
||||
Ok(false) => Ok(()),
|
||||
@@ -523,10 +552,10 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
verify_attestation_target_root::<T::EthSpec>(&head_block, attestation)?;
|
||||
|
||||
// Ensure that the attestation has participants.
|
||||
if attestation.aggregation_bits.is_zero() {
|
||||
if attestation.is_aggregation_bits_zero() {
|
||||
Err(Error::EmptyAggregationBitfield)
|
||||
} else {
|
||||
Ok(attestation_data_root)
|
||||
Ok(observed_attestation_key_root)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,23 +565,47 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, AttestationSlashInfo<'a, T, Error>> {
|
||||
use AttestationSlashInfo::*;
|
||||
|
||||
let attestation = &signed_aggregate.message.aggregate;
|
||||
let aggregator_index = signed_aggregate.message.aggregator_index;
|
||||
let attestation_data_root = match Self::verify_early_checks(signed_aggregate, chain) {
|
||||
let observed_attestation_key_root = match Self::verify_early_checks(signed_aggregate, chain)
|
||||
{
|
||||
Ok(root) => root,
|
||||
Err(e) => return Err(SignatureNotChecked(&signed_aggregate.message.aggregate, e)),
|
||||
Err(e) => {
|
||||
return Err(SignatureNotChecked(
|
||||
signed_aggregate.message().aggregate(),
|
||||
e,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// Committees must be sorted by ascending index order 0..committees_per_slot
|
||||
let get_indexed_attestation_with_committee =
|
||||
|(committee, _): (BeaconCommittee, CommitteesPerSlot)| {
|
||||
// Note: this clones the signature which is known to be a relatively slow operation.
|
||||
//
|
||||
// Future optimizations should remove this clone.
|
||||
let selection_proof =
|
||||
SelectionProof::from(signed_aggregate.message.selection_proof.clone());
|
||||
|(committees, _): (Vec<BeaconCommittee>, CommitteesPerSlot)| {
|
||||
let (index, aggregator_index, selection_proof, data) = match signed_aggregate {
|
||||
SignedAggregateAndProof::Base(signed_aggregate) => (
|
||||
signed_aggregate.message.aggregate.data.index,
|
||||
signed_aggregate.message.aggregator_index,
|
||||
// Note: this clones the signature which is known to be a relatively slow operation.
|
||||
// Future optimizations should remove this clone.
|
||||
signed_aggregate.message.selection_proof.clone(),
|
||||
signed_aggregate.message.aggregate.data.clone(),
|
||||
),
|
||||
SignedAggregateAndProof::Electra(signed_aggregate) => (
|
||||
signed_aggregate
|
||||
.message
|
||||
.aggregate
|
||||
.committee_index()
|
||||
.ok_or(Error::NotExactlyOneCommitteeBitSet(0))?,
|
||||
signed_aggregate.message.aggregator_index,
|
||||
signed_aggregate.message.selection_proof.clone(),
|
||||
signed_aggregate.message.aggregate.data.clone(),
|
||||
),
|
||||
};
|
||||
let slot = data.slot;
|
||||
|
||||
if !selection_proof
|
||||
let committee = committees
|
||||
.get(index as usize)
|
||||
.ok_or(Error::NoCommitteeForSlotAndIndex { slot, index })?;
|
||||
|
||||
if !SelectionProof::from(selection_proof)
|
||||
.is_aggregator(committee.committee.len(), &chain.spec)
|
||||
.map_err(|e| Error::BeaconChainError(e.into()))?
|
||||
{
|
||||
@@ -564,23 +617,44 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
return Err(Error::AggregatorNotInCommittee { aggregator_index });
|
||||
}
|
||||
|
||||
get_indexed_attestation(committee.committee, attestation)
|
||||
.map_err(|e| BeaconChainError::from(e).into())
|
||||
// p2p aggregates have a single committee, we can assert that aggregation_bits is always
|
||||
// less then MaxValidatorsPerCommittee
|
||||
match signed_aggregate {
|
||||
SignedAggregateAndProof::Base(signed_aggregate) => {
|
||||
attesting_indices_base::get_indexed_attestation(
|
||||
committee.committee,
|
||||
&signed_aggregate.message.aggregate,
|
||||
)
|
||||
.map_err(|e| BeaconChainError::from(e).into())
|
||||
}
|
||||
SignedAggregateAndProof::Electra(signed_aggregate) => {
|
||||
attesting_indices_electra::get_indexed_attestation(
|
||||
&committees,
|
||||
&signed_aggregate.message.aggregate,
|
||||
)
|
||||
.map_err(|e| BeaconChainError::from(e).into())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let indexed_attestation = match map_attestation_committee(
|
||||
let attestation = signed_aggregate.message().aggregate();
|
||||
let indexed_attestation = match map_attestation_committees(
|
||||
chain,
|
||||
attestation,
|
||||
get_indexed_attestation_with_committee,
|
||||
) {
|
||||
Ok(indexed_attestation) => indexed_attestation,
|
||||
Err(e) => return Err(SignatureNotChecked(&signed_aggregate.message.aggregate, e)),
|
||||
Err(e) => {
|
||||
return Err(SignatureNotChecked(
|
||||
signed_aggregate.message().aggregate(),
|
||||
e,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(IndexedAggregatedAttestation {
|
||||
signed_aggregate,
|
||||
indexed_attestation,
|
||||
attestation_data_root,
|
||||
observed_attestation_key_root,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -589,11 +663,11 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
/// Run the checks that happen after the indexed attestation and signature have been checked.
|
||||
fn verify_late_checks(
|
||||
signed_aggregate: &SignedAggregateAndProof<T::EthSpec>,
|
||||
attestation_data_root: Hash256,
|
||||
observed_attestation_key_root: Hash256,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(), Error> {
|
||||
let attestation = &signed_aggregate.message.aggregate;
|
||||
let aggregator_index = signed_aggregate.message.aggregator_index;
|
||||
let attestation = signed_aggregate.message().aggregate();
|
||||
let aggregator_index = signed_aggregate.message().aggregator_index();
|
||||
|
||||
// Observe the valid attestation so we do not re-process it.
|
||||
//
|
||||
@@ -602,11 +676,13 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
if let ObserveOutcome::Subset = chain
|
||||
.observed_attestations
|
||||
.write()
|
||||
.observe_item(attestation, Some(attestation_data_root))
|
||||
.observe_item(attestation, Some(observed_attestation_key_root))
|
||||
.map_err(|e| Error::BeaconChainError(e.into()))?
|
||||
{
|
||||
metrics::inc_counter(&metrics::AGGREGATED_ATTESTATION_SUBSETS);
|
||||
return Err(Error::AttestationSupersetKnown(attestation_data_root));
|
||||
return Err(Error::AttestationSupersetKnown(
|
||||
observed_attestation_key_root,
|
||||
));
|
||||
}
|
||||
|
||||
// Observe the aggregator so we don't process another aggregate from them.
|
||||
@@ -616,12 +692,12 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
if chain
|
||||
.observed_aggregators
|
||||
.write()
|
||||
.observe_validator(attestation.data.target.epoch, aggregator_index as usize)
|
||||
.observe_validator(attestation.data().target.epoch, aggregator_index as usize)
|
||||
.map_err(BeaconChainError::from)?
|
||||
{
|
||||
return Err(Error::PriorAttestationKnown {
|
||||
validator_index: aggregator_index,
|
||||
epoch: attestation.data.target.epoch,
|
||||
epoch: attestation.data().target.epoch,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -666,7 +742,7 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
let IndexedAggregatedAttestation {
|
||||
signed_aggregate,
|
||||
indexed_attestation,
|
||||
attestation_data_root,
|
||||
observed_attestation_key_root,
|
||||
} = signed_aggregate;
|
||||
|
||||
match check_signature {
|
||||
@@ -690,7 +766,9 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
CheckAttestationSignature::No => (),
|
||||
};
|
||||
|
||||
if let Err(e) = Self::verify_late_checks(signed_aggregate, attestation_data_root, chain) {
|
||||
if let Err(e) =
|
||||
Self::verify_late_checks(signed_aggregate, observed_attestation_key_root, chain)
|
||||
{
|
||||
return Err(SignatureValid(indexed_attestation, e));
|
||||
}
|
||||
|
||||
@@ -701,8 +779,8 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
}
|
||||
|
||||
/// Returns the underlying `attestation` for the `signed_aggregate`.
|
||||
pub fn attestation(&self) -> &Attestation<T::EthSpec> {
|
||||
&self.signed_aggregate.message.aggregate
|
||||
pub fn attestation(&self) -> AttestationRef<'a, T::EthSpec> {
|
||||
self.signed_aggregate.message().aggregate()
|
||||
}
|
||||
|
||||
/// Returns the underlying `signed_aggregate`.
|
||||
@@ -714,16 +792,16 @@ impl<'a, T: BeaconChainTypes> VerifiedAggregatedAttestation<'a, T> {
|
||||
impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
/// Run the checks that happen before an indexed attestation is constructed.
|
||||
pub fn verify_early_checks(
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(), Error> {
|
||||
let attestation_epoch = attestation.data.slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
let attestation_epoch = attestation.data().slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
|
||||
// Check the attestation's epoch matches its target.
|
||||
if attestation_epoch != attestation.data.target.epoch {
|
||||
if attestation_epoch != attestation.data().target.epoch {
|
||||
return Err(Error::InvalidTargetEpoch {
|
||||
slot: attestation.data.slot,
|
||||
epoch: attestation.data.target.epoch,
|
||||
slot: attestation.data().slot,
|
||||
epoch: attestation.data().target.epoch,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -735,11 +813,14 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
|
||||
// Check to ensure that the attestation is "unaggregated". I.e., it has exactly one
|
||||
// aggregation bit set.
|
||||
let num_aggregation_bits = attestation.aggregation_bits.num_set_bits();
|
||||
let num_aggregation_bits = attestation.num_set_aggregation_bits();
|
||||
if num_aggregation_bits != 1 {
|
||||
return Err(Error::NotExactlyOneAggregationBitSet(num_aggregation_bits));
|
||||
}
|
||||
|
||||
// [New in Electra:EIP7549]
|
||||
verify_committee_index(attestation)?;
|
||||
|
||||
// Attestations must be for a known block. If the block is unknown, we simply drop the
|
||||
// attestation and do not delay consideration for later.
|
||||
//
|
||||
@@ -755,14 +836,14 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
|
||||
/// Run the checks that apply to the indexed attestation before the signature is checked.
|
||||
pub fn verify_middle_checks(
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
indexed_attestation: &IndexedAttestation<T::EthSpec>,
|
||||
committees_per_slot: u64,
|
||||
subnet_id: Option<SubnetId>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(u64, SubnetId), Error> {
|
||||
let expected_subnet_id = SubnetId::compute_subnet_for_attestation_data::<T::EthSpec>(
|
||||
&indexed_attestation.data,
|
||||
let expected_subnet_id = SubnetId::compute_subnet_for_attestation::<T::EthSpec>(
|
||||
attestation,
|
||||
committees_per_slot,
|
||||
&chain.spec,
|
||||
)
|
||||
@@ -779,8 +860,7 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
};
|
||||
|
||||
let validator_index = *indexed_attestation
|
||||
.attesting_indices
|
||||
.first()
|
||||
.attesting_indices_first()
|
||||
.ok_or(Error::NotExactlyOneAggregationBitSet(0))?;
|
||||
|
||||
/*
|
||||
@@ -790,12 +870,12 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
if chain
|
||||
.observed_gossip_attesters
|
||||
.read()
|
||||
.validator_has_been_observed(attestation.data.target.epoch, validator_index as usize)
|
||||
.validator_has_been_observed(attestation.data().target.epoch, validator_index as usize)
|
||||
.map_err(BeaconChainError::from)?
|
||||
{
|
||||
return Err(Error::PriorAttestationKnown {
|
||||
validator_index,
|
||||
epoch: attestation.data.target.epoch,
|
||||
epoch: attestation.data().target.epoch,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -812,7 +892,7 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
subnet_id: Option<SubnetId>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, Error> {
|
||||
Self::verify_slashable(attestation, subnet_id, chain)
|
||||
Self::verify_slashable(attestation.to_ref(), subnet_id, chain)
|
||||
.map(|verified_unaggregated| {
|
||||
if let Some(slasher) = chain.slasher.as_ref() {
|
||||
slasher.accept_attestation(verified_unaggregated.indexed_attestation.clone());
|
||||
@@ -824,7 +904,7 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
|
||||
/// Verify the attestation, producing extra information about whether it might be slashable.
|
||||
pub fn verify_slashable(
|
||||
attestation: &'a Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<'a, T::EthSpec>,
|
||||
subnet_id: Option<SubnetId>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, AttestationSlashInfo<'a, T, Error>> {
|
||||
@@ -873,7 +953,7 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
impl<'a, T: BeaconChainTypes> VerifiedUnaggregatedAttestation<'a, T> {
|
||||
/// Run the checks that apply after the signature has been checked.
|
||||
fn verify_late_checks(
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
validator_index: u64,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(), Error> {
|
||||
@@ -886,12 +966,12 @@ impl<'a, T: BeaconChainTypes> VerifiedUnaggregatedAttestation<'a, T> {
|
||||
if chain
|
||||
.observed_gossip_attesters
|
||||
.write()
|
||||
.observe_validator(attestation.data.target.epoch, validator_index as usize)
|
||||
.observe_validator(attestation.data().target.epoch, validator_index as usize)
|
||||
.map_err(BeaconChainError::from)?
|
||||
{
|
||||
return Err(Error::PriorAttestationKnown {
|
||||
validator_index,
|
||||
epoch: attestation.data.target.epoch,
|
||||
epoch: attestation.data().target.epoch,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
@@ -967,7 +1047,7 @@ impl<'a, T: BeaconChainTypes> VerifiedUnaggregatedAttestation<'a, T> {
|
||||
}
|
||||
|
||||
/// Returns the wrapped `attestation`.
|
||||
pub fn attestation(&self) -> &Attestation<T::EthSpec> {
|
||||
pub fn attestation(&self) -> AttestationRef<T::EthSpec> {
|
||||
self.attestation
|
||||
}
|
||||
|
||||
@@ -997,34 +1077,34 @@ impl<'a, T: BeaconChainTypes> VerifiedUnaggregatedAttestation<'a, T> {
|
||||
/// already finalized.
|
||||
fn verify_head_block_is_known<T: BeaconChainTypes>(
|
||||
chain: &BeaconChain<T>,
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
max_skip_slots: Option<u64>,
|
||||
) -> Result<ProtoBlock, Error> {
|
||||
let block_opt = chain
|
||||
.canonical_head
|
||||
.fork_choice_read_lock()
|
||||
.get_block(&attestation.data.beacon_block_root)
|
||||
.get_block(&attestation.data().beacon_block_root)
|
||||
.or_else(|| {
|
||||
chain
|
||||
.early_attester_cache
|
||||
.get_proto_block(attestation.data.beacon_block_root)
|
||||
.get_proto_block(attestation.data().beacon_block_root)
|
||||
});
|
||||
|
||||
if let Some(block) = block_opt {
|
||||
// Reject any block that exceeds our limit on skipped slots.
|
||||
if let Some(max_skip_slots) = max_skip_slots {
|
||||
if attestation.data.slot > block.slot + max_skip_slots {
|
||||
if attestation.data().slot > block.slot + max_skip_slots {
|
||||
return Err(Error::TooManySkippedSlots {
|
||||
head_block_slot: block.slot,
|
||||
attestation_slot: attestation.data.slot,
|
||||
attestation_slot: attestation.data().slot,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(block)
|
||||
} else if chain.is_pre_finalization_block(attestation.data.beacon_block_root)? {
|
||||
} else if chain.is_pre_finalization_block(attestation.data().beacon_block_root)? {
|
||||
Err(Error::HeadBlockFinalized {
|
||||
beacon_block_root: attestation.data.beacon_block_root,
|
||||
beacon_block_root: attestation.data().beacon_block_root,
|
||||
})
|
||||
} else {
|
||||
// The block is either:
|
||||
@@ -1034,7 +1114,7 @@ fn verify_head_block_is_known<T: BeaconChainTypes>(
|
||||
// 2) A post-finalization block that we don't know about yet. We'll queue
|
||||
// the attestation until the block becomes available (or we time out).
|
||||
Err(Error::UnknownHeadBlock {
|
||||
beacon_block_root: attestation.data.beacon_block_root,
|
||||
beacon_block_root: attestation.data().beacon_block_root,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1045,10 +1125,10 @@ fn verify_head_block_is_known<T: BeaconChainTypes>(
|
||||
/// Accounts for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
||||
pub fn verify_propagation_slot_range<S: SlotClock, E: EthSpec>(
|
||||
slot_clock: &S,
|
||||
attestation: &Attestation<E>,
|
||||
attestation: AttestationRef<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let attestation_slot = attestation.data.slot;
|
||||
let attestation_slot = attestation.data().slot;
|
||||
let latest_permissible_slot = slot_clock
|
||||
.now_with_future_tolerance(spec.maximum_gossip_clock_disparity())
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||
@@ -1067,14 +1147,13 @@ pub fn verify_propagation_slot_range<S: SlotClock, E: EthSpec>(
|
||||
|
||||
let current_fork =
|
||||
spec.fork_name_at_slot::<E>(slot_clock.now().ok_or(BeaconChainError::UnableToReadSlot)?);
|
||||
let earliest_permissible_slot = match current_fork {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => {
|
||||
one_epoch_prior
|
||||
}
|
||||
// EIP-7045
|
||||
ForkName::Deneb | ForkName::Electra => one_epoch_prior
|
||||
let earliest_permissible_slot = if !current_fork.deneb_enabled() {
|
||||
one_epoch_prior
|
||||
// EIP-7045
|
||||
} else {
|
||||
one_epoch_prior
|
||||
.epoch(E::slots_per_epoch())
|
||||
.start_slot(E::slots_per_epoch()),
|
||||
.start_slot(E::slots_per_epoch())
|
||||
};
|
||||
|
||||
if attestation_slot < earliest_permissible_slot {
|
||||
@@ -1102,18 +1181,17 @@ pub fn verify_attestation_signature<T: BeaconChainTypes>(
|
||||
|
||||
let fork = chain
|
||||
.spec
|
||||
.fork_at_epoch(indexed_attestation.data.target.epoch);
|
||||
.fork_at_epoch(indexed_attestation.data().target.epoch);
|
||||
|
||||
let signature_set = indexed_attestation_signature_set_from_pubkeys(
|
||||
|validator_index| pubkey_cache.get(validator_index).map(Cow::Borrowed),
|
||||
&indexed_attestation.signature,
|
||||
indexed_attestation.signature(),
|
||||
indexed_attestation,
|
||||
&fork,
|
||||
chain.genesis_validators_root,
|
||||
&chain.spec,
|
||||
)
|
||||
.map_err(BeaconChainError::SignatureSetError)?;
|
||||
|
||||
metrics::stop_timer(signature_setup_timer);
|
||||
|
||||
let _signature_verification_timer =
|
||||
@@ -1130,11 +1208,11 @@ pub fn verify_attestation_signature<T: BeaconChainTypes>(
|
||||
/// `attestation.data.beacon_block_root`.
|
||||
pub fn verify_attestation_target_root<E: EthSpec>(
|
||||
head_block: &ProtoBlock,
|
||||
attestation: &Attestation<E>,
|
||||
attestation: AttestationRef<E>,
|
||||
) -> Result<(), Error> {
|
||||
// Check the attestation target root.
|
||||
let head_block_epoch = head_block.slot.epoch(E::slots_per_epoch());
|
||||
let attestation_epoch = attestation.data.slot.epoch(E::slots_per_epoch());
|
||||
let attestation_epoch = attestation.data().slot.epoch(E::slots_per_epoch());
|
||||
if head_block_epoch > attestation_epoch {
|
||||
// The epoch references an invalid head block from a future epoch.
|
||||
//
|
||||
@@ -1147,7 +1225,7 @@ pub fn verify_attestation_target_root<E: EthSpec>(
|
||||
// Reference:
|
||||
// https://github.com/ethereum/eth2.0-specs/pull/2001#issuecomment-699246659
|
||||
return Err(Error::InvalidTargetRoot {
|
||||
attestation: attestation.data.target.root,
|
||||
attestation: attestation.data().target.root,
|
||||
// It is not clear what root we should expect in this case, since the attestation is
|
||||
// fundamentally invalid.
|
||||
expected: None,
|
||||
@@ -1166,9 +1244,9 @@ pub fn verify_attestation_target_root<E: EthSpec>(
|
||||
};
|
||||
|
||||
// Reject any attestation with an invalid target root.
|
||||
if target_root != attestation.data.target.root {
|
||||
if target_root != attestation.data().target.root {
|
||||
return Err(Error::InvalidTargetRoot {
|
||||
attestation: attestation.data.target.root,
|
||||
attestation: attestation.data().target.root,
|
||||
expected: Some(target_root),
|
||||
});
|
||||
}
|
||||
@@ -1199,14 +1277,14 @@ pub fn verify_signed_aggregate_signatures<T: BeaconChainTypes>(
|
||||
.try_read_for(VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT)
|
||||
.ok_or(BeaconChainError::ValidatorPubkeyCacheLockTimeout)?;
|
||||
|
||||
let aggregator_index = signed_aggregate.message.aggregator_index;
|
||||
let aggregator_index = signed_aggregate.message().aggregator_index();
|
||||
if aggregator_index >= pubkey_cache.len() as u64 {
|
||||
return Err(Error::AggregatorPubkeyUnknown(aggregator_index));
|
||||
}
|
||||
|
||||
let fork = chain
|
||||
.spec
|
||||
.fork_at_epoch(indexed_attestation.data.target.epoch);
|
||||
.fork_at_epoch(indexed_attestation.data().target.epoch);
|
||||
|
||||
let signature_sets = vec![
|
||||
signed_aggregate_selection_proof_signature_set(
|
||||
@@ -1227,7 +1305,7 @@ pub fn verify_signed_aggregate_signatures<T: BeaconChainTypes>(
|
||||
.map_err(BeaconChainError::SignatureSetError)?,
|
||||
indexed_attestation_signature_set_from_pubkeys(
|
||||
|validator_index| pubkey_cache.get(validator_index).map(Cow::Borrowed),
|
||||
&indexed_attestation.signature,
|
||||
indexed_attestation.signature(),
|
||||
indexed_attestation,
|
||||
&fork,
|
||||
chain.genesis_validators_root,
|
||||
@@ -1239,6 +1317,28 @@ pub fn verify_signed_aggregate_signatures<T: BeaconChainTypes>(
|
||||
Ok(verify_signature_sets(signature_sets.iter()))
|
||||
}
|
||||
|
||||
/// Verify that the `attestation` committee index is properly set for the attestation's fork.
|
||||
/// This function will only apply verification post-Electra.
|
||||
pub fn verify_committee_index<E: EthSpec>(attestation: AttestationRef<E>) -> Result<(), Error> {
|
||||
if let Ok(committee_bits) = attestation.committee_bits() {
|
||||
// Check to ensure that the attestation is for a single committee.
|
||||
let num_committee_bits = get_committee_indices::<E>(committee_bits);
|
||||
if num_committee_bits.len() != 1 {
|
||||
return Err(Error::NotExactlyOneCommitteeBitSet(
|
||||
num_committee_bits.len(),
|
||||
));
|
||||
}
|
||||
|
||||
// Ensure the attestation index is set to zero post Electra.
|
||||
if attestation.data().index != 0 {
|
||||
return Err(Error::CommitteeIndexNonZero(
|
||||
attestation.data().index as usize,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Assists in readability.
|
||||
type CommitteesPerSlot = u64;
|
||||
|
||||
@@ -1246,35 +1346,71 @@ type CommitteesPerSlot = u64;
|
||||
/// public keys cached in the `chain`.
|
||||
pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
|
||||
chain: &BeaconChain<T>,
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
) -> Result<(IndexedAttestation<T::EthSpec>, CommitteesPerSlot), Error> {
|
||||
map_attestation_committee(chain, attestation, |(committee, committees_per_slot)| {
|
||||
get_indexed_attestation(committee.committee, attestation)
|
||||
.map(|attestation| (attestation, committees_per_slot))
|
||||
.map_err(Error::Invalid)
|
||||
map_attestation_committees(chain, attestation, |(committees, committees_per_slot)| {
|
||||
match attestation {
|
||||
AttestationRef::Base(att) => {
|
||||
let committee = committees
|
||||
.iter()
|
||||
.filter(|&committee| committee.index == att.data.index)
|
||||
.at_most_one()
|
||||
.map_err(|_| Error::NoCommitteeForSlotAndIndex {
|
||||
slot: att.data.slot,
|
||||
index: att.data.index,
|
||||
})?;
|
||||
|
||||
if let Some(committee) = committee {
|
||||
attesting_indices_base::get_indexed_attestation(committee.committee, att)
|
||||
.map(|attestation| (attestation, committees_per_slot))
|
||||
.map_err(Error::Invalid)
|
||||
} else {
|
||||
Err(Error::NoCommitteeForSlotAndIndex {
|
||||
slot: att.data.slot,
|
||||
index: att.data.index,
|
||||
})
|
||||
}
|
||||
}
|
||||
AttestationRef::Electra(att) => {
|
||||
attesting_indices_electra::get_indexed_attestation(&committees, att)
|
||||
.map(|attestation| (attestation, committees_per_slot))
|
||||
.map_err(|e| {
|
||||
if let BlockOperationError::BeaconStateError(NoCommitteeFound(index)) = e {
|
||||
Error::NoCommitteeForSlotAndIndex {
|
||||
slot: att.data.slot,
|
||||
index,
|
||||
}
|
||||
} else {
|
||||
Error::Invalid(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Runs the `map_fn` with the committee and committee count per slot for the given `attestation`.
|
||||
///
|
||||
/// This function exists in this odd "map" pattern because efficiently obtaining the committee for
|
||||
/// an attestation can be complex. It might involve reading straight from the
|
||||
/// This function exists in this odd "map" pattern because efficiently obtaining the committees for
|
||||
/// an attestation's slot can be complex. It might involve reading straight from the
|
||||
/// `beacon_chain.shuffling_cache` or it might involve reading it from a state from the DB. Due to
|
||||
/// the complexities of `RwLock`s on the shuffling cache, a simple `Cow` isn't suitable here.
|
||||
///
|
||||
/// If the committee for `attestation` isn't found in the `shuffling_cache`, we will read a state
|
||||
/// If the committees for an `attestation`'s slot aren't found in the `shuffling_cache`, we will read a state
|
||||
/// from disk and then update the `shuffling_cache`.
|
||||
fn map_attestation_committee<T, F, R>(
|
||||
///
|
||||
/// Committees are sorted by ascending index order 0..committees_per_slot
|
||||
fn map_attestation_committees<T, F, R>(
|
||||
chain: &BeaconChain<T>,
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
map_fn: F,
|
||||
) -> Result<R, Error>
|
||||
where
|
||||
T: BeaconChainTypes,
|
||||
F: Fn((BeaconCommittee, CommitteesPerSlot)) -> Result<R, Error>,
|
||||
F: Fn((Vec<BeaconCommittee>, CommitteesPerSlot)) -> Result<R, Error>,
|
||||
{
|
||||
let attestation_epoch = attestation.data.slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
let target = &attestation.data.target;
|
||||
let attestation_epoch = attestation.data().slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
let target = &attestation.data().target;
|
||||
|
||||
// Attestation target must be for a known block.
|
||||
//
|
||||
@@ -1297,12 +1433,12 @@ where
|
||||
let committees_per_slot = committee_cache.committees_per_slot();
|
||||
|
||||
Ok(committee_cache
|
||||
.get_beacon_committee(attestation.data.slot, attestation.data.index)
|
||||
.map(|committee| map_fn((committee, committees_per_slot)))
|
||||
.unwrap_or_else(|| {
|
||||
.get_beacon_committees_at_slot(attestation.data().slot)
|
||||
.map(|committees| map_fn((committees, committees_per_slot)))
|
||||
.unwrap_or_else(|_| {
|
||||
Err(Error::NoCommitteeForSlotAndIndex {
|
||||
slot: attestation.data.slot,
|
||||
index: attestation.data.index,
|
||||
slot: attestation.data().slot,
|
||||
index: attestation.committee_index().unwrap_or(0),
|
||||
})
|
||||
}))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user