mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(engine): compare invalid block witness against a healthy node (#10844)
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -7367,6 +7367,8 @@ dependencies = [
|
|||||||
"alloy-rlp",
|
"alloy-rlp",
|
||||||
"alloy-rpc-types-debug",
|
"alloy-rpc-types-debug",
|
||||||
"eyre",
|
"eyre",
|
||||||
|
"futures",
|
||||||
|
"jsonrpsee",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
"reth-engine-primitives",
|
"reth-engine-primitives",
|
||||||
@ -7374,6 +7376,7 @@ dependencies = [
|
|||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
"reth-provider",
|
"reth-provider",
|
||||||
"reth-revm",
|
"reth-revm",
|
||||||
|
"reth-rpc-api",
|
||||||
"reth-tracing",
|
"reth-tracing",
|
||||||
"reth-trie",
|
"reth-trie",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -7643,6 +7646,7 @@ dependencies = [
|
|||||||
"eyre",
|
"eyre",
|
||||||
"fdlimit",
|
"fdlimit",
|
||||||
"futures",
|
"futures",
|
||||||
|
"jsonrpsee",
|
||||||
"rayon",
|
"rayon",
|
||||||
"reth-auto-seal-consensus",
|
"reth-auto-seal-consensus",
|
||||||
"reth-beacon-consensus",
|
"reth-beacon-consensus",
|
||||||
@ -7676,6 +7680,7 @@ dependencies = [
|
|||||||
"reth-provider",
|
"reth-provider",
|
||||||
"reth-prune",
|
"reth-prune",
|
||||||
"reth-rpc",
|
"reth-rpc",
|
||||||
|
"reth-rpc-api",
|
||||||
"reth-rpc-builder",
|
"reth-rpc-builder",
|
||||||
"reth-rpc-engine-api",
|
"reth-rpc-engine-api",
|
||||||
"reth-rpc-eth-types",
|
"reth-rpc-eth-types",
|
||||||
|
|||||||
5
book/cli/reth/node.md
vendored
5
book/cli/reth/node.md
vendored
@ -541,12 +541,15 @@ Debug:
|
|||||||
The path to store engine API messages at. If specified, all of the intercepted engine API messages will be written to specified location
|
The path to store engine API messages at. If specified, all of the intercepted engine API messages will be written to specified location
|
||||||
|
|
||||||
--debug.invalid-block-hook <INVALID_BLOCK_HOOK>
|
--debug.invalid-block-hook <INVALID_BLOCK_HOOK>
|
||||||
Determines which type of bad block hook to install
|
Determines which type of invalid block hook to install
|
||||||
|
|
||||||
Example: `witness,prestate`
|
Example: `witness,prestate`
|
||||||
|
|
||||||
[possible values: witness, pre-state, opcode]
|
[possible values: witness, pre-state, opcode]
|
||||||
|
|
||||||
|
--debug.healthy-node-rpc-url <URL>
|
||||||
|
The RPC URL of a healthy node to use for comparing invalid block hook results against.
|
||||||
|
|
||||||
Database:
|
Database:
|
||||||
--db.log-level <LOG_LEVEL>
|
--db.log-level <LOG_LEVEL>
|
||||||
Database logging level. Levels higher than "notice" require a debug build
|
Database logging level. Levels higher than "notice" require a debug build
|
||||||
|
|||||||
@ -18,6 +18,7 @@ reth-evm.workspace = true
|
|||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-provider.workspace = true
|
reth-provider.workspace = true
|
||||||
reth-revm.workspace = true
|
reth-revm.workspace = true
|
||||||
|
reth-rpc-api = { workspace = true, features = ["client"] }
|
||||||
reth-tracing.workspace = true
|
reth-tracing.workspace = true
|
||||||
reth-trie = { workspace = true, features = ["serde"] }
|
reth-trie = { workspace = true, features = ["serde"] }
|
||||||
|
|
||||||
@ -25,7 +26,11 @@ reth-trie = { workspace = true, features = ["serde"] }
|
|||||||
alloy-rlp.workspace = true
|
alloy-rlp.workspace = true
|
||||||
alloy-rpc-types-debug.workspace = true
|
alloy-rpc-types-debug.workspace = true
|
||||||
|
|
||||||
|
# async
|
||||||
|
futures.workspace = true
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
eyre.workspace = true
|
eyre.workspace = true
|
||||||
|
jsonrpsee.workspace = true
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@ -17,25 +17,33 @@ use reth_revm::{
|
|||||||
primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg},
|
primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg},
|
||||||
DatabaseCommit, StateBuilder,
|
DatabaseCommit, StateBuilder,
|
||||||
};
|
};
|
||||||
|
use reth_rpc_api::DebugApiClient;
|
||||||
use reth_tracing::tracing::warn;
|
use reth_tracing::tracing::warn;
|
||||||
use reth_trie::{updates::TrieUpdates, HashedPostState, HashedStorage};
|
use reth_trie::{updates::TrieUpdates, HashedPostState, HashedStorage};
|
||||||
|
|
||||||
/// Generates a witness for the given block and saves it to a file.
|
/// Generates a witness for the given block and saves it to a file.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InvalidBlockWitnessHook<P, EvmConfig> {
|
pub struct InvalidBlockWitnessHook<P, EvmConfig> {
|
||||||
/// The directory to write the witness to. Additionally, diff files will be written to this
|
|
||||||
/// directory in case of failed sanity checks.
|
|
||||||
output_directory: PathBuf,
|
|
||||||
/// The provider to read the historical state and do the EVM execution.
|
/// The provider to read the historical state and do the EVM execution.
|
||||||
provider: P,
|
provider: P,
|
||||||
/// The EVM configuration to use for the execution.
|
/// The EVM configuration to use for the execution.
|
||||||
evm_config: EvmConfig,
|
evm_config: EvmConfig,
|
||||||
|
/// The directory to write the witness to. Additionally, diff files will be written to this
|
||||||
|
/// directory in case of failed sanity checks.
|
||||||
|
output_directory: PathBuf,
|
||||||
|
/// The healthy node client to compare the witness against.
|
||||||
|
healthy_node_client: Option<jsonrpsee::http_client::HttpClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, EvmConfig> InvalidBlockWitnessHook<P, EvmConfig> {
|
impl<P, EvmConfig> InvalidBlockWitnessHook<P, EvmConfig> {
|
||||||
/// Creates a new witness hook.
|
/// Creates a new witness hook.
|
||||||
pub const fn new(output_directory: PathBuf, provider: P, evm_config: EvmConfig) -> Self {
|
pub const fn new(
|
||||||
Self { output_directory, provider, evm_config }
|
provider: P,
|
||||||
|
evm_config: EvmConfig,
|
||||||
|
output_directory: PathBuf,
|
||||||
|
healthy_node_client: Option<jsonrpsee::http_client::HttpClient>,
|
||||||
|
) -> Self {
|
||||||
|
Self { provider, evm_config, output_directory, healthy_node_client }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,12 +156,13 @@ where
|
|||||||
let witness = state_provider.witness(Default::default(), hashed_state.clone())?;
|
let witness = state_provider.witness(Default::default(), hashed_state.clone())?;
|
||||||
|
|
||||||
// Write the witness to the output directory.
|
// Write the witness to the output directory.
|
||||||
let mut file = File::options()
|
|
||||||
.write(true)
|
|
||||||
.create_new(true)
|
|
||||||
.open(self.output_directory.join(format!("{}_{}.json", block.number, block.hash())))?;
|
|
||||||
let response = ExecutionWitness { witness, state_preimages: Some(state_preimages) };
|
let response = ExecutionWitness { witness, state_preimages: Some(state_preimages) };
|
||||||
file.write_all(serde_json::to_string(&response)?.as_bytes())?;
|
File::create_new(self.output_directory.join(format!(
|
||||||
|
"{}_{}.json",
|
||||||
|
block.number,
|
||||||
|
block.hash()
|
||||||
|
)))?
|
||||||
|
.write_all(serde_json::to_string(&response)?.as_bytes())?;
|
||||||
|
|
||||||
// The bundle state after re-execution should match the original one.
|
// The bundle state after re-execution should match the original one.
|
||||||
if bundle_state != output.state {
|
if bundle_state != output.state {
|
||||||
@ -179,6 +188,33 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(healthy_node_client) = &self.healthy_node_client {
|
||||||
|
// Compare the witness against the healthy node.
|
||||||
|
let healthy_node_witness = futures::executor::block_on(async move {
|
||||||
|
DebugApiClient::debug_execution_witness(
|
||||||
|
healthy_node_client,
|
||||||
|
block.number.into(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Write the healthy node witness to the output directory.
|
||||||
|
File::create_new(self.output_directory.join(format!(
|
||||||
|
"{}_{}.healthy_witness.json",
|
||||||
|
block.number,
|
||||||
|
block.hash()
|
||||||
|
)))?
|
||||||
|
.write_all(serde_json::to_string(&healthy_node_witness)?.as_bytes())?;
|
||||||
|
|
||||||
|
// If the witnesses are different, write the diff to the output directory.
|
||||||
|
if response != healthy_node_witness {
|
||||||
|
let filename = format!("{}_{}.healthy_witness.diff", block.number, block.hash());
|
||||||
|
let path = self.save_diff(filename, &response, &healthy_node_witness)?;
|
||||||
|
warn!(target: "engine::invalid_block_hooks::witness", path = %path.display(), "Witness mismatch against healthy node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,11 +227,7 @@ where
|
|||||||
) -> eyre::Result<PathBuf> {
|
) -> eyre::Result<PathBuf> {
|
||||||
let path = self.output_directory.join(filename);
|
let path = self.output_directory.join(filename);
|
||||||
let diff = Comparison::new(original, new);
|
let diff = Comparison::new(original, new);
|
||||||
File::options()
|
File::create_new(&path)?.write_all(diff.to_string().as_bytes())?;
|
||||||
.write(true)
|
|
||||||
.create_new(true)
|
|
||||||
.open(&path)?
|
|
||||||
.write_all(diff.to_string().as_bytes())?;
|
|
||||||
|
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -561,7 +561,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the bad block hook.
|
/// Sets the invalid block hook.
|
||||||
fn set_invalid_block_hook(&mut self, invalid_block_hook: Box<dyn InvalidBlockHook>) {
|
fn set_invalid_block_hook(&mut self, invalid_block_hook: Box<dyn InvalidBlockHook>) {
|
||||||
self.invalid_block_hook = invalid_block_hook;
|
self.invalid_block_hook = invalid_block_hook;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ reth-payload-validator.workspace = true
|
|||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-provider.workspace = true
|
reth-provider.workspace = true
|
||||||
reth-prune.workspace = true
|
reth-prune.workspace = true
|
||||||
|
reth-rpc-api.workspace = true
|
||||||
reth-rpc-builder.workspace = true
|
reth-rpc-builder.workspace = true
|
||||||
reth-rpc-engine-api.workspace = true
|
reth-rpc-engine-api.workspace = true
|
||||||
reth-rpc-eth-types.workspace = true
|
reth-rpc-eth-types.workspace = true
|
||||||
@ -82,6 +83,7 @@ secp256k1 = { workspace = true, features = [
|
|||||||
aquamarine.workspace = true
|
aquamarine.workspace = true
|
||||||
eyre.workspace = true
|
eyre.workspace = true
|
||||||
fdlimit.workspace = true
|
fdlimit.workspace = true
|
||||||
|
jsonrpsee.workspace = true
|
||||||
rayon.workspace = true
|
rayon.workspace = true
|
||||||
|
|
||||||
# tracing
|
# tracing
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
use std::{sync::Arc, thread::available_parallelism};
|
use std::{sync::Arc, thread::available_parallelism};
|
||||||
|
|
||||||
use alloy_primitives::{BlockNumber, B256};
|
use alloy_primitives::{BlockNumber, B256};
|
||||||
use eyre::Context;
|
use eyre::{Context, OptionExt};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use reth_auto_seal_consensus::MiningMode;
|
use reth_auto_seal_consensus::MiningMode;
|
||||||
use reth_beacon_consensus::EthBeaconConsensus;
|
use reth_beacon_consensus::EthBeaconConsensus;
|
||||||
@ -23,6 +23,7 @@ use reth_invalid_block_hooks::InvalidBlockWitnessHook;
|
|||||||
use reth_network_p2p::headers::client::HeadersClient;
|
use reth_network_p2p::headers::client::HeadersClient;
|
||||||
use reth_node_api::{FullNodeTypes, NodeTypes, NodeTypesWithDB};
|
use reth_node_api::{FullNodeTypes, NodeTypes, NodeTypesWithDB};
|
||||||
use reth_node_core::{
|
use reth_node_core::{
|
||||||
|
args::InvalidBlockHookType,
|
||||||
dirs::{ChainPath, DataDirPath},
|
dirs::{ChainPath, DataDirPath},
|
||||||
node_config::NodeConfig,
|
node_config::NodeConfig,
|
||||||
version::{
|
version::{
|
||||||
@ -44,6 +45,7 @@ use reth_provider::{
|
|||||||
TreeViewer,
|
TreeViewer,
|
||||||
};
|
};
|
||||||
use reth_prune::{PruneModes, PrunerBuilder};
|
use reth_prune::{PruneModes, PrunerBuilder};
|
||||||
|
use reth_rpc_api::clients::EthApiClient;
|
||||||
use reth_rpc_builder::config::RethRpcServerConfig;
|
use reth_rpc_builder::config::RethRpcServerConfig;
|
||||||
use reth_rpc_layer::JwtSecret;
|
use reth_rpc_layer::JwtSecret;
|
||||||
use reth_stages::{sets::DefaultStages, MetricEvent, Pipeline, PipelineTarget, StageId};
|
use reth_stages::{sets::DefaultStages, MetricEvent, Pipeline, PipelineTarget, StageId};
|
||||||
@ -855,35 +857,62 @@ where
|
|||||||
{
|
{
|
||||||
/// Returns the [`InvalidBlockHook`] to use for the node.
|
/// Returns the [`InvalidBlockHook`] to use for the node.
|
||||||
pub fn invalid_block_hook(&self) -> eyre::Result<Box<dyn InvalidBlockHook>> {
|
pub fn invalid_block_hook(&self) -> eyre::Result<Box<dyn InvalidBlockHook>> {
|
||||||
Ok(if let Some(ref hook) = self.node_config().debug.invalid_block_hook {
|
let Some(ref hook) = self.node_config().debug.invalid_block_hook else {
|
||||||
let output_directory = self.data_dir().invalid_block_hooks();
|
return Ok(Box::new(NoopInvalidBlockHook::default()))
|
||||||
let hooks = hook
|
};
|
||||||
.iter()
|
let healthy_node_rpc_client = self.get_healthy_node_client()?;
|
||||||
.copied()
|
|
||||||
.map(|hook| {
|
|
||||||
let output_directory = output_directory.join(hook.to_string());
|
|
||||||
fs::create_dir_all(&output_directory)?;
|
|
||||||
|
|
||||||
Ok(match hook {
|
let output_directory = self.data_dir().invalid_block_hooks();
|
||||||
reth_node_core::args::InvalidBlockHook::Witness => {
|
let hooks = hook
|
||||||
Box::new(InvalidBlockWitnessHook::new(
|
.iter()
|
||||||
output_directory,
|
.copied()
|
||||||
self.blockchain_db().clone(),
|
.map(|hook| {
|
||||||
self.components().evm_config().clone(),
|
let output_directory = output_directory.join(hook.to_string());
|
||||||
)) as Box<dyn InvalidBlockHook>
|
fs::create_dir_all(&output_directory)?;
|
||||||
}
|
|
||||||
reth_node_core::args::InvalidBlockHook::PreState |
|
|
||||||
reth_node_core::args::InvalidBlockHook::Opcode => {
|
|
||||||
eyre::bail!("invalid block hook {hook:?} is not implemented yet")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
Box::new(InvalidBlockHooks(hooks))
|
Ok(match hook {
|
||||||
} else {
|
InvalidBlockHookType::Witness => Box::new(InvalidBlockWitnessHook::new(
|
||||||
Box::new(NoopInvalidBlockHook::default())
|
self.blockchain_db().clone(),
|
||||||
})
|
self.components().evm_config().clone(),
|
||||||
|
output_directory,
|
||||||
|
healthy_node_rpc_client.clone(),
|
||||||
|
)),
|
||||||
|
InvalidBlockHookType::PreState | InvalidBlockHookType::Opcode => {
|
||||||
|
eyre::bail!("invalid block hook {hook:?} is not implemented yet")
|
||||||
|
}
|
||||||
|
} as Box<dyn InvalidBlockHook>)
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
Ok(Box::new(InvalidBlockHooks(hooks)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an RPC client for the healthy node, if configured in the node config.
|
||||||
|
fn get_healthy_node_client(&self) -> eyre::Result<Option<jsonrpsee::http_client::HttpClient>> {
|
||||||
|
self.node_config()
|
||||||
|
.debug
|
||||||
|
.healthy_node_rpc_url
|
||||||
|
.as_ref()
|
||||||
|
.map(|url| {
|
||||||
|
let client = jsonrpsee::http_client::HttpClientBuilder::default().build(url)?;
|
||||||
|
|
||||||
|
// Verify that the healthy node is running the same chain as the current node.
|
||||||
|
let chain_id = futures::executor::block_on(async {
|
||||||
|
EthApiClient::<
|
||||||
|
reth_rpc_types::Transaction,
|
||||||
|
reth_rpc_types::Block,
|
||||||
|
reth_rpc_types::Receipt,
|
||||||
|
>::chain_id(&client)
|
||||||
|
.await
|
||||||
|
})?
|
||||||
|
.ok_or_eyre("healthy node rpc client didn't return a chain id")?;
|
||||||
|
if chain_id.to::<u64>() != self.chain_id().id() {
|
||||||
|
eyre::bail!("invalid chain id for healthy node: {chain_id}")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(client)
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -68,11 +68,20 @@ pub struct DebugArgs {
|
|||||||
#[arg(long = "debug.engine-api-store", help_heading = "Debug", value_name = "PATH")]
|
#[arg(long = "debug.engine-api-store", help_heading = "Debug", value_name = "PATH")]
|
||||||
pub engine_api_store: Option<PathBuf>,
|
pub engine_api_store: Option<PathBuf>,
|
||||||
|
|
||||||
/// Determines which type of bad block hook to install
|
/// Determines which type of invalid block hook to install
|
||||||
///
|
///
|
||||||
/// Example: `witness,prestate`
|
/// Example: `witness,prestate`
|
||||||
#[arg(long = "debug.invalid-block-hook", help_heading = "Debug", value_parser = InvalidBlockSelectionValueParser::default())]
|
#[arg(long = "debug.invalid-block-hook", help_heading = "Debug", value_parser = InvalidBlockSelectionValueParser::default())]
|
||||||
pub invalid_block_hook: Option<InvalidBlockSelection>,
|
pub invalid_block_hook: Option<InvalidBlockSelection>,
|
||||||
|
|
||||||
|
/// The RPC URL of a healthy node to use for comparing invalid block hook results against.
|
||||||
|
#[arg(
|
||||||
|
long = "debug.healthy-node-rpc-url",
|
||||||
|
help_heading = "Debug",
|
||||||
|
value_name = "URL",
|
||||||
|
verbatim_doc_comment
|
||||||
|
)]
|
||||||
|
pub healthy_node_rpc_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the invalid block hooks that should be installed.
|
/// Describes the invalid block hooks that should be installed.
|
||||||
@ -82,11 +91,11 @@ pub struct DebugArgs {
|
|||||||
/// Create a [`InvalidBlockSelection`] from a selection.
|
/// Create a [`InvalidBlockSelection`] from a selection.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use reth_node_core::args::{InvalidBlockHook, InvalidBlockSelection};
|
/// use reth_node_core::args::{InvalidBlockHookType, InvalidBlockSelection};
|
||||||
/// let config: InvalidBlockSelection = vec![InvalidBlockHook::Witness].into();
|
/// let config: InvalidBlockSelection = vec![InvalidBlockHookType::Witness].into();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Deref)]
|
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Deref)]
|
||||||
pub struct InvalidBlockSelection(HashSet<InvalidBlockHook>);
|
pub struct InvalidBlockSelection(HashSet<InvalidBlockHookType>);
|
||||||
|
|
||||||
impl InvalidBlockSelection {
|
impl InvalidBlockSelection {
|
||||||
/// Creates a new _unique_ [`InvalidBlockSelection`] from the given items.
|
/// Creates a new _unique_ [`InvalidBlockSelection`] from the given items.
|
||||||
@ -97,73 +106,73 @@ impl InvalidBlockSelection {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// Create a selection from the [`InvalidBlockHook`] string identifiers
|
/// Create a selection from the [`InvalidBlockHookType`] string identifiers
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use reth_node_core::args::{InvalidBlockHook, InvalidBlockSelection};
|
/// use reth_node_core::args::{InvalidBlockHookType, InvalidBlockSelection};
|
||||||
/// let selection = vec!["witness", "prestate", "opcode"];
|
/// let selection = vec!["witness", "prestate", "opcode"];
|
||||||
/// let config = InvalidBlockSelection::try_from_selection(selection).unwrap();
|
/// let config = InvalidBlockSelection::try_from_selection(selection).unwrap();
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// config,
|
/// config,
|
||||||
/// InvalidBlockSelection::from([
|
/// InvalidBlockSelection::from([
|
||||||
/// InvalidBlockHook::Witness,
|
/// InvalidBlockHookType::Witness,
|
||||||
/// InvalidBlockHook::PreState,
|
/// InvalidBlockHookType::PreState,
|
||||||
/// InvalidBlockHook::Opcode
|
/// InvalidBlockHookType::Opcode
|
||||||
/// ])
|
/// ])
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Create a unique selection from the [`InvalidBlockHook`] string identifiers
|
/// Create a unique selection from the [`InvalidBlockHookType`] string identifiers
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use reth_node_core::args::{InvalidBlockHook, InvalidBlockSelection};
|
/// use reth_node_core::args::{InvalidBlockHookType, InvalidBlockSelection};
|
||||||
/// let selection = vec!["witness", "prestate", "opcode", "witness", "prestate"];
|
/// let selection = vec!["witness", "prestate", "opcode", "witness", "prestate"];
|
||||||
/// let config = InvalidBlockSelection::try_from_selection(selection).unwrap();
|
/// let config = InvalidBlockSelection::try_from_selection(selection).unwrap();
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// config,
|
/// config,
|
||||||
/// InvalidBlockSelection::from([
|
/// InvalidBlockSelection::from([
|
||||||
/// InvalidBlockHook::Witness,
|
/// InvalidBlockHookType::Witness,
|
||||||
/// InvalidBlockHook::PreState,
|
/// InvalidBlockHookType::PreState,
|
||||||
/// InvalidBlockHook::Opcode
|
/// InvalidBlockHookType::Opcode
|
||||||
/// ])
|
/// ])
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_from_selection<I, T>(selection: I) -> Result<Self, T::Error>
|
pub fn try_from_selection<I, T>(selection: I) -> Result<Self, T::Error>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
T: TryInto<InvalidBlockHook>,
|
T: TryInto<InvalidBlockHookType>,
|
||||||
{
|
{
|
||||||
selection.into_iter().map(TryInto::try_into).collect()
|
selection.into_iter().map(TryInto::try_into).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clones the set of configured [`InvalidBlockHook`].
|
/// Clones the set of configured [`InvalidBlockHookType`].
|
||||||
pub fn to_selection(&self) -> HashSet<InvalidBlockHook> {
|
pub fn to_selection(&self) -> HashSet<InvalidBlockHookType> {
|
||||||
self.0.clone()
|
self.0.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&[InvalidBlockHook]> for InvalidBlockSelection {
|
impl From<&[InvalidBlockHookType]> for InvalidBlockSelection {
|
||||||
fn from(s: &[InvalidBlockHook]) -> Self {
|
fn from(s: &[InvalidBlockHookType]) -> Self {
|
||||||
Self(s.iter().copied().collect())
|
Self(s.iter().copied().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<InvalidBlockHook>> for InvalidBlockSelection {
|
impl From<Vec<InvalidBlockHookType>> for InvalidBlockSelection {
|
||||||
fn from(s: Vec<InvalidBlockHook>) -> Self {
|
fn from(s: Vec<InvalidBlockHookType>) -> Self {
|
||||||
Self(s.into_iter().collect())
|
Self(s.into_iter().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> From<[InvalidBlockHook; N]> for InvalidBlockSelection {
|
impl<const N: usize> From<[InvalidBlockHookType; N]> for InvalidBlockSelection {
|
||||||
fn from(s: [InvalidBlockHook; N]) -> Self {
|
fn from(s: [InvalidBlockHookType; N]) -> Self {
|
||||||
Self(s.iter().copied().collect())
|
Self(s.iter().copied().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<InvalidBlockHook> for InvalidBlockSelection {
|
impl FromIterator<InvalidBlockHookType> for InvalidBlockSelection {
|
||||||
fn from_iter<I>(iter: I) -> Self
|
fn from_iter<I>(iter: I) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = InvalidBlockHook>,
|
I: IntoIterator<Item = InvalidBlockHookType>,
|
||||||
{
|
{
|
||||||
Self(iter.into_iter().collect())
|
Self(iter.into_iter().collect())
|
||||||
}
|
}
|
||||||
@ -205,7 +214,7 @@ impl TypedValueParser for InvalidBlockSelectionValueParser {
|
|||||||
value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
|
value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
|
||||||
val.parse::<InvalidBlockSelection>().map_err(|err| {
|
val.parse::<InvalidBlockSelection>().map_err(|err| {
|
||||||
let arg = arg.map(|a| a.to_string()).unwrap_or_else(|| "...".to_owned());
|
let arg = arg.map(|a| a.to_string()).unwrap_or_else(|| "...".to_owned());
|
||||||
let possible_values = InvalidBlockHook::all_variant_names().to_vec().join(",");
|
let possible_values = InvalidBlockHookType::all_variant_names().to_vec().join(",");
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"Invalid value '{val}' for {arg}: {err}.\n [possible values: {possible_values}]"
|
"Invalid value '{val}' for {arg}: {err}.\n [possible values: {possible_values}]"
|
||||||
);
|
);
|
||||||
@ -214,12 +223,12 @@ impl TypedValueParser for InvalidBlockSelectionValueParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
|
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
|
||||||
let values = InvalidBlockHook::all_variant_names().iter().map(PossibleValue::new);
|
let values = InvalidBlockHookType::all_variant_names().iter().map(PossibleValue::new);
|
||||||
Some(Box::new(values))
|
Some(Box::new(values))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type of bad block hook to install
|
/// The type of invalid block hook to install
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
@ -234,7 +243,7 @@ impl TypedValueParser for InvalidBlockSelectionValueParser {
|
|||||||
EnumIter,
|
EnumIter,
|
||||||
)]
|
)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub enum InvalidBlockHook {
|
pub enum InvalidBlockHookType {
|
||||||
/// A witness value enum
|
/// A witness value enum
|
||||||
Witness,
|
Witness,
|
||||||
/// A prestate trace value enum
|
/// A prestate trace value enum
|
||||||
@ -243,7 +252,7 @@ pub enum InvalidBlockHook {
|
|||||||
Opcode,
|
Opcode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for InvalidBlockHook {
|
impl FromStr for InvalidBlockHookType {
|
||||||
type Err = ParseError;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
@ -256,20 +265,20 @@ impl FromStr for InvalidBlockHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for InvalidBlockHook {
|
impl TryFrom<&str> for InvalidBlockHookType {
|
||||||
type Error = ParseError;
|
type Error = ParseError;
|
||||||
fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
|
fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
|
||||||
FromStr::from_str(s)
|
FromStr::from_str(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for InvalidBlockHook {
|
impl fmt::Display for InvalidBlockHookType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.pad(self.as_ref())
|
f.pad(self.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InvalidBlockHook {
|
impl InvalidBlockHookType {
|
||||||
/// Returns all variant names of the enum
|
/// Returns all variant names of the enum
|
||||||
pub const fn all_variant_names() -> &'static [&'static str] {
|
pub const fn all_variant_names() -> &'static [&'static str] {
|
||||||
<Self as VariantNames>::VARIANTS
|
<Self as VariantNames>::VARIANTS
|
||||||
@ -298,7 +307,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_invalid_block_args() {
|
fn test_parse_invalid_block_args() {
|
||||||
let expected_args = DebugArgs {
|
let expected_args = DebugArgs {
|
||||||
invalid_block_hook: Some(InvalidBlockSelection::from([InvalidBlockHook::Witness])),
|
invalid_block_hook: Some(InvalidBlockSelection::from([InvalidBlockHookType::Witness])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let args = CommandParser::<DebugArgs>::parse_from([
|
let args = CommandParser::<DebugArgs>::parse_from([
|
||||||
@ -311,8 +320,8 @@ mod tests {
|
|||||||
|
|
||||||
let expected_args = DebugArgs {
|
let expected_args = DebugArgs {
|
||||||
invalid_block_hook: Some(InvalidBlockSelection::from([
|
invalid_block_hook: Some(InvalidBlockSelection::from([
|
||||||
InvalidBlockHook::Witness,
|
InvalidBlockHookType::Witness,
|
||||||
InvalidBlockHook::PreState,
|
InvalidBlockHookType::PreState,
|
||||||
])),
|
])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,7 +14,7 @@ pub use rpc_state_cache::RpcStateCacheArgs;
|
|||||||
|
|
||||||
/// DebugArgs struct for debugging purposes
|
/// DebugArgs struct for debugging purposes
|
||||||
mod debug;
|
mod debug;
|
||||||
pub use debug::{DebugArgs, InvalidBlockHook, InvalidBlockSelection};
|
pub use debug::{DebugArgs, InvalidBlockHookType, InvalidBlockSelection};
|
||||||
|
|
||||||
/// DatabaseArgs struct for configuring the database
|
/// DatabaseArgs struct for configuring the database
|
||||||
mod database;
|
mod database;
|
||||||
|
|||||||
Reference in New Issue
Block a user