mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
validate config
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -9118,6 +9118,7 @@ dependencies = [
|
||||
"serde",
|
||||
"shellexpand",
|
||||
"strum 0.27.2",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Configuration files.
|
||||
use reth_network_types::{PeersConfig, SessionsConfig};
|
||||
use reth_prune_types::PruneModes;
|
||||
use reth_prune_types::{PruneModes, PruneSegmentError};
|
||||
use reth_stages_types::ExecutionStageThresholds;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
@@ -455,6 +455,11 @@ impl PruneConfig {
|
||||
self.segments.receipts.is_some()
|
||||
}
|
||||
|
||||
/// Validates the configuration.
|
||||
pub fn validate(&self) -> Result<(), PruneSegmentError> {
|
||||
self.segments.receipts_log_filter.validate()
|
||||
}
|
||||
|
||||
/// Merges another `PruneConfig` into this one, taking values from the other config if and only
|
||||
/// if the corresponding value in this config is not set.
|
||||
pub fn merge(&mut self, other: Self) {
|
||||
|
||||
@@ -1172,7 +1172,6 @@ mod tests {
|
||||
storage_history_before: None,
|
||||
bodies_pre_merge: false,
|
||||
bodies_distance: None,
|
||||
#[expect(deprecated)]
|
||||
receipts_log_filter: None,
|
||||
bodies_before: None,
|
||||
},
|
||||
|
||||
@@ -44,14 +44,15 @@ alloy-consensus.workspace = true
|
||||
alloy-eips.workspace = true
|
||||
|
||||
# misc
|
||||
eyre.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
derive_more.workspace = true
|
||||
eyre.workspace = true
|
||||
humantime.workspace = true
|
||||
rand.workspace = true
|
||||
derive_more.workspace = true
|
||||
toml.workspace = true
|
||||
serde.workspace = true
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
toml.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
# io
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
//! Pruning and full node arguments
|
||||
|
||||
use std::ops::Not;
|
||||
use std::{num::ParseIntError, ops::Not};
|
||||
|
||||
use crate::primitives::EthereumHardfork;
|
||||
use alloy_primitives::BlockNumber;
|
||||
use alloy_primitives::{Address, BlockNumber};
|
||||
use clap::{builder::RangedU64ValueParser, Args};
|
||||
use reth_chainspec::EthereumHardforks;
|
||||
use reth_config::config::PruneConfig;
|
||||
use reth_prune_types::{PruneMode, PruneModes, ReceiptsLogPruneConfig, MINIMUM_PRUNING_DISTANCE};
|
||||
use reth_prune_types::{
|
||||
PruneMode, PruneModes, PruneSegmentError, ReceiptsLogPruneConfig, MINIMUM_PRUNING_DISTANCE,
|
||||
};
|
||||
|
||||
/// Parameters for pruning and full node
|
||||
#[derive(Debug, Clone, Args, PartialEq, Eq, Default)]
|
||||
@@ -61,14 +63,8 @@ pub struct PruningArgs {
|
||||
#[arg(long = "prune.receipts.before", value_name = "BLOCK_NUMBER", conflicts_with_all = &["receipts_full", "receipts_pre_merge", "receipts_distance"])]
|
||||
pub receipts_before: Option<BlockNumber>,
|
||||
/// Receipts Log Filter
|
||||
#[arg(
|
||||
long = "prune.receipts-log-filter",
|
||||
alias = "prune.receiptslogfilter",
|
||||
value_name = "FILTER_CONFIG",
|
||||
hide = true
|
||||
)]
|
||||
#[deprecated]
|
||||
pub receipts_log_filter: Option<String>,
|
||||
#[arg(long = "prune.receiptslogfilter", value_name = "FILTER_CONFIG", conflicts_with_all = &["receipts_full", "receipts_pre_merge", "receipts_distance", "receipts_before"], value_parser = parse_receipts_log_filter)]
|
||||
pub receipts_log_filter: Option<ReceiptsLogPruneConfig>,
|
||||
|
||||
// Account History
|
||||
/// Prunes all account history.
|
||||
@@ -136,7 +132,7 @@ impl PruningArgs {
|
||||
.block_number()
|
||||
.map(PruneMode::Before),
|
||||
merkle_changesets: PruneMode::Distance(MINIMUM_PRUNING_DISTANCE),
|
||||
receipts_log_filter: ReceiptsLogPruneConfig::empty(),
|
||||
receipts_log_filter: ReceiptsLogPruneConfig::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -164,15 +160,6 @@ impl PruningArgs {
|
||||
config.segments.storage_history = Some(mode);
|
||||
}
|
||||
|
||||
// Log warning if receipts_log_filter is set (deprecated feature)
|
||||
#[expect(deprecated)]
|
||||
if self.receipts_log_filter.is_some() {
|
||||
tracing::warn!(
|
||||
target: "reth::cli",
|
||||
"The --prune.receiptslogfilter flag is deprecated and has no effect. It will be removed in a future release."
|
||||
);
|
||||
}
|
||||
|
||||
config.is_default().not().then_some(config)
|
||||
}
|
||||
|
||||
@@ -258,3 +245,165 @@ impl PruningArgs {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error while parsing a `[ReceiptsLogPruneConfig`]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub(crate) enum ReceiptsLogError {
|
||||
/// The format of the filter is invalid.
|
||||
#[error("invalid filter format: {0}")]
|
||||
InvalidFilterFormat(String),
|
||||
/// Address is invalid.
|
||||
#[error("address is invalid: {0}")]
|
||||
InvalidAddress(String),
|
||||
/// The prune mode is not one of full, distance, before.
|
||||
#[error("prune mode is invalid: {0}")]
|
||||
InvalidPruneMode(String),
|
||||
/// The distance value supplied is invalid.
|
||||
#[error("distance is invalid: {0}")]
|
||||
InvalidDistance(ParseIntError),
|
||||
/// The block number supplied is invalid.
|
||||
#[error("block number is invalid: {0}")]
|
||||
InvalidBlockNumber(ParseIntError),
|
||||
#[error(transparent)]
|
||||
PruneSegment(#[from] PruneSegmentError),
|
||||
}
|
||||
|
||||
/// Parses `,` separated pruning info into [`ReceiptsLogPruneConfig`].
|
||||
pub(crate) fn parse_receipts_log_filter(
|
||||
value: &str,
|
||||
) -> Result<ReceiptsLogPruneConfig, ReceiptsLogError> {
|
||||
let mut config = ReceiptsLogPruneConfig::new();
|
||||
// Split out each of the filters.
|
||||
let filters = value.split(',');
|
||||
for filter in filters {
|
||||
let parts: Vec<&str> = filter.split(':').collect();
|
||||
if parts.len() < 2 {
|
||||
return Err(ReceiptsLogError::InvalidFilterFormat(filter.to_string()));
|
||||
}
|
||||
// Parse the address
|
||||
let address = parts[0]
|
||||
.parse::<Address>()
|
||||
.map_err(|_| ReceiptsLogError::InvalidAddress(parts[0].to_string()))?;
|
||||
|
||||
// Parse the prune mode
|
||||
let prune_mode = match parts[1] {
|
||||
"full" => PruneMode::Full,
|
||||
s if s.starts_with("distance") => {
|
||||
if parts.len() < 3 {
|
||||
return Err(ReceiptsLogError::InvalidFilterFormat(filter.to_string()));
|
||||
}
|
||||
let distance =
|
||||
parts[2].parse::<u64>().map_err(ReceiptsLogError::InvalidDistance)?;
|
||||
PruneMode::Distance(distance)
|
||||
}
|
||||
s if s.starts_with("before") => {
|
||||
if parts.len() < 3 {
|
||||
return Err(ReceiptsLogError::InvalidFilterFormat(filter.to_string()));
|
||||
}
|
||||
let block_number =
|
||||
parts[2].parse::<u64>().map_err(ReceiptsLogError::InvalidBlockNumber)?;
|
||||
PruneMode::Before(block_number)
|
||||
}
|
||||
_ => return Err(ReceiptsLogError::InvalidPruneMode(parts[1].to_string())),
|
||||
};
|
||||
config.insert(address, prune_mode);
|
||||
}
|
||||
|
||||
config.validate()?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloy_primitives::address;
|
||||
use clap::Parser;
|
||||
|
||||
/// A helper type to parse Args more easily
|
||||
#[derive(Parser)]
|
||||
struct CommandParser<T: Args> {
|
||||
#[command(flatten)]
|
||||
args: T,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pruning_args_sanity_check() {
|
||||
let args = CommandParser::<PruningArgs>::parse_from([
|
||||
"reth",
|
||||
"--prune.receiptslogfilter",
|
||||
"0x0000000000000000000000000000000000000003:before:5000000",
|
||||
])
|
||||
.args;
|
||||
let mut config = ReceiptsLogPruneConfig::default();
|
||||
config.insert(
|
||||
address!("0x0000000000000000000000000000000000000003"),
|
||||
PruneMode::Before(5000000),
|
||||
);
|
||||
assert_eq!(args.receipts_log_filter, Some(config));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_receiptslogfilter() {
|
||||
let default_args = PruningArgs::default();
|
||||
let args = CommandParser::<PruningArgs>::parse_from(["reth"]).args;
|
||||
assert_eq!(args, default_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_receipts_log_filter() {
|
||||
let filter1 = "0x0000000000000000000000000000000000000001:full";
|
||||
let filter2 = "0x0000000000000000000000000000000000000002:distance:1000";
|
||||
let filter3 = "0x0000000000000000000000000000000000000003:before:5000000";
|
||||
let filters = [filter1, filter2, filter3].join(",");
|
||||
|
||||
// Args can be parsed.
|
||||
let result = parse_receipts_log_filter(&filters);
|
||||
assert!(result.is_ok());
|
||||
let config = result.unwrap();
|
||||
assert_eq!(config.len(), 3);
|
||||
|
||||
// Check that the args were parsed correctly.
|
||||
let addr1: Address = "0x0000000000000000000000000000000000000001".parse().unwrap();
|
||||
let addr2: Address = "0x0000000000000000000000000000000000000002".parse().unwrap();
|
||||
let addr3: Address = "0x0000000000000000000000000000000000000003".parse().unwrap();
|
||||
|
||||
assert_eq!(config.get(&addr1), Some(&PruneMode::Full));
|
||||
assert_eq!(config.get(&addr2), Some(&PruneMode::Distance(1000)));
|
||||
assert_eq!(config.get(&addr3), Some(&PruneMode::Before(5000000)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_receipts_log_filter_invalid_filter_format() {
|
||||
let result = parse_receipts_log_filter("invalid_format");
|
||||
assert!(matches!(result, Err(ReceiptsLogError::InvalidFilterFormat(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_receipts_log_filter_invalid_address() {
|
||||
let result = parse_receipts_log_filter("invalid_address:full");
|
||||
assert!(matches!(result, Err(ReceiptsLogError::InvalidAddress(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_receipts_log_filter_invalid_prune_mode() {
|
||||
let result =
|
||||
parse_receipts_log_filter("0x0000000000000000000000000000000000000000:invalid_mode");
|
||||
assert!(matches!(result, Err(ReceiptsLogError::InvalidPruneMode(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_receipts_log_filter_invalid_distance() {
|
||||
let result = parse_receipts_log_filter(
|
||||
"0x0000000000000000000000000000000000000000:distance:invalid_distance",
|
||||
);
|
||||
assert!(matches!(result, Err(ReceiptsLogError::InvalidDistance(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_receipts_log_filter_invalid_block_number() {
|
||||
let result = parse_receipts_log_filter(
|
||||
"0x0000000000000000000000000000000000000000:before:invalid_block",
|
||||
);
|
||||
assert!(matches!(result, Err(ReceiptsLogError::InvalidBlockNumber(_))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use alloy_primitives::{Address, BlockNumber};
|
||||
pub use checkpoint::PruneCheckpoint;
|
||||
use derive_more::{Deref, From};
|
||||
use derive_more::{Deref, DerefMut, From};
|
||||
pub use event::PrunerEvent;
|
||||
pub use mode::PruneMode;
|
||||
pub use pruner::{
|
||||
@@ -30,17 +30,17 @@ pub use pruner::{
|
||||
SegmentOutputCheckpoint,
|
||||
};
|
||||
pub use segment::{PrunePurpose, PruneSegment, PruneSegmentError};
|
||||
use serde::Deserialize;
|
||||
use serde::{de::Error, Deserialize, Deserializer};
|
||||
pub use target::{PruneModes, UnwindTargetPrunedError, MINIMUM_PRUNING_DISTANCE};
|
||||
|
||||
/// Configuration for pruning receipts not associated with logs emitted by the specified contracts.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Deref, From)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Deref, DerefMut, From)]
|
||||
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ReceiptsLogPruneConfig(pub BTreeMap<Address, PruneMode>);
|
||||
|
||||
impl ReceiptsLogPruneConfig {
|
||||
/// Creates an empty config.
|
||||
pub const fn empty() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self(BTreeMap::new())
|
||||
}
|
||||
|
||||
@@ -51,18 +51,23 @@ impl ReceiptsLogPruneConfig {
|
||||
|
||||
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let map = BTreeMap::<Address, PruneMode>::deserialize(deserializer)?;
|
||||
if let Some(address) =
|
||||
map.iter().find_map(|(address, mode)| mode.is_distance().then_some(address))
|
||||
{
|
||||
return Err(serde::de::Error::custom(format!(
|
||||
"address {} has a distance-based pruning mode which is no longer supported. Please either use `full` or `before`.",
|
||||
address
|
||||
)));
|
||||
let config = Self(BTreeMap::deserialize(deserializer)?);
|
||||
config.validate().map_err(D::Error::custom)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Validates the configuration.
|
||||
pub fn validate(&self) -> Result<(), PruneSegmentError> {
|
||||
for (address, mode) in &self.0 {
|
||||
if mode.is_distance() {
|
||||
return Err(PruneSegmentError::UnsupportedReceiptsLogFilterPruneMode(
|
||||
*address, *mode,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(Self(map))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Given the `tip` block number, consolidates the structure so it can easily be queried for
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(deprecated)] // necessary to all defining deprecated `PruneSegment` variants
|
||||
|
||||
use crate::MINIMUM_PRUNING_DISTANCE;
|
||||
use crate::{PruneMode, MINIMUM_PRUNING_DISTANCE};
|
||||
use alloy_primitives::Address;
|
||||
use derive_more::Display;
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use thiserror::Error;
|
||||
@@ -115,6 +116,9 @@ pub enum PruneSegmentError {
|
||||
/// Invalid configuration of a prune segment.
|
||||
#[error("the configuration provided for {0} is invalid")]
|
||||
Configuration(PruneSegment),
|
||||
/// Unsupported receipts log filter prune mode for address.
|
||||
#[error("unsupported receipts log filter prune mode for address {0}: {1:?}")]
|
||||
UnsupportedReceiptsLogFilterPruneMode(Address, PruneMode),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -120,7 +120,7 @@ impl Default for PruneModes {
|
||||
storage_history: None,
|
||||
bodies_history: None,
|
||||
merkle_changesets: default_merkle_changesets_mode(),
|
||||
receipts_log_filter: ReceiptsLogPruneConfig::empty(),
|
||||
receipts_log_filter: ReceiptsLogPruneConfig::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ impl PruneModes {
|
||||
storage_history: Some(PruneMode::Full),
|
||||
bodies_history: Some(PruneMode::Full),
|
||||
merkle_changesets: PruneMode::Full,
|
||||
receipts_log_filter: ReceiptsLogPruneConfig::empty(),
|
||||
receipts_log_filter: ReceiptsLogPruneConfig::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user