Skip to content

Commit

Permalink
move the check for is_height_processed forward (#7855)
Browse files Browse the repository at this point in the history
Move the check for is_height_processed before process_block_header. Previously, this check happens after, which means, the node will re-process the block header (which takes a few ms) and re-broadcast an invalid block before drops it. In the case when there are many invalid blocks circulating in the network, this can cause the node to be too busy,
  • Loading branch information
mzhangmzz authored and nikurt committed Oct 25, 2022
1 parent b205542 commit 10a7fcd
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 18 deletions.
5 changes: 5 additions & 0 deletions chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4288,6 +4288,11 @@ impl Chain {
self.blocks_in_processing.contains(hash)
}

#[inline]
pub fn is_height_processed(&self, height: BlockHeight) -> Result<bool, Error> {
self.store.is_height_processed(height)
}

/// Check if can sync with sync_hash
pub fn check_sync_hash_validity(&self, sync_hash: &CryptoHash) -> Result<bool, Error> {
let head = self.head()?;
Expand Down
18 changes: 0 additions & 18 deletions chain/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,24 +817,6 @@ impl Client {
provenance: Provenance,
apply_chunks_done_callback: DoneApplyChunkCallback,
) -> Result<(), near_chain::Error> {
let is_requested = match provenance {
Provenance::PRODUCED | Provenance::SYNC => true,
Provenance::NONE => false,
};
// Drop the block if a) it is not requested, b) we already processed this height,
// c) it is not building on top of current head
if !is_requested
&& block.header().prev_hash()
!= &self
.chain
.head()
.map_or_else(|_| CryptoHash::default(), |tip| tip.last_block_hash)
{
if self.chain.store().is_height_processed(block.header().height())? {
return Ok(());
}
}

let mut block_processing_artifacts = BlockProcessingArtifact::default();

let result = {
Expand Down
17 changes: 17 additions & 0 deletions chain/client/src/client_actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,23 @@ impl ClientActor {
debug!(target: "client", tail_height = tail, "Dropping a block that is too far behind.");
return;
}
// drop the block if a) it is not requested, b) we already processed this height, c) it is not building on top of current head
// Note that this check must happen before process_block where we try to validate block
// header and rebroadcast blocks, otherwise blocks that failed processing could be
// processed and rebroadcasted again and again.
if !was_requested
&& block.header().prev_hash()
!= &self
.client
.chain
.head()
.map_or_else(|_| CryptoHash::default(), |tip| tip.last_block_hash)
{
if self.client.chain.is_height_processed(block.header().height()).unwrap_or_default() {
debug!(target: "client", height = block.header().height(), "Dropping a block because we've seen this height before and we didn't request it");
return;
}
}
let prev_hash = *block.header().prev_hash();
let provenance =
if was_requested { near_chain::Provenance::SYNC } else { near_chain::Provenance::NONE };
Expand Down
3 changes: 3 additions & 0 deletions integration-tests/src/tests/client/process_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2129,6 +2129,9 @@ fn test_sync_hash_validity() {
}

/// Only process one block per height
/// Temporarily disable this test because the is_height_processed check is moved to client actor
/// TODO (Min): refactor client actor receive_block code to move it to client
#[ignore]
#[test]
fn test_not_process_height_twice() {
let mut env = TestEnv::builder(ChainGenesis::test()).build();
Expand Down

0 comments on commit 10a7fcd

Please sign in to comment.