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>
This commit is contained in:
ethDreamer
2024-05-02 18:00:21 -05:00
committed by GitHub
parent 3b7132bc0d
commit e6c7f145dd
53 changed files with 1405 additions and 437 deletions

View File

@@ -471,7 +471,7 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
if chain
.observed_attestations
.write()
.is_known_subset(attestation, attestation_data_root)
.is_known_subset(attestation.to_ref(), attestation_data_root)
.map_err(|e| Error::BeaconChainError(e.into()))?
{
metrics::inc_counter(&metrics::AGGREGATED_ATTESTATION_SUBSETS);
@@ -559,7 +559,7 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
return Err(Error::AggregatorNotInCommittee { aggregator_index });
}
get_indexed_attestation(committee.committee, attestation)
get_indexed_attestation(committee.committee, attestation.to_ref())
.map_err(|e| BeaconChainError::from(e).into())
};
@@ -597,7 +597,7 @@ 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.to_ref(), Some(attestation_data_root))
.map_err(|e| Error::BeaconChainError(e.into()))?
{
metrics::inc_counter(&metrics::AGGREGATED_ATTESTATION_SUBSETS);
@@ -1241,7 +1241,7 @@ pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
attestation: &Attestation<T::EthSpec>,
) -> Result<(IndexedAttestation<T::EthSpec>, CommitteesPerSlot), Error> {
map_attestation_committee(chain, attestation, |(committee, committees_per_slot)| {
get_indexed_attestation(committee.committee, attestation)
get_indexed_attestation(committee.committee, attestation.to_ref())
.map(|attestation| (attestation, committees_per_slot))
.map_err(Error::Invalid)
})

View File

@@ -2113,7 +2113,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.fork_choice_write_lock()
.on_attestation(
self.slot()?,
verified.indexed_attestation(),
verified.indexed_attestation().to_ref(),
AttestationFromBlock::False,
)
.map_err(Into::into)
@@ -2465,7 +2465,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Add to fork choice.
self.canonical_head
.fork_choice_write_lock()
.on_attester_slashing(attester_slashing.as_inner());
.on_attester_slashing(attester_slashing.as_inner().to_ref());
// Add to the op pool (if we have the ability to propose blocks).
if self.eth1_chain.is_some() {
@@ -3820,7 +3820,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
continue;
}
};
slasher.accept_attestation(indexed_attestation.clone());
slasher.accept_attestation(indexed_attestation.clone_as_indexed_attestation());
}
}
}
@@ -3839,7 +3839,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
if block.slot() + 2 * T::EthSpec::slots_per_epoch() >= current_slot {
metrics::observe(
&metrics::OPERATIONS_PER_BLOCK_ATTESTATION,
block.body().attestations().len() as f64,
block.body().attestations().count() as f64,
);
if let Ok(sync_aggregate) = block.body().sync_aggregate() {
@@ -4859,7 +4859,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
metrics::start_timer(&metrics::BLOCK_PRODUCTION_UNAGGREGATED_TIMES);
for attestation in self.naive_aggregation_pool.read().iter() {
let import = |attestation: &Attestation<T::EthSpec>| {
let attesting_indices = get_attesting_indices_from_state(&state, attestation)?;
let attesting_indices =
get_attesting_indices_from_state(&state, attestation.to_ref())?;
self.op_pool
.insert_attestation(attestation.clone(), attesting_indices)
};
@@ -4909,7 +4910,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
attestations.retain(|att| {
verify_attestation_for_block_inclusion(
&state,
att,
att.to_ref(),
&mut tmp_ctxt,
VerifySignatures::True,
&self.spec,
@@ -5040,6 +5041,72 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
bls_to_execution_changes,
} = partial_beacon_block;
let (attester_slashings_base, attester_slashings_electra) =
attester_slashings.into_iter().fold(
(Vec::new(), Vec::new()),
|(mut base, mut electra), slashing| {
match slashing {
AttesterSlashing::Base(slashing) => base.push(slashing),
AttesterSlashing::Electra(slashing) => electra.push(slashing),
}
(base, electra)
},
);
let (attestations_base, attestations_electra) = attestations.into_iter().fold(
(Vec::new(), Vec::new()),
|(mut base, mut electra), attestation| {
match attestation {
Attestation::Base(attestation) => base.push(attestation),
Attestation::Electra(attestation) => electra.push(attestation),
}
(base, electra)
},
);
// TODO(electra): figure out what should *actually* be done here when we have attestations / attester_slashings of the wrong type
match &state {
BeaconState::Base(_)
| BeaconState::Altair(_)
| BeaconState::Merge(_)
| BeaconState::Capella(_)
| BeaconState::Deneb(_) => {
if !attestations_electra.is_empty() {
error!(
self.log,
"Tried to produce block with attestations of the wrong type";
"slot" => slot,
"attestations" => attestations_electra.len(),
);
}
if !attester_slashings_electra.is_empty() {
error!(
self.log,
"Tried to produce block with attester slashings of the wrong type";
"slot" => slot,
"attester_slashings" => attester_slashings_electra.len(),
);
}
}
BeaconState::Electra(_) => {
if !attestations_base.is_empty() {
error!(
self.log,
"Tried to produce block with attestations of the wrong type";
"slot" => slot,
"attestations" => attestations_base.len(),
);
}
if !attester_slashings_base.is_empty() {
error!(
self.log,
"Tried to produce block with attester slashings of the wrong type";
"slot" => slot,
"attester_slashings" => attester_slashings_base.len(),
);
}
}
};
let (inner_block, maybe_blobs_and_proofs, execution_payload_value) = match &state {
BeaconState::Base(_) => (
BeaconBlock::Base(BeaconBlockBase {
@@ -5052,8 +5119,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: attestations.into(),
attester_slashings: attester_slashings_base.into(),
attestations: attestations_base.into(),
deposits: deposits.into(),
voluntary_exits: voluntary_exits.into(),
_phantom: PhantomData,
@@ -5073,8 +5140,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: attestations.into(),
attester_slashings: attester_slashings_base.into(),
attestations: attestations_base.into(),
deposits: deposits.into(),
voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate
@@ -5100,8 +5167,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: attestations.into(),
attester_slashings: attester_slashings_base.into(),
attestations: attestations_base.into(),
deposits: deposits.into(),
voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate
@@ -5132,8 +5199,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: attestations.into(),
attester_slashings: attester_slashings_base.into(),
attestations: attestations_base.into(),
deposits: deposits.into(),
voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate
@@ -5166,8 +5233,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: attestations.into(),
attester_slashings: attester_slashings_base.into(),
attestations: attestations_base.into(),
deposits: deposits.into(),
voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate
@@ -5204,8 +5271,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: attestations.into(),
attester_slashings: attester_slashings_electra.into(),
attestations: attestations_electra.into(),
deposits: deposits.into(),
voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate
@@ -5216,6 +5283,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
bls_to_execution_changes: bls_to_execution_changes.into(),
blob_kzg_commitments: kzg_commitments
.ok_or(BlockProductionError::InvalidPayloadFork)?,
// TODO(electra): finish consolidations when they're more spec'd out
consolidations: Vec::new().into(),
},
}),
maybe_blobs_and_proofs,
@@ -5321,7 +5390,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.log,
"Produced beacon block";
"parent" => ?block.parent_root(),
"attestations" => block.body().attestations().len(),
"attestations" => block.body().attestations_len(),
"slot" => block.slot()
);

View File

@@ -27,10 +27,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let split_attestations = block
.body()
.attestations()
.iter()
.map(|att| {
let attesting_indices = get_attesting_indices_from_state(state, att)?;
Ok(SplitAttestation::new(att.clone(), attesting_indices))
Ok(SplitAttestation::new(
att.clone_as_attestation(),
attesting_indices,
))
})
.collect::<Result<Vec<_>, BeaconChainError>>()?;
@@ -86,7 +88,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
block
.body()
.attestations()
.iter()
.map(|a| a.data().clone())
.collect()
} else {

View File

@@ -1623,7 +1623,7 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
}
// Register each attestation in the block with fork choice.
for (i, attestation) in block.message().body().attestations().iter().enumerate() {
for (i, attestation) in block.message().body().attestations().enumerate() {
let _fork_choice_attestation_timer =
metrics::start_timer(&metrics::FORK_CHOICE_PROCESS_ATTESTATION_TIMES);

View File

@@ -10,7 +10,7 @@ use types::consts::altair::{
SYNC_COMMITTEE_SUBNET_COUNT, TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE,
};
use types::slot_data::SlotData;
use types::{Attestation, EthSpec, Hash256, Slot, SyncCommitteeContribution};
use types::{Attestation, AttestationRef, EthSpec, Hash256, Slot, SyncCommitteeContribution};
pub type ObservedSyncContributions<E> = ObservedAggregates<
SyncCommitteeContribution<E>,
@@ -102,30 +102,30 @@ pub trait SubsetItem {
fn root(&self) -> Hash256;
}
impl<E: EthSpec> SubsetItem for Attestation<E> {
impl<'a, E: EthSpec> SubsetItem for AttestationRef<'a, E> {
type Item = BitList<E::MaxValidatorsPerCommittee>;
fn is_subset(&self, other: &Self::Item) -> bool {
match self {
Attestation::Base(att) => att.aggregation_bits.is_subset(other),
Self::Base(att) => att.aggregation_bits.is_subset(other),
// TODO(electra) implement electra variant
Attestation::Electra(_) => todo!(),
Self::Electra(_) => todo!(),
}
}
fn is_superset(&self, other: &Self::Item) -> bool {
match self {
Attestation::Base(att) => other.is_subset(&att.aggregation_bits),
Self::Base(att) => other.is_subset(&att.aggregation_bits),
// TODO(electra) implement electra variant
Attestation::Electra(_) => todo!(),
Self::Electra(_) => todo!(),
}
}
/// Returns the sync contribution aggregation bits.
fn get_item(&self) -> Self::Item {
match self {
Attestation::Base(att) => att.aggregation_bits.clone(),
Self::Base(att) => att.aggregation_bits.clone(),
// TODO(electra) implement electra variant
Attestation::Electra(_) => todo!(),
Self::Electra(_) => todo!(),
}
}
@@ -135,7 +135,7 @@ impl<E: EthSpec> SubsetItem for Attestation<E> {
}
}
impl<E: EthSpec> SubsetItem for SyncCommitteeContribution<E> {
impl<'a, E: EthSpec> SubsetItem for &'a SyncCommitteeContribution<E> {
type Item = BitVector<E::SyncSubcommitteeSize>;
fn is_subset(&self, other: &Self::Item) -> bool {
self.aggregation_bits.is_subset(other)
@@ -208,7 +208,7 @@ impl<I> SlotHashSet<I> {
/// Store the items in self so future observations recognise its existence.
pub fn observe_item<S: SlotData + SubsetItem<Item = I>>(
&mut self,
item: &S,
item: S,
root: Hash256,
) -> Result<ObserveOutcome, Error> {
if item.get_slot() != self.slot {
@@ -254,7 +254,7 @@ impl<I> SlotHashSet<I> {
/// the given root and slot.
pub fn is_known_subset<S: SlotData + SubsetItem<Item = I>>(
&self,
item: &S,
item: S,
root: Hash256,
) -> Result<bool, Error> {
if item.get_slot() != self.slot {
@@ -276,16 +276,43 @@ impl<I> SlotHashSet<I> {
}
}
/// Trait for observable items that can be observed from their reference type.
///
/// This is used to make observations for `Attestation`s from `AttestationRef`s.
pub trait AsReference {
type Reference<'a>
where
Self: 'a;
fn as_reference(&self) -> Self::Reference<'_>;
}
impl<E: EthSpec> AsReference for Attestation<E> {
type Reference<'a> = AttestationRef<'a, E>;
fn as_reference(&self) -> AttestationRef<'_, E> {
self.to_ref()
}
}
impl<E: EthSpec> AsReference for SyncCommitteeContribution<E> {
type Reference<'a> = &'a Self;
fn as_reference(&self) -> &Self {
self
}
}
/// Stores the roots of objects for some number of `Slots`, so we can determine if
/// these have previously been seen on the network.
pub struct ObservedAggregates<T: SlotData + Consts, E: EthSpec, I> {
pub struct ObservedAggregates<T: Consts + AsReference, E: EthSpec, I> {
lowest_permissible_slot: Slot,
sets: Vec<SlotHashSet<I>>,
_phantom_spec: PhantomData<E>,
_phantom_tree_hash: PhantomData<T>,
}
impl<T: SlotData + Consts, E: EthSpec, I> Default for ObservedAggregates<T, E, I> {
impl<T: Consts + AsReference, E: EthSpec, I> Default for ObservedAggregates<T, E, I> {
fn default() -> Self {
Self {
lowest_permissible_slot: Slot::new(0),
@@ -296,13 +323,18 @@ impl<T: SlotData + Consts, E: EthSpec, I> Default for ObservedAggregates<T, E, I
}
}
impl<T: SlotData + Consts + SubsetItem<Item = I>, E: EthSpec, I> ObservedAggregates<T, E, I> {
impl<T, E, I> ObservedAggregates<T, E, I>
where
T: Consts + AsReference,
E: EthSpec,
for<'a> T::Reference<'a>: SubsetItem<Item = I> + SlotData,
{
/// Store `item` in `self` keyed at `root`.
///
/// `root` must equal `item.root::<SubsetItem>()`.
pub fn observe_item(
&mut self,
item: &T,
item: T::Reference<'_>,
root_opt: Option<Hash256>,
) -> Result<ObserveOutcome, Error> {
let index = self.get_set_index(item.get_slot())?;
@@ -319,7 +351,11 @@ impl<T: SlotData + Consts + SubsetItem<Item = I>, E: EthSpec, I> ObservedAggrega
///
/// `root` must equal `item.root::<SubsetItem>()`.
#[allow(clippy::wrong_self_convention)]
pub fn is_known_subset(&mut self, item: &T, root: Hash256) -> Result<bool, Error> {
pub fn is_known_subset(
&mut self,
item: T::Reference<'_>,
root: Hash256,
) -> Result<bool, Error> {
let index = self.get_set_index(item.get_slot())?;
self.sets
@@ -417,8 +453,8 @@ mod tests {
fn get_attestation(slot: Slot, beacon_block_root: u64) -> Attestation<E> {
let mut a: Attestation<E> = test_random_instance();
a.data.slot = slot;
a.data.beacon_block_root = Hash256::from_low_u64_be(beacon_block_root);
a.data_mut().slot = slot;
a.data_mut().beacon_block_root = Hash256::from_low_u64_be(beacon_block_root);
a
}
@@ -444,12 +480,12 @@ mod tests {
for a in &items {
assert_eq!(
store.is_known_subset(a, a.root()),
store.is_known_subset(a.as_reference(), a.as_reference().root()),
Ok(false),
"should indicate an unknown attestation is unknown"
);
assert_eq!(
store.observe_item(a, None),
store.observe_item(a.as_reference(), None),
Ok(ObserveOutcome::New),
"should observe new attestation"
);
@@ -457,12 +493,12 @@ mod tests {
for a in &items {
assert_eq!(
store.is_known_subset(a, a.root()),
store.is_known_subset(a.as_reference(), a.as_reference().root()),
Ok(true),
"should indicate a known attestation is known"
);
assert_eq!(
store.observe_item(a, Some(a.root())),
store.observe_item(a.as_reference(), Some(a.as_reference().root())),
Ok(ObserveOutcome::Subset),
"should acknowledge an existing attestation"
);

View File

@@ -1,7 +1,6 @@
use derivative::Derivative;
use smallvec::{smallvec, SmallVec};
use ssz::{Decode, Encode};
use state_processing::{SigVerifiedOp, VerifyOperation, VerifyOperationAt};
use state_processing::{SigVerifiedOp, TransformPersist, VerifyOperation, VerifyOperationAt};
use std::collections::HashSet;
use std::marker::PhantomData;
use types::{
@@ -34,7 +33,7 @@ pub struct ObservedOperations<T: ObservableOperation<E>, E: EthSpec> {
/// Was the observed operation new and valid for further processing, or a useless duplicate?
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ObservationOutcome<T: Encode + Decode, E: EthSpec> {
pub enum ObservationOutcome<T: TransformPersist, E: EthSpec> {
New(SigVerifiedOp<T, E>),
AlreadyKnown,
}
@@ -62,12 +61,12 @@ impl<E: EthSpec> ObservableOperation<E> for ProposerSlashing {
impl<E: EthSpec> ObservableOperation<E> for AttesterSlashing<E> {
fn observed_validators(&self) -> SmallVec<[u64; SMALL_VEC_SIZE]> {
let attestation_1_indices = self
.attestation_1
.attestation_1()
.attesting_indices_iter()
.copied()
.collect::<HashSet<u64>>();
let attestation_2_indices = self
.attestation_2
.attestation_2()
.attesting_indices_iter()
.copied()
.collect::<HashSet<u64>>();

View File

@@ -1492,7 +1492,8 @@ where
) -> AttesterSlashing<E> {
let fork = self.chain.canonical_head.cached_head().head_fork();
let mut attestation_1 = IndexedAttestation::Base(IndexedAttestationBase {
// TODO(electra): consider making this test fork-agnostic
let mut attestation_1 = IndexedAttestationBase {
attesting_indices: VariableList::new(validator_indices).unwrap(),
data: AttestationData {
slot: Slot::new(0),
@@ -1508,36 +1509,37 @@ where
},
},
signature: AggregateSignature::infinity(),
});
};
let mut attestation_2 = attestation_1.clone();
attestation_2.data_mut().index += 1;
attestation_2.data_mut().source.epoch = source2.unwrap_or(Epoch::new(0));
attestation_2.data_mut().target.epoch = target2.unwrap_or(fork.epoch);
attestation_2.data.index += 1;
attestation_2.data.source.epoch = source2.unwrap_or(Epoch::new(0));
attestation_2.data.target.epoch = target2.unwrap_or(fork.epoch);
for attestation in &mut [&mut attestation_1, &mut attestation_2] {
// TODO(electra) we could explore iter mut here
for i in attestation.attesting_indices_to_vec() {
let sk = &self.validator_keypairs[i as usize].sk;
for i in attestation.attesting_indices.iter() {
let sk = &self.validator_keypairs[*i as usize].sk;
let genesis_validators_root = self.chain.genesis_validators_root;
let domain = self.chain.spec.get_domain(
attestation.data().target.epoch,
attestation.data.target.epoch,
Domain::BeaconAttester,
&fork,
genesis_validators_root,
);
let message = attestation.data().signing_root(domain);
let message = attestation.data.signing_root(domain);
attestation.signature_mut().add_assign(&sk.sign(message));
attestation.signature.add_assign(&sk.sign(message));
}
}
AttesterSlashing {
// TODO(electra): fix this test
AttesterSlashing::Base(AttesterSlashingBase {
attestation_1,
attestation_2,
}
})
}
pub fn make_attester_slashing_different_indices(
@@ -1559,43 +1561,45 @@ where
},
};
let mut attestation_1 = IndexedAttestation::Base(IndexedAttestationBase {
// TODO(electra): make this test fork-agnostic
let mut attestation_1 = IndexedAttestationBase {
attesting_indices: VariableList::new(validator_indices_1).unwrap(),
data: data.clone(),
signature: AggregateSignature::infinity(),
});
};
let mut attestation_2 = IndexedAttestation::Base(IndexedAttestationBase {
let mut attestation_2 = IndexedAttestationBase {
attesting_indices: VariableList::new(validator_indices_2).unwrap(),
data,
signature: AggregateSignature::infinity(),
});
};
attestation_2.data_mut().index += 1;
attestation_2.data.index += 1;
let fork = self.chain.canonical_head.cached_head().head_fork();
for attestation in &mut [&mut attestation_1, &mut attestation_2] {
for i in attestation.attesting_indices_to_vec() {
let sk = &self.validator_keypairs[i as usize].sk;
for i in attestation.attesting_indices.iter() {
let sk = &self.validator_keypairs[*i as usize].sk;
let genesis_validators_root = self.chain.genesis_validators_root;
let domain = self.chain.spec.get_domain(
attestation.data().target.epoch,
attestation.data.target.epoch,
Domain::BeaconAttester,
&fork,
genesis_validators_root,
);
let message = attestation.data().signing_root(domain);
let message = attestation.data.signing_root(domain);
attestation.signature_mut().add_assign(&sk.sign(message));
attestation.signature.add_assign(&sk.sign(message));
}
}
AttesterSlashing {
// TODO(electra): fix this test
AttesterSlashing::Base(AttesterSlashingBase {
attestation_1,
attestation_2,
}
})
}
pub fn make_proposer_slashing(&self, validator_index: u64) -> ProposerSlashing {

View File

@@ -25,9 +25,10 @@ use types::consts::altair::{
TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX,
};
use types::{
Attestation, AttestationData, AttesterSlashing, BeaconBlockRef, BeaconState, BeaconStateError,
ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, ProposerSlashing, PublicKeyBytes,
SignedAggregateAndProof, SignedContributionAndProof, Slot, SyncCommitteeMessage, VoluntaryExit,
Attestation, AttestationData, AttesterSlashingRef, BeaconBlockRef, BeaconState,
BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation,
IndexedAttestationRef, ProposerSlashing, PublicKeyBytes, SignedAggregateAndProof,
SignedContributionAndProof, Slot, SyncCommitteeMessage, VoluntaryExit,
};
/// Used for Prometheus labels.
@@ -1410,7 +1411,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
/// Note: Blocks that get orphaned will skew the inclusion distance calculation.
pub fn register_attestation_in_block(
&self,
indexed_attestation: &IndexedAttestation<E>,
indexed_attestation: IndexedAttestationRef<'_, E>,
parent_slot: Slot,
spec: &ChainSpec,
) {
@@ -1783,30 +1784,30 @@ impl<E: EthSpec> ValidatorMonitor<E> {
}
/// Register an attester slashing from the gossip network.
pub fn register_gossip_attester_slashing(&self, slashing: &AttesterSlashing<E>) {
pub fn register_gossip_attester_slashing(&self, slashing: AttesterSlashingRef<'_, E>) {
self.register_attester_slashing("gossip", slashing)
}
/// Register an attester slashing from the HTTP API.
pub fn register_api_attester_slashing(&self, slashing: &AttesterSlashing<E>) {
pub fn register_api_attester_slashing(&self, slashing: AttesterSlashingRef<'_, E>) {
self.register_attester_slashing("api", slashing)
}
/// Register an attester slashing included in a *valid* `BeaconBlock`.
pub fn register_block_attester_slashing(&self, slashing: &AttesterSlashing<E>) {
pub fn register_block_attester_slashing(&self, slashing: AttesterSlashingRef<'_, E>) {
self.register_attester_slashing("block", slashing)
}
fn register_attester_slashing(&self, src: &str, slashing: &AttesterSlashing<E>) {
let data = slashing.attestation_1.data();
fn register_attester_slashing(&self, src: &str, slashing: AttesterSlashingRef<'_, E>) {
let data = slashing.attestation_1().data();
let attestation_1_indices: HashSet<u64> = slashing
.attestation_1
.attestation_1()
.attesting_indices_iter()
.copied()
.collect();
slashing
.attestation_2
.attestation_2()
.attesting_indices_iter()
.filter(|index| attestation_1_indices.contains(index))
.filter_map(|index| self.get_validator(*index))

View File

@@ -8,7 +8,9 @@ use beacon_chain::{metrics, StateSkipConfig, WhenSlotSkipped};
use lazy_static::lazy_static;
use std::sync::Arc;
use tree_hash::TreeHash;
use types::{AggregateSignature, EthSpec, Keypair, MainnetEthSpec, RelativeEpoch, Slot};
use types::{
AggregateSignature, Attestation, EthSpec, Keypair, MainnetEthSpec, RelativeEpoch, Slot,
};
pub const VALIDATOR_COUNT: usize = 16;
@@ -188,20 +190,35 @@ async fn produces_attestations() {
.produce_unaggregated_attestation(slot, index)
.expect("should produce attestation");
let data = &attestation.data;
match &attestation {
Attestation::Base(att) => {
assert_eq!(
att.aggregation_bits.len(),
committee_len,
"bad committee len"
);
assert!(
att.aggregation_bits.is_zero(),
"some committee bits are set"
);
}
Attestation::Electra(att) => {
assert_eq!(
att.aggregation_bits.len(),
committee_len,
"bad committee len"
);
assert!(
att.aggregation_bits.is_zero(),
"some committee bits are set"
);
}
}
let data = attestation.data();
assert_eq!(
attestation.aggregation_bits.len(),
committee_len,
"bad committee len"
);
assert!(
attestation.aggregation_bits.is_zero(),
"some committee bits are set"
);
assert_eq!(
attestation.signature,
AggregateSignature::empty(),
attestation.signature(),
&AggregateSignature::empty(),
"bad signature"
);
assert_eq!(data.index, index, "bad index");
@@ -329,10 +346,10 @@ async fn early_attester_cache_old_request() {
.produce_unaggregated_attestation(attest_slot, 0)
.unwrap();
assert_eq!(attestation.data.slot, attest_slot);
assert_eq!(attestation.data().slot, attest_slot);
let attested_block = harness
.chain
.get_blinded_block(&attestation.data.beacon_block_root)
.get_blinded_block(&attestation.data().beacon_block_root)
.unwrap()
.unwrap();
assert_eq!(attested_block.slot(), attest_slot);

View File

@@ -125,7 +125,7 @@ fn get_valid_unaggregated_attestation<T: BeaconChainTypes>(
let validator_committee_index = 0;
let validator_index = *head
.beacon_state
.get_beacon_committee(current_slot, valid_attestation.data.index)
.get_beacon_committee(current_slot, valid_attestation.data().index)
.expect("should get committees")
.committee
.get(validator_committee_index)
@@ -144,7 +144,7 @@ fn get_valid_unaggregated_attestation<T: BeaconChainTypes>(
.expect("should sign attestation");
let subnet_id = SubnetId::compute_subnet_for_attestation_data::<E>(
&valid_attestation.data,
valid_attestation.data(),
head.beacon_state
.get_committee_count_at_slot(current_slot)
.expect("should get committee count"),
@@ -170,7 +170,7 @@ fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
let current_slot = chain.slot().expect("should get slot");
let committee = state
.get_beacon_committee(current_slot, aggregate.data.index)
.get_beacon_committee(current_slot, aggregate.data().index)
.expect("should get committees");
let committee_len = committee.committee.len();
@@ -181,7 +181,7 @@ fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
let aggregator_sk = generate_deterministic_keypair(val_index).sk;
let proof = SelectionProof::new::<T::EthSpec>(
aggregate.data.slot,
aggregate.data().slot,
&aggregator_sk,
&state.fork(),
chain.genesis_validators_root,
@@ -220,7 +220,7 @@ fn get_non_aggregator<T: BeaconChainTypes>(
let current_slot = chain.slot().expect("should get slot");
let committee = state
.get_beacon_committee(current_slot, aggregate.data.index)
.get_beacon_committee(current_slot, aggregate.data().index)
.expect("should get committees");
let committee_len = committee.committee.len();
@@ -231,7 +231,7 @@ fn get_non_aggregator<T: BeaconChainTypes>(
let aggregator_sk = generate_deterministic_keypair(val_index).sk;
let proof = SelectionProof::new::<T::EthSpec>(
aggregate.data.slot,
aggregate.data().slot,
&aggregator_sk,
&state.fork(),
chain.genesis_validators_root,
@@ -301,7 +301,7 @@ impl GossipTester {
get_valid_aggregated_attestation(&harness.chain, valid_attestation.clone());
let mut invalid_attestation = valid_attestation.clone();
invalid_attestation.data.beacon_block_root = Hash256::repeat_byte(13);
invalid_attestation.data_mut().beacon_block_root = Hash256::repeat_byte(13);
let (mut invalid_aggregate, _, _) =
get_valid_aggregated_attestation(&harness.chain, invalid_attestation.clone());
@@ -490,7 +490,7 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate from future slot",
|tester, a| a.message.aggregate.data.slot = tester.slot() + 1,
|tester, a| a.message.aggregate.data_mut().slot = tester.slot() + 1,
|tester, err| {
assert!(matches!(
err,
@@ -504,8 +504,9 @@ 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.slot = too_early_slot;
a.message.aggregate.data.target.epoch = too_early_slot.epoch(E::slots_per_epoch());
a.message.aggregate.data_mut().slot = too_early_slot;
a.message.aggregate.data_mut().target.epoch =
too_early_slot.epoch(E::slots_per_epoch());
},
|tester, err| {
let valid_early_slot = tester.earliest_valid_attestation_slot();
@@ -529,7 +530,7 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"attestation with invalid target epoch",
|_, a| a.message.aggregate.data.target.epoch += 1,
|_, a| a.message.aggregate.data_mut().target.epoch += 1,
|_, err| assert!(matches!(err, AttnError::InvalidTargetEpoch { .. })),
)
/*
@@ -538,7 +539,7 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"attestation with invalid target root",
|_, a| a.message.aggregate.data.target.root = Hash256::repeat_byte(42),
|_, a| a.message.aggregate.data_mut().target.root = Hash256::repeat_byte(42),
|_, err| assert!(matches!(err, AttnError::InvalidTargetRoot { .. })),
)
/*
@@ -548,7 +549,7 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate with unknown head block",
|_, a| a.message.aggregate.data.beacon_block_root = Hash256::repeat_byte(42),
|_, a| a.message.aggregate.data_mut().beacon_block_root = Hash256::repeat_byte(42),
|_, err| {
assert!(matches!(
err,
@@ -567,10 +568,19 @@ async fn aggregated_gossip_verification() {
.inspect_aggregate_err(
"aggregate with no participants",
|_, a| {
let aggregation_bits = &mut a.message.aggregate.aggregation_bits;
aggregation_bits.difference_inplace(&aggregation_bits.clone());
assert!(aggregation_bits.is_zero());
a.message.aggregate.signature = AggregateSignature::infinity();
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.message.aggregate.signature_mut() = AggregateSignature::infinity();
},
|_, err| assert!(matches!(err, AttnError::EmptyAggregationBitfield)),
)
@@ -598,7 +608,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();
@@ -634,7 +644,7 @@ 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 = agg_sig;
*a.message.aggregate.signature_mut() = agg_sig;
},
|_, err| assert!(matches!(err, AttnError::InvalidSignature)),
)
@@ -729,7 +739,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()
))
},
)
@@ -741,7 +751,7 @@ async fn aggregated_gossip_verification() {
*/
.inspect_aggregate_err(
"aggregate from aggregator that has already been seen",
|_, a| a.message.aggregate.data.beacon_block_root = Hash256::repeat_byte(42),
|_, a| a.message.aggregate.data_mut().beacon_block_root = Hash256::repeat_byte(42),
|tester, err| {
assert!(matches!(
err,
@@ -766,12 +776,12 @@ async fn unaggregated_gossip_verification() {
.inspect_unaggregate_err(
"attestation with invalid committee index",
|tester, a, _| {
a.data.index = tester
a.data_mut().index = tester
.harness
.chain
.head_snapshot()
.beacon_state
.get_committee_count_at_slot(a.data.slot)
.get_committee_count_at_slot(a.data().slot)
.unwrap()
},
|_, err| assert!(matches!(err, AttnError::NoCommitteeForSlotAndIndex { .. })),
@@ -806,7 +816,7 @@ async fn unaggregated_gossip_verification() {
*/
.inspect_unaggregate_err(
"attestation from future slot",
|tester, a, _| a.data.slot = tester.slot() + 1,
|tester, a, _| a.data_mut().slot = tester.slot() + 1,
|tester, err| {
assert!(matches!(
err,
@@ -822,8 +832,8 @@ async fn unaggregated_gossip_verification() {
"attestation from past slot",
|tester, a, _| {
let too_early_slot = tester.earliest_valid_attestation_slot() - 1;
a.data.slot = too_early_slot;
a.data.target.epoch = too_early_slot.epoch(E::slots_per_epoch());
a.data_mut().slot = too_early_slot;
a.data_mut().target.epoch = too_early_slot.epoch(E::slots_per_epoch());
},
|tester, err| {
let valid_early_slot = tester.earliest_valid_attestation_slot();
@@ -847,7 +857,7 @@ async fn unaggregated_gossip_verification() {
*/
.inspect_unaggregate_err(
"attestation with invalid target epoch",
|_, a, _| a.data.target.epoch += 1,
|_, a, _| a.data_mut().target.epoch += 1,
|_, err| {
assert!(matches!(
err,
@@ -863,15 +873,29 @@ async fn unaggregated_gossip_verification() {
*/
.inspect_unaggregate_err(
"attestation without any aggregation bits set",
|tester, a, _| {
a.aggregation_bits
.set(tester.attester_committee_index, false)
.expect("should unset aggregation bit");
assert_eq!(
a.aggregation_bits.num_set_bits(),
0,
"test requires no set bits"
);
|tester, mut a, _| {
match &mut a {
Attestation::Base(ref mut att) => {
att.aggregation_bits
.set(tester.attester_committee_index, false)
.expect("should unset aggregation bit");
assert_eq!(
att.aggregation_bits.num_set_bits(),
0,
"test requires no set bits"
);
}
Attestation::Electra(ref mut att) => {
att.aggregation_bits
.set(tester.attester_committee_index, false)
.expect("should unset aggregation bit");
assert_eq!(
att.aggregation_bits.num_set_bits(),
0,
"test requires no set bits"
);
}
}
},
|_, err| {
assert!(matches!(
@@ -882,10 +906,19 @@ async fn unaggregated_gossip_verification() {
)
.inspect_unaggregate_err(
"attestation with two aggregation bits set",
|tester, a, _| {
a.aggregation_bits
.set(tester.attester_committee_index + 1, true)
.expect("should set second aggregation bit");
|tester, mut a, _| {
match &mut a {
Attestation::Base(ref mut att) => {
att.aggregation_bits
.set(tester.attester_committee_index + 1, true)
.expect("should set second aggregation bit");
}
Attestation::Electra(ref mut att) => {
att.aggregation_bits
.set(tester.attester_committee_index + 1, true)
.expect("should set second aggregation bit");
}
}
},
|_, err| {
assert!(matches!(
@@ -903,11 +936,22 @@ async fn unaggregated_gossip_verification() {
*/
.inspect_unaggregate_err(
"attestation with invalid bitfield",
|_, a, _| {
let bits = a.aggregation_bits.iter().collect::<Vec<_>>();
a.aggregation_bits = BitList::with_capacity(bits.len() + 1).unwrap();
for (i, bit) in bits.into_iter().enumerate() {
a.aggregation_bits.set(i, bit).unwrap();
|_, mut a, _| {
match &mut a {
Attestation::Base(ref mut att) => {
let bits = att.aggregation_bits.iter().collect::<Vec<_>>();
att.aggregation_bits = BitList::with_capacity(bits.len() + 1).unwrap();
for (i, bit) in bits.into_iter().enumerate() {
att.aggregation_bits.set(i, bit).unwrap();
}
}
Attestation::Electra(ref mut att) => {
let bits = att.aggregation_bits.iter().collect::<Vec<_>>();
att.aggregation_bits = BitList::with_capacity(bits.len() + 1).unwrap();
for (i, bit) in bits.into_iter().enumerate() {
att.aggregation_bits.set(i, bit).unwrap();
}
}
}
},
|_, err| {
@@ -927,7 +971,7 @@ async fn unaggregated_gossip_verification() {
.inspect_unaggregate_err(
"attestation with unknown head block",
|_, a, _| {
a.data.beacon_block_root = Hash256::repeat_byte(42);
a.data_mut().beacon_block_root = Hash256::repeat_byte(42);
},
|_, err| {
assert!(matches!(
@@ -949,7 +993,7 @@ async fn unaggregated_gossip_verification() {
.inspect_unaggregate_err(
"attestation with invalid target root",
|_, a, _| {
a.data.target.root = Hash256::repeat_byte(42);
a.data_mut().target.root = Hash256::repeat_byte(42);
},
|_, err| {
assert!(matches!(
@@ -968,7 +1012,7 @@ async fn unaggregated_gossip_verification() {
|tester, a, _| {
let mut agg_sig = AggregateSignature::infinity();
agg_sig.add_assign(&tester.attester_sk.sign(Hash256::repeat_byte(42)));
a.signature = agg_sig;
*a.signature_mut() = agg_sig;
},
|_, err| {
assert!(matches!(
@@ -1055,7 +1099,7 @@ async fn attestation_that_skips_epochs() {
.cloned()
.expect("should have at least one attestation in committee");
let block_root = attestation.data.beacon_block_root;
let block_root = attestation.data().beacon_block_root;
let block_slot = harness
.chain
.store
@@ -1066,7 +1110,7 @@ async fn attestation_that_skips_epochs() {
.slot();
assert!(
attestation.data.slot - block_slot > E::slots_per_epoch() * 2,
attestation.data().slot - block_slot > E::slots_per_epoch() * 2,
"the attestation must skip more than two epochs"
);
@@ -1228,7 +1272,7 @@ async fn attestation_to_finalized_block() {
.first()
.cloned()
.expect("should have at least one attestation in committee");
assert_eq!(attestation.data.beacon_block_root, earlier_block_root);
assert_eq!(attestation.data().beacon_block_root, earlier_block_root);
// Attestation should be rejected for attesting to a pre-finalization block.
let res = harness
@@ -1281,7 +1325,7 @@ 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 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));
@@ -1338,7 +1382,7 @@ async fn verify_attestation_for_gossip_doppelganger_detection() {
.verify_unaggregated_attestation_for_gossip(&valid_attestation, Some(subnet_id))
.expect("should verify attestation");
let epoch = valid_attestation.data.target.epoch;
let epoch = valid_attestation.data().target.epoch;
assert!(harness.chain.validator_seen_at_epoch(index, epoch));
// Check the correct beacon cache is populated

View File

@@ -664,7 +664,7 @@ async fn invalid_signature_attester_slashing() {
for &block_index in BLOCK_INDICES {
let harness = get_invalid_sigs_harness(&chain_segment).await;
let mut snapshots = chain_segment.clone();
let indexed_attestation = IndexedAttestation {
let indexed_attestation = IndexedAttestationBase {
attesting_indices: vec![0].into(),
data: AttestationData {
slot: Slot::new(0),
@@ -681,7 +681,7 @@ async fn invalid_signature_attester_slashing() {
},
signature: junk_aggregate_signature(),
};
let attester_slashing = AttesterSlashing {
let attester_slashing = AttesterSlashingBase {
attestation_1: indexed_attestation.clone(),
attestation_2: indexed_attestation,
};
@@ -690,11 +690,36 @@ async fn invalid_signature_attester_slashing() {
.as_ref()
.clone()
.deconstruct();
block
.body_mut()
.attester_slashings_mut()
.push(attester_slashing)
.expect("should update attester slashing");
match &mut block.body_mut() {
BeaconBlockBodyRefMut::Base(ref mut blk) => {
blk.attester_slashings
.push(attester_slashing)
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Altair(ref mut blk) => {
blk.attester_slashings
.push(attester_slashing)
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Merge(ref mut blk) => {
blk.attester_slashings
.push(attester_slashing)
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Capella(ref mut blk) => {
blk.attester_slashings
.push(attester_slashing)
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Deneb(ref mut blk) => {
blk.attester_slashings
.push(attester_slashing)
.expect("should update attester slashing");
}
BeaconBlockBodyRefMut::Electra(_) => {
panic!("electra test not implemented!");
}
}
snapshots[block_index].beacon_block =
Arc::new(SignedBeaconBlock::from_block(block, signature));
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
@@ -724,8 +749,34 @@ async fn invalid_signature_attestation() {
.as_ref()
.clone()
.deconstruct();
if let Some(attestation) = block.body_mut().attestations_mut().get_mut(0) {
attestation.signature = junk_aggregate_signature();
match &mut block.body_mut() {
BeaconBlockBodyRefMut::Base(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Altair(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Merge(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Capella(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Deneb(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
BeaconBlockBodyRefMut::Electra(ref mut blk) => blk
.attestations
.get_mut(0)
.map(|att| att.signature = junk_aggregate_signature()),
};
if block.body().attestations_len() > 0 {
snapshots[block_index].beacon_block =
Arc::new(SignedBeaconBlock::from_block(block, signature));
update_parent_roots(&mut snapshots, &mut chain_segment_blobs);
@@ -1187,9 +1238,13 @@ async fn verify_block_for_gossip_doppelganger_detection() {
let state = harness.get_current_state();
let ((block, _), _) = harness.make_block(state.clone(), Slot::new(1)).await;
let attestations = block
.message()
.body()
.attestations()
.map(|att| att.clone_as_attestation())
.collect::<Vec<_>>();
let verified_block = harness.chain.verify_block_for_gossip(block).await.unwrap();
let attestations = verified_block.block.message().body().attestations().clone();
harness
.chain
.process_block(
@@ -1202,37 +1257,69 @@ async fn verify_block_for_gossip_doppelganger_detection() {
.unwrap();
for att in attestations.iter() {
let epoch = att.data.target.epoch;
let epoch = att.data().target.epoch;
let committee = state
.get_beacon_committee(att.data.slot, att.data.index)
.get_beacon_committee(att.data().slot, att.data().index)
.unwrap();
let indexed_attestation = get_indexed_attestation(committee.committee, att).unwrap();
let indexed_attestation =
get_indexed_attestation(committee.committee, att.to_ref()).unwrap();
for &index in &indexed_attestation.attesting_indices {
let index = index as usize;
match indexed_attestation {
IndexedAttestation::Base(indexed_attestation) => {
for &index in &indexed_attestation.attesting_indices {
let index = index as usize;
assert!(harness.chain.validator_seen_at_epoch(index, epoch));
assert!(harness.chain.validator_seen_at_epoch(index, epoch));
// Check the correct beacon cache is populated
assert!(harness
.chain
.observed_block_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if block attester was observed"));
assert!(!harness
.chain
.observed_gossip_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip attester was observed"));
assert!(!harness
.chain
.observed_aggregators
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip aggregator was observed"));
}
// Check the correct beacon cache is populated
assert!(harness
.chain
.observed_block_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if block attester was observed"));
assert!(!harness
.chain
.observed_gossip_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip attester was observed"));
assert!(!harness
.chain
.observed_aggregators
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip aggregator was observed"));
}
}
IndexedAttestation::Electra(indexed_attestation) => {
for &index in &indexed_attestation.attesting_indices {
let index = index as usize;
assert!(harness.chain.validator_seen_at_epoch(index, epoch));
// Check the correct beacon cache is populated
assert!(harness
.chain
.observed_block_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if block attester was observed"));
assert!(!harness
.chain
.observed_gossip_attesters
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip attester was observed"));
assert!(!harness
.chain
.observed_aggregators
.read()
.validator_has_been_observed(epoch, index)
.expect("should check if gossip aggregator was observed"));
}
}
};
}
}

View File

@@ -1191,9 +1191,17 @@ async fn attesting_to_optimistic_head() {
.produce_unaggregated_attestation(Slot::new(0), 0)
.unwrap();
attestation.aggregation_bits.set(0, true).unwrap();
attestation.data.slot = slot;
attestation.data.beacon_block_root = root;
match &mut attestation {
Attestation::Base(ref mut att) => {
att.aggregation_bits.set(0, true).unwrap();
}
Attestation::Electra(ref mut att) => {
att.aggregation_bits.set(0, true).unwrap();
}
}
attestation.data_mut().slot = slot;
attestation.data_mut().beacon_block_root = root;
rig.harness
.chain
@@ -1214,15 +1222,15 @@ async fn attesting_to_optimistic_head() {
let get_aggregated = || {
rig.harness
.chain
.get_aggregated_attestation(&attestation.data)
.get_aggregated_attestation(attestation.data())
};
let get_aggregated_by_slot_and_root = || {
rig.harness
.chain
.get_aggregated_attestation_by_slot_and_root(
attestation.data.slot,
&attestation.data.tree_hash_root(),
attestation.data().slot,
&attestation.data().tree_hash_root(),
)
};

View File

@@ -606,7 +606,7 @@ async fn epoch_boundary_state_attestation_processing() {
for (attestation, subnet_id) in late_attestations.into_iter().flatten() {
// load_epoch_boundary_state is idempotent!
let block_root = attestation.data.beacon_block_root;
let block_root = attestation.data().beacon_block_root;
let block = store
.get_blinded_block(&block_root)
.unwrap()
@@ -629,7 +629,7 @@ async fn epoch_boundary_state_attestation_processing() {
.verify_unaggregated_attestation_for_gossip(&attestation, Some(subnet_id));
let current_slot = harness.chain.slot().expect("should get slot");
let expected_attestation_slot = attestation.data.slot;
let expected_attestation_slot = attestation.data().slot;
// Extra -1 to handle gossip clock disparity.
let expected_earliest_permissible_slot = current_slot - E::slots_per_epoch() - 1;
@@ -1028,8 +1028,7 @@ async fn multiple_attestations_per_block() {
.as_ref()
.message()
.body()
.attestations()
.len() as u64,
.attestations_len() as u64,
if slot <= 1 { 0 } else { committees_per_slot }
);
}

View File

@@ -573,7 +573,7 @@ async fn attestations_with_increasing_slots() {
.verify_unaggregated_attestation_for_gossip(&attestation, Some(subnet_id));
let current_slot = harness.chain.slot().expect("should get slot");
let expected_attestation_slot = attestation.data.slot;
let expected_attestation_slot = attestation.data().slot;
let expected_earliest_permissible_slot =
current_slot - MinimalEthSpec::slots_per_epoch() - 1;

View File

@@ -10,8 +10,8 @@ use std::collections::{HashMap, HashSet};
use std::marker::PhantomData;
use std::sync::Arc;
use types::{
Attestation, BeaconCommittee, BeaconState, BeaconStateError, BlindedPayload, ChainSpec, Epoch,
EthSpec, Hash256, OwnedBeaconCommittee, RelativeEpoch, SignedBeaconBlock, Slot,
AttestationRef, BeaconCommittee, BeaconState, BeaconStateError, BlindedPayload, ChainSpec,
Epoch, EthSpec, Hash256, OwnedBeaconCommittee, RelativeEpoch, SignedBeaconBlock, Slot,
};
use warp_utils::reject::{beacon_chain_error, custom_bad_request, custom_server_error};
@@ -111,9 +111,9 @@ impl<E: EthSpec> PackingEfficiencyHandler<E> {
let attestations = block_body.attestations();
let mut attestations_in_block = HashMap::new();
for attestation in attestations.iter() {
for attestation in attestations {
match attestation {
Attestation::Base(attn) => {
AttestationRef::Base(attn) => {
for (position, voted) in attn.aggregation_bits.iter().enumerate() {
if voted {
let unique_attestation = UniqueAttestation {
@@ -133,7 +133,7 @@ impl<E: EthSpec> PackingEfficiencyHandler<E> {
}
}
// TODO(electra) implement electra variant
Attestation::Electra(_) => {
AttestationRef::Electra(_) => {
todo!()
}
}

View File

@@ -1635,7 +1635,12 @@ pub fn serve<T: BeaconChainTypes>(
let (block, execution_optimistic, finalized) =
block_id.blinded_block(&chain)?;
Ok(api_types::GenericResponse::from(
block.message().body().attestations().clone(),
block
.message()
.body()
.attestations()
.map(|att| att.clone_as_attestation())
.collect::<Vec<_>>(),
)
.add_execution_optimistic_finalized(execution_optimistic, finalized))
})
@@ -1833,7 +1838,7 @@ pub fn serve<T: BeaconChainTypes>(
chain
.validator_monitor
.read()
.register_api_attester_slashing(&slashing);
.register_api_attester_slashing(slashing.to_ref());
if let ObservationOutcome::New(slashing) = outcome {
publish_pubsub_message(

View File

@@ -1633,7 +1633,13 @@ impl ApiTester {
let expected = block_id.full_block(&self.chain).await.ok().map(
|(block, _execution_optimistic, _finalized)| {
block.message().body().attestations().clone().into()
block
.message()
.body()
.attestations()
.map(|att| att.clone_as_attestation())
.collect::<Vec<_>>()
.into()
},
);
@@ -1793,7 +1799,14 @@ impl ApiTester {
pub async fn test_post_beacon_pool_attester_slashings_invalid(mut self) -> Self {
let mut slashing = self.attester_slashing.clone();
slashing.attestation_1.data_mut().slot += 1;
match &mut slashing {
AttesterSlashing::Base(ref mut slashing) => {
slashing.attestation_1.data.slot += 1;
}
AttesterSlashing::Electra(ref mut slashing) => {
slashing.attestation_1.data.slot += 1;
}
}
self.client
.post_beacon_pool_attester_slashings(&slashing)
@@ -3183,8 +3196,10 @@ impl ApiTester {
.head_beacon_block()
.message()
.body()
.attestations()[0]
.clone();
.attestations()
.next()
.unwrap()
.clone_as_attestation();
let result = self
.client

View File

@@ -7,12 +7,12 @@ use ssz::{Decode, Encode};
use std::io::{Error, ErrorKind};
use std::sync::Arc;
use types::{
Attestation, AttesterSlashing, BlobSidecar, EthSpec, ForkContext, ForkName,
LightClientFinalityUpdate, LightClientOptimisticUpdate, ProposerSlashing,
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase,
SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra,
SignedBeaconBlockMerge, SignedBlsToExecutionChange, SignedContributionAndProof,
SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId,
Attestation, AttesterSlashing, AttesterSlashingBase, AttesterSlashingElectra, BlobSidecar,
EthSpec, ForkContext, ForkName, LightClientFinalityUpdate, LightClientOptimisticUpdate,
ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair,
SignedBeaconBlockBase, SignedBeaconBlockCapella, SignedBeaconBlockDeneb,
SignedBeaconBlockElectra, SignedBeaconBlockMerge, SignedBlsToExecutionChange,
SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId,
};
#[derive(Debug, Clone, PartialEq)]
@@ -239,8 +239,28 @@ impl<E: EthSpec> PubsubMessage<E> {
Ok(PubsubMessage::ProposerSlashing(Box::new(proposer_slashing)))
}
GossipKind::AttesterSlashing => {
let attester_slashing = AttesterSlashing::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?;
// TODO(electra): could an older attester slashing still be floating around during the fork transition?
let attester_slashing =
match fork_context.from_context_bytes(gossip_topic.fork_digest) {
Some(ForkName::Base)
| Some(ForkName::Altair)
| Some(ForkName::Merge)
| Some(ForkName::Capella)
| Some(ForkName::Deneb) => AttesterSlashing::Base(
AttesterSlashingBase::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?,
),
Some(ForkName::Electra) => AttesterSlashing::Electra(
AttesterSlashingElectra::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?,
),
None => {
return Err(format!(
"Unknown gossipsub fork digest: {:?}",
gossip_topic.fork_digest
))
}
};
Ok(PubsubMessage::AttesterSlashing(Box::new(attester_slashing)))
}
GossipKind::SignedContributionAndProof => {

View File

@@ -1428,7 +1428,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
self.chain
.validator_monitor
.read()
.register_gossip_attester_slashing(slashing.as_inner());
.register_gossip_attester_slashing(slashing.as_inner().to_ref());
self.chain.import_attester_slashing(slashing);
debug!(self.log, "Successfully imported attester slashing");

View File

@@ -2,8 +2,9 @@ use crate::AttestationStats;
use itertools::Itertools;
use std::collections::HashMap;
use types::{
attestation::{AttestationBase, AttestationElectra}, superstruct, AggregateSignature, Attestation, AttestationData,
BeaconState, BitList, BitVector, Checkpoint, Epoch, EthSpec, Hash256, Slot,
attestation::{AttestationBase, AttestationElectra},
superstruct, AggregateSignature, Attestation, AttestationData, BeaconState, BitList, BitVector,
Checkpoint, Epoch, EthSpec, Hash256, Slot,
};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
@@ -121,12 +122,14 @@ impl<'a, E: EthSpec> AttestationRef<'a, E> {
data: self.attestation_data(),
signature: indexed_att.signature.clone(),
}),
CompactIndexedAttestation::Electra(indexed_att) => Attestation::Electra(AttestationElectra {
aggregation_bits: indexed_att.aggregation_bits.clone(),
data: self.attestation_data(),
signature: indexed_att.signature.clone(),
committee_bits: indexed_att.committee_bits.clone(),
}),
CompactIndexedAttestation::Electra(indexed_att) => {
Attestation::Electra(AttestationElectra {
aggregation_bits: indexed_att.aggregation_bits.clone(),
data: self.attestation_data(),
signature: indexed_att.signature.clone(),
committee_bits: indexed_att.committee_bits.clone(),
})
}
}
}
}

View File

@@ -1,17 +1,17 @@
use crate::max_cover::MaxCover;
use state_processing::per_block_processing::get_slashable_indices_modular;
use std::collections::{HashMap, HashSet};
use types::{AttesterSlashing, BeaconState, EthSpec};
use types::{AttesterSlashing, AttesterSlashingRef, BeaconState, EthSpec};
#[derive(Debug, Clone)]
pub struct AttesterSlashingMaxCover<'a, E: EthSpec> {
slashing: &'a AttesterSlashing<E>,
slashing: AttesterSlashingRef<'a, E>,
effective_balances: HashMap<u64, u64>,
}
impl<'a, E: EthSpec> AttesterSlashingMaxCover<'a, E> {
pub fn new(
slashing: &'a AttesterSlashing<E>,
slashing: AttesterSlashingRef<'a, E>,
proposer_slashing_indices: &HashSet<u64>,
state: &BeaconState<E>,
) -> Option<Self> {
@@ -39,16 +39,16 @@ impl<'a, E: EthSpec> AttesterSlashingMaxCover<'a, E> {
impl<'a, E: EthSpec> MaxCover for AttesterSlashingMaxCover<'a, E> {
/// The result type, of which we would eventually like a collection of maximal quality.
type Object = AttesterSlashing<E>;
type Intermediate = AttesterSlashing<E>;
type Intermediate = AttesterSlashingRef<'a, E>;
/// The type used to represent sets.
type Set = HashMap<u64, u64>;
fn intermediate(&self) -> &AttesterSlashing<E> {
self.slashing
fn intermediate(&self) -> &AttesterSlashingRef<'a, E> {
&self.slashing
}
fn convert_to_object(slashing: &AttesterSlashing<E>) -> AttesterSlashing<E> {
slashing.clone()
fn convert_to_object(slashing: &AttesterSlashingRef<'a, E>) -> AttesterSlashing<E> {
slashing.clone_as_attester_slashing()
}
/// Get the set of elements covered.
@@ -58,7 +58,7 @@ impl<'a, E: EthSpec> MaxCover for AttesterSlashingMaxCover<'a, E> {
/// Update the set of items covered, for the inclusion of some object in the solution.
fn update_covering_set(
&mut self,
_best_slashing: &AttesterSlashing<E>,
_best_slashing: &AttesterSlashingRef<'a, E>,
covered_validator_indices: &HashMap<u64, u64>,
) {
self.effective_balances

View File

@@ -428,7 +428,7 @@ impl<E: EthSpec> OperationPool<E> {
let relevant_attester_slashings = reader.iter().flat_map(|slashing| {
if slashing.signature_is_still_valid(&state.fork()) {
AttesterSlashingMaxCover::new(slashing.as_inner(), to_be_slashed, state)
AttesterSlashingMaxCover::new(slashing.as_inner().to_ref(), to_be_slashed, state)
} else {
None
}
@@ -442,7 +442,7 @@ impl<E: EthSpec> OperationPool<E> {
.into_iter()
.map(|cover| {
to_be_slashed.extend(cover.covering_set().keys());
cover.intermediate().clone()
AttesterSlashingMaxCover::convert_to_object(cover.intermediate())
})
.collect()
}
@@ -463,16 +463,19 @@ impl<E: EthSpec> OperationPool<E> {
// Check that the attestation's signature is still valid wrt the fork version.
let signature_ok = slashing.signature_is_still_valid(&head_state.fork());
// Slashings that don't slash any validators can also be dropped.
let slashing_ok =
get_slashable_indices_modular(head_state, slashing.as_inner(), |_, validator| {
let slashing_ok = get_slashable_indices_modular(
head_state,
slashing.as_inner().to_ref(),
|_, validator| {
// Declare that a validator is still slashable if they have not exited prior
// to the finalized epoch.
//
// We cannot check the `slashed` field since the `head` is not finalized and
// a fork could un-slash someone.
validator.exit_epoch > head_state.finalized_checkpoint().epoch
})
.map_or(false, |indices| !indices.is_empty());
},
)
.map_or(false, |indices| !indices.is_empty());
signature_ok && slashing_ok
});
@@ -907,8 +910,8 @@ mod release_tests {
})
.unwrap();
let att1_indices = get_attesting_indices_from_state(&state, &att1).unwrap();
let att2_indices = get_attesting_indices_from_state(&state, &att2).unwrap();
let att1_indices = get_attesting_indices_from_state(&state, att1.to_ref()).unwrap();
let att2_indices = get_attesting_indices_from_state(&state, att2.to_ref()).unwrap();
let att1_split = SplitAttestation::new(att1.clone(), att1_indices);
let att2_split = SplitAttestation::new(att2.clone(), att2_indices);
@@ -981,7 +984,8 @@ mod release_tests {
for (atts, _) in attestations {
for (att, _) in atts {
let attesting_indices = get_attesting_indices_from_state(&state, &att).unwrap();
let attesting_indices =
get_attesting_indices_from_state(&state, att.to_ref()).unwrap();
op_pool.insert_attestation(att, attesting_indices).unwrap();
}
}
@@ -1051,7 +1055,7 @@ mod release_tests {
for (_, aggregate) in attestations {
let att = aggregate.unwrap().message.aggregate;
let attesting_indices = get_attesting_indices_from_state(&state, &att).unwrap();
let attesting_indices = get_attesting_indices_from_state(&state, att.to_ref()).unwrap();
op_pool
.insert_attestation(att.clone(), attesting_indices.clone())
.unwrap();
@@ -1139,7 +1143,8 @@ mod release_tests {
.collect::<Vec<_>>();
for att in aggs1.into_iter().chain(aggs2.into_iter()) {
let attesting_indices = get_attesting_indices_from_state(&state, &att).unwrap();
let attesting_indices =
get_attesting_indices_from_state(&state, att.to_ref()).unwrap();
op_pool.insert_attestation(att, attesting_indices).unwrap();
}
}
@@ -1211,7 +1216,8 @@ mod release_tests {
.collect::<Vec<_>>();
for att in aggs {
let attesting_indices = get_attesting_indices_from_state(&state, &att).unwrap();
let attesting_indices =
get_attesting_indices_from_state(&state, att.to_ref()).unwrap();
op_pool.insert_attestation(att, attesting_indices).unwrap();
}
};
@@ -1306,7 +1312,8 @@ mod release_tests {
.collect::<Vec<_>>();
for att in aggs {
let attesting_indices = get_attesting_indices_from_state(&state, &att).unwrap();
let attesting_indices =
get_attesting_indices_from_state(&state, att.to_ref()).unwrap();
op_pool.insert_attestation(att, attesting_indices).unwrap();
}
};
@@ -1349,7 +1356,7 @@ mod release_tests {
reward_cache.update(&state).unwrap();
for att in best_attestations {
let attesting_indices = get_attesting_indices_from_state(&state, &att).unwrap();
let attesting_indices = get_attesting_indices_from_state(&state, att.to_ref()).unwrap();
let split_attestation = SplitAttestation::new(att, attesting_indices);
let mut fresh_validators_rewards = AttMaxCover::new(
split_attestation.as_ref(),

View File

@@ -39,9 +39,7 @@ pub struct PersistedOperationPool<E: EthSpec> {
pub attestations: Vec<(Attestation<E>, Vec<u64>)>,
/// Mapping from sync contribution ID to sync contributions and aggregate.
pub sync_contributions: PersistedSyncContributions<E>,
/// [DEPRECATED] Attester slashings.
#[superstruct(only(V5))]
pub attester_slashings_v5: Vec<(AttesterSlashing<E>, ForkVersion)>,
/// TODO(electra): we've made a DB change here!!!
/// Attester slashings.
#[superstruct(only(V12, V14, V15))]
pub attester_slashings: Vec<SigVerifiedOp<AttesterSlashing<E>, E>>,