Attestation superstruct changes for EIP 7549 (#5644)

* update

* experiment

* superstruct changes

* revert

* superstruct changes

* fix tests

* indexed attestation

* indexed attestation superstruct

* updated TODOs
This commit is contained in:
Eitan Seri-Levi
2024-04-30 19:49:08 +03:00
committed by GitHub
parent 4a48d7b546
commit 3b7132bc0d
56 changed files with 943 additions and 429 deletions

View File

@@ -27,6 +27,12 @@ pub fn get_attesting_indices_from_state<E: EthSpec>(
state: &BeaconState<E>,
att: &Attestation<E>,
) -> Result<Vec<u64>, BeaconStateError> {
let committee = state.get_beacon_committee(att.data.slot, att.data.index)?;
get_attesting_indices::<E>(committee.committee, &att.aggregation_bits)
let committee = state.get_beacon_committee(att.data().slot, att.data().index)?;
match att {
Attestation::Base(att) => {
get_attesting_indices::<E>(committee.committee, &att.aggregation_bits)
}
// TODO(electra) implement get_attesting_indices for electra
Attestation::Electra(_) => todo!(),
}
}

View File

@@ -1,6 +1,6 @@
use super::get_attesting_indices;
use crate::per_block_processing::errors::{AttestationInvalid as Invalid, BlockOperationError};
use types::*;
use types::{indexed_attestation::IndexedAttestationBase, *};
type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
@@ -11,11 +11,15 @@ pub fn get_indexed_attestation<E: EthSpec>(
committee: &[usize],
attestation: &Attestation<E>,
) -> Result<IndexedAttestation<E>> {
let attesting_indices = get_attesting_indices::<E>(committee, &attestation.aggregation_bits)?;
let attesting_indices = match attestation {
Attestation::Base(att) => get_attesting_indices::<E>(committee, &att.aggregation_bits)?,
// TODO(electra) implement get_attesting_indices for electra
Attestation::Electra(_) => todo!(),
};
Ok(IndexedAttestation {
Ok(IndexedAttestation::Base(IndexedAttestationBase {
attesting_indices: VariableList::new(attesting_indices)?,
data: attestation.data.clone(),
signature: attestation.signature.clone(),
})
data: attestation.data().clone(),
signature: attestation.signature().clone(),
}))
}

View File

@@ -21,8 +21,13 @@ pub struct ConsensusContext<E: EthSpec> {
/// Block root of the block at `slot`.
pub current_block_root: Option<Hash256>,
/// Cache of indexed attestations constructed during block processing.
pub indexed_attestations:
HashMap<(AttestationData, BitList<E::MaxValidatorsPerCommittee>), IndexedAttestation<E>>,
pub indexed_attestations: HashMap<
(
AttestationData,
BitList<E::MaxValidatorsPerCommitteePerSlot>,
),
IndexedAttestation<E>,
>,
}
#[derive(Debug, PartialEq, Clone)]
@@ -153,16 +158,29 @@ impl<E: EthSpec> ConsensusContext<E> {
state: &BeaconState<E>,
attestation: &Attestation<E>,
) -> Result<&IndexedAttestation<E>, BlockOperationError<AttestationInvalid>> {
let key = (
attestation.data.clone(),
attestation.aggregation_bits.clone(),
);
let aggregation_bits = match attestation {
Attestation::Base(attn) => {
let mut extended_aggregation_bits: BitList<E::MaxValidatorsPerCommitteePerSlot> =
BitList::with_capacity(attn.aggregation_bits.len())
.map_err(BeaconStateError::from)?;
for (i, bit) in attn.aggregation_bits.iter().enumerate() {
extended_aggregation_bits
.set(i, bit)
.map_err(BeaconStateError::from)?;
}
extended_aggregation_bits
}
Attestation::Electra(attn) => attn.aggregation_bits.clone(),
};
let key = (attestation.data().clone(), aggregation_bits);
match self.indexed_attestations.entry(key) {
Entry::Occupied(occupied) => Ok(occupied.into_mut()),
Entry::Vacant(vacant) => {
let committee =
state.get_beacon_committee(attestation.data.slot, attestation.data.index)?;
let committee = state
.get_beacon_committee(attestation.data().slot, attestation.data().index)?;
let indexed_attestation =
get_indexed_attestation(committee.committee, attestation)?;
Ok(vacant.insert(indexed_attestation))
@@ -178,7 +196,10 @@ impl<E: EthSpec> ConsensusContext<E> {
pub fn set_indexed_attestations(
mut self,
attestations: HashMap<
(AttestationData, BitList<E::MaxValidatorsPerCommittee>),
(
AttestationData,
BitList<E::MaxValidatorsPerCommitteePerSlot>,
),
IndexedAttestation<E>,
>,
) -> Self {

View File

@@ -290,7 +290,7 @@ where
self.sets.push(indexed_attestation_signature_set(
self.state,
self.get_pubkey.clone(),
&attestation.signature,
attestation.signature(),
indexed_attestation,
self.spec,
)?);

View File

@@ -17,7 +17,7 @@ pub fn is_valid_indexed_attestation<E: EthSpec>(
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<()> {
let indices = &indexed_attestation.attesting_indices;
let indices = indexed_attestation.attesting_indices_to_vec();
// Verify that indices aren't empty
verify!(!indices.is_empty(), Invalid::IndicesEmpty);
@@ -36,14 +36,14 @@ pub fn is_valid_indexed_attestation<E: EthSpec>(
})?;
Ok(())
};
check_sorted(indices)?;
check_sorted(&indices)?;
if verify_signatures.is_true() {
verify!(
indexed_attestation_signature_set(
state,
|i| get_pubkey_from_state(state, i),
&indexed_attestation.signature,
indexed_attestation.signature(),
indexed_attestation,
spec
)?

View File

@@ -73,24 +73,33 @@ pub mod base {
)
.map_err(|e| e.into_with_index(i))?;
let pending_attestation = PendingAttestation {
aggregation_bits: attestation.aggregation_bits.clone(),
data: attestation.data.clone(),
inclusion_delay: state.slot().safe_sub(attestation.data.slot)?.as_u64(),
proposer_index,
};
match attestation {
Attestation::Base(att) => {
let pending_attestation = PendingAttestation {
aggregation_bits: att.aggregation_bits.clone(),
data: att.data.clone(),
inclusion_delay: state.slot().safe_sub(att.data.slot)?.as_u64(),
proposer_index,
};
if attestation.data.target.epoch == state.current_epoch() {
state
.as_base_mut()?
.current_epoch_attestations
.push(pending_attestation)?;
} else {
state
.as_base_mut()?
.previous_epoch_attestations
.push(pending_attestation)?;
}
if attestation.data().target.epoch == state.current_epoch() {
state
.as_base_mut()?
.current_epoch_attestations
.push(pending_attestation)?;
} else {
state
.as_base_mut()?
.previous_epoch_attestations
.push(pending_attestation)?;
}
}
Attestation::Electra(_) => {
// TODO(electra) pending attestations are only phase 0
// so we should just raise a relevant error here
todo!()
}
};
}
Ok(())
@@ -128,26 +137,24 @@ pub mod altair_deneb {
let previous_epoch = ctxt.previous_epoch;
let current_epoch = ctxt.current_epoch;
let attesting_indices = verify_attestation_for_block_inclusion(
let indexed_att = verify_attestation_for_block_inclusion(
state,
attestation,
ctxt,
verify_signatures,
spec,
)
.map_err(|e| e.into_with_index(att_index))?
.attesting_indices
.clone();
.map_err(|e| e.into_with_index(att_index))?;
// Matching roots, participation flag indices
let data = &attestation.data;
let data = attestation.data();
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
let participation_flag_indices =
get_attestation_participation_flag_indices(state, data, inclusion_delay, spec)?;
// Update epoch participation flags.
let mut proposer_reward_numerator = 0;
for index in &attesting_indices {
for index in indexed_att.attesting_indices_iter() {
let index = *index as usize;
let validator_effective_balance = state.epoch_cache().get_effective_balance(index)?;

View File

@@ -279,21 +279,21 @@ where
E: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let mut pubkeys = Vec::with_capacity(indexed_attestation.attesting_indices.len());
for &validator_idx in &indexed_attestation.attesting_indices {
let mut pubkeys = Vec::with_capacity(indexed_attestation.attesting_indices_len());
for &validator_idx in indexed_attestation.attesting_indices_iter() {
pubkeys.push(
get_pubkey(validator_idx as usize).ok_or(Error::ValidatorUnknown(validator_idx))?,
);
}
let domain = spec.get_domain(
indexed_attestation.data.target.epoch,
indexed_attestation.data().target.epoch,
Domain::BeaconAttester,
&state.fork(),
state.genesis_validators_root(),
);
let message = indexed_attestation.data.signing_root(domain);
let message = indexed_attestation.data().signing_root(domain);
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
}
@@ -312,21 +312,21 @@ where
E: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let mut pubkeys = Vec::with_capacity(indexed_attestation.attesting_indices.len());
for &validator_idx in &indexed_attestation.attesting_indices {
let mut pubkeys = Vec::with_capacity(indexed_attestation.attesting_indices_len());
for &validator_idx in indexed_attestation.attesting_indices_iter() {
pubkeys.push(
get_pubkey(validator_idx as usize).ok_or(Error::ValidatorUnknown(validator_idx))?,
);
}
let domain = spec.get_domain(
indexed_attestation.data.target.epoch,
indexed_attestation.data().target.epoch,
Domain::BeaconAttester,
fork,
genesis_validators_root,
);
let message = indexed_attestation.data.signing_root(domain);
let message = indexed_attestation.data().signing_root(domain);
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
}
@@ -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,
)?,
@@ -425,7 +425,7 @@ where
E: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let slot = signed_aggregate_and_proof.message.aggregate.data.slot;
let slot = signed_aggregate_and_proof.message.aggregate.data().slot;
let domain = spec.get_domain(
slot.epoch(E::slots_per_epoch()),
@@ -458,7 +458,7 @@ where
let target_epoch = signed_aggregate_and_proof
.message
.aggregate
.data
.data()
.target
.epoch;

View File

@@ -389,7 +389,7 @@ async fn invalid_attestation_no_committee_for_index() {
.deconstruct()
.0;
head_block.to_mut().body_mut().attestations_mut()[0]
.data
.data_mut()
.index += 1;
let mut ctxt = ConsensusContext::new(state.slot());
let result = process_operations::process_attestations(
@@ -423,11 +423,11 @@ async fn invalid_attestation_wrong_justified_checkpoint() {
.clone()
.deconstruct()
.0;
let old_justified_checkpoint = head_block.body().attestations()[0].data.source;
let old_justified_checkpoint = head_block.body().attestations()[0].data().source;
let mut new_justified_checkpoint = old_justified_checkpoint;
new_justified_checkpoint.epoch += Epoch::new(1);
head_block.to_mut().body_mut().attestations_mut()[0]
.data
.data_mut()
.source = new_justified_checkpoint;
let mut ctxt = ConsensusContext::new(state.slot());
@@ -467,8 +467,9 @@ async fn invalid_attestation_bad_aggregation_bitfield_len() {
.clone()
.deconstruct()
.0;
head_block.to_mut().body_mut().attestations_mut()[0].aggregation_bits =
Bitfield::with_capacity(spec.target_committee_size).unwrap();
*head_block.to_mut().body_mut().attestations_mut()[0]
.aggregation_bits_base_mut()
.unwrap() = Bitfield::with_capacity(spec.target_committee_size).unwrap();
let mut ctxt = ConsensusContext::new(state.slot());
let result = process_operations::process_attestations(
@@ -501,7 +502,8 @@ async fn invalid_attestation_bad_signature() {
.clone()
.deconstruct()
.0;
head_block.to_mut().body_mut().attestations_mut()[0].signature = AggregateSignature::empty();
*head_block.to_mut().body_mut().attestations_mut()[0].signature_mut() =
AggregateSignature::empty();
let mut ctxt = ConsensusContext::new(state.slot());
let result = process_operations::process_attestations(
@@ -536,10 +538,10 @@ async fn invalid_attestation_included_too_early() {
.clone()
.deconstruct()
.0;
let new_attesation_slot = head_block.body().attestations()[0].data.slot
let new_attesation_slot = head_block.body().attestations()[0].data().slot
+ Slot::new(MainnetEthSpec::slots_per_epoch());
head_block.to_mut().body_mut().attestations_mut()[0]
.data
.data_mut()
.slot = new_attesation_slot;
let mut ctxt = ConsensusContext::new(state.slot());
@@ -579,10 +581,10 @@ async fn invalid_attestation_included_too_late() {
.clone()
.deconstruct()
.0;
let new_attesation_slot = head_block.body().attestations()[0].data.slot
let new_attesation_slot = head_block.body().attestations()[0].data().slot
- Slot::new(MainnetEthSpec::slots_per_epoch());
head_block.to_mut().body_mut().attestations_mut()[0]
.data
.data_mut()
.slot = new_attesation_slot;
let mut ctxt = ConsensusContext::new(state.slot());
@@ -620,7 +622,7 @@ async fn invalid_attestation_target_epoch_slot_mismatch() {
.deconstruct()
.0;
head_block.to_mut().body_mut().attestations_mut()[0]
.data
.data_mut()
.target
.epoch += Epoch::new(1);
@@ -699,7 +701,10 @@ async fn invalid_attester_slashing_1_invalid() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT).await;
let mut attester_slashing = harness.make_attester_slashing(vec![1, 2]);
attester_slashing.attestation_1.attesting_indices = VariableList::from(vec![2, 1]);
*attester_slashing
.attestation_1
.attesting_indices_base_mut()
.unwrap() = VariableList::from(vec![2, 1]);
let mut state = harness.get_current_state();
let mut ctxt = ConsensusContext::new(state.slot());
@@ -730,7 +735,10 @@ async fn invalid_attester_slashing_2_invalid() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT).await;
let mut attester_slashing = harness.make_attester_slashing(vec![1, 2]);
attester_slashing.attestation_2.attesting_indices = VariableList::from(vec![2, 1]);
*attester_slashing
.attestation_2
.attesting_indices_base_mut()
.unwrap() = VariableList::from(vec![2, 1]);
let mut state = harness.get_current_state();
let mut ctxt = ConsensusContext::new(state.slot());

View File

@@ -22,7 +22,7 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>(
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<&'ctxt IndexedAttestation<E>> {
let data = &attestation.data;
let data = attestation.data();
verify!(
data.slot.safe_add(spec.min_attestation_inclusion_delay)? <= state.slot(),
@@ -66,7 +66,7 @@ pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<&'ctxt IndexedAttestation<E>> {
let data = &attestation.data;
let data = attestation.data();
verify!(
data.index < state.get_committee_count_at_slot(data.slot)?,
@@ -90,7 +90,7 @@ fn verify_casper_ffg_vote<E: EthSpec>(
attestation: &Attestation<E>,
state: &BeaconState<E>,
) -> Result<()> {
let data = &attestation.data;
let data = attestation.data();
verify!(
data.target.epoch == data.slot.epoch(E::slots_per_epoch()),
Invalid::TargetEpochSlotMismatch {

View File

@@ -66,13 +66,12 @@ where
let attestation_2 = &attester_slashing.attestation_2;
let attesting_indices_1 = attestation_1
.attesting_indices
.iter()
.attesting_indices_iter()
.cloned()
.collect::<BTreeSet<_>>();
let attesting_indices_2 = attestation_2
.attesting_indices
.iter()
.attesting_indices_iter()
.cloned()
.collect::<BTreeSet<_>>();

View File

@@ -159,8 +159,8 @@ impl<E: EthSpec> VerifyOperation<E> for AttesterSlashing<E> {
#[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
self.attestation_1.data().target.epoch,
self.attestation_2.data().target.epoch
]
}
}