diff --git a/beacon_node/beacon_chain/src/attestation_verification.rs b/beacon_node/beacon_chain/src/attestation_verification.rs index 11eb4bbdb..ddd7f93ca 100644 --- a/beacon_node/beacon_chain/src/attestation_verification.rs +++ b/beacon_node/beacon_chain/src/attestation_verification.rs @@ -399,6 +399,16 @@ impl VerifiedAggregatedAttestation { // We do not queue future attestations for later processing. verify_propagation_slot_range(chain, attestation)?; + // Check the attestation's epoch matches its target. + if attestation.data.slot.epoch(T::EthSpec::slots_per_epoch()) + != attestation.data.target.epoch + { + return Err(Error::InvalidTargetEpoch { + slot: attestation.data.slot, + epoch: attestation.data.target.epoch, + }); + } + // Ensure the valid aggregated attestation has not already been seen locally. let attestation_root = attestation.tree_hash_root(); if chain diff --git a/beacon_node/beacon_chain/tests/attestation_verification.rs b/beacon_node/beacon_chain/tests/attestation_verification.rs index 580307ddc..0b7e7ef8d 100644 --- a/beacon_node/beacon_chain/tests/attestation_verification.rs +++ b/beacon_node/beacon_chain/tests/attestation_verification.rs @@ -276,6 +276,23 @@ fn aggregated_gossip_verification() { && earliest_permissible_slot == current_slot - E::slots_per_epoch() - 1 ); + /* + * The following test ensures: + * + * The aggregate attestation's epoch matches its target -- i.e. `aggregate.data.target.epoch == + * compute_epoch_at_slot(attestation.data.slot)` + * + */ + + assert_invalid!( + "attestation with invalid target epoch", + { + let mut a = valid_aggregate.clone(); + a.message.aggregate.data.target.epoch += 1; + a + }, + AttnError::InvalidTargetEpoch { .. } + ); /* * This is not in the specification for aggregate attestations (only unaggregates), but we * check it anyway to avoid weird edge cases.