Skip to content

Commit 30ed62e

Browse files
eserilevrealbigsean
authored andcommitted
EIP7549 get_attestation_indices (sigp#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
1 parent 41d7465 commit 30ed62e

27 files changed

+600
-435
lines changed

beacon_node/beacon_chain/src/attestation_verification.rs

Lines changed: 97 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,7 @@ use proto_array::Block as ProtoBlock;
4747
use slog::debug;
4848
use slot_clock::SlotClock;
4949
use state_processing::{
50-
common::{
51-
attesting_indices_base,
52-
attesting_indices_electra::{self, get_committee_indices},
53-
},
50+
common::{attesting_indices_base, attesting_indices_electra},
5451
per_block_processing::errors::{AttestationValidationError, BlockOperationError},
5552
signature_sets::{
5653
indexed_attestation_signature_set_from_pubkeys,
@@ -61,8 +58,9 @@ use std::borrow::Cow;
6158
use strum::AsRefStr;
6259
use tree_hash::TreeHash;
6360
use types::{
64-
Attestation, AttestationRef, BeaconCommittee, ChainSpec, CommitteeIndex, Epoch, EthSpec,
65-
ForkName, Hash256, IndexedAttestation, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
61+
Attestation, AttestationRef, BeaconCommittee, BeaconStateError::NoCommitteeFound, ChainSpec,
62+
CommitteeIndex, Epoch, EthSpec, ForkName, Hash256, IndexedAttestation, SelectionProof,
63+
SignedAggregateAndProof, Slot, SubnetId,
6664
};
6765

6866
pub use batch::{batch_verify_aggregated_attestations, batch_verify_unaggregated_attestations};
@@ -572,37 +570,59 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
572570

573571
// Committees must be sorted by ascending index order 0..committees_per_slot
574572
let get_indexed_attestation_with_committee =
575-
|(committee, _): (BeaconCommittee, CommitteesPerSlot)| {
576-
// Note: this clones the signature which is known to be a relatively slow operation.
577-
//
578-
// Future optimizations should remove this clone.
579-
let selection_proof =
580-
SelectionProof::from(signed_aggregate.message().selection_proof().clone());
581-
582-
let committee = committees
583-
.get(index as usize)
584-
.ok_or(Error::NoCommitteeForSlotAndIndex { slot, index })?;
585-
586-
if !SelectionProof::from(selection_proof)
587-
.is_aggregator(committee.committee.len(), &chain.spec)
588-
.map_err(|e| Error::BeaconChainError(e.into()))?
589-
{
590-
return Err(Error::InvalidSelectionProof { aggregator_index });
591-
}
592-
593-
// Ensure the aggregator is a member of the committee for which it is aggregating.
594-
if !committee.committee.contains(&(aggregator_index as usize)) {
595-
return Err(Error::AggregatorNotInCommittee { aggregator_index });
573+
|(committees, _): (Vec<BeaconCommittee>, CommitteesPerSlot)| {
574+
match attestation {
575+
AttestationRef::Base(att) => {
576+
let committee = committees
577+
.iter()
578+
.filter(|&committee| committee.index == att.data.index)
579+
.at_most_one()
580+
.map_err(|_| Error::NoCommitteeForSlotAndIndex {
581+
slot: att.data.slot,
582+
index: att.data.index,
583+
})?;
584+
585+
if let Some(committee) = committee {
586+
// Note: this clones the signature which is known to be a relatively slow operation.
587+
//
588+
// Future optimizations should remove this clone.
589+
let selection_proof = SelectionProof::from(
590+
signed_aggregate.message().selection_proof().clone(),
591+
);
592+
593+
if !selection_proof
594+
.is_aggregator(committee.committee.len(), &chain.spec)
595+
.map_err(|e| Error::BeaconChainError(e.into()))?
596+
{
597+
return Err(Error::InvalidSelectionProof { aggregator_index });
598+
}
599+
600+
// Ensure the aggregator is a member of the committee for which it is aggregating.
601+
if !committee.committee.contains(&(aggregator_index as usize)) {
602+
return Err(Error::AggregatorNotInCommittee { aggregator_index });
603+
}
604+
attesting_indices_base::get_indexed_attestation(
605+
committee.committee,
606+
att,
607+
)
608+
.map_err(|e| BeaconChainError::from(e).into())
609+
} else {
610+
Err(Error::NoCommitteeForSlotAndIndex {
611+
slot: att.data.slot,
612+
index: att.data.index,
613+
})
614+
}
615+
}
616+
AttestationRef::Electra(att) => {
617+
attesting_indices_electra::get_indexed_attestation(&committees, att)
618+
.map_err(|e| BeaconChainError::from(e).into())
619+
}
596620
}
597-
598-
get_indexed_attestation(committee.committee, attestation)
599-
.map_err(|e| BeaconChainError::from(e).into())
600621
};
601622

602-
let attestation = signed_aggregate.message().aggregate();
603623
let indexed_attestation = match map_attestation_committees(
604624
chain,
605-
attestation,
625+
&attestation,
606626
get_indexed_attestation_with_committee,
607627
) {
608628
Ok(indexed_attestation) => indexed_attestation,
@@ -1310,13 +1330,49 @@ pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
13101330
chain: &BeaconChain<T>,
13111331
attestation: AttestationRef<T::EthSpec>,
13121332
) -> Result<(IndexedAttestation<T::EthSpec>, CommitteesPerSlot), Error> {
1313-
map_attestation_committee(chain, attestation, |(committee, committees_per_slot)| {
1314-
get_indexed_attestation(committee.committee, attestation)
1315-
.map(|attestation| (attestation, committees_per_slot))
1316-
.map_err(Error::Invalid)
1333+
map_attestation_committees(chain, &attestation, |(committees, committees_per_slot)| {
1334+
match attestation {
1335+
AttestationRef::Base(att) => {
1336+
let committee = committees
1337+
.iter()
1338+
.filter(|&committee| committee.index == att.data.index)
1339+
.at_most_one()
1340+
.map_err(|_| Error::NoCommitteeForSlotAndIndex {
1341+
slot: att.data.slot,
1342+
index: att.data.index,
1343+
})?;
1344+
1345+
if let Some(committee) = committee {
1346+
attesting_indices_base::get_indexed_attestation(committee.committee, att)
1347+
.map(|attestation| (attestation, committees_per_slot))
1348+
.map_err(Error::Invalid)
1349+
} else {
1350+
Err(Error::NoCommitteeForSlotAndIndex {
1351+
slot: att.data.slot,
1352+
index: att.data.index,
1353+
})
1354+
}
1355+
}
1356+
AttestationRef::Electra(att) => {
1357+
attesting_indices_electra::get_indexed_attestation(&committees, att)
1358+
.map(|attestation| (attestation, committees_per_slot))
1359+
.map_err(|e| {
1360+
if e == BlockOperationError::BeaconStateError(NoCommitteeFound) {
1361+
Error::NoCommitteeForSlotAndIndex {
1362+
slot: att.data.slot,
1363+
index: att.committee_index(),
1364+
}
1365+
} else {
1366+
Error::Invalid(e)
1367+
}
1368+
})
1369+
}
1370+
}
13171371
})
13181372
}
13191373

1374+
// TODO(electra) update comments below to reflect logic changes
1375+
// i.e. this now runs the map_fn on a list of committees for the slot of the provided attestation
13201376
/// Runs the `map_fn` with the committee and committee count per slot for the given `attestation`.
13211377
///
13221378
/// This function exists in this odd "map" pattern because efficiently obtaining the committees for
@@ -1326,11 +1382,9 @@ pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
13261382
///
13271383
/// If the committees for an `attestation`'s slot aren't found in the `shuffling_cache`, we will read a state
13281384
/// from disk and then update the `shuffling_cache`.
1329-
///
1330-
/// Committees are sorted by ascending index order 0..committees_per_slot
13311385
fn map_attestation_committees<T, F, R>(
13321386
chain: &BeaconChain<T>,
1333-
attestation: AttestationRef<T::EthSpec>,
1387+
attestation: &AttestationRef<T::EthSpec>,
13341388
map_fn: F,
13351389
) -> Result<R, Error>
13361390
where
@@ -1361,12 +1415,12 @@ where
13611415
let committees_per_slot = committee_cache.committees_per_slot();
13621416

13631417
Ok(committee_cache
1364-
.get_beacon_committee(attestation.data().slot, attestation.data().index)
1365-
.map(|committee| map_fn((committee, committees_per_slot)))
1366-
.unwrap_or_else(|| {
1418+
.get_beacon_committees_at_slot(attestation.data().slot)
1419+
.map(|committees| map_fn((committees, committees_per_slot)))
1420+
.unwrap_or_else(|_| {
13671421
Err(Error::NoCommitteeForSlotAndIndex {
13681422
slot: attestation.data().slot,
1369-
index: attestation.data().index,
1423+
index: attestation.committee_index(),
13701424
})
13711425
}))
13721426
})

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,18 +1994,36 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
19941994
};
19951995
drop(cache_timer);
19961996

1997-
// TODO(electra) implement electra variant
1998-
Ok(Attestation::Base(AttestationBase {
1999-
aggregation_bits: BitList::with_capacity(committee_len)?,
2000-
data: AttestationData {
2001-
slot: request_slot,
2002-
index: request_index,
2003-
beacon_block_root,
2004-
source: justified_checkpoint,
2005-
target,
2006-
},
2007-
signature: AggregateSignature::empty(),
2008-
}))
1997+
if self.spec.fork_name_at_slot::<T::EthSpec>(request_slot) >= ForkName::Electra {
1998+
let mut committee_bits = BitVector::default();
1999+
if committee_len > 0 {
2000+
committee_bits.set(request_index as usize, true)?;
2001+
}
2002+
Ok(Attestation::Electra(AttestationElectra {
2003+
aggregation_bits: BitList::with_capacity(committee_len)?,
2004+
data: AttestationData {
2005+
slot: request_slot,
2006+
index: 0u64,
2007+
beacon_block_root,
2008+
source: justified_checkpoint,
2009+
target,
2010+
},
2011+
committee_bits,
2012+
signature: AggregateSignature::empty(),
2013+
}))
2014+
} else {
2015+
Ok(Attestation::Base(AttestationBase {
2016+
aggregation_bits: BitList::with_capacity(committee_len)?,
2017+
data: AttestationData {
2018+
slot: request_slot,
2019+
index: request_index,
2020+
beacon_block_root,
2021+
source: justified_checkpoint,
2022+
target,
2023+
},
2024+
signature: AggregateSignature::empty(),
2025+
}))
2026+
}
20092027
}
20102028

20112029
/// Performs the same validation as `Self::verify_unaggregated_attestation_for_gossip`, but for

beacon_node/beacon_chain/src/early_attester_cache.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,18 +124,40 @@ impl<E: EthSpec> EarlyAttesterCache<E> {
124124
.get_committee_length::<E>(request_slot, request_index, spec)?;
125125

126126
// TODO(electra) make fork-agnostic
127-
let attestation = Attestation::Base(AttestationBase {
128-
aggregation_bits: BitList::with_capacity(committee_len)
129-
.map_err(BeaconStateError::from)?,
130-
data: AttestationData {
131-
slot: request_slot,
132-
index: request_index,
133-
beacon_block_root: item.beacon_block_root,
134-
source: item.source,
135-
target: item.target,
136-
},
137-
signature: AggregateSignature::empty(),
138-
});
127+
let attestation = if spec.fork_name_at_slot::<E>(request_slot) >= ForkName::Electra {
128+
let mut committee_bits = BitVector::default();
129+
if committee_len > 0 {
130+
committee_bits
131+
.set(request_index as usize, true)
132+
.map_err(BeaconStateError::from)?;
133+
}
134+
Attestation::Electra(AttestationElectra {
135+
aggregation_bits: BitList::with_capacity(committee_len)
136+
.map_err(BeaconStateError::from)?,
137+
committee_bits,
138+
data: AttestationData {
139+
slot: request_slot,
140+
index: 0u64,
141+
beacon_block_root: item.beacon_block_root,
142+
source: item.source,
143+
target: item.target,
144+
},
145+
signature: AggregateSignature::empty(),
146+
})
147+
} else {
148+
Attestation::Base(AttestationBase {
149+
aggregation_bits: BitList::with_capacity(committee_len)
150+
.map_err(BeaconStateError::from)?,
151+
data: AttestationData {
152+
slot: request_slot,
153+
index: request_index,
154+
beacon_block_root: item.beacon_block_root,
155+
source: item.source,
156+
target: item.target,
157+
},
158+
signature: AggregateSignature::empty(),
159+
})
160+
};
139161

140162
metrics::inc_counter(&metrics::BEACON_EARLY_ATTESTER_CACHE_HITS);
141163

beacon_node/beacon_chain/src/observed_aggregates.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,6 @@ pub type ObservedSyncContributions<E> = ObservedAggregates<
2121
pub type ObservedAggregateAttestations<E> =
2222
ObservedAggregates<Attestation<E>, E, BitList<<E as types::EthSpec>::MaxValidatorsPerSlot>>;
2323

24-
/// Attestation data augmented with committee index
25-
///
26-
/// This is hashed and used to key the map of observed aggregate attestations. This is important
27-
/// post-Electra where the attestation data committee index is 0 and we want to avoid accidentally
28-
/// comparing aggregation bits for *different* committees.
29-
#[derive(TreeHash)]
30-
pub struct ObservedAttestationKey {
31-
pub committee_index: u64,
32-
pub attestation_data: AttestationData,
33-
}
34-
3524
/// A trait use to associate capacity constants with the type being stored in `ObservedAggregates`.
3625
pub trait Consts {
3726
/// The default capacity of items stored per slot, in a single `SlotHashSet`.
@@ -112,29 +101,39 @@ pub trait SubsetItem {
112101
}
113102

114103
impl<'a, E: EthSpec> SubsetItem for AttestationRef<'a, E> {
115-
type Item = BitList<E::MaxValidatorsPerCommittee>;
104+
type Item = BitList<E::MaxValidatorsPerSlot>;
116105
fn is_subset(&self, other: &Self::Item) -> bool {
117106
match self {
118-
Self::Base(att) => att.aggregation_bits.is_subset(other),
119-
// TODO(electra) implement electra variant
120-
Self::Electra(_) => todo!(),
107+
Self::Base(att) => {
108+
if let Ok(extended_aggregation_bits) = att.extend_aggregation_bits() {
109+
return extended_aggregation_bits.is_subset(other);
110+
}
111+
false
112+
}
113+
Self::Electra(att) => att.aggregation_bits.is_subset(other),
121114
}
122115
}
123116

124117
fn is_superset(&self, other: &Self::Item) -> bool {
125118
match self {
126-
Self::Base(att) => other.is_subset(&att.aggregation_bits),
127-
// TODO(electra) implement electra variant
128-
Self::Electra(_) => todo!(),
119+
Self::Base(att) => {
120+
if let Ok(extended_aggregation_bits) = att.extend_aggregation_bits() {
121+
return other.is_subset(&extended_aggregation_bits);
122+
}
123+
false
124+
}
125+
Self::Electra(att) => other.is_subset(&att.aggregation_bits),
129126
}
130127
}
131128

132129
/// Returns the sync contribution aggregation bits.
133130
fn get_item(&self) -> Self::Item {
134131
match self {
135-
Self::Base(att) => att.aggregation_bits.clone(),
136-
// TODO(electra) implement electra variant
137-
Self::Electra(_) => todo!(),
132+
Self::Base(att) => {
133+
// TODO(electra) fix unwrap
134+
att.extend_aggregation_bits().unwrap()
135+
}
136+
Self::Electra(att) => att.aggregation_bits.clone(),
138137
}
139138
}
140139

0 commit comments

Comments
 (0)