mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Optimise and refine SingleAttestation conversion (#6934)
Closes - https://github.com/sigp/lighthouse/issues/6805 - Use a new `WorkEvent::GossipAttestationToConvert` to handle the conversion from `SingleAttestation` to `Attestation` _on_ the beacon processor (prevents a Tokio thread being blocked). - Improve the error handling for single attestations. I think previously we had no ability to reprocess single attestations for unknown blocks -- we would just error. This seemed to be the case in both gossip processing and processing of `SingleAttestation`s from the HTTP API. - Move the `SingleAttestation -> Attestation` conversion function into `beacon_chain` so that it can return the `attestation_verification::Error` type, which has well-defined error handling and peer penalties. The now-unused variants of `types::Attestation::Error` have been removed.
This commit is contained in:
@@ -60,9 +60,9 @@ use std::borrow::Cow;
|
||||
use strum::AsRefStr;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
Attestation, AttestationRef, BeaconCommittee, BeaconStateError::NoCommitteeFound, ChainSpec,
|
||||
CommitteeIndex, Epoch, EthSpec, Hash256, IndexedAttestation, SelectionProof,
|
||||
SignedAggregateAndProof, SingleAttestation, Slot, SubnetId,
|
||||
Attestation, AttestationData, AttestationRef, BeaconCommittee,
|
||||
BeaconStateError::NoCommitteeFound, ChainSpec, CommitteeIndex, Epoch, EthSpec, Hash256,
|
||||
IndexedAttestation, SelectionProof, SignedAggregateAndProof, SingleAttestation, Slot, SubnetId,
|
||||
};
|
||||
|
||||
pub use batch::{batch_verify_aggregated_attestations, batch_verify_unaggregated_attestations};
|
||||
@@ -115,6 +115,17 @@ pub enum Error {
|
||||
///
|
||||
/// The peer has sent an invalid message.
|
||||
AggregatorNotInCommittee { aggregator_index: u64 },
|
||||
/// The `attester_index` for a `SingleAttestation` is not a member of the committee defined
|
||||
/// by its `beacon_block_root`, `committee_index` and `slot`.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// The peer has sent an invalid message.
|
||||
AttesterNotInCommittee {
|
||||
attester_index: u64,
|
||||
committee_index: u64,
|
||||
slot: Slot,
|
||||
},
|
||||
/// The aggregator index refers to a validator index that we have not seen.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
@@ -485,7 +496,11 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
|
||||
// MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance).
|
||||
//
|
||||
// We do not queue future attestations for later processing.
|
||||
verify_propagation_slot_range(&chain.slot_clock, attestation, &chain.spec)?;
|
||||
verify_propagation_slot_range::<_, T::EthSpec>(
|
||||
&chain.slot_clock,
|
||||
attestation.data(),
|
||||
&chain.spec,
|
||||
)?;
|
||||
|
||||
// Check the attestation's epoch matches its target.
|
||||
if attestation.data().slot.epoch(T::EthSpec::slots_per_epoch())
|
||||
@@ -817,7 +832,11 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
|
||||
// MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance).
|
||||
//
|
||||
// We do not queue future attestations for later processing.
|
||||
verify_propagation_slot_range(&chain.slot_clock, attestation, &chain.spec)?;
|
||||
verify_propagation_slot_range::<_, T::EthSpec>(
|
||||
&chain.slot_clock,
|
||||
attestation.data(),
|
||||
&chain.spec,
|
||||
)?;
|
||||
|
||||
// Check to ensure that the attestation is "unaggregated". I.e., it has exactly one
|
||||
// aggregation bit set.
|
||||
@@ -1133,10 +1152,10 @@ fn verify_head_block_is_known<T: BeaconChainTypes>(
|
||||
/// Accounts for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
||||
pub fn verify_propagation_slot_range<S: SlotClock, E: EthSpec>(
|
||||
slot_clock: &S,
|
||||
attestation: AttestationRef<E>,
|
||||
attestation: &AttestationData,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let attestation_slot = attestation.data().slot;
|
||||
let attestation_slot = attestation.slot;
|
||||
let latest_permissible_slot = slot_clock
|
||||
.now_with_future_tolerance(spec.maximum_gossip_clock_disparity())
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||
|
||||
@@ -54,6 +54,7 @@ mod pre_finalization_cache;
|
||||
pub mod proposer_prep_service;
|
||||
pub mod schema_change;
|
||||
pub mod shuffling_cache;
|
||||
pub mod single_attestation;
|
||||
pub mod state_advance_timer;
|
||||
pub mod sync_committee_rewards;
|
||||
pub mod sync_committee_verification;
|
||||
|
||||
46
beacon_node/beacon_chain/src/single_attestation.rs
Normal file
46
beacon_node/beacon_chain/src/single_attestation.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use crate::attestation_verification::Error;
|
||||
use types::{Attestation, AttestationElectra, BitList, BitVector, EthSpec, SingleAttestation};
|
||||
|
||||
pub fn single_attestation_to_attestation<E: EthSpec>(
|
||||
single_attestation: &SingleAttestation,
|
||||
committee: &[usize],
|
||||
) -> Result<Attestation<E>, Error> {
|
||||
let attester_index = single_attestation.attester_index;
|
||||
let committee_index = single_attestation.committee_index;
|
||||
let slot = single_attestation.data.slot;
|
||||
|
||||
let aggregation_bit = committee
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, &validator_index)| {
|
||||
if attester_index as usize == validator_index {
|
||||
return Some(i);
|
||||
}
|
||||
None
|
||||
})
|
||||
.ok_or(Error::AttesterNotInCommittee {
|
||||
attester_index,
|
||||
committee_index,
|
||||
slot,
|
||||
})?;
|
||||
|
||||
let mut committee_bits: BitVector<E::MaxCommitteesPerSlot> = BitVector::default();
|
||||
committee_bits
|
||||
.set(committee_index as usize, true)
|
||||
.map_err(|e| Error::Invalid(e.into()))?;
|
||||
|
||||
let mut aggregation_bits =
|
||||
BitList::with_capacity(committee.len()).map_err(|e| Error::Invalid(e.into()))?;
|
||||
aggregation_bits
|
||||
.set(aggregation_bit, true)
|
||||
.map_err(|e| Error::Invalid(e.into()))?;
|
||||
|
||||
// TODO(electra): consider eventually allowing conversion to non-Electra attestations as well
|
||||
// to maintain invertability (`Attestation` -> `SingleAttestation` -> `Attestation`).
|
||||
Ok(Attestation::Electra(AttestationElectra {
|
||||
aggregation_bits,
|
||||
committee_bits,
|
||||
data: single_attestation.data.clone(),
|
||||
signature: single_attestation.signature.clone(),
|
||||
}))
|
||||
}
|
||||
@@ -7,6 +7,7 @@ pub use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||
pub use crate::{
|
||||
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
|
||||
migrate::MigratorConfig,
|
||||
single_attestation::single_attestation_to_attestation,
|
||||
sync_committee_verification::Error as SyncCommitteeError,
|
||||
validator_monitor::{ValidatorMonitor, ValidatorMonitorConfig},
|
||||
BeaconChainError, NotifyExecutionLayer, ProduceBlockVerification,
|
||||
@@ -1133,7 +1134,8 @@ where
|
||||
let single_attestation =
|
||||
attestation.to_single_attestation_with_attester_index(attester_index as u64)?;
|
||||
|
||||
let attestation: Attestation<E> = single_attestation.to_attestation(committee.committee)?;
|
||||
let attestation: Attestation<E> =
|
||||
single_attestation_to_attestation(&single_attestation, committee.committee).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
single_attestation.committee_index,
|
||||
|
||||
Reference in New Issue
Block a user