mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-17 21:08:32 +00:00
merge with upstream
This commit is contained in:
@@ -41,11 +41,12 @@
|
||||
use crate::sync::manager::BlockProcessType;
|
||||
use crate::{metrics, service::NetworkMessage, sync::SyncMessage};
|
||||
use beacon_chain::parking_lot::Mutex;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, GossipVerifiedBlock};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, GossipVerifiedBlock, NotifyExecutionLayer};
|
||||
use derivative::Derivative;
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
use futures::task::Poll;
|
||||
use lighthouse_network::rpc::methods::{BlobsByRangeRequest, BlobsByRootRequest};
|
||||
use lighthouse_network::rpc::LightClientBootstrapRequest;
|
||||
use lighthouse_network::{
|
||||
rpc::{BlocksByRangeRequest, BlocksByRootRequest, StatusMessage},
|
||||
Client, MessageId, NetworkGlobals, PeerId, PeerRequestId,
|
||||
@@ -171,6 +172,10 @@ const MAX_BLOCK_AND_BLOBS_BY_ROOTS_QUEUE_LEN: usize = 1_024;
|
||||
/// is activated.
|
||||
const MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN: usize = 16_384;
|
||||
|
||||
/// The maximum number of queued `LightClientBootstrapRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The name of the manager tokio task.
|
||||
const MANAGER_TASK_NAME: &str = "beacon_processor_manager";
|
||||
|
||||
@@ -213,6 +218,7 @@ pub const BLOCKS_BY_RANGE_REQUEST: &str = "blocks_by_range_request";
|
||||
pub const BLOCKS_BY_ROOTS_REQUEST: &str = "blocks_by_roots_request";
|
||||
pub const BLOBS_BY_RANGE_REQUEST: &str = "blobs_by_range_request";
|
||||
pub const BLOBS_BY_ROOTS_REQUEST: &str = "blobs_by_roots_request";
|
||||
pub const LIGHT_CLIENT_BOOTSTRAP_REQUEST: &str = "light_client_bootstrap";
|
||||
pub const UNKNOWN_BLOCK_ATTESTATION: &str = "unknown_block_attestation";
|
||||
pub const UNKNOWN_BLOCK_AGGREGATE: &str = "unknown_block_aggregate";
|
||||
pub const GOSSIP_BLS_TO_EXECUTION_CHANGE: &str = "gossip_bls_to_execution_change";
|
||||
@@ -627,6 +633,22 @@ impl<T: BeaconChainTypes> WorkEvent<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new work event to process `LightClientBootstrap`s from the RPC network.
|
||||
pub fn lightclient_bootstrap_request(
|
||||
peer_id: PeerId,
|
||||
request_id: PeerRequestId,
|
||||
request: LightClientBootstrapRequest,
|
||||
) -> Self {
|
||||
Self {
|
||||
drop_during_sync: true,
|
||||
work: Work::LightClientBootstrapRequest {
|
||||
peer_id,
|
||||
request_id,
|
||||
request,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blobs_by_root_request(
|
||||
peer_id: PeerId,
|
||||
request_id: PeerRequestId,
|
||||
@@ -835,6 +857,11 @@ pub enum Work<T: BeaconChainTypes> {
|
||||
peer_id: PeerId,
|
||||
bls_to_execution_change: Box<SignedBlsToExecutionChange>,
|
||||
},
|
||||
LightClientBootstrapRequest {
|
||||
peer_id: PeerId,
|
||||
request_id: PeerRequestId,
|
||||
request: LightClientBootstrapRequest,
|
||||
},
|
||||
BlobsByRootsRequest {
|
||||
peer_id: PeerId,
|
||||
request_id: PeerRequestId,
|
||||
@@ -865,6 +892,7 @@ impl<T: BeaconChainTypes> Work<T> {
|
||||
Work::BlocksByRootsRequest { .. } => BLOCKS_BY_ROOTS_REQUEST,
|
||||
Work::BlobsByRangeRequest { .. } => BLOBS_BY_RANGE_REQUEST,
|
||||
Work::BlobsByRootsRequest { .. } => BLOBS_BY_ROOTS_REQUEST,
|
||||
Work::LightClientBootstrapRequest { .. } => LIGHT_CLIENT_BOOTSTRAP_REQUEST,
|
||||
Work::UnknownBlockAttestation { .. } => UNKNOWN_BLOCK_ATTESTATION,
|
||||
Work::UnknownBlockAggregate { .. } => UNKNOWN_BLOCK_AGGREGATE,
|
||||
Work::GossipBlsToExecutionChange { .. } => GOSSIP_BLS_TO_EXECUTION_CHANGE,
|
||||
@@ -1017,6 +1045,7 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
let mut gossip_bls_to_execution_change_queue =
|
||||
FifoQueue::new(MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN);
|
||||
|
||||
let mut lcbootstrap_queue = FifoQueue::new(MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN);
|
||||
// Channels for sending work to the re-process scheduler (`work_reprocessing_tx`) and to
|
||||
// receive them back once they are ready (`ready_work_rx`).
|
||||
let (ready_work_tx, ready_work_rx) = mpsc::channel(MAX_SCHEDULED_WORK_QUEUE_LEN);
|
||||
@@ -1266,6 +1295,8 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
} else if let Some(item) = backfill_chain_segment.pop() {
|
||||
self.spawn_worker(item, toolbox);
|
||||
// This statement should always be the final else statement.
|
||||
} else if let Some(item) = lcbootstrap_queue.pop() {
|
||||
self.spawn_worker(item, toolbox);
|
||||
} else {
|
||||
// Let the journal know that a worker is freed and there's nothing else
|
||||
// for it to do.
|
||||
@@ -1372,6 +1403,9 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
Work::BlobsByRangeRequest { .. } => {
|
||||
blbrange_queue.push(work, work_id, &self.log)
|
||||
}
|
||||
Work::LightClientBootstrapRequest { .. } => {
|
||||
lcbootstrap_queue.push(work, work_id, &self.log)
|
||||
}
|
||||
Work::UnknownBlockAttestation { .. } => {
|
||||
unknown_block_attestation_queue.push(work)
|
||||
}
|
||||
@@ -1735,8 +1769,24 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
/*
|
||||
* Verification for a chain segment (multiple blocks).
|
||||
*/
|
||||
Work::ChainSegment { process_id, blocks } => task_spawner
|
||||
.spawn_async(async move { worker.process_chain_segment(process_id, blocks).await }),
|
||||
Work::ChainSegment { process_id, blocks } => {
|
||||
let notify_execution_layer = if self
|
||||
.network_globals
|
||||
.sync_state
|
||||
.read()
|
||||
.is_syncing_finalized()
|
||||
{
|
||||
NotifyExecutionLayer::No
|
||||
} else {
|
||||
NotifyExecutionLayer::Yes
|
||||
};
|
||||
|
||||
task_spawner.spawn_async(async move {
|
||||
worker
|
||||
.process_chain_segment(process_id, blocks, notify_execution_layer)
|
||||
.await
|
||||
})
|
||||
}
|
||||
/*
|
||||
* Processing of Status Messages.
|
||||
*/
|
||||
@@ -1775,7 +1825,6 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
request,
|
||||
)
|
||||
}),
|
||||
|
||||
Work::BlobsByRangeRequest {
|
||||
peer_id,
|
||||
request_id,
|
||||
@@ -1804,6 +1853,16 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
)
|
||||
}),
|
||||
|
||||
/*
|
||||
* Processing of lightclient bootstrap requests from other peers.
|
||||
*/
|
||||
Work::LightClientBootstrapRequest {
|
||||
peer_id,
|
||||
request_id,
|
||||
request,
|
||||
} => task_spawner.spawn_blocking(move || {
|
||||
worker.handle_light_client_bootstrap(peer_id, request_id, request)
|
||||
}),
|
||||
Work::UnknownBlockAttestation {
|
||||
message_id,
|
||||
peer_id,
|
||||
|
||||
@@ -8,7 +8,7 @@ use beacon_chain::{
|
||||
sync_committee_verification::{self, Error as SyncCommitteeError},
|
||||
validator_monitor::get_block_delay_ms,
|
||||
BeaconChainError, BeaconChainTypes, BlockError, CountUnrealized, ForkChoiceError,
|
||||
GossipVerifiedBlock,
|
||||
GossipVerifiedBlock, NotifyExecutionLayer,
|
||||
};
|
||||
use lighthouse_network::{Client, MessageAcceptance, MessageId, PeerAction, PeerId, ReportSource};
|
||||
use slog::{crit, debug, error, info, trace, warn};
|
||||
@@ -797,7 +797,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
| Err(e @ BlockError::BlockIsAlreadyKnown)
|
||||
| Err(e @ BlockError::RepeatProposal { .. })
|
||||
| Err(e @ BlockError::NotFinalizedDescendant { .. }) => {
|
||||
debug!(self.log, "Could not verify block for gossip, ignoring the block";
|
||||
debug!(self.log, "Could not verify block for gossip. Ignoring the block";
|
||||
"error" => %e);
|
||||
// Prevent recurring behaviour by penalizing the peer slightly.
|
||||
self.gossip_penalize_peer(
|
||||
@@ -809,7 +809,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
return None;
|
||||
}
|
||||
Err(ref e @ BlockError::ExecutionPayloadError(ref epe)) if !epe.penalize_peer() => {
|
||||
debug!(self.log, "Could not verify block for gossip, ignoring the block";
|
||||
debug!(self.log, "Could not verify block for gossip. Ignoring the block";
|
||||
"error" => %e);
|
||||
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
|
||||
return None;
|
||||
@@ -831,7 +831,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
// TODO(merge): reconsider peer scoring for this event.
|
||||
| Err(e @ BlockError::ParentExecutionPayloadInvalid { .. })
|
||||
| Err(e @ BlockError::GenesisBlock) => {
|
||||
warn!(self.log, "Could not verify block for gossip, rejecting the block";
|
||||
warn!(self.log, "Could not verify block for gossip. Rejecting the block";
|
||||
"error" => %e);
|
||||
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject);
|
||||
self.gossip_penalize_peer(
|
||||
@@ -939,7 +939,12 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
|
||||
match self
|
||||
.chain
|
||||
.process_block(block_root, verified_block, CountUnrealized::True)
|
||||
.process_block(
|
||||
block_root,
|
||||
verified_block,
|
||||
CountUnrealized::True,
|
||||
NotifyExecutionLayer::Yes,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(block_root) => {
|
||||
|
||||
@@ -38,7 +38,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
/// Creates a log if there is an internal error.
|
||||
fn send_network_message(&self, message: NetworkMessage<T::EthSpec>) {
|
||||
self.network_tx.send(message).unwrap_or_else(|e| {
|
||||
debug!(self.log, "Could not send message to the network service, likely shutdown";
|
||||
debug!(self.log, "Could not send message to the network service. Likely shutdown";
|
||||
"error" => %e)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use slot_clock::SlotClock;
|
||||
use ssz_types::VariableList;
|
||||
use std::sync::Arc;
|
||||
use task_executor::TaskExecutor;
|
||||
use types::light_client_bootstrap::LightClientBootstrap;
|
||||
use types::{Epoch, EthSpec, Hash256, SignedBeaconBlockAndBlobsSidecar, Slot};
|
||||
|
||||
use super::Worker;
|
||||
@@ -308,6 +309,79 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Handle a `BlocksByRoot` request from the peer.
|
||||
pub fn handle_light_client_bootstrap(
|
||||
self,
|
||||
peer_id: PeerId,
|
||||
request_id: PeerRequestId,
|
||||
request: LightClientBootstrapRequest,
|
||||
) {
|
||||
let block_root = request.root;
|
||||
let state_root = match self.chain.get_blinded_block(&block_root) {
|
||||
Ok(signed_block) => match signed_block {
|
||||
Some(signed_block) => signed_block.state_root(),
|
||||
None => {
|
||||
self.send_error_response(
|
||||
peer_id,
|
||||
RPCResponseErrorCode::ResourceUnavailable,
|
||||
"Bootstrap not avaiable".into(),
|
||||
request_id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.send_error_response(
|
||||
peer_id,
|
||||
RPCResponseErrorCode::ResourceUnavailable,
|
||||
"Bootstrap not avaiable".into(),
|
||||
request_id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut beacon_state = match self.chain.get_state(&state_root, None) {
|
||||
Ok(beacon_state) => match beacon_state {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
self.send_error_response(
|
||||
peer_id,
|
||||
RPCResponseErrorCode::ResourceUnavailable,
|
||||
"Bootstrap not avaiable".into(),
|
||||
request_id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.send_error_response(
|
||||
peer_id,
|
||||
RPCResponseErrorCode::ResourceUnavailable,
|
||||
"Bootstrap not avaiable".into(),
|
||||
request_id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let bootstrap = match LightClientBootstrap::from_beacon_state(&mut beacon_state) {
|
||||
Ok(bootstrap) => bootstrap,
|
||||
Err(_) => {
|
||||
self.send_error_response(
|
||||
peer_id,
|
||||
RPCResponseErrorCode::ResourceUnavailable,
|
||||
"Bootstrap not avaiable".into(),
|
||||
request_id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.send_response(
|
||||
peer_id,
|
||||
Response::LightClientBootstrap(bootstrap),
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
/// Handle a `BlocksByRange` request from the peer.
|
||||
pub fn handle_blocks_by_range_request(
|
||||
self,
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::sync::{BatchProcessResult, ChainId};
|
||||
use beacon_chain::CountUnrealized;
|
||||
use beacon_chain::{
|
||||
BeaconChainError, BeaconChainTypes, BlockError, ChainSegmentResult, HistoricalBlockError,
|
||||
NotifyExecutionLayer,
|
||||
};
|
||||
use lighthouse_network::PeerAction;
|
||||
use slog::{debug, error, info, warn};
|
||||
@@ -89,7 +90,12 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
let slot = block.slot();
|
||||
let result = self
|
||||
.chain
|
||||
.process_block(block_root, block, CountUnrealized::True)
|
||||
.process_block(
|
||||
block_root,
|
||||
block,
|
||||
CountUnrealized::True,
|
||||
NotifyExecutionLayer::Yes,
|
||||
)
|
||||
.await;
|
||||
|
||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_RPC_BLOCK_IMPORTED_TOTAL);
|
||||
@@ -131,6 +137,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
&self,
|
||||
sync_type: ChainSegmentProcessId,
|
||||
downloaded_blocks: Vec<BlockWrapper<T::EthSpec>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) {
|
||||
let result = match sync_type {
|
||||
// this a request from the range sync
|
||||
@@ -140,7 +147,11 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
let sent_blocks = downloaded_blocks.len();
|
||||
|
||||
match self
|
||||
.process_blocks(downloaded_blocks.iter(), count_unrealized)
|
||||
.process_blocks(
|
||||
downloaded_blocks.iter(),
|
||||
count_unrealized,
|
||||
notify_execution_layer,
|
||||
)
|
||||
.await
|
||||
{
|
||||
(_, Ok(_)) => {
|
||||
@@ -230,7 +241,11 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
// parent blocks are ordered from highest slot to lowest, so we need to process in
|
||||
// reverse
|
||||
match self
|
||||
.process_blocks(downloaded_blocks.iter().rev(), CountUnrealized::True)
|
||||
.process_blocks(
|
||||
downloaded_blocks.iter().rev(),
|
||||
CountUnrealized::True,
|
||||
notify_execution_layer,
|
||||
)
|
||||
.await
|
||||
{
|
||||
(imported_blocks, Err(e)) => {
|
||||
@@ -261,11 +276,12 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
&self,
|
||||
downloaded_blocks: impl Iterator<Item = &'a BlockWrapper<T::EthSpec>>,
|
||||
count_unrealized: CountUnrealized,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> (usize, Result<(), ChainSegmentFailed>) {
|
||||
let blocks: Vec<_> = downloaded_blocks.cloned().collect();
|
||||
match self
|
||||
.chain
|
||||
.process_chain_segment(blocks, count_unrealized)
|
||||
.process_chain_segment(blocks, count_unrealized, notify_execution_layer)
|
||||
.await
|
||||
{
|
||||
ChainSegmentResult::Successful { imported_blocks } => {
|
||||
@@ -443,7 +459,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
debug!(
|
||||
self.log, "Block is slightly ahead of our slot clock, ignoring.";
|
||||
self.log, "Block is slightly ahead of our slot clock. Ignoring.";
|
||||
"present_slot" => present_slot,
|
||||
"block_slot" => block_slot,
|
||||
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
|
||||
|
||||
@@ -174,6 +174,9 @@ impl<T: BeaconChainTypes> Router<T> {
|
||||
Request::BlobsByRoot(request) => self
|
||||
.processor
|
||||
.on_blobs_by_root_request(peer_id, id, request),
|
||||
Request::LightClientBootstrap(request) => self
|
||||
.processor
|
||||
.on_lightclient_bootstrap(peer_id, id, request),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +209,7 @@ impl<T: BeaconChainTypes> Router<T> {
|
||||
self.processor
|
||||
.on_blobs_by_root_response(peer_id, request_id, beacon_blob);
|
||||
}
|
||||
Response::LightClientBootstrap(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,18 @@ impl<T: BeaconChainTypes> Processor<T> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Handle a `LightClientBootstrap` request from the peer.
|
||||
pub fn on_lightclient_bootstrap(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
request_id: PeerRequestId,
|
||||
request: LightClientBootstrapRequest,
|
||||
) {
|
||||
self.send_beacon_processor_work(BeaconWorkEvent::lightclient_bootstrap_request(
|
||||
peer_id, request_id, request,
|
||||
))
|
||||
}
|
||||
|
||||
/// Handle a `BlocksByRange` request from the peer.
|
||||
pub fn on_blocks_by_range_request(
|
||||
&mut self,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use beacon_chain::{BeaconChainTypes, BlockError};
|
||||
@@ -16,6 +17,7 @@ use types::signed_block_and_blobs::BlockWrapper;
|
||||
use crate::beacon_processor::{ChainSegmentProcessId, WorkEvent};
|
||||
use crate::metrics;
|
||||
|
||||
use self::parent_lookup::PARENT_FAIL_TOLERANCE;
|
||||
use self::{
|
||||
parent_lookup::{ParentLookup, VerifyError},
|
||||
single_block_lookup::SingleBlockRequest,
|
||||
@@ -39,8 +41,11 @@ const FAILED_CHAINS_CACHE_EXPIRY_SECONDS: u64 = 60;
|
||||
const SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS: u8 = 3;
|
||||
|
||||
pub(crate) struct BlockLookups<T: BeaconChainTypes> {
|
||||
/// A collection of parent block lookups.
|
||||
parent_queue: SmallVec<[ParentLookup<T>; 3]>,
|
||||
/// Parent chain lookups being downloaded.
|
||||
parent_lookups: SmallVec<[ParentLookup<T>; 3]>,
|
||||
|
||||
processing_parent_lookups:
|
||||
HashMap<Hash256, (Vec<Hash256>, SingleBlockRequest<PARENT_FAIL_TOLERANCE>)>,
|
||||
|
||||
/// A cache of failed chain lookups to prevent duplicate searches.
|
||||
failed_chains: LRUTimeCache<Hash256>,
|
||||
@@ -58,7 +63,8 @@ pub(crate) struct BlockLookups<T: BeaconChainTypes> {
|
||||
impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
pub fn new(log: Logger) -> Self {
|
||||
Self {
|
||||
parent_queue: Default::default(),
|
||||
parent_lookups: Default::default(),
|
||||
processing_parent_lookups: Default::default(),
|
||||
failed_chains: LRUTimeCache::new(Duration::from_secs(
|
||||
FAILED_CHAINS_CACHE_EXPIRY_SECONDS,
|
||||
)),
|
||||
@@ -81,6 +87,23 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.parent_lookups.iter_mut().any(|parent_req| {
|
||||
parent_req.add_peer(&hash, &peer_id) || parent_req.contains_block(&hash)
|
||||
}) {
|
||||
// If the block was already downloaded, or is being downloaded in this moment, do not
|
||||
// request it.
|
||||
return;
|
||||
}
|
||||
|
||||
if self
|
||||
.processing_parent_lookups
|
||||
.values()
|
||||
.any(|(hashes, _last_parent_request)| hashes.contains(&hash))
|
||||
{
|
||||
// we are already processing this block, ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(
|
||||
self.log,
|
||||
"Searching for block";
|
||||
@@ -123,8 +146,8 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
|
||||
// Make sure this block is not already downloaded, and that neither it or its parent is
|
||||
// being searched for.
|
||||
if self.parent_queue.iter_mut().any(|parent_req| {
|
||||
parent_req.contains_block(block.block())
|
||||
if self.parent_lookups.iter_mut().any(|parent_req| {
|
||||
parent_req.contains_block(&block_root)
|
||||
|| parent_req.add_peer(&block_root, &peer_id)
|
||||
|| parent_req.add_peer(&parent_root, &peer_id)
|
||||
}) {
|
||||
@@ -132,6 +155,15 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
if self
|
||||
.processing_parent_lookups
|
||||
.values()
|
||||
.any(|(hashes, _peers)| hashes.contains(&block_root) || hashes.contains(&parent_root))
|
||||
{
|
||||
// we are already processing this block, ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
let parent_lookup = ParentLookup::new(block_root, block, peer_id);
|
||||
self.request_parent(parent_lookup, cx);
|
||||
}
|
||||
@@ -212,11 +244,11 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
cx: &mut SyncNetworkContext<T>,
|
||||
) {
|
||||
let mut parent_lookup = if let Some(pos) = self
|
||||
.parent_queue
|
||||
.parent_lookups
|
||||
.iter()
|
||||
.position(|request| request.pending_response(id))
|
||||
{
|
||||
self.parent_queue.remove(pos)
|
||||
self.parent_lookups.remove(pos)
|
||||
} else {
|
||||
if block.is_some() {
|
||||
debug!(self.log, "Response for a parent lookup request that was not found"; "peer_id" => %peer_id);
|
||||
@@ -238,13 +270,13 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
self.parent_queue.push(parent_lookup)
|
||||
self.parent_lookups.push(parent_lookup)
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// Request finished successfully, nothing else to do. It will be removed after the
|
||||
// processing result arrives.
|
||||
self.parent_queue.push(parent_lookup);
|
||||
self.parent_lookups.push(parent_lookup);
|
||||
}
|
||||
Err(e) => match e {
|
||||
VerifyError::RootMismatch
|
||||
@@ -281,7 +313,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
|
||||
metrics::set_gauge(
|
||||
&metrics::SYNC_PARENT_BLOCK_LOOKUPS,
|
||||
self.parent_queue.len() as i64,
|
||||
self.parent_lookups.len() as i64,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -329,11 +361,11 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
|
||||
/* Check disconnection for parent lookups */
|
||||
while let Some(pos) = self
|
||||
.parent_queue
|
||||
.parent_lookups
|
||||
.iter_mut()
|
||||
.position(|req| req.check_peer_disconnected(peer_id).is_err())
|
||||
{
|
||||
let parent_lookup = self.parent_queue.remove(pos);
|
||||
let parent_lookup = self.parent_lookups.remove(pos);
|
||||
trace!(self.log, "Parent lookup's peer disconnected"; &parent_lookup);
|
||||
self.request_parent(parent_lookup, cx);
|
||||
}
|
||||
@@ -347,11 +379,11 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
cx: &mut SyncNetworkContext<T>,
|
||||
) {
|
||||
if let Some(pos) = self
|
||||
.parent_queue
|
||||
.parent_lookups
|
||||
.iter()
|
||||
.position(|request| request.pending_response(id))
|
||||
{
|
||||
let mut parent_lookup = self.parent_queue.remove(pos);
|
||||
let mut parent_lookup = self.parent_lookups.remove(pos);
|
||||
parent_lookup.download_failed();
|
||||
trace!(self.log, "Parent lookup request failed"; &parent_lookup);
|
||||
self.request_parent(parent_lookup, cx);
|
||||
@@ -360,7 +392,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
};
|
||||
metrics::set_gauge(
|
||||
&metrics::SYNC_PARENT_BLOCK_LOOKUPS,
|
||||
self.parent_queue.len() as i64,
|
||||
self.parent_lookups.len() as i64,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -475,7 +507,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
cx: &mut SyncNetworkContext<T>,
|
||||
) {
|
||||
let (mut parent_lookup, peer_id) = if let Some((pos, peer)) = self
|
||||
.parent_queue
|
||||
.parent_lookups
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(pos, request)| {
|
||||
@@ -483,7 +515,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
.get_processing_peer(chain_hash)
|
||||
.map(|peer| (pos, peer))
|
||||
}) {
|
||||
(self.parent_queue.remove(pos), peer)
|
||||
(self.parent_lookups.remove(pos), peer)
|
||||
} else {
|
||||
return debug!(self.log, "Process response for a parent lookup request that was not found"; "chain_hash" => %chain_hash);
|
||||
};
|
||||
@@ -525,15 +557,15 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
);
|
||||
}
|
||||
};
|
||||
let chain_hash = parent_lookup.chain_hash();
|
||||
let blocks = parent_lookup.chain_blocks();
|
||||
let (chain_hash, blocks, hashes, request) = parent_lookup.parts_for_processing();
|
||||
let process_id = ChainSegmentProcessId::ParentLookup(chain_hash);
|
||||
|
||||
let work = WorkEvent::chain_segment(process_id, blocks);
|
||||
|
||||
match beacon_processor_send.try_send(work) {
|
||||
Ok(_) => {
|
||||
self.parent_queue.push(parent_lookup);
|
||||
self.processing_parent_lookups
|
||||
.insert(chain_hash, (hashes, request));
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@@ -587,7 +619,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
|
||||
metrics::set_gauge(
|
||||
&metrics::SYNC_PARENT_BLOCK_LOOKUPS,
|
||||
self.parent_queue.len() as i64,
|
||||
self.parent_lookups.len() as i64,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -597,14 +629,11 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
result: BatchProcessResult,
|
||||
cx: &mut SyncNetworkContext<T>,
|
||||
) {
|
||||
let parent_lookup = if let Some(pos) = self
|
||||
.parent_queue
|
||||
.iter()
|
||||
.position(|request| request.chain_hash() == chain_hash)
|
||||
{
|
||||
self.parent_queue.remove(pos)
|
||||
} else {
|
||||
return debug!(self.log, "Chain process response for a parent lookup request that was not found"; "chain_hash" => %chain_hash);
|
||||
let request = match self.processing_parent_lookups.remove(&chain_hash) {
|
||||
Some((_hashes, request)) => request,
|
||||
None => {
|
||||
return debug!(self.log, "Chain process response for a parent lookup request that was not found"; "chain_hash" => %chain_hash, "result" => ?result)
|
||||
}
|
||||
};
|
||||
|
||||
debug!(self.log, "Parent chain processed"; "chain_hash" => %chain_hash, "result" => ?result);
|
||||
@@ -616,8 +645,8 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
imported_blocks: _,
|
||||
penalty,
|
||||
} => {
|
||||
self.failed_chains.insert(parent_lookup.chain_hash());
|
||||
for &peer_id in parent_lookup.used_peers() {
|
||||
self.failed_chains.insert(chain_hash);
|
||||
for peer_id in request.used_peers {
|
||||
cx.report_peer(peer_id, penalty, "parent_chain_failure")
|
||||
}
|
||||
}
|
||||
@@ -628,7 +657,7 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
|
||||
metrics::set_gauge(
|
||||
&metrics::SYNC_PARENT_BLOCK_LOOKUPS,
|
||||
self.parent_queue.len() as i64,
|
||||
self.parent_lookups.len() as i64,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -704,14 +733,14 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
}
|
||||
Ok(_) => {
|
||||
debug!(self.log, "Requesting parent"; &parent_lookup);
|
||||
self.parent_queue.push(parent_lookup)
|
||||
self.parent_lookups.push(parent_lookup)
|
||||
}
|
||||
}
|
||||
|
||||
// We remove and add back again requests so we want this updated regardless of outcome.
|
||||
metrics::set_gauge(
|
||||
&metrics::SYNC_PARENT_BLOCK_LOOKUPS,
|
||||
self.parent_queue.len() as i64,
|
||||
self.parent_lookups.len() as i64,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -722,6 +751,6 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
|
||||
|
||||
/// Drops all the parent chain requests and returns how many requests were dropped.
|
||||
pub fn drop_parent_chain_requests(&mut self) -> usize {
|
||||
self.parent_queue.drain(..).len()
|
||||
self.parent_lookups.drain(..).len()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(crate) struct ParentLookup<T: BeaconChainTypes> {
|
||||
/// The root of the block triggering this parent request.
|
||||
chain_hash: Hash256,
|
||||
/// The blocks that have currently been downloaded.
|
||||
downloaded_blocks: Vec<BlockWrapper<T::EthSpec>>,
|
||||
downloaded_blocks: Vec<RootBlockTuple<T::EthSpec>>,
|
||||
/// Request of the last parent.
|
||||
current_parent_request: SingleBlockRequest<PARENT_FAIL_TOLERANCE>,
|
||||
/// Id of the last parent request.
|
||||
@@ -54,10 +54,10 @@ pub enum RequestError {
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> ParentLookup<T> {
|
||||
pub fn contains_block(&self, block: &SignedBeaconBlock<T::EthSpec>) -> bool {
|
||||
pub fn contains_block(&self, block_root: &Hash256) -> bool {
|
||||
self.downloaded_blocks
|
||||
.iter()
|
||||
.any(|d_block| d_block.block() == block)
|
||||
.any(|(root, _d_block)| root == block_root)
|
||||
}
|
||||
|
||||
pub fn new(block_root: Hash256, block: BlockWrapper<T::EthSpec>, peer_id: PeerId) -> Self {
|
||||
@@ -65,7 +65,7 @@ impl<T: BeaconChainTypes> ParentLookup<T> {
|
||||
|
||||
Self {
|
||||
chain_hash: block_root,
|
||||
downloaded_blocks: vec![block],
|
||||
downloaded_blocks: vec![(block_root, block)],
|
||||
current_parent_request,
|
||||
current_parent_request_id: None,
|
||||
}
|
||||
@@ -97,7 +97,8 @@ impl<T: BeaconChainTypes> ParentLookup<T> {
|
||||
|
||||
pub fn add_block(&mut self, block: BlockWrapper<T::EthSpec>) {
|
||||
let next_parent = block.parent_root();
|
||||
self.downloaded_blocks.push(block);
|
||||
let current_root = self.current_parent_request.hash;
|
||||
self.downloaded_blocks.push((current_root, block));
|
||||
self.current_parent_request.hash = next_parent;
|
||||
self.current_parent_request.state = single_block_lookup::State::AwaitingDownload;
|
||||
self.current_parent_request_id = None;
|
||||
@@ -107,6 +108,32 @@ impl<T: BeaconChainTypes> ParentLookup<T> {
|
||||
self.current_parent_request_id == Some(req_id)
|
||||
}
|
||||
|
||||
/// Consumes the parent request and destructures it into it's parts.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn parts_for_processing(
|
||||
self,
|
||||
) -> (
|
||||
Hash256,
|
||||
Vec<BlockWrapper<T::EthSpec>>,
|
||||
Vec<Hash256>,
|
||||
SingleBlockRequest<PARENT_FAIL_TOLERANCE>,
|
||||
) {
|
||||
let ParentLookup {
|
||||
chain_hash,
|
||||
downloaded_blocks,
|
||||
current_parent_request,
|
||||
current_parent_request_id: _,
|
||||
} = self;
|
||||
let block_count = downloaded_blocks.len();
|
||||
let mut blocks = Vec::with_capacity(block_count);
|
||||
let mut hashes = Vec::with_capacity(block_count);
|
||||
for (hash, block) in downloaded_blocks {
|
||||
blocks.push(block);
|
||||
hashes.push(hash);
|
||||
}
|
||||
(chain_hash, blocks, hashes, current_parent_request)
|
||||
}
|
||||
|
||||
/// Get the parent lookup's chain hash.
|
||||
pub fn chain_hash(&self) -> Hash256 {
|
||||
self.chain_hash
|
||||
@@ -122,10 +149,6 @@ impl<T: BeaconChainTypes> ParentLookup<T> {
|
||||
self.current_parent_request_id = None;
|
||||
}
|
||||
|
||||
pub fn chain_blocks(&mut self) -> Vec<BlockWrapper<T::EthSpec>> {
|
||||
std::mem::take(&mut self.downloaded_blocks)
|
||||
}
|
||||
|
||||
/// Verifies that the received block is what we requested. If so, parent lookup now waits for
|
||||
/// the processing result of the block.
|
||||
pub fn verify_block(
|
||||
|
||||
@@ -255,7 +255,7 @@ fn test_single_block_lookup_becomes_parent_request() {
|
||||
assert_eq!(bl.single_block_lookups.len(), 0);
|
||||
rig.expect_parent_request();
|
||||
rig.expect_empty_network();
|
||||
assert_eq!(bl.parent_queue.len(), 1);
|
||||
assert_eq!(bl.parent_lookups.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -283,7 +283,7 @@ fn test_parent_lookup_happy_path() {
|
||||
was_non_empty: true,
|
||||
};
|
||||
bl.parent_chain_processed(chain_hash, process_result, &mut cx);
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -320,7 +320,7 @@ fn test_parent_lookup_wrong_response() {
|
||||
was_non_empty: true,
|
||||
};
|
||||
bl.parent_chain_processed(chain_hash, process_result, &mut cx);
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -352,7 +352,7 @@ fn test_parent_lookup_empty_response() {
|
||||
was_non_empty: true,
|
||||
};
|
||||
bl.parent_chain_processed(chain_hash, process_result, &mut cx);
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -383,7 +383,7 @@ fn test_parent_lookup_rpc_failure() {
|
||||
was_non_empty: true,
|
||||
};
|
||||
bl.parent_chain_processed(chain_hash, process_result, &mut cx);
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -415,11 +415,11 @@ fn test_parent_lookup_too_many_attempts() {
|
||||
}
|
||||
}
|
||||
if i < parent_lookup::PARENT_FAIL_TOLERANCE {
|
||||
assert_eq!(bl.parent_queue[0].failed_attempts(), dbg!(i));
|
||||
assert_eq!(bl.parent_lookups[0].failed_attempts(), dbg!(i));
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -446,11 +446,11 @@ fn test_parent_lookup_too_many_download_attempts_no_blacklist() {
|
||||
rig.expect_penalty();
|
||||
}
|
||||
if i < parent_lookup::PARENT_FAIL_TOLERANCE {
|
||||
assert_eq!(bl.parent_queue[0].failed_attempts(), dbg!(i));
|
||||
assert_eq!(bl.parent_lookups[0].failed_attempts(), dbg!(i));
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
assert!(!bl.failed_chains.contains(&block_hash));
|
||||
assert!(!bl.failed_chains.contains(&parent.canonical_root()));
|
||||
}
|
||||
@@ -487,7 +487,7 @@ fn test_parent_lookup_too_many_processing_attempts_must_blacklist() {
|
||||
}
|
||||
|
||||
assert!(bl.failed_chains.contains(&block_hash));
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -541,7 +541,7 @@ fn test_parent_lookup_disconnection() {
|
||||
&mut cx,
|
||||
);
|
||||
bl.peer_disconnected(&peer_id, &mut cx);
|
||||
assert!(bl.parent_queue.is_empty());
|
||||
assert!(bl.parent_lookups.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -594,5 +594,78 @@ fn test_parent_lookup_ignored_response() {
|
||||
// Return an Ignored result. The request should be dropped
|
||||
bl.parent_block_processed(chain_hash, BlockProcessResult::Ignored, &mut cx);
|
||||
rig.expect_empty_network();
|
||||
assert_eq!(bl.parent_queue.len(), 0);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
/// This is a regression test.
|
||||
#[test]
|
||||
fn test_same_chain_race_condition() {
|
||||
let (mut bl, mut cx, mut rig) = TestRig::test_setup(Some(Level::Debug));
|
||||
|
||||
#[track_caller]
|
||||
fn parent_lookups_consistency(bl: &BlockLookups<T>) {
|
||||
let hashes: Vec<_> = bl
|
||||
.parent_lookups
|
||||
.iter()
|
||||
.map(|req| req.chain_hash())
|
||||
.collect();
|
||||
let expected = hashes.len();
|
||||
assert_eq!(
|
||||
expected,
|
||||
hashes
|
||||
.into_iter()
|
||||
.collect::<std::collections::HashSet<_>>()
|
||||
.len(),
|
||||
"duplicated chain hashes in parent queue"
|
||||
)
|
||||
}
|
||||
// if we use one or two blocks it will match on the hash or the parent hash, so make a longer
|
||||
// chain.
|
||||
let depth = 4;
|
||||
let mut blocks = Vec::<Arc<SignedBeaconBlock<E>>>::with_capacity(depth);
|
||||
while blocks.len() < depth {
|
||||
let parent = blocks
|
||||
.last()
|
||||
.map(|b| b.canonical_root())
|
||||
.unwrap_or_else(Hash256::random);
|
||||
let block = Arc::new(rig.block_with_parent(parent));
|
||||
blocks.push(block);
|
||||
}
|
||||
|
||||
let peer_id = PeerId::random();
|
||||
let trigger_block = blocks.pop().unwrap();
|
||||
let chain_hash = trigger_block.canonical_root();
|
||||
bl.search_parent(chain_hash, trigger_block.clone(), peer_id, &mut cx);
|
||||
|
||||
for (i, block) in blocks.into_iter().rev().enumerate() {
|
||||
let id = rig.expect_parent_request();
|
||||
// the block
|
||||
bl.parent_lookup_response(id, peer_id, Some(block.clone()), D, &mut cx);
|
||||
// the stream termination
|
||||
bl.parent_lookup_response(id, peer_id, None, D, &mut cx);
|
||||
// the processing request
|
||||
rig.expect_block_process();
|
||||
// the processing result
|
||||
if i + 2 == depth {
|
||||
// one block was removed
|
||||
bl.parent_block_processed(chain_hash, BlockError::BlockIsAlreadyKnown.into(), &mut cx)
|
||||
} else {
|
||||
bl.parent_block_processed(chain_hash, BlockError::ParentUnknown(block).into(), &mut cx)
|
||||
}
|
||||
parent_lookups_consistency(&bl)
|
||||
}
|
||||
|
||||
// Processing succeeds, now the rest of the chain should be sent for processing.
|
||||
rig.expect_parent_chain_process();
|
||||
|
||||
// Try to get this block again while the chain is being processed. We should not request it again.
|
||||
let peer_id = PeerId::random();
|
||||
bl.search_parent(chain_hash, trigger_block, peer_id, &mut cx);
|
||||
parent_lookups_consistency(&bl);
|
||||
|
||||
let process_result = BatchProcessResult::Success {
|
||||
was_non_empty: true,
|
||||
};
|
||||
bl.parent_chain_processed(chain_hash, process_result, &mut cx);
|
||||
assert_eq!(bl.parent_lookups.len(), 0);
|
||||
}
|
||||
|
||||
@@ -710,7 +710,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
|
||||
// Some logs.
|
||||
if dropped_single_blocks_requests > 0 || dropped_parent_chain_requests > 0 {
|
||||
debug!(self.log, "Execution engine not online, dropping active requests.";
|
||||
debug!(self.log, "Execution engine not online. Dropping active requests.";
|
||||
"dropped_single_blocks_requests" => dropped_single_blocks_requests,
|
||||
"dropped_parent_chain_requests" => dropped_parent_chain_requests,
|
||||
);
|
||||
|
||||
@@ -525,7 +525,7 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
source: ReportSource::SyncService,
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
warn!(self.log, "Could not report peer, channel failed");
|
||||
warn!(self.log, "Could not report peer: channel failed");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
msg,
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
warn!(self.log, "Could not report peer, channel failed"; "error"=> %e);
|
||||
warn!(self.log, "Could not report peer: channel failed"; "error"=> %e);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user