mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 04:37:13 +00:00
Merge branch 'electra_attestation_changes' of https://github.com/sigp/lighthouse into block-processing-electra
This commit is contained in:
@@ -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(),
|
||||
})
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -1207,7 +1207,7 @@ async fn attesting_to_optimistic_head() {
|
||||
.chain
|
||||
.naive_aggregation_pool
|
||||
.write()
|
||||
.insert(&attestation)
|
||||
.insert(attestation.to_ref())
|
||||
.unwrap();
|
||||
|
||||
attestation
|
||||
|
||||
Reference in New Issue
Block a user