From 49b3f4c2ee994f8078afde16e0c5ee2af125299e Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 5 Dec 2025 22:50:49 +0800 Subject: [PATCH 01/17] resolve issues --- crates/node/builder/src/rpc.rs | 19 ++++ crates/rpc/rpc-api/src/debug.rs | 4 +- crates/rpc/rpc-builder/src/lib.rs | 53 +++++++---- crates/rpc/rpc/src/debug.rs | 143 +++++++++++++++++++++++++----- crates/rpc/rpc/src/lib.rs | 2 +- 5 files changed, 180 insertions(+), 41 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 7fb219ff7af..811bfa0e8f8 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -10,6 +10,7 @@ use crate::{ }; use alloy_rpc_types::engine::ClientVersionV1; use alloy_rpc_types_engine::ExecutionData; +use futures::StreamExt; use jsonrpsee::{core::middleware::layer::Either, RpcModule}; use reth_chain_state::CanonStateSubscriptions; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks, Hardforks}; @@ -23,6 +24,7 @@ use reth_node_core::{ version::{version_metadata, CLIENT_CODE}, }; use reth_payload_builder::{PayloadBuilderHandle, PayloadStore}; +use reth_primitives_traits::RecoveredBlock; use reth_rpc::{ eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer}, AdminApi, @@ -1018,6 +1020,23 @@ where registry.eth_api().signers().write().extend(signers); } + // keep track of invalid blocks for `debug_getBadBlocks` + let bad_block_store = registry.bad_block_store().clone(); + let mut engine_events_stream = engine_events.new_listener(); + node.task_executor().spawn_critical( + "collect bad blocks", + Box::pin(async move { + while let Some(event) = engine_events_stream.next().await { + if let ConsensusEngineEvent::InvalidBlock(block) = event + && let Ok(recovered) = + RecoveredBlock::try_recover_sealed(block.as_ref().clone()) + { + bad_block_store.insert(recovered); + } + } + }), + ); + let mut registry = RpcRegistry { registry }; let ctx = RpcContext { node: node.clone(), diff --git a/crates/rpc/rpc-api/src/debug.rs b/crates/rpc/rpc-api/src/debug.rs index 0fca5f18457..5eae8df091c 100644 --- a/crates/rpc/rpc-api/src/debug.rs +++ b/crates/rpc/rpc-api/src/debug.rs @@ -3,7 +3,7 @@ use alloy_genesis::ChainConfig; use alloy_json_rpc::RpcObject; use alloy_primitives::{Address, Bytes, B256}; use alloy_rpc_types_debug::ExecutionWitness; -use alloy_rpc_types_eth::{Block, Bundle, StateContext}; +use alloy_rpc_types_eth::{BadBlock, Bundle, StateContext}; use alloy_rpc_types_trace::geth::{ BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult, }; @@ -38,7 +38,7 @@ pub trait DebugApi { /// Returns an array of recent bad blocks that the client has seen on the network. #[method(name = "getBadBlocks")] - async fn bad_blocks(&self) -> RpcResult>; + async fn bad_blocks(&self) -> RpcResult>; /// Returns the structured logs created during the execution of EVM between two blocks /// (excluding start) as a JSON object. diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 5348fedc3af..42f64c21dcd 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -36,8 +36,8 @@ use reth_evm::ConfigureEvm; use reth_network_api::{noop::NoopNetwork, NetworkInfo, Peers}; use reth_primitives_traits::{NodePrimitives, TxTy}; use reth_rpc::{ - AdminApi, DebugApi, EngineEthApi, EthApi, EthApiBuilder, EthBundle, MinerApi, NetApi, - OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, ValidationApiConfig, Web3Api, + AdminApi, BadBlockStore, DebugApi, EngineEthApi, EthApi, EthApiBuilder, EthBundle, MinerApi, + NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, ValidationApiConfig, Web3Api, }; use reth_rpc_api::servers::*; use reth_rpc_eth_api::{ @@ -52,8 +52,7 @@ use reth_rpc_eth_api::{ use reth_rpc_eth_types::{receipt::EthReceiptConverter, EthConfig, EthSubscriptionIdProvider}; use reth_rpc_layer::{AuthLayer, Claims, CompressionLayer, JwtAuthValidator, JwtSecret}; use reth_storage_api::{ - AccountReader, BlockReader, ChangeSetReader, FullRpcProvider, ProviderBlock, - StateProviderFactory, + AccountReader, BlockReader, ChangeSetReader, FullRpcProvider, StateProviderFactory, }; use reth_tasks::{pool::BlockingTaskGuard, TaskSpawner, TokioTaskExecutor}; use reth_transaction_pool::{noop::NoopTransactionPool, TransactionPool}; @@ -332,7 +331,8 @@ where RpcRegistryInner, ) where - EthApi: FullEthApiServer, + EthApi: + FullEthApiServer + RpcNodeCore, { let Self { provider, pool, network, executor, consensus, evm_config, .. } = self; @@ -359,7 +359,7 @@ where eth: EthApi, ) -> RpcRegistryInner where - EthApi: EthApiTypes + 'static, + EthApi: EthApiTypes + RpcNodeCore + 'static, { let Self { provider, pool, network, executor, consensus, evm_config, .. } = self; RpcRegistryInner::new(provider, pool, network, executor, consensus, config, evm_config, eth) @@ -373,7 +373,8 @@ where eth: EthApi, ) -> TransportRpcModules<()> where - EthApi: FullEthApiServer, + EthApi: + FullEthApiServer + RpcNodeCore, { let mut modules = TransportRpcModules::default(); @@ -511,6 +512,8 @@ pub struct RpcRegistryInner< modules: HashMap, /// eth config settings eth_config: EthConfig, + /// Recent bad blocks observed by the node. + bad_block_store: BadBlockStore, } // === impl RpcRegistryInner === @@ -527,7 +530,7 @@ where + 'static, Pool: Send + Sync + Clone + 'static, Network: Clone + 'static, - EthApi: EthApiTypes + 'static, + EthApi: EthApiTypes + RpcNodeCore + 'static, EvmConfig: ConfigureEvm, { /// Creates a new, empty instance. @@ -560,6 +563,7 @@ where blocking_pool_guard, eth_config: config.eth, evm_config, + bad_block_store: BadBlockStore::default(), } } } @@ -595,6 +599,11 @@ where &self.provider } + /// Returns the bad block store. + pub const fn bad_block_store(&self) -> &BadBlockStore { + &self.bad_block_store + } + /// Returns all installed methods pub fn methods(&self) -> Vec { self.modules.values().cloned().collect() @@ -706,8 +715,7 @@ where /// If called outside of the tokio runtime. See also [`Self::eth_api`] pub fn register_debug(&mut self) -> &mut Self where - EthApi: EthApiSpec + EthTransactions + TraceExt, - EvmConfig::Primitives: NodePrimitives>, + EthApi: EthApiSpec + EthTransactions + TraceExt + RpcNodeCore, { let debug_api = self.debug_api(); self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into()); @@ -814,8 +822,15 @@ where /// # Panics /// /// If called outside of the tokio runtime. See also [`Self::eth_api`] - pub fn debug_api(&self) -> DebugApi { - DebugApi::new(self.eth_api().clone(), self.blocking_pool_guard.clone()) + pub fn debug_api(&self) -> DebugApi + where + EthApi: EthApiTypes + RpcNodeCore, + { + DebugApi::new( + self.eth_api().clone(), + self.blocking_pool_guard.clone(), + self.bad_block_store.clone(), + ) } /// Instantiates `NetApi` @@ -847,7 +862,7 @@ where + ChangeSetReader, Pool: TransactionPool + Clone + 'static, Network: NetworkInfo + Peers + Clone + 'static, - EthApi: FullEthApiServer, + EthApi: FullEthApiServer + RpcNodeCore, EvmConfig: ConfigureEvm + 'static, Consensus: FullConsensus + Clone + 'static, { @@ -933,11 +948,13 @@ where ) .into_rpc() .into(), - RethRpcModule::Debug => { - DebugApi::new(eth_api.clone(), self.blocking_pool_guard.clone()) - .into_rpc() - .into() - } + RethRpcModule::Debug => DebugApi::new( + eth_api.clone(), + self.blocking_pool_guard.clone(), + self.bad_block_store.clone(), + ) + .into_rpc() + .into(), RethRpcModule::Eth => { // merge all eth handlers let mut module = eth_api.clone().into_rpc(); diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 9a53b2ad3aa..0d42dacdb63 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -9,23 +9,27 @@ use alloy_primitives::{hex::decode, uint, Address, Bytes, B256}; use alloy_rlp::{Decodable, Encodable}; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_eth::{ - state::EvmOverrides, Block as RpcBlock, BlockError, Bundle, StateContext, + state::EvmOverrides, BadBlock, BlockError, BlockTransactionsKind, Bundle, StateContext, + TransactionInfo, }; use alloy_rpc_types_trace::geth::{ - BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult, + mux::MuxConfig, BlockTraceResult, CallConfig, FourByteFrame, GethDebugBuiltInTracerType, + GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, + GethDefaultTracingOptions, GethTrace, NoopFrame, PreStateConfig, TraceResult, }; use async_trait::async_trait; use jsonrpsee::core::RpcResult; +use parking_lot::RwLock; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; use reth_errors::RethError; use reth_evm::{execute::Executor, ConfigureEvm, EvmEnvFor}; -use reth_primitives_traits::{Block as _, BlockBody, ReceiptWithBloom, RecoveredBlock}; +use reth_primitives_traits::{Block as BlockTrait, BlockBody, ReceiptWithBloom, RecoveredBlock}; use reth_revm::{db::State, witness::ExecutionWitnessRecord}; use reth_rpc_api::DebugApiServer; use reth_rpc_convert::RpcTxReq; use reth_rpc_eth_api::{ helpers::{EthTransactions, TraceExt}, - EthApiTypes, FromEthApiError, RpcNodeCore, + EthApiTypes, FromEthApiError, RpcConvert, RpcNodeCore, }; use reth_rpc_eth_types::EthApiError; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; @@ -35,24 +39,84 @@ use reth_storage_api::{ }; use reth_tasks::pool::BlockingTaskGuard; use reth_trie_common::{updates::TrieUpdates, HashedPostState}; -use revm::DatabaseCommit; -use revm_inspectors::tracing::{DebugInspector, TransactionContext}; -use std::sync::Arc; +use revm::{ + context::{ + result::{HaltReasonTr, ResultAndState}, + ContextTr, + }, + inspector::{JournalExt, NoOpInspector}, + interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, + DatabaseCommit, DatabaseRef, Inspector, +}; +use revm_inspectors::tracing::{ + FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext, +}; +use revm_primitives::{Log, U256}; +use serde::{Deserialize, Serialize}; +use std::{collections::VecDeque, sync::Arc}; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; /// `debug` API implementation. /// /// This type provides the functionality for handling `debug` related requests. -pub struct DebugApi { +pub struct DebugApi { inner: Arc>, } +/// A bounded, deduplicating store of recently observed bad blocks. +#[derive(Clone, Debug)] +pub struct BadBlockStore { + inner: Arc>>>>, + limit: usize, +} + +impl BadBlockStore { + /// Creates a new store with the given capacity. + pub fn new(limit: usize) -> Self { + Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit } + } + + /// Inserts a recovered block, keeping only the most recent `limit` entries and deduplicating + /// by block hash. + pub fn insert(&self, block: RecoveredBlock) { + let hash = block.hash(); + let mut guard = self.inner.write(); + + // drop existing entry with same hash to keep most recent order + guard.retain(|b| b.hash() != hash); + guard.push_back(Arc::new(block)); + + while guard.len() > self.limit { + guard.pop_front(); + } + } + + /// Returns all cached bad blocks ordered from newest to oldest. + pub fn all(&self) -> Vec>> { + let guard = self.inner.read(); + guard.iter().rev().cloned().collect() + } +} + +impl Default for BadBlockStore { + fn default() -> Self { + Self::new(64) + } +} + // === impl DebugApi === -impl DebugApi { +impl DebugApi +where + Eth: RpcNodeCore, +{ /// Create a new instance of the [`DebugApi`] - pub fn new(eth_api: Eth, blocking_task_guard: BlockingTaskGuard) -> Self { - let inner = Arc::new(DebugApiInner { eth_api, blocking_task_guard }); + pub fn new( + eth_api: Eth, + blocking_task_guard: BlockingTaskGuard, + bad_block_store: BadBlockStore>, + ) -> Self { + let inner = Arc::new(DebugApiInner { eth_api, blocking_task_guard, bad_block_store }); Self { inner } } @@ -60,13 +124,16 @@ impl DebugApi { pub fn eth_api(&self) -> &Eth { &self.inner.eth_api } -} -impl DebugApi { /// Access the underlying provider. pub fn provider(&self) -> &Eth::Provider { self.inner.eth_api.provider() } + + /// Access the bad block store. + pub fn bad_block_store(&self) -> &BadBlockStore> { + &self.inner.bad_block_store + } } // === impl DebugApi === @@ -610,7 +677,7 @@ where #[async_trait] impl DebugApiServer> for DebugApi where - Eth: EthApiTypes + EthTransactions + TraceExt + 'static, + Eth: EthApiTypes + EthTransactions + TraceExt + RpcNodeCore + 'static, { /// Handler for `debug_getRawHeader` async fn raw_header(&self, block_id: BlockId) -> RpcResult { @@ -660,7 +727,7 @@ where /// Handler for `debug_getRawTransactions` /// Returns the bytes of the transaction for the given hash. async fn raw_transactions(&self, block_id: BlockId) -> RpcResult> { - let block = self + let block: RecoveredBlock> = self .provider() .block_with_senders_by_id(block_id, TransactionVariant::NoHash) .to_rpc_result()? @@ -681,8 +748,43 @@ where } /// Handler for `debug_getBadBlocks` - async fn bad_blocks(&self) -> RpcResult> { - Ok(vec![]) + async fn bad_blocks(&self) -> RpcResult> { + let blocks = self.bad_block_store().all(); + let mut bad_blocks = Vec::with_capacity(blocks.len()); + + #[derive(Serialize, Deserialize)] + struct BadBlockSerde { + block: alloy_rpc_types_eth::Block, + hash: B256, + rlp: Bytes, + } + + for block in blocks { + let rpc_block = block + .clone_into_rpc_block( + BlockTransactionsKind::Full, + |tx, tx_info| self.eth_api().converter().fill(tx, tx_info), + |header, size| self.eth_api().converter().convert_header(header, size), + ) + .map_err(|err| internal_rpc_err(err.to_string()))?; + + // Convert to default RPC block via serde to align with BadBlock fields. + let rpc_block: alloy_rpc_types_eth::Block = serde_json::to_value(&rpc_block) + .and_then(serde_json::from_value) + .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?; + + let mut rlp = Vec::new(); + block.clone_sealed_block().encode(&mut rlp); + + let wrapper = BadBlockSerde { block: rpc_block, hash: block.hash(), rlp: rlp.into() }; + let bad_block: BadBlock = serde_json::to_value(wrapper) + .and_then(serde_json::from_value) + .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?; + + bad_blocks.push(bad_block); + } + + Ok(bad_blocks) } /// Handler for `debug_traceChain` @@ -1045,21 +1147,22 @@ where } } -impl std::fmt::Debug for DebugApi { +impl std::fmt::Debug for DebugApi { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DebugApi").finish_non_exhaustive() } } -impl Clone for DebugApi { +impl Clone for DebugApi { fn clone(&self) -> Self { Self { inner: Arc::clone(&self.inner) } } } -struct DebugApiInner { +struct DebugApiInner { /// The implementation of `eth` API eth_api: Eth, // restrict the number of concurrent calls to blocking calls blocking_task_guard: BlockingTaskGuard, + bad_block_store: BadBlockStore>, } diff --git a/crates/rpc/rpc/src/lib.rs b/crates/rpc/rpc/src/lib.rs index b5a20c19cf6..9fc841dfd2c 100644 --- a/crates/rpc/rpc/src/lib.rs +++ b/crates/rpc/rpc/src/lib.rs @@ -49,7 +49,7 @@ mod web3; pub use admin::AdminApi; pub use aliases::*; -pub use debug::DebugApi; +pub use debug::{BadBlockStore, DebugApi}; pub use engine::{EngineApi, EngineEthApi}; pub use eth::{helpers::SyncListener, EthApi, EthApiBuilder, EthBundle, EthFilter, EthPubSub}; pub use miner::MinerApi; From e10662028426a446e78b9115562a8351dee70142 Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 5 Dec 2025 23:04:25 +0800 Subject: [PATCH 02/17] Fix debug api imports after rebase --- crates/rpc/rpc/src/debug.rs | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 0d42dacdb63..9767c3003b7 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -10,12 +10,10 @@ use alloy_rlp::{Decodable, Encodable}; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_eth::{ state::EvmOverrides, BadBlock, BlockError, BlockTransactionsKind, Bundle, StateContext, - TransactionInfo, }; use alloy_rpc_types_trace::geth::{ - mux::MuxConfig, BlockTraceResult, CallConfig, FourByteFrame, GethDebugBuiltInTracerType, - GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, - GethDefaultTracingOptions, GethTrace, NoopFrame, PreStateConfig, TraceResult, + BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, + TraceResult, }; use async_trait::async_trait; use jsonrpsee::core::RpcResult; @@ -39,19 +37,8 @@ use reth_storage_api::{ }; use reth_tasks::pool::BlockingTaskGuard; use reth_trie_common::{updates::TrieUpdates, HashedPostState}; -use revm::{ - context::{ - result::{HaltReasonTr, ResultAndState}, - ContextTr, - }, - inspector::{JournalExt, NoOpInspector}, - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, - DatabaseCommit, DatabaseRef, Inspector, -}; -use revm_inspectors::tracing::{ - FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext, -}; -use revm_primitives::{Log, U256}; +use revm::DatabaseCommit; +use revm_inspectors::tracing::{DebugInspector, TransactionContext}; use serde::{Deserialize, Serialize}; use std::{collections::VecDeque, sync::Arc}; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; From 1eb823f0146fa5a3a28d5ec6935f0671d24ea9e0 Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 5 Dec 2025 23:05:46 +0800 Subject: [PATCH 03/17] rebase main --- crates/node/builder/src/rpc.rs | 4 ++-- crates/rpc/rpc/src/debug.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 811bfa0e8f8..876632fa65a 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -1027,8 +1027,8 @@ where "collect bad blocks", Box::pin(async move { while let Some(event) = engine_events_stream.next().await { - if let ConsensusEngineEvent::InvalidBlock(block) = event - && let Ok(recovered) = + if let ConsensusEngineEvent::InvalidBlock(block) = event && + let Ok(recovered) = RecoveredBlock::try_recover_sealed(block.as_ref().clone()) { bad_block_store.insert(recovered); diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 9767c3003b7..67b81e2255a 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -12,8 +12,7 @@ use alloy_rpc_types_eth::{ state::EvmOverrides, BadBlock, BlockError, BlockTransactionsKind, Bundle, StateContext, }; use alloy_rpc_types_trace::geth::{ - BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, - TraceResult, + BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult, }; use async_trait::async_trait; use jsonrpsee::core::RpcResult; From 91ccdca35f444068d4f7d2298c9218e9cf1e019e Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 5 Dec 2025 23:59:11 +0800 Subject: [PATCH 04/17] resolve errors --- crates/e2e-test-utils/src/rpc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/e2e-test-utils/src/rpc.rs b/crates/e2e-test-utils/src/rpc.rs index ff030c390b9..df3613e5fa3 100644 --- a/crates/e2e-test-utils/src/rpc.rs +++ b/crates/e2e-test-utils/src/rpc.rs @@ -22,7 +22,8 @@ where Node: FullNodeComponents>, EthApi: EthApiSpec>> + EthTransactions - + TraceExt, + + TraceExt + + reth_rpc_eth_api::RpcNodeCore, { /// Injects a raw transaction into the node tx pool via RPC server pub async fn inject_tx(&self, raw_tx: Bytes) -> Result { From a80f588fc2716294192149738b7975fa966d031a Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 6 Dec 2025 00:26:16 +0800 Subject: [PATCH 05/17] clean up --- crates/rpc/rpc/src/debug.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 67b81e2255a..ec84b2ffd4f 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -90,7 +90,6 @@ impl Default for BadBlockStore { } } -// === impl DebugApi === impl DebugApi where From 12571c4bf8956d25d7062934512a447c721ba447 Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 6 Dec 2025 00:37:24 +0800 Subject: [PATCH 06/17] lint --- crates/e2e-test-utils/src/node.rs | 3 ++- crates/rpc/rpc/src/debug.rs | 1 - examples/exex-hello-world/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index 4dd1ae63e1a..f0c06516b07 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -90,7 +90,8 @@ where where AddOns::EthApi: EthApiSpec>> + EthTransactions - + TraceExt, + + TraceExt + + reth_rpc_eth_api::RpcNodeCore, { let mut chain = Vec::with_capacity(length as usize); for i in 0..length { diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index ec84b2ffd4f..076c7bb16f0 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -90,7 +90,6 @@ impl Default for BadBlockStore { } } - impl DebugApi where Eth: RpcNodeCore, diff --git a/examples/exex-hello-world/src/main.rs b/examples/exex-hello-world/src/main.rs index 2c89fb72627..a33e9ef2807 100644 --- a/examples/exex-hello-world/src/main.rs +++ b/examples/exex-hello-world/src/main.rs @@ -66,7 +66,7 @@ async fn ethapi_exex( ) -> eyre::Result<()> where Node: FullNodeComponents>, - EthApi: FullEthApi, + EthApi: FullEthApi + reth_ethereum::rpc::eth::RpcNodeCore, { // Wait for the ethapi to be sent from the main function let rpc_handle = rpc_handle.await?; From 558e7710f306c39c6de023df279ea2de065005f7 Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 12:55:15 +0800 Subject: [PATCH 07/17] Use RPC block type for debug_getBadBlocks --- crates/rpc/rpc/src/debug.rs | 17 ++++++++--------- crates/stages/types/src/id.rs | 1 + crates/trie/trie/src/node_iter.rs | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 076c7bb16f0..32cee038b76 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -26,7 +26,7 @@ use reth_rpc_api::DebugApiServer; use reth_rpc_convert::RpcTxReq; use reth_rpc_eth_api::{ helpers::{EthTransactions, TraceExt}, - EthApiTypes, FromEthApiError, RpcConvert, RpcNodeCore, + EthApiTypes, FromEthApiError, RpcBlock, RpcConvert, RpcNodeCore, }; use reth_rpc_eth_types::EthApiError; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; @@ -737,8 +737,8 @@ where let mut bad_blocks = Vec::with_capacity(blocks.len()); #[derive(Serialize, Deserialize)] - struct BadBlockSerde { - block: alloy_rpc_types_eth::Block, + struct BadBlockSerde { + block: T, hash: B256, rlp: Bytes, } @@ -752,15 +752,14 @@ where ) .map_err(|err| internal_rpc_err(err.to_string()))?; - // Convert to default RPC block via serde to align with BadBlock fields. - let rpc_block: alloy_rpc_types_eth::Block = serde_json::to_value(&rpc_block) - .and_then(serde_json::from_value) - .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?; - let mut rlp = Vec::new(); block.clone_sealed_block().encode(&mut rlp); - let wrapper = BadBlockSerde { block: rpc_block, hash: block.hash(), rlp: rlp.into() }; + let wrapper = BadBlockSerde::> { + block: rpc_block, + hash: block.hash(), + rlp: rlp.into(), + }; let bad_block: BadBlock = serde_json::to_value(wrapper) .and_then(serde_json::from_value) .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?; diff --git a/crates/stages/types/src/id.rs b/crates/stages/types/src/id.rs index 8c0a91c8731..bbf19a749b1 100644 --- a/crates/stages/types/src/id.rs +++ b/crates/stages/types/src/id.rs @@ -114,6 +114,7 @@ impl StageId { /// Get a pre-encoded raw Vec, for example, to be used as the DB key for /// `tables::StageCheckpoints` and `tables::StageCheckpointProgresses` + #[allow(clippy::missing_const_for_fn)] pub fn get_pre_encoded(&self) -> Option<&Vec> { #[cfg(not(feature = "std"))] { diff --git a/crates/trie/trie/src/node_iter.rs b/crates/trie/trie/src/node_iter.rs index b57fc2da707..425754f351b 100644 --- a/crates/trie/trie/src/node_iter.rs +++ b/crates/trie/trie/src/node_iter.rs @@ -94,6 +94,7 @@ where } /// Creates a new [`TrieNodeIter`]. + #[allow(clippy::missing_const_for_fn)] fn new(walker: TrieWalker, hashed_cursor: H, trie_type: TrieType) -> Self { Self { walker, From b0f5d4050382c6967664ddec750e33dc0938c44d Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 13:34:27 +0800 Subject: [PATCH 08/17] Use EthApiTypes instead of specific type --- crates/node/builder/src/rpc.rs | 3 ++- crates/optimism/rpc/src/eth/mod.rs | 9 ++++---- crates/rpc/rpc-builder/src/lib.rs | 21 +++++++++-------- crates/rpc/rpc-eth-api/src/types.rs | 5 +++- crates/rpc/rpc/src/debug.rs | 36 +++++++++++++++-------------- crates/rpc/rpc/src/eth/core.rs | 3 ++- 6 files changed, 44 insertions(+), 33 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 876632fa65a..b9f3ff00c00 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -26,7 +26,7 @@ use reth_node_core::{ use reth_payload_builder::{PayloadBuilderHandle, PayloadStore}; use reth_primitives_traits::RecoveredBlock; use reth_rpc::{ - eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer}, + eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer, RpcNodeCore}, AdminApi, }; use reth_rpc_api::{eth::helpers::EthTransactions, IntoEngineApiRpcModule}; @@ -1208,6 +1208,7 @@ impl<'a, N: FullNodeComponents: Default + Send + 'static { /// The Ethapi implementation this builder will build. type EthApi: EthApiTypes + + RpcNodeCore> + FullEthApiServer + Unpin + 'static; diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index d5f42a5473d..1a9a68fe639 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -21,7 +21,7 @@ pub use receipt::{OpReceiptBuilder, OpReceiptFieldsBuilder}; use reqwest::Url; use reth_chainspec::{EthereumHardforks, Hardforks}; use reth_evm::ConfigureEvm; -use reth_node_api::{FullNodeComponents, FullNodeTypes, HeaderTy, NodeTypes}; +use reth_node_api::{FullNodeComponents, FullNodeTypes, HeaderTy, NodeTypes, PrimitivesTy}; use reth_node_builder::rpc::{EthApiBuilder, EthApiCtx}; use reth_optimism_flashblocks::{ FlashBlockBuildInfo, FlashBlockCompleteSequence, FlashBlockCompleteSequenceRx, @@ -38,7 +38,7 @@ use reth_rpc_eth_api::{ RpcNodeCoreExt, RpcTypes, }; use reth_rpc_eth_types::{EthStateCache, FeeHistoryCache, GasPriceOracle, PendingBlock}; -use reth_storage_api::{BlockReaderIdExt, ProviderHeader}; +use reth_storage_api::{BlockReaderIdExt, ProviderBlock, ProviderHeader}; use reth_tasks::{ pool::{BlockingTaskGuard, BlockingTaskPool}, TaskSpawner, @@ -193,6 +193,7 @@ where type Error = OpEthApiError; type NetworkTypes = Rpc::Network; type RpcConvert = Rpc; + type ProviderBlock = ProviderBlock; fn converter(&self) -> &Self::RpcConvert { self.inner.eth_api.converter() @@ -487,8 +488,8 @@ where >, NetworkT: RpcTypes, OpRpcConvert: RpcConvert, - OpEthApi>: - FullEthApiServer, + OpEthApi>: FullEthApiServer + + RpcNodeCore>, { type EthApi = OpEthApi>; diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 42f64c21dcd..3c8bbc66e20 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -331,8 +331,8 @@ where RpcRegistryInner, ) where - EthApi: - FullEthApiServer + RpcNodeCore, + EthApi: FullEthApiServer + + RpcNodeCore, { let Self { provider, pool, network, executor, consensus, evm_config, .. } = self; @@ -359,7 +359,7 @@ where eth: EthApi, ) -> RpcRegistryInner where - EthApi: EthApiTypes + RpcNodeCore + 'static, + EthApi: EthApiTypes + RpcNodeCore + 'static, { let Self { provider, pool, network, executor, consensus, evm_config, .. } = self; RpcRegistryInner::new(provider, pool, network, executor, consensus, config, evm_config, eth) @@ -373,8 +373,8 @@ where eth: EthApi, ) -> TransportRpcModules<()> where - EthApi: - FullEthApiServer + RpcNodeCore, + EthApi: FullEthApiServer + + RpcNodeCore, { let mut modules = TransportRpcModules::default(); @@ -530,7 +530,7 @@ where + 'static, Pool: Send + Sync + Clone + 'static, Network: Clone + 'static, - EthApi: EthApiTypes + RpcNodeCore + 'static, + EthApi: EthApiTypes + RpcNodeCore + 'static, EvmConfig: ConfigureEvm, { /// Creates a new, empty instance. @@ -715,7 +715,10 @@ where /// If called outside of the tokio runtime. See also [`Self::eth_api`] pub fn register_debug(&mut self) -> &mut Self where - EthApi: EthApiSpec + EthTransactions + TraceExt + RpcNodeCore, + EthApi: EthApiSpec + + EthTransactions + + TraceExt + + RpcNodeCore, { let debug_api = self.debug_api(); self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into()); @@ -824,7 +827,7 @@ where /// If called outside of the tokio runtime. See also [`Self::eth_api`] pub fn debug_api(&self) -> DebugApi where - EthApi: EthApiTypes + RpcNodeCore, + EthApi: EthApiTypes + RpcNodeCore, { DebugApi::new( self.eth_api().clone(), @@ -862,7 +865,7 @@ where + ChangeSetReader, Pool: TransactionPool + Clone + 'static, Network: NetworkInfo + Peers + Clone + 'static, - EthApi: FullEthApiServer + RpcNodeCore, + EthApi: FullEthApiServer + RpcNodeCore, EvmConfig: ConfigureEvm + 'static, Consensus: FullConsensus + Clone + 'static, { diff --git a/crates/rpc/rpc-eth-api/src/types.rs b/crates/rpc/rpc-eth-api/src/types.rs index b4b23e01680..e32194d07cc 100644 --- a/crates/rpc/rpc-eth-api/src/types.rs +++ b/crates/rpc/rpc-eth-api/src/types.rs @@ -2,6 +2,7 @@ use crate::{AsEthApiError, FromEthApiError, RpcNodeCore}; use alloy_rpc_types_eth::Block; +use reth_primitives_traits::Block as BlockTrait; use reth_rpc_convert::{RpcConvert, SignableTxRequest}; pub use reth_rpc_convert::{RpcTransaction, RpcTxReq, RpcTypes}; use reth_storage_api::ProviderTx; @@ -15,7 +16,7 @@ use std::error::Error; /// /// This type is stateful so that it can provide additional context if necessary, e.g. populating /// receipts with additional data. -pub trait EthApiTypes: Send + Sync + Clone { +pub trait EthApiTypes: RpcNodeCore + Send + Sync + Clone { /// Extension of [`FromEthApiError`], with network specific errors. type Error: Into> + FromEthApiError @@ -28,6 +29,8 @@ pub trait EthApiTypes: Send + Sync + Clone { type NetworkTypes: RpcTypes; /// Conversion methods for transaction RPC type. type RpcConvert: RpcConvert; + /// Provider block type used by RPC. + type ProviderBlock: BlockTrait; /// Returns reference to transaction response builder. fn converter(&self) -> &Self::RpcConvert; diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 32cee038b76..9141392b995 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -20,19 +20,21 @@ use parking_lot::RwLock; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; use reth_errors::RethError; use reth_evm::{execute::Executor, ConfigureEvm, EvmEnvFor}; -use reth_primitives_traits::{Block as BlockTrait, BlockBody, ReceiptWithBloom, RecoveredBlock}; +use reth_primitives_traits::{ + Block as BlockTrait, BlockBody, BlockTy, ReceiptWithBloom, RecoveredBlock, +}; use reth_revm::{db::State, witness::ExecutionWitnessRecord}; use reth_rpc_api::DebugApiServer; use reth_rpc_convert::RpcTxReq; use reth_rpc_eth_api::{ helpers::{EthTransactions, TraceExt}, - EthApiTypes, FromEthApiError, RpcBlock, RpcConvert, RpcNodeCore, + EthApiTypes, FromEthApiError, RpcBlock, RpcConvert, }; use reth_rpc_eth_types::EthApiError; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; use reth_storage_api::{ - BlockIdReader, BlockReaderIdExt, HeaderProvider, ProviderBlock, ReceiptProviderIdExt, - StateProofProvider, StateProviderFactory, StateRootProvider, TransactionVariant, + BlockIdReader, BlockReaderIdExt, HeaderProvider, ReceiptProviderIdExt, StateProofProvider, + StateProviderFactory, StateRootProvider, TransactionVariant, }; use reth_tasks::pool::BlockingTaskGuard; use reth_trie_common::{updates::TrieUpdates, HashedPostState}; @@ -45,7 +47,7 @@ use tokio::sync::{AcquireError, OwnedSemaphorePermit}; /// `debug` API implementation. /// /// This type provides the functionality for handling `debug` related requests. -pub struct DebugApi { +pub struct DebugApi { inner: Arc>, } @@ -92,13 +94,13 @@ impl Default for BadBlockStore { impl DebugApi where - Eth: RpcNodeCore, + Eth: EthApiTypes, { /// Create a new instance of the [`DebugApi`] pub fn new( eth_api: Eth, blocking_task_guard: BlockingTaskGuard, - bad_block_store: BadBlockStore>, + bad_block_store: BadBlockStore>, ) -> Self { let inner = Arc::new(DebugApiInner { eth_api, blocking_task_guard, bad_block_store }); Self { inner } @@ -115,7 +117,7 @@ where } /// Access the bad block store. - pub fn bad_block_store(&self) -> &BadBlockStore> { + pub fn bad_block_store(&self) -> &BadBlockStore> { &self.inner.bad_block_store } } @@ -134,7 +136,7 @@ where /// Trace the entire block asynchronously async fn trace_block( &self, - block: Arc>>, + block: Arc>>, evm_env: EvmEnvFor, opts: GethDebugTracingOptions, ) -> Result, Eth::Error> { @@ -194,7 +196,7 @@ where rlp_block: Bytes, opts: GethDebugTracingOptions, ) -> Result, Eth::Error> { - let block: ProviderBlock = Decodable::decode(&mut rlp_block.as_ref()) + let block: BlockTy = Decodable::decode(&mut rlp_block.as_ref()) .map_err(BlockError::RlpDecodeRawBlock) .map_err(Eth::Error::from_eth_err)?; @@ -565,7 +567,7 @@ where /// Generates an execution witness, using the given recovered block. pub async fn debug_execution_witness_for_block( &self, - block: Arc>>, + block: Arc>>, ) -> Result { let block_number = block.header().number(); @@ -661,7 +663,7 @@ where #[async_trait] impl DebugApiServer> for DebugApi where - Eth: EthApiTypes + EthTransactions + TraceExt + RpcNodeCore + 'static, + Eth: EthApiTypes + EthTransactions + TraceExt + 'static, { /// Handler for `debug_getRawHeader` async fn raw_header(&self, block_id: BlockId) -> RpcResult { @@ -711,7 +713,7 @@ where /// Handler for `debug_getRawTransactions` /// Returns the bytes of the transaction for the given hash. async fn raw_transactions(&self, block_id: BlockId) -> RpcResult> { - let block: RecoveredBlock> = self + let block: RecoveredBlock> = self .provider() .block_with_senders_by_id(block_id, TransactionVariant::NoHash) .to_rpc_result()? @@ -1130,22 +1132,22 @@ where } } -impl std::fmt::Debug for DebugApi { +impl std::fmt::Debug for DebugApi { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DebugApi").finish_non_exhaustive() } } -impl Clone for DebugApi { +impl Clone for DebugApi { fn clone(&self) -> Self { Self { inner: Arc::clone(&self.inner) } } } -struct DebugApiInner { +struct DebugApiInner { /// The implementation of `eth` API eth_api: Eth, // restrict the number of concurrent calls to blocking calls blocking_task_guard: BlockingTaskGuard, - bad_block_store: BadBlockStore>, + bad_block_store: BadBlockStore>, } diff --git a/crates/rpc/rpc/src/eth/core.rs b/crates/rpc/rpc/src/eth/core.rs index 5b16efd6b40..89abde44381 100644 --- a/crates/rpc/rpc/src/eth/core.rs +++ b/crates/rpc/rpc/src/eth/core.rs @@ -24,7 +24,7 @@ use reth_rpc_eth_types::{ builder::config::PendingBlockKind, receipt::EthReceiptConverter, tx_forward::ForwardConfig, EthApiError, EthStateCache, FeeHistoryCache, GasCap, GasPriceOracle, PendingBlock, }; -use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, ProviderHeader}; +use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, ProviderBlock, ProviderHeader}; use reth_tasks::{ pool::{BlockingTaskGuard, BlockingTaskPool}, TaskSpawner, TokioTaskExecutor, @@ -189,6 +189,7 @@ where type Error = EthApiError; type NetworkTypes = Rpc::Network; type RpcConvert = Rpc; + type ProviderBlock = ProviderBlock; fn converter(&self) -> &Self::RpcConvert { &self.converter From 539d2b2b9382bdf02075e8a5dc3f36f6a200344e Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 13:38:27 +0800 Subject: [PATCH 09/17] skip the duplicated bad block hash --- crates/rpc/rpc/src/debug.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 9141392b995..de1dfe81319 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -70,8 +70,10 @@ impl BadBlockStore { let hash = block.hash(); let mut guard = self.inner.write(); - // drop existing entry with same hash to keep most recent order - guard.retain(|b| b.hash() != hash); + // skip if we already recorded this bad block , and keep original ordering + if guard.iter().any(|b| b.hash() == hash) { + return; + } guard.push_back(Arc::new(block)); while guard.len() > self.limit { From ff75393e50c473393562b4e6ac5ddf8e95bdd796 Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 13:46:27 +0800 Subject: [PATCH 10/17] reoder BadBlockStore func location --- crates/rpc/rpc/src/debug.rs | 86 ++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index de1dfe81319..b1f269753b2 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -51,49 +51,6 @@ pub struct DebugApi { inner: Arc>, } -/// A bounded, deduplicating store of recently observed bad blocks. -#[derive(Clone, Debug)] -pub struct BadBlockStore { - inner: Arc>>>>, - limit: usize, -} - -impl BadBlockStore { - /// Creates a new store with the given capacity. - pub fn new(limit: usize) -> Self { - Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit } - } - - /// Inserts a recovered block, keeping only the most recent `limit` entries and deduplicating - /// by block hash. - pub fn insert(&self, block: RecoveredBlock) { - let hash = block.hash(); - let mut guard = self.inner.write(); - - // skip if we already recorded this bad block , and keep original ordering - if guard.iter().any(|b| b.hash() == hash) { - return; - } - guard.push_back(Arc::new(block)); - - while guard.len() > self.limit { - guard.pop_front(); - } - } - - /// Returns all cached bad blocks ordered from newest to oldest. - pub fn all(&self) -> Vec>> { - let guard = self.inner.read(); - guard.iter().rev().cloned().collect() - } -} - -impl Default for BadBlockStore { - fn default() -> Self { - Self::new(64) - } -} - impl DebugApi where Eth: EthApiTypes, @@ -1146,6 +1103,49 @@ impl Clone for DebugApi { } } +/// A bounded, deduplicating store of recently observed bad blocks. +#[derive(Clone, Debug)] +pub struct BadBlockStore { + inner: Arc>>>>, + limit: usize, +} + +impl BadBlockStore { + /// Creates a new store with the given capacity. + pub fn new(limit: usize) -> Self { + Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit } + } + + /// Inserts a recovered block, keeping only the most recent `limit` entries and deduplicating + /// by block hash. + pub fn insert(&self, block: RecoveredBlock) { + let hash = block.hash(); + let mut guard = self.inner.write(); + + // skip if we already recorded this bad block , and keep original ordering + if guard.iter().any(|b| b.hash() == hash) { + return; + } + guard.push_back(Arc::new(block)); + + while guard.len() > self.limit { + guard.pop_front(); + } + } + + /// Returns all cached bad blocks ordered from newest to oldest. + pub fn all(&self) -> Vec>> { + let guard = self.inner.read(); + guard.iter().rev().cloned().collect() + } +} + +impl Default for BadBlockStore { + fn default() -> Self { + Self::new(64) + } +} + struct DebugApiInner { /// The implementation of `eth` API eth_api: Eth, From 028be7c9d41ee06d555ed89b235ebfcbb9c5750a Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 14:01:00 +0800 Subject: [PATCH 11/17] only enable blockstore collection when debug installed --- crates/node/builder/src/rpc.rs | 40 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index b9f3ff00c00..efd33a78fde 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -33,7 +33,8 @@ use reth_rpc_api::{eth::helpers::EthTransactions, IntoEngineApiRpcModule}; use reth_rpc_builder::{ auth::{AuthRpcModule, AuthServerHandle}, config::RethRpcServerConfig, - RpcModuleBuilder, RpcRegistryInner, RpcServerConfig, RpcServerHandle, TransportRpcModules, + RethRpcModule, RpcModuleBuilder, RpcRegistryInner, RpcServerConfig, RpcServerHandle, + TransportRpcModules, }; use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; use reth_rpc_eth_types::{cache::cache_new_blocks_task, EthConfig, EthStateCache}; @@ -1003,6 +1004,11 @@ where let auth_config = config.rpc.auth_server_config(jwt_secret)?; let module_config = config.rpc.transport_rpc_module_config(); + // Only start collecting bad blocks if the debug_ endpoint installed + let debug_enabled = + module_config.http().is_some_and(|sel| sel.contains(&RethRpcModule::Debug)) || + module_config.ws().is_some_and(|sel| sel.contains(&RethRpcModule::Debug)) || + module_config.ipc().is_some_and(|sel| sel.contains(&RethRpcModule::Debug)); debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config"); let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default() @@ -1020,22 +1026,24 @@ where registry.eth_api().signers().write().extend(signers); } - // keep track of invalid blocks for `debug_getBadBlocks` - let bad_block_store = registry.bad_block_store().clone(); - let mut engine_events_stream = engine_events.new_listener(); - node.task_executor().spawn_critical( - "collect bad blocks", - Box::pin(async move { - while let Some(event) = engine_events_stream.next().await { - if let ConsensusEngineEvent::InvalidBlock(block) = event && - let Ok(recovered) = - RecoveredBlock::try_recover_sealed(block.as_ref().clone()) - { - bad_block_store.insert(recovered); + // keep track of invalid blocks for `debug_getBadBlocks` only if debug RPC is enabled + if debug_enabled { + let bad_block_store = registry.bad_block_store().clone(); + let mut engine_events_stream = engine_events.new_listener(); + node.task_executor().spawn_critical( + "collect bad blocks", + Box::pin(async move { + while let Some(event) = engine_events_stream.next().await { + if let ConsensusEngineEvent::InvalidBlock(block) = event && + let Ok(recovered) = + RecoveredBlock::try_recover_sealed(block.as_ref().clone()) + { + bad_block_store.insert(recovered); + } } - } - }), - ); + }), + ); + } let mut registry = RpcRegistry { registry }; let ctx = RpcContext { From 561df782071948439c221ac9776ab4f288bdaae5 Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 14:08:58 +0800 Subject: [PATCH 12/17] spawn regular task --- crates/node/builder/src/rpc.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index efd33a78fde..90ec9369651 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -1030,19 +1030,16 @@ where if debug_enabled { let bad_block_store = registry.bad_block_store().clone(); let mut engine_events_stream = engine_events.new_listener(); - node.task_executor().spawn_critical( - "collect bad blocks", - Box::pin(async move { - while let Some(event) = engine_events_stream.next().await { - if let ConsensusEngineEvent::InvalidBlock(block) = event && - let Ok(recovered) = - RecoveredBlock::try_recover_sealed(block.as_ref().clone()) - { - bad_block_store.insert(recovered); - } + node.task_executor().spawn(Box::pin(async move { + while let Some(event) = engine_events_stream.next().await { + if let ConsensusEngineEvent::InvalidBlock(block) = event && + let Ok(recovered) = + RecoveredBlock::try_recover_sealed(block.as_ref().clone()) + { + bad_block_store.insert(recovered); } - }), - ); + } + })); } let mut registry = RpcRegistry { registry }; From a78fee790ab49b8bc8a04bc87728c138ca9c7ad7 Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 14:25:04 +0800 Subject: [PATCH 13/17] complete mattse changes --- crates/e2e-test-utils/src/node.rs | 5 ++++- crates/e2e-test-utils/src/rpc.rs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index f0c06516b07..23990b63ab2 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -91,7 +91,10 @@ where AddOns::EthApi: EthApiSpec>> + EthTransactions + TraceExt - + reth_rpc_eth_api::RpcNodeCore, + + reth_rpc_eth_api::RpcNodeCore< + Provider = Node::Provider, + Primitives = ::Primitives, + >, { let mut chain = Vec::with_capacity(length as usize); for i in 0..length { diff --git a/crates/e2e-test-utils/src/rpc.rs b/crates/e2e-test-utils/src/rpc.rs index df3613e5fa3..8e939c1eac1 100644 --- a/crates/e2e-test-utils/src/rpc.rs +++ b/crates/e2e-test-utils/src/rpc.rs @@ -9,7 +9,7 @@ use reth_provider::BlockReader; use reth_rpc_api::DebugApiServer; use reth_rpc_eth_api::{ helpers::{EthApiSpec, EthTransactions, TraceExt}, - EthApiTypes, + EthApiTypes, RpcNodeCore, }; #[expect(missing_debug_implementations)] @@ -23,7 +23,7 @@ where EthApi: EthApiSpec>> + EthTransactions + TraceExt - + reth_rpc_eth_api::RpcNodeCore, + + RpcNodeCore::Primitives>, { /// Injects a raw transaction into the node tx pool via RPC server pub async fn inject_tx(&self, raw_tx: Bytes) -> Result { From 947b00e11c1673a1f16abc55beeddc9dbce019a5 Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 7 Dec 2025 14:36:55 +0800 Subject: [PATCH 14/17] clippy --- examples/exex-hello-world/src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/exex-hello-world/src/main.rs b/examples/exex-hello-world/src/main.rs index a33e9ef2807..97aa0b005b0 100644 --- a/examples/exex-hello-world/src/main.rs +++ b/examples/exex-hello-world/src/main.rs @@ -66,7 +66,11 @@ async fn ethapi_exex( ) -> eyre::Result<()> where Node: FullNodeComponents>, - EthApi: FullEthApi + reth_ethereum::rpc::eth::RpcNodeCore, + EthApi: FullEthApi + + reth_ethereum::rpc::eth::RpcNodeCore< + Provider = Node::Provider, + Primitives = ::Primitives, + >, { // Wait for the ethapi to be sent from the main function let rpc_handle = rpc_handle.await?; From 8f3586df6055066638209651c0c101395f4e4e21 Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 8 Dec 2025 13:16:35 +0800 Subject: [PATCH 15/17] move specific logic to inside debugApi --- crates/node/builder/src/rpc.rs | 18 ++++-------------- crates/rpc/rpc/src/debug.rs | 31 ++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 90ec9369651..e8d37de7516 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -10,7 +10,6 @@ use crate::{ }; use alloy_rpc_types::engine::ClientVersionV1; use alloy_rpc_types_engine::ExecutionData; -use futures::StreamExt; use jsonrpsee::{core::middleware::layer::Either, RpcModule}; use reth_chain_state::CanonStateSubscriptions; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks, Hardforks}; @@ -24,7 +23,6 @@ use reth_node_core::{ version::{version_metadata, CLIENT_CODE}, }; use reth_payload_builder::{PayloadBuilderHandle, PayloadStore}; -use reth_primitives_traits::RecoveredBlock; use reth_rpc::{ eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer, RpcNodeCore}, AdminApi, @@ -1028,18 +1026,10 @@ where // keep track of invalid blocks for `debug_getBadBlocks` only if debug RPC is enabled if debug_enabled { - let bad_block_store = registry.bad_block_store().clone(); - let mut engine_events_stream = engine_events.new_listener(); - node.task_executor().spawn(Box::pin(async move { - while let Some(event) = engine_events_stream.next().await { - if let ConsensusEngineEvent::InvalidBlock(block) = event && - let Ok(recovered) = - RecoveredBlock::try_recover_sealed(block.as_ref().clone()) - { - bad_block_store.insert(recovered); - } - } - })); + registry.debug_api().spawn_invalid_block_listener( + engine_events.new_listener(), + node.task_executor().clone(), + ); } let mut registry = RpcRegistry { registry }; diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index b1f269753b2..fe9ba89d981 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -18,6 +18,7 @@ use async_trait::async_trait; use jsonrpsee::core::RpcResult; use parking_lot::RwLock; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; +use reth_engine_primitives::ConsensusEngineEvent; use reth_errors::RethError; use reth_evm::{execute::Executor, ConfigureEvm, EvmEnvFor}; use reth_primitives_traits::{ @@ -36,13 +37,14 @@ use reth_storage_api::{ BlockIdReader, BlockReaderIdExt, HeaderProvider, ReceiptProviderIdExt, StateProofProvider, StateProviderFactory, StateRootProvider, TransactionVariant, }; -use reth_tasks::pool::BlockingTaskGuard; +use reth_tasks::{pool::BlockingTaskGuard, TaskSpawner}; use reth_trie_common::{updates::TrieUpdates, HashedPostState}; use revm::DatabaseCommit; use revm_inspectors::tracing::{DebugInspector, TransactionContext}; use serde::{Deserialize, Serialize}; use std::{collections::VecDeque, sync::Arc}; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; +use tokio_stream::StreamExt; /// `debug` API implementation. /// @@ -1103,6 +1105,33 @@ impl Clone for DebugApi { } } +impl DebugApi +where + Eth: EthApiTypes, +{ + /// Listens for invalid blocks and records them in the bad block store. + pub fn spawn_invalid_block_listener(&self, mut stream: St, executor: S) + where + S: TaskSpawner + Clone + 'static, + St: tokio_stream::Stream> + + Send + + Unpin + + 'static, + { + let bad_block_store = self.bad_block_store().clone(); + executor.spawn(Box::pin(async move { + while let Some(event) = stream.next().await { + if let ConsensusEngineEvent::InvalidBlock(block) = event && + let Ok(recovered) = + RecoveredBlock::try_recover_sealed(block.as_ref().clone()) + { + bad_block_store.insert(recovered); + } + } + })); + } +} + /// A bounded, deduplicating store of recently observed bad blocks. #[derive(Clone, Debug)] pub struct BadBlockStore { From 6720a482ed0608b8a8fbdc0e5a8cab1e2a3933ca Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 8 Dec 2025 13:55:34 +0800 Subject: [PATCH 16/17] clean up unnessary trait changes --- crates/rpc/rpc-eth-api/src/types.rs | 6 +----- crates/rpc/rpc/src/debug.rs | 18 +++++++++--------- crates/rpc/rpc/src/eth/core.rs | 3 +-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/types.rs b/crates/rpc/rpc-eth-api/src/types.rs index e32194d07cc..c4f723e3eec 100644 --- a/crates/rpc/rpc-eth-api/src/types.rs +++ b/crates/rpc/rpc-eth-api/src/types.rs @@ -2,7 +2,6 @@ use crate::{AsEthApiError, FromEthApiError, RpcNodeCore}; use alloy_rpc_types_eth::Block; -use reth_primitives_traits::Block as BlockTrait; use reth_rpc_convert::{RpcConvert, SignableTxRequest}; pub use reth_rpc_convert::{RpcTransaction, RpcTxReq, RpcTypes}; use reth_storage_api::ProviderTx; @@ -16,7 +15,7 @@ use std::error::Error; /// /// This type is stateful so that it can provide additional context if necessary, e.g. populating /// receipts with additional data. -pub trait EthApiTypes: RpcNodeCore + Send + Sync + Clone { +pub trait EthApiTypes: Send + Sync + Clone { /// Extension of [`FromEthApiError`], with network specific errors. type Error: Into> + FromEthApiError @@ -29,9 +28,6 @@ pub trait EthApiTypes: RpcNodeCore + Send + Sync + Clone { type NetworkTypes: RpcTypes; /// Conversion methods for transaction RPC type. type RpcConvert: RpcConvert; - /// Provider block type used by RPC. - type ProviderBlock: BlockTrait; - /// Returns reference to transaction response builder. fn converter(&self) -> &Self::RpcConvert; } diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index fe9ba89d981..1fb7fe0d34a 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -29,7 +29,7 @@ use reth_rpc_api::DebugApiServer; use reth_rpc_convert::RpcTxReq; use reth_rpc_eth_api::{ helpers::{EthTransactions, TraceExt}, - EthApiTypes, FromEthApiError, RpcBlock, RpcConvert, + EthApiTypes, FromEthApiError, RpcBlock, RpcConvert, RpcNodeCore, }; use reth_rpc_eth_types::EthApiError; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; @@ -49,13 +49,13 @@ use tokio_stream::StreamExt; /// `debug` API implementation. /// /// This type provides the functionality for handling `debug` related requests. -pub struct DebugApi { +pub struct DebugApi { inner: Arc>, } impl DebugApi where - Eth: EthApiTypes, + Eth: EthApiTypes + RpcNodeCore, { /// Create a new instance of the [`DebugApi`] pub fn new( @@ -87,7 +87,7 @@ where impl DebugApi where - Eth: EthApiTypes + TraceExt + 'static, + Eth: EthApiTypes + RpcNodeCore + TraceExt + 'static, { /// Acquires a permit to execute a tracing call. async fn acquire_trace_permit(&self) -> Result { @@ -624,7 +624,7 @@ where #[async_trait] impl DebugApiServer> for DebugApi where - Eth: EthApiTypes + EthTransactions + TraceExt + 'static, + Eth: EthApiTypes + RpcNodeCore + EthTransactions + TraceExt + 'static, { /// Handler for `debug_getRawHeader` async fn raw_header(&self, block_id: BlockId) -> RpcResult { @@ -1093,13 +1093,13 @@ where } } -impl std::fmt::Debug for DebugApi { +impl std::fmt::Debug for DebugApi { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DebugApi").finish_non_exhaustive() } } -impl Clone for DebugApi { +impl Clone for DebugApi { fn clone(&self) -> Self { Self { inner: Arc::clone(&self.inner) } } @@ -1107,7 +1107,7 @@ impl Clone for DebugApi { impl DebugApi where - Eth: EthApiTypes, + Eth: EthApiTypes + RpcNodeCore, { /// Listens for invalid blocks and records them in the bad block store. pub fn spawn_invalid_block_listener(&self, mut stream: St, executor: S) @@ -1175,7 +1175,7 @@ impl Default for BadBlockStore { } } -struct DebugApiInner { +struct DebugApiInner { /// The implementation of `eth` API eth_api: Eth, // restrict the number of concurrent calls to blocking calls diff --git a/crates/rpc/rpc/src/eth/core.rs b/crates/rpc/rpc/src/eth/core.rs index 89abde44381..5b16efd6b40 100644 --- a/crates/rpc/rpc/src/eth/core.rs +++ b/crates/rpc/rpc/src/eth/core.rs @@ -24,7 +24,7 @@ use reth_rpc_eth_types::{ builder::config::PendingBlockKind, receipt::EthReceiptConverter, tx_forward::ForwardConfig, EthApiError, EthStateCache, FeeHistoryCache, GasCap, GasPriceOracle, PendingBlock, }; -use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, ProviderBlock, ProviderHeader}; +use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, ProviderHeader}; use reth_tasks::{ pool::{BlockingTaskGuard, BlockingTaskPool}, TaskSpawner, TokioTaskExecutor, @@ -189,7 +189,6 @@ where type Error = EthApiError; type NetworkTypes = Rpc::Network; type RpcConvert = Rpc; - type ProviderBlock = ProviderBlock; fn converter(&self) -> &Self::RpcConvert { &self.converter From 09a99f3bd2a778e64c42853273a3297347ccfbf1 Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 8 Dec 2025 14:01:36 +0800 Subject: [PATCH 17/17] clippy --- crates/optimism/rpc/src/eth/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index 1a9a68fe639..d49ebc0e948 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -38,7 +38,7 @@ use reth_rpc_eth_api::{ RpcNodeCoreExt, RpcTypes, }; use reth_rpc_eth_types::{EthStateCache, FeeHistoryCache, GasPriceOracle, PendingBlock}; -use reth_storage_api::{BlockReaderIdExt, ProviderBlock, ProviderHeader}; +use reth_storage_api::{BlockReaderIdExt, ProviderHeader}; use reth_tasks::{ pool::{BlockingTaskGuard, BlockingTaskPool}, TaskSpawner, @@ -193,7 +193,6 @@ where type Error = OpEthApiError; type NetworkTypes = Rpc::Network; type RpcConvert = Rpc; - type ProviderBlock = ProviderBlock; fn converter(&self) -> &Self::RpcConvert { self.inner.eth_api.converter()