mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-27 16:18:08 -05:00
993 lines
33 KiB
Rust
993 lines
33 KiB
Rust
//! Builder support for rpc components.
|
|
|
|
use crate::{BeaconConsensusEngineEvent, BeaconConsensusEngineHandle};
|
|
use alloy_rpc_types::engine::ClientVersionV1;
|
|
use alloy_rpc_types_engine::ExecutionData;
|
|
use jsonrpsee::RpcModule;
|
|
use reth_chain_state::CanonStateSubscriptions;
|
|
use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
|
|
use reth_node_api::{
|
|
AddOnsContext, BlockTy, EngineTypes, EngineValidator, FullNodeComponents, FullNodeTypes,
|
|
NodeAddOns, NodeTypes, PayloadTypes, ReceiptTy,
|
|
};
|
|
use reth_node_core::{
|
|
node_config::NodeConfig,
|
|
version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA},
|
|
};
|
|
use reth_payload_builder::{PayloadBuilderHandle, PayloadStore};
|
|
use reth_rpc::eth::{EthApiTypes, FullEthApiServer};
|
|
use reth_rpc_api::{eth::helpers::AddDevSigners, IntoEngineApiRpcModule};
|
|
use reth_rpc_builder::{
|
|
auth::{AuthRpcModule, AuthServerHandle},
|
|
config::RethRpcServerConfig,
|
|
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};
|
|
use reth_tokio_util::EventSender;
|
|
use reth_tracing::tracing::{debug, info};
|
|
use std::{
|
|
fmt::{self, Debug},
|
|
future::Future,
|
|
ops::{Deref, DerefMut},
|
|
};
|
|
|
|
/// Contains the handles to the spawned RPC servers.
|
|
///
|
|
/// This can be used to access the endpoints of the servers.
|
|
#[derive(Debug, Clone)]
|
|
pub struct RethRpcServerHandles {
|
|
/// The regular RPC server handle to all configured transports.
|
|
pub rpc: RpcServerHandle,
|
|
/// The handle to the auth server (engine API)
|
|
pub auth: AuthServerHandle,
|
|
}
|
|
|
|
/// Contains hooks that are called during the rpc setup.
|
|
pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
|
|
/// Hooks to run once RPC server is running.
|
|
pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
|
|
/// Hooks to run to configure RPC server API.
|
|
pub extend_rpc_modules: Box<dyn ExtendRpcModules<Node, EthApi>>,
|
|
}
|
|
|
|
impl<Node, EthApi> Default for RpcHooks<Node, EthApi>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn default() -> Self {
|
|
Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
|
|
}
|
|
}
|
|
|
|
impl<Node, EthApi> RpcHooks<Node, EthApi>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
/// Sets the hook that is run once the rpc server is started.
|
|
pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
|
|
where
|
|
F: OnRpcStarted<Node, EthApi> + 'static,
|
|
{
|
|
self.on_rpc_started = Box::new(hook);
|
|
self
|
|
}
|
|
|
|
/// Sets the hook that is run once the rpc server is started.
|
|
#[expect(unused)]
|
|
pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
|
|
where
|
|
F: OnRpcStarted<Node, EthApi> + 'static,
|
|
{
|
|
self.set_on_rpc_started(hook);
|
|
self
|
|
}
|
|
|
|
/// Sets the hook that is run to configure the rpc modules.
|
|
pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
|
|
where
|
|
F: ExtendRpcModules<Node, EthApi> + 'static,
|
|
{
|
|
self.extend_rpc_modules = Box::new(hook);
|
|
self
|
|
}
|
|
|
|
/// Sets the hook that is run to configure the rpc modules.
|
|
#[expect(unused)]
|
|
pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
|
|
where
|
|
F: ExtendRpcModules<Node, EthApi> + 'static,
|
|
{
|
|
self.set_extend_rpc_modules(hook);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<Node, EthApi> fmt::Debug for RpcHooks<Node, EthApi>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("RpcHooks")
|
|
.field("on_rpc_started", &"...")
|
|
.field("extend_rpc_modules", &"...")
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
/// Event hook that is called once the rpc server is started.
|
|
pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
|
|
/// The hook that is called once the rpc server is started.
|
|
fn on_rpc_started(
|
|
self: Box<Self>,
|
|
ctx: RpcContext<'_, Node, EthApi>,
|
|
handles: RethRpcServerHandles,
|
|
) -> eyre::Result<()>;
|
|
}
|
|
|
|
impl<Node, EthApi, F> OnRpcStarted<Node, EthApi> for F
|
|
where
|
|
F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + Send,
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn on_rpc_started(
|
|
self: Box<Self>,
|
|
ctx: RpcContext<'_, Node, EthApi>,
|
|
handles: RethRpcServerHandles,
|
|
) -> eyre::Result<()> {
|
|
(*self)(ctx, handles)
|
|
}
|
|
}
|
|
|
|
impl<Node, EthApi> OnRpcStarted<Node, EthApi> for ()
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn on_rpc_started(
|
|
self: Box<Self>,
|
|
_: RpcContext<'_, Node, EthApi>,
|
|
_: RethRpcServerHandles,
|
|
) -> eyre::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Event hook that is called when the rpc server is started.
|
|
pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
|
|
/// The hook that is called once the rpc server is started.
|
|
fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()>;
|
|
}
|
|
|
|
impl<Node, EthApi, F> ExtendRpcModules<Node, EthApi> for F
|
|
where
|
|
F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send,
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
|
|
(*self)(ctx)
|
|
}
|
|
}
|
|
|
|
impl<Node, EthApi> ExtendRpcModules<Node, EthApi> for ()
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn extend_rpc_modules(self: Box<Self>, _: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Helper wrapper type to encapsulate the [`RpcRegistryInner`] over components trait.
|
|
#[derive(Debug, Clone)]
|
|
#[expect(clippy::type_complexity)]
|
|
pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
pub(crate) registry: RpcRegistryInner<
|
|
Node::Provider,
|
|
Node::Pool,
|
|
Node::Network,
|
|
EthApi,
|
|
Node::Evm,
|
|
Node::Consensus,
|
|
>,
|
|
}
|
|
|
|
impl<Node, EthApi> Deref for RpcRegistry<Node, EthApi>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
type Target = RpcRegistryInner<
|
|
Node::Provider,
|
|
Node::Pool,
|
|
Node::Network,
|
|
EthApi,
|
|
Node::Evm,
|
|
Node::Consensus,
|
|
>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.registry
|
|
}
|
|
}
|
|
|
|
impl<Node, EthApi> DerefMut for RpcRegistry<Node, EthApi>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.registry
|
|
}
|
|
}
|
|
|
|
/// Helper container for the parameters commonly passed to RPC module extension functions.
|
|
#[expect(missing_debug_implementations)]
|
|
pub struct RpcModuleContainer<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
/// Holds installed modules per transport type.
|
|
pub modules: &'a mut TransportRpcModules,
|
|
/// Holds jwt authenticated rpc module.
|
|
pub auth_module: &'a mut AuthRpcModule,
|
|
/// A Helper type the holds instances of the configured modules.
|
|
pub registry: &'a mut RpcRegistry<Node, EthApi>,
|
|
}
|
|
|
|
/// Helper container to encapsulate [`RpcRegistryInner`], [`TransportRpcModules`] and
|
|
/// [`AuthRpcModule`].
|
|
///
|
|
/// This can be used to access installed modules, or create commonly used handlers like
|
|
/// [`reth_rpc::eth::EthApi`], and ultimately merge additional rpc handler into the configured
|
|
/// transport modules [`TransportRpcModules`] as well as configured authenticated methods
|
|
/// [`AuthRpcModule`].
|
|
#[expect(missing_debug_implementations)]
|
|
pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
/// The node components.
|
|
pub(crate) node: Node,
|
|
|
|
/// Gives access to the node configuration.
|
|
pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
|
|
|
|
/// A Helper type the holds instances of the configured modules.
|
|
///
|
|
/// This provides easy access to rpc handlers, such as [`RpcRegistryInner::eth_api`].
|
|
pub registry: &'a mut RpcRegistry<Node, EthApi>,
|
|
/// Holds installed modules per transport type.
|
|
///
|
|
/// This can be used to merge additional modules into the configured transports (http, ipc,
|
|
/// ws). See [`TransportRpcModules::merge_configured`]
|
|
pub modules: &'a mut TransportRpcModules,
|
|
/// Holds jwt authenticated rpc module.
|
|
///
|
|
/// This can be used to merge additional modules into the configured authenticated methods
|
|
pub auth_module: &'a mut AuthRpcModule,
|
|
}
|
|
|
|
impl<Node, EthApi> RpcContext<'_, Node, EthApi>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthApi: EthApiTypes,
|
|
{
|
|
/// Returns the config of the node.
|
|
pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
|
|
self.config
|
|
}
|
|
|
|
/// Returns a reference to the configured node.
|
|
pub const fn node(&self) -> &Node {
|
|
&self.node
|
|
}
|
|
|
|
/// Returns the transaction pool instance.
|
|
pub fn pool(&self) -> &Node::Pool {
|
|
self.node.pool()
|
|
}
|
|
|
|
/// Returns provider to interact with the node.
|
|
pub fn provider(&self) -> &Node::Provider {
|
|
self.node.provider()
|
|
}
|
|
|
|
/// Returns the handle to the network
|
|
pub fn network(&self) -> &Node::Network {
|
|
self.node.network()
|
|
}
|
|
|
|
/// Returns the handle to the payload builder service
|
|
pub fn payload_builder_handle(
|
|
&self,
|
|
) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
|
|
self.node.payload_builder_handle()
|
|
}
|
|
}
|
|
|
|
/// Handle to the launched RPC servers.
|
|
pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
/// Handles to launched servers.
|
|
pub rpc_server_handles: RethRpcServerHandles,
|
|
/// Configured RPC modules.
|
|
pub rpc_registry: RpcRegistry<Node, EthApi>,
|
|
/// Notification channel for engine API events
|
|
///
|
|
/// Caution: This is a multi-producer, multi-consumer broadcast and allows grants access to
|
|
/// dispatch events
|
|
pub engine_events:
|
|
EventSender<BeaconConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
|
|
/// Handle to the beacon consensus engine.
|
|
pub beacon_engine_handle: BeaconConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
|
|
}
|
|
|
|
impl<Node: FullNodeComponents, EthApi: EthApiTypes> Clone for RpcHandle<Node, EthApi> {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
rpc_server_handles: self.rpc_server_handles.clone(),
|
|
rpc_registry: self.rpc_registry.clone(),
|
|
engine_events: self.engine_events.clone(),
|
|
beacon_engine_handle: self.beacon_engine_handle.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Node: FullNodeComponents, EthApi: EthApiTypes> Deref for RpcHandle<Node, EthApi> {
|
|
type Target = RpcRegistry<Node, EthApi>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.rpc_registry
|
|
}
|
|
}
|
|
|
|
impl<Node: FullNodeComponents, EthApi: EthApiTypes> Debug for RpcHandle<Node, EthApi>
|
|
where
|
|
RpcRegistry<Node, EthApi>: Debug,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("RpcHandle")
|
|
.field("rpc_server_handles", &self.rpc_server_handles)
|
|
.field("rpc_registry", &self.rpc_registry)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
/// Handle returned when only the regular RPC server (HTTP/WS/IPC) is launched.
|
|
///
|
|
/// This handle provides access to the RPC server endpoints and registry, but does not
|
|
/// include an authenticated Engine API server. Use this when you only need regular
|
|
/// RPC functionality.
|
|
#[derive(Debug, Clone)]
|
|
pub struct RpcServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
/// Handle to the RPC server
|
|
pub rpc_server_handle: RpcServerHandle,
|
|
/// Configured RPC modules.
|
|
pub rpc_registry: RpcRegistry<Node, EthApi>,
|
|
/// Notification channel for engine API events
|
|
pub engine_events:
|
|
EventSender<BeaconConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
|
|
/// Handle to the consensus engine.
|
|
pub engine_handle: BeaconConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
|
|
}
|
|
|
|
/// Handle returned when only the authenticated Engine API server is launched.
|
|
///
|
|
/// This handle provides access to the Engine API server and registry, but does not
|
|
/// include the regular RPC servers (HTTP/WS/IPC). Use this for specialized setups
|
|
/// that only need Engine API functionality.
|
|
#[derive(Debug, Clone)]
|
|
pub struct AuthServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
/// Handle to the auth server (engine API)
|
|
pub auth_server_handle: AuthServerHandle,
|
|
/// Configured RPC modules.
|
|
pub rpc_registry: RpcRegistry<Node, EthApi>,
|
|
/// Notification channel for engine API events
|
|
pub engine_events:
|
|
EventSender<BeaconConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
|
|
/// Handle to the consensus engine.
|
|
pub engine_handle: BeaconConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
|
|
}
|
|
|
|
/// Internal context struct for RPC setup shared between different launch methods
|
|
struct RpcSetupContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
|
|
node: Node,
|
|
config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
|
|
modules: TransportRpcModules,
|
|
auth_module: AuthRpcModule,
|
|
auth_config: reth_rpc_builder::auth::AuthServerConfig,
|
|
registry: RpcRegistry<Node, EthApi>,
|
|
on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
|
|
engine_events: EventSender<BeaconConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
|
|
engine_handle: BeaconConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
|
|
}
|
|
|
|
/// Node add-ons containing RPC server configuration, with customizable eth API handler.
|
|
///
|
|
/// This struct can be used to provide the RPC server functionality. It is responsible for launching
|
|
/// the regular RPC and the authenticated RPC server (engine API). It is intended to be used and
|
|
/// modified as part of the [`NodeAddOns`] see for example `OpRpcAddons`, `EthereumAddOns`.
|
|
///
|
|
/// It can be modified to register RPC API handlers, see [`RpcAddOns::launch_add_ons_with`] which
|
|
/// takes a closure that provides access to all the configured modules (namespaces), and is invoked
|
|
/// just before the servers are launched. This can be used to extend the node with custom RPC
|
|
/// methods or even replace existing method handlers, see also [`TransportRpcModules`].
|
|
pub struct RpcAddOns<
|
|
Node: FullNodeComponents,
|
|
EthB: EthApiBuilder<Node>,
|
|
EV,
|
|
EB = BasicEngineApiBuilder<EV>,
|
|
> {
|
|
/// Additional RPC add-ons.
|
|
pub hooks: RpcHooks<Node, EthB::EthApi>,
|
|
/// Builder for `EthApi`
|
|
eth_api_builder: EthB,
|
|
/// Engine validator
|
|
engine_validator_builder: EV,
|
|
/// Builder for `EngineApi`
|
|
engine_api_builder: EB,
|
|
}
|
|
|
|
impl<Node, EthB, EV, EB> Debug for RpcAddOns<Node, EthB, EV, EB>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthB: EthApiBuilder<Node>,
|
|
EV: Debug,
|
|
EB: Debug,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("RpcAddOns")
|
|
.field("hooks", &self.hooks)
|
|
.field("eth_api_builder", &"...")
|
|
.field("engine_validator_builder", &self.engine_validator_builder)
|
|
.field("engine_api_builder", &self.engine_api_builder)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<Node, EthB, EV, EB> RpcAddOns<Node, EthB, EV, EB>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthB: EthApiBuilder<Node>,
|
|
{
|
|
/// Creates a new instance of the RPC add-ons.
|
|
pub fn new(
|
|
eth_api_builder: EthB,
|
|
engine_validator_builder: EV,
|
|
engine_api_builder: EB,
|
|
) -> Self {
|
|
Self {
|
|
hooks: RpcHooks::default(),
|
|
eth_api_builder,
|
|
engine_validator_builder,
|
|
engine_api_builder,
|
|
}
|
|
}
|
|
|
|
/// Maps the [`EngineApiBuilder`] builder type.
|
|
pub fn with_engine_api<T>(self, engine_api_builder: T) -> RpcAddOns<Node, EthB, EV, T> {
|
|
let Self { hooks, eth_api_builder, engine_validator_builder, .. } = self;
|
|
RpcAddOns { hooks, eth_api_builder, engine_validator_builder, engine_api_builder }
|
|
}
|
|
|
|
/// Maps the [`EngineValidatorBuilder`] builder type.
|
|
pub fn with_engine_validator<T>(
|
|
self,
|
|
engine_validator_builder: T,
|
|
) -> RpcAddOns<Node, EthB, T, EB> {
|
|
let Self { hooks, eth_api_builder, engine_api_builder, .. } = self;
|
|
RpcAddOns { hooks, eth_api_builder, engine_validator_builder, engine_api_builder }
|
|
}
|
|
|
|
/// Sets the hook that is run once the rpc server is started.
|
|
pub fn on_rpc_started<F>(mut self, hook: F) -> Self
|
|
where
|
|
F: FnOnce(RpcContext<'_, Node, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
|
|
+ Send
|
|
+ 'static,
|
|
{
|
|
self.hooks.set_on_rpc_started(hook);
|
|
self
|
|
}
|
|
|
|
/// Sets the hook that is run to configure the rpc modules.
|
|
pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
|
|
where
|
|
F: FnOnce(RpcContext<'_, Node, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
|
|
{
|
|
self.hooks.set_extend_rpc_modules(hook);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<Node, EthB, EV, EB> Default for RpcAddOns<Node, EthB, EV, EB>
|
|
where
|
|
Node: FullNodeComponents,
|
|
EthB: EthApiBuilder<Node>,
|
|
EV: Default,
|
|
EB: Default,
|
|
{
|
|
fn default() -> Self {
|
|
Self::new(EthB::default(), EV::default(), EB::default())
|
|
}
|
|
}
|
|
|
|
impl<N, EthB, EV, EB> RpcAddOns<N, EthB, EV, EB>
|
|
where
|
|
N: FullNodeComponents,
|
|
N::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
|
|
EthB: EthApiBuilder<N>,
|
|
EV: EngineValidatorBuilder<N>,
|
|
EB: EngineApiBuilder<N>,
|
|
{
|
|
/// Launches only the regular RPC server (HTTP/WS/IPC), without the authenticated Engine API
|
|
/// server.
|
|
///
|
|
/// This is useful when you only need the regular RPC functionality and want to avoid
|
|
/// starting the auth server.
|
|
pub async fn launch_rpc_server<F>(
|
|
self,
|
|
ctx: AddOnsContext<'_, N>,
|
|
ext: F,
|
|
) -> eyre::Result<RpcServerOnlyHandle<N, EthB::EthApi>>
|
|
where
|
|
F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
|
|
{
|
|
let setup_ctx = self.setup_rpc_components(ctx, ext).await?;
|
|
let RpcSetupContext {
|
|
node,
|
|
config,
|
|
mut modules,
|
|
mut auth_module,
|
|
auth_config: _,
|
|
mut registry,
|
|
on_rpc_started,
|
|
engine_events,
|
|
engine_handle,
|
|
} = setup_ctx;
|
|
|
|
let server_config = config.rpc.rpc_server_config();
|
|
let rpc_server_handle = Self::launch_rpc_server_internal(server_config, &modules).await?;
|
|
|
|
let handles =
|
|
RethRpcServerHandles { rpc: rpc_server_handle.clone(), auth: AuthServerHandle::noop() };
|
|
Self::finalize_rpc_setup(
|
|
&mut registry,
|
|
&mut modules,
|
|
&mut auth_module,
|
|
&node,
|
|
config,
|
|
on_rpc_started,
|
|
handles,
|
|
)?;
|
|
|
|
Ok(RpcServerOnlyHandle {
|
|
rpc_server_handle,
|
|
rpc_registry: registry,
|
|
engine_events,
|
|
engine_handle,
|
|
})
|
|
}
|
|
|
|
/// Launches the RPC servers with the given context and an additional hook for extending
|
|
/// modules.
|
|
pub async fn launch_add_ons_with<F>(
|
|
self,
|
|
ctx: AddOnsContext<'_, N>,
|
|
ext: F,
|
|
) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
|
|
where
|
|
F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
|
|
{
|
|
let setup_ctx = self.setup_rpc_components(ctx, ext).await?;
|
|
let RpcSetupContext {
|
|
node,
|
|
config,
|
|
mut modules,
|
|
mut auth_module,
|
|
auth_config,
|
|
mut registry,
|
|
on_rpc_started,
|
|
engine_events,
|
|
engine_handle,
|
|
} = setup_ctx;
|
|
|
|
let server_config = config.rpc.rpc_server_config();
|
|
let auth_module_clone = auth_module.clone();
|
|
|
|
// launch servers concurrently
|
|
let (rpc, auth) = futures::future::try_join(
|
|
Self::launch_rpc_server_internal(server_config, &modules),
|
|
Self::launch_auth_server_internal(auth_module_clone, auth_config),
|
|
)
|
|
.await?;
|
|
|
|
let handles = RethRpcServerHandles { rpc, auth };
|
|
|
|
Self::finalize_rpc_setup(
|
|
&mut registry,
|
|
&mut modules,
|
|
&mut auth_module,
|
|
&node,
|
|
config,
|
|
on_rpc_started,
|
|
handles.clone(),
|
|
)?;
|
|
|
|
Ok(RpcHandle {
|
|
rpc_server_handles: handles,
|
|
rpc_registry: registry,
|
|
engine_events,
|
|
beacon_engine_handle: engine_handle,
|
|
})
|
|
}
|
|
|
|
/// Common setup for RPC server initialization
|
|
async fn setup_rpc_components<'a, F>(
|
|
self,
|
|
ctx: AddOnsContext<'a, N>,
|
|
ext: F,
|
|
) -> eyre::Result<RpcSetupContext<'a, N, EthB::EthApi>>
|
|
where
|
|
F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
|
|
{
|
|
let Self { eth_api_builder, engine_api_builder, hooks, .. } = self;
|
|
|
|
let engine_api = engine_api_builder.build_engine_api(&ctx).await?;
|
|
let AddOnsContext { node, config, beacon_engine_handle, jwt_secret, engine_events } = ctx;
|
|
|
|
info!(target: "reth::cli", "Engine API handler initialized");
|
|
|
|
let cache = EthStateCache::spawn_with(
|
|
node.provider().clone(),
|
|
config.rpc.eth_config().cache,
|
|
node.task_executor().clone(),
|
|
);
|
|
|
|
let new_canonical_blocks = node.provider().canonical_state_stream();
|
|
let c = cache.clone();
|
|
node.task_executor().spawn_critical(
|
|
"cache canonical blocks task",
|
|
Box::pin(async move {
|
|
cache_new_blocks_task(c, new_canonical_blocks).await;
|
|
}),
|
|
);
|
|
|
|
let ctx = EthApiCtx { components: &node, config: config.rpc.eth_config(), cache };
|
|
let eth_api = eth_api_builder.build_eth_api(ctx).await?;
|
|
|
|
let auth_config = config.rpc.auth_server_config(jwt_secret)?;
|
|
let module_config = config.rpc.transport_rpc_module_config();
|
|
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()
|
|
.with_provider(node.provider().clone())
|
|
.with_pool(node.pool().clone())
|
|
.with_network(node.network().clone())
|
|
.with_executor(Box::new(node.task_executor().clone()))
|
|
.with_evm_config(node.evm_config().clone())
|
|
.with_consensus(node.consensus().clone())
|
|
.build_with_auth_server(module_config, engine_api, eth_api);
|
|
|
|
// in dev mode we generate 20 random dev-signer accounts
|
|
if config.dev.dev {
|
|
registry.eth_api().with_dev_accounts();
|
|
}
|
|
|
|
let mut registry = RpcRegistry { registry };
|
|
let ctx = RpcContext {
|
|
node: node.clone(),
|
|
config,
|
|
registry: &mut registry,
|
|
modules: &mut modules,
|
|
auth_module: &mut auth_module,
|
|
};
|
|
|
|
let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
|
|
|
|
ext(RpcModuleContainer {
|
|
modules: ctx.modules,
|
|
auth_module: ctx.auth_module,
|
|
registry: ctx.registry,
|
|
})?;
|
|
extend_rpc_modules.extend_rpc_modules(ctx)?;
|
|
|
|
Ok(RpcSetupContext {
|
|
node,
|
|
config,
|
|
modules,
|
|
auth_module,
|
|
auth_config,
|
|
registry,
|
|
on_rpc_started,
|
|
engine_events,
|
|
engine_handle: beacon_engine_handle,
|
|
})
|
|
}
|
|
|
|
/// Helper to launch the RPC server
|
|
async fn launch_rpc_server_internal(
|
|
server_config: RpcServerConfig,
|
|
modules: &TransportRpcModules,
|
|
) -> eyre::Result<RpcServerHandle> {
|
|
let handle = server_config.start(modules).await?;
|
|
|
|
if let Some(path) = handle.ipc_endpoint() {
|
|
info!(target: "reth::cli", %path, "RPC IPC server started");
|
|
}
|
|
if let Some(addr) = handle.http_local_addr() {
|
|
info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
|
|
}
|
|
if let Some(addr) = handle.ws_local_addr() {
|
|
info!(target: "reth::cli", url=%addr, "RPC WS server started");
|
|
}
|
|
|
|
Ok(handle)
|
|
}
|
|
|
|
/// Helper to launch the auth server
|
|
async fn launch_auth_server_internal(
|
|
auth_module: AuthRpcModule,
|
|
auth_config: reth_rpc_builder::auth::AuthServerConfig,
|
|
) -> eyre::Result<AuthServerHandle> {
|
|
auth_module.start_server(auth_config)
|
|
.await
|
|
.map_err(Into::into)
|
|
.inspect(|handle| {
|
|
let addr = handle.local_addr();
|
|
if let Some(ipc_endpoint) = handle.ipc_endpoint() {
|
|
info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint, "RPC auth server started");
|
|
} else {
|
|
info!(target: "reth::cli", url=%addr, "RPC auth server started");
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Helper to finalize RPC setup by creating context and calling hooks
|
|
fn finalize_rpc_setup(
|
|
registry: &mut RpcRegistry<N, EthB::EthApi>,
|
|
modules: &mut TransportRpcModules,
|
|
auth_module: &mut AuthRpcModule,
|
|
node: &N,
|
|
config: &NodeConfig<<N::Types as NodeTypes>::ChainSpec>,
|
|
on_rpc_started: Box<dyn OnRpcStarted<N, EthB::EthApi>>,
|
|
handles: RethRpcServerHandles,
|
|
) -> eyre::Result<()> {
|
|
let ctx = RpcContext { node: node.clone(), config, registry, modules, auth_module };
|
|
|
|
on_rpc_started.on_rpc_started(ctx, handles)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<N, EthB, EV, EB> NodeAddOns<N> for RpcAddOns<N, EthB, EV, EB>
|
|
where
|
|
N: FullNodeComponents,
|
|
<N as FullNodeTypes>::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
|
|
EthB: EthApiBuilder<N>,
|
|
EV: EngineValidatorBuilder<N>,
|
|
EB: EngineApiBuilder<N>,
|
|
{
|
|
type Handle = RpcHandle<N, EthB::EthApi>;
|
|
|
|
async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
|
|
self.launch_add_ons_with(ctx, |_| Ok(())).await
|
|
}
|
|
}
|
|
|
|
/// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher
|
|
/// implementations.
|
|
pub trait RethRpcAddOns<N: FullNodeComponents>:
|
|
NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
|
|
{
|
|
/// eth API implementation.
|
|
type EthApi: EthApiTypes;
|
|
|
|
/// Returns a mutable reference to RPC hooks.
|
|
fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi>;
|
|
}
|
|
|
|
impl<N: FullNodeComponents, EthB, EV, EB> RethRpcAddOns<N> for RpcAddOns<N, EthB, EV, EB>
|
|
where
|
|
Self: NodeAddOns<N, Handle = RpcHandle<N, EthB::EthApi>>,
|
|
EthB: EthApiBuilder<N>,
|
|
{
|
|
type EthApi = EthB::EthApi;
|
|
|
|
fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
|
|
&mut self.hooks
|
|
}
|
|
}
|
|
|
|
/// `EthApiCtx` struct
|
|
/// This struct is used to pass the necessary context to the `EthApiBuilder` to build the `EthApi`.
|
|
#[derive(Debug)]
|
|
pub struct EthApiCtx<'a, N: FullNodeTypes> {
|
|
/// Reference to the node components
|
|
pub components: &'a N,
|
|
/// Eth API configuration
|
|
pub config: EthConfig,
|
|
/// Cache for eth state
|
|
pub cache: EthStateCache<BlockTy<N::Types>, ReceiptTy<N::Types>>,
|
|
}
|
|
|
|
/// A `EthApi` that knows how to build `eth` namespace API from [`FullNodeComponents`].
|
|
pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
|
|
/// The Ethapi implementation this builder will build.
|
|
type EthApi: EthApiTypes
|
|
+ FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
|
|
+ AddDevSigners
|
|
+ Unpin
|
|
+ 'static;
|
|
|
|
/// Builds the [`EthApiServer`](reth_rpc_api::eth::EthApiServer) from the given context.
|
|
fn build_eth_api(
|
|
self,
|
|
ctx: EthApiCtx<'_, N>,
|
|
) -> impl Future<Output = eyre::Result<Self::EthApi>> + Send;
|
|
}
|
|
|
|
/// Helper trait that provides the validator for the engine API
|
|
pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
|
|
/// The Validator type to use for the engine API.
|
|
type Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
|
|
+ Clone;
|
|
|
|
/// Creates the engine validator for an engine API based node.
|
|
fn engine_validator(
|
|
&self,
|
|
ctx: &AddOnsContext<'_, Node>,
|
|
) -> impl Future<Output = eyre::Result<Self::Validator>>;
|
|
}
|
|
|
|
impl<N, EthB, EV, EB> EngineValidatorAddOn<N> for RpcAddOns<N, EthB, EV, EB>
|
|
where
|
|
N: FullNodeComponents,
|
|
EthB: EthApiBuilder<N>,
|
|
EV: EngineValidatorBuilder<N>,
|
|
EB: EngineApiBuilder<N>,
|
|
{
|
|
type Validator = EV::Validator;
|
|
|
|
async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
|
|
self.engine_validator_builder.clone().build(ctx).await
|
|
}
|
|
}
|
|
|
|
/// A type that knows how to build the engine validator.
|
|
pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
|
|
/// The consensus implementation to build.
|
|
type Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
|
|
+ Clone;
|
|
|
|
/// Creates the engine validator.
|
|
fn build(
|
|
self,
|
|
ctx: &AddOnsContext<'_, Node>,
|
|
) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
|
|
}
|
|
|
|
impl<Node, F, Fut, Validator> EngineValidatorBuilder<Node> for F
|
|
where
|
|
Node: FullNodeComponents,
|
|
Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
|
|
+ Clone
|
|
+ Unpin
|
|
+ 'static,
|
|
F: FnOnce(&AddOnsContext<'_, Node>) -> Fut + Send + Sync + Clone,
|
|
Fut: Future<Output = eyre::Result<Validator>> + Send,
|
|
{
|
|
type Validator = Validator;
|
|
|
|
fn build(
|
|
self,
|
|
ctx: &AddOnsContext<'_, Node>,
|
|
) -> impl Future<Output = eyre::Result<Self::Validator>> {
|
|
self(ctx)
|
|
}
|
|
}
|
|
|
|
/// Builder for engine API RPC module.
|
|
///
|
|
/// This builder type is responsible for providing an instance of [`IntoEngineApiRpcModule`], which
|
|
/// is effectively a helper trait that provides the type erased [`jsonrpsee::RpcModule`] instance
|
|
/// that contains the method handlers for the engine API. See [`EngineApi`] for an implementation of
|
|
/// [`IntoEngineApiRpcModule`].
|
|
pub trait EngineApiBuilder<Node: FullNodeComponents>: Send + Sync {
|
|
/// The engine API RPC module. Only required to be convertible to an [`jsonrpsee::RpcModule`].
|
|
type EngineApi: IntoEngineApiRpcModule + Send + Sync;
|
|
|
|
/// Builds the engine API instance given the provided [`AddOnsContext`].
|
|
///
|
|
/// [`Self::EngineApi`] will be converted into the method handlers of the authenticated RPC
|
|
/// server (engine API).
|
|
fn build_engine_api(
|
|
self,
|
|
ctx: &AddOnsContext<'_, Node>,
|
|
) -> impl Future<Output = eyre::Result<Self::EngineApi>> + Send;
|
|
}
|
|
|
|
/// Builder for basic [`EngineApi`] implementation.
|
|
///
|
|
/// This provides a basic default implementation for opstack and ethereum engine API via
|
|
/// [`EngineTypes`] and uses the general purpose [`EngineApi`] implementation as the builder's
|
|
/// output.
|
|
#[derive(Debug, Default)]
|
|
pub struct BasicEngineApiBuilder<EV> {
|
|
engine_validator_builder: EV,
|
|
}
|
|
|
|
impl<N, EV> EngineApiBuilder<N> for BasicEngineApiBuilder<EV>
|
|
where
|
|
N: FullNodeComponents<
|
|
Types: NodeTypes<
|
|
ChainSpec: EthereumHardforks,
|
|
Payload: PayloadTypes<ExecutionData = ExecutionData> + EngineTypes,
|
|
>,
|
|
>,
|
|
EV: EngineValidatorBuilder<N>,
|
|
{
|
|
type EngineApi = EngineApi<
|
|
N::Provider,
|
|
<N::Types as NodeTypes>::Payload,
|
|
N::Pool,
|
|
EV::Validator,
|
|
<N::Types as NodeTypes>::ChainSpec,
|
|
>;
|
|
|
|
async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
|
|
let Self { engine_validator_builder } = self;
|
|
|
|
let engine_validator = engine_validator_builder.build(ctx).await?;
|
|
let client = ClientVersionV1 {
|
|
code: CLIENT_CODE,
|
|
name: NAME_CLIENT.to_string(),
|
|
version: CARGO_PKG_VERSION.to_string(),
|
|
commit: VERGEN_GIT_SHA.to_string(),
|
|
};
|
|
Ok(EngineApi::new(
|
|
ctx.node.provider().clone(),
|
|
ctx.config.chain.clone(),
|
|
ctx.beacon_engine_handle.clone(),
|
|
PayloadStore::new(ctx.node.payload_builder_handle().clone()),
|
|
ctx.node.pool().clone(),
|
|
Box::new(ctx.node.task_executor().clone()),
|
|
client,
|
|
EngineCapabilities::default(),
|
|
engine_validator,
|
|
ctx.config.engine.accept_execution_requests_hash,
|
|
))
|
|
}
|
|
}
|
|
|
|
/// A noop Builder that satisfies the [`EngineApiBuilder`] trait without actually configuring an
|
|
/// engine API module
|
|
///
|
|
/// This is intended to be used as a workaround for reusing all the existing ethereum node launch
|
|
/// utilities which require an engine API.
|
|
#[derive(Debug, Clone, Default)]
|
|
#[non_exhaustive]
|
|
pub struct NoopEngineApiBuilder;
|
|
|
|
impl<N: FullNodeComponents> EngineApiBuilder<N> for NoopEngineApiBuilder {
|
|
type EngineApi = NoopEngineApi;
|
|
|
|
async fn build_engine_api(self, _ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
|
|
Ok(NoopEngineApi::default())
|
|
}
|
|
}
|
|
|
|
/// Represents an empty Engine API [`RpcModule`].
|
|
///
|
|
/// This is only intended to be used in combination with the [`NoopEngineApiBuilder`] in order to
|
|
/// satisfy trait bounds in the regular ethereum launch routine that mandate an engine API instance.
|
|
#[derive(Debug, Clone, Default)]
|
|
#[non_exhaustive]
|
|
pub struct NoopEngineApi;
|
|
|
|
impl IntoEngineApiRpcModule for NoopEngineApi {
|
|
fn into_rpc_module(self) -> RpcModule<()> {
|
|
RpcModule::new(())
|
|
}
|
|
}
|