mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-11 18:04:18 +00:00
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:
@@ -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!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
)?);
|
||||
|
||||
@@ -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
|
||||
)?
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<_>>();
|
||||
|
||||
|
||||
@@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user