diff --git a/beacon_node/beacon_chain/src/attestation_verification.rs b/beacon_node/beacon_chain/src/attestation_verification.rs index c3dea3dbb4..2951057e97 100644 --- a/beacon_node/beacon_chain/src/attestation_verification.rs +++ b/beacon_node/beacon_chain/src/attestation_verification.rs @@ -629,6 +629,7 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> { attesting_indices_electra::get_indexed_attestation( &committees, &signed_aggregate.message.aggregate, + &chain.spec, ) .map_err(|e| BeaconChainError::from(e).into()) } @@ -1364,7 +1365,7 @@ pub fn obtain_indexed_attestation_and_committees_per_slot( } } AttestationRef::Electra(att) => { - attesting_indices_electra::get_indexed_attestation(&committees, att) + attesting_indices_electra::get_indexed_attestation(&committees, att, &chain.spec) .map(|attestation| (attestation, committees_per_slot)) .map_err(|e| { if let BlockOperationError::BeaconStateError(NoCommitteeFound(index)) = e { diff --git a/beacon_node/beacon_chain/src/beacon_block_reward.rs b/beacon_node/beacon_chain/src/beacon_block_reward.rs index e0bb79bf38..def5a3f07f 100644 --- a/beacon_node/beacon_chain/src/beacon_block_reward.rs +++ b/beacon_node/beacon_chain/src/beacon_block_reward.rs @@ -226,7 +226,7 @@ impl BeaconChain { let inclusion_delay = state.slot().safe_sub(attestation.data().slot)?.as_u64(); let sqrt_total_active_balance = SqrtTotalActiveBalance::new(processing_epoch_end.get_total_active_balance()?); - for attester in get_attesting_indices_from_state(state, attestation)? { + for attester in get_attesting_indices_from_state(state, attestation, &self.spec)? { let validator = processing_epoch_end.get_validator(attester as usize)?; if !validator.slashed && !rewarded_attesters.contains(&attester) @@ -281,7 +281,8 @@ impl BeaconChain { &self.spec, )?; - let attesting_indices = get_attesting_indices_from_state(state, attestation)?; + let attesting_indices = + get_attesting_indices_from_state(state, attestation, &self.spec)?; let mut proposer_reward_numerator = 0; for index in attesting_indices { let index = index as usize; diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index fcfa743952..b12f449ae4 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -4202,19 +4202,20 @@ impl BeaconChain { // Attestations. for attestation in block.body().attestations() { - let indexed_attestation = match ctxt.get_indexed_attestation(state, attestation) { - Ok(indexed) => indexed, - Err(e) => { - debug!( - self.log, - "Failed to get indexed attestation"; - "purpose" => "validator monitor", - "attestation_slot" => attestation.data().slot, - "error" => ?e, - ); - continue; - } - }; + let indexed_attestation = + match ctxt.get_indexed_attestation(state, attestation, &self.spec) { + Ok(indexed) => indexed, + Err(e) => { + debug!( + self.log, + "Failed to get indexed attestation"; + "purpose" => "validator monitor", + "attestation_slot" => attestation.data().slot, + "error" => ?e, + ); + continue; + } + }; validator_monitor.register_attestation_in_block( indexed_attestation, parent_block_slot, @@ -4270,7 +4271,7 @@ impl BeaconChain { } } - let indexed_attestation = match ctxt.get_indexed_attestation(state, a) { + let indexed_attestation = match ctxt.get_indexed_attestation(state, a, &self.spec) { Ok(indexed) => indexed, Err(e) => { debug!( @@ -4311,19 +4312,20 @@ impl BeaconChain { ) { if let Some(slasher) = self.slasher.as_ref() { for attestation in block.body().attestations() { - let indexed_attestation = match ctxt.get_indexed_attestation(state, attestation) { - Ok(indexed) => indexed, - Err(e) => { - debug!( - self.log, - "Failed to get indexed attestation"; - "purpose" => "slasher", - "attestation_slot" => attestation.data().slot, - "error" => ?e, - ); - continue; - } - }; + let indexed_attestation = + match ctxt.get_indexed_attestation(state, attestation, &self.spec) { + Ok(indexed) => indexed, + Err(e) => { + debug!( + self.log, + "Failed to get indexed attestation"; + "purpose" => "slasher", + "attestation_slot" => attestation.data().slot, + "error" => ?e, + ); + continue; + } + }; slasher.accept_attestation(indexed_attestation.clone_as_indexed_attestation()); } } @@ -5355,7 +5357,7 @@ impl BeaconChain { for attestation in self.naive_aggregation_pool.read().iter() { let import = |attestation: &Attestation| { let attesting_indices = - get_attesting_indices_from_state(&state, attestation.to_ref())?; + get_attesting_indices_from_state(&state, attestation.to_ref(), &self.spec)?; self.op_pool .insert_attestation(attestation.clone(), attesting_indices) }; diff --git a/beacon_node/beacon_chain/src/block_reward.rs b/beacon_node/beacon_chain/src/block_reward.rs index 69eecc89b8..c606d5579e 100644 --- a/beacon_node/beacon_chain/src/block_reward.rs +++ b/beacon_node/beacon_chain/src/block_reward.rs @@ -28,7 +28,7 @@ impl BeaconChain { .body() .attestations() .map(|att| { - let attesting_indices = get_attesting_indices_from_state(state, att)?; + let attesting_indices = get_attesting_indices_from_state(state, att, &self.spec)?; Ok(SplitAttestation::new( att.clone_as_attestation(), attesting_indices, diff --git a/beacon_node/beacon_chain/src/block_verification.rs b/beacon_node/beacon_chain/src/block_verification.rs index 4c5f53248f..add61a9425 100644 --- a/beacon_node/beacon_chain/src/block_verification.rs +++ b/beacon_node/beacon_chain/src/block_verification.rs @@ -1650,7 +1650,7 @@ impl ExecutionPendingBlock { // Register each attestation in the block with fork choice. for (i, attestation) in block.message().body().attestations().enumerate() { let indexed_attestation = consensus_context - .get_indexed_attestation(&state, attestation) + .get_indexed_attestation(&state, attestation, &chain.spec) .map_err(|e| BlockError::PerBlockProcessingError(e.into_with_index(i)))?; match fork_choice.on_attestation( diff --git a/consensus/state_processing/src/common/get_attesting_indices.rs b/consensus/state_processing/src/common/get_attesting_indices.rs index b131f7679a..4b8d2bf10b 100644 --- a/consensus/state_processing/src/common/get_attesting_indices.rs +++ b/consensus/state_processing/src/common/get_attesting_indices.rs @@ -56,11 +56,13 @@ pub mod attesting_indices_electra { pub fn get_indexed_attestation( committees: &[BeaconCommittee], attestation: &AttestationElectra, + spec: &ChainSpec, ) -> Result, BlockOperationError> { let attesting_indices = get_attesting_indices::( committees, &attestation.aggregation_bits, &attestation.committee_bits, + spec, )?; Ok(IndexedAttestation::Electra(IndexedAttestationElectra { @@ -73,18 +75,45 @@ pub mod attesting_indices_electra { pub fn get_indexed_attestation_from_state( beacon_state: &BeaconState, attestation: &AttestationElectra, + spec: &ChainSpec, ) -> Result, BlockOperationError> { let committees = beacon_state.get_beacon_committees_at_slot(attestation.data.slot)?; - get_indexed_attestation(&committees, attestation) + get_indexed_attestation(&committees, attestation, spec) } /// Shortcut for getting the attesting indices while fetching the committee from the state's cache. pub fn get_attesting_indices_from_state( state: &BeaconState, att: &AttestationElectra, + spec: &ChainSpec, ) -> Result, BeaconStateError> { let committees = state.get_beacon_committees_at_slot(att.data.slot)?; - get_attesting_indices::(&committees, &att.aggregation_bits, &att.committee_bits) + get_attesting_indices::( + &committees, + &att.aggregation_bits, + &att.committee_bits, + spec, + ) + } + + /// Returns a set of the PTC if the attestation slot is post EIP-7732, otherwise returns an empty set. + pub fn get_ptc_set( + committees: &[BeaconCommittee], + spec: &ChainSpec, + ) -> Result, BeaconStateError> { + let attestation_slot = committees + .get(0) + .map(|committee| committee.slot) + .ok_or(Error::NoCommitteeFound(0))?; + if spec + .fork_name_at_slot::(attestation_slot) + .eip7732_enabled() + { + PTC::::from_committees(committees) + .map(|ptc| ptc.into_iter().map(|i| i as u64).collect()) + } else { + Ok(HashSet::new()) + } } /// Returns validator indices which participated in the attestation, sorted by increasing index. @@ -94,9 +123,11 @@ pub mod attesting_indices_electra { committees: &[BeaconCommittee], aggregation_bits: &BitList, committee_bits: &BitVector, + spec: &ChainSpec, ) -> Result, BeaconStateError> { let mut attesting_indices = vec![]; + let ptc_set = get_ptc_set::(committees, spec)?; let committee_indices = get_committee_indices::(committee_bits); let mut committee_offset = 0; @@ -125,6 +156,8 @@ pub mod attesting_indices_electra { } None }) + // EIP-7732: filter out the PTC + .filter(|index| !ptc_set.contains(index)) .collect::>(); attesting_indices.extend(committee_attesters); @@ -156,6 +189,7 @@ pub mod attesting_indices_electra { pub fn get_attesting_indices_from_state( state: &BeaconState, att: AttestationRef, + spec: &ChainSpec, ) -> Result, BeaconStateError> { match att { AttestationRef::Base(att) => { @@ -166,7 +200,7 @@ pub fn get_attesting_indices_from_state( ) } AttestationRef::Electra(att) => { - attesting_indices_electra::get_attesting_indices_from_state::(state, att) + attesting_indices_electra::get_attesting_indices_from_state::(state, att, spec) } } } diff --git a/consensus/state_processing/src/common/get_payload_attesting_indices.rs b/consensus/state_processing/src/common/get_payload_attesting_indices.rs index 499c070a2b..5b51afe0b3 100644 --- a/consensus/state_processing/src/common/get_payload_attesting_indices.rs +++ b/consensus/state_processing/src/common/get_payload_attesting_indices.rs @@ -24,14 +24,14 @@ pub fn get_payload_attesting_indices( ) -> Result, BeaconStateError> { let ptc = state.get_ptc(slot)?; let bitlist = &payload_attestation.aggregation_bits; - if bitlist.len() != ptc.len() { + if bitlist.len() != E::PTCSize::to_usize() { return Err(BeaconStateError::InvalidBitfield); } let mut attesting_indices = Vec::::new(); for (i, index) in ptc.into_iter().enumerate() { if let Ok(true) = bitlist.get(i) { - attesting_indices.push(*index as u64); + attesting_indices.push(index as u64); } } attesting_indices.sort_unstable(); diff --git a/consensus/state_processing/src/consensus_context.rs b/consensus/state_processing/src/consensus_context.rs index eef9c22e60..10c47843bc 100644 --- a/consensus/state_processing/src/consensus_context.rs +++ b/consensus/state_processing/src/consensus_context.rs @@ -161,6 +161,7 @@ impl ConsensusContext { &'a mut self, state: &BeaconState, attestation: AttestationRef<'a, E>, + spec: &ChainSpec, ) -> Result, BlockOperationError> { let key = attestation.tree_hash_root(); match attestation { @@ -177,7 +178,9 @@ impl ConsensusContext { Entry::Occupied(occupied) => Ok(occupied.into_mut()), Entry::Vacant(vacant) => { let indexed_attestation = - attesting_indices_electra::get_indexed_attestation_from_state(state, attn)?; + attesting_indices_electra::get_indexed_attestation_from_state( + state, attn, spec, + )?; Ok(vacant.insert(indexed_attestation)) } }, 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 24cb51d755..ee45205ea2 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 @@ -282,7 +282,8 @@ where .body() .attestations() .try_for_each(|attestation| { - let indexed_attestation = ctxt.get_indexed_attestation(self.state, attestation)?; + let indexed_attestation = + ctxt.get_indexed_attestation(self.state, attestation, self.spec)?; self.sets.push(indexed_attestation_signature_set( self.state, 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 aee9a4e5d7..55149e0bd5 100644 --- a/consensus/state_processing/src/per_block_processing/verify_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_attestation.rs @@ -87,7 +87,7 @@ pub fn verify_attestation_for_state<'ctxt, E: EthSpec>( verify_casper_ffg_vote(attestation, state)?; // Check signature and bitfields - let indexed_attestation = ctxt.get_indexed_attestation(state, attestation)?; + let indexed_attestation = ctxt.get_indexed_attestation(state, attestation, spec)?; is_valid_indexed_attestation(state, indexed_attestation, verify_signatures, spec)?; Ok(indexed_attestation) diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index 13ce8b92f2..52fe695271 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -1843,40 +1843,12 @@ impl BeaconState { /// Get the PTC /// Requires the committee cache to be initialized. - /// TODO(EIP-7732): is it easier to return u64 or usize? /// TODO(EIP-7732): definitely gonna have to cache this.. - pub fn get_ptc(&self, slot: Slot) -> Result, Error> { - // this function is only used here and - // I have no idea where else to put it - fn bit_floor(n: u64) -> u64 { - if n == 0 { - 0 - } else { - 1 << (n.leading_zeros() as u64 ^ 63) - } - } - + pub fn get_ptc(&self, slot: Slot) -> Result, Error> { let committee_cache = self.committee_cache_at_slot(slot)?; - let committee_count_per_slot = committee_cache.committees_per_slot(); - - let committees_per_slot = bit_floor(std::cmp::min( - committee_count_per_slot as u64, - E::PTCSize::to_u64(), - )); - let members_per_committee = - committee_count_per_slot.safe_div(committees_per_slot)? as usize; - let committees = committee_cache.get_beacon_committees_at_slot(slot)?; - let mut validator_indices = Vec::with_capacity(committees_per_slot as usize); - for idx in 0..committees_per_slot { - let beacon_committee = committees - .get(idx as usize) - .ok_or_else(|| Error::InvalidCommitteeIndex(idx))?; - validator_indices - .extend_from_slice(&beacon_committee.committee[..members_per_committee]); - } - Ok(FixedVector::new(validator_indices)?) + PTC::from_committees(&committees) } /// Build all caches (except the tree hash cache), if they need to be built. diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 20af39e3d2..53d2a6f28a 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -67,6 +67,7 @@ pub mod pending_consolidation; pub mod pending_partial_withdrawal; pub mod proposer_preparation_data; pub mod proposer_slashing; +pub mod ptc; pub mod relative_epoch; pub mod selection_proof; pub mod shuffling_id; @@ -236,6 +237,7 @@ pub use crate::preset::{ }; pub use crate::proposer_preparation_data::ProposerPreparationData; pub use crate::proposer_slashing::ProposerSlashing; +pub use crate::ptc::PTC; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::runtime_var_list::RuntimeVariableList; pub use crate::selection_proof::SelectionProof; diff --git a/consensus/types/src/ptc.rs b/consensus/types/src/ptc.rs new file mode 100644 index 0000000000..f9582cf7ce --- /dev/null +++ b/consensus/types/src/ptc.rs @@ -0,0 +1,55 @@ +use crate::*; +use safe_arith::SafeArith; + +/// TODO(EIP-7732): is it easier to return u64 or usize? +#[derive(Clone, Debug, PartialEq)] +pub struct PTC(FixedVector); + +impl PTC { + pub fn from_committees(committees: &[BeaconCommittee]) -> Result { + // this function is only used here and + // I have no idea where else to put it + fn bit_floor(n: u64) -> u64 { + if n == 0 { + 0 + } else { + 1 << (n.leading_zeros() as u64 ^ 63) + } + } + + let committee_count_per_slot = committees.len() as u64; + let committees_per_slot = bit_floor(std::cmp::min( + committee_count_per_slot, + E::PTCSize::to_u64(), + )) as usize; + let members_per_committee = E::PTCSize::to_usize().safe_div(committees_per_slot)?; + + let mut ptc = Vec::with_capacity(E::PTCSize::to_usize()); + for idx in 0..committees_per_slot { + let beacon_committee = committees + .get(idx as usize) + .ok_or_else(|| Error::InvalidCommitteeIndex(idx as u64))?; + ptc.extend_from_slice(&beacon_committee.committee[..members_per_committee]); + } + + Ok(Self(FixedVector::from(ptc))) + } +} + +impl<'a, E: EthSpec> IntoIterator for &'a PTC { + type Item = &'a usize; + type IntoIter = std::slice::Iter<'a, usize>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl IntoIterator for PTC { + type Item = usize; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/lcli/src/indexed_attestations.rs b/lcli/src/indexed_attestations.rs index ccc1417112..d322391a09 100644 --- a/lcli/src/indexed_attestations.rs +++ b/lcli/src/indexed_attestations.rs @@ -39,7 +39,7 @@ pub fn run(matches: &ArgMatches) -> Result<(), String> { attesting_indices_base::get_indexed_attestation(committee.committee, &att) } Attestation::Electra(att) => { - attesting_indices_electra::get_indexed_attestation_from_state(&state, &att) + attesting_indices_electra::get_indexed_attestation_from_state(&state, &att, spec) } }) .collect::, _>>()