diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index c38e2eb5166..6ad52c1d0b3 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -3820,7 +3820,7 @@ impl BeaconChain { continue; } }; - slasher.accept_attestation(indexed_attestation.clone()); + slasher.accept_attestation(indexed_attestation.clone_as_indexed_attestation()); } } } @@ -5039,6 +5039,72 @@ impl BeaconChain { 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) = 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) + }, + ); + + // 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 { @@ -5051,8 +5117,8 @@ impl BeaconChain { 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, @@ -5072,8 +5138,8 @@ impl BeaconChain { 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 @@ -5099,8 +5165,8 @@ impl BeaconChain { 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 @@ -5131,8 +5197,8 @@ impl BeaconChain { 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 @@ -5165,8 +5231,8 @@ impl BeaconChain { 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 @@ -5203,8 +5269,8 @@ impl BeaconChain { 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 diff --git a/beacon_node/beacon_chain/src/validator_monitor.rs b/beacon_node/beacon_chain/src/validator_monitor.rs index d8937249938..ea0fe46caeb 100644 --- a/beacon_node/beacon_chain/src/validator_monitor.rs +++ b/beacon_node/beacon_chain/src/validator_monitor.rs @@ -26,9 +26,9 @@ use types::consts::altair::{ }; use types::{ Attestation, AttestationData, AttesterSlashingRef, BeaconBlockRef, BeaconState, - BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, ProposerSlashing, - PublicKeyBytes, SignedAggregateAndProof, SignedContributionAndProof, Slot, - SyncCommitteeMessage, VoluntaryExit, + BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, + IndexedAttestationRef, ProposerSlashing, PublicKeyBytes, SignedAggregateAndProof, + SignedContributionAndProof, Slot, SyncCommitteeMessage, VoluntaryExit, }; /// Used for Prometheus labels. @@ -1411,7 +1411,7 @@ impl ValidatorMonitor { /// Note: Blocks that get orphaned will skew the inclusion distance calculation. pub fn register_attestation_in_block( &self, - indexed_attestation: &IndexedAttestation, + indexed_attestation: IndexedAttestationRef<'_, E>, parent_slot: Slot, spec: &ChainSpec, ) { diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index 2c81955f227..613c861cbfc 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -15,8 +15,8 @@ use std::time::Duration; use types::{ consts::merge::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint, - Epoch, EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestation, RelativeEpoch, - SignedBeaconBlock, Slot, + Epoch, EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestation, + IndexedAttestationRef, RelativeEpoch, SignedBeaconBlock, Slot, }; #[derive(Debug)] @@ -1087,7 +1087,7 @@ where /// /// We assume that the attester slashing provided to this function has already been verified. pub fn on_attester_slashing(&mut self, slashing: AttesterSlashingRef<'_, E>) { - let attesting_indices_set = |att: &IndexedAttestation| { + let attesting_indices_set = |att: IndexedAttestationRef<'_, E>| { att.attesting_indices_iter() .copied() .collect::>() diff --git a/consensus/state_processing/src/consensus_context.rs b/consensus/state_processing/src/consensus_context.rs index b1196c942d3..b28f218fc8a 100644 --- a/consensus/state_processing/src/consensus_context.rs +++ b/consensus/state_processing/src/consensus_context.rs @@ -4,8 +4,9 @@ use crate::EpochCacheError; use std::collections::{hash_map::Entry, HashMap}; use tree_hash::TreeHash; use types::{ - AbstractExecPayload, Attestation, AttestationData, BeaconState, BeaconStateError, BitList, - ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, SignedBeaconBlock, Slot, + AbstractExecPayload, AttestationData, AttestationRef, BeaconState, BeaconStateError, BitList, + ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, IndexedAttestationRef, + SignedBeaconBlock, Slot, }; #[derive(Debug, PartialEq, Clone)] @@ -153,13 +154,13 @@ impl ConsensusContext { } } - pub fn get_indexed_attestation( - &mut self, + pub fn get_indexed_attestation<'a>( + &'a mut self, state: &BeaconState, - attestation: &Attestation, - ) -> Result<&IndexedAttestation, BlockOperationError> { + attestation: AttestationRef<'a, E>, + ) -> Result, BlockOperationError> { let aggregation_bits = match attestation { - Attestation::Base(attn) => { + AttestationRef::Base(attn) => { let mut extended_aggregation_bits: BitList = BitList::with_capacity(attn.aggregation_bits.len()) .map_err(BeaconStateError::from)?; @@ -171,7 +172,7 @@ impl ConsensusContext { } extended_aggregation_bits } - Attestation::Electra(attn) => attn.aggregation_bits.clone(), + AttestationRef::Electra(attn) => attn.aggregation_bits.clone(), }; let key = (attestation.data().clone(), aggregation_bits); @@ -186,6 +187,7 @@ impl ConsensusContext { Ok(vacant.insert(indexed_attestation)) } } + .map(|indexed_attestation| (*indexed_attestation).to_ref()) } pub fn num_cached_indexed_attestations(&self) -> usize { diff --git a/consensus/state_processing/src/per_block_processing/block_signature_verifier.rs b/consensus/state_processing/src/per_block_processing/block_signature_verifier.rs index d59f5893ddf..74477f5e481 100644 --- a/consensus/state_processing/src/per_block_processing/block_signature_verifier.rs +++ b/consensus/state_processing/src/per_block_processing/block_signature_verifier.rs @@ -276,13 +276,12 @@ where ) -> Result<()> { self.sets .sets - .reserve(block.message().body().attestations().len()); + .reserve(block.message().body().attestations_len()); block .message() .body() .attestations() - .iter() .try_for_each(|attestation| { let indexed_attestation = ctxt.get_indexed_attestation(self.state, attestation)?; diff --git a/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs b/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs index 7c7c9e474ac..30e5f2679ca 100644 --- a/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs @@ -11,9 +11,9 @@ fn error(reason: Invalid) -> BlockOperationError { } /// Verify an `IndexedAttestation`. -pub fn is_valid_indexed_attestation( +pub fn is_valid_indexed_attestation<'a, E: EthSpec>( state: &BeaconState, - indexed_attestation: &IndexedAttestation, + indexed_attestation: IndexedAttestationRef<'a, E>, verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<()> { diff --git a/consensus/state_processing/src/per_block_processing/signature_sets.rs b/consensus/state_processing/src/per_block_processing/signature_sets.rs index a59bc9d0775..f19714dc193 100644 --- a/consensus/state_processing/src/per_block_processing/signature_sets.rs +++ b/consensus/state_processing/src/per_block_processing/signature_sets.rs @@ -9,8 +9,8 @@ use tree_hash::TreeHash; use types::{ AbstractExecPayload, AggregateSignature, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, - InconsistentFork, IndexedAttestation, ProposerSlashing, PublicKey, PublicKeyBytes, Signature, - SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, + InconsistentFork, IndexedAttestation, IndexedAttestationRef, ProposerSlashing, PublicKey, + PublicKeyBytes, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlsToExecutionChange, SignedContributionAndProof, SignedRoot, SignedVoluntaryExit, SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData, Unsigned, }; @@ -272,7 +272,7 @@ pub fn indexed_attestation_signature_set<'a, 'b, E, F>( state: &'a BeaconState, get_pubkey: F, signature: &'a AggregateSignature, - indexed_attestation: &'b IndexedAttestation, + indexed_attestation: IndexedAttestationRef<'b, E>, spec: &'a ChainSpec, ) -> Result> where @@ -346,14 +346,14 @@ where indexed_attestation_signature_set( state, get_pubkey.clone(), - &attester_slashing.attestation_1().signature, + attester_slashing.attestation_1().signature(), attester_slashing.attestation_1(), spec, )?, indexed_attestation_signature_set( state, get_pubkey, - &attester_slashing.attestation_2().signature, + attester_slashing.attestation_2().signature(), attester_slashing.attestation_2(), spec, )?, diff --git a/consensus/state_processing/src/per_block_processing/verify_attestation.rs b/consensus/state_processing/src/per_block_processing/verify_attestation.rs index 8369f988f73..2a6e1f630f3 100644 --- a/consensus/state_processing/src/per_block_processing/verify_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_attestation.rs @@ -21,7 +21,7 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>( ctxt: &'ctxt mut ConsensusContext, verify_signatures: VerifySignatures, spec: &ChainSpec, -) -> Result<&'ctxt IndexedAttestation> { +) -> Result> { let data = attestation.data(); verify!( @@ -65,7 +65,7 @@ pub fn verify_attestation_for_state<'ctxt, E: EthSpec>( ctxt: &'ctxt mut ConsensusContext, verify_signatures: VerifySignatures, spec: &ChainSpec, -) -> Result<&'ctxt IndexedAttestation> { +) -> Result> { let data = attestation.data(); verify!( diff --git a/consensus/state_processing/src/verify_operation.rs b/consensus/state_processing/src/verify_operation.rs index 84be8e27b3f..c12467fbb57 100644 --- a/consensus/state_processing/src/verify_operation.rs +++ b/consensus/state_processing/src/verify_operation.rs @@ -267,32 +267,6 @@ impl VerifyOperation for AttesterSlashing { } } -impl VerifyOperation for AttesterSlashingElectra { - type Error = AttesterSlashingValidationError; - - fn validate( - self, - state: &BeaconState, - spec: &ChainSpec, - ) -> Result, Self::Error> { - verify_attester_slashing( - state, - AttesterSlashingRef::Electra(&self), - VerifySignatures::True, - spec, - )?; - Ok(SigVerifiedOp::new(self, state)) - } - - #[allow(clippy::arithmetic_side_effects)] - fn verification_epochs(&self) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> { - smallvec![ - self.attestation_1().data().target.epoch, - self.attestation_2().data().target.epoch - ] - } -} - impl VerifyOperation for ProposerSlashing { type Error = ProposerSlashingValidationError; diff --git a/consensus/types/src/attester_slashing.rs b/consensus/types/src/attester_slashing.rs index aa727984586..fd750969554 100644 --- a/consensus/types/src/attester_slashing.rs +++ b/consensus/types/src/attester_slashing.rs @@ -1,4 +1,7 @@ -use crate::{test_utils::TestRandom, EthSpec, IndexedAttestation}; +use crate::indexed_attestation::{ + IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, +}; +use crate::{test_utils::TestRandom, EthSpec}; use derivative::Derivative; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -36,8 +39,15 @@ use tree_hash_derive::TreeHash; #[ssz(enum_behaviour = "transparent")] #[tree_hash(enum_behaviour = "transparent")] pub struct AttesterSlashing { - pub attestation_1: IndexedAttestation, - pub attestation_2: IndexedAttestation, + // TODO(electra) change this to `#[superstruct(flatten)]` when 0.8 is out.. + #[superstruct(only(Base), partial_getter(rename = "attestation_1_base"))] + pub attestation_1: IndexedAttestationBase, + #[superstruct(only(Electra), partial_getter(rename = "attestation_1_electra"))] + pub attestation_1: IndexedAttestationElectra, + #[superstruct(only(Base), partial_getter(rename = "attestation_2_base"))] + pub attestation_2: IndexedAttestationBase, + #[superstruct(only(Electra), partial_getter(rename = "attestation_2_electra"))] + pub attestation_2: IndexedAttestationElectra, } /// This is a copy of the `AttesterSlashing` enum but with `Encode` and `Decode` derived @@ -107,6 +117,52 @@ impl<'a, E: EthSpec> AttesterSlashingRef<'a, E> { } } } + + pub fn attestation_1(&self) -> IndexedAttestationRef<'a, E> { + match self { + AttesterSlashingRef::Base(attester_slashing) => { + IndexedAttestationRef::Base(&attester_slashing.attestation_1) + } + AttesterSlashingRef::Electra(attester_slashing) => { + IndexedAttestationRef::Electra(&attester_slashing.attestation_1) + } + } + } + + pub fn attestation_2(&self) -> IndexedAttestationRef<'a, E> { + match self { + AttesterSlashingRef::Base(attester_slashing) => { + IndexedAttestationRef::Base(&attester_slashing.attestation_2) + } + AttesterSlashingRef::Electra(attester_slashing) => { + IndexedAttestationRef::Electra(&attester_slashing.attestation_2) + } + } + } +} + +impl AttesterSlashing { + pub fn attestation_1(&self) -> IndexedAttestationRef { + match self { + AttesterSlashing::Base(attester_slashing) => { + IndexedAttestationRef::Base(&attester_slashing.attestation_1) + } + AttesterSlashing::Electra(attester_slashing) => { + IndexedAttestationRef::Electra(&attester_slashing.attestation_1) + } + } + } + + pub fn attestation_2(&self) -> IndexedAttestationRef { + match self { + AttesterSlashing::Base(attester_slashing) => { + IndexedAttestationRef::Base(&attester_slashing.attestation_2) + } + AttesterSlashing::Electra(attester_slashing) => { + IndexedAttestationRef::Electra(&attester_slashing.attestation_2) + } + } + } } #[cfg(test)] diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 1c29a957bc9..045cfb0ef5c 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -327,16 +327,15 @@ impl> BeaconBlockBase { message: header, signature: Signature::empty(), }; - let indexed_attestation: IndexedAttestation = - IndexedAttestation::Base(IndexedAttestationBase { - attesting_indices: VariableList::new(vec![ - 0_u64; - E::MaxValidatorsPerCommittee::to_usize() - ]) - .unwrap(), - data: AttestationData::default(), - signature: AggregateSignature::empty(), - }); + let indexed_attestation = IndexedAttestationBase { + attesting_indices: VariableList::new(vec![ + 0_u64; + E::MaxValidatorsPerCommittee::to_usize() + ]) + .unwrap(), + data: AttestationData::default(), + signature: AggregateSignature::empty(), + }; let deposit_data = DepositData { pubkey: PublicKeyBytes::empty(), @@ -354,12 +353,12 @@ impl> BeaconBlockBase { attestation_2: indexed_attestation, }; - let attestation: Attestation = Attestation::Base(AttestationBase { + let attestation = AttestationBase { aggregation_bits: BitList::with_capacity(E::MaxValidatorsPerCommittee::to_usize()) .unwrap(), data: AttestationData::default(), signature: AggregateSignature::empty(), - }); + }; let deposit = Deposit { proof: FixedVector::from_elem(Hash256::zero()), @@ -611,7 +610,8 @@ impl> BeaconBlockElectra let indexed_attestation: IndexedAttestationElectra = IndexedAttestationElectra { attesting_indices: VariableList::new(vec![ 0_u64; - E::MaxValidatorsPerCommitteePerSlot::to_usize() + E::MaxValidatorsPerCommitteePerSlot::to_usize( + ) ]) .unwrap(), data: AttestationData::default(), @@ -626,6 +626,21 @@ impl> BeaconBlockElectra E::max_attester_slashings_electra() ] .into(); + // TODO(electra): check this + let attestation = AttestationElectra { + aggregation_bits: BitList::with_capacity( + E::MaxValidatorsPerCommitteePerSlot::to_usize(), + ) + .unwrap(), + data: AttestationData::default(), + signature: AggregateSignature::empty(), + // TODO(electra): does this actually allocate the size correctly? + committee_bits: BitVector::new(), + }; + let mut attestations_electra = vec![]; + for _ in 0..E::MaxAttestationsElectra::to_usize() { + attestations_electra.push(attestation.clone()); + } let bls_to_execution_changes = vec![ SignedBlsToExecutionChange { @@ -651,7 +666,7 @@ impl> BeaconBlockElectra body: BeaconBlockBodyElectra { proposer_slashings: base_block.body.proposer_slashings, attester_slashings, - attestations: base_block.body.attestations, + attestations: attestations_electra.into(), deposits: base_block.body.deposits, voluntary_exits: base_block.body.voluntary_exits, bls_to_execution_changes, diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index d353b808e41..412886e2215 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -71,7 +71,13 @@ pub struct BeaconBlockBody = FullPay #[superstruct(only(Electra), partial_getter(rename = "attester_slashings_electra"))] pub attester_slashings: VariableList, E::MaxAttesterSlashingsElectra>, - pub attestations: VariableList, E::MaxAttestations>, + #[superstruct( + only(Base, Altair, Merge, Capella, Deneb), + partial_getter(rename = "attestations_base") + )] + pub attestations: VariableList, E::MaxAttestations>, + #[superstruct(only(Electra), partial_getter(rename = "attestations_electra"))] + pub attestations: VariableList, E::MaxAttestationsElectra>, pub deposits: VariableList, pub voluntary_exits: VariableList, #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] @@ -263,6 +269,17 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, .map_or(false, |blobs| !blobs.is_empty()) } + pub fn attestations_len(&self) -> usize { + match self { + Self::Base(body) => body.attestations.len(), + Self::Altair(body) => body.attestations.len(), + Self::Merge(body) => body.attestations.len(), + Self::Capella(body) => body.attestations.len(), + Self::Deneb(body) => body.attestations.len(), + Self::Electra(body) => body.attestations.len(), + } + } + pub fn attester_slashings_len(&self) -> usize { match self { Self::Base(body) => body.attester_slashings.len(), @@ -274,6 +291,17 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, } } + pub fn attestations(&self) -> Box> + 'a> { + match self { + Self::Base(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), + Self::Altair(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), + Self::Merge(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), + Self::Capella(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), + Self::Deneb(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), + Self::Electra(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)), + } + } + pub fn attester_slashings(&self) -> Box> + 'a> { match self { Self::Base(body) => Box::new( @@ -818,6 +846,15 @@ impl BeaconBlockBody { _ => return Err(Error::IndexNotSupported(generalized_index)), }; + let attestations_root = match self { + BeaconBlockBody::Base(_) + | BeaconBlockBody::Altair(_) + | BeaconBlockBody::Merge(_) + | BeaconBlockBody::Capella(_) + | BeaconBlockBody::Deneb(_) => self.attestations_base()?.tree_hash_root(), + BeaconBlockBody::Electra(_) => self.attestations_electra()?.tree_hash_root(), + }; + let attester_slashings_root = match self { BeaconBlockBody::Base(_) | BeaconBlockBody::Altair(_) @@ -833,7 +870,7 @@ impl BeaconBlockBody { self.graffiti().tree_hash_root(), self.proposer_slashings().tree_hash_root(), attester_slashings_root, - self.attestations().tree_hash_root(), + attestations_root, self.deposits().tree_hash_root(), self.voluntary_exits().tree_hash_root(), ]; diff --git a/consensus/types/src/indexed_attestation.rs b/consensus/types/src/indexed_attestation.rs index 4477b236fad..d17d00a3fa8 100644 --- a/consensus/types/src/indexed_attestation.rs +++ b/consensus/types/src/indexed_attestation.rs @@ -69,15 +69,16 @@ impl IndexedAttestation { /// /// Spec v0.12.1 pub fn is_double_vote(&self, other: &Self) -> bool { - self.data().target.epoch == other.data().target.epoch && self.data() != other.data() + // reuse the ref implementation to ensure logic is the same + self.to_ref().is_double_vote(other.to_ref()) } /// Check if ``attestation_data_1`` surrounds ``attestation_data_2``. /// /// Spec v0.12.1 pub fn is_surround_vote(&self, other: &Self) -> bool { - self.data().source.epoch < other.data().source.epoch - && other.data().target.epoch < self.data().target.epoch + // reuse the ref implementation to ensure logic is the same + self.to_ref().is_surround_vote(other.to_ref()) } pub fn attesting_indices_len(&self) -> usize { @@ -116,6 +117,59 @@ impl IndexedAttestation { } } +impl<'a, E: EthSpec> IndexedAttestationRef<'a, E> { + pub fn is_double_vote(&self, other: Self) -> bool { + self.data().target.epoch == other.data().target.epoch && self.data() != other.data() + } + + pub fn is_surround_vote(&self, other: Self) -> bool { + self.data().source.epoch < other.data().source.epoch + && other.data().target.epoch < self.data().target.epoch + } + + pub fn attesting_indices_len(&self) -> usize { + match self { + IndexedAttestationRef::Base(att) => att.attesting_indices.len(), + IndexedAttestationRef::Electra(att) => att.attesting_indices.len(), + } + } + + pub fn attesting_indices_to_vec(&self) -> Vec { + match self { + IndexedAttestationRef::Base(att) => att.attesting_indices.to_vec(), + IndexedAttestationRef::Electra(att) => att.attesting_indices.to_vec(), + } + } + + pub fn attesting_indices_is_empty(&self) -> bool { + match self { + IndexedAttestationRef::Base(att) => att.attesting_indices.is_empty(), + IndexedAttestationRef::Electra(att) => att.attesting_indices.is_empty(), + } + } + + pub fn attesting_indices_iter(&self) -> Iter<'_, u64> { + match self { + IndexedAttestationRef::Base(att) => att.attesting_indices.iter(), + IndexedAttestationRef::Electra(att) => att.attesting_indices.iter(), + } + } + + pub fn attesting_indices_first(&self) -> Option<&u64> { + match self { + IndexedAttestationRef::Base(att) => att.attesting_indices.first(), + IndexedAttestationRef::Electra(att) => att.attesting_indices.first(), + } + } + + pub fn clone_as_indexed_attestation(self) -> IndexedAttestation { + match self { + IndexedAttestationRef::Base(att) => IndexedAttestation::Base(att.clone()), + IndexedAttestationRef::Electra(att) => IndexedAttestation::Electra(att.clone()), + } + } +} + impl Decode for IndexedAttestation { fn is_ssz_fixed_len() -> bool { false diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 5300fbcf9a1..42359f55537 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -115,7 +115,9 @@ use ethereum_types::{H160, H256}; pub use crate::activation_queue::ActivationQueue; pub use crate::aggregate_and_proof::AggregateAndProof; -pub use crate::attestation::{Attestation, Error as AttestationError}; +pub use crate::attestation::{ + Attestation, AttestationBase, AttestationElectra, AttestationRef, Error as AttestationError, +}; pub use crate::attestation_data::AttestationData; pub use crate::attestation_duty::AttestationDuty; pub use crate::attester_slashing::{ @@ -172,7 +174,9 @@ pub use crate::fork_name::{ForkName, InconsistentFork}; pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedResponse}; pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN}; pub use crate::historical_batch::HistoricalBatch; -pub use crate::indexed_attestation::IndexedAttestation; +pub use crate::indexed_attestation::{ + IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, +}; pub use crate::light_client_bootstrap::{ LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, LightClientBootstrapDeneb, diff --git a/slasher/src/lib.rs b/slasher/src/lib.rs index e575b2fd4d3..2577829577e 100644 --- a/slasher/src/lib.rs +++ b/slasher/src/lib.rs @@ -28,8 +28,8 @@ pub use database::{ }; pub use error::Error; -use types::AttesterSlashingBase; -use types::{AttesterSlashing, EthSpec, IndexedAttestation, ProposerSlashing}; +use types::{AttesterSlashing, AttesterSlashingBase, AttesterSlashingElectra}; +use types::{EthSpec, IndexedAttestation, ProposerSlashing}; #[derive(Debug, PartialEq)] pub enum AttesterSlashingStatus { @@ -60,18 +60,48 @@ impl AttesterSlashingStatus { match self { NotSlashable => None, AlreadyDoubleVoted => None, - // TODO(electra): fix this once we superstruct IndexedAttestation (return the correct type) DoubleVote(existing) | SurroundedByExisting(existing) => { - Some(AttesterSlashing::Base(AttesterSlashingBase { - attestation_1: *existing, - attestation_2: new_attestation.clone(), - })) + match (*existing, new_attestation) { + // TODO(electra) - determine when we would convert a Base attestation to Electra / how to handle mismatched attestations here + (IndexedAttestation::Base(existing_att), IndexedAttestation::Base(new_att)) => { + Some(AttesterSlashing::Base(AttesterSlashingBase { + attestation_1: existing_att, + attestation_2: new_att.clone(), + })) + } + ( + IndexedAttestation::Electra(existing_att), + IndexedAttestation::Electra(new_att), + ) => Some(AttesterSlashing::Electra(AttesterSlashingElectra { + attestation_1: existing_att, + attestation_2: new_att.clone(), + })), + _ => panic!("attestations must be of the same type"), + } } // TODO(electra): fix this once we superstruct IndexedAttestation (return the correct type) - SurroundsExisting(existing) => Some(AttesterSlashing::Base(AttesterSlashingBase { + SurroundsExisting(existing) => match (*existing, new_attestation) { + (IndexedAttestation::Base(existing_att), IndexedAttestation::Base(new_att)) => { + Some(AttesterSlashing::Base(AttesterSlashingBase { + attestation_1: new_att.clone(), + attestation_2: existing_att, + })) + } + ( + IndexedAttestation::Electra(existing_att), + IndexedAttestation::Electra(new_att), + ) => Some(AttesterSlashing::Electra(AttesterSlashingElectra { + attestation_1: new_att.clone(), + attestation_2: existing_att, + })), + _ => panic!("attestations must be of the same type"), + }, + /* + Some(AttesterSlashing::Base(AttesterSlashingBase { attestation_1: new_attestation.clone(), attestation_2: *existing, })), + */ } } } diff --git a/slasher/src/test_utils.rs b/slasher/src/test_utils.rs index 60084524c1a..634b4d52113 100644 --- a/slasher/src/test_utils.rs +++ b/slasher/src/test_utils.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; use types::{ indexed_attestation::IndexedAttestationBase, AggregateSignature, AttestationData, - AttesterSlashing, AttesterSlashingBase, BeaconBlockHeader, Checkpoint, Epoch, Hash256, - IndexedAttestation, MainnetEthSpec, Signature, SignedBeaconBlockHeader, Slot, + AttesterSlashing, AttesterSlashingBase, AttesterSlashingElectra, BeaconBlockHeader, Checkpoint, + Epoch, Hash256, IndexedAttestation, MainnetEthSpec, Signature, SignedBeaconBlockHeader, Slot, }; pub type E = MainnetEthSpec; @@ -38,10 +38,21 @@ pub fn att_slashing( attestation_2: &IndexedAttestation, ) -> AttesterSlashing { // TODO(electra): fix this one we superstruct IndexedAttestation (return the correct type) - AttesterSlashing::Base(AttesterSlashingBase { - attestation_1: attestation_1.clone(), - attestation_2: attestation_2.clone(), - }) + match (attestation_1, attestation_2) { + (IndexedAttestation::Base(att1), IndexedAttestation::Base(att2)) => { + AttesterSlashing::Base(AttesterSlashingBase { + attestation_1: att1.clone(), + attestation_2: att2.clone(), + }) + } + (IndexedAttestation::Electra(att1), IndexedAttestation::Electra(att2)) => { + AttesterSlashing::Electra(AttesterSlashingElectra { + attestation_1: att1.clone(), + attestation_2: att2.clone(), + }) + } + _ => panic!("attestations must be of the same type"), + } } pub fn hashset_intersection(