Merge parent and current sync lookups (#5655)

* Drop lookup type trait for a simple arg

* Drop reconstructed for processing

* Send parent blocks one by one

* Merge current and parent lookups

* Merge current and parent lookups clean up todos

* Merge current and parent lookups tests

* Merge remote-tracking branch 'origin/unstable' into sync-merged-lookup

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into sync-merged-lookup

* fix compile after merge

* #5655 pr review (#26)

* fix compile after merge

* remove todos, fix typos etc

* fix compile

* stable rng

* delete TODO and unfilled out test

* make download result a struct

* enums instead of bools as params

* fix comment

* Various fixes

* Track ignored child components

* Track dropped lookup reason as metric

* fix test

* add comment describing behavior of avail check error

*  update ordering
This commit is contained in:
Lion - dapplion
2024-05-01 05:12:15 +09:00
committed by GitHub
parent 196d9fd110
commit ce66582c16
17 changed files with 1633 additions and 2503 deletions

View File

@@ -1,21 +1,21 @@
use crate::sync::block_lookups::parent_lookup::PARENT_FAIL_TOLERANCE;
use crate::sync::block_lookups::single_block_lookup::{
LookupRequestError, SingleBlockLookup, SingleLookupRequestState,
};
use crate::sync::block_lookups::{
BlobRequestState, BlockLookups, BlockRequestState, PeerId, SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS,
BlobRequestState, BlockRequestState, PeerId, SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS,
};
use crate::sync::manager::{BlockProcessType, Id, SingleLookupReqId};
use crate::sync::manager::{BlockProcessType, Id, SLOT_IMPORT_TOLERANCE};
use crate::sync::network_context::{
BlobsByRootSingleBlockRequest, BlocksByRootSingleRequest, SyncNetworkContext,
};
use beacon_chain::block_verification_types::RpcBlock;
use beacon_chain::data_availability_checker::ChildComponents;
use beacon_chain::BeaconChainTypes;
use std::sync::Arc;
use std::time::Duration;
use types::blob_sidecar::FixedBlobSidecarList;
use types::{Hash256, SignedBeaconBlock};
use types::SignedBeaconBlock;
use super::single_block_lookup::DownloadResult;
use super::SingleLookupId;
#[derive(Debug, Copy, Clone)]
pub enum ResponseType {
@@ -23,20 +23,15 @@ pub enum ResponseType {
Blob,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum LookupType {
Current,
Parent,
}
/// The maximum depth we will search for a parent block. In principle we should have sync'd any
/// canonical chain to its head once the peer connects. A chain should not appear where it's depth
/// is further back than the most recent head slot.
pub(crate) const PARENT_DEPTH_TOLERANCE: usize = SLOT_IMPORT_TOLERANCE * 2;
impl LookupType {
fn max_attempts(&self) -> u8 {
match self {
LookupType::Current => SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS,
LookupType::Parent => PARENT_FAIL_TOLERANCE,
}
}
}
/// Wrapper around bool to prevent mixing this argument with `BlockIsProcessed`
pub(crate) struct AwaitingParent(pub bool);
/// Wrapper around bool to prevent mixing this argument with `AwaitingParent`
pub(crate) struct BlockIsProcessed(pub bool);
/// This trait unifies common single block lookup functionality across blocks and blobs. This
/// includes making requests, verifying responses, and handling processing results. A
@@ -53,115 +48,68 @@ pub trait RequestState<T: BeaconChainTypes> {
/// The type created after validation.
type VerifiedResponseType: Clone;
/// We convert a `VerifiedResponseType` to this type prior to sending it to the beacon processor.
type ReconstructedResponseType;
/* Request building methods */
/// Construct a new request.
fn build_request(
&mut self,
lookup_type: LookupType,
) -> Result<(PeerId, Self::RequestType), LookupRequestError> {
// Verify and construct request.
self.too_many_attempts(lookup_type)?;
let peer = self.get_peer()?;
let request = self.new_request();
Ok((peer, request))
}
/// Construct a new request and send it.
fn build_request_and_send(
/// Potentially makes progress on this request if it's in a progress-able state
fn continue_request(
&mut self,
id: Id,
lookup_type: LookupType,
awaiting_parent: AwaitingParent,
downloaded_block_expected_blobs: Option<usize>,
block_is_processed: BlockIsProcessed,
cx: &mut SyncNetworkContext<T>,
) -> Result<(), LookupRequestError> {
// Check if request is necessary.
if !self.get_state().is_awaiting_download() {
return Ok(());
// Attempt to progress awaiting downloads
if self.get_state().is_awaiting_download() {
// Verify the current request has not exceeded the maximum number of attempts.
let request_state = self.get_state();
if request_state.failed_attempts() >= SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS {
let cannot_process = request_state.more_failed_processing_attempts();
return Err(LookupRequestError::TooManyAttempts { cannot_process });
}
let peer_id = self
.get_state_mut()
.use_rand_available_peer()
.ok_or(LookupRequestError::NoPeers)?;
// make_request returns true only if a request was made
if self.make_request(id, peer_id, downloaded_block_expected_blobs, cx)? {
self.get_state_mut().on_download_start()?;
}
// Otherwise, attempt to progress awaiting processing
// If this request is awaiting a parent lookup to be processed, do not send for processing.
// The request will be rejected with unknown parent error.
} else if !awaiting_parent.0
&& (block_is_processed.0 || matches!(Self::response_type(), ResponseType::Block))
{
// maybe_start_processing returns Some if state == AwaitingProcess. This pattern is
// useful to conditionally access the result data.
if let Some(result) = self.get_state_mut().maybe_start_processing() {
return Self::send_for_processing(id, result, cx);
}
}
// Construct request.
let (peer_id, request) = self.build_request(lookup_type)?;
// Update request state.
let req_counter = self.get_state_mut().on_download_start(peer_id);
// Make request
let id = SingleLookupReqId {
id,
req_counter,
lookup_type,
};
Self::make_request(id, peer_id, request, cx)
Ok(())
}
/// Verify the current request has not exceeded the maximum number of attempts.
fn too_many_attempts(&self, lookup_type: LookupType) -> Result<(), LookupRequestError> {
let request_state = self.get_state();
if request_state.failed_attempts() >= lookup_type.max_attempts() {
let cannot_process = request_state.more_failed_processing_attempts();
Err(LookupRequestError::TooManyAttempts { cannot_process })
} else {
Ok(())
}
}
/// Get the next peer to request. Draws from the set of peers we think should have both the
/// block and blob first. If that fails, we draw from the set of peers that may have either.
fn get_peer(&mut self) -> Result<PeerId, LookupRequestError> {
self.get_state_mut()
.use_rand_available_peer()
.ok_or(LookupRequestError::NoPeers)
}
/// Initialize `Self::RequestType`.
fn new_request(&self) -> Self::RequestType;
/// Send the request to the network service.
fn make_request(
id: SingleLookupReqId,
&self,
id: Id,
peer_id: PeerId,
request: Self::RequestType,
downloaded_block_expected_blobs: Option<usize>,
cx: &mut SyncNetworkContext<T>,
) -> Result<(), LookupRequestError>;
) -> Result<bool, LookupRequestError>;
/* Response handling methods */
/// A getter for the parent root of the response. Returns an `Option` because we won't know
/// the blob parent if we don't end up getting any blobs in the response.
fn get_parent_root(verified_response: &Self::VerifiedResponseType) -> Option<Hash256>;
/// Caches the verified response in the lookup if necessary. This is only necessary for lookups
/// triggered by `UnknownParent` errors.
fn add_to_child_components(
verified_response: Self::VerifiedResponseType,
components: &mut ChildComponents<T::EthSpec>,
);
/// Convert a verified response to the type we send to the beacon processor.
fn verified_to_reconstructed(
block_root: Hash256,
verified: Self::VerifiedResponseType,
) -> Self::ReconstructedResponseType;
/// Send the response to the beacon processor.
fn send_reconstructed_for_processing(
fn send_for_processing(
id: Id,
bl: &BlockLookups<T>,
block_root: Hash256,
verified: Self::ReconstructedResponseType,
duration: Duration,
result: DownloadResult<Self::VerifiedResponseType>,
cx: &SyncNetworkContext<T>,
) -> Result<(), LookupRequestError>;
/// Register a failure to process the block or blob.
fn register_failure_downloading(&mut self) {
self.get_state_mut().on_download_failure()
}
/* Utility methods */
/// Returns the `ResponseType` associated with this trait implementation. Useful in logging.
@@ -171,64 +119,49 @@ pub trait RequestState<T: BeaconChainTypes> {
fn request_state_mut(request: &mut SingleBlockLookup<T>) -> &mut Self;
/// A getter for a reference to the `SingleLookupRequestState` associated with this trait.
fn get_state(&self) -> &SingleLookupRequestState;
fn get_state(&self) -> &SingleLookupRequestState<Self::VerifiedResponseType>;
/// A getter for a mutable reference to the SingleLookupRequestState associated with this trait.
fn get_state_mut(&mut self) -> &mut SingleLookupRequestState;
fn get_state_mut(&mut self) -> &mut SingleLookupRequestState<Self::VerifiedResponseType>;
}
impl<T: BeaconChainTypes> RequestState<T> for BlockRequestState {
impl<T: BeaconChainTypes> RequestState<T> for BlockRequestState<T::EthSpec> {
type RequestType = BlocksByRootSingleRequest;
type VerifiedResponseType = Arc<SignedBeaconBlock<T::EthSpec>>;
type ReconstructedResponseType = RpcBlock<T::EthSpec>;
fn new_request(&self) -> Self::RequestType {
BlocksByRootSingleRequest(self.requested_block_root)
}
fn make_request(
id: SingleLookupReqId,
&self,
id: SingleLookupId,
peer_id: PeerId,
request: Self::RequestType,
_: Option<usize>,
cx: &mut SyncNetworkContext<T>,
) -> Result<(), LookupRequestError> {
cx.block_lookup_request(id, peer_id, request)
.map_err(LookupRequestError::SendFailed)
) -> Result<bool, LookupRequestError> {
cx.block_lookup_request(
id,
peer_id,
BlocksByRootSingleRequest(self.requested_block_root),
)
.map_err(LookupRequestError::SendFailed)
}
fn get_parent_root(verified_response: &Arc<SignedBeaconBlock<T::EthSpec>>) -> Option<Hash256> {
Some(verified_response.parent_root())
}
fn add_to_child_components(
verified_response: Arc<SignedBeaconBlock<T::EthSpec>>,
components: &mut ChildComponents<T::EthSpec>,
) {
components.merge_block(verified_response);
}
fn verified_to_reconstructed(
block_root: Hash256,
block: Arc<SignedBeaconBlock<T::EthSpec>>,
) -> RpcBlock<T::EthSpec> {
RpcBlock::new_without_blobs(Some(block_root), block)
}
fn send_reconstructed_for_processing(
id: Id,
bl: &BlockLookups<T>,
block_root: Hash256,
constructed: RpcBlock<T::EthSpec>,
duration: Duration,
fn send_for_processing(
id: SingleLookupId,
download_result: DownloadResult<Self::VerifiedResponseType>,
cx: &SyncNetworkContext<T>,
) -> Result<(), LookupRequestError> {
bl.send_block_for_processing(
let DownloadResult {
value,
block_root,
constructed,
duration,
seen_timestamp,
peer_id: _,
} = download_result;
cx.send_block_for_processing(
block_root,
RpcBlock::new_without_blobs(Some(block_root), value),
seen_timestamp,
BlockProcessType::SingleBlock { id },
cx,
)
.map_err(LookupRequestError::SendFailed)
}
fn response_type() -> ResponseType {
@@ -237,73 +170,52 @@ impl<T: BeaconChainTypes> RequestState<T> for BlockRequestState {
fn request_state_mut(request: &mut SingleBlockLookup<T>) -> &mut Self {
&mut request.block_request_state
}
fn get_state(&self) -> &SingleLookupRequestState {
fn get_state(&self) -> &SingleLookupRequestState<Self::VerifiedResponseType> {
&self.state
}
fn get_state_mut(&mut self) -> &mut SingleLookupRequestState {
fn get_state_mut(&mut self) -> &mut SingleLookupRequestState<Self::VerifiedResponseType> {
&mut self.state
}
}
impl<T: BeaconChainTypes> RequestState<T> for BlobRequestState {
impl<T: BeaconChainTypes> RequestState<T> for BlobRequestState<T::EthSpec> {
type RequestType = BlobsByRootSingleBlockRequest;
type VerifiedResponseType = FixedBlobSidecarList<T::EthSpec>;
type ReconstructedResponseType = FixedBlobSidecarList<T::EthSpec>;
fn new_request(&self) -> Self::RequestType {
BlobsByRootSingleBlockRequest {
block_root: self.block_root,
indices: self.requested_ids.indices(),
}
}
fn make_request(
id: SingleLookupReqId,
peer_id: PeerId,
request: Self::RequestType,
cx: &mut SyncNetworkContext<T>,
) -> Result<(), LookupRequestError> {
cx.blob_lookup_request(id, peer_id, request)
.map_err(LookupRequestError::SendFailed)
}
fn get_parent_root(verified_response: &FixedBlobSidecarList<T::EthSpec>) -> Option<Hash256> {
verified_response
.into_iter()
.filter_map(|blob| blob.as_ref())
.map(|blob| blob.block_parent_root())
.next()
}
fn add_to_child_components(
verified_response: FixedBlobSidecarList<T::EthSpec>,
components: &mut ChildComponents<T::EthSpec>,
) {
components.merge_blobs(verified_response);
}
fn verified_to_reconstructed(
_block_root: Hash256,
blobs: FixedBlobSidecarList<T::EthSpec>,
) -> FixedBlobSidecarList<T::EthSpec> {
blobs
}
fn send_reconstructed_for_processing(
&self,
id: Id,
bl: &BlockLookups<T>,
block_root: Hash256,
verified: FixedBlobSidecarList<T::EthSpec>,
duration: Duration,
peer_id: PeerId,
downloaded_block_expected_blobs: Option<usize>,
cx: &mut SyncNetworkContext<T>,
) -> Result<bool, LookupRequestError> {
cx.blob_lookup_request(
id,
peer_id,
self.block_root,
downloaded_block_expected_blobs,
)
.map_err(LookupRequestError::SendFailed)
}
fn send_for_processing(
id: Id,
download_result: DownloadResult<Self::VerifiedResponseType>,
cx: &SyncNetworkContext<T>,
) -> Result<(), LookupRequestError> {
bl.send_blobs_for_processing(
let DownloadResult {
value,
block_root,
verified,
duration,
seen_timestamp,
peer_id: _,
} = download_result;
cx.send_blobs_for_processing(
block_root,
value,
seen_timestamp,
BlockProcessType::SingleBlob { id },
cx,
)
.map_err(LookupRequestError::SendFailed)
}
fn response_type() -> ResponseType {
@@ -312,10 +224,10 @@ impl<T: BeaconChainTypes> RequestState<T> for BlobRequestState {
fn request_state_mut(request: &mut SingleBlockLookup<T>) -> &mut Self {
&mut request.blob_request_state
}
fn get_state(&self) -> &SingleLookupRequestState {
fn get_state(&self) -> &SingleLookupRequestState<Self::VerifiedResponseType> {
&self.state
}
fn get_state_mut(&mut self) -> &mut SingleLookupRequestState {
fn get_state_mut(&mut self) -> &mut SingleLookupRequestState<Self::VerifiedResponseType> {
&mut self.state
}
}