Avoid processing redundant RPC blocks (#4179)

## Proposed Changes

We already make some attempts to avoid processing RPC blocks when a block from the same proposer is already being processed through gossip. This PR strengthens that guarantee by using the existing cache for `observed_block_producers` to inform whether an RPC block's processing should be delayed.
This commit is contained in:
Michael Sproul 2023-04-13 07:05:02 +00:00
parent b90c0c3fb1
commit a3669abac5
3 changed files with 49 additions and 3 deletions

View File

@ -352,7 +352,7 @@ pub struct BeaconChain<T: BeaconChainTypes> {
/// in recent epochs.
pub(crate) observed_sync_aggregators: RwLock<ObservedSyncAggregators<T::EthSpec>>,
/// Maintains a record of which validators have proposed blocks for each slot.
pub(crate) observed_block_producers: RwLock<ObservedBlockProducers<T::EthSpec>>,
pub observed_block_producers: RwLock<ObservedBlockProducers<T::EthSpec>>,
/// Maintains a record of which validators have submitted voluntary exits.
pub(crate) observed_voluntary_exits: Mutex<ObservedOperations<SignedVoluntaryExit, T::EthSpec>>,
/// Maintains a record of which validators we've seen proposer slashings for.

View File

@ -56,7 +56,7 @@ pub const QUEUED_ATTESTATION_DELAY: Duration = Duration::from_secs(12);
pub const QUEUED_LIGHT_CLIENT_UPDATE_DELAY: Duration = Duration::from_secs(12);
/// For how long to queue rpc blocks before sending them back for reprocessing.
pub const QUEUED_RPC_BLOCK_DELAY: Duration = Duration::from_secs(3);
pub const QUEUED_RPC_BLOCK_DELAY: Duration = Duration::from_secs(4);
/// Set an arbitrary upper-bound on the number of queued blocks to avoid DoS attacks. The fact that
/// we signature-verify blocks before putting them in the queue *should* protect against this, but
@ -521,7 +521,7 @@ impl<T: BeaconChainTypes> ReprocessQueue<T> {
return;
}
// Queue the block for 1/4th of a slot
// Queue the block for 1/3rd of a slot
self.rpc_block_delay_queue
.insert(rpc_block, QUEUED_RPC_BLOCK_DELAY);
}

View File

@ -83,6 +83,52 @@ impl<T: BeaconChainTypes> Worker<T> {
return;
}
};
// Check if a block from this proposer is already known. If so, defer processing until later
// to avoid wasting time processing duplicates.
let proposal_already_known = self
.chain
.observed_block_producers
.read()
.proposer_has_been_observed(block.message())
.map_err(|e| {
error!(
self.log,
"Failed to check observed proposers";
"error" => ?e,
"source" => "rpc",
"block_root" => %block_root
);
})
.unwrap_or(true);
if proposal_already_known {
debug!(
self.log,
"Delaying processing of duplicate RPC block";
"block_root" => ?block_root,
"proposer" => block.message().proposer_index(),
"slot" => block.slot()
);
// Send message to work reprocess queue to retry the block
let reprocess_msg = ReprocessQueueMessage::RpcBlock(QueuedRpcBlock {
block_root,
block: block.clone(),
process_type,
seen_timestamp,
should_process: true,
});
if reprocess_tx.try_send(reprocess_msg).is_err() {
error!(
self.log,
"Failed to inform block import";
"source" => "rpc",
"block_root" => %block_root
);
}
return;
}
let slot = block.slot();
let parent_root = block.message().parent_root();
let result = self