//! Fixed test vectors for the syncer. use std::{collections::HashMap, iter, sync::Arc, time::Duration}; use color_eyre::Report; use futures::{Future, FutureExt}; use zebra_chain::{ block::{self, Block, Height}, chain_tip::mock::{MockChainTip, MockChainTipSender}, serialization::ZcashDeserializeInto, }; use zebra_consensus::Config as ConsensusConfig; use zebra_network::InventoryResponse; use zebra_state::Config as StateConfig; use zebra_test::mock_service::{MockService, PanicAssertion}; use zebra_network as zn; use zebra_state as zs; use crate::{ components::{ sync::{self, SyncStatus}, ChainSync, }, config::ZebradConfig, }; use InventoryResponse::*; /// Maximum time to wait for a request to any test service. /// /// The default [`MockService`] value can be too short for some of these tests that take a little /// longer than expected to actually send the request. /// /// Increasing this value causes the tests to take longer to complete, so it can't be too large. const MAX_SERVICE_REQUEST_DELAY: Duration = Duration::from_millis(1000); /// Test that the syncer downloads genesis, blocks 1-2 using obtain_tips, and blocks 3-4 using extend_tips. /// /// This test also makes sure that the syncer downloads blocks in order. #[tokio::test] async fn sync_blocks_ok() -> Result<(), crate::BoxError> { // Get services let ( chain_sync_future, _sync_status, mut router_verifier, mut peer_set, mut state_service, _mock_chain_tip_sender, ) = setup(); // Get blocks let block0: Arc = zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.zcash_deserialize_into()?; let block0_hash = block0.hash(); let block1: Arc = zebra_test::vectors::BLOCK_MAINNET_1_BYTES.zcash_deserialize_into()?; let block1_hash = block1.hash(); let block2: Arc = zebra_test::vectors::BLOCK_MAINNET_2_BYTES.zcash_deserialize_into()?; let block2_hash = block2.hash(); let block3: Arc = zebra_test::vectors::BLOCK_MAINNET_3_BYTES.zcash_deserialize_into()?; let block3_hash = block3.hash(); let block4: Arc = zebra_test::vectors::BLOCK_MAINNET_4_BYTES.zcash_deserialize_into()?; let block4_hash = block4.hash(); let block5: Arc = zebra_test::vectors::BLOCK_MAINNET_5_BYTES.zcash_deserialize_into()?; let block5_hash = block5.hash(); // Start the syncer let chain_sync_task_handle = tokio::spawn(chain_sync_future); // ChainSync::request_genesis // State is checked for genesis state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(None)); // Block 0 is fetched and committed to the state peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block0_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block0.clone())])); router_verifier .expect_request(zebra_consensus::Request::Commit(block0)) .await .respond(block0_hash); // Check that nothing unexpected happened. // We expect more requests to the state service, because the syncer keeps on running. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for genesis again state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(Some(zs::KnownBlock::BestChain))); // ChainSync::obtain_tips // State is asked for a block locator. state_service .expect_request(zs::Request::BlockLocator) .await .respond(zs::Response::BlockLocator(vec![block0_hash])); // Network is sent the block locator peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block1_hash, // tip block2_hash, // expected_next block3_hash, // (discarded - last hash, possibly incorrect) ])); // State is checked for the first unknown block (block 1) state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test obtain tips error"))); } // Check that nothing unexpected happened. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for all non-tip blocks (blocks 1 & 2) in response order state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); state_service .expect_request(zs::Request::KnownBlock(block2_hash)) .await .respond(zs::Response::KnownBlock(None)); // Blocks 1 & 2 are fetched in order, then verified concurrently peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block1_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block1.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block2_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block2.clone())])); // We can't guarantee the verification request order let mut remaining_blocks: HashMap> = [(block1_hash, block1), (block2_hash, block2)] .iter() .cloned() .collect(); for _ in 1..=2 { router_verifier .expect_request_that(|req| remaining_blocks.remove(&req.block().hash()).is_some()) .await .respond_with(|req| req.block().hash()); } assert_eq!( remaining_blocks, HashMap::new(), "expected all non-tip blocks to be verified by obtain tips" ); // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; // ChainSync::extend_tips // Network is sent a block locator based on the tip peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block1_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block2_hash, // tip (discarded - already fetched) block3_hash, // expected_next block4_hash, block5_hash, // (discarded - last hash, possibly incorrect) ])); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block1_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test extend tips error"))); } // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; // Blocks 3 & 4 are fetched in order, then verified concurrently peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block3_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block3.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block4_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block4.clone())])); // We can't guarantee the verification request order let mut remaining_blocks: HashMap> = [(block3_hash, block3), (block4_hash, block4)] .iter() .cloned() .collect(); for _ in 3..=4 { router_verifier .expect_request_that(|req| remaining_blocks.remove(&req.block().hash()).is_some()) .await .respond_with(|req| req.block().hash()); } assert_eq!( remaining_blocks, HashMap::new(), "expected all non-tip blocks to be verified by extend tips" ); // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; let chain_sync_result = chain_sync_task_handle.now_or_never(); assert!( chain_sync_result.is_none(), "unexpected error or panic in chain sync task: {chain_sync_result:?}", ); Ok(()) } /// Test that the syncer downloads genesis, blocks 1-2 using obtain_tips, and blocks 3-4 using extend_tips, /// with duplicate block hashes. /// /// This test also makes sure that the syncer downloads blocks in order. #[tokio::test] async fn sync_blocks_duplicate_hashes_ok() -> Result<(), crate::BoxError> { // Get services let ( chain_sync_future, _sync_status, mut router_verifier, mut peer_set, mut state_service, _mock_chain_tip_sender, ) = setup(); // Get blocks let block0: Arc = zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.zcash_deserialize_into()?; let block0_hash = block0.hash(); let block1: Arc = zebra_test::vectors::BLOCK_MAINNET_1_BYTES.zcash_deserialize_into()?; let block1_hash = block1.hash(); let block2: Arc = zebra_test::vectors::BLOCK_MAINNET_2_BYTES.zcash_deserialize_into()?; let block2_hash = block2.hash(); let block3: Arc = zebra_test::vectors::BLOCK_MAINNET_3_BYTES.zcash_deserialize_into()?; let block3_hash = block3.hash(); let block4: Arc = zebra_test::vectors::BLOCK_MAINNET_4_BYTES.zcash_deserialize_into()?; let block4_hash = block4.hash(); let block5: Arc = zebra_test::vectors::BLOCK_MAINNET_5_BYTES.zcash_deserialize_into()?; let block5_hash = block5.hash(); // Start the syncer let chain_sync_task_handle = tokio::spawn(chain_sync_future); // ChainSync::request_genesis // State is checked for genesis state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(None)); // Block 0 is fetched and committed to the state peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block0_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block0.clone())])); router_verifier .expect_request(zebra_consensus::Request::Commit(block0)) .await .respond(block0_hash); // Check that nothing unexpected happened. // We expect more requests to the state service, because the syncer keeps on running. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for genesis again state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(Some(zs::KnownBlock::BestChain))); // ChainSync::obtain_tips // State is asked for a block locator. state_service .expect_request(zs::Request::BlockLocator) .await .respond(zs::Response::BlockLocator(vec![block0_hash])); // Network is sent the block locator peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block1_hash, block1_hash, block1_hash, // tip block2_hash, // expected_next block3_hash, // (discarded - last hash, possibly incorrect) ])); // State is checked for the first unknown block (block 1) state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test obtain tips error"))); } // Check that nothing unexpected happened. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for all non-tip blocks (blocks 1 & 2) in response order state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); state_service .expect_request(zs::Request::KnownBlock(block2_hash)) .await .respond(zs::Response::KnownBlock(None)); // Blocks 1 & 2 are fetched in order, then verified concurrently peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block1_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block1.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block2_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block2.clone())])); // We can't guarantee the verification request order let mut remaining_blocks: HashMap> = [(block1_hash, block1), (block2_hash, block2)] .iter() .cloned() .collect(); for _ in 1..=2 { router_verifier .expect_request_that(|req| remaining_blocks.remove(&req.block().hash()).is_some()) .await .respond_with(|req| req.block().hash()); } assert_eq!( remaining_blocks, HashMap::new(), "expected all non-tip blocks to be verified by obtain tips" ); // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; // ChainSync::extend_tips // Network is sent a block locator based on the tip peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block1_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block2_hash, // tip (discarded - already fetched) block3_hash, // expected_next block4_hash, block3_hash, block4_hash, block5_hash, // (discarded - last hash, possibly incorrect) ])); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block1_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test extend tips error"))); } // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; // Blocks 3 & 4 are fetched in order, then verified concurrently peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block3_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block3.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block4_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block4.clone())])); // We can't guarantee the verification request order let mut remaining_blocks: HashMap> = [(block3_hash, block3), (block4_hash, block4)] .iter() .cloned() .collect(); for _ in 3..=4 { router_verifier .expect_request_that(|req| remaining_blocks.remove(&req.block().hash()).is_some()) .await .respond_with(|req| req.block().hash()); } assert_eq!( remaining_blocks, HashMap::new(), "expected all non-tip blocks to be verified by extend tips" ); // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; let chain_sync_result = chain_sync_task_handle.now_or_never(); assert!( chain_sync_result.is_none(), "unexpected error or panic in chain sync task: {chain_sync_result:?}", ); Ok(()) } /// Test that zebra-network rejects blocks that are a long way ahead of the state tip. #[tokio::test] async fn sync_block_lookahead_drop() -> Result<(), crate::BoxError> { // Get services let ( chain_sync_future, _sync_status, mut router_verifier, mut peer_set, mut state_service, _mock_chain_tip_sender, ) = setup(); // Get blocks let block0: Arc = zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.zcash_deserialize_into()?; let block0_hash = block0.hash(); // Get a block that is a long way away from genesis let block982k: Arc = zebra_test::vectors::BLOCK_MAINNET_982681_BYTES.zcash_deserialize_into()?; // Start the syncer let chain_sync_task_handle = tokio::spawn(chain_sync_future); // State is checked for genesis state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(None)); // Block 0 is fetched, but the peer returns a much higher block. // (Mismatching hashes are usually ignored by the network service, // but we use them here to test the syncer lookahead.) peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block0_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block982k.clone())])); // Block is dropped because it is too far ahead of the tip. // We expect more requests to the state service, because the syncer keeps on running. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; let chain_sync_result = chain_sync_task_handle.now_or_never(); assert!( chain_sync_result.is_none(), "unexpected error or panic in chain sync task: {chain_sync_result:?}", ); Ok(()) } /// Test that the sync downloader rejects blocks that are too high in obtain_tips. /// /// TODO: also test that it rejects blocks behind the tip limit. (Needs ~100 fake blocks.) #[tokio::test] async fn sync_block_too_high_obtain_tips() -> Result<(), crate::BoxError> { // Get services let ( chain_sync_future, _sync_status, mut router_verifier, mut peer_set, mut state_service, _mock_chain_tip_sender, ) = setup(); // Get blocks let block0: Arc = zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.zcash_deserialize_into()?; let block0_hash = block0.hash(); let block1: Arc = zebra_test::vectors::BLOCK_MAINNET_1_BYTES.zcash_deserialize_into()?; let block1_hash = block1.hash(); let block2: Arc = zebra_test::vectors::BLOCK_MAINNET_2_BYTES.zcash_deserialize_into()?; let block2_hash = block2.hash(); let block3: Arc = zebra_test::vectors::BLOCK_MAINNET_3_BYTES.zcash_deserialize_into()?; let block3_hash = block3.hash(); // Also get a block that is a long way away from genesis let block982k: Arc = zebra_test::vectors::BLOCK_MAINNET_982681_BYTES.zcash_deserialize_into()?; let block982k_hash = block982k.hash(); // Start the syncer let chain_sync_task_handle = tokio::spawn(chain_sync_future); // ChainSync::request_genesis // State is checked for genesis state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(None)); // Block 0 is fetched and committed to the state peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block0_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block0.clone())])); router_verifier .expect_request(zebra_consensus::Request::Commit(block0)) .await .respond(block0_hash); // Check that nothing unexpected happened. // We expect more requests to the state service, because the syncer keeps on running. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for genesis again state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(Some(zs::KnownBlock::BestChain))); // ChainSync::obtain_tips // State is asked for a block locator. state_service .expect_request(zs::Request::BlockLocator) .await .respond(zs::Response::BlockLocator(vec![block0_hash])); // Network is sent the block locator peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block982k_hash, block1_hash, // tip block2_hash, // expected_next block3_hash, // (discarded - last hash, possibly incorrect) ])); // State is checked for the first unknown block (block 982k) state_service .expect_request(zs::Request::KnownBlock(block982k_hash)) .await .respond(zs::Response::KnownBlock(None)); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test obtain tips error"))); } // Check that nothing unexpected happened. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for all non-tip blocks (blocks 982k, 1, 2) in response order state_service .expect_request(zs::Request::KnownBlock(block982k_hash)) .await .respond(zs::Response::KnownBlock(None)); state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); state_service .expect_request(zs::Request::KnownBlock(block2_hash)) .await .respond(zs::Response::KnownBlock(None)); // Blocks 982k, 1, 2 are fetched in order, then verified concurrently, // but block 982k verification is skipped because it is too high. peer_set .expect_request(zn::Request::BlocksByHash( iter::once(block982k_hash).collect(), )) .await .respond(zn::Response::Blocks(vec![Available(block982k.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block1_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block1.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block2_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block2.clone())])); // At this point, the following tasks race: // - The valid chain verifier requests // - The block too high error, which causes a syncer reset and ChainSync::obtain_tips // - ChainSync::extend_tips for the next tip let chain_sync_result = chain_sync_task_handle.now_or_never(); assert!( chain_sync_result.is_none(), "unexpected error or panic in chain sync task: {chain_sync_result:?}", ); Ok(()) } /// Test that the sync downloader rejects blocks that are too high in extend_tips. /// /// TODO: also test that it rejects blocks behind the tip limit. (Needs ~100 fake blocks.) #[tokio::test] async fn sync_block_too_high_extend_tips() -> Result<(), crate::BoxError> { // Get services let ( chain_sync_future, _sync_status, mut router_verifier, mut peer_set, mut state_service, _mock_chain_tip_sender, ) = setup(); // Get blocks let block0: Arc = zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.zcash_deserialize_into()?; let block0_hash = block0.hash(); let block1: Arc = zebra_test::vectors::BLOCK_MAINNET_1_BYTES.zcash_deserialize_into()?; let block1_hash = block1.hash(); let block2: Arc = zebra_test::vectors::BLOCK_MAINNET_2_BYTES.zcash_deserialize_into()?; let block2_hash = block2.hash(); let block3: Arc = zebra_test::vectors::BLOCK_MAINNET_3_BYTES.zcash_deserialize_into()?; let block3_hash = block3.hash(); let block4: Arc = zebra_test::vectors::BLOCK_MAINNET_4_BYTES.zcash_deserialize_into()?; let block4_hash = block4.hash(); let block5: Arc = zebra_test::vectors::BLOCK_MAINNET_5_BYTES.zcash_deserialize_into()?; let block5_hash = block5.hash(); // Also get a block that is a long way away from genesis let block982k: Arc = zebra_test::vectors::BLOCK_MAINNET_982681_BYTES.zcash_deserialize_into()?; let block982k_hash = block982k.hash(); // Start the syncer let chain_sync_task_handle = tokio::spawn(chain_sync_future); // ChainSync::request_genesis // State is checked for genesis state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(None)); // Block 0 is fetched and committed to the state peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block0_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block0.clone())])); router_verifier .expect_request(zebra_consensus::Request::Commit(block0)) .await .respond(block0_hash); // Check that nothing unexpected happened. // We expect more requests to the state service, because the syncer keeps on running. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for genesis again state_service .expect_request(zs::Request::KnownBlock(block0_hash)) .await .respond(zs::Response::KnownBlock(Some(zs::KnownBlock::BestChain))); // ChainSync::obtain_tips // State is asked for a block locator. state_service .expect_request(zs::Request::BlockLocator) .await .respond(zs::Response::BlockLocator(vec![block0_hash])); // Network is sent the block locator peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block1_hash, // tip block2_hash, // expected_next block3_hash, // (discarded - last hash, possibly incorrect) ])); // State is checked for the first unknown block (block 1) state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block0_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test obtain tips error"))); } // Check that nothing unexpected happened. peer_set.expect_no_requests().await; router_verifier.expect_no_requests().await; // State is checked for all non-tip blocks (blocks 1 & 2) in response order state_service .expect_request(zs::Request::KnownBlock(block1_hash)) .await .respond(zs::Response::KnownBlock(None)); state_service .expect_request(zs::Request::KnownBlock(block2_hash)) .await .respond(zs::Response::KnownBlock(None)); // Blocks 1 & 2 are fetched in order, then verified concurrently peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block1_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block1.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block2_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block2.clone())])); // We can't guarantee the verification request order let mut remaining_blocks: HashMap> = [(block1_hash, block1), (block2_hash, block2)] .iter() .cloned() .collect(); for _ in 1..=2 { router_verifier .expect_request_that(|req| remaining_blocks.remove(&req.block().hash()).is_some()) .await .respond_with(|req| req.block().hash()); } assert_eq!( remaining_blocks, HashMap::new(), "expected all non-tip blocks to be verified by obtain tips" ); // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; // ChainSync::extend_tips // Network is sent a block locator based on the tip peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block1_hash], stop: None, }) .await .respond(zn::Response::BlockHashes(vec![ block2_hash, // tip (discarded - already fetched) block3_hash, // expected_next block4_hash, block982k_hash, block5_hash, // (discarded - last hash, possibly incorrect) ])); // Clear remaining block locator requests for _ in 0..(sync::FANOUT - 1) { peer_set .expect_request(zn::Request::FindBlocks { known_blocks: vec![block1_hash], stop: None, }) .await .respond(Err(zn::BoxError::from("synthetic test extend tips error"))); } // Check that nothing unexpected happened. router_verifier.expect_no_requests().await; state_service.expect_no_requests().await; // Blocks 3, 4, 982k are fetched in order, then verified concurrently, // but block 982k verification is skipped because it is too high. peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block3_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block3.clone())])); peer_set .expect_request(zn::Request::BlocksByHash(iter::once(block4_hash).collect())) .await .respond(zn::Response::Blocks(vec![Available(block4.clone())])); peer_set .expect_request(zn::Request::BlocksByHash( iter::once(block982k_hash).collect(), )) .await .respond(zn::Response::Blocks(vec![Available(block982k.clone())])); // At this point, the following tasks race: // - The valid chain verifier requests // - The block too high error, which causes a syncer reset and ChainSync::obtain_tips // - ChainSync::extend_tips for the next tip let chain_sync_result = chain_sync_task_handle.now_or_never(); assert!( chain_sync_result.is_none(), "unexpected error or panic in chain sync task: {chain_sync_result:?}", ); Ok(()) } fn setup() -> ( // ChainSync impl Future> + Send, SyncStatus, // BlockVerifierRouter MockService, // PeerSet MockService, // StateService MockService, MockChainTipSender, ) { let _init_guard = zebra_test::init(); let consensus_config = ConsensusConfig::default(); let state_config = StateConfig::ephemeral(); let config = ZebradConfig { consensus: consensus_config, state: state_config, ..Default::default() }; // These tests run multiple tasks in parallel. // So machines under heavy load need a longer delay. // (For example, CI machines with limited cores.) let peer_set = MockService::build() .with_max_request_delay(MAX_SERVICE_REQUEST_DELAY) .for_unit_tests(); let router_verifier = MockService::build() .with_max_request_delay(MAX_SERVICE_REQUEST_DELAY) .for_unit_tests(); let state_service = MockService::build() .with_max_request_delay(MAX_SERVICE_REQUEST_DELAY) .for_unit_tests(); let (mock_chain_tip, mock_chain_tip_sender) = MockChainTip::new(); let (chain_sync, sync_status) = ChainSync::new( &config, Height(0), peer_set.clone(), router_verifier.clone(), state_service.clone(), mock_chain_tip, ); let chain_sync_future = chain_sync.sync(); ( chain_sync_future, sync_status, router_verifier, peer_set, state_service, mock_chain_tip_sender, ) }