-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit to introduce the json rpc proxy-server
- Loading branch information
1 parent
06c46d4
commit 3f04d5a
Showing
18 changed files
with
3,567 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "jito-block-engine-proxy-server" | ||
version = "0.1.0" | ||
authors = ["Jito Team <[email protected]>"] | ||
edition = "2021" | ||
description = "A sample proxy server to sign and forward json rpc requests" | ||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
axum = "0.5.17" | ||
clap = { version = "4", features = ["cargo", "derive", "env"] } | ||
futures-util = "0.3.29" | ||
http = "0.2.11" | ||
hyper = { version = "0.14", features = ["full"] } | ||
reqwest = { version = "0.11", features = ["json", "stream"] } | ||
serde = { version = "1.0.189", features = ["derive"] } | ||
serde_json = "1.0.107" | ||
solana-sdk = "=1.16" | ||
tokio = { version = "1.14.1", features = ["full"] } | ||
|
||
[workspace] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# syntax=docker/dockerfile:1.4.0 | ||
FROM rust:1.73-slim-bullseye as builder | ||
|
||
RUN set -x \ | ||
&& apt-get -qq update \ | ||
&& apt-get -qq -y install \ | ||
clang \ | ||
cmake \ | ||
libudev-dev \ | ||
unzip \ | ||
libssl-dev \ | ||
pkg-config \ | ||
zlib1g-dev \ | ||
curl | ||
|
||
RUN rustup component add rustfmt && update-ca-certificates | ||
|
||
ENV HOME=/home/root | ||
WORKDIR $HOME/app | ||
COPY . . | ||
|
||
# cache these directories for reuse | ||
# see: https://docs.docker.com/build/cache/#use-the-dedicated-run-cache | ||
RUN --mount=type=cache,mode=0777,target=/home/root/app/target \ | ||
--mount=type=cache,mode=0777,target=/usr/local/cargo/registry \ | ||
--mount=type=cache,mode=0777,target=/usr/local/cargo/git \ | ||
RUSTFLAGS="-C target-cpu=native" cargo build --release && cp target/release/jito-* ./; | ||
|
||
FROM debian:bullseye-slim as base_image | ||
# read in build arg from ./b, default to false | ||
ARG debug=false | ||
|
||
RUN apt-get -qq update && apt-get -qq -y install ca-certificates libssl1.1 && rm -rf /var/lib/apt/lists/*; | ||
|
||
FROM base_image as proxy_server | ||
ENV APP="jito-block-engine-proxy-server" | ||
WORKDIR /app | ||
COPY --from=builder /home/root/app/${APP} ./ | ||
ENTRYPOINT ["/app/jito-block-engine-proxy-server"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Jito proxy server for json rpc | ||
|
||
The sample proxy server can be used to sign and send the payload to the Jito json rpc endpoint. | ||
|
||
Expected environment variables are set in the config/.env file | ||
|
||
The binary can be run by itself by providing the parameters | ||
--proxy-json-rpc-port <PROXY_JSON_RPC_PORT> | ||
--json-rpc-url <JSON_RPC_URL> | ||
--key-pair-path <KEY_PAIR_PATH> | ||
|
||
## Running as a container | ||
|
||
The server can be run as a container using the provided script, run_proxy_server.sh. | ||
|
||
```shell | ||
# starts the proxy server. | ||
# The script reads env values from config/.env. Modify as necessary | ||
./run_proxy_server | ||
``` | ||
|
||
## Stopping the container | ||
|
||
The container can be stopped and resources removed using, stop_proxy_server.sh. | ||
|
||
```shell | ||
# starts the proxy server. | ||
# The script reads env values from config/.env. Modify as necessary | ||
./stop_proxy_server | ||
``` | ||
|
||
## Running the binary | ||
|
||
The binary can be run by itself by providing the parameters | ||
--proxy-json-rpc-port <PROXY_JSON_RPC_PORT> | ||
--json-rpc-url <JSON_RPC_URL> | ||
--key-pair-path <KEY_PAIR_PATH> | ||
|
||
```shell | ||
# starts the proxy server. | ||
# You can also source the env variables and pass them as parameters as well | ||
cargo run --proxy-json-rpc-port 8080 --json-rpc-url url --key-pair-path | ||
``` | ||
|
||
## Use curl or similar to send request | ||
|
||
```shell | ||
curl 'http://127.0.0.1:8080/api/v1/bundles' -POST -d '{"jsonrpc": "2.0", "method": "getTipAccounts", "params": [], "id": 1}' -H 'Content-Type: application/json | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/bin/bash | ||
|
||
# Port at which the proxy server should be listening on | ||
export PROXY_JSON_RPC_PORT=8080 | ||
|
||
# JSON RPC url for the Jito bundle service | ||
export JSON_RPC_URL="http://searcher_service:1008/api/v1/bundles" | ||
|
||
# The keypair you want to use to sign the request. Please note, the pubkey should | ||
# be shared in advance with the Jito team. If you are a searcher and already have | ||
# added your pubkey with Jito, no need to share it again. | ||
export KEY_PAIR_PATH="./config/tppvXqEfck4Mchr3aZiceiGiRjMYzBSbYYZTtSRb8en.json" | ||
|
||
# This is any external network you want your container to connect to | ||
export DOCKER_NETWORK="block-engine_default" |
1 change: 1 addition & 0 deletions
1
proxy_server/config/tppvXqEfck4Mchr3aZiceiGiRjMYzBSbYYZTtSRb8en.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[60,14,105,58,167,30,16,112,37,125,39,0,112,16,13,160,159,149,41,192,120,133,121,74,51,221,90,60,253,193,113,249,13,70,178,214,120,17,107,70,76,187,71,17,102,61,110,231,58,78,191,13,90,173,27,193,229,109,159,122,79,82,165,187] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# See ./b for running instructions | ||
version: "3.8" | ||
|
||
services: | ||
# Proxy server | ||
proxy-server: | ||
image: $ORG/block-engine-proxy-server:$TAG | ||
container_name: proxy_server | ||
build: | ||
context: . | ||
dockerfile: Dockerfile | ||
target: proxy_server | ||
environment: | ||
- RUST_BACKTRACE=1 | ||
- PROXY_JSON_RPC_PORT=$PROXY_JSON_RPC_PORT | ||
- JSON_RPC_URL=$JSON_RPC_URL | ||
- KEY_PAIR_PATH=/etc/$KEY_PAIR_PATH | ||
expose: | ||
- "$PROXY_JSON_RPC_PORT" | ||
ports: | ||
- "$PROXY_JSON_RPC_PORT:$PROXY_JSON_RPC_PORT" | ||
restart: on-failure | ||
volumes: | ||
- "$KEY_PAIR_PATH:/etc/$KEY_PAIR_PATH" | ||
networks: | ||
proxy_server_network: {} | ||
|
||
networks: | ||
proxy_server_network: | ||
external: true | ||
name: $DOCKER_NETWORK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#!/usr/bin/env bash | ||
set -euxo pipefail | ||
|
||
ENV_FILE=./config/.env | ||
if [[ -f $ENV_FILE ]]; then | ||
# A little hacky, but .env files can't execute so this is the best we have for now | ||
# shellcheck disable=SC2046 | ||
export $(grep -v '#' "$ENV_FILE" | awk '/=/ {print $1}') | ||
else | ||
echo ".env file not found at ${ENV_FILE}" | ||
exit 1 | ||
fi | ||
|
||
# Some container vars | ||
TAG=$(git describe --match=NeVeRmAtCh --always --abbrev=0 --dirty) | ||
ORG="jitolabs" | ||
|
||
export DOCKER_LOCALHOST=172.17.0.1 | ||
if [[ $(uname) == "Darwin" ]]; then | ||
DOCKER_LOCALHOST=docker.for.mac.localhost | ||
fi | ||
|
||
# Build and run | ||
DOCKER_BUILDKIT=1 \ | ||
BUILDKIT_PROGRESS=plain \ | ||
TAG=$TAG \ | ||
ORG=$ORG \ | ||
docker compose --env-file="${ENV_FILE}" up --build --remove-orphans --renew-anon-volumes -d | ||
|
||
# Set a timeout (in seconds) | ||
timeout=300 | ||
start_time=$(date +%s) | ||
|
||
# Wait for the container to be up with timeout | ||
while [ "$(TAG=$TAG ORG=$ORG docker compose --env-file="${ENV_FILE}" ps -q | xargs docker inspect --format '{{.State.Status}}' | grep -c "running")" -eq 0 ]; do | ||
# Check if the timeout has been reached | ||
current_time=$(date +%s) | ||
elapsed_time=$((current_time - start_time)) | ||
if [ $elapsed_time -ge $timeout ]; then | ||
echo "Timeout reached. Container did not start within $timeout seconds." | ||
exit 1 | ||
fi | ||
|
||
# Sleep for a while before checking again | ||
sleep 5 | ||
done | ||
|
||
# Tag the image as latest | ||
IMAGE_NAME="$(TAG=$TAG ORG=$ORG docker compose --env-file="${ENV_FILE}" ps -q | xargs docker inspect --format '{{.Config.Image}}')" | ||
docker image tag "$IMAGE_NAME" "${IMAGE_NAME%%:*}":latest | ||
|
||
echo -e "\e[1;32mProxy server is up and running.\e[0m" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[toolchain] | ||
# Needs to be compiled with the same rust version that the RPC loading this plugin was compiled with. | ||
# https://github.com/solana-labs/solana/blob/v1.16.14/rust-toolchain.toml | ||
channel = "1.73.0" | ||
components = [ "rustfmt", "rustc-dev", "clippy", "cargo" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
edition = "2021" # required by rust-analyzer | ||
imports_granularity="Crate" | ||
format_code_in_doc_comments = true | ||
error_on_unformatted = true | ||
group_imports = "StdExternalCrate" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub struct JsonRpcConsts; | ||
|
||
impl JsonRpcConsts { | ||
// Some const definitions for json rpc http headers | ||
pub const AUTHORIZATION_HEADER: &'static str = "Authorization"; | ||
pub const SENDER_PUBKEY_HEADER: &'static str = "X-Sender-Pubkey"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod consts; | ||
pub mod request_parser; | ||
pub mod server; | ||
pub mod signer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::fmt::Display; | ||
|
||
use axum::{ | ||
body::{Bytes, HttpBody}, | ||
http::StatusCode, | ||
}; | ||
|
||
#[derive(Clone)] | ||
pub struct JsonrpcRequestParser; | ||
|
||
impl JsonrpcRequestParser { | ||
pub async fn get_req_body<B>(body: B) -> Result<Bytes, (StatusCode, String)> | ||
where | ||
B: HttpBody<Data = Bytes>, | ||
B::Error: Display, | ||
{ | ||
let bytes = match hyper::body::to_bytes(body).await { | ||
Ok(bytes) => bytes, | ||
Err(err) => { | ||
return Err(( | ||
StatusCode::BAD_REQUEST, | ||
format!("failed to read request body: {}", err), | ||
)); | ||
} | ||
}; | ||
Ok(bytes) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; | ||
|
||
use axum::{body::StreamBody, http::HeaderMap, response::IntoResponse, routing::post, Router}; | ||
use tokio::task::JoinHandle; | ||
|
||
use crate::json_rpc::signer::{SignerContext, SignerMiddleware}; | ||
|
||
pub async fn shutdown_signal() { | ||
let ctrl_c = async { | ||
tokio::signal::ctrl_c() | ||
.await | ||
.expect("failed to install Ctrl+C handler"); | ||
}; | ||
|
||
#[cfg(unix)] | ||
let terminate = async { | ||
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) | ||
.expect("failed to install signal handler") | ||
.recv() | ||
.await; | ||
}; | ||
|
||
#[cfg(not(unix))] | ||
let terminate = std::future::pending::<()>(); | ||
|
||
tokio::select! { | ||
_ = ctrl_c => {}, | ||
_ = terminate => {}, | ||
} | ||
|
||
println!("signal received, starting graceful shutdown"); | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct ProxyServerImpl { | ||
block_engine_url: String, | ||
client: reqwest::Client, | ||
} | ||
|
||
impl ProxyServerImpl { | ||
fn new(block_engine_url: String) -> Self { | ||
Self { | ||
block_engine_url, | ||
client: reqwest::Client::builder() | ||
.redirect(reqwest::redirect::Policy::none()) | ||
.build() | ||
.unwrap(), | ||
} | ||
} | ||
|
||
/// Method to handle the incoming json rpc requests | ||
async fn handle_jsonrpc( | ||
&self, | ||
headers: HeaderMap, | ||
payload: String, | ||
) -> Result<impl IntoResponse, (http::StatusCode, String)> { | ||
println!("Sending request {:?} to {}", payload, self.block_engine_url); | ||
let response = self | ||
.client | ||
.post(self.block_engine_url.clone()) | ||
.headers(headers) | ||
.body(payload) | ||
.send() | ||
.await | ||
.map_err(|err| (http::StatusCode::BAD_GATEWAY, err.to_string()))?; | ||
if response.status().is_success() { | ||
Ok(StreamBody::new(response.bytes_stream())) | ||
} else { | ||
Err((response.status(), response.status().to_string())) | ||
} | ||
} | ||
|
||
/// Creates and runs a json rpc server. The server binds to the given port of localhost | ||
pub async fn run(block_engine_url: String, keypair_path: String, port: u16) -> JoinHandle<()> { | ||
println!( | ||
"Starting proxy server at port: {}, with json_rpc_url: {} and keypair path: {}", | ||
port, block_engine_url, keypair_path | ||
); | ||
let handler = ProxyServerImpl::new(block_engine_url); | ||
let auth_context = SignerContext::new(keypair_path); | ||
|
||
let app = Router::new() | ||
.route( | ||
"/api/v1/bundles", | ||
post(|headers: HeaderMap, payload: String| async move { | ||
handler.handle_jsonrpc(headers, payload).await | ||
}), | ||
) | ||
.layer(axum::middleware::from_fn(SignerMiddleware::sign)) | ||
.layer(axum::Extension(auth_context)); | ||
|
||
tokio::spawn(async move { | ||
axum::Server::bind(&SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port)) | ||
.serve(app.into_make_service()) | ||
.with_graceful_shutdown(shutdown_signal()) | ||
.await | ||
.unwrap() | ||
}) | ||
} | ||
} |
Oops, something went wrong.