Merge branch 'electra_attestation_changes' of https://github.com/sigp/lighthouse into block-processing-electra

This commit is contained in:
realbigsean
2024-05-08 12:48:09 -04:00
33 changed files with 844 additions and 317 deletions

View File

@@ -40,12 +40,13 @@ use crate::{
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},
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 +56,9 @@ use std::borrow::Cow;
use strum::AsRefStr;
use tree_hash::TreeHash;
use types::{
Attestation, AttestationRef, BeaconCommittee, ChainSpec, CommitteeIndex, Epoch, EthSpec,
ForkName, Hash256, IndexedAttestation, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
Attestation, AttestationRef, BeaconCommittee, BeaconStateError::NoCommitteeFound, ChainSpec,
CommitteeIndex, Epoch, EthSpec, ForkName, Hash256, IndexedAttestation, SelectionProof,
SignedAggregateAndProof, Slot, SubnetId,
};
pub use batch::{batch_verify_aggregated_attestations, batch_verify_unaggregated_attestations};
@@ -545,30 +547,58 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
};
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)| {
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 !selection_proof
.is_aggregator(committee.committee.len(), &chain.spec)
.map_err(|e| Error::BeaconChainError(e.into()))?
{
return Err(Error::InvalidSelectionProof { aggregator_index });
if let Some(committee) = committee {
// TODO(electra):
// 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(),
);
if !selection_proof
.is_aggregator(committee.committee.len(), &chain.spec)
.map_err(|e| Error::BeaconChainError(e.into()))?
{
return Err(Error::InvalidSelectionProof { aggregator_index });
}
// Ensure the aggregator is a member of the committee for which it is aggregating.
if !committee.committee.contains(&(aggregator_index as usize)) {
return Err(Error::AggregatorNotInCommittee { aggregator_index });
}
attesting_indices_base::get_indexed_attestation(
committee.committee,
att,
)
.map_err(|e| BeaconChainError::from(e).into())
} else {
Err(Error::NoCommitteeForSlotAndIndex {
slot: att.data.slot,
index: att.data.index,
})
}
}
AttestationRef::Electra(att) => {
attesting_indices_electra::get_indexed_attestation(&committees, att)
.map_err(|e| BeaconChainError::from(e).into())
}
}
// Ensure the aggregator is a member of the committee for which it is aggregating.
if !committee.committee.contains(&(aggregator_index as usize)) {
return Err(Error::AggregatorNotInCommittee { aggregator_index });
}
get_indexed_attestation(committee.committee, attestation)
.map_err(|e| BeaconChainError::from(e).into())
};
let indexed_attestation = match map_attestation_committee(
let indexed_attestation = match map_attestation_committees(
chain,
attestation,
get_indexed_attestation_with_committee,
@@ -1252,13 +1282,49 @@ pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
chain: &BeaconChain<T>,
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 e == BlockOperationError::BeaconStateError(NoCommitteeFound) {
Error::NoCommitteeForSlotAndIndex {
slot: att.data.slot,
index: att.committee_index(),
}
} else {
Error::Invalid(e)
}
})
}
}
})
}
// TODO(electra) update comments below to reflect logic changes
// i.e. this now runs the map_fn on a list of committees for the slot of the provided attestation
/// 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
@@ -1268,14 +1334,14 @@ pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
///
/// If the committee for `attestation` isn'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>(
fn map_attestation_committees<T, F, R>(
chain: &BeaconChain<T>,
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;
@@ -1301,12 +1367,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,
index: attestation.committee_index(),
})
}))
})

View File

@@ -1917,18 +1917,36 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
};
drop(cache_timer);
// TODO(electra) implement electra variant
Ok(Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_len)?,
data: AttestationData {
slot: request_slot,
index: request_index,
beacon_block_root,
source: justified_checkpoint,
target,
},
signature: AggregateSignature::empty(),
}))
if self.spec.fork_name_at_slot::<T::EthSpec>(request_slot) >= ForkName::Electra {
let mut committee_bits = BitVector::default();
if committee_len > 0 {
committee_bits.set(request_index as usize, true)?;
}
Ok(Attestation::Electra(AttestationElectra {
aggregation_bits: BitList::with_capacity(committee_len)?,
data: AttestationData {
slot: request_slot,
index: 0u64,
beacon_block_root,
source: justified_checkpoint,
target,
},
committee_bits,
signature: AggregateSignature::empty(),
}))
} else {
Ok(Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_len)?,
data: AttestationData {
slot: request_slot,
index: request_index,
beacon_block_root,
source: justified_checkpoint,
target,
},
signature: AggregateSignature::empty(),
}))
}
}
/// Performs the same validation as `Self::verify_unaggregated_attestation_for_gossip`, but for

