Add crate ere-server (#153)

This commit is contained in:
Han
2025-10-01 21:11:29 +08:00
committed by GitHub
parent 215a326995
commit 70a84a375c
16 changed files with 927 additions and 25 deletions

238
Cargo.lock generated
View File

@@ -1694,7 +1694,7 @@ dependencies = [
"http-body 0.4.6",
"hyper 0.14.32",
"itoa",
"matchit",
"matchit 0.7.3",
"memchr",
"mime",
"percent-encoding",
@@ -1723,7 +1723,7 @@ dependencies = [
"hyper 1.6.0",
"hyper-util",
"itoa",
"matchit",
"matchit 0.7.3",
"memchr",
"mime",
"percent-encoding",
@@ -1741,6 +1741,39 @@ dependencies = [
"tracing",
]
[[package]]
name = "axum"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98e529aee37b5c8206bb4bf4c44797127566d72f76952c970bd3d1e85de8f4e2"
dependencies = [
"axum-core 0.5.4",
"bytes",
"form_urlencoded",
"futures-util",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"itoa",
"matchit 0.8.4",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper 1.0.2",
"tokio",
"tower 0.5.2",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.3.4"
@@ -1779,6 +1812,25 @@ dependencies = [
"tracing",
]
[[package]]
name = "axum-core"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ac7a6beb1182c7e30253ee75c3e918080bfb83f5a3023bcdf7209d85fd147e6"
dependencies = [
"bytes",
"futures-core",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"mime",
"pin-project-lite",
"sync_wrapper 1.0.2",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "backoff"
version = "0.4.0"
@@ -3731,6 +3783,34 @@ dependencies = [
"zkvm-interface",
]
[[package]]
name = "ere-server"
version = "0.0.12"
dependencies = [
"anyhow",
"bincode 1.3.3",
"clap",
"ere-jolt",
"ere-miden",
"ere-nexus",
"ere-openvm",
"ere-pico",
"ere-risc0",
"ere-sp1",
"ere-ziren",
"ere-zisk",
"prost 0.13.5",
"prost-build 0.13.5",
"serde",
"tokio",
"tower-http",
"tracing",
"tracing-subscriber 0.3.19",
"twirp",
"twirp-build",
"zkvm-interface",
]
[[package]]
name = "ere-sp1"
version = "0.0.12"
@@ -5095,7 +5175,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"socket2 0.5.9",
"tokio",
"tower-service",
"tracing",
@@ -5213,7 +5293,7 @@ dependencies = [
"hyper 1.6.0",
"libc",
"pin-project-lite",
"socket2",
"socket2 0.5.9",
"tokio",
"tower-service",
"tracing",
@@ -5468,13 +5548,24 @@ dependencies = [
"rustversion",
]
[[package]]
name = "io-uring"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
"libc",
]
[[package]]
name = "ipconfig"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
dependencies = [
"socket2",
"socket2 0.5.9",
"widestring",
"windows-sys 0.48.0",
"winreg",
@@ -5875,7 +5966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.53.3",
]
[[package]]
@@ -6108,6 +6199,12 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "matrixmultiply"
version = "0.3.10"
@@ -10071,6 +10168,26 @@ dependencies = [
"tempfile",
]
[[package]]
name = "prost-build"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
dependencies = [
"heck 0.5.0",
"itertools 0.14.0",
"log",
"multimap 0.10.1",
"once_cell",
"petgraph 0.7.1",
"prettyplease 0.2.32",
"prost 0.13.5",
"prost-types 0.13.5",
"regex",
"syn 2.0.101",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.11.9"
@@ -10128,6 +10245,15 @@ dependencies = [
"prost 0.12.6",
]
[[package]]
name = "prost-types"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
dependencies = [
"prost 0.13.5",
]
[[package]]
name = "puffin"
version = "0.19.1"
@@ -10176,7 +10302,7 @@ dependencies = [
"quinn-udp",
"rustc-hash 2.1.1",
"rustls 0.23.27",
"socket2",
"socket2 0.5.9",
"thiserror 2.0.12",
"tokio",
"tracing",
@@ -10213,7 +10339,7 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"socket2 0.5.9",
"tracing",
"windows-sys 0.59.0",
]
@@ -11634,10 +11760,11 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
[[package]]
name = "serde"
version = "1.0.219"
version = "1.0.221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
checksum = "341877e04a22458705eb4e131a1508483c877dca2792b3781d4e5d8a6019ec43"
dependencies = [
"serde_core",
"serde_derive",
]
@@ -11669,10 +11796,19 @@ dependencies = [
]
[[package]]
name = "serde_derive"
version = "1.0.219"
name = "serde_core"
version = "1.0.221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
checksum = "0c459bc0a14c840cb403fc14b148620de1e0778c96ecd6e0c8c3cacb6d8d00fe"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6185cf75117e20e62b1ff867b9518577271e58abe0037c40bb4794969355ab0"
dependencies = [
"proc-macro2",
"quote",
@@ -11952,6 +12088,16 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "solang-parser"
version = "0.3.3"
@@ -13072,20 +13218,22 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.45.0"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"slab",
"socket2 0.6.0",
"tokio-macros",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -13338,7 +13486,7 @@ dependencies = [
"prost 0.13.5",
"rustls-native-certs 0.8.1",
"rustls-pemfile 2.2.0",
"socket2",
"socket2 0.5.9",
"tokio",
"tokio-rustls 0.26.2",
"tokio-stream",
@@ -13397,6 +13545,24 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-http"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"bitflags 2.9.0",
"bytes",
"futures-util",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"pin-project-lite",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
@@ -13612,6 +13778,42 @@ dependencies = [
"utf-8",
]
[[package]]
name = "twirp"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c52cc4e4423b6b3e2e2659523c8c9e19af514a06422fe77a95d86f6bf3478a"
dependencies = [
"anyhow",
"async-trait",
"axum 0.8.5",
"futures",
"http 1.3.1",
"http-body-util",
"hyper 1.6.0",
"prost 0.13.5",
"reqwest 0.12.15",
"serde",
"serde_json",
"thiserror 2.0.12",
"tokio",
"tower 0.5.2",
"url",
]
[[package]]
name = "twirp-build"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361c9d2474d96655b8a2fbaefba5801d5b1ffe74157b92cc3eae721ebb48d7a9"
dependencies = [
"prettyplease 0.2.32",
"proc-macro2",
"prost-build 0.13.5",
"quote",
"syn 2.0.101",
]
[[package]]
name = "twirp-rs"
version = "0.13.0-succinct"

View File

@@ -18,6 +18,7 @@ members = [
# CLI and dockerized zkVM
"crates/ere-cli",
"crates/ere-dockerized",
"crates/ere-server",
]
resolver = "2"
@@ -41,6 +42,8 @@ clap = "4.5.42"
dashmap = "6.1.0"
erased-serde = "0.4.6"
indexmap = "2.10.0"
prost = "0.13"
prost-build = "0.13"
rand = "0.9.2"
serde = "1.0.219"
serde_json = "1.0.142"
@@ -49,9 +52,13 @@ sha2 = "0.10.9"
strum = "0.27.2"
tempfile = "3.20.0"
thiserror = "2.0.12"
tokio = "1.0"
toml = "0.8.23"
tower-http = "0.6.6"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
twirp = "0.9.1"
twirp-build = "0.9.0"
# Jolt dependencies
ark-serialize = "0.5.0"
@@ -101,6 +108,7 @@ compile-utils = { path = "crates/compile-utils" }
test-utils = { path = "crates/test-utils" }
ere-cli = { path = "crates/ere-cli", default-features = false }
ere-dockerized = { path = "crates/ere-dockerized" }
ere-server = { path = "crates/ere-server" }
ere-jolt = { path = "crates/ere-jolt", default-features = false }
ere-miden = { path = "crates/ere-miden", default-features = false }
ere-nexus = { path = "crates/ere-nexus", default-features = false }

View File

@@ -47,6 +47,12 @@
- Nexus
- Miden
## Prerequisites
The following dependencies are required:
- `protobuf-compiler`
## Quick Start
This guide assumes you have Rust and Cargo installed. If not, please refer to the [Rust installation guide](https://www.rust-lang.org/tools/install).

View File

@@ -0,0 +1,59 @@
[package]
name = "ere-server"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
anyhow.workspace = true
bincode.workspace = true
prost.workspace = true
serde = { workspace = true, features = ["derive"] }
tokio.workspace = true
twirp.workspace = true
# Server
clap = { workspace = true, optional = true }
tower-http = { workspace = true, features = ["catch-panic"], optional = true }
tracing = { workspace = true, optional = true }
tracing-subscriber = { workspace = true, features = ["env-filter"], optional = true }
# Local dependencies
ere-jolt = { workspace = true, optional = true }
ere-miden = { workspace = true, optional = true }
ere-nexus = { workspace = true, optional = true }
ere-openvm = { workspace = true, optional = true }
ere-pico = { workspace = true, optional = true }
ere-risc0 = { workspace = true, optional = true }
ere-sp1 = { workspace = true, optional = true }
ere-ziren = { workspace = true, optional = true }
ere-zisk = { workspace = true, optional = true }
zkvm-interface = { workspace = true, features = ["clap"] }
[dev-dependencies]
[lints]
workspace = true
[build-dependencies]
prost-build.workspace = true
twirp-build.workspace = true
[features]
default = []
server = ["dep:clap", "tower-http", "tracing", "dep:tracing-subscriber", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
# zkVM
jolt = ["dep:ere-jolt", "server"]
miden = ["dep:ere-miden", "server"]
nexus = ["dep:ere-nexus", "server"]
openvm = ["dep:ere-openvm", "server"]
pico = ["dep:ere-pico", "server"]
risc0 = ["dep:ere-risc0", "server"]
sp1 = ["dep:ere-sp1", "server"]
ziren = ["dep:ere-ziren", "server"]
zisk = ["dep:ere-zisk", "server"]
# Cuda
cuda = ["ere-openvm?/cuda"]

View File

@@ -0,0 +1,8 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
prost_build::Config::new()
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") // enable support for JSON encoding
.service_generator(twirp_build::service_generator())
.compile_protos(&["./proto/api.proto"], &["./proto"])
.expect("error compiling protos");
Ok(())
}

View File

@@ -0,0 +1,36 @@
syntax = "proto3";
package api;
service ZkvmService {
rpc Execute(ExecuteRequest) returns (ExecuteResponse) {}
rpc Prove(ProveRequest) returns (ProveResponse) {}
rpc Verify(VerifyRequest) returns (VerifyResponse) {}
}
message ExecuteRequest {
bytes input = 1;
}
message ExecuteResponse {
bytes public_values = 1;
bytes report = 2;
}
message ProveRequest {
bytes input = 1;
}
message ProveResponse {
bytes public_values = 1;
bytes proof = 2;
bytes report = 3;
}
message VerifyRequest {
bytes proof = 1;
}
message VerifyResponse {
bytes public_values = 1;
}

View File

@@ -0,0 +1,196 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(dead_code)]
// This file is @generated by prost-build.
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteRequest {
#[prost(bytes = "vec", tag = "1")]
pub input: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteResponse {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveRequest {
#[prost(bytes = "vec", tag = "1")]
pub input: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveResponse {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub proof: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "3")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyRequest {
#[prost(bytes = "vec", tag = "1")]
pub proof: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyResponse {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
}
pub use twirp;
#[twirp::async_trait::async_trait]
pub trait ZkvmService: Send + Sync {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>>;
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>>;
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>>;
}
#[twirp::async_trait::async_trait]
impl<T> ZkvmService for std::sync::Arc<T>
where
T: ZkvmService + Sync + Send,
{
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
T::execute(&*self, req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
T::prove(&*self, req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
T::verify(&*self, req).await
}
}
pub fn router<T>(api: T) -> twirp::Router
where
T: ZkvmService + Clone + Send + Sync + 'static,
{
twirp::details::TwirpRouterBuilder::new("/api.ZkvmService", api)
.route(
"/Execute",
|api: T, req: twirp::Request<ExecuteRequest>| async move {
api.execute(req).await
},
)
.route(
"/Prove",
|api: T, req: twirp::Request<ProveRequest>| async move {
api.prove(req).await
},
)
.route(
"/Verify",
|api: T, req: twirp::Request<VerifyRequest>| async move {
api.verify(req).await
},
)
.build()
}
#[twirp::async_trait::async_trait]
impl ZkvmService for twirp::client::Client {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
self.request("api.ZkvmService/Execute", req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
self.request("api.ZkvmService/Prove", req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
self.request("api.ZkvmService/Verify", req).await
}
}
#[allow(dead_code)]
pub mod handler {
use super::*;
pub struct ZkvmServiceHandler {
inner: std::sync::Arc<dyn ZkvmService>,
}
impl ZkvmServiceHandler {
#[allow(clippy::new_ret_no_self)]
pub fn new<M: ZkvmService + 'static>(inner: M) -> Self {
Self {
inner: std::sync::Arc::new(inner),
}
}
}
#[twirp::async_trait::async_trait]
impl twirp::client::DirectHandler for ZkvmServiceHandler {
fn service(&self) -> &str {
"api.ZkvmService"
}
async fn handle(
&self,
method: &str,
req: twirp::reqwest::Request,
) -> twirp::Result<twirp::reqwest::Response> {
match method {
"Execute" => {
twirp::details::encode_response(
self
.inner
.execute(twirp::details::decode_request(req).await?)
.await?,
)
}
"Prove" => {
twirp::details::encode_response(
self
.inner
.prove(twirp::details::decode_request(req).await?)
.await?,
)
}
"Verify" => {
twirp::details::encode_response(
self
.inner
.verify(twirp::details::decode_request(req).await?)
.await?,
)
}
_ => {
Err(
twirp::bad_route(
format!(
"unknown rpc `{method}` for service `{}`, url: {:?}",
"api.ZkvmService",
req.url(),
),
),
)
}
}
}
}
}

View File

@@ -0,0 +1,110 @@
use crate::{
api::{
ExecuteRequest, ExecuteResponse, ProveRequest, ProveResponse, VerifyRequest,
VerifyResponse, ZkvmService,
},
input::SerializedInput,
};
use anyhow::{Context, Error, bail};
use std::time::{Duration, Instant};
use tokio::time::sleep;
use twirp::{Client, Request, reqwest, url::Url};
use zkvm_interface::{ProgramExecutionReport, ProgramProvingReport, Proof, PublicValues};
/// zkVM client of the `zkVMServer`.
#[allow(non_camel_case_types)]
pub struct zkVMClient {
client: Client,
}
impl zkVMClient {
pub async fn new(url: Url) -> Result<Self, Error> {
const TIMEOUT: Duration = Duration::from_secs(300); // 5mins
const INTERVAL: Duration = Duration::from_millis(500);
let http_client = reqwest::Client::new();
let start = Instant::now();
loop {
if start.elapsed() > TIMEOUT {
bail!("Health check timeout after 30 seconds")
}
match http_client.get(url.join("health").unwrap()).send().await {
Ok(response) if response.status().is_success() => break,
_ => sleep(INTERVAL).await,
}
}
let client = Client::new(url.join("twirp").unwrap(), http_client, Vec::new(), None);
Ok(Self { client })
}
pub async fn execute(
&self,
input: SerializedInput,
) -> Result<(PublicValues, ProgramExecutionReport), Error> {
let input = bincode::serialize(&input).with_context(|| "Failed to serialize input")?;
let request = Request::new(ExecuteRequest { input });
let response = self
.client
.execute(request)
.await
.with_context(|| "Execute RPC failed")?;
let ExecuteResponse {
public_values,
report,
} = response.into_body();
let report: ProgramExecutionReport = bincode::deserialize(&report)
.with_context(|| "Failed to deserialize execution report")?;
Ok((public_values, report))
}
pub async fn prove(
&self,
input: SerializedInput,
) -> Result<(PublicValues, Proof, ProgramProvingReport), Error> {
let input = bincode::serialize(&input).with_context(|| "Failed to serialize input")?;
let request = Request::new(ProveRequest { input });
let response = self
.client
.prove(request)
.await
.with_context(|| "Prove RPC failed")?;
let ProveResponse {
public_values,
proof,
report,
} = response.into_body();
let report: ProgramProvingReport = bincode::deserialize(&report)
.with_context(|| "Failed to deserialize proving report")?;
Ok((public_values, proof, report))
}
pub async fn verify(&self, proof: &[u8]) -> Result<PublicValues, Error> {
let request = Request::new(VerifyRequest {
proof: proof.to_vec(),
});
let response = self
.client
.verify(request)
.await
.with_context(|| "Verify RPC failed")?;
let VerifyResponse { public_values } = response.into_body();
Ok(public_values)
}
}

View File

@@ -0,0 +1,29 @@
use serde::{Deserialize, Serialize};
use zkvm_interface::{Input, InputItem};
#[derive(Serialize, Deserialize)]
pub struct SerializedInput(pub Vec<SerializedInputItem>);
impl From<SerializedInput> for Input {
fn from(value: SerializedInput) -> Self {
Self::from(value.0.into_iter().map(Into::into).collect::<Vec<_>>())
}
}
/// `InputItem` but only `SerializedObject` and `Byte` variants remain.
///
/// The user must serialize the `InputItem::Object` in the way the zkVM expects.
#[derive(Serialize, Deserialize)]
pub enum SerializedInputItem {
SerializedObject(Vec<u8>),
Bytes(Vec<u8>),
}
impl From<SerializedInputItem> for InputItem {
fn from(value: SerializedInputItem) -> Self {
match value {
SerializedInputItem::SerializedObject(bytes) => Self::SerializedObject(bytes),
SerializedInputItem::Bytes(bytes) => Self::Bytes(bytes),
}
}
}

View File

@@ -0,0 +1,6 @@
pub(crate) mod api;
pub mod client;
pub mod input;
#[cfg(feature = "server")]
pub mod server;

View File

@@ -0,0 +1,142 @@
use anyhow::{Context, Error};
use clap::Parser;
use ere_server::server::{router, zkVMServer};
use std::{
io::{self, Read},
net::{Ipv4Addr, SocketAddr},
sync::Arc,
};
use tokio::{net::TcpListener, signal};
use tower_http::catch_panic::CatchPanicLayer;
use tracing_subscriber::EnvFilter;
use twirp::{
Router,
axum::{self, routing::get},
reqwest::StatusCode,
server::not_found_handler,
};
use zkvm_interface::{ProverResourceType, zkVM};
// Compile-time check to ensure exactly one backend feature is enabled for CLI mode
const _: () = {
if cfg!(feature = "server") {
assert!(
(cfg!(feature = "jolt") as u8
+ cfg!(feature = "miden") as u8
+ cfg!(feature = "nexus") as u8
+ cfg!(feature = "openvm") as u8
+ cfg!(feature = "pico") as u8
+ cfg!(feature = "risc0") as u8
+ cfg!(feature = "sp1") as u8
+ cfg!(feature = "ziren") as u8
+ cfg!(feature = "zisk") as u8)
== 1,
"Exactly one zkVM backend feature must be enabled for CLI mode"
);
}
};
#[derive(Parser)]
#[command(author, version)]
struct Args {
#[arg(long, default_value = "3000")]
port: u16,
#[command(subcommand)]
resource: ProverResourceType,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
let args = Args::parse();
// Read serialized program from stdin.
let mut program = Vec::new();
io::stdin().read_to_end(&mut program)?;
let zkvm = construct_zkvm(program, args.resource)?;
let server = Arc::new(zkVMServer::new(zkvm));
let app = Router::new()
.nest("/twirp", router(server))
.route("/health", get(health))
.fallback(not_found_handler)
.layer(CatchPanicLayer::new());
let addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), args.port);
let tcp_listener = TcpListener::bind(addr).await?;
tracing::info!("Listening on {}", addr);
axum::serve(tcp_listener, app)
.with_graceful_shutdown(shutdown_signal())
.await?;
tracing::info!("Shutdown gracefully");
Ok(())
}
async fn health() -> StatusCode {
StatusCode::OK
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
tokio::select! {
_ = ctrl_c => {
tracing::info!("Received Ctrl+C, shutting down gracefully");
},
_ = terminate => {
tracing::info!("Received SIGTERM, shutting down gracefully");
},
}
}
fn construct_zkvm(program: Vec<u8>, resource: ProverResourceType) -> Result<impl zkVM, Error> {
let program =
bincode::deserialize(&program).with_context(|| "Failed to deserialize program")?;
#[cfg(feature = "jolt")]
let zkvm = ere_jolt::EreJolt::new(program, resource);
#[cfg(feature = "miden")]
let zkvm = ere_miden::EreMiden::new(program, resource);
#[cfg(feature = "nexus")]
let zkvm = Ok::<_, Error>(ere_nexus::EreNexus::new(program, resource));
#[cfg(feature = "openvm")]
let zkvm = ere_openvm::EreOpenVM::new(program, resource);
#[cfg(feature = "pico")]
let zkvm = Ok::<_, Error>(ere_pico::ErePico::new(program, resource));
#[cfg(feature = "risc0")]
let zkvm = ere_risc0::EreRisc0::new(program, resource);
#[cfg(feature = "sp1")]
let zkvm = Ok::<_, Error>(ere_sp1::EreSP1::new(program, resource));
#[cfg(feature = "ziren")]
let zkvm = Ok::<_, Error>(ere_ziren::EreZiren::new(program, resource));
#[cfg(feature = "zisk")]
let zkvm = ere_zisk::EreZisk::new(program, resource);
zkvm.with_context(|| "Failed to instantiate zkVM")
}

View File

@@ -0,0 +1,84 @@
use crate::{
api::{
self, ExecuteRequest, ExecuteResponse, ProveRequest, ProveResponse, VerifyRequest,
VerifyResponse, ZkvmService,
},
input::SerializedInput,
};
use twirp::{Request, Response, async_trait::async_trait, invalid_argument};
use zkvm_interface::zkVM;
pub use api::router;
/// zkVM server that handles the request by forwarding to the underlying
/// [`zkVM`] implementation methods.
#[allow(non_camel_case_types)]
pub struct zkVMServer<T> {
zkvm: T,
}
impl<T: 'static + zkVM + Send + Sync> zkVMServer<T> {
pub fn new(zkvm: T) -> Self {
Self { zkvm }
}
}
#[async_trait]
impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
async fn execute(
&self,
request: Request<ExecuteRequest>,
) -> twirp::Result<Response<ExecuteResponse>> {
let request = request.into_body();
let input = bincode::deserialize::<SerializedInput>(&request.input)
.map_err(|_| invalid_argument("failed to deserialize input"))?
.into();
let (public_values, report) = self
.zkvm
.execute(&input)
.map_err(|err| invalid_argument(format!("failed to execute: {err:?}")))?;
Ok(Response::new(ExecuteResponse {
public_values,
report: bincode::serialize(&report).unwrap(),
}))
}
async fn prove(
&self,
request: Request<ProveRequest>,
) -> twirp::Result<Response<ProveResponse>> {
let request = request.into_body();
let input = bincode::deserialize::<SerializedInput>(&request.input)
.map_err(|_| invalid_argument("failed to deserialize input"))?
.into();
let (public_values, proof, report) = self
.zkvm
.prove(&input)
.map_err(|err| invalid_argument(format!("failed to prove: {err:?}")))?;
Ok(Response::new(ProveResponse {
public_values,
proof,
report: bincode::serialize(&report).unwrap(),
}))
}
async fn verify(
&self,
request: Request<VerifyRequest>,
) -> twirp::Result<Response<VerifyResponse>> {
let request = request.into_body();
let public_values = self
.zkvm
.verify(&request.proof)
.map_err(|err| invalid_argument(format!("failed to verify: {err:?}")))?;
Ok(Response::new(VerifyResponse { public_values }))
}
}

View File

@@ -25,6 +25,7 @@ RUN apt-get update && \
ca-certificates \
openssl \
libssl-dev \
protobuf-compiler \
# Clean up apt cache
&& apt-get clean && \
rm -rf /var/lib/apt/lists/*

21
docker/server/Dockerfile Normal file
View File

@@ -0,0 +1,21 @@
ARG BASE_ZKVM_IMAGE_TAG=ere-base-zkvm:latest
FROM ${BASE_ZKVM_IMAGE_TAG}
COPY . /ere
WORKDIR /ere
ARG ZKVM
ARG RUSTFLAGS="-Ctarget-cpu=native"
# If current environment is in CI or not.
ARG CI
RUN if [ -n "$CI" ]; then FEATURES=${ZKVM}; else FEATURES=${ZKVM},cuda; fi && \
RUSTFLAGS=${RUSTFLAGS} cargo build --release --package ere-server --bin ere-server --features $FEATURES && \
cp /ere/target/release/ere-server /ere/ere-server && \
cargo clean && \
rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache
ENTRYPOINT ["/ere/ere-server"]

View File

@@ -2,12 +2,6 @@ ARG BASE_IMAGE_TAG=ere-base:latest
FROM ${BASE_IMAGE_TAG}
# Install protobuf-compiler because cargo-ziren requires
RUN apt-get update && apt-get install -y --no-install-recommends protobuf-compiler \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install go because cargo-ziren requires (no way to turn off)
RUN curl -fsSL https://golang.org/dl/go1.23.1.linux-amd64.tar.gz | tar -C /usr/local -xzf -

View File

@@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libomp-dev \
libgmp-dev \
nlohmann-json3-dev \
protobuf-compiler \
# protobuf-compiler is in ere-base
uuid-dev \
libgrpc++-dev \
libsecp256k1-dev \