diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index ac5eaad8c3d7..12466f399672 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -1489,28 +1489,46 @@ where mod tests { use std::sync::Arc; + use itertools::Itertools; + use rand::Rng; use reth_chain_state::{ExecutedBlock, NewCanonicalChain}; use reth_chainspec::ChainSpecProvider; - use reth_db::{test_utils::TempDatabase, DatabaseEnv}; + use reth_db::{models::AccountBeforeTx, test_utils::TempDatabase, DatabaseEnv}; use reth_execution_types::ExecutionOutcome; use reth_primitives::{ BlockHashOrNumber, BlockNumberOrTag, Header, Receipt, SealedBlock, StaticFileSegment, B256, }; use reth_storage_api::{ BlockHashReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, - HeaderProvider, ReceiptProviderIdExt, + ChangeSetReader, HeaderProvider, ReceiptProviderIdExt, }; - use reth_testing_utils::generators::{self, random_block, random_block_range, random_receipt}; + use reth_testing_utils::generators::{ + self, random_block, random_block_range, random_changeset_range, random_eoa_accounts, + random_receipt, + }; + use revm::db::BundleState; use crate::{ - providers::BlockchainProvider2, test_utils::create_test_provider_factory, + providers::BlockchainProvider2, test_utils::create_test_provider_factory, BlockWriter, CanonChainTracker, StaticFileWriter, }; const TEST_BLOCKS_COUNT: usize = 5; + fn random_blocks( + rng: &mut impl Rng, + database_blocks: usize, + in_memory_blocks: usize, + ) -> (Vec, Vec) { + let block_range = (database_blocks + in_memory_blocks - 1) as u64; + let blocks = random_block_range(rng, 0..=block_range, B256::ZERO, 0..1); + let (database_blocks, in_memory_blocks) = blocks.split_at(database_blocks); + (database_blocks.to_vec(), in_memory_blocks.to_vec()) + } + #[allow(clippy::type_complexity)] fn provider_with_random_blocks( + rng: &mut impl Rng, database_blocks: usize, in_memory_blocks: usize, ) -> eyre::Result<( @@ -1519,22 +1537,20 @@ mod tests { Vec, Vec>, )> { - let mut rng = generators::rng(); - let block_range = (database_blocks + in_memory_blocks - 1) as u64; - let blocks = random_block_range(&mut rng, 0..=block_range, B256::ZERO, 0..1); - let receipts: Vec> = blocks + let (database_blocks, in_memory_blocks) = + random_blocks(rng, database_blocks, in_memory_blocks); + let receipts: Vec> = database_blocks .iter() + .chain(in_memory_blocks.iter()) .map(|block| block.body.iter()) - .map(|tx| tx.map(|tx| random_receipt(&mut rng, tx, Some(2))).collect()) + .map(|tx| tx.map(|tx| random_receipt(rng, tx, Some(2))).collect()) .collect(); let factory = create_test_provider_factory(); let provider_rw = factory.provider_rw()?; - let mut blocks_iter = blocks.clone().into_iter(); - // Insert blocks and receipts into the database - for block in (0..database_blocks).map_while(|_| blocks_iter.next()) { + for block in &database_blocks { provider_rw.insert_historical_block( block.clone().seal_with_senders().expect("failed to seal block with senders"), )?; @@ -1553,7 +1569,8 @@ mod tests { // Insert the rest of the blocks and receipts into the in-memory state let chain = NewCanonicalChain::Commit { - new: blocks_iter + new: in_memory_blocks + .iter() .map(|block| { let senders = block.senders().expect("failed to recover senders"); let block_receipts = receipts.get(block.number as usize).unwrap().clone(); @@ -1561,7 +1578,7 @@ mod tests { ExecutionOutcome { receipts: block_receipts.into(), ..Default::default() }; ExecutedBlock::new( - Arc::new(block), + Arc::new(block.clone()), Arc::new(senders), execution_outcome.into(), Default::default(), @@ -1573,17 +1590,17 @@ mod tests { provider.canonical_in_memory_state.update_chain(chain); // Get canonical, safe, and finalized blocks - let block_count = database_blocks + in_memory_blocks; - let canonical_block = blocks.get(block_count - 1).unwrap().clone(); - let safe_block = blocks.get(block_count - 2).unwrap().clone(); - let finalized_block = blocks.get(block_count - 3).unwrap().clone(); + let blocks = database_blocks.iter().chain(in_memory_blocks.iter()).collect::>(); + let block_count = blocks.len(); + let canonical_block = blocks.get(block_count - 1).unwrap(); + let safe_block = blocks.get(block_count - 2).unwrap(); + let finalized_block = blocks.get(block_count - 3).unwrap(); // Set the canonical head, safe, and finalized blocks - provider.set_canonical_head(canonical_block.header); - provider.set_safe(safe_block.header); - provider.set_finalized(finalized_block.header); + provider.set_canonical_head(canonical_block.header.clone()); + provider.set_safe(safe_block.header.clone()); + provider.set_finalized(finalized_block.header.clone()); - let (database_blocks, in_memory_blocks) = blocks.split_at(database_blocks); Ok((provider, database_blocks.to_vec(), in_memory_blocks.to_vec(), receipts)) } @@ -1751,8 +1768,9 @@ mod tests { #[test] fn test_block_reader_pending_block() -> eyre::Result<()> { + let mut rng = generators::rng(); let (provider, _, _, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); // Generate a random block let mut rng = generators::rng(); @@ -1851,8 +1869,9 @@ mod tests { #[test] fn test_block_hash_reader() -> eyre::Result<()> { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT)?; + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT)?; let database_block = database_blocks.first().unwrap().clone(); let in_memory_block = in_memory_blocks.last().unwrap().clone(); @@ -1874,8 +1893,9 @@ mod tests { #[test] fn test_header_provider() -> eyre::Result<()> { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); let database_block = database_blocks.first().unwrap().clone(); let in_memory_block = in_memory_blocks.last().unwrap().clone(); @@ -1938,8 +1958,9 @@ mod tests { #[test] fn test_block_num_reader() -> eyre::Result<()> { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); assert_eq!(provider.best_block_number()?, in_memory_blocks.last().unwrap().number); assert_eq!(provider.last_block_number()?, database_blocks.last().unwrap().number); @@ -1954,8 +1975,9 @@ mod tests { #[test] fn test_block_reader_id_ext_block_by_id() { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); let database_block = database_blocks.first().unwrap().clone(); let in_memory_block = in_memory_blocks.last().unwrap().clone(); @@ -1982,8 +2004,9 @@ mod tests { #[test] fn test_block_reader_id_ext_header_by_number_or_tag() { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); let database_block = database_blocks.first().unwrap().clone(); @@ -2032,8 +2055,9 @@ mod tests { #[test] fn test_block_reader_id_ext_header_by_id() { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); let database_block = database_blocks.first().unwrap().clone(); let in_memory_block = in_memory_blocks.last().unwrap().clone(); @@ -2083,8 +2107,9 @@ mod tests { #[test] fn test_block_reader_id_ext_ommers_by_id() { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT).unwrap(); let database_block = database_blocks.first().unwrap().clone(); let in_memory_block = in_memory_blocks.last().unwrap().clone(); @@ -2116,8 +2141,9 @@ mod tests { #[test] fn test_receipt_provider_id_ext_receipts_by_block_id() -> eyre::Result<()> { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, receipts) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT)?; + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT)?; let database_block = database_blocks.first().unwrap().clone(); let in_memory_block = in_memory_blocks.last().unwrap().clone(); @@ -2151,8 +2177,9 @@ mod tests { #[test] fn test_receipt_provider_id_ext_receipts_by_block_number_or_tag() -> eyre::Result<()> { + let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, receipts) = - provider_with_random_blocks(TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT)?; + provider_with_random_blocks(&mut rng, TEST_BLOCKS_COUNT, TEST_BLOCKS_COUNT)?; let database_block = database_blocks.first().unwrap().clone(); @@ -2180,4 +2207,117 @@ mod tests { Ok(()) } + + #[test] + fn test_changeset_reader() -> eyre::Result<()> { + let mut rng = generators::rng(); + let (database_blocks, in_memory_blocks) = random_blocks(&mut rng, TEST_BLOCKS_COUNT, 1); + let first_database_block = database_blocks.first().map(|block| block.number).unwrap(); + let last_database_block = database_blocks.last().map(|block| block.number).unwrap(); + let first_in_memory_block = in_memory_blocks.first().map(|block| block.number).unwrap(); + + let accounts = random_eoa_accounts(&mut rng, 2); + + let (database_changesets, database_state) = random_changeset_range( + &mut rng, + &database_blocks, + accounts.into_iter().map(|(address, account)| (address, (account, Vec::new()))), + 0..0, + 0..0, + ); + let (in_memory_changesets, in_memory_state) = random_changeset_range( + &mut rng, + &in_memory_blocks, + database_state + .iter() + .map(|(address, (account, storage))| (*address, (*account, storage.clone()))), + 0..0, + 0..0, + ); + + let factory = create_test_provider_factory(); + + let provider_rw = factory.provider_rw()?; + provider_rw.append_blocks_with_state( + database_blocks + .into_iter() + .map(|b| b.seal_with_senders().expect("failed to seal block with senders")) + .collect(), + ExecutionOutcome { + bundle: BundleState::new( + database_state.into_iter().map(|(address, (account, _))| { + (address, None, Some(account.into()), Default::default()) + }), + database_changesets + .iter() + .map(|block_changesets| { + block_changesets.iter().map(|(address, account, _)| { + (*address, Some(Some((*account).into())), []) + }) + }) + .collect::>(), + Vec::new(), + ), + first_block: first_database_block, + ..Default::default() + }, + Default::default(), + Default::default(), + )?; + provider_rw.commit()?; + + let provider = BlockchainProvider2::new(factory)?; + + let in_memory_changesets = in_memory_changesets.into_iter().next().unwrap(); + let chain = NewCanonicalChain::Commit { + new: vec![in_memory_blocks + .first() + .map(|block| { + let senders = block.senders().expect("failed to recover senders"); + ExecutedBlock::new( + Arc::new(block.clone()), + Arc::new(senders), + Arc::new(ExecutionOutcome { + bundle: BundleState::new( + in_memory_state.into_iter().map(|(address, (account, _))| { + (address, None, Some(account.into()), Default::default()) + }), + [in_memory_changesets.iter().map(|(address, account, _)| { + (*address, Some(Some((*account).into())), Vec::new()) + })], + [], + ), + first_block: first_in_memory_block, + ..Default::default() + }), + Default::default(), + Default::default(), + ) + }) + .unwrap()], + }; + provider.canonical_in_memory_state.update_chain(chain); + + assert_eq!( + provider.account_block_changeset(last_database_block).unwrap(), + database_changesets + .into_iter() + .last() + .unwrap() + .into_iter() + .sorted_by_key(|(address, _, _)| *address) + .map(|(address, account, _)| AccountBeforeTx { address, info: Some(account) }) + .collect::>() + ); + assert_eq!( + provider.account_block_changeset(first_in_memory_block).unwrap(), + in_memory_changesets + .into_iter() + .sorted_by_key(|(address, _, _)| *address) + .map(|(address, account, _)| AccountBeforeTx { address, info: Some(account) }) + .collect::>() + ); + + Ok(()) + } }