View File

@@ -124,18 +124,40 @@ impl<E: EthSpec> EarlyAttesterCache<E> {
.get_committee_length::<E>(request_slot, request_index, spec)?;
// TODO(electra) make fork-agnostic
let attestation = Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_len)
.map_err(BeaconStateError::from)?,
data: AttestationData {
slot: request_slot,
index: request_index,
beacon_block_root: item.beacon_block_root,
source: item.source,
target: item.target,
},
signature: AggregateSignature::empty(),
});
let attestation = if spec.fork_name_at_slot::<E>(request_slot) >= ForkName::Electra {
let mut committee_bits = BitVector::default();
if committee_len > 0 {
committee_bits
.set(request_index as usize, true)
.map_err(BeaconStateError::from)?;
}
Attestation::Electra(AttestationElectra {
aggregation_bits: BitList::with_capacity(committee_len)
.map_err(BeaconStateError::from)?,
committee_bits,
data: AttestationData {
slot: request_slot,
index: 0u64,
beacon_block_root: item.beacon_block_root,
source: item.source,
target: item.target,
},
signature: AggregateSignature::empty(),
})
} else {
Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_len)
.map_err(BeaconStateError::from)?,
data: AttestationData {
slot: request_slot,
index: request_index,
beacon_block_root: item.beacon_block_root,
source: item.source,
target: item.target,
},
signature: AggregateSignature::empty(),
})
};
metrics::inc_counter(&metrics::BEACON_EARLY_ATTESTER_CACHE_HITS);

View File

