Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/e2e-test-utils/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ where
where
AddOns::EthApi: EthApiSpec<Provider: BlockReader<Block = BlockTy<Node::Types>>>
+ EthTransactions
+ TraceExt,
+ TraceExt
+ reth_rpc_eth_api::RpcNodeCore<
Provider = Node::Provider,
Primitives = <Node::Types as NodeTypes>::Primitives,
>,
{
let mut chain = Vec::with_capacity(length as usize);
for i in 0..length {
Expand Down
5 changes: 3 additions & 2 deletions crates/e2e-test-utils/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -22,7 +22,8 @@ where
Node: FullNodeComponents<Types: NodeTypes<ChainSpec: EthereumHardforks>>,
EthApi: EthApiSpec<Provider: BlockReader<Block = BlockTy<Node::Types>>>
+ EthTransactions
+ TraceExt,
+ TraceExt
+ RpcNodeCore<Provider = Node::Provider, Primitives = <Node::Types as NodeTypes>::Primitives>,
{
/// Injects a raw transaction into the node tx pool via RPC server
pub async fn inject_tx(&self, raw_tx: Bytes) -> Result<B256, EthApi::Error> {
Expand Down
29 changes: 27 additions & 2 deletions crates/node/builder/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -23,15 +24,17 @@ 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},
eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer, RpcNodeCore},
AdminApi,
};
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};
Expand Down Expand Up @@ -1001,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()
Expand All @@ -1018,6 +1026,22 @@ where
registry.eth_api().signers().write().extend(signers);
}

// 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);
}
}
}));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm can we not have this being handled by DebugApi itself?

Copy link
Contributor Author

@0xKarl98 0xKarl98 Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have moved this part of logic to inside DebugApi


let mut registry = RpcRegistry { registry };
let ctx = RpcContext {
node: node.clone(),
Expand Down Expand Up @@ -1189,6 +1213,7 @@ impl<'a, N: FullNodeComponents<Types: NodeTypes<ChainSpec: Hardforks + EthereumH
pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
/// The Ethapi implementation this builder will build.
type EthApi: EthApiTypes
+ RpcNodeCore<Primitives = PrimitivesTy<N::Types>>
+ FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
+ Unpin
+ 'static;
Expand Down
9 changes: 5 additions & 4 deletions crates/optimism/rpc/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -193,6 +193,7 @@ where
type Error = OpEthApiError;
type NetworkTypes = Rpc::Network;
type RpcConvert = Rpc;
type ProviderBlock = ProviderBlock<N::Provider>;

fn converter(&self) -> &Self::RpcConvert {
self.inner.eth_api.converter()
Expand Down Expand Up @@ -487,8 +488,8 @@ where
>,
NetworkT: RpcTypes,
OpRpcConvert<N, NetworkT>: RpcConvert<Network = NetworkT>,
OpEthApi<N, OpRpcConvert<N, NetworkT>>:
FullEthApiServer<Provider = N::Provider, Pool = N::Pool>,
OpEthApi<N, OpRpcConvert<N, NetworkT>>: FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
+ RpcNodeCore<Primitives = PrimitivesTy<N::Types>>,
{
type EthApi = OpEthApi<N, OpRpcConvert<N, NetworkT>>;

Expand Down
4 changes: 2 additions & 2 deletions crates/rpc/rpc-api/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -38,7 +38,7 @@ pub trait DebugApi<TxReq: RpcObject> {

/// Returns an array of recent bad blocks that the client has seen on the network.
#[method(name = "getBadBlocks")]
async fn bad_blocks(&self) -> RpcResult<Vec<Block>>;
async fn bad_blocks(&self) -> RpcResult<Vec<BadBlock>>;

/// Returns the structured logs created during the execution of EVM between two blocks
/// (excluding start) as a JSON object.
Expand Down
56 changes: 38 additions & 18 deletions crates/rpc/rpc-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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};
Expand Down Expand Up @@ -332,7 +331,8 @@ where
RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>,
)
where
EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>
+ RpcNodeCore<Provider = Provider, Primitives = N>,
{
let Self { provider, pool, network, executor, consensus, evm_config, .. } = self;

Expand All @@ -359,7 +359,7 @@ where
eth: EthApi,
) -> RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
where
EthApi: EthApiTypes + 'static,
EthApi: EthApiTypes + RpcNodeCore<Provider = Provider, Primitives = N> + 'static,
{
let Self { provider, pool, network, executor, consensus, evm_config, .. } = self;
RpcRegistryInner::new(provider, pool, network, executor, consensus, config, evm_config, eth)
Expand All @@ -373,7 +373,8 @@ where
eth: EthApi,
) -> TransportRpcModules<()>
where
EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>
+ RpcNodeCore<Provider = Provider, Primitives = N>,
{
let mut modules = TransportRpcModules::default();

Expand Down Expand Up @@ -511,6 +512,8 @@ pub struct RpcRegistryInner<
modules: HashMap<RethRpcModule, Methods>,
/// eth config settings
eth_config: EthConfig,
/// Recent bad blocks observed by the node.
bad_block_store: BadBlockStore<Provider::Block>,
}

// === impl RpcRegistryInner ===
Expand All @@ -527,7 +530,7 @@ where
+ 'static,
Pool: Send + Sync + Clone + 'static,
Network: Clone + 'static,
EthApi: EthApiTypes + 'static,
EthApi: EthApiTypes + RpcNodeCore<Provider = Provider, Primitives = N> + 'static,
EvmConfig: ConfigureEvm<Primitives = N>,
{
/// Creates a new, empty instance.
Expand Down Expand Up @@ -560,6 +563,7 @@ where
blocking_pool_guard,
eth_config: config.eth,
evm_config,
bad_block_store: BadBlockStore::default(),
}
}
}
Expand Down Expand Up @@ -595,6 +599,11 @@ where
&self.provider
}

