reth_optimism_node/rpc.rs
1//! RPC component builder
2//!
3//! # Example
4//!
5//! Builds offline `TraceApi` with only EVM and database. This can be useful
6//! for example when downloading a state snapshot (pre-synced node) from some mirror.
7//!
8//! ```rust
9//! use alloy_rpc_types_eth::BlockId;
10//! use op_alloy_network::Optimism;
11//! use reth_db::test_utils::create_test_rw_db_with_path;
12//! use reth_node_builder::{
13//! components::ComponentsBuilder,
14//! hooks::OnComponentInitializedHook,
15//! rpc::{EthApiBuilder, EthApiCtx},
16//! LaunchContext, NodeConfig, RethFullAdapter,
17//! };
18//! use reth_optimism_chainspec::OP_SEPOLIA;
19//! use reth_optimism_evm::OpEvmConfig;
20//! use reth_optimism_node::{OpExecutorBuilder, OpNetworkPrimitives, OpNode};
21//! use reth_optimism_rpc::OpEthApiBuilder;
22//! use reth_optimism_txpool::OpPooledTransaction;
23//! use reth_provider::providers::BlockchainProvider;
24//! use reth_rpc::TraceApi;
25//! use reth_rpc_eth_types::{EthConfig, EthStateCache};
26//! use reth_tasks::{pool::BlockingTaskGuard, TaskManager};
27//! use std::sync::Arc;
28//!
29//! #[tokio::main]
30//! async fn main() {
31//! // build core node with all components disabled except EVM and state
32//! let sepolia = NodeConfig::new(OP_SEPOLIA.clone());
33//! let db = create_test_rw_db_with_path(sepolia.datadir());
34//! let tasks = TaskManager::current();
35//! let launch_ctx = LaunchContext::new(tasks.executor(), sepolia.datadir());
36//! let node = launch_ctx
37//! .with_loaded_toml_config(sepolia)
38//! .unwrap()
39//! .attach(Arc::new(db))
40//! .with_provider_factory::<_, OpEvmConfig>()
41//! .await
42//! .unwrap()
43//! .with_genesis()
44//! .unwrap()
45//! .with_metrics_task() // todo: shouldn't be req to set up blockchain db
46//! .with_blockchain_db::<RethFullAdapter<_, OpNode>, _>(move |provider_factory| {
47//! Ok(BlockchainProvider::new(provider_factory).unwrap())
48//! })
49//! .unwrap()
50//! .with_components(
51//! ComponentsBuilder::default()
52//! .node_types::<RethFullAdapter<_, OpNode>>()
53//! .noop_pool::<OpPooledTransaction>()
54//! .noop_network::<OpNetworkPrimitives>()
55//! .noop_consensus()
56//! .executor(OpExecutorBuilder::default())
57//! .noop_payload(),
58//! Box::new(()) as Box<dyn OnComponentInitializedHook<_>>,
59//! )
60//! .await
61//! .unwrap();
62//!
63//! // build `eth` namespace API
64//! let config = EthConfig::default();
65//! let cache = EthStateCache::spawn_with(
66//! node.provider_factory().clone(),
67//! config.cache,
68//! node.task_executor().clone(),
69//! );
70//! let ctx = EthApiCtx { components: node.node_adapter(), config, cache };
71//! let eth_api = OpEthApiBuilder::<Optimism>::default().build_eth_api(ctx).await.unwrap();
72//!
73//! // build `trace` namespace API
74//! let trace_api = TraceApi::new(eth_api, BlockingTaskGuard::new(10), EthConfig::default());
75//!
76//! // fetch traces for latest block
77//! let traces = trace_api.trace_block(BlockId::latest()).await.unwrap();
78//! }
79//! ```
80
81pub use reth_optimism_rpc::{OpEngineApi, OpEthApi, OpEthApiBuilder};
82
83use crate::OP_NAME_CLIENT;
84use alloy_rpc_types_engine::ClientVersionV1;
85use op_alloy_rpc_types_engine::OpExecutionData;
86use reth_chainspec::EthereumHardforks;
87use reth_node_api::{
88 AddOnsContext, EngineApiValidator, EngineTypes, FullNodeComponents, NodeTypes,
89};
90use reth_node_builder::rpc::{EngineApiBuilder, PayloadValidatorBuilder};
91use reth_node_core::version::{version_metadata, CLIENT_CODE};
92use reth_optimism_rpc::engine::OP_ENGINE_CAPABILITIES;
93use reth_payload_builder::PayloadStore;
94use reth_rpc_engine_api::{EngineApi, EngineCapabilities};
95
96/// Builder for basic [`OpEngineApi`] implementation.
97#[derive(Debug, Default, Clone)]
98pub struct OpEngineApiBuilder<EV> {
99 engine_validator_builder: EV,
100}
101
102impl<N, EV> EngineApiBuilder<N> for OpEngineApiBuilder<EV>
103where
104 N: FullNodeComponents<
105 Types: NodeTypes<
106 ChainSpec: EthereumHardforks,
107 Payload: EngineTypes<ExecutionData = OpExecutionData>,
108 >,
109 >,
110 EV: PayloadValidatorBuilder<N>,
111 EV::Validator: EngineApiValidator<<N::Types as NodeTypes>::Payload>,
112{
113 type EngineApi = OpEngineApi<
114 N::Provider,
115 <N::Types as NodeTypes>::Payload,
116 N::Pool,
117 EV::Validator,
118 <N::Types as NodeTypes>::ChainSpec,
119 >;
120
121 async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
122 let Self { engine_validator_builder } = self;
123
124 let engine_validator = engine_validator_builder.build(ctx).await?;
125 let client = ClientVersionV1 {
126 code: CLIENT_CODE,
127 name: OP_NAME_CLIENT.to_string(),
128 version: version_metadata().cargo_pkg_version.to_string(),
129 commit: version_metadata().vergen_git_sha.to_string(),
130 };
131 let inner = EngineApi::new(
132 ctx.node.provider().clone(),
133 ctx.config.chain.clone(),
134 ctx.beacon_engine_handle.clone(),
135 PayloadStore::new(ctx.node.payload_builder_handle().clone()),
136 ctx.node.pool().clone(),
137 Box::new(ctx.node.task_executor().clone()),
138 client,
139 EngineCapabilities::new(OP_ENGINE_CAPABILITIES.iter().copied()),
140 engine_validator,
141 ctx.config.engine.accept_execution_requests_hash,
142 );
143
144 Ok(OpEngineApi::new(inner))
145 }
146}