mirror of
https://github.com/eth-act/ere.git
synced 2026-04-03 03:00:17 -04:00
Add crate ere-server (#153)
This commit is contained in:
238
Cargo.lock
generated
238
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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).
|
||||
|
||||
59
crates/ere-server/Cargo.toml
Normal file
59
crates/ere-server/Cargo.toml
Normal 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"]
|
||||
8
crates/ere-server/build.rs
Normal file
8
crates/ere-server/build.rs
Normal 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(())
|
||||
}
|
||||
36
crates/ere-server/proto/api.proto
Normal file
36
crates/ere-server/proto/api.proto
Normal 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;
|
||||
}
|
||||
196
crates/ere-server/src/api.rs
Normal file
196
crates/ere-server/src/api.rs
Normal 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(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
crates/ere-server/src/client.rs
Normal file
110
crates/ere-server/src/client.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
29
crates/ere-server/src/input.rs
Normal file
29
crates/ere-server/src/input.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
6
crates/ere-server/src/lib.rs
Normal file
6
crates/ere-server/src/lib.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
pub(crate) mod api;
|
||||
pub mod client;
|
||||
pub mod input;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub mod server;
|
||||
142
crates/ere-server/src/main.rs
Normal file
142
crates/ere-server/src/main.rs
Normal 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")
|
||||
}
|
||||
84
crates/ere-server/src/server.rs
Normal file
84
crates/ere-server/src/server.rs
Normal 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 }))
|
||||
}
|
||||
}
|
||||
@@ -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
21
docker/server/Dockerfile
Normal 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"]
|
||||
@@ -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 -
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user