/// Returns the bad block store.
pub const fn bad_block_store(&self) -> &BadBlockStore<Provider::Block> {
&self.bad_block_store
}

/// Returns all installed methods
pub fn methods(&self) -> Vec<Methods> {
self.modules.values().cloned().collect()
Expand Down Expand Up @@ -706,8 +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,
EvmConfig::Primitives: NodePrimitives<Block = ProviderBlock<EthApi::Provider>>,
EthApi: EthApiSpec
+ EthTransactions
+ TraceExt
+ RpcNodeCore<Provider = Provider, Primitives = N>,
{
let debug_api = self.debug_api();
self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into());
Expand Down Expand Up @@ -814,8 +825,15 @@ where
/// # Panics
///
/// If called outside of the tokio runtime. See also [`Self::eth_api`]
pub fn debug_api(&self) -> DebugApi<EthApi> {
DebugApi::new(self.eth_api().clone(), self.blocking_pool_guard.clone())
pub fn debug_api(&self) -> DebugApi<EthApi>
where
EthApi: EthApiTypes + RpcNodeCore<Provider = Provider, Primitives = N>,
{
DebugApi::new(
self.eth_api().clone(),
self.blocking_pool_guard.clone(),
self.bad_block_store.clone(),
)
}

/// Instantiates `NetApi`
Expand Down Expand Up @@ -847,7 +865,7 @@ where
+ ChangeSetReader,
Pool: TransactionPool + Clone + 'static,
Network: NetworkInfo + Peers + Clone + 'static,
EthApi: FullEthApiServer,
EthApi: FullEthApiServer + RpcNodeCore<Provider = Provider, Primitives = N>,
EvmConfig: ConfigureEvm<Primitives = N> + 'static,
Consensus: FullConsensus<N, Error = ConsensusError> + Clone + 'static,
{
Expand Down Expand Up @@ -933,11 +951,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();
Expand Down
5 changes: 4 additions & 1 deletion crates/rpc/rpc-eth-api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<jsonrpsee_types::error::ErrorObject<'static>>
+ FromEthApiError
Expand All @@ -28,6 +29,8 @@ pub trait EthApiTypes: Send + Sync + Clone {
type NetworkTypes: RpcTypes;
/// Conversion methods for transaction RPC type.
type RpcConvert: RpcConvert<Network = Self::NetworkTypes>;
/// Provider block type used by RPC.
type ProviderBlock: BlockTrait;

/// Returns reference to transaction response builder.
fn converter(&self) -> &Self::RpcConvert;
Expand Down
Loading
Loading