Skip to content

Commit 5318965

Browse files
committed
add example code for launching node with custom rpc middleware
1 parent 972acb9 commit 5318965

File tree

4 files changed

+144
-0
lines changed

4 files changed

+144
-0
lines changed

Cargo.lock

Lines changed: 11 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
@@ -153,6 +153,7 @@ members = [
153153
"examples/custom-node-components/",
154154
"examples/custom-payload-builder/",
155155
"examples/custom-rlpx-subprotocol",
156+
"examples/custom-rpc-middleware",
156157
"examples/custom-node",
157158
"examples/db-access",
158159
"examples/engine-api-access",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "example-custom-rpc-middleware"
3+
version = "0.0.0"
4+
publish = false
5+
edition.workspace = true
6+
license.workspace = true
7+
8+
[dependencies]
9+
reth-ethereum = { workspace = true, features = ["node", "pool", "rpc", "cli"] }
10+
11+
clap = { workspace = true, features = ["derive"] }
12+
jsonrpsee = { workspace = true, features = ["server", "macros"] }
13+
tracing.workspace = true
14+
tower.workspace = true
15+
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! Example of how to create a node with custom middleware that alters a returned error message from
2+
//! the RPC
3+
//!
4+
//! Run with
5+
//!
6+
//! ```sh
7+
//! cargo run -p example-custom-rpc-middleware node --http --dev --dev.block-time 12s --http.api=debug,eth
8+
//! ```
9+
//!
10+
//! Then make an RPC request that will result in an error
11+
//!
12+
//! ```sh
13+
//! curl -s -X POST http://localhost:8545 \
14+
//! -H "Content-Type: application/json" \
15+
//! -d '{
16+
//! "jsonrpc": "2.0",
17+
//! "method": "debug_getRawBlock",
18+
//! "params": ["2"],
19+
//! "id": 1
20+
//! }' | jq
21+
//! ```
22+
23+
use clap::Parser;
24+
use jsonrpsee::{
25+
core::{
26+
middleware::{Batch, Notification, RpcServiceT},
27+
server::MethodResponse,
28+
},
29+
types::{ErrorObjectOwned, Id, Request},
30+
};
31+
use reth_ethereum::{
32+
cli::{chainspec::EthereumChainSpecParser, interface::Cli},
33+
node::{EthereumAddOns, EthereumNode},
34+
};
35+
use tower::Layer;
36+
37+
fn main() {
38+
Cli::<EthereumChainSpecParser>::parse()
39+
.run(|builder, _| async move {
40+
let handle = builder
41+
.with_types::<EthereumNode>()
42+
.with_components(EthereumNode::components())
43+
.with_add_ons(
44+
//create ethereum addons with our custom rpc middleware
45+
EthereumAddOns::default().with_rpc_middleware(ResponseMutationLayer::default()),
46+
)
47+
.launch_with_debug_capabilities()
48+
.await?;
49+
50+
handle.wait_for_node_exit().await
51+
})
52+
.unwrap();
53+
}
54+
55+
#[derive(Clone, Default)]
56+
pub struct ResponseMutationLayer;
57+
58+
impl<S> Layer<S> for ResponseMutationLayer {
59+
type Service = ResponseMutationService<S>;
60+
61+
fn layer(&self, inner: S) -> Self::Service {
62+
ResponseMutationService { service: inner }
63+
}
64+
}
65+
66+
#[derive(Clone)]
67+
pub struct ResponseMutationService<S> {
68+
service: S,
69+
}
70+
71+
impl<S> RpcServiceT for ResponseMutationService<S>
72+
where
73+
S: RpcServiceT<
74+
MethodResponse = jsonrpsee::MethodResponse,
75+
BatchResponse = jsonrpsee::MethodResponse,
76+
NotificationResponse = jsonrpsee::MethodResponse,
77+
> + Send
78+
+ Sync
79+
+ Clone
80+
+ 'static,
81+
{
82+
type MethodResponse = S::MethodResponse;
83+
type NotificationResponse = S::NotificationResponse;
84+
type BatchResponse = S::BatchResponse;
85+
86+
fn call<'a>(&self, req: Request<'a>) -> impl Future<Output = Self::MethodResponse> + Send + 'a {
87+
tracing::info!("processed call {:?}", req);
88+
let service = self.service.clone();
89+
Box::pin(async move {
90+
let resp = service.call(req).await;
91+
92+
//we can modify the response with our own custom error
93+
if resp.is_error() {
94+
let err = ErrorObjectOwned::owned(
95+
-31404,
96+
"CustomError",
97+
Some("Our very own custom error message"),
98+
);
99+
return MethodResponse::error(Id::Number(1), err);
100+
}
101+
102+
//otherwise just return the original response
103+
resp
104+
})
105+
}
106+
107+
fn batch<'a>(&self, req: Batch<'a>) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
108+
self.service.batch(req)
109+
}
110+
111+
fn notification<'a>(
112+
&self,
113+
n: Notification<'a>,
114+
) -> impl Future<Output = Self::NotificationResponse> + Send + 'a {
115+
self.service.notification(n)
116+
}
117+
}

0 commit comments

Comments
 (0)