Skip to content

Commit

Permalink
Merge pull request #66 from ethereum-optimism/0xkitsune/server
Browse files Browse the repository at this point in the history
feat(opt8n): Implement `opt8n server`
  • Loading branch information
refcell authored Aug 21, 2024
2 parents f6bbb01 + e533b4a commit 822a8aa
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 15 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ tokio = { version = "1", features = ["full"] }
futures = "0.3"
clap = { version = "4", features = ["derive"] }
shellwords = "1"
reqwest = "0.12"
reqwest = { version = "0.12", features = ["stream"] }
tracing-subscriber = "0.3.18"
hashbrown = "0.14.5"

Expand Down
6 changes: 5 additions & 1 deletion bin/opt8n/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ tracing.workspace = true
tokio.workspace = true
futures.workspace = true
color-eyre.workspace = true
axum = "0.7.5"
http-body-util = "0.1.2"

# CLI
clap.workspace = true
Expand All @@ -36,4 +38,6 @@ revm.workspace = true
# OP Types
op-test-vectors.workspace = true
op-alloy-rpc-types.workspace = true
hyper = "1.4.1"
thiserror.workspace = true
reqwest.workspace = true
hyper = "1.4.1"
27 changes: 16 additions & 11 deletions bin/opt8n/src/cmd/script.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anvil::cmd::NodeArgs;
use clap::Parser;
use clap::{Parser, ValueHint};
use color_eyre::eyre::eyre;
use futures::StreamExt;

Expand All @@ -9,38 +9,43 @@ use crate::opt8n::{Opt8n, Opt8nArgs};
pub struct ScriptArgs {
#[command(flatten)]
opt8n_args: Opt8nArgs,
#[command(flatten)]
inner: forge_script::ScriptArgs,
#[arg(value_hint = ValueHint::FilePath)]
pub path: String,
#[command(flatten)]
pub node_args: NodeArgs,
}

impl ScriptArgs {
pub async fn run(mut self) -> color_eyre::Result<()> {
pub async fn run(self) -> color_eyre::Result<()> {
let opt8n = Opt8n::new(
Some(self.node_args.clone()),
self.opt8n_args.output.clone(),
self.opt8n_args.genesis.clone(),
)
.await?;

let mut script_args = forge_script::ScriptArgs {
path: self.path.clone(),
..Default::default()
};

foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args(
self.inner.opts.silent,
self.inner.json,
script_args.opts.silent,
script_args.json,
))?;

self.inner.broadcast = true;
self.inner.evm_opts.sender = Some(
script_args.broadcast = true;
script_args.evm_opts.sender = Some(
opt8n
.node_handle
.genesis_accounts()
.last()
.expect("Could not get genesis account"),
);
self.inner.unlocked = true;
self.inner.evm_opts.fork_url = Some(opt8n.node_handle.http_endpoint());
script_args.unlocked = true;
script_args.evm_opts.fork_url = Some(opt8n.node_handle.http_endpoint());

run_script(opt8n, Box::new(self.inner)).await?;
run_script(opt8n, Box::new(script_args)).await?;

Ok(())
}
Expand Down
165 changes: 163 additions & 2 deletions bin/opt8n/src/cmd/server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
use anvil::cmd::NodeArgs;
use axum::body::{Body, Bytes};
use axum::extract::State;
use axum::http::{Request, StatusCode};
use axum::response::{IntoResponse, Response};
use clap::Parser;
use color_eyre::eyre::eyre;
use color_eyre::owo_colors::OwoColorize;
use futures::StreamExt;
use http_body_util::BodyExt;
use std::net::SocketAddr;
use std::sync::Arc;
use thiserror::Error;
use tokio::net::TcpListener;
use tokio::sync::mpsc::Sender;
use tokio::sync::Mutex;

use crate::opt8n::Opt8nArgs;
use crate::opt8n::{Opt8n, Opt8nArgs};

#[derive(Parser, Clone, Debug)]
pub struct ServerArgs {
Expand All @@ -13,6 +27,153 @@ pub struct ServerArgs {

impl ServerArgs {
pub async fn run(&self) -> color_eyre::Result<()> {
unimplemented!()
let opt8n = Opt8n::new(
Some(self.node_args.clone()),
self.opt8n_args.output.clone(),
self.opt8n_args.genesis.clone(),
)
.await?;

let opt8n = Arc::new(Mutex::new(opt8n));

let (dump_tx, mut dump_rx) = tokio::sync::mpsc::channel::<()>(1);

let dump_fixture_router = axum::Router::new()
.route("/dump_fixture", axum::routing::post(dump_execution_fixture))
.with_state((opt8n.clone(), dump_tx));

let router = axum::Router::new()
.route("/mine_prestate", axum::routing::post(mine_prestate))
.fallback(fallback_handler)
.with_state(opt8n);

let router = dump_fixture_router.merge(router);

let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
let listener = TcpListener::bind(addr).await?;
let local_addr = listener.local_addr()?;

let server = axum::serve(listener, router.into_make_service());
let _ = println!("Opt8n server listening on: {:#?}", local_addr).green();

tokio::select! {
err = server => {
todo!("Handle server error: {:#?}", err);
}

_ = dump_rx.recv() => {
let _ = println!("Exuction fixture dumped to: {:#?}", self.opt8n_args.output).green();

}
}

Ok(())
}
}

async fn dump_execution_fixture(
State((opt8n, dump_tx)): State<(Arc<Mutex<Opt8n>>, Sender<()>)>,
) -> Result<(), ServerError> {
mine_block(opt8n).await?;
dump_tx.send(()).await.map_err(ServerError::SendError)?;

Ok(())
}

async fn mine_prestate(State(opt8n): State<Arc<Mutex<Opt8n>>>) -> Result<(), ServerError> {
mine_block(opt8n.clone()).await?;
Ok(())
}

async fn mine_block(opt8n: Arc<Mutex<Opt8n>>) -> Result<(), ServerError> {
let mut opt8n = opt8n.lock().await;

let mut new_blocks = opt8n.eth_api.backend.new_block_notifications();

opt8n.mine_block().await;

let block = new_blocks
.next()
.await
.ok_or(eyre!("No new block"))
.map_err(ServerError::Opt8nError)?;
if let Some(block) = opt8n.eth_api.backend.get_block_by_hash(block.hash) {
opt8n
.generate_execution_fixture(block)
.await
.map_err(ServerError::Opt8nError)?;
}

Ok(())
}

async fn fallback_handler(
State(opt8n): State<Arc<Mutex<Opt8n>>>,
req: Request<Body>,
) -> Result<(), ServerError> {
let anvil_endpoint = opt8n.lock().await.node_handle.http_endpoint();
proxy_to_anvil(req, anvil_endpoint).await?;
Ok(())
}

pub async fn proxy_to_anvil(
req: Request<Body>,
anvil_endpoint: String,
) -> Result<Response<Body>, ServerError> {
let http_client = reqwest::Client::new();

let (headers, body) = req.into_parts();
let body = body
.collect()
.await
.map_err(ServerError::AxumError)?
.to_bytes();

let axum_req: Request<Bytes> = Request::from_parts(headers, body);
let mut req = reqwest::Request::try_from(axum_req).expect("TODO: handle error");
req.url_mut().set_fragment(Some(&anvil_endpoint));

let res: Response<reqwest::Body> = http_client
.execute(req)
.await
.expect("TODO: handle error ")
.into();

let (headers, body) = res.into_parts();

let body = body
.collect()
.await
.map_err(ServerError::ReqwestError)?
.to_bytes()
.into();

Ok(Response::from_parts(headers, body))
}

#[derive(Error, Debug)]
pub enum ServerError {
#[error("Opt8n error: {0}")]
Opt8nError(color_eyre::Report),
#[error("Axum error: {0}")]
AxumError(axum::Error),
#[error("Reqwest error: {0}")]
ReqwestError(reqwest::Error),
#[error("Senderror: {0}")]
SendError(tokio::sync::mpsc::error::SendError<()>),
}

impl IntoResponse for ServerError {
fn into_response(self) -> Response {
let message = match self {
ServerError::Opt8nError(err) => err.to_string(),
ServerError::ReqwestError(err) => err.to_string(),
ServerError::AxumError(err) => err.to_string(),
ServerError::SendError(err) => err.to_string(),
};

let body = Body::from(message);

(StatusCode::INTERNAL_SERVER_ERROR, body).into_response()
}
}
30 changes: 30 additions & 0 deletions examples/exec-scripts/script/Counter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";

contract CounterScript is Script {
Counter public counter;

function setUp() public {}

function run() public {
vm.startBroadcast();

counter = new Counter();

vm.stopBroadcast();
}
}

contract Counter {
uint256 public number;

function setNumber(uint256 newNumber) public {
number = newNumber;
}

function increment() public {
number++;
}
}

0 comments on commit 822a8aa

Please sign in to comment.