@@ -17,11 +17,8 @@ pub type ObservedSyncContributions<E> = ObservedAggregates<
E,
BitVector<<E as types::EthSpec>::SyncSubcommitteeSize>,
>;
pub type ObservedAggregateAttestations<E> = ObservedAggregates<
Attestation<E>,
E,
BitList<<E as types::EthSpec>::MaxValidatorsPerCommittee>,
>;
pub type ObservedAggregateAttestations<E> =
ObservedAggregates<Attestation<E>, E, BitList<<E as types::EthSpec>::MaxValidatorsPerSlot>>;
/// A trait use to associate capacity constants with the type being stored in `ObservedAggregates`.
pub trait Consts {
@@ -103,29 +100,39 @@ pub trait SubsetItem {
}
impl<'a, E: EthSpec> SubsetItem for AttestationRef<'a, E> {
type Item = BitList<E::MaxValidatorsPerCommittee>;
type Item = BitList<E::MaxValidatorsPerSlot>;
fn is_subset(&self, other: &Self::Item) -> bool {
match self {
Self::Base(att) => att.aggregation_bits.is_subset(other),
// TODO(electra) implement electra variant
Self::Electra(_) => todo!(),
Self::Base(att) => {
if let Ok(extended_aggregation_bits) = att.extend_aggregation_bits() {
return extended_aggregation_bits.is_subset(other);
}
false
}
Self::Electra(att) => att.aggregation_bits.is_subset(other),
}
}
fn is_superset(&self, other: &Self::Item) -> bool {
match self {
Self::Base(att) => other.is_subset(&att.aggregation_bits),
// TODO(electra) implement electra variant
Self::Electra(_) => todo!(),
Self::Base(att) => {
if let Ok(extended_aggregation_bits) = att.extend_aggregation_bits() {
return other.is_subset(&extended_aggregation_bits);
}
false
}
Self::Electra(att) => other.is_subset(&att.aggregation_bits),
}
}
/// Returns the sync contribution aggregation bits.
fn get_item(&self) -> Self::Item {
match self {
Self::Base(att) => att.aggregation_bits.clone(),
// TODO(electra) implement electra variant
Self::Electra(_) => todo!(),
Self::Base(att) => {
// TODO(electra) fix unwrap
att.extend_aggregation_bits().unwrap()
}
Self::Electra(att) => att.aggregation_bits.clone(),
}
}

View File

@@ -1032,21 +1032,40 @@ where
*state.get_block_root(target_slot)?
};
// TODO(electra) make test fork-agnostic
Ok(Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_len)?,
data: AttestationData {
slot,
index,
beacon_block_root,
source: state.current_justified_checkpoint(),
target: Checkpoint {
epoch,
root: target_root,
if self.spec.fork_name_at_slot::<E>(slot) >= ForkName::Electra {
let mut committee_bits = BitVector::default();
committee_bits.set(index as usize, true)?;
Ok(Attestation::Electra(AttestationElectra {
aggregation_bits: BitList::with_capacity(committee_len)?,
committee_bits,
data: AttestationData {
slot,
index: 0u64,
beacon_block_root,
source: state.current_justified_checkpoint(),
target: Checkpoint {
epoch,
root: target_root,
},
},
},
signature: AggregateSignature::empty(),
}))
signature: AggregateSignature::empty(),
}))
} else {
Ok(Attestation::Base(AttestationBase {
aggregation_bits: BitList::with_capacity(committee_len)?,
data: AttestationData {
slot,
index,
beacon_block_root,
source: state.current_justified_checkpoint(),
target: Checkpoint {
epoch,
root: target_root,
},
},
signature: AggregateSignature::empty(),
}))
}
}
/// A list of attestations for each committee for the given slot.
@@ -1121,11 +1140,14 @@ where
)
.unwrap();
attestation
.aggregation_bits_base_mut()
.unwrap()
.set(i, true)
.unwrap();
match attestation {
Attestation::Base(ref mut att) => {
att.aggregation_bits.set(i, true).unwrap()
}
Attestation::Electra(ref mut att) => {
att.aggregation_bits.set(i, true).unwrap()
}
}
*attestation.signature_mut() = {
let domain = self.spec.get_domain(
@@ -1366,7 +1388,7 @@ where
let signed_aggregate = SignedAggregateAndProof::from_aggregate(
aggregator_index as u64,
aggregate,
aggregate.to_ref(),
None,
&self.validator_keypairs[aggregator_index].sk,
&fork,

View File

@@ -14,14 +14,17 @@ use beacon_chain::{
use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
use int_to_bytes::int_to_bytes32;
use lazy_static::lazy_static;
use ssz_types::BitVector;
use state_processing::{
per_block_processing::errors::AttestationValidationError, per_slot_processing,
};
use tree_hash::TreeHash;
use types::{
signed_aggregate_and_proof::SignedAggregateAndProofRefMut,
test_utils::generate_deterministic_keypair, Address, AggregateSignature, Attestation,
BeaconStateError, BitList, ChainSpec, Epoch, EthSpec, ForkName, Hash256, Keypair,
MainnetEthSpec, SecretKey, SelectionProof, SignedAggregateAndProof, Slot, SubnetId, Unsigned,
AttestationRef, AttestationRefMut, BeaconStateError, BitList, ChainSpec, Epoch, EthSpec,
ForkName, Hash256, Keypair, MainnetEthSpec, SecretKey, SelectionProof, SignedAggregateAndProof,
Slot, SubnetId, Unsigned,
};
pub type E = MainnetEthSpec;
@@ -198,7 +201,7 @@ fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
let signed_aggregate = SignedAggregateAndProof::from_aggregate(
aggregator_index as u64,
aggregate,
aggregate.to_ref(),
None,
&aggregator_sk,
&state.fork(),
@@ -213,12 +216,13 @@ fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
/// attestation.
fn get_non_aggregator<T: BeaconChainTypes>(
chain: &BeaconChain<T>,
aggregate: &Attestation<T::EthSpec>,
aggregate: &AttestationRef<T::EthSpec>,
) -> (usize, SecretKey) {
let head = chain.head_snapshot();
let state = &head.beacon_state;
let current_slot = chain.slot().expect("should get slot");
// TODO(electra) make fork-agnostic
let committee = state
.get_beacon_committee(current_slot, aggregate.data().index)
.expect("should get committees");
@@ -305,11 +309,15 @@ impl GossipTester {
let (mut invalid_aggregate, _, _) =
get_valid_aggregated_attestation(&harness.chain, invalid_attestation.clone());
invalid_aggregate.message.aggregator_index = invalid_aggregate
.message
.aggregator_index
.checked_sub(1)
.unwrap();
match invalid_aggregate.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregator_index = att.message.aggregator_index.checked_sub(1).unwrap();
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregator_index = att.message.aggregator_index.checked_sub(1).unwrap();
}
}
Self {
harness,
@@ -361,7 +369,10 @@ impl GossipTester {
}
pub fn non_aggregator(&self) -> (usize, SecretKey) {
get_non_aggregator(&self.harness.chain, &self.valid_aggregate.message.aggregate)
get_non_aggregator(
&self.harness.chain,
&self.valid_aggregate.message().aggregate(),
)
}
pub fn import_valid_aggregate(self) -> Self {
@@ -490,7 +501,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate from future slot",
|tester, a| a.message.aggregate.data_mut().slot = tester.slot() + 1,
|tester, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.data.slot = tester.slot() + 1
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.data.slot = tester.slot() + 1
}
},
|tester, err| {
assert!(matches!(
err,
@@ -504,9 +522,18 @@ async fn aggregated_gossip_verification() {
"aggregate from past slot",
|tester, a| {
let too_early_slot = tester.earliest_valid_attestation_slot() - 1;
a.message.aggregate.data_mut().slot = too_early_slot;
a.message.aggregate.data_mut().target.epoch =
too_early_slot.epoch(E::slots_per_epoch());
match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.data.slot = too_early_slot;
att.message.aggregate.data.target.epoch =
too_early_slot.epoch(E::slots_per_epoch());
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.data.slot = too_early_slot;
att.message.aggregate.data.target.epoch =
too_early_slot.epoch(E::slots_per_epoch());
}
}
},
|tester, err| {
let valid_early_slot = tester.earliest_valid_attestation_slot();
@@ -530,7 +557,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"attestation with invalid target epoch",
|_, a| a.message.aggregate.data_mut().target.epoch += 1,
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.data.target.epoch += 1
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.data.target.epoch += 1
}
},
|_, err| assert!(matches!(err, AttnError::InvalidTargetEpoch { .. })),
)
/*
@@ -539,7 +573,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"attestation with invalid target root",
|_, a| a.message.aggregate.data_mut().target.root = Hash256::repeat_byte(42),
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.data.target.root = Hash256::repeat_byte(42)
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.data.target.root = Hash256::repeat_byte(42)
}
},
|_, err| assert!(matches!(err, AttnError::InvalidTargetRoot { .. })),
)
/*
@@ -549,7 +590,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate with unknown head block",
|_, a| a.message.aggregate.data_mut().beacon_block_root = Hash256::repeat_byte(42),
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.data.beacon_block_root = Hash256::repeat_byte(42)
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.data.beacon_block_root = Hash256::repeat_byte(42)
}
},
|_, err| {
assert!(matches!(
err,
@@ -567,20 +615,19 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate with no participants",
|_, a| {
match &mut a.message.aggregate {
Attestation::Base(ref mut att) => {
let aggregation_bits = &mut att.aggregation_bits;
aggregation_bits.difference_inplace(&aggregation_bits.clone());
assert!(aggregation_bits.is_zero());
}
Attestation::Electra(ref mut att) => {
let aggregation_bits = &mut att.aggregation_bits;
aggregation_bits.difference_inplace(&aggregation_bits.clone());
assert!(aggregation_bits.is_zero());
}
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
let aggregation_bits = &mut att.message.aggregate.aggregation_bits;
aggregation_bits.difference_inplace(&aggregation_bits.clone());
assert!(aggregation_bits.is_zero());
att.message.aggregate.signature = AggregateSignature::infinity()
}
SignedAggregateAndProofRefMut::Electra(att) => {
let aggregation_bits = &mut att.message.aggregate.aggregation_bits;
aggregation_bits.difference_inplace(&aggregation_bits.clone());
assert!(aggregation_bits.is_zero());
att.message.aggregate.signature = AggregateSignature::infinity()
}
*a.message.aggregate.signature_mut() = AggregateSignature::infinity();
},
|_, err| assert!(matches!(err, AttnError::EmptyAggregationBitfield)),
)
@@ -591,7 +638,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate with bad signature",
|tester, a| a.signature = tester.aggregator_sk.sign(Hash256::repeat_byte(42)),
|tester, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.signature = tester.aggregator_sk.sign(Hash256::repeat_byte(42))
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.signature = tester.aggregator_sk.sign(Hash256::repeat_byte(42))
}
},
|_, err| assert!(matches!(err, AttnError::InvalidSignature)),
)
/*
@@ -608,7 +662,7 @@ async fn aggregated_gossip_verification() {
.chain
.head_snapshot()
.beacon_state
.get_beacon_committee(tester.slot(), a.message.aggregate.data().index)
.get_beacon_committee(tester.slot(), a.message().aggregate().data().index)
.expect("should get committees")
.committee
.len();
@@ -618,19 +672,38 @@ async fn aggregated_gossip_verification() {
//
// Could run for ever, but that seems _really_ improbable.
let mut i: u64 = 0;
a.message.selection_proof = loop {
i += 1;
let proof: SelectionProof = tester
.aggregator_sk
.sign(Hash256::from_slice(&int_to_bytes32(i)))
.into();
if proof
.is_aggregator(committee_len, &tester.harness.chain.spec)
.unwrap()
{
break proof.into();
match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.selection_proof = loop {
i += 1;
let proof: SelectionProof = tester
.aggregator_sk
.sign(Hash256::from_slice(&int_to_bytes32(i)))
.into();
if proof
.is_aggregator(committee_len, &tester.harness.chain.spec)
.unwrap()
{
break proof.into();
}
};
}
};
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.selection_proof = loop {
i += 1;
let proof: SelectionProof = tester
.aggregator_sk
.sign(Hash256::from_slice(&int_to_bytes32(i)))
.into();
if proof
.is_aggregator(committee_len, &tester.harness.chain.spec)
.unwrap()
{
break proof.into();
}
};
}
}
},
|_, err| assert!(matches!(err, AttnError::InvalidSignature)),
)
@@ -644,7 +717,14 @@ async fn aggregated_gossip_verification() {
|tester, a| {
let mut agg_sig = AggregateSignature::infinity();
agg_sig.add_assign(&tester.aggregator_sk.sign(Hash256::repeat_byte(42)));
*a.message.aggregate.signature_mut() = agg_sig;
match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.signature = agg_sig;
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.signature = agg_sig;
}
}
},
|_, err| assert!(matches!(err, AttnError::InvalidSignature)),
)
@@ -653,8 +733,15 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate with too-high aggregator index",
|_, a| {
a.message.aggregator_index = <E as EthSpec>::ValidatorRegistryLimit::to_u64() + 1
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregator_index =
<E as EthSpec>::ValidatorRegistryLimit::to_u64() + 1
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregator_index =
<E as EthSpec>::ValidatorRegistryLimit::to_u64() + 1
}
},
|_, err| {
assert!(matches!(
@@ -673,7 +760,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate with unknown aggregator index",
|_, a| a.message.aggregator_index = VALIDATOR_COUNT as u64,
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregator_index = VALIDATOR_COUNT as u64
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregator_index = VALIDATOR_COUNT as u64
}
},
|_, err| {
assert!(matches!(
err,
@@ -681,10 +775,15 @@ async fn aggregated_gossip_verification() {
//
// AttnError::AggregatorPubkeyUnknown(unknown_validator)
//
// However the following error is triggered first:
// However, the following error is triggered first:
AttnError::AggregatorNotInCommittee {
aggregator_index
}
} |
// unless were working with electra attestations
// in which case this error is triggered instead:
AttnError::AggregatorPubkeyUnknown(
aggregator_index
)
if aggregator_index == VALIDATOR_COUNT as u64
))
},
@@ -703,7 +802,7 @@ async fn aggregated_gossip_verification() {
let (index, sk) = tester.non_aggregator();
*a = SignedAggregateAndProof::from_aggregate(
index as u64,
tester.valid_aggregate.message.aggregate.clone(),
tester.valid_aggregate.message().aggregate().clone(),
None,
&sk,
&chain.canonical_head.cached_head().head_fork(),
@@ -739,7 +838,7 @@ async fn aggregated_gossip_verification() {
assert!(matches!(
err,
AttnError::AttestationSupersetKnown(hash)
if hash == tester.valid_aggregate.message.aggregate.data().tree_hash_root()
if hash == tester.valid_aggregate.message().aggregate().data().tree_hash_root()
))
},
)
@@ -751,7 +850,14 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate from aggregator that has already been seen",
|_, a| a.message.aggregate.data_mut().beacon_block_root = Hash256::repeat_byte(42),
|_, a| match a.to_mut() {
SignedAggregateAndProofRefMut::Base(att) => {
att.message.aggregate.data.beacon_block_root = Hash256::repeat_byte(42)
}
SignedAggregateAndProofRefMut::Electra(att) => {
att.message.aggregate.data.beacon_block_root = Hash256::repeat_byte(42)
}
},
|tester, err| {
assert!(matches!(
err,
@@ -776,13 +882,29 @@ async fn unaggregated_gossip_verification() {
.inspect_unaggregate_err(
"attestation with invalid committee index",
|tester, a, _| {
a.data_mut().index = tester
.harness
.chain
.head_snapshot()
.beacon_state
.get_committee_count_at_slot(a.data().slot)
.unwrap()
match a.to_mut() {
AttestationRefMut::Base(attn) => {
attn.data.index = tester
.harness
.chain
.head_snapshot()
.beacon_state
.get_committee_count_at_slot(attn.data.slot)
.unwrap();
}
AttestationRefMut::Electra(attn) => {
let committee_index = tester
.harness
.chain
.head_snapshot()
.beacon_state
.get_committee_count_at_slot(attn.data.slot)
.unwrap();
// overwrite the existing committee bits before setting
attn.committee_bits = BitVector::default();
attn.committee_bits.set(committee_index as usize, true).unwrap();
}
}
},
|_, err| assert!(matches!(err, AttnError::NoCommitteeForSlotAndIndex { .. })),
)
@@ -1325,8 +1447,8 @@ async fn verify_aggregate_for_gossip_doppelganger_detection() {
.verify_aggregated_attestation_for_gossip(&valid_aggregate)
.expect("should verify aggregate attestation");
let epoch = valid_aggregate.message.aggregate.data().target.epoch;
let index = valid_aggregate.message.aggregator_index as usize;
let epoch = valid_aggregate.message().aggregate().data().target.epoch;
let index = valid_aggregate.message().aggregator_index() as usize;
assert!(harness.chain.validator_seen_at_epoch(index, epoch));
// Check the correct beacon cache is populated

View File

@@ -13,7 +13,7 @@ use lazy_static::lazy_static;
use logging::test_logger;
use slasher::{Config as SlasherConfig, Slasher};
use state_processing::{
common::get_indexed_attestation,
common::{attesting_indices_base, attesting_indices_electra},
per_block_processing::{per_block_processing, BlockSignatureStrategy},
per_slot_processing, BlockProcessingError, ConsensusContext, VerifyBlockRoot,
};
@@ -1258,11 +1258,17 @@ async fn verify_block_for_gossip_doppelganger_detection() {
for att in attestations.iter() {
let epoch = att.data().target.epoch;
let committee = state
.get_beacon_committee(att.data().slot, att.data().index)
.unwrap();
let indexed_attestation =
get_indexed_attestation(committee.committee, att.to_ref()).unwrap();
let indexed_attestation = match att {
Attestation::Base(att) => {
let committee = state
.get_beacon_committee(att.data.slot, att.data.index)
.unwrap();
attesting_indices_base::get_indexed_attestation(committee.committee, att).unwrap()
}
Attestation::Electra(att) => {
attesting_indices_electra::get_indexed_attestation_from_state(&state, att).unwrap()
}
};
match indexed_attestation {
IndexedAttestation::Base(indexed_attestation) => {

View File

@@ -1207,7 +1207,7 @@ async fn attesting_to_optimistic_head() {
.chain
.naive_aggregation_pool
.write()
.insert(&attestation)
.insert(attestation.to_ref())
.unwrap();
attestation