Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion crates/config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ pub struct PruneConfig {

impl Default for PruneConfig {
fn default() -> Self {
Self { block_interval: DEFAULT_BLOCK_INTERVAL, segments: PruneModes::none() }
Self { block_interval: DEFAULT_BLOCK_INTERVAL, segments: PruneModes::default() }
}
}

Expand All @@ -464,6 +464,7 @@ impl PruneConfig {
account_history,
storage_history,
bodies_history,
merkle_changesets,
receipts_log_filter,
},
} = other;
Expand All @@ -480,6 +481,8 @@ impl PruneConfig {
self.segments.account_history = self.segments.account_history.or(account_history);
self.segments.storage_history = self.segments.storage_history.or(storage_history);
self.segments.bodies_history = self.segments.bodies_history.or(bodies_history);
// Merkle changesets is not optional, so we just replace it if provided
self.segments.merkle_changesets = merkle_changesets;

if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() {
self.segments.receipts_log_filter = receipts_log_filter;
Expand Down Expand Up @@ -1001,6 +1004,7 @@ receipts = 'full'
account_history: None,
storage_history: Some(PruneMode::Before(5000)),
bodies_history: None,
merkle_changesets: PruneMode::Before(0),
receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([(
Address::random(),
PruneMode::Full,
Expand All @@ -1017,6 +1021,7 @@ receipts = 'full'
account_history: Some(PruneMode::Distance(2000)),
storage_history: Some(PruneMode::Distance(3000)),
bodies_history: None,
merkle_changesets: PruneMode::Distance(10000),
receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([
(Address::random(), PruneMode::Distance(1000)),
(Address::random(), PruneMode::Before(2000)),
Expand All @@ -1035,6 +1040,7 @@ receipts = 'full'
assert_eq!(config1.segments.receipts, Some(PruneMode::Distance(1000)));
assert_eq!(config1.segments.account_history, Some(PruneMode::Distance(2000)));
assert_eq!(config1.segments.storage_history, Some(PruneMode::Before(5000)));
assert_eq!(config1.segments.merkle_changesets, PruneMode::Distance(10000));
assert_eq!(config1.segments.receipts_log_filter, original_filter);
}

Expand Down
2 changes: 1 addition & 1 deletion crates/exex/exex/src/backfill/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl<E, P> BackfillJobFactory<E, P> {
Self {
evm_config,
provider,
prune_modes: PruneModes::none(),
prune_modes: PruneModes::default(),
thresholds: ExecutionStageThresholds {
// Default duration for a database transaction to be considered long-lived is
// 60 seconds, so we limit the backfill job to the half of it to be sure we finish
Expand Down
1 change: 1 addition & 0 deletions crates/node/core/src/args/pruning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ impl PruningArgs {
storage_history: Some(PruneMode::Distance(MINIMUM_PRUNING_DISTANCE)),
// TODO: set default to pre-merge block if available
bodies_history: None,
merkle_changesets: PruneMode::Distance(MINIMUM_PRUNING_DISTANCE),
receipts_log_filter: Default::default(),
},
}
Expand Down
8 changes: 5 additions & 3 deletions crates/prune/prune/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use reth_db_api::{table::Value, transaction::DbTxMut};
use reth_exex_types::FinishedExExHeight;
use reth_primitives_traits::NodePrimitives;
use reth_provider::{
providers::StaticFileProvider, BlockReader, DBProvider, DatabaseProviderFactory,
NodePrimitivesProvider, PruneCheckpointReader, PruneCheckpointWriter,
providers::StaticFileProvider, BlockReader, ChainStateBlockReader, DBProvider,
DatabaseProviderFactory, NodePrimitivesProvider, PruneCheckpointReader, PruneCheckpointWriter,
StaticFileProviderFactory,
};
use reth_prune_types::PruneModes;
Expand Down Expand Up @@ -83,6 +83,7 @@ impl PrunerBuilder {
ProviderRW: PruneCheckpointWriter
+ PruneCheckpointReader
+ BlockReader<Transaction: Encodable2718>
+ ChainStateBlockReader
+ StaticFileProviderFactory<
Primitives: NodePrimitives<SignedTx: Value, Receipt: Value, BlockHeader: Value>,
>,
Expand Down Expand Up @@ -113,6 +114,7 @@ impl PrunerBuilder {
Primitives: NodePrimitives<SignedTx: Value, Receipt: Value, BlockHeader: Value>,
> + DBProvider<Tx: DbTxMut>
+ BlockReader<Transaction: Encodable2718>
+ ChainStateBlockReader
+ PruneCheckpointWriter
+ PruneCheckpointReader,
{
Expand All @@ -132,7 +134,7 @@ impl Default for PrunerBuilder {
fn default() -> Self {
Self {
block_interval: 5,
segments: PruneModes::none(),
segments: PruneModes::default(),
delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
timeout: None,
finished_exex_height: watch::channel(FinishedExExHeight::NoExExs).1,
Expand Down
14 changes: 9 additions & 5 deletions crates/prune/prune/src/segments/set.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::segments::{
AccountHistory, ReceiptsByLogs, Segment, SenderRecovery, StorageHistory, TransactionLookup,
UserReceipts,
AccountHistory, MerkleChangeSets, ReceiptsByLogs, Segment, SenderRecovery, StorageHistory,
TransactionLookup, UserReceipts,
};
use alloy_eips::eip2718::Encodable2718;
use reth_db_api::{table::Value, transaction::DbTxMut};
use reth_primitives_traits::NodePrimitives;
use reth_provider::{
providers::StaticFileProvider, BlockReader, DBProvider, PruneCheckpointReader,
PruneCheckpointWriter, StaticFileProviderFactory,
providers::StaticFileProvider, BlockReader, ChainStateBlockReader, DBProvider,
PruneCheckpointReader, PruneCheckpointWriter, StaticFileProviderFactory,
};
use reth_prune_types::PruneModes;

Expand Down Expand Up @@ -52,7 +52,8 @@ where
> + DBProvider<Tx: DbTxMut>
+ PruneCheckpointWriter
+ PruneCheckpointReader
+ BlockReader<Transaction: Encodable2718>,
+ BlockReader<Transaction: Encodable2718>
+ ChainStateBlockReader,
{
/// Creates a [`SegmentSet`] from an existing components, such as [`StaticFileProvider`] and
/// [`PruneModes`].
Expand All @@ -67,6 +68,7 @@ where
account_history,
storage_history,
bodies_history: _,
merkle_changesets,
receipts_log_filter,
} = prune_modes;

Expand All @@ -77,6 +79,8 @@ where
.segment(StaticFileTransactions::new(static_file_provider.clone()))
// Static file receipts
.segment(StaticFileReceipts::new(static_file_provider))
// Merkle changesets
.segment(MerkleChangeSets::new(merkle_changesets))
// Account history
.segment_opt(account_history.map(AccountHistory::new))
// Storage history
Expand Down
21 changes: 15 additions & 6 deletions crates/prune/prune/src/segments/user/merkle_change_sets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use crate::{
segments::{PruneInput, Segment},
PrunerError,
};
use reth_db_api::{models::BlockNumberAddress, table::Value, tables, transaction::DbTxMut};
use alloy_primitives::B256;
use reth_db_api::{models::BlockNumberHashedAddress, table::Value, tables, transaction::DbTxMut};
use reth_primitives_traits::NodePrimitives;
use reth_provider::{
errors::provider::ProviderResult, BlockReader, DBProvider, NodePrimitivesProvider,
PruneCheckpointWriter, TransactionsProvider,
errors::provider::ProviderResult, BlockReader, ChainStateBlockReader, DBProvider,
NodePrimitivesProvider, PruneCheckpointWriter, TransactionsProvider,
};
use reth_prune_types::{
PruneCheckpoint, PruneMode, PrunePurpose, PruneSegment, SegmentOutput, SegmentOutputCheckpoint,
Expand All @@ -31,6 +32,7 @@ where
+ PruneCheckpointWriter
+ TransactionsProvider
+ BlockReader
+ ChainStateBlockReader
+ NodePrimitivesProvider<Primitives: NodePrimitives<Receipt: Value>>,
{
fn segment(&self) -> PruneSegment {
Expand All @@ -55,13 +57,20 @@ where
let block_range_end = *block_range.end();
let mut limiter = input.limiter;

// Create range for StoragesTrieChangeSets which uses BlockNumberHashedAddress as key
let storage_range_start: BlockNumberHashedAddress =
(*block_range.start(), B256::ZERO).into();
let storage_range_end: BlockNumberHashedAddress =
(*block_range.end() + 1, B256::ZERO).into();
let storage_range = storage_range_start..storage_range_end;

let mut last_storages_pruned_block = None;
let (storages_pruned, done) =
provider.tx_ref().prune_table_with_range::<tables::StorageChangeSets>(
BlockNumberAddress::range(block_range.clone()),
provider.tx_ref().prune_table_with_range::<tables::StoragesTrieChangeSets>(
storage_range,
&mut limiter,
|_| false,
|(BlockNumberAddress((block_number, ..)), ..)| {
|(BlockNumberHashedAddress((block_number, _)), _)| {
last_storages_pruned_block = Some(block_number);
},
)?;
Expand Down
84 changes: 68 additions & 16 deletions crates/prune/types/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ pub enum HistoryType {
StorageHistory,
}

/// Default pruning mode for merkle changesets - aggressively prune to finalized block
const fn default_merkle_changesets_mode() -> PruneMode {
PruneMode::Distance(MINIMUM_PRUNING_DISTANCE)
}

/// Pruning configuration for every segment of the data that can be pruned.
#[derive(Debug, Clone, Default, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "serde"), serde(default))]
pub struct PruneModes {
Expand Down Expand Up @@ -84,6 +89,16 @@ pub struct PruneModes {
)
)]
pub bodies_history: Option<PruneMode>,
/// Merkle Changesets pruning configuration for `AccountsTrieChangeSets` and
/// `StoragesTrieChangeSets`.
#[cfg_attr(
any(test, feature = "serde"),
serde(
default = "default_merkle_changesets_mode",
deserialize_with = "deserialize_prune_mode_with_min_blocks::<MINIMUM_PRUNING_DISTANCE, _>"
)
)]
pub merkle_changesets: PruneMode,
/// Receipts pruning configuration by retaining only those receipts that contain logs emitted
/// by the specified addresses, discarding others. This setting is overridden by `receipts`.
///
Expand All @@ -92,12 +107,22 @@ pub struct PruneModes {
pub receipts_log_filter: ReceiptsLogPruneConfig,
}

impl PruneModes {
/// Sets pruning to no target.
pub fn none() -> Self {
Self::default()
impl Default for PruneModes {
fn default() -> Self {
Self {
sender_recovery: None,
transaction_lookup: None,
receipts: None,
account_history: None,
storage_history: None,
bodies_history: None,
merkle_changesets: default_merkle_changesets_mode(),
receipts_log_filter: ReceiptsLogPruneConfig::default(),
}
}
}

impl PruneModes {
/// Sets pruning to all targets.
pub fn all() -> Self {
Self {
Expand All @@ -107,6 +132,7 @@ impl PruneModes {
account_history: Some(PruneMode::Full),
storage_history: Some(PruneMode::Full),
bodies_history: Some(PruneMode::Full),
merkle_changesets: PruneMode::Full,
receipts_log_filter: Default::default(),
}
}
Expand All @@ -116,11 +142,6 @@ impl PruneModes {
self.receipts.is_some() || !self.receipts_log_filter.is_empty()
}

/// Returns true if all prune modes are set to [`None`].
pub fn is_empty(&self) -> bool {
self == &Self::none()
}

/// Returns an error if we can't unwind to the targeted block because the target block is
/// outside the range.
///
Expand Down Expand Up @@ -170,6 +191,28 @@ impl PruneModes {
}
}

/// Deserializes [`PruneMode`] and validates that the value is not less than the const
/// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be
/// left in database after the pruning.
///
/// 1. For [`PruneMode::Full`], it fails if `MIN_BLOCKS > 0`.
/// 2. For [`PruneMode::Distance`], it fails if `distance < MIN_BLOCKS + 1`. `+ 1` is needed because
/// `PruneMode::Distance(0)` means that we leave zero blocks from the latest, meaning we have one
/// block in the database.
#[cfg(any(test, feature = "serde"))]
fn deserialize_prune_mode_with_min_blocks<
'de,
const MIN_BLOCKS: u64,
D: serde::Deserializer<'de>,
>(
deserializer: D,
) -> Result<PruneMode, D::Error> {
use serde::Deserialize;
let prune_mode = PruneMode::deserialize(deserializer)?;
serde_deserialize_validate::<MIN_BLOCKS, D>(&prune_mode)?;
Ok(prune_mode)
}

/// Deserializes [`Option<PruneMode>`] and validates that the value is not less than the const
/// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be
/// left in database after the pruning.
Expand All @@ -186,28 +229,37 @@ fn deserialize_opt_prune_mode_with_min_blocks<
>(
deserializer: D,
) -> Result<Option<PruneMode>, D::Error> {
use alloc::format;
use serde::Deserialize;
let prune_mode = Option::<PruneMode>::deserialize(deserializer)?;
if let Some(prune_mode) = prune_mode.as_ref() {
serde_deserialize_validate::<MIN_BLOCKS, D>(prune_mode)?;
}
Ok(prune_mode)
}

#[cfg(any(test, feature = "serde"))]
fn serde_deserialize_validate<'a, 'de, const MIN_BLOCKS: u64, D: serde::Deserializer<'de>>(
prune_mode: &'a PruneMode,
) -> Result<(), D::Error> {
use alloc::format;
match prune_mode {
Some(PruneMode::Full) if MIN_BLOCKS > 0 => {
PruneMode::Full if MIN_BLOCKS > 0 => {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str("full"),
// This message should have "expected" wording
&format!("prune mode that leaves at least {MIN_BLOCKS} blocks in the database")
.as_str(),
))
}
Some(PruneMode::Distance(distance)) if distance < MIN_BLOCKS => {
PruneMode::Distance(distance) if *distance < MIN_BLOCKS => {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Unsigned(distance),
serde::de::Unexpected::Unsigned(*distance),
// This message should have "expected" wording
&format!("prune mode that leaves at least {MIN_BLOCKS} blocks in the database")
.as_str(),
))
}
_ => Ok(prune_mode),
_ => Ok(()),
}
}

Expand Down Expand Up @@ -240,7 +292,7 @@ mod tests {
#[test]
fn test_unwind_target_unpruned() {
// Test case 1: No pruning configured - should always succeed
let prune_modes = PruneModes::none();
let prune_modes = PruneModes::default();
assert!(prune_modes.ensure_unwind_target_unpruned(1000, 500, &[]).is_ok());
assert!(prune_modes.ensure_unwind_target_unpruned(1000, 0, &[]).is_ok());

Expand Down
14 changes: 7 additions & 7 deletions crates/stages/stages/src/sets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use reth_primitives_traits::{Block, NodePrimitives};
use reth_provider::HeaderSyncGapProvider;
use reth_prune_types::PruneModes;
use reth_stages_api::Stage;
use std::{ops::Not, sync::Arc};
use std::sync::Arc;
use tokio::sync::watch;

/// A set containing all stages to run a fully syncing instance of reth.
Expand Down Expand Up @@ -337,12 +337,12 @@ where
stages_config: self.stages_config.clone(),
prune_modes: self.prune_modes.clone(),
})
// If any prune modes are set, add the prune stage.
.add_stage_opt(self.prune_modes.is_empty().not().then(|| {
// Prune stage should be added after all hashing stages, because otherwise it will
// delete
PruneStage::new(self.prune_modes.clone(), self.stages_config.prune.commit_threshold)
}))
// Prune stage should be added after all hashing stages, because otherwise it will
// delete
.add_stage(PruneStage::new(
self.prune_modes.clone(),
self.stages_config.prune.commit_threshold,
))
}
}

Expand Down
Loading