Do not request blocks below the common number when syncing (#2045)

This changes `BlockCollection` logic so we don't download block ranges
from peers with which we have these ranges already in sync.

Improves situation with
https://github.com/paritytech/polkadot-sdk/issues/1915.
This commit is contained in:
Dmitry Markin
2023-11-03 17:09:01 +02:00
committed by GitHub
parent f6f4c5aac8
commit 8dc41ba49d
3 changed files with 255 additions and 122 deletions
@@ -245,113 +245,6 @@ fn build_block(client: &mut Arc<TestClient>, at: Option<Hash>, fork: bool) -> Bl
block
}
/// This test is a regression test as observed on a real network.
///
/// The node is connected to multiple peers. Both of these peers are having a best block (1)
/// that is below our best block (3). Now peer 2 announces a fork of block 3 that we will
/// request from peer 2. After importing the fork, peer 2 and then peer 1 will announce block 4.
/// But as peer 1 in our view is still at block 1, we will request block 2 (which we already
/// have) from it. In the meanwhile peer 2 sends us block 4 and 3 and we send another request
/// for block 2 to peer 2. Peer 1 answers with block 2 and then peer 2. This will need to
/// succeed, as we have requested block 2 from both peers.
#[test]
fn do_not_report_peer_on_block_response_for_block_request() {
sp_tracing::try_init_simple();
let mut client = Arc::new(TestClientBuilder::new().build());
let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new();
let mut sync = ChainSync::new(
SyncMode::Full,
client.clone(),
ProtocolName::from("test-block-announce-protocol"),
5,
64,
None,
chain_sync_network_handle,
)
.unwrap();
let peer_id1 = PeerId::random();
let peer_id2 = PeerId::random();
let mut client2 = client.clone();
let mut build_block_at = |at, import| {
let mut block_builder = client2.new_block_at(at, Default::default(), false).unwrap();
// Make sure we generate a different block as fork
block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap();
let block = block_builder.build().unwrap().block;
if import {
block_on(client2.import(BlockOrigin::Own, block.clone())).unwrap();
}
block
};
let block1 = build_block(&mut client, None, false);
let block2 = build_block(&mut client, None, false);
let block3 = build_block(&mut client, None, false);
let block3_fork = build_block_at(block2.hash(), false);
// Add two peers which are on block 1.
sync.new_peer(peer_id1, block1.hash(), 1).unwrap();
sync.new_peer(peer_id2, block1.hash(), 1).unwrap();
// Tell sync that our best block is 3.
sync.update_chain_info(&block3.hash(), 3);
// There should be no requests.
assert!(sync.block_requests().is_empty());
// Let peer2 announce a fork of block 3
send_block_announce(block3_fork.header().clone(), peer_id2, &mut sync);
// Import and tell sync that we now have the fork.
block_on(client.import(BlockOrigin::Own, block3_fork.clone())).unwrap();
sync.update_chain_info(&block3_fork.hash(), 3);
let block4 = build_block_at(block3_fork.hash(), false);
// Let peer2 announce block 4 and check that sync wants to get the block.
send_block_announce(block4.header().clone(), peer_id2, &mut sync);
let request = get_block_request(&mut sync, FromBlock::Hash(block4.hash()), 2, &peer_id2);
// Peer1 announces the same block, but as the common block is still `1`, sync will request
// block 2 again.
send_block_announce(block4.header().clone(), peer_id1, &mut sync);
let request2 = get_block_request(&mut sync, FromBlock::Number(2), 1, &peer_id1);
let response = create_block_response(vec![block4.clone(), block3_fork.clone()]);
let res = sync.on_block_data(&peer_id2, Some(request), response).unwrap();
// We should not yet import the blocks, because there is still an open request for fetching
// block `2` which blocks the import.
assert!(
matches!(res, OnBlockData::Import(ImportBlocksAction{ origin: _, blocks }) if blocks.is_empty())
);
let request3 = get_block_request(&mut sync, FromBlock::Number(2), 1, &peer_id2);
let response = create_block_response(vec![block2.clone()]);
let res = sync.on_block_data(&peer_id1, Some(request2), response).unwrap();
assert!(matches!(
res,
OnBlockData::Import(ImportBlocksAction{ origin: _, blocks })
if blocks.iter().all(|b| [2, 3, 4].contains(b.header.as_ref().unwrap().number()))
));
let response = create_block_response(vec![block2.clone()]);
let res = sync.on_block_data(&peer_id2, Some(request3), response).unwrap();
// Nothing to import
assert!(
matches!(res, OnBlockData::Import(ImportBlocksAction{ origin: _, blocks }) if blocks.is_empty())
);
}
fn unwrap_from_block_number(from: FromBlock<Hash, u64>) -> u64 {
if let FromBlock::Number(from) = from {
from