From 9d46b0642061facc264d5894556f4506c90bdb9d Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Tue, 3 Sep 2024 16:02:18 +0100 Subject: [PATCH] feat(engine): invalid block hooks crate (#10629) Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> --- .github/assets/check_wasm.sh | 7 ++- Cargo.lock | 10 ++++ Cargo.toml | 2 + crates/engine/invalid-block-hooks/Cargo.toml | 17 ++++++ crates/engine/invalid-block-hooks/src/lib.rs | 5 ++ .../engine/invalid-block-hooks/src/witness.rs | 13 +++++ .../tree/src/tree/invalid_block_hook.rs | 55 +++++++++++++------ crates/engine/tree/src/tree/mod.rs | 17 ++++-- crates/node/builder/Cargo.toml | 1 + crates/node/builder/src/launch/common.rs | 28 +++++++++- crates/node/builder/src/launch/engine.rs | 6 +- crates/node/core/src/args/debug.rs | 7 ++- 12 files changed, 137 insertions(+), 31 deletions(-) create mode 100644 crates/engine/invalid-block-hooks/Cargo.toml create mode 100644 crates/engine/invalid-block-hooks/src/lib.rs create mode 100644 crates/engine/invalid-block-hooks/src/witness.rs diff --git a/.github/assets/check_wasm.sh b/.github/assets/check_wasm.sh index 5eaa9cd7021a..1791894c9e10 100755 --- a/.github/assets/check_wasm.sh +++ b/.github/assets/check_wasm.sh @@ -66,17 +66,18 @@ exclude_crates=( reth-stages reth-storage-errors # The following are not supposed to be working - reth # all of the crates below + reth # all of the crates below reth-db # mdbx + reth-invalid-block-hooks # reth-provider reth-libmdbx # mdbx reth-mdbx-sys # mdbx reth-nippy-jar # sucds reth-provider # reth-db, reth-nippy-jar reth-prune # reth-db reth-stages-api # reth-provider, reth-prune - reth-static-file # reth-nippy-jar + reth-static-file # reth-nippy-jar reth-transaction-pool # c-kzg - reth-trie-db # reth-db + reth-trie-db # reth-db reth-trie-parallel # reth-db ) diff --git a/Cargo.lock b/Cargo.lock index af5c5ebe6834..aebeeffa9266 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7250,6 +7250,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "reth-invalid-block-hooks" +version = "1.0.6" +dependencies = [ + "reth-primitives", + "reth-provider", + "reth-trie", +] + [[package]] name = "reth-ipc" version = "1.0.6" @@ -7533,6 +7542,7 @@ dependencies = [ "reth-engine-util", "reth-evm", "reth-exex", + "reth-invalid-block-hooks", "reth-network", "reth-network-api", "reth-network-p2p", diff --git a/Cargo.toml b/Cargo.toml index 050f71a23c53..1d833f973404 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "crates/consensus/consensus/", "crates/consensus/debug-client/", "crates/e2e-test-utils/", + "crates/engine/invalid-block-hooks/", "crates/engine/primitives/", "crates/engine/service", "crates/engine/tree/", @@ -330,6 +331,7 @@ reth-exex = { path = "crates/exex/exex" } reth-exex-test-utils = { path = "crates/exex/test-utils" } reth-exex-types = { path = "crates/exex/types" } reth-fs-util = { path = "crates/fs-util" } +reth-invalid-block-hooks = { path = "crates/engine/invalid-block-hooks" } reth-ipc = { path = "crates/rpc/ipc" } reth-libmdbx = { path = "crates/storage/libmdbx-rs" } reth-mdbx-sys = { path = "crates/storage/libmdbx-rs/mdbx-sys" } diff --git a/crates/engine/invalid-block-hooks/Cargo.toml b/crates/engine/invalid-block-hooks/Cargo.toml new file mode 100644 index 000000000000..404ff96ce68d --- /dev/null +++ b/crates/engine/invalid-block-hooks/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "reth-invalid-block-hooks" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-primitives.workspace = true +reth-provider.workspace = true +reth-trie.workspace = true diff --git a/crates/engine/invalid-block-hooks/src/lib.rs b/crates/engine/invalid-block-hooks/src/lib.rs new file mode 100644 index 000000000000..4a27e1fd1e42 --- /dev/null +++ b/crates/engine/invalid-block-hooks/src/lib.rs @@ -0,0 +1,5 @@ +//! Invalid block hook implementations. + +mod witness; + +pub use witness::witness; diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs new file mode 100644 index 000000000000..75261ce5e18f --- /dev/null +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -0,0 +1,13 @@ +use reth_primitives::{Receipt, SealedBlockWithSenders, SealedHeader, B256}; +use reth_provider::BlockExecutionOutput; +use reth_trie::updates::TrieUpdates; + +/// Generates a witness for the given block and saves it to a file. +pub fn witness( + _block: &SealedBlockWithSenders, + _header: &SealedHeader, + _output: &BlockExecutionOutput, + _trie_updates: Option<(&TrieUpdates, B256)>, +) { + unimplemented!("witness generation is not supported") +} diff --git a/crates/engine/tree/src/tree/invalid_block_hook.rs b/crates/engine/tree/src/tree/invalid_block_hook.rs index 1c9e64914448..9c32b781b12a 100644 --- a/crates/engine/tree/src/tree/invalid_block_hook.rs +++ b/crates/engine/tree/src/tree/invalid_block_hook.rs @@ -7,29 +7,29 @@ pub trait InvalidBlockHook: Send + Sync { /// Invoked when a bad block is encountered. fn on_invalid_block( &self, - block: SealedBlockWithSenders, - header: SealedHeader, - output: BlockExecutionOutput, - trie_updates: Option<(TrieUpdates, B256)>, + block: &SealedBlockWithSenders, + header: &SealedHeader, + output: &BlockExecutionOutput, + trie_updates: Option<(&TrieUpdates, B256)>, ); } impl InvalidBlockHook for F where F: Fn( - SealedBlockWithSenders, - SealedHeader, - BlockExecutionOutput, - Option<(TrieUpdates, B256)>, + &SealedBlockWithSenders, + &SealedHeader, + &BlockExecutionOutput, + Option<(&TrieUpdates, B256)>, ) + Send + Sync, { fn on_invalid_block( &self, - block: SealedBlockWithSenders, - header: SealedHeader, - output: BlockExecutionOutput, - trie_updates: Option<(TrieUpdates, B256)>, + block: &SealedBlockWithSenders, + header: &SealedHeader, + output: &BlockExecutionOutput, + trie_updates: Option<(&TrieUpdates, B256)>, ) { self(block, header, output, trie_updates) } @@ -43,10 +43,33 @@ pub struct NoopInvalidBlockHook; impl InvalidBlockHook for NoopInvalidBlockHook { fn on_invalid_block( &self, - _block: SealedBlockWithSenders, - _header: SealedHeader, - _output: BlockExecutionOutput, - _trie_updates: Option<(TrieUpdates, B256)>, + _block: &SealedBlockWithSenders, + _header: &SealedHeader, + _output: &BlockExecutionOutput, + _trie_updates: Option<(&TrieUpdates, B256)>, ) { } } + +/// Multiple [`InvalidBlockHook`]s that are executed in order. +pub struct InvalidBlockHooks(pub Vec>); + +impl std::fmt::Debug for InvalidBlockHooks { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InvalidBlockHooks").field("len", &self.0.len()).finish() + } +} + +impl InvalidBlockHook for InvalidBlockHooks { + fn on_invalid_block( + &self, + block: &SealedBlockWithSenders, + header: &SealedHeader, + output: &BlockExecutionOutput, + trie_updates: Option<(&TrieUpdates, B256)>, + ) { + for hook in &self.0 { + hook.on_invalid_block(block, header, output, trie_updates); + } + } +} diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index d954efe474bf..7e170d3419ea 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -62,7 +62,7 @@ mod invalid_block_hook; mod metrics; use crate::{engine::EngineApiRequest, tree::metrics::EngineApiMetrics}; pub use config::TreeConfig; -pub use invalid_block_hook::{InvalidBlockHook, NoopInvalidBlockHook}; +pub use invalid_block_hook::{InvalidBlockHook, InvalidBlockHooks, NoopInvalidBlockHook}; /// Keeps track of the state of the tree. /// @@ -1909,7 +1909,12 @@ where PostExecutionInput::new(&output.receipts, &output.requests), ) { // call post-block hook - self.invalid_block_hook.on_invalid_block(block.seal_slow(), parent_block, output, None); + self.invalid_block_hook.on_invalid_block( + &block.seal_slow(), + &parent_block, + &output, + None, + ); return Err(err.into()) } @@ -1921,10 +1926,10 @@ where if state_root != block.state_root { // call post-block hook self.invalid_block_hook.on_invalid_block( - block.clone().seal_slow(), - parent_block, - output, - Some((trie_output, state_root)), + &block.clone().seal_slow(), + &parent_block, + &output, + Some((&trie_output, state_root)), ); return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), diff --git a/crates/node/builder/Cargo.toml b/crates/node/builder/Cargo.toml index e5290233b8eb..933c85af2287 100644 --- a/crates/node/builder/Cargo.toml +++ b/crates/node/builder/Cargo.toml @@ -54,6 +54,7 @@ reth-payload-validator.workspace = true reth-engine-service.workspace = true reth-tokio-util.workspace = true reth-engine-tree.workspace = true +reth-invalid-block-hooks.workspace = true ## ethereum alloy-network.workspace = true diff --git a/crates/node/builder/src/launch/common.rs b/crates/node/builder/src/launch/common.rs index 51423f10fcf8..d0ab6929feaa 100644 --- a/crates/node/builder/src/launch/common.rs +++ b/crates/node/builder/src/launch/common.rs @@ -15,6 +15,7 @@ use reth_consensus::Consensus; use reth_db_api::{database::Database, database_metrics::DatabaseMetrics}; use reth_db_common::init::{init_genesis, InitDatabaseError}; use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader}; +use reth_engine_tree::tree::{InvalidBlockHook, InvalidBlockHooks, NoopInvalidBlockHook}; use reth_evm::noop::NoopBlockExecutorProvider; use reth_network_p2p::headers::client::HeadersClient; use reth_node_api::FullNodeTypes; @@ -813,7 +814,7 @@ where inconsistent_stage_checkpoint = stage_checkpoint, "Pipeline sync progress is inconsistent" ); - return self.blockchain_db().block_hash(first_stage_checkpoint) + return self.blockchain_db().block_hash(first_stage_checkpoint); } } @@ -839,6 +840,31 @@ where pub const fn components(&self) -> &CB::Components { &self.node_adapter().components } + + /// Returns the [`InvalidBlockHook`] to use for the node. + pub fn invalid_block_hook(&self) -> eyre::Result> { + Ok(if let Some(ref hook) = self.node_config().debug.invalid_block_hook { + let hooks = hook + .iter() + .copied() + .map(|hook| { + Ok(match hook { + reth_node_core::args::InvalidBlockHook::Witness => { + Box::new(reth_invalid_block_hooks::witness) as Box + } + reth_node_core::args::InvalidBlockHook::PreState | + reth_node_core::args::InvalidBlockHook::Opcode => { + eyre::bail!("invalid block hook {hook:?} is not implemented yet") + } + }) + }) + .collect::>()?; + + Box::new(InvalidBlockHooks(hooks)) + } else { + Box::new(NoopInvalidBlockHook::default()) + }) + } } /// Joins two attachments together. diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index 23a6d352a123..e4ed58d2cb2b 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -10,7 +10,7 @@ use reth_chainspec::ChainSpec; use reth_engine_service::service::{ChainEvent, EngineService}; use reth_engine_tree::{ engine::{EngineApiRequest, EngineRequestHandler}, - tree::{NoopInvalidBlockHook, TreeConfig}, + tree::TreeConfig, }; use reth_engine_util::EngineMessageStreamExt; use reth_exex::ExExManagerHandle; @@ -207,8 +207,6 @@ where warn!(target: "reth::cli", ?hook_type, "Invalid block hooks are not implemented yet! The `debug.invalid-block-hook` flag will do nothing for now."); } - let invalid_block_hook = Box::new(NoopInvalidBlockHook::default()); - // Configure the consensus engine let mut eth_service = EngineService::new( ctx.consensus(), @@ -223,7 +221,7 @@ where pruner, ctx.components().payload_builder().clone(), TreeConfig::default(), - invalid_block_hook, + ctx.invalid_block_hook()?, ); let event_sender = EventSender::default(); diff --git a/crates/node/core/src/args/debug.rs b/crates/node/core/src/args/debug.rs index 57b5d3b72f69..d706604429a4 100644 --- a/crates/node/core/src/args/debug.rs +++ b/crates/node/core/src/args/debug.rs @@ -85,7 +85,7 @@ pub struct DebugArgs { /// use reth_node_core::args::{InvalidBlockHook, InvalidBlockSelection}; /// let config: InvalidBlockSelection = vec![InvalidBlockHook::Witness].into(); /// ``` -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, derive_more::Deref)] pub struct InvalidBlockSelection(HashSet); impl InvalidBlockSelection { @@ -135,6 +135,11 @@ impl InvalidBlockSelection { { selection.into_iter().map(TryInto::try_into).collect() } + + /// Clones the set of configured [`InvalidBlockHook`]. + pub fn to_selection(&self) -> HashSet { + self.0.clone() + } } impl From<&[InvalidBlockHook]> for InvalidBlockSelection {