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:
Michael Sproul
2025-02-08 10:18:57 +11:00
committed by GitHub
parent cb117f859d
commit 2bd5bbdffb
10 changed files with 379 additions and 145 deletions

View File

@@ -36,8 +36,8 @@
//! attestations and there's no immediate cause for concern.
use crate::task_spawner::{Priority, TaskSpawner};
use beacon_chain::{
validator_monitor::timestamp_now, AttestationError, BeaconChain, BeaconChainError,
BeaconChainTypes,
single_attestation::single_attestation_to_attestation, validator_monitor::timestamp_now,
AttestationError, BeaconChain, BeaconChainError, BeaconChainTypes,
};
use beacon_processor::work_reprocessing_queue::{QueuedUnaggregate, ReprocessQueueMessage};
use either::Either;
@@ -183,10 +183,10 @@ fn convert_to_attestation<'a, T: BeaconChainTypes>(
chain: &Arc<BeaconChain<T>>,
attestation: &'a Either<Attestation<T::EthSpec>, SingleAttestation>,
) -> Result<Cow<'a, Attestation<T::EthSpec>>, Error> {
let a = match attestation {
Either::Left(a) => Cow::Borrowed(a),
Either::Right(single_attestation) => chain
.with_committee_cache(
match attestation {
Either::Left(a) => Ok(Cow::Borrowed(a)),
Either::Right(single_attestation) => {
let conversion_result = chain.with_committee_cache(
single_attestation.data.target.root,
single_attestation
.data
@@ -197,24 +197,33 @@ fn convert_to_attestation<'a, T: BeaconChainTypes>(
single_attestation.data.slot,
single_attestation.committee_index,
) else {
return Err(BeaconChainError::AttestationError(
types::AttestationError::NoCommitteeForSlotAndIndex {
slot: single_attestation.data.slot,
index: single_attestation.committee_index,
},
));
return Ok(Err(AttestationError::NoCommitteeForSlotAndIndex {
slot: single_attestation.data.slot,
index: single_attestation.committee_index,
}));
};
let attestation =
single_attestation.to_attestation::<T::EthSpec>(committee.committee)?;
Ok(Cow::Owned(attestation))
Ok(single_attestation_to_attestation::<T::EthSpec>(
single_attestation,
committee.committee,
)
.map(Cow::Owned))
},
)
.map_err(Error::FailedConversion)?,
};
Ok(a)
);
match conversion_result {
Ok(Ok(attestation)) => Ok(attestation),
Ok(Err(e)) => Err(Error::Validation(e)),
// Map the error returned by `with_committee_cache` for unknown blocks into the
// `UnknownHeadBlock` error that is gracefully handled.
Err(BeaconChainError::MissingBeaconBlock(beacon_block_root)) => {
Err(Error::Validation(AttestationError::UnknownHeadBlock {
beacon_block_root,
}))
}
Err(e) => Err(Error::FailedConversion(e)),
}
}
}
}
pub async fn publish_attestations<T: BeaconChainTypes>(