mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-08 03:01:12 -04:00
feat(node): --minimal flag (#20960)
This commit is contained in:
@@ -1079,18 +1079,6 @@ transaction_lookup = 'full'
|
||||
receipts = { distance = 16384 }
|
||||
#";
|
||||
let _conf: Config = toml::from_str(s).unwrap();
|
||||
|
||||
let s = r"#
|
||||
[prune]
|
||||
block_interval = 5
|
||||
|
||||
[prune.segments]
|
||||
sender_recovery = { distance = 16384 }
|
||||
transaction_lookup = 'full'
|
||||
receipts = 'full'
|
||||
#";
|
||||
let err = toml::from_str::<Config>(s).unwrap_err().to_string();
|
||||
assert!(err.contains("invalid value: string \"full\""), "{}", err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -170,7 +170,8 @@ impl LaunchContext {
|
||||
toml_config.peers.trusted_nodes_only = config.network.trusted_only;
|
||||
|
||||
// Merge static file CLI arguments with config file, giving priority to CLI
|
||||
toml_config.static_files = config.static_files.merge_with_config(toml_config.static_files);
|
||||
toml_config.static_files =
|
||||
config.static_files.merge_with_config(toml_config.static_files, config.pruning.minimal);
|
||||
|
||||
Ok(toml_config)
|
||||
}
|
||||
@@ -1301,6 +1302,7 @@ mod tests {
|
||||
let node_config = NodeConfig {
|
||||
pruning: PruningArgs {
|
||||
full: true,
|
||||
minimal: false,
|
||||
block_interval: None,
|
||||
sender_recovery_full: false,
|
||||
sender_recovery_distance: None,
|
||||
|
||||
@@ -78,7 +78,7 @@ pub use era::{DefaultEraHost, EraArgs, EraSourceArgs};
|
||||
|
||||
/// `StaticFilesArgs` for configuring static files.
|
||||
mod static_files;
|
||||
pub use static_files::StaticFilesArgs;
|
||||
pub use static_files::{StaticFilesArgs, MINIMAL_BLOCKS_PER_FILE};
|
||||
|
||||
mod error;
|
||||
pub mod types;
|
||||
|
||||
@@ -16,9 +16,18 @@ use std::{collections::BTreeMap, ops::Not};
|
||||
#[command(next_help_heading = "Pruning")]
|
||||
pub struct PruningArgs {
|
||||
/// Run full node. Only the most recent [`MINIMUM_PRUNING_DISTANCE`] block states are stored.
|
||||
#[arg(long, default_value_t = false)]
|
||||
#[arg(long, default_value_t = false, conflicts_with = "minimal")]
|
||||
pub full: bool,
|
||||
|
||||
/// Run minimal storage mode with maximum pruning and smaller static files.
|
||||
///
|
||||
/// This mode configures the node to use minimal disk space by:
|
||||
/// - Fully pruning sender recovery, transaction lookup, receipts
|
||||
/// - Leaving 10,064 blocks for account, storage history and block bodies
|
||||
/// - Using 10,000 blocks per static file segment
|
||||
#[arg(long, default_value_t = false, conflicts_with = "full")]
|
||||
pub minimal: bool,
|
||||
|
||||
/// Minimum pruning interval measured in blocks.
|
||||
#[arg(long = "prune.block-interval", alias = "block-interval", value_parser = RangedU64ValueParser::<u64>::new().range(1..))]
|
||||
pub block_interval: Option<u64>,
|
||||
@@ -140,6 +149,23 @@ impl PruningArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// If --minimal is set, use minimal storage mode with aggressive pruning.
|
||||
if self.minimal {
|
||||
config = PruneConfig {
|
||||
block_interval: config.block_interval,
|
||||
segments: PruneModes {
|
||||
sender_recovery: Some(PruneMode::Full),
|
||||
transaction_lookup: Some(PruneMode::Full),
|
||||
receipts: Some(PruneMode::Full),
|
||||
account_history: Some(PruneMode::Distance(10064)),
|
||||
storage_history: Some(PruneMode::Distance(10064)),
|
||||
bodies_history: Some(PruneMode::Distance(10064)),
|
||||
merkle_changesets: PruneMode::Distance(MERKLE_CHANGESETS_RETENTION_BLOCKS),
|
||||
receipts_log_filter: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Override with any explicitly set prune.* flags.
|
||||
if let Some(block_interval) = self.block_interval {
|
||||
config.block_interval = block_interval as usize;
|
||||
|
||||
@@ -4,6 +4,11 @@ use clap::Args;
|
||||
use reth_config::config::{BlocksPerFileConfig, StaticFilesConfig};
|
||||
use reth_provider::StorageSettings;
|
||||
|
||||
/// Blocks per static file when running in `--minimal` node.
|
||||
///
|
||||
/// 10000 blocks per static file allows us to prune all history every 10k blocks.
|
||||
pub const MINIMAL_BLOCKS_PER_FILE: u64 = 10000;
|
||||
|
||||
/// Parameters for static files configuration
|
||||
#[derive(Debug, Args, PartialEq, Eq, Default, Clone, Copy)]
|
||||
#[command(next_help_heading = "Static Files")]
|
||||
@@ -61,14 +66,25 @@ pub struct StaticFilesArgs {
|
||||
impl StaticFilesArgs {
|
||||
/// Merges the CLI arguments with an existing [`StaticFilesConfig`], giving priority to CLI
|
||||
/// args.
|
||||
pub fn merge_with_config(&self, config: StaticFilesConfig) -> StaticFilesConfig {
|
||||
///
|
||||
/// If `minimal` is true, uses [`MINIMAL_BLOCKS_PER_FILE`] blocks per file as the default for
|
||||
/// headers, transactions, and receipts segments.
|
||||
pub fn merge_with_config(&self, config: StaticFilesConfig, minimal: bool) -> StaticFilesConfig {
|
||||
let minimal_blocks_per_file = minimal.then_some(MINIMAL_BLOCKS_PER_FILE);
|
||||
StaticFilesConfig {
|
||||
blocks_per_file: BlocksPerFileConfig {
|
||||
headers: self.blocks_per_file_headers.or(config.blocks_per_file.headers),
|
||||
headers: self
|
||||
.blocks_per_file_headers
|
||||
.or(minimal_blocks_per_file)
|
||||
.or(config.blocks_per_file.headers),
|
||||
transactions: self
|
||||
.blocks_per_file_transactions
|
||||
.or(minimal_blocks_per_file)
|
||||
.or(config.blocks_per_file.transactions),
|
||||
receipts: self.blocks_per_file_receipts.or(config.blocks_per_file.receipts),
|
||||
receipts: self
|
||||
.blocks_per_file_receipts
|
||||
.or(minimal_blocks_per_file)
|
||||
.or(config.blocks_per_file.receipts),
|
||||
transaction_senders: self
|
||||
.blocks_per_file_transaction_senders
|
||||
.or(config.blocks_per_file.transaction_senders),
|
||||
|
||||
@@ -42,15 +42,15 @@ impl PruneMode {
|
||||
purpose: PrunePurpose,
|
||||
) -> Result<Option<(BlockNumber, Self)>, PruneSegmentError> {
|
||||
let result = match self {
|
||||
Self::Full if segment.min_blocks(purpose) == 0 => Some((tip, *self)),
|
||||
Self::Full if segment.min_blocks() == 0 => Some((tip, *self)),
|
||||
Self::Distance(distance) if *distance > tip => None, // Nothing to prune yet
|
||||
Self::Distance(distance) if *distance >= segment.min_blocks(purpose) => {
|
||||
Self::Distance(distance) if *distance >= segment.min_blocks() => {
|
||||
Some((tip - distance, *self))
|
||||
}
|
||||
Self::Before(n) if *n == tip + 1 && purpose.is_static_file() => Some((tip, *self)),
|
||||
Self::Before(n) if *n > tip => None, // Nothing to prune yet
|
||||
Self::Before(n) => {
|
||||
(tip - n >= segment.min_blocks(purpose)).then(|| ((*n).saturating_sub(1), *self))
|
||||
(tip - n >= segment.min_blocks()).then(|| ((*n).saturating_sub(1), *self))
|
||||
}
|
||||
_ => return Err(PruneSegmentError::Configuration(segment)),
|
||||
};
|
||||
@@ -93,7 +93,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prune_target_block() {
|
||||
let tip = 20000;
|
||||
let segment = PruneSegment::Receipts;
|
||||
let segment = PruneSegment::AccountHistory;
|
||||
|
||||
let tests = vec![
|
||||
// MINIMUM_PRUNING_DISTANCE makes this impossible
|
||||
@@ -101,8 +101,8 @@ mod tests {
|
||||
// Nothing to prune
|
||||
(PruneMode::Distance(tip + 1), Ok(None)),
|
||||
(
|
||||
PruneMode::Distance(segment.min_blocks(PrunePurpose::User) + 1),
|
||||
Ok(Some(tip - (segment.min_blocks(PrunePurpose::User) + 1))),
|
||||
PruneMode::Distance(segment.min_blocks() + 1),
|
||||
Ok(Some(tip - (segment.min_blocks() + 1))),
|
||||
),
|
||||
// Nothing to prune
|
||||
(PruneMode::Before(tip + 1), Ok(None)),
|
||||
|
||||
@@ -61,15 +61,12 @@ impl PruneSegment {
|
||||
}
|
||||
|
||||
/// Returns minimum number of blocks to keep in the database for this segment.
|
||||
pub const fn min_blocks(&self, purpose: PrunePurpose) -> u64 {
|
||||
pub const fn min_blocks(&self) -> u64 {
|
||||
match self {
|
||||
Self::SenderRecovery | Self::TransactionLookup => 0,
|
||||
Self::Receipts if purpose.is_static_file() => 0,
|
||||
Self::ContractLogs |
|
||||
Self::AccountHistory |
|
||||
Self::StorageHistory |
|
||||
Self::Bodies |
|
||||
Self::Receipts => MINIMUM_PRUNING_DISTANCE,
|
||||
Self::SenderRecovery | Self::TransactionLookup | Self::Receipts | Self::Bodies => 0,
|
||||
Self::ContractLogs | Self::AccountHistory | Self::StorageHistory => {
|
||||
MINIMUM_PRUNING_DISTANCE
|
||||
}
|
||||
Self::MerkleChangeSets => MERKLE_CHANGESETS_RETENTION_BLOCKS,
|
||||
#[expect(deprecated)]
|
||||
#[expect(clippy::match_same_arms)]
|
||||
|
||||
@@ -58,13 +58,7 @@ pub struct PruneModes {
|
||||
pub transaction_lookup: Option<PruneMode>,
|
||||
/// Receipts pruning configuration. This setting overrides `receipts_log_filter`
|
||||
/// and offers improved performance.
|
||||
#[cfg_attr(
|
||||
any(test, feature = "serde"),
|
||||
serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "deserialize_opt_prune_mode_with_min_blocks::<MINIMUM_PRUNING_DISTANCE, _>"
|
||||
)
|
||||
)]
|
||||
#[cfg_attr(any(test, feature = "serde"), serde(skip_serializing_if = "Option::is_none",))]
|
||||
pub receipts: Option<PruneMode>,
|
||||
/// Account History pruning configuration.
|
||||
#[cfg_attr(
|
||||
@@ -85,13 +79,7 @@ pub struct PruneModes {
|
||||
)]
|
||||
pub storage_history: Option<PruneMode>,
|
||||
/// Bodies History pruning configuration.
|
||||
#[cfg_attr(
|
||||
any(test, feature = "serde"),
|
||||
serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "deserialize_opt_prune_mode_with_min_blocks::<MINIMUM_PRUNING_DISTANCE, _>"
|
||||
)
|
||||
)]
|
||||
#[cfg_attr(any(test, feature = "serde"), serde(skip_serializing_if = "Option::is_none",))]
|
||||
pub bodies_history: Option<PruneMode>,
|
||||
/// Merkle Changesets pruning configuration for `AccountsTrieChangeSets` and
|
||||
/// `StoragesTrieChangeSets`.
|
||||
|
||||
Reference in New Issue
Block a user