Skip to content

Commit ea7eaf6

Browse files
feat: enable external EngineApi access (#16248)
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
1 parent 5f745ed commit ea7eaf6

File tree

7 files changed

+154
-1
lines changed

7 files changed

+154
-1
lines changed

Cargo.lock

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ members = [
147147
"examples/custom-rlpx-subprotocol",
148148
"examples/custom-node",
149149
"examples/db-access",
150+
"examples/engine-api-access",
150151
"examples/exex-hello-world",
151152
"examples/exex-subscription",
152153
"examples/exex-test",
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! `EngineApiBuilder` callback wrapper
2+
//!
3+
//! Wraps an `EngineApiBuilder` to provide access to the built Engine API instance.
4+
5+
use crate::rpc::EngineApiBuilder;
6+
use eyre::Result;
7+
use reth_node_api::{AddOnsContext, FullNodeComponents};
8+
use reth_rpc_api::IntoEngineApiRpcModule;
9+
10+
/// Provides access to an `EngineApi` instance with a callback
11+
#[derive(Debug)]
12+
pub struct EngineApiExt<B, F> {
13+
/// The inner builder that constructs the actual `EngineApi`
14+
inner: B,
15+
/// Optional callback function to execute with the built API
16+
callback: Option<F>,
17+
}
18+
19+
impl<B, F> EngineApiExt<B, F> {
20+
/// Creates a new wrapper that calls `callback` when the API is built.
21+
pub const fn new(inner: B, callback: F) -> Self {
22+
Self { inner, callback: Some(callback) }
23+
}
24+
}
25+
26+
impl<N, B, F> EngineApiBuilder<N> for EngineApiExt<B, F>
27+
where
28+
B: EngineApiBuilder<N>,
29+
N: FullNodeComponents,
30+
B::EngineApi: IntoEngineApiRpcModule + Send + Sync + Clone + 'static,
31+
F: FnOnce(B::EngineApi) + Send + Sync + 'static,
32+
{
33+
type EngineApi = B::EngineApi;
34+
35+
/// Builds the `EngineApi` and executes the callback if present.
36+
async fn build_engine_api(mut self, ctx: &AddOnsContext<'_, N>) -> Result<Self::EngineApi> {
37+
let api = self.inner.build_engine_api(ctx).await?;
38+
39+
if let Some(callback) = self.callback.take() {
40+
callback(api.clone());
41+
}
42+
43+
Ok(api)
44+
}
45+
}

crates/node/builder/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ pub mod hooks;
1818
pub mod node;
1919
pub use node::*;
2020

21+
/// Support for accessing the EngineApi outside the RPC server context.
22+
mod engine_api_ext;
23+
pub use engine_api_ext::EngineApiExt;
24+
2125
/// Support for configuring the components of a node.
2226
pub mod components;
2327
pub use components::{NodeComponents, NodeComponentsBuilder};

crates/optimism/node/src/rpc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use reth_payload_builder::PayloadStore;
1414
use reth_rpc_engine_api::{EngineApi, EngineCapabilities};
1515

1616
/// Builder for basic [`OpEngineApi`] implementation.
17-
#[derive(Debug, Default)]
17+
#[derive(Debug, Default, Clone)]
1818
pub struct OpEngineApiBuilder<EV> {
1919
engine_validator_builder: EV,
2020
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "example-engine-api-access"
3+
version = "0.0.0"
4+
publish = false
5+
edition.workspace = true
6+
license.workspace = true
7+
8+
[dependencies]
9+
# reth
10+
reth-db = { workspace = true, features = ["op", "test-utils"] }
11+
reth-node-builder.workspace = true
12+
reth-optimism-consensus.workspace = true
13+
reth-tasks.workspace = true
14+
reth-node-api.workspace = true
15+
reth-rpc-api.workspace = true
16+
reth-tracing.workspace = true
17+
reth-provider.workspace = true
18+
reth-optimism-node.workspace = true
19+
reth-optimism-chainspec.workspace = true
20+
21+
# alloy
22+
alloy-rpc-types-engine.workspace = true
23+
24+
async-trait.workspace = true
25+
clap = { workspace = true, features = ["derive"] }
26+
eyre.workspace = true
27+
jsonrpsee.workspace = true
28+
futures.workspace = true
29+
serde_json.workspace = true
30+
tokio = { workspace = true, features = ["sync"] }
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//! Example demonstrating how to access the Engine API instance during construction.
2+
//!
3+
//! Run with
4+
//!
5+
//! ```sh
6+
//! cargo run -p example-engine-api-access
7+
//! ```
8+
9+
use reth_db::test_utils::create_test_rw_db;
10+
use reth_node_builder::{EngineApiExt, FullNodeComponents, NodeBuilder, NodeConfig};
11+
use reth_optimism_chainspec::BASE_MAINNET;
12+
use reth_optimism_node::{
13+
args::RollupArgs,
14+
node::{OpAddOns, OpEngineValidatorBuilder},
15+
OpEngineApiBuilder, OpNode,
16+
};
17+
use tokio::sync::oneshot;
18+
19+
#[tokio::main]
20+
async fn main() {
21+
// Op node configuration and setup
22+
let config = NodeConfig::new(BASE_MAINNET.clone());
23+
let db = create_test_rw_db();
24+
let args = RollupArgs::default();
25+
let op_node = OpNode::new(args);
26+
27+
let (engine_api_tx, _engine_api_rx) = oneshot::channel();
28+
29+
let engine_api =
30+
EngineApiExt::new(OpEngineApiBuilder::<OpEngineValidatorBuilder>::default(), move |api| {
31+
let _ = engine_api_tx.send(api);
32+
});
33+
34+
let _builder = NodeBuilder::new(config)
35+
.with_database(db)
36+
.with_types::<OpNode>()
37+
.with_components(op_node.components())
38+
.with_add_ons(OpAddOns::default().with_engine_api(engine_api))
39+
.on_component_initialized(move |ctx| {
40+
let _provider = ctx.provider();
41+
Ok(())
42+
})
43+
.on_node_started(|_full_node| Ok(()))
44+
.on_rpc_started(|_ctx, handles| {
45+
let _client = handles.rpc.http_client();
46+
Ok(())
47+
})
48+
.check_launch();
49+
}

0 commit comments

Comments
 (0)