chore: move receipts pruning to provider (#13886)

This commit is contained in:
Arsenii Kulikov
2025-01-22 17:41:13 +04:00
committed by GitHub
parent cd44fc3d09
commit 23ebf96188
8 changed files with 75 additions and 250 deletions

View File

@ -3,11 +3,11 @@
use alloc::vec::Vec;
use alloy_eips::eip7685::Requests;
use alloy_primitives::{map::HashSet, Address, BlockNumber, Log};
use reth_execution_errors::{BlockExecutionError, InternalBlockExecutionError};
use alloy_primitives::{BlockNumber, Log};
use reth_execution_errors::BlockExecutionError;
use reth_primitives::Receipts;
use reth_primitives_traits::Receipt;
use reth_prune_types::{PruneMode, PruneModes, PruneSegmentError, MINIMUM_PRUNING_DISTANCE};
use reth_prune_types::PruneModes;
use revm::db::states::bundle_state::BundleRetention;
/// Takes care of:
@ -31,11 +31,6 @@ pub struct BlockBatchRecord<T = reth_primitives::Receipt> {
/// A transaction may have zero or more requests, so the length of the inner vector is not
/// guaranteed to be the same as the number of transactions.
requests: Vec<Requests>,
/// Memoized address pruning filter.
///
/// Empty implies that there is going to be addresses to include in the filter in a future
/// block. None means there isn't any kind of configuration.
pruning_address_filter: Option<(u64, HashSet<Address>)>,
/// First block will be initialized to `None`
/// and be set to the block number of first block executed.
first_block: Option<BlockNumber>,
@ -49,7 +44,6 @@ impl<T> Default for BlockBatchRecord<T> {
prune_modes: Default::default(),
receipts: Default::default(),
requests: Default::default(),
pruning_address_filter: Default::default(),
first_block: Default::default(),
tip: Default::default(),
}
@ -58,10 +52,7 @@ impl<T> Default for BlockBatchRecord<T> {
impl<T> BlockBatchRecord<T> {
/// Create a new receipts recorder with the given pruning configuration.
pub fn new(prune_modes: PruneModes) -> Self
where
T: Default,
{
pub fn new(prune_modes: PruneModes) -> Self {
Self { prune_modes, ..Default::default() }
}
@ -133,64 +124,12 @@ impl<T> BlockBatchRecord<T> {
where
T: Receipt<Log = Log>,
{
let mut receipts = receipts.into_iter().map(Some).collect();
// Prune receipts if necessary.
self.prune_receipts(&mut receipts).map_err(InternalBlockExecutionError::from)?;
let receipts = receipts.into_iter().map(Some).collect();
// Save receipts.
self.receipts.push(receipts);
Ok(())
}
/// Prune receipts according to the pruning configuration.
fn prune_receipts(&mut self, receipts: &mut Vec<Option<T>>) -> Result<(), PruneSegmentError>
where
T: Receipt<Log = Log>,
{
let (Some(first_block), Some(tip)) = (self.first_block, self.tip) else { return Ok(()) };
let block_number = first_block + self.receipts.len() as u64;
// Block receipts should not be retained
if self.prune_modes.receipts == Some(PruneMode::Full) ||
// [`PruneSegment::Receipts`] takes priority over [`PruneSegment::ContractLogs`]
self.prune_modes.receipts.is_some_and(|mode| mode.should_prune(block_number, tip))
{
receipts.clear();
return Ok(())
}
// All receipts from the last 128 blocks are required for blockchain tree, even with
// [`PruneSegment::ContractLogs`].
let prunable_receipts =
PruneMode::Distance(MINIMUM_PRUNING_DISTANCE).should_prune(block_number, tip);
if !prunable_receipts {
return Ok(())
}
let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?;
if !contract_log_pruner.is_empty() {
let (prev_block, filter) =
self.pruning_address_filter.get_or_insert_with(|| (0, Default::default()));
for (_, addresses) in contract_log_pruner.range(*prev_block..=block_number) {
filter.extend(addresses.iter().copied());
}
}
if let Some((_, filter)) = &self.pruning_address_filter {
for receipt in receipts.iter_mut() {
// If there is an address_filter, it does not contain any of the
// contract addresses, then remove this receipt.
let inner_receipt = receipt.as_ref().expect("receipts have not been pruned");
if !inner_receipt.logs().iter().any(|log| filter.contains(&log.address)) {
receipt.take();
}
}
}
Ok(())
}
/// Save EIP-7685 requests to the executor.
pub fn save_requests(&mut self, requests: Requests) {
self.requests.push(requests);
@ -200,10 +139,6 @@ impl<T> BlockBatchRecord<T> {
#[cfg(test)]
mod tests {
use super::*;
use alloc::collections::BTreeMap;
use alloy_primitives::Address;
use reth_primitives::{Log, Receipt};
use reth_prune_types::{PruneMode, ReceiptsLogPruneConfig};
#[test]
fn test_save_receipts_empty() {
@ -216,170 +151,4 @@ mod tests {
// Verify that the saved receipts are equal to a nested empty vector
assert_eq!(*recorder.receipts(), vec![vec![]].into());
}
#[test]
fn test_save_receipts_non_empty_no_pruning() {
let mut recorder = BlockBatchRecord::default();
let receipts = vec![Receipt::default()];
// Verify that saving receipts completes without error
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that there is one block of receipts
assert_eq!(recorder.receipts().len(), 1);
// Verify that the first block contains one receipt
assert_eq!(recorder.receipts()[0].len(), 1);
// Verify that the saved receipt is the default receipt
assert_eq!(recorder.receipts()[0][0], Some(Receipt::default()));
}
#[test]
fn test_save_receipts_with_pruning_no_prunable_receipts() {
let mut recorder = BlockBatchRecord::default();
// Set the first block number
recorder.set_first_block(1);
// Set the tip (highest known block)
recorder.set_tip(130);
// Create a vector of receipts with a default receipt
let receipts = vec![Receipt::default()];
// Verify that saving receipts completes without error
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that there is one block of receipts
assert_eq!(recorder.receipts().len(), 1);
// Verify that the first block contains one receipt
assert_eq!(recorder.receipts()[0].len(), 1);
// Verify that the saved receipt is the default receipt
assert_eq!(recorder.receipts()[0][0], Some(Receipt::default()));
}
#[test]
fn test_save_receipts_with_pruning_no_tip() {
// Create a PruneModes with receipts set to PruneMode::Full
let prune_modes = PruneModes { receipts: Some(PruneMode::Full), ..Default::default() };
let mut recorder = BlockBatchRecord::new(prune_modes);
// Set the first block number
recorder.set_first_block(1);
// Create a vector of receipts with a default receipt
let receipts = vec![Receipt::default()];
// Verify that saving receipts completes without error
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that there is one block of receipts
assert_eq!(recorder.receipts().len(), 1);
// Verify that the first block contains one receipt
assert_eq!(recorder.receipts()[0].len(), 1);
// Verify that the saved receipt is the default receipt
assert_eq!(recorder.receipts()[0][0], Some(Receipt::default()));
}
#[test]
fn test_save_receipts_with_pruning_no_block_number() {
// Create a PruneModes with receipts set to PruneMode::Full
let prune_modes = PruneModes { receipts: Some(PruneMode::Full), ..Default::default() };
// Create a BlockBatchRecord with the prune_modes
let mut recorder = BlockBatchRecord::new(prune_modes);
// Set the tip (highest known block)
recorder.set_tip(130);
// Create a vector of receipts with a default receipt
let receipts = vec![Receipt::default()];
// Verify that saving receipts completes without error
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that there is one block of receipts
assert_eq!(recorder.receipts().len(), 1);
// Verify that the first block contains one receipt
assert_eq!(recorder.receipts()[0].len(), 1);
// Verify that the saved receipt is the default receipt
assert_eq!(recorder.receipts()[0][0], Some(Receipt::default()));
}
// Test saving receipts with pruning configuration and receipts should be pruned
#[test]
fn test_save_receipts_with_pruning_should_prune() {
// Create a PruneModes with receipts set to PruneMode::Full
let prune_modes = PruneModes { receipts: Some(PruneMode::Full), ..Default::default() };
// Create a BlockBatchRecord with the prune_modes
let mut recorder = BlockBatchRecord::new(prune_modes);
// Set the first block number
recorder.set_first_block(1);
// Set the tip (highest known block)
recorder.set_tip(130);
// Create a vector of receipts with a default receipt
let receipts = vec![Receipt::default()];
// Verify that saving receipts completes without error
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that there is one block of receipts
assert_eq!(recorder.receipts().len(), 1);
// Verify that the receipts are pruned (empty)
assert!(recorder.receipts()[0].is_empty());
}
// Test saving receipts with address filter pruning
#[test]
fn test_save_receipts_with_address_filter_pruning() {
// Create a PruneModes with receipts_log_filter configuration
let prune_modes = PruneModes {
receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([
(Address::with_last_byte(1), PruneMode::Before(1300001)),
(Address::with_last_byte(2), PruneMode::Before(1300002)),
(Address::with_last_byte(3), PruneMode::Distance(1300003)),
])),
..Default::default()
};
// Create a BlockBatchRecord with the prune_modes
let mut recorder = BlockBatchRecord::new(prune_modes);
// Set the first block number
recorder.set_first_block(1);
// Set the tip (highest known block)
recorder.set_tip(1300000);
// With a receipt that should be pruned (address 4 not in the log filter)
let mut receipt = Receipt::default();
receipt.logs.push(Log { address: Address::with_last_byte(4), ..Default::default() });
let receipts = vec![receipt.clone()];
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that the receipts are pruned (empty)
assert_eq!(recorder.receipts().len(), 1);
assert_eq!(recorder.receipts()[0], vec![None]);
// With a receipt that should not be pruned (address 1 in the log filter)
let mut receipt1 = Receipt::default();
receipt1.logs.push(Log { address: Address::with_last_byte(1), ..Default::default() });
let receipts = vec![receipt1.clone()];
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that the second block of receipts contains the receipt
assert_eq!(recorder.receipts().len(), 2);
assert_eq!(recorder.receipts()[1][0], Some(receipt1));
// With a receipt that should not be pruned (address 2 in the log filter)
let mut receipt2 = Receipt::default();
receipt2.logs.push(Log { address: Address::with_last_byte(2), ..Default::default() });
let receipts = vec![receipt2.clone()];
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that the third block of receipts contains the receipt
assert_eq!(recorder.receipts().len(), 3);
assert_eq!(recorder.receipts()[2][0], Some(receipt2));
// With a receipt that should not be pruned (address 3 in the log filter)
let mut receipt3 = Receipt::default();
receipt3.logs.push(Log { address: Address::with_last_byte(3), ..Default::default() });
let receipts = vec![receipt3.clone()];
assert!(recorder.save_receipts(receipts).is_ok());
// Verify that the fourth block of receipts contains the receipt
assert_eq!(recorder.receipts().len(), 4);
assert_eq!(recorder.receipts()[3][0], Some(receipt3));
}
}