feat(engine): invalid block hooks crate (#10629)

Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
This commit is contained in:
Alexey Shekhirin
2024-09-03 16:02:18 +01:00
committed by GitHub
parent 6b509ddb1c
commit 9d46b06420
12 changed files with 137 additions and 31 deletions

View File

@ -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

View File

@ -0,0 +1,5 @@
//! Invalid block hook implementations.
mod witness;
pub use witness::witness;

View File

@ -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<Receipt>,
_trie_updates: Option<(&TrieUpdates, B256)>,
) {
unimplemented!("witness generation is not supported")
}

View File

@ -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<Receipt>,
trie_updates: Option<(TrieUpdates, B256)>,
block: &SealedBlockWithSenders,
header: &SealedHeader,
output: &BlockExecutionOutput<Receipt>,
trie_updates: Option<(&TrieUpdates, B256)>,
);
}
impl<F> InvalidBlockHook for F
where
F: Fn(
SealedBlockWithSenders,
SealedHeader,
BlockExecutionOutput<Receipt>,
Option<(TrieUpdates, B256)>,
&SealedBlockWithSenders,
&SealedHeader,
&BlockExecutionOutput<Receipt>,
Option<(&TrieUpdates, B256)>,
) + Send
+ Sync,
{
fn on_invalid_block(
&self,
block: SealedBlockWithSenders,
header: SealedHeader,
output: BlockExecutionOutput<Receipt>,
trie_updates: Option<(TrieUpdates, B256)>,
block: &SealedBlockWithSenders,
header: &SealedHeader,
output: &BlockExecutionOutput<Receipt>,
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<Receipt>,
_trie_updates: Option<(TrieUpdates, B256)>,
_block: &SealedBlockWithSenders,
_header: &SealedHeader,
_output: &BlockExecutionOutput<Receipt>,
_trie_updates: Option<(&TrieUpdates, B256)>,
) {
}
}
/// Multiple [`InvalidBlockHook`]s that are executed in order.
pub struct InvalidBlockHooks(pub Vec<Box<dyn InvalidBlockHook>>);
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<Receipt>,
trie_updates: Option<(&TrieUpdates, B256)>,
) {
for hook in &self.0 {
hook.on_invalid_block(block, header, output, trie_updates);
}
}
}

View File

@ -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(),

View File

@ -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

View File

@ -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<Box<dyn InvalidBlockHook>> {
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<dyn InvalidBlockHook>
}
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))
} else {
Box::new(NoopInvalidBlockHook::default())
})
}
}
/// Joins two attachments together.

View File

@ -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();

View File

@ -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<InvalidBlockHook>);
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<InvalidBlockHook> {
self.0.clone()
}
}
impl From<&[InvalidBlockHook]> for InvalidBlockSelection {