Skip to content

Commit 0425227

Browse files
authored
[SEQ-115] feat: Add skeleton of metabased sequencer (#1)
* chore: cargo init * chore: Install alloy * wip: JSON RPC skeleton * chore: Add support for eth_sendRawTransaction * chore: Reject eth_sendTransaction * chore: Add JSON-RPC import * chore: Switch to jsonrpsee from deprecated jsonrpc package * feat: Switch to jsonrpsee middleware example * fix: Fix jsonrpsee install * chore: Import serde * chore: Add remaining imports and fix build * chore: Remove build warnings for unused import Also switch to more modern way to handle tracing_subscriber formatting * chore: Add GitHub Action to run cargo * chore: Update checkout version and toolchain override * chore: Update GitHub Action with rust-toolchain * chore: Add GitHub Action for Clippy Check * chore: Update pull request logic for GH Action * chore: Remove redundant import * chore: Update clippy check Uses https://doc.rust-lang.org/stable/clippy/continuous_integration/github_actions.html instead of the deprecated GitHub Action * docs: Update TODOs for next steps * test: Intentionally re-add redundant import to test Clippy Check failure * Revert "test: Intentionally re-add redundant import to test Clippy Check failure" This reverts commit 75653f8ca3a68bbebbe2390ede0796a407fe1220.
1 parent a4926f1 commit 0425227

File tree

6 files changed

+157
-1
lines changed

6 files changed

+157
-1
lines changed

.github/workflows/rust-clippy.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Rust Clippy Check
2+
3+
on:
4+
pull_request:
5+
branches: [main] # Adjust this if your main branch has a different name
6+
7+
env:
8+
CARGO_TERM_COLOR: always
9+
# Make sure CI fails on all warnings, including Clippy lints
10+
RUSTFLAGS: "-Dwarnings"
11+
12+
jobs:
13+
clippy_check:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- run: rustup component add clippy
19+
20+
- name: Cache Cargo dependencies
21+
uses: Swatinem/rust-cache@v2
22+
23+
- name: Run Clippy
24+
run: cargo clippy --all-targets --all-features

.github/workflows/rust-tests.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Rust Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main] # Adjust this if your main branch has a different name
6+
7+
env:
8+
CARGO_TERM_COLOR: always
9+
10+
jobs:
11+
test:
12+
name: Run Rust Tests
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
# GitHub Actions supports rustup by default, so we can install the toolchain directly
19+
# selecting a toolchain either by action or manual `rustup` calls should happen
20+
# before the plugin, as the cache uses the current rustc version as its cache key
21+
- run: rustup toolchain install stable --profile minimal
22+
23+
- name: Cache Cargo dependencies
24+
uses: Swatinem/rust-cache@v2
25+
26+
- name: Run tests
27+
run: cargo test --all

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ Cargo.lock
1818
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
1919
# and can be added to the global gitignore or merged into this file. For a more nuclear
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21-
#.idea/
21+
#.idea/
22+
23+
# Added by cargo
24+
25+
/target

Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "metabased-sequencer"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
alloy = { version = "0.3.1", features = ["full"] }
8+
# TODO: Opt into specific features
9+
jsonrpsee = { version = "0.24.3", features = ["full"] }
10+
tokio = { version = "1.0", features = ["full"] }
11+
anyhow = "1.0"
12+
serde_json = "1.0.128"
13+
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

rust-toolchain.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[toolchain]
2+
channel = "stable"
3+
components = ["rustfmt", "clippy"]
4+
targets = [
5+
"aarch64-apple-darwin", # For M1 Mac
6+
"x86_64-unknown-linux-gnu" # For Linux server
7+
]
8+
profile = "default"

src/main.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Based on https://github.com/paritytech/jsonrpsee/blob/master/examples/examples/rpc_middleware_modify_request.rs
2+
3+
use jsonrpsee::core::client::ClientT;
4+
use jsonrpsee::server::middleware::rpc::{RpcServiceBuilder, RpcServiceT};
5+
use jsonrpsee::server::Server;
6+
use jsonrpsee::types::Request;
7+
use jsonrpsee::ws_client::WsClientBuilder;
8+
use jsonrpsee::{rpc_params, RpcModule};
9+
use std::borrow::Cow as StdCow;
10+
use std::net::SocketAddr;
11+
use tracing_subscriber::EnvFilter;
12+
13+
#[derive(Clone)]
14+
pub struct ModifyRequestIf<S>(S);
15+
16+
impl<'a, S> RpcServiceT<'a> for ModifyRequestIf<S>
17+
where
18+
S: Send + Sync + RpcServiceT<'a>,
19+
{
20+
type Future = S::Future;
21+
22+
fn call(&self, mut req: Request<'a>) -> Self::Future {
23+
// Example how to modify the params in the call.
24+
// TODO: Change this to intercept eth_sendRawTransaction
25+
if req.method == "say_hello" {
26+
// It's a bit awkward to create new params in the request
27+
// but this shows how to do it.
28+
let raw_value = serde_json::value::to_raw_value("myparams").unwrap();
29+
req.params = Some(StdCow::Owned(raw_value));
30+
}
31+
// Re-direct all calls that isn't `say_hello` to `say_goodbye`
32+
// TODO: Change this to redirect all calls to the underlying RPC
33+
// (optionally, via an env variable if you want to forward)
34+
// If the node operator does not want to forward
35+
// non-eth_sendRawTransaction calls, then reject all other calls
36+
else if req.method != "say_hello" {
37+
req.method = "say_goodbye".into();
38+
}
39+
40+
self.0.call(req)
41+
}
42+
}
43+
44+
#[tokio::main]
45+
async fn main() -> anyhow::Result<()> {
46+
tracing_subscriber::fmt()
47+
.with_env_filter(EnvFilter::from_default_env())
48+
.try_init()
49+
.expect("setting default subscriber failed");
50+
51+
let addr = run_server().await?;
52+
let url = format!("ws://{}", addr);
53+
54+
let client = WsClientBuilder::default().build(&url).await?;
55+
let _response: String = client.request("say_hello", rpc_params![]).await?;
56+
let _response: Result<String, _> = client.request("unknown_method", rpc_params![]).await;
57+
let _: String = client.request("say_hello", rpc_params![]).await?;
58+
59+
Ok(())
60+
}
61+
62+
async fn run_server() -> anyhow::Result<SocketAddr> {
63+
let rpc_middleware = RpcServiceBuilder::new().layer_fn(ModifyRequestIf);
64+
let server = Server::builder()
65+
.set_rpc_middleware(rpc_middleware)
66+
.build("127.0.0.1:0")
67+
.await?;
68+
let mut module = RpcModule::new(());
69+
module.register_method("say_hello", |_, _, _| "lo")?;
70+
module.register_method("say_goodbye", |_, _, _| "goodbye")?;
71+
let addr = server.local_addr()?;
72+
73+
let handle = server.start(module);
74+
75+
// In this example we don't care about doing shutdown so let's it run forever.
76+
// You may use the `ServerHandle` to shut it down or manage it yourself.
77+
tokio::spawn(handle.stopped());
78+
79+
Ok(addr)
80+
}

0 commit comments

Comments
 (0)