From e2ea8ac28c384bdc427e5a2d40cff474e34c77cf Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Wed, 21 Aug 2024 01:48:50 -0700 Subject: [PATCH] test: `BlockReader` implementation of `BlockchainProvider2` (#10370) --- .../src/providers/blockchain_provider.rs | 290 +++++++++++++++++- 1 file changed, 277 insertions(+), 13 deletions(-) diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 98580622ad..3fae8b39e7 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -505,7 +505,6 @@ where if let Some(block_state) = self.canonical_in_memory_state.state_by_number(num) { return Ok(Some(block_state.block().block().clone().unseal())); } - self.database.block_by_number(num) } } @@ -635,11 +634,11 @@ where let mut blocks = Vec::with_capacity(capacity); // First, fetch the blocks from the database - let mut db_blocks = self.database.block_range(range.clone())?; - blocks.append(&mut db_blocks); + let mut database_blocks = self.database.block_range(range.clone())?; + blocks.append(&mut database_blocks); // Advance the range iterator by the number of blocks fetched from the database - range.nth(db_blocks.len() - 1); + range.nth(database_blocks.len() - 1); // Fetch the remaining blocks from the in-memory state for num in range { @@ -663,11 +662,11 @@ where let mut blocks = Vec::with_capacity(capacity); // First, fetch the blocks from the database - let mut db_blocks = self.database.block_with_senders_range(range.clone())?; - blocks.append(&mut db_blocks); + let mut database_blocks = self.database.block_with_senders_range(range.clone())?; + blocks.append(&mut database_blocks); // Advance the range iterator by the number of blocks fetched from the database - range.nth(db_blocks.len() - 1); + range.nth(database_blocks.len() - 1); // Fetch the remaining blocks from the in-memory state for num in range { @@ -693,11 +692,11 @@ where let mut blocks = Vec::with_capacity(capacity); // First, fetch the blocks from the database - let mut db_blocks = self.database.sealed_block_with_senders_range(range.clone())?; - blocks.append(&mut db_blocks); + let mut database_blocks = self.database.sealed_block_with_senders_range(range.clone())?; + blocks.append(&mut database_blocks); // Advance the range iterator by the number of blocks fetched from the database - range.nth(db_blocks.len() - 1); + range.nth(database_blocks.len() - 1); // Fetch the remaining blocks from the in-memory state for num in range { @@ -1491,10 +1490,13 @@ mod tests { use std::sync::Arc; use reth_chain_state::{ExecutedBlock, NewCanonicalChain}; + use reth_chainspec::ChainSpecProvider; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; - use reth_primitives::{BlockNumberOrTag, SealedBlock, B256}; - use reth_storage_api::{BlockHashReader, BlockNumReader, BlockReaderIdExt, HeaderProvider}; - use reth_testing_utils::generators::{self, random_block_range}; + use reth_primitives::{BlockHashOrNumber, BlockNumberOrTag, Header, SealedBlock, B256}; + use reth_storage_api::{ + BlockHashReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, HeaderProvider, + }; + use reth_testing_utils::generators::{self, random_block, random_block_range}; use crate::{ providers::BlockchainProvider2, test_utils::create_test_provider_factory, CanonChainTracker, @@ -1562,6 +1564,268 @@ mod tests { Ok((provider, database_blocks.to_vec(), in_memory_blocks.to_vec())) } + #[test] + fn test_block_reader_find_block_by_hash() -> eyre::Result<()> { + // Initialize random number generator and provider factory + let mut rng = generators::rng(); + let factory = create_test_provider_factory(); + + // Generate 10 random blocks and split into database and in-memory blocks + let blocks = random_block_range(&mut rng, 0..=10, B256::ZERO, 0..1); + let (database_blocks, in_memory_blocks) = blocks.split_at(5); + + // Insert first 5 blocks into the database + let provider_rw = factory.provider_rw()?; + for block in database_blocks { + provider_rw.insert_historical_block( + block.clone().seal_with_senders().expect("failed to seal block with senders"), + )?; + } + provider_rw.commit()?; + + // Create a new provider + let provider = BlockchainProvider2::new(factory)?; + + // Useful blocks + let first_db_block = database_blocks.first().unwrap(); + let first_in_mem_block = in_memory_blocks.first().unwrap(); + let last_in_mem_block = in_memory_blocks.last().unwrap(); + + // No block in memory before setting in memory state + assert_eq!(provider.find_block_by_hash(first_in_mem_block.hash(), BlockSource::Any)?, None); + assert_eq!( + provider.find_block_by_hash(first_in_mem_block.hash(), BlockSource::Canonical)?, + None + ); + // No pending block in memory + assert_eq!( + provider.find_block_by_hash(first_in_mem_block.hash(), BlockSource::Pending)?, + None + ); + + // Insert first block into the in-memory state + let in_memory_block_senders = + first_in_mem_block.senders().expect("failed to recover senders"); + let chain = NewCanonicalChain::Commit { + new: vec![ExecutedBlock::new( + Arc::new(first_in_mem_block.clone()), + Arc::new(in_memory_block_senders), + Default::default(), + Default::default(), + Default::default(), + )], + }; + provider.canonical_in_memory_state.update_chain(chain); + + // Now the block should be found in memory + assert_eq!( + provider.find_block_by_hash(first_in_mem_block.hash(), BlockSource::Any)?, + Some(first_in_mem_block.clone().into()) + ); + assert_eq!( + provider.find_block_by_hash(first_in_mem_block.hash(), BlockSource::Canonical)?, + Some(first_in_mem_block.clone().into()) + ); + + // Find the first block in database by hash + assert_eq!( + provider.find_block_by_hash(first_db_block.hash(), BlockSource::Any)?, + Some(first_db_block.clone().into()) + ); + assert_eq!( + provider.find_block_by_hash(first_db_block.hash(), BlockSource::Canonical)?, + Some(first_db_block.clone().into()) + ); + + // No pending block in database + assert_eq!(provider.find_block_by_hash(first_db_block.hash(), BlockSource::Pending)?, None); + + // Insert the last block into the pending state + provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + block: Arc::new(last_in_mem_block.clone()), + senders: Default::default(), + execution_output: Default::default(), + hashed_state: Default::default(), + trie: Default::default(), + }); + + // Now the last block should be found in memory + assert_eq!( + provider.find_block_by_hash(last_in_mem_block.hash(), BlockSource::Pending)?, + Some(last_in_mem_block.clone().into()) + ); + + Ok(()) + } + + #[test] + fn test_block_reader_block() -> eyre::Result<()> { + // Initialize random number generator and provider factory + let mut rng = generators::rng(); + let factory = create_test_provider_factory(); + + // Generate 10 random blocks and split into database and in-memory blocks + let blocks = random_block_range(&mut rng, 0..=10, B256::ZERO, 0..1); + let (database_blocks, in_memory_blocks) = blocks.split_at(5); + + // Insert first 5 blocks into the database + let provider_rw = factory.provider_rw()?; + for block in database_blocks { + provider_rw.insert_historical_block( + block.clone().seal_with_senders().expect("failed to seal block with senders"), + )?; + } + provider_rw.commit()?; + + // Create a new provider + let provider = BlockchainProvider2::new(factory)?; + + // First in memory block + let first_in_mem_block = in_memory_blocks.first().unwrap(); + // First database block + let first_db_block = database_blocks.first().unwrap(); + + // First in memory block should not be found yet as not integrated to the in-memory state + assert_eq!(provider.block(BlockHashOrNumber::Hash(first_in_mem_block.hash()))?, None); + assert_eq!(provider.block(BlockHashOrNumber::Number(first_in_mem_block.number))?, None); + + // Insert first block into the in-memory state + let in_memory_block_senders = + first_in_mem_block.senders().expect("failed to recover senders"); + let chain = NewCanonicalChain::Commit { + new: vec![ExecutedBlock::new( + Arc::new(first_in_mem_block.clone()), + Arc::new(in_memory_block_senders), + Default::default(), + Default::default(), + Default::default(), + )], + }; + provider.canonical_in_memory_state.update_chain(chain); + + // First in memory block should be found + assert_eq!( + provider.block(BlockHashOrNumber::Hash(first_in_mem_block.hash()))?, + Some(first_in_mem_block.clone().into()) + ); + assert_eq!( + provider.block(BlockHashOrNumber::Number(first_in_mem_block.number))?, + Some(first_in_mem_block.clone().into()) + ); + + // First database block should be found + assert_eq!( + provider.block(BlockHashOrNumber::Hash(first_db_block.hash()))?, + Some(first_db_block.clone().into()) + ); + assert_eq!( + provider.block(BlockHashOrNumber::Number(first_db_block.number))?, + Some(first_db_block.clone().into()) + ); + + Ok(()) + } + + #[test] + fn test_block_reader_pending_block() -> eyre::Result<()> { + let (provider, _, _) = + provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + + // Generate a random block + let mut rng = generators::rng(); + let block = random_block(&mut rng, 0, Some(B256::ZERO), None, None); + + // Set the block as pending + provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + block: Arc::new(block.clone()), + senders: Default::default(), + execution_output: Default::default(), + hashed_state: Default::default(), + trie: Default::default(), + }); + + // Assertions related to the pending block + assert_eq!(provider.pending_block()?, Some(block.clone())); + + assert_eq!( + provider.pending_block_with_senders()?, + Some(reth_primitives::SealedBlockWithSenders { + block: block.clone(), + senders: block.senders().unwrap() + }) + ); + + assert_eq!(provider.pending_block_and_receipts()?, Some((block, vec![]))); + + Ok(()) + } + + #[test] + fn test_block_reader_ommers() -> eyre::Result<()> { + // Initialize random number generator and provider factory + let mut rng = generators::rng(); + let factory = create_test_provider_factory(); + + // Generate 10 random blocks and split into database and in-memory blocks + let blocks = random_block_range(&mut rng, 0..=10, B256::ZERO, 0..1); + let (database_blocks, in_memory_blocks) = blocks.split_at(5); + + // Take the first in memory block and add 7 ommers to it + let first_in_mem_block = SealedBlock { + ommers: vec![Header::default(); 7], + ..in_memory_blocks.first().unwrap().clone() + }; + + // Insert first 5 blocks into the database + let provider_rw = factory.provider_rw()?; + for block in database_blocks { + provider_rw.insert_historical_block( + block.clone().seal_with_senders().expect("failed to seal block with senders"), + )?; + } + provider_rw.commit()?; + + // Create a new provider + let provider = BlockchainProvider2::new(factory.clone())?; + + // Insert first block into the in-memory state + let in_memory_block_senders = + first_in_mem_block.senders().expect("failed to recover senders"); + let chain = NewCanonicalChain::Commit { + new: vec![ExecutedBlock::new( + Arc::new(first_in_mem_block.clone()), + Arc::new(in_memory_block_senders), + Default::default(), + Default::default(), + Default::default(), + )], + }; + provider.canonical_in_memory_state.update_chain(chain); + + // If the block is after the Merge, we should have an empty ommers list + assert_eq!( + provider.ommers( + (factory.chain_spec().paris_block_and_final_difficulty.unwrap().0 + 2).into() + )?, + Some(vec![]) + ); + + // First in memory block ommers should be found + assert_eq!( + provider.ommers(first_in_mem_block.number.into())?, + Some(first_in_mem_block.ommers.clone()) + ); + assert_eq!( + provider.ommers(first_in_mem_block.hash().into())?, + Some(first_in_mem_block.ommers) + ); + + // A random hash should return None as the block number is not found + assert_eq!(provider.ommers(B256::random().into())?, None); + + Ok(()) + } + #[test] fn test_block_hash_reader() -> eyre::Result<()> { let (provider, database_blocks, in_memory_blocks) =