Skip to content

Commit

Permalink
chore(provider): add more test coverage on `BlockchainProvider::*by_t…
Browse files Browse the repository at this point in the history
…x_range` queries (#11480)
  • Loading branch information
joshieDo authored Oct 4, 2024
1 parent d5ddb41 commit a42cfbb
Showing 1 changed file with 82 additions and 81 deletions.
163 changes: 82 additions & 81 deletions crates/storage/provider/src/providers/blockchain_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,17 @@ impl<N: ProviderNodeTypes> BlockchainProvider2<N> {
let block_tx_count = block_state.block_ref().block().body.transactions.len();
let remaining = (tx_range.end() - tx_range.start() + 1) as usize;

// This should only be more than 0 in the first iteration, in case of a partial range
// If the transaction range start is higher than this block last transaction, advance
if *tx_range.start() > in_memory_tx_num + block_tx_count as u64 - 1 {
in_memory_tx_num += block_tx_count as u64;
continue
}

// This should only be more than 0 once, in case of a partial range inside a block.
let skip = (tx_range.start() - in_memory_tx_num) as usize;

items.extend(fetch_from_block_state(
skip..=(remaining.min(block_tx_count) - 1),
skip..=skip + (remaining.min(block_tx_count - skip) - 1),
block_state,
)?);

Expand Down Expand Up @@ -4115,51 +4121,76 @@ mod tests {
Ok(())
}

#[test]
fn test_transactions_by_tx_range() -> eyre::Result<()> {
let mut rng = generators::rng();
let (provider, database_blocks, _, _) = provider_with_random_blocks(
&mut rng,
TEST_BLOCKS_COUNT,
0,
BlockRangeParams {
tx_count: TEST_TRANSACTIONS_COUNT..TEST_TRANSACTIONS_COUNT,
..Default::default()
},
)?;
macro_rules! test_by_tx_range {
($provider:expr, $database_blocks:expr, $in_memory_blocks:expr, [$(($method:ident, $data_extractor:expr)),* $(,)?]) => {{
let db_tx_count =
$database_blocks.iter().map(|b| b.body.transactions.len()).sum::<usize>() as u64;
let in_mem_tx_count =
$in_memory_blocks.iter().map(|b| b.body.transactions.len()).sum::<usize>() as u64;

// Define a valid transaction range within the database
let start_tx_num = 0;
let end_tx_num = 1;
let db_range = 0..=(db_tx_count - 1);
let in_mem_range = db_tx_count..=(in_mem_tx_count + db_range.end());

// Retrieve the transactions for this transaction number range
let result = provider.transactions_by_tx_range(start_tx_num..=end_tx_num)?;
$(
// Retrieve the expected database data
let database_data =
$database_blocks.iter().flat_map(|b| $data_extractor(b)).collect::<Vec<_>>();
assert_eq!($provider.$method(db_range.clone())?, database_data);

// Ensure the transactions match the expected transactions in the database
assert_eq!(result.len(), 2);
assert_eq!(result[0], database_blocks[0].body.transactions[0].clone().into());
assert_eq!(result[1], database_blocks[0].body.transactions[1].clone().into());
// Retrieve the expected in-memory data
let in_memory_data =
$in_memory_blocks.iter().flat_map(|b| $data_extractor(b)).collect::<Vec<_>>();
assert_eq!($provider.$method(in_mem_range.clone())?, in_memory_data);

// Define an empty range that should return no transactions
let start_tx_num = u64::MAX;
let end_tx_num = u64::MAX;
// Test partial in-memory range
assert_eq!(
&$provider.$method(in_mem_range.start() + 1..=in_mem_range.end() - 1)?,
&in_memory_data[1..in_memory_data.len() - 1]
);

// Retrieve the transactions for this range
let result = provider.transactions_by_tx_range(start_tx_num..end_tx_num)?;
// Test range in in-memory to unbounded end
assert_eq!($provider.$method(in_mem_range.start() + 1..)?, &in_memory_data[1..]);

// Ensure no transactions are returned
assert!(
result.is_empty(),
"No transactions should be found for an empty transaction range"
);
// Test last element in-memory
assert_eq!($provider.$method(in_mem_range.end()..)?, &in_memory_data[in_memory_data.len() -1 ..]);

Ok(())
// Test range that spans database and in-memory
assert_eq!(
$provider.$method(in_mem_range.start() - 2..=in_mem_range.end() - 1)?,
database_data[database_data.len() - 2..]
.iter()
.chain(&in_memory_data[..in_memory_data.len() - 1])
.cloned()
.collect::<Vec<_>>()
);

// Test range that spans database and in-memory with unbounded end
assert_eq!(
$provider.$method(in_mem_range.start() - 2..)?,
database_data[database_data.len() - 2..]
.iter()
.chain(&in_memory_data[..])
.cloned()
.collect::<Vec<_>>()
);

// Test invalid range
let start_tx_num = u64::MAX;
let end_tx_num = u64::MAX;
let result = $provider.$method(start_tx_num..end_tx_num)?;
assert!(result.is_empty(), "No data should be found for an invalid transaction range");

// Test empty range
let result = $provider.$method(in_mem_range.end()+10..in_mem_range.end()+20)?;
assert!(result.is_empty(), "No data should be found for an empty transaction range");
)*
}};
}

#[test]
fn test_senders_by_tx_range() -> eyre::Result<()> {
fn test_methods_by_tx_range() -> eyre::Result<()> {
let mut rng = generators::rng();
let (provider, database_blocks, in_memory_blocks, _) = provider_with_random_blocks(
let (provider, database_blocks, in_memory_blocks, receipts) = provider_with_random_blocks(
&mut rng,
TEST_BLOCKS_COUNT,
TEST_BLOCKS_COUNT,
Expand All @@ -4169,51 +4200,21 @@ mod tests {
},
)?;

let db_tx_count =
database_blocks.iter().map(|b| b.body.transactions.len()).sum::<usize>() as u64;
let in_mem_tx_count =
in_memory_blocks.iter().map(|b| b.body.transactions.len()).sum::<usize>() as u64;

let db_range = 0..=(db_tx_count - 1);
let in_mem_range = db_tx_count..=(in_mem_tx_count + db_range.end());

// Retrieve the senders for the whole database range
let database_senders =
database_blocks.iter().flat_map(|b| b.senders().unwrap()).collect::<Vec<_>>();
assert_eq!(provider.senders_by_tx_range(db_range)?, database_senders);

// Retrieve the senders for the whole in-memory range
let in_memory_senders =
in_memory_blocks.iter().flat_map(|b| b.senders().unwrap()).collect::<Vec<_>>();
assert_eq!(provider.senders_by_tx_range(in_mem_range.clone())?, in_memory_senders);

// Retrieve the senders for a partial in-memory range
assert_eq!(
&provider.senders_by_tx_range(in_mem_range.start() + 1..=in_mem_range.end() - 1)?,
&in_memory_senders[1..in_memory_senders.len() - 1]
);

// Retrieve the senders for a range that spans database and in-memory
assert_eq!(
provider.senders_by_tx_range(in_mem_range.start() - 2..=in_mem_range.end() - 1)?,
database_senders[database_senders.len() - 2..]
.iter()
.chain(&in_memory_senders[..in_memory_senders.len() - 1])
.copied()
.collect::<Vec<_>>()
);

// Define an empty range that should return no sender addresses
let start_tx_num = u64::MAX;
let end_tx_num = u64::MAX;

// Retrieve the senders for this range
let result = provider.senders_by_tx_range(start_tx_num..end_tx_num)?;

// Ensure no sender addresses are returned
assert!(
result.is_empty(),
"No sender addresses should be found for an empty transaction range"
test_by_tx_range!(
provider,
database_blocks,
in_memory_blocks,
[
(senders_by_tx_range, |block: &SealedBlock| block.senders().unwrap()),
(transactions_by_tx_range, |block: &SealedBlock| block
.body
.transactions
.iter()
.map(|tx| Into::<TransactionSignedNoHash>::into(tx.clone()))
.collect::<Vec<_>>()),
(receipts_by_tx_range, |block: &SealedBlock| receipts[block.number as usize]
.clone())
]
);

Ok(())
Expand Down

0 comments on commit a42cfbb

Please sign in to comment.