Add ProverResouce::Cluster and add cluster support for ZisK (#284)

This commit is contained in:
Han
2026-02-07 11:05:23 +09:00
committed by GitHub
parent 3f27243b60
commit c085ccd9f3
49 changed files with 2126 additions and 841 deletions

View File

@@ -9,12 +9,13 @@ IMAGE_REGISTRY=""
BUILD_BASE=false
BUILD_COMPILER=false
BUILD_SERVER=false
BUILD_CLUSTER=false
CUDA=false
CUDA_ARCH=""
RUSTFLAGS=""
usage() {
echo "Usage: $0 --zkvm <zkvm> --tag <tag> [--base] [--compiler] [--server] [--registry <registry>] [--cuda] [--cuda-arch <arch>] [--rustflags <flags>]"
echo "Usage: $0 --zkvm <zkvm> --tag <tag> [--base] [--compiler] [--server] [--cluster] [--registry <registry>] [--cuda] [--cuda-arch <arch>] [--rustflags <flags>]"
echo ""
echo "Required:"
echo " --zkvm <zkvm> zkVM to build for (e.g., zisk, sp1, risc0)"
@@ -24,6 +25,7 @@ usage() {
echo " --base Build the base images"
echo " --compiler Build the compiler image"
echo " --server Build the server image"
echo " --cluster Build the cluster image"
echo ""
echo "Optional:"
echo " --registry <reg> Registry prefix (e.g., ghcr.io/eth-act/ere)"
@@ -60,6 +62,10 @@ while [[ $# -gt 0 ]]; do
BUILD_SERVER=true
shift
;;
--cluster)
BUILD_CLUSTER=true
shift
;;
--cuda)
CUDA=true
shift
@@ -93,8 +99,8 @@ if [ -z "$IMAGE_TAG" ]; then
usage
fi
if [ "$BUILD_BASE" = false ] && [ "$BUILD_COMPILER" = false ] && [ "$BUILD_SERVER" = false ]; then
echo "Error: At least one of --base, --compiler, --server is required"
if [ "$BUILD_BASE" = false ] && [ "$BUILD_COMPILER" = false ] && [ "$BUILD_SERVER" = false ] && [ "$BUILD_CLUSTER" = false ]; then
echo "Error: At least one of --base, --compiler, --server, --cluster is required"
usage
fi
@@ -118,6 +124,7 @@ BASE_IMAGE="${IMAGE_PREFIX}ere-base:${IMAGE_TAG}"
BASE_ZKVM_IMAGE="${IMAGE_PREFIX}ere-base-${ZKVM}:${IMAGE_TAG}"
COMPILER_ZKVM_IMAGE="${IMAGE_PREFIX}ere-compiler-${ZKVM}:${IMAGE_TAG}"
SERVER_ZKVM_IMAGE="${IMAGE_PREFIX}ere-server-${ZKVM}:${IMAGE_TAG}"
CLUSTER_ZKVM_IMAGE="${IMAGE_PREFIX}ere-cluster-${ZKVM}:${IMAGE_TAG}"
# Prepare build arguments
@@ -125,16 +132,19 @@ BASE_BUILD_ARGS=()
BASE_ZKVM_BUILD_ARGS=(--build-arg "BASE_IMAGE=$BASE_IMAGE")
COMPILER_ZKVM_BUILD_ARGS=(--build-arg "BASE_ZKVM_IMAGE=$BASE_ZKVM_IMAGE")
SERVER_ZKVM_BUILD_ARGS=(--build-arg "BASE_ZKVM_IMAGE=$BASE_ZKVM_IMAGE")
CLUSTER_ZKVM_BUILD_ARGS=()
if [ "$CUDA" = true ]; then
BASE_BUILD_ARGS+=(--build-arg "CUDA=1")
BASE_ZKVM_BUILD_ARGS+=(--build-arg "CUDA=1")
SERVER_ZKVM_BUILD_ARGS+=(--build-arg "CUDA=1")
CLUSTER_ZKVM_BUILD_ARGS+=(--build-arg "CUDA=1")
fi
if [ -n "$CUDA_ARCH" ]; then
BASE_ZKVM_BUILD_ARGS+=(--build-arg "CUDA_ARCH=$CUDA_ARCH")
SERVER_ZKVM_BUILD_ARGS+=(--build-arg "CUDA_ARCH=$CUDA_ARCH")
CLUSTER_ZKVM_BUILD_ARGS+=(--build-arg "CUDA_ARCH=$CUDA_ARCH")
fi
if [ -n "$RUSTFLAGS" ]; then
@@ -178,4 +188,13 @@ if [ "$BUILD_SERVER" = true ]; then
.
fi
if [ "$BUILD_CLUSTER" = true ]; then
echo "Building zkvm cluster image: $CLUSTER_ZKVM_IMAGE"
docker build \
--file "docker/${ZKVM}/Dockerfile.cluster" \
--tag "$CLUSTER_ZKVM_IMAGE" \
"${CLUSTER_ZKVM_BUILD_ARGS[@]}" \
.
fi
echo "Build complete!"

597
Cargo.lock generated
View File

@@ -2145,6 +2145,16 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "build-probe-mpi"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78ace2bb02fc18ad937f1599a853fcf3da2327bc1eb3c8e62b1f2fe4573bfd6"
dependencies = [
"pkg-config",
"shell-words",
]
[[package]]
name = "bumpalo"
version = "3.17.0"
@@ -2333,10 +2343,11 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.22"
version = "1.2.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
@@ -2573,6 +2584,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "colored"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "common"
version = "0.2.0"
@@ -2688,6 +2708,15 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "conv"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
dependencies = [
"custom_derive",
]
[[package]]
name = "convert_case"
version = "0.7.1"
@@ -3059,6 +3088,23 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "curves"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"fields",
"num-bigint 0.4.6",
"num-traits",
"rand 0.9.2",
]
[[package]]
name = "custom_derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
[[package]]
name = "darling"
version = "0.20.11"
@@ -3784,6 +3830,15 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "env"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc95de49ad098572c02d3fbf368c9a020bfff5ae78483685b77f51d8a7e9486d"
dependencies = [
"num_threads",
]
[[package]]
name = "env_filter"
version = "0.1.3"
@@ -4206,12 +4261,20 @@ dependencies = [
"ere-compile-utils",
"ere-test-utils",
"ere-zkvm-interface",
"futures-util",
"http 1.3.1",
"parking_lot",
"proofman-verifier",
"serde",
"strum 0.27.2",
"tempfile",
"thiserror 2.0.12",
"tokio",
"tonic 0.14.3",
"tracing",
"uuid 1.17.0",
"wait-timeout",
"zisk-distributed-grpc-api",
]
[[package]]
@@ -4224,7 +4287,6 @@ dependencies = [
"clap",
"indexmap 2.10.0",
"serde",
"serde-untagged",
"serde_json",
"serde_yaml",
"strum 0.27.2",
@@ -4557,7 +4619,7 @@ dependencies = [
"tokio",
"tracing",
"walkdir",
"yansi",
"yansi 0.5.1",
]
[[package]]
@@ -4697,6 +4759,16 @@ dependencies = [
"serde",
]
[[package]]
name = "fields"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"num-bigint 0.4.6",
"paste",
"serde",
]
[[package]]
name = "final_reduced_risc_v_machine"
version = "0.1.0"
@@ -4709,6 +4781,12 @@ dependencies = [
"verifier_generator",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "findshlibs"
version = "0.10.2"
@@ -6522,6 +6600,25 @@ version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libffi"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2"
dependencies = [
"libc",
"libffi-sys",
]
[[package]]
name = "libffi-sys"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c"
dependencies = [
"cc",
]
[[package]]
name = "libgit2-sys"
version = "0.17.0+1.8.1"
@@ -7260,6 +7357,32 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "mpi"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677762a4bde2c81158fc566a69b97d11b0c3358694e64f4f922ac5189be311cc"
dependencies = [
"build-probe-mpi",
"conv",
"libffi",
"mpi-sys",
"once_cell",
"smallvec",
"thiserror 1.0.69",
]
[[package]]
name = "mpi-sys"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f655543f54b263cbc3d2456bf714bd807d66a33eff8f70136687f0776d34f76"
dependencies = [
"bindgen 0.69.5",
"build-probe-mpi",
"cc",
]
[[package]]
name = "multimap"
version = "0.8.3"
@@ -7857,6 +7980,25 @@ dependencies = [
"malloc_buf",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "objc2-io-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
dependencies = [
"libc",
"objc2-core-foundation",
]
[[package]]
name = "object"
version = "0.35.0"
@@ -10244,6 +10386,15 @@ dependencies = [
"group 0.13.0",
]
[[package]]
name = "papergrid"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608b6444acf7f5ea39e8bd06dd6037e34a4b5ddfb29ae840edad49ea798e9e79"
dependencies = [
"unicode-width 0.1.14",
]
[[package]]
name = "parity-scale-codec"
version = "3.7.4"
@@ -10433,6 +10584,17 @@ dependencies = [
"indexmap 2.10.0",
]
[[package]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
"fixedbitset 0.5.7",
"hashbrown 0.15.3",
"indexmap 2.10.0",
]
[[package]]
name = "pharos"
version = "0.5.3"
@@ -10605,7 +10767,7 @@ dependencies = [
"static_assertions",
"strum 0.26.3",
"strum_macros 0.26.4",
"sysinfo",
"sysinfo 0.30.13",
"thiserror 1.0.69",
"tiny-keccak",
"tracing",
@@ -10616,6 +10778,23 @@ dependencies = [
"zkhash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pil-std-lib"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"colored",
"fields",
"num-bigint 0.4.6",
"num-traits",
"proofman-common",
"proofman-hints",
"proofman-util",
"rayon",
"tracing",
"witness",
]
[[package]]
name = "pin-project"
version = "1.1.10"
@@ -10874,6 +11053,126 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "proofman"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"blake3",
"borsh",
"bytemuck",
"chrono",
"colored",
"crossbeam-channel",
"csv",
"curves",
"fields",
"libloading 0.8.7",
"mpi",
"num-bigint 0.4.6",
"num-traits",
"pil-std-lib",
"proofman-common",
"proofman-hints",
"proofman-macros",
"proofman-starks-lib-c",
"proofman-util",
"proofman-verifier",
"rand 0.9.2",
"rayon",
"serde",
"serde_json",
"tokio",
"tokio-util",
"tracing",
"witness",
]
[[package]]
name = "proofman-common"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"borsh",
"colored",
"crossbeam-channel",
"crossbeam-queue",
"csv",
"env",
"fields",
"indexmap 2.10.0",
"lazy_static",
"libloading 0.8.7",
"mpi",
"num_cpus",
"proofman-macros",
"proofman-starks-lib-c",
"proofman-util",
"rayon",
"serde",
"serde_json",
"sysinfo 0.35.2",
"tabled",
"thiserror 2.0.12",
"tracing",
"tracing-subscriber 0.3.20",
"yansi 1.0.1",
]
[[package]]
name = "proofman-hints"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"fields",
"proofman-common",
"proofman-starks-lib-c",
"proofman-util",
"tracing",
]
[[package]]
name = "proofman-macros"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"proc-macro2",
"quote",
"rayon",
"syn 2.0.101",
]
[[package]]
name = "proofman-starks-lib-c"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"crossbeam-channel",
"tracing",
]
[[package]]
name = "proofman-util"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"colored",
"fields",
"sysinfo 0.35.2",
"tracing",
]
[[package]]
name = "proofman-verifier"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"bytemuck",
"fields",
"rayon",
"tracing",
]
[[package]]
name = "proptest"
version = "1.9.0"
@@ -10923,6 +11222,16 @@ dependencies = [
"prost-derive 0.13.5",
]
[[package]]
name = "prost"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
dependencies = [
"bytes",
"prost-derive 0.14.3",
]
[[package]]
name = "prost-build"
version = "0.11.9"
@@ -10986,6 +11295,27 @@ dependencies = [
"tempfile",
]
[[package]]
name = "prost-build"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
dependencies = [
"heck 0.5.0",
"itertools 0.14.0",
"log",
"multimap 0.10.1",
"petgraph 0.8.3",
"prettyplease 0.2.32",
"prost 0.14.3",
"prost-types 0.14.3",
"pulldown-cmark",
"pulldown-cmark-to-cmark",
"regex",
"syn 2.0.101",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.11.9"
@@ -11025,6 +11355,19 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "prost-derive"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
dependencies = [
"anyhow",
"itertools 0.14.0",
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "prost-types"
version = "0.11.9"
@@ -11052,6 +11395,15 @@ dependencies = [
"prost 0.13.5",
]
[[package]]
name = "prost-types"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7"
dependencies = [
"prost 0.14.3",
]
[[package]]
name = "prover"
version = "0.1.0"
@@ -11114,6 +11466,26 @@ dependencies = [
"parking_lot",
]
[[package]]
name = "pulldown-cmark"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
dependencies = [
"bitflags 2.9.0",
"memchr",
"unicase",
]
[[package]]
name = "pulldown-cmark-to-cmark"
version = "22.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90"
dependencies = [
"pulldown-cmark",
]
[[package]]
name = "quanta"
version = "0.12.5"
@@ -12772,18 +13144,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde-untagged"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058"
dependencies = [
"erased-serde",
"serde",
"serde_core",
"typeid",
]
[[package]]
name = "serde_arrays"
version = "0.1.0"
@@ -12838,6 +13198,7 @@ version = "1.0.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
dependencies = [
"indexmap 2.10.0",
"itoa",
"memchr",
"ryu",
@@ -13034,6 +13395,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shell-words"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
[[package]]
name = "shlex"
version = "1.3.0"
@@ -13596,7 +13963,7 @@ dependencies = [
"sp1-stark",
"strum 0.26.3",
"strum_macros 0.26.4",
"sysinfo",
"sysinfo 0.30.13",
"tempfile",
"thiserror 1.0.69",
"tokio",
@@ -13635,7 +14002,7 @@ dependencies = [
"sp1-derive",
"sp1-primitives",
"strum 0.26.3",
"sysinfo",
"sysinfo 0.30.13",
"tracing",
]
@@ -14104,6 +14471,20 @@ dependencies = [
"windows 0.52.0",
]
[[package]]
name = "sysinfo"
version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e"
dependencies = [
"libc",
"memchr",
"ntapi",
"objc2-core-foundation",
"objc2-io-kit",
"windows 0.61.1",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
@@ -14146,6 +14527,27 @@ dependencies = [
"libc",
]
[[package]]
name = "tabled"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2407502760ccfd538f2fb1f843dd87b6daf1a17848d57bc5a25617e408ef4c7a"
dependencies = [
"papergrid",
"tabled_derive",
]
[[package]]
name = "tabled_derive"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278ea3921cee8c5a69e0542998a089f7a14fa43c9c4e4f9951295da89bd0c943"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "tap"
version = "1.0.1"
@@ -14486,9 +14888,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.15"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
@@ -14656,6 +15058,35 @@ dependencies = [
"tracing",
]
[[package]]
name = "tonic"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a286e33f82f8a1ee2df63f4fa35c0becf4a85a0cb03091a15fd7bf0b402dc94a"
dependencies = [
"async-trait",
"axum 0.8.5",
"base64 0.22.1",
"bytes",
"h2 0.4.10",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"hyper 1.6.0",
"hyper-timeout 0.5.2",
"hyper-util",
"percent-encoding",
"pin-project",
"socket2 0.6.0",
"sync_wrapper 1.0.2",
"tokio",
"tokio-stream",
"tower 0.5.2",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tonic-build"
version = "0.8.4"
@@ -14669,6 +15100,45 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "tonic-build"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27aac809edf60b741e2d7db6367214d078856b8a5bff0087e94ff330fb97b6fc"
dependencies = [
"prettyplease 0.2.32",
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "tonic-prost"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6c55a2d6a14174563de34409c9f92ff981d006f56da9c6ecd40d9d4a31500b0"
dependencies = [
"bytes",
"prost 0.14.3",
"tonic 0.14.3",
]
[[package]]
name = "tonic-prost-build"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4556786613791cfef4ed134aa670b61a85cfcacf71543ef33e8d801abae988f"
dependencies = [
"prettyplease 0.2.32",
"proc-macro2",
"prost-build 0.14.3",
"prost-types 0.14.3",
"quote",
"syn 2.0.101",
"tempfile",
"tonic-build 0.14.3",
]
[[package]]
name = "tower"
version = "0.4.13"
@@ -14697,9 +15167,12 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"indexmap 2.10.0",
"pin-project-lite",
"slab",
"sync_wrapper 1.0.2",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
@@ -14870,6 +15343,16 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.2.25"
@@ -14889,12 +15372,15 @@ dependencies = [
"nu-ansi-term",
"once_cell",
"regex-automata",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]
@@ -15159,6 +15645,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]]
name = "unicode-ident"
version = "1.0.18"
@@ -15293,6 +15785,7 @@ dependencies = [
"getrandom 0.3.3",
"js-sys",
"rand 0.9.2",
"serde",
"uuid-macro-internal",
"wasm-bindgen",
]
@@ -16144,6 +16637,20 @@ dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "witness"
version = "0.15.0"
source = "git+https://github.com/0xPolygonHermez/pil2-proofman?tag=v0.15.0#78497c5a05ea316df2188f98c1df66bffb80192f"
dependencies = [
"colored",
"fields",
"libloading 0.8.7",
"proofman-common",
"proofman-util",
"serde_json",
"tracing",
]
[[package]]
name = "worker"
version = "0.1.0"
@@ -16220,6 +16727,12 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yoke"
version = "0.8.0"
@@ -16375,6 +16888,44 @@ dependencies = [
"zopfli",
]
[[package]]
name = "zisk-distributed-common"
version = "0.1.0"
source = "git+https://github.com/han0110/zisk?branch=patch%2Fv0.15.0-cluster#6ffe5f4123a258765cf814c5ff3d3bb8988fb964"
dependencies = [
"anyhow",
"borsh",
"chrono",
"proofman",
"proofman-common",
"serde",
"serde_json",
"thiserror 2.0.12",
"tracing",
"tracing-appender",
"tracing-subscriber 0.3.20",
"uuid 1.17.0",
]
[[package]]
name = "zisk-distributed-grpc-api"
version = "0.15.0"
source = "git+https://github.com/han0110/zisk?branch=patch%2Fv0.15.0-cluster#6ffe5f4123a258765cf814c5ff3d3bb8988fb964"
dependencies = [
"anyhow",
"chrono",
"prost 0.14.3",
"prost-types 0.14.3",
"serde",
"serde_json",
"tonic 0.14.3",
"tonic-prost",
"tonic-prost-build",
"tracing",
"uuid 1.17.0",
"zisk-distributed-common",
]
[[package]]
name = "ziskos"
version = "0.15.0"
@@ -16838,7 +17389,7 @@ dependencies = [
"thiserror 1.0.69",
"tokio",
"tonic 0.8.3",
"tonic-build",
"tonic-build 0.8.4",
"tracing",
"twirp-rs",
"uuid 1.17.0",
@@ -16885,7 +17436,7 @@ dependencies = [
"serde",
"strum 0.26.3",
"strum_macros 0.26.4",
"sysinfo",
"sysinfo 0.30.13",
"tracing",
"tracing-forest",
"tracing-subscriber 0.3.20",

View File

@@ -49,7 +49,7 @@ anyhow = "1.0.98"
auto_impl = "1.3.0"
bincode = { version = "2.0.1", default-features = false }
blake3 = "1.8.2"
bytemuck = "1.24.0"
bytemuck = "1.25.0"
cargo_metadata = "0.19.0"
ciborium = { version = "0.2.2", default-features = false }
ciborium-io = { version = "0.2.2", default-features = false }
@@ -58,7 +58,10 @@ dashmap = "6.1.0"
digest = { version = "0.10.7", default-features = false }
eyre = "0.6.12"
fnv = { version = "1.0.7", default-features = false }
futures-util = "0.3"
http = "1"
indexmap = "2.10.0"
parking_lot = "0.12.5"
paste = "1.0.15"
postcard = { version = "1.0.8", default-features = false }
prost = "0.13"
@@ -68,7 +71,6 @@ rkyv = { version = "0.8.12", default-features = false }
serde = { version = "1.0.219", default-features = false }
serde_bytes = { version = "0.11.19", default-features = false }
serde_json = "1.0.142"
serde-untagged = "0.1"
serde_yaml = "0.9.34"
sha2 = { version = "0.10.9", default-features = false }
strum = "0.27.2"
@@ -76,11 +78,13 @@ tempfile = "3.20.0"
thiserror = "2.0.12"
tokio = "1.0"
toml = "0.8.23"
tonic = "0.14"
tower-http = "0.6.6"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
twirp = "0.9.1"
twirp-build = "0.9.0"
uuid = "1"
wait-timeout = "0.2.1"
# Airbender dependencies
@@ -140,6 +144,8 @@ zkm-zkvm = { git = "https://github.com/ProjectZKM/Ziren.git", tag = "v1.2.3", de
# ZisK dependencies
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", tag = "v0.15.0" }
zisk-distributed-grpc-api = { git = "https://github.com/han0110/zisk", branch = "patch/v0.15.0-cluster" }
zisk-proofman-verifier = { git = "https://github.com/0xPolygonHermez/pil2-proofman", package = "proofman-verifier", tag = "v0.15.0" }
# Local dependencies
ere-zkvm-interface = { path = "crates/zkvm-interface" }

View File

@@ -184,7 +184,7 @@ OutputHashedPlatform::<OpenVMPlatform, Sha256>::write_whole_output(&large_output
| Risc0 | [`3.0.4`](https://github.com/risc0/risc0/tree/v3.0.4) | Yes |
| SP1 | [`5.2.4`](https://github.com/succinctlabs/sp1/tree/v5.2.4) | Yes |
| Ziren | [`1.2.3`](https://github.com/ProjectZKM/Ziren/tree/v1.2.3) | No |
| Zisk | [`0.13.0`](https://github.com/0xPolygonHermez/zisk/tree/v0.13.0) | Yes |
| Zisk | [`0.15.0`](https://github.com/0xPolygonHermez/zisk/tree/v0.15.0) | Yes |
## Examples
@@ -273,7 +273,7 @@ ere-sp1 = { git = "https://github.com/eth-act/ere.git" }
use ere_sp1::{compiler::RustRv32imaCustomized, zkvm::EreSP1};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::path::Path;
@@ -285,7 +285,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let program = compiler.compile(guest_directory)?;
// Create zkVM instance (setup/preprocessing happens here)
let zkvm = EreSP1::new(program, ProverResourceType::Cpu)?;
let zkvm = EreSP1::new(program, ProverResource::Cpu)?;
// Prepare input
// Use `with_prefixed_stdin` when guest uses `Platform::read_whole_input()`
@@ -341,7 +341,7 @@ ere-dockerized = { git = "https://github.com/eth-act/ere.git" }
use ere_dockerized::{CompilerKind, DockerizedCompiler, DockerizedzkVM, zkVMKind};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::path::Path;
@@ -355,7 +355,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create zkVM instance (builds Docker images if needed)
// It spawns a container that runs a gRPC server handling zkVM operations
let zkvm = DockerizedzkVM::new(zkVMKind::SP1, program, ProverResourceType::Cpu)?;
let zkvm = DockerizedzkVM::new(zkVMKind::SP1, program, ProverResource::Cpu)?;
// Prepare input
// Use `with_prefixed_stdin` when guest uses `Platform::read_whole_input()`

View File

@@ -1,7 +1,7 @@
use anyhow::{Context, Error};
use clap::Parser;
use ere_server::server::{router, zkVMServer};
use ere_zkvm_interface::zkvm::{ProverResourceType, zkVM};
use ere_zkvm_interface::zkvm::{ProverResource, zkVM};
use std::{
io::{self, Read},
net::{Ipv4Addr, SocketAddr},
@@ -48,7 +48,7 @@ struct Args {
program_path: Option<String>,
/// Prover resource type.
#[command(subcommand)]
resource: ProverResourceType,
resource: ProverResource,
}
#[tokio::main]
@@ -120,7 +120,7 @@ async fn shutdown_signal() {
}
}
fn construct_zkvm(program: Vec<u8>, resource: ProverResourceType) -> Result<impl zkVM, Error> {
fn construct_zkvm(program: Vec<u8>, resource: ProverResource) -> Result<impl zkVM, Error> {
let (program, _) = bincode::serde::decode_from_slice(&program, bincode::config::legacy())
.with_context(|| "Failed to deserialize program")?;

View File

@@ -16,7 +16,7 @@
//! 4. `ere-server-{zkvm}:{version}` - Server image with the `ere-server` binary
//! built with the selected zkVM feature
//!
//! When [`ProverResourceType::Gpu`] is selected, the image with GPU support
//! When [`ProverResource::Gpu`] is selected, the image with GPU support
//! will be built and tagged with specific suffix.
//!
//! To force rebuild all images, set the environment variable
@@ -29,7 +29,7 @@
//! use ere_dockerized::{CompilerKind, DockerizedCompiler, DockerizedzkVM, zkVMKind};
//! use ere_zkvm_interface::{
//! compiler::Compiler,
//! zkvm::{Input, ProofKind, ProverResourceType, zkVM},
//! zkvm::{Input, ProofKind, ProverResource, zkVM},
//! };
//! use std::path::Path;
//!
@@ -45,7 +45,7 @@
//! let program = compiler.compile(&guest_path)?;
//!
//! // Create zkVM instance
//! let resource = ProverResourceType::Cpu;
//! let resource = ProverResource::Cpu;
//! let zkvm = DockerizedzkVM::new(zkvm_kind, program, resource)?;
//!
//! // Serialize input

View File

@@ -22,7 +22,7 @@ use ere_server::{
use ere_zkvm_interface::{
CommonError,
zkvm::{
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResource,
PublicValues, zkVM,
},
};
@@ -47,7 +47,7 @@ pub use error::Error;
/// 3. `ere-server-{zkvm}:{version}` - Server image with the `ere-server` binary
/// built with the selected zkVM feature
///
/// When [`ProverResourceType::Gpu`] is selected, the image with GPU support
/// When [`ProverResource::Gpu`] is selected, the image with GPU support
/// will be built and tagged with specific suffix.
///
/// Images are cached and only rebuilt if they don't exist or if the
@@ -158,12 +158,12 @@ impl ServerContainer {
fn new(
zkvm_kind: zkVMKind,
program: &SerializedProgram,
resource: &ProverResourceType,
resource: &ProverResource,
) -> Result<Self, Error> {
let port = Self::PORT_OFFSET + zkvm_kind as u16;
let name = format!("ere-server-{zkvm_kind}");
let gpu = matches!(resource, ProverResourceType::Gpu);
let gpu = resource.is_gpu();
let mut cmd = DockerRunCmd::new(server_zkvm_image(zkvm_kind, gpu))
.rm()
.inherit_env("RUST_LOG")
@@ -181,23 +181,25 @@ impl ServerContainer {
// zkVM specific options
cmd = match zkvm_kind {
zkVMKind::Risc0 => cmd
.inherit_env("RISC0_SEGMENT_PO2")
.inherit_env("RISC0_KECCAK_PO2"),
.inherit_env("ERE_RISC0_SEGMENT_PO2")
.inherit_env("ERE_RISC0_KECCAK_PO2"),
// ZisK uses shared memory to exchange data between processes, it
// requires at least 16G shared memory, here we set 32G for safety.
zkVMKind::Zisk => cmd
.option("shm-size", "32G")
.option("ulimit", "memlock=-1:-1")
.inherit_env("ZISK_PORT")
.inherit_env("ZISK_CHUNK_SIZE_BITS")
.inherit_env("ZISK_UNLOCK_MAPPED_MEMORY")
.inherit_env("ZISK_MINIMAL_MEMORY")
.inherit_env("ZISK_PREALLOCATE")
.inherit_env("ZISK_SHARED_TABLES")
.inherit_env("ZISK_MAX_STREAMS")
.inherit_env("ZISK_NUMBER_THREADS_WITNESS")
.inherit_env("ZISK_MAX_WITNESS_STORED")
.inherit_env("ZISK_PROVE_TIMEOUT_SEC"),
.inherit_env("ERE_ZISK_SETUP_ON_INIT")
.inherit_env("ERE_ZISK_PORT")
.inherit_env("ERE_ZISK_UNLOCK_MAPPED_MEMORY")
.inherit_env("ERE_ZISK_MINIMAL_MEMORY")
.inherit_env("ERE_ZISK_PREALLOCATE")
.inherit_env("ERE_ZISK_SHARED_TABLES")
.inherit_env("ERE_ZISK_MAX_STREAMS")
.inherit_env("ERE_ZISK_NUMBER_THREADS_WITNESS")
.inherit_env("ERE_ZISK_MAX_WITNESS_STORED")
.inherit_env("ERE_ZISK_START_SERVER_TIMEOUT_SEC")
.inherit_env("ERE_ZISK_SHUTDOWN_SERVER_TIMEOUT_SEC")
.inherit_env("ERE_ZISK_PROVE_TIMEOUT_SEC"),
_ => cmd,
};
@@ -272,7 +274,7 @@ impl ServerContainer {
pub struct DockerizedzkVM {
zkvm_kind: zkVMKind,
program: SerializedProgram,
resource: ProverResourceType,
resource: ProverResource,
container: RwLock<Option<ServerContainer>>,
}
@@ -280,9 +282,9 @@ impl DockerizedzkVM {
pub fn new(
zkvm_kind: zkVMKind,
program: SerializedProgram,
resource: ProverResourceType,
resource: ProverResource,
) -> Result<Self, Error> {
build_server_image(zkvm_kind, matches!(resource, ProverResourceType::Gpu))?;
build_server_image(zkvm_kind, resource.is_gpu())?;
let container = ServerContainer::new(zkvm_kind, &program, &resource)?;
@@ -302,7 +304,7 @@ impl DockerizedzkVM {
&self.program
}
pub fn resource(&self) -> &ProverResourceType {
pub fn resource(&self) -> &ProverResource {
&self.resource
}
@@ -424,14 +426,15 @@ async fn wait_until_healthy(endpoint: &Url, http_client: Client) -> Result<(), E
}
fn block_on<T>(future: impl Future<Output = T>) -> T {
static FALLBACK_RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
let handle = tokio::runtime::Handle::try_current().unwrap_or_else(|_| {
FALLBACK_RT
.get_or_init(|| tokio::runtime::Runtime::new().expect("Failed to create runtime"))
.handle()
.clone()
});
tokio::task::block_in_place(|| handle.block_on(future))
match tokio::runtime::Handle::try_current() {
Ok(handle) => tokio::task::block_in_place(|| handle.block_on(future)),
Err(_) => {
static FALLBACK_RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
FALLBACK_RT
.get_or_init(|| tokio::runtime::Runtime::new().expect("Failed to create runtime"))
.block_on(future)
}
}
}
#[cfg(test)]
@@ -445,7 +448,7 @@ mod test {
use ere_test_utils::{
host::*, io::serde::bincode::BincodeLegacy, program::basic::BasicProgram,
};
use ere_zkvm_interface::zkvm::{Input, ProofKind, ProverResourceType, zkVM};
use ere_zkvm_interface::zkvm::{Input, ProofKind, ProverResource, zkVM};
fn zkvm(
zkvm_kind: zkVMKind,
@@ -453,7 +456,7 @@ mod test {
program: &'static str,
) -> DockerizedzkVM {
let program = compile(zkvm_kind, compiler_kind, program).clone();
DockerizedzkVM::new(zkvm_kind, program, ProverResourceType::Cpu).unwrap()
DockerizedzkVM::new(zkvm_kind, program, ProverResource::Cpu).unwrap()
}
macro_rules! test {

View File

@@ -11,7 +11,6 @@ auto_impl.workspace = true
bincode = { workspace = true, features = ["alloc", "serde"] }
indexmap = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
serde-untagged.workspace = true
strum = { workspace = true, features = ["derive"] }
thiserror.workspace = true

View File

@@ -11,7 +11,7 @@ mod resource;
pub use error::CommonError;
pub use proof::{Proof, ProofKind};
pub use report::{ProgramExecutionReport, ProgramProvingReport};
pub use resource::{NetworkProverConfig, ProverResourceType};
pub use resource::{ProverResource, ProverResourceKind, RemoteProverConfig};
/// Input for the prover to execute/prove a guest program.
#[derive(Clone, Debug, Default)]

View File

@@ -1,4 +1,4 @@
use crate::zkvm::ProofKind;
use crate::zkvm::{ProofKind, resource::ProverResourceKind};
use std::{
io,
path::Path,
@@ -51,6 +51,12 @@ pub enum CommonError {
#[error("Unsupported input: {0}")]
UnsupportedInput(String),
#[error("Unsupported prover resource kind {unsupported:?}, expect one of {supported:?}")]
UnsupportedProverResourceKind {
unsupported: ProverResourceKind,
supported: Vec<ProverResourceKind>,
},
#[error("Unsupported proof kind {unsupported:?}, expect one of {supported:?}")]
UnsupportedProofKind {
unsupported: ProofKind,
@@ -141,6 +147,16 @@ impl CommonError {
Self::UnsupportedInput(reason.as_ref().to_string())
}
pub fn unsupported_prover_resource_kind(
unsupported: ProverResourceKind,
supported: impl IntoIterator<Item = ProverResourceKind>,
) -> Self {
Self::UnsupportedProverResourceKind {
unsupported,
supported: supported.into_iter().collect(),
}
}
pub fn unsupported_proof_kind(
unsupported: ProofKind,
supported: impl IntoIterator<Item = ProofKind>,

View File

@@ -1,11 +1,11 @@
use serde::{Deserialize, Deserializer, Serialize, de::Unexpected};
use serde_untagged::UntaggedEnumVisitor;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumDiscriminants, EnumIs, EnumIter, EnumString};
/// Configuration for network-based proving
/// Configuration for remote proving
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "clap", derive(clap::Args))]
pub struct NetworkProverConfig {
/// The endpoint URL of the prover network service
pub struct RemoteProverConfig {
/// The endpoint URL of the remote prover
#[cfg_attr(feature = "clap", arg(long))]
pub endpoint: String,
/// Optional API key for authentication
@@ -14,7 +14,7 @@ pub struct NetworkProverConfig {
}
#[cfg(feature = "clap")]
impl NetworkProverConfig {
impl RemoteProverConfig {
pub fn to_args(&self) -> Vec<&str> {
core::iter::once(["--endpoint", self.endpoint.as_str()])
.chain(self.api_key.as_deref().map(|val| ["--api-key", val]))
@@ -24,18 +24,35 @@ impl NetworkProverConfig {
}
/// ResourceType specifies what resource will be used to create the proofs.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, EnumDiscriminants, EnumIs,
)]
#[strum_discriminants(
name(ProverResourceKind),
derive(Display, EnumString, EnumIter, Hash),
strum(serialize_all = "lowercase")
)]
#[cfg_attr(feature = "clap", derive(clap::Subcommand))]
pub enum ProverResourceType {
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum ProverResource {
#[default]
Cpu,
Gpu,
/// Use a remote prover network
Network(NetworkProverConfig),
/// Official proving network
Network(RemoteProverConfig),
/// Self-hosted proving cluster
Cluster(RemoteProverConfig),
}
impl ProverResource {
/// Returns [`ProverResourceKind`].
pub fn kind(&self) -> ProverResourceKind {
self.into()
}
}
#[cfg(feature = "clap")]
impl ProverResourceType {
impl ProverResource {
pub fn to_args(&self) -> Vec<&str> {
match self {
Self::Cpu => vec!["cpu"],
@@ -43,51 +60,22 @@ impl ProverResourceType {
Self::Network(config) => core::iter::once("network")
.chain(config.to_args())
.collect(),
Self::Cluster(config) => core::iter::once("cluster")
.chain(config.to_args())
.collect(),
}
}
}
impl Serialize for ProverResourceType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Cpu => "cpu".serialize(serializer),
Self::Gpu => "gpu".serialize(serializer),
Self::Network(config) => config.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for ProverResourceType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.string(|resource| match resource {
"cpu" => Ok(Self::Cpu),
"gpu" => Ok(Self::Gpu),
_ => Err(serde::de::Error::invalid_value(
Unexpected::Str(resource),
&r#""cpu" or "gpu""#,
)),
})
.map(|map| map.deserialize().map(Self::Network))
.deserialize(deserializer)
}
}
#[cfg(test)]
mod test {
use crate::zkvm::resource::ProverResourceType;
use crate::zkvm::resource::ProverResource;
use core::fmt::Debug;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Config {
resources: Vec<ProverResourceType>,
resources: Vec<ProverResource>,
}
fn test_round_trip<'de, SE: Debug, DE: Debug>(
@@ -101,7 +89,20 @@ mod test {
#[test]
fn test_round_trip_toml() {
const TOML: &str = r#"
resources = ["cpu", "gpu", { endpoint = "http://localhost:3000" }]
[[resources]]
kind = "cpu"
[[resources]]
kind = "gpu"
[[resources]]
kind = "network"
endpoint = "http://localhost:3000"
api_key = "my_api_key"
[[resources]]
kind = "cluster"
endpoint = "http://localhost:3000"
"#;
test_round_trip(TOML, toml::to_string, toml::from_str);
}
@@ -110,9 +111,13 @@ resources = ["cpu", "gpu", { endpoint = "http://localhost:3000" }]
fn test_round_trip_yaml() {
const YAML: &str = r#"
resources:
- cpu
- gpu
- endpoint: http://localhost:3000
- kind: cpu
- kind: gpu
- kind: network
endpoint: http://localhost:3000
api_key: my_api_key
- kind: cluster
endpoint: http://localhost:3000
api_key: null
"#;
test_round_trip(YAML, serde_yaml::to_string, serde_yaml::from_str);
@@ -123,10 +128,20 @@ resources:
const JSON: &str = r#"
{
"resources": [
"cpu",
"gpu",
{
"endpoint": "",
"kind": "cpu"
},
{
"kind": "gpu"
},
{
"kind": "network",
"endpoint": "http://localhost:3000",
"api_key": "my_api_key"
},
{
"kind": "cluster",
"endpoint": "http://localhost:3000",
"api_key": null
}
]

View File

@@ -33,13 +33,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//! | `Cluster` | No |
//!
//! [`install_airbender_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_airbender_sdk.sh

View File

@@ -1,9 +1,12 @@
use crate::{program::AirbenderProgram, zkvm::sdk::AirbenderSdk};
use airbender_execution_utils::ProgramProof;
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
use ere_zkvm_interface::{
ProverResourceKind,
zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResource, PublicValues, zkVM, zkVMProgramDigest,
},
};
use std::time::Instant;
@@ -20,9 +23,14 @@ pub struct EreAirbender {
}
impl EreAirbender {
pub fn new(program: AirbenderProgram, resource: ProverResourceType) -> Result<Self, Error> {
let gpu = matches!(resource, ProverResourceType::Gpu);
let sdk = AirbenderSdk::new(program.bin(), gpu);
pub fn new(program: AirbenderProgram, resource: ProverResource) -> Result<Self, Error> {
if !matches!(resource, ProverResource::Cpu | ProverResource::Gpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu, ProverResourceKind::Gpu],
))?;
}
let sdk = AirbenderSdk::new(program.bin(), resource.is_gpu());
Ok(Self { sdk })
}
}
@@ -30,7 +38,9 @@ impl EreAirbender {
impl zkVM for EreAirbender {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let start = Instant::now();
@@ -53,13 +63,15 @@ impl zkVM for EreAirbender {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let start = Instant::now();
let (public_values, proof) = self.sdk.prove(input.stdin())?;
@@ -77,10 +89,10 @@ impl zkVM for EreAirbender {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
let (proof, _): (ProgramProof, _) =
@@ -119,7 +131,7 @@ mod tests {
};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::sync::OnceLock;
@@ -137,7 +149,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreAirbender::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case().into_output_sha256();
run_zkvm_execute(&zkvm, &test_case);
@@ -146,7 +158,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreAirbender::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -159,7 +171,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreAirbender::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case().into_output_sha256();
run_zkvm_prove(&zkvm, &test_case);
@@ -168,7 +180,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreAirbender::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),

View File

@@ -62,7 +62,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProverResourceType, zkVM},
zkvm::{ProverResource, zkVM},
};
#[test]
@@ -76,7 +76,7 @@ mod tests {
fn test_execute() {
let guest_directory = testing_guest_directory("jolt", "stock_nightly_no_std");
let program = RustRv64imac.compile(&guest_directory).unwrap();
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreJolt::new(program, ProverResource::Cpu).unwrap();
zkvm.execute(&Input::new()).unwrap();
}

View File

@@ -21,13 +21,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//! | `Cluster` | No |
//!
//! [`install_jolt_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_jolt_sdk.sh

View File

@@ -5,7 +5,7 @@ use crate::{
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM,
ProverResource, ProverResourceKind, PublicValues, zkVM,
};
use jolt_ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use std::{env, io::Cursor, time::Instant};
@@ -22,9 +22,12 @@ pub struct EreJolt {
}
impl EreJolt {
pub fn new(program: JoltProgram, resource: ProverResourceType) -> Result<Self, Error> {
if !matches!(resource, ProverResourceType::Cpu) {
panic!("Network or GPU proving not yet implemented for Miden. Use CPU resource type.");
pub fn new(program: JoltProgram, resource: ProverResource) -> Result<Self, Error> {
if !matches!(resource, ProverResource::Cpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu],
))?;
}
let sdk = JoltSdk::new(program.elf(), JoltConfig::from_env());
Ok(EreJolt { sdk })
@@ -34,7 +37,9 @@ impl EreJolt {
impl zkVM for EreJolt {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let start = Instant::now();
@@ -57,13 +62,15 @@ impl zkVM for EreJolt {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let start = Instant::now();
@@ -84,10 +91,10 @@ impl zkVM for EreJolt {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
let proof = JoltProof::deserialize_compressed(&mut Cursor::new(proof))
@@ -118,7 +125,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProofKind, ProverResourceType, zkVM},
zkvm::{ProofKind, ProverResource, zkVM},
};
use std::sync::{Mutex, OnceLock};
@@ -141,7 +148,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreJolt::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -150,7 +157,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreJolt::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -165,7 +172,7 @@ mod tests {
let _guard = PROVE_LOCK.lock().unwrap();
let program = basic_program();
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreJolt::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
@@ -176,7 +183,7 @@ mod tests {
let _guard = PROVE_LOCK.lock().unwrap();
let program = basic_program();
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreJolt::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),

View File

@@ -10,13 +10,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//! | `Cluster` | No |
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),

View File

@@ -2,7 +2,7 @@ use crate::program::{MidenProgram, MidenProgramInfo, MidenSerdeWrapper};
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, ProverResourceKind, PublicValues, zkVM, zkVMProgramDigest,
};
use miden_core::{
Program,
@@ -38,9 +38,12 @@ pub struct EreMiden {
}
impl EreMiden {
pub fn new(program: MidenProgram, resource: ProverResourceType) -> Result<Self, Error> {
if !matches!(resource, ProverResourceType::Cpu) {
panic!("Network or GPU proving not yet implemented for Miden. Use CPU resource type.");
pub fn new(program: MidenProgram, resource: ProverResource) -> Result<Self, Error> {
if !matches!(resource, ProverResource::Cpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu],
))?;
}
Ok(Self { program: program.0 })
}
@@ -58,7 +61,9 @@ impl EreMiden {
impl zkVM for EreMiden {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let stack_inputs = StackInputs::default();
@@ -92,13 +97,15 @@ impl zkVM for EreMiden {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let stack_inputs = StackInputs::default();
@@ -129,10 +136,10 @@ impl zkVM for EreMiden {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
let program_info: ProgramInfo = self.program.clone().into();
@@ -200,7 +207,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProofKind, ProverResourceType, zkVM},
zkvm::{ProofKind, ProverResource, zkVM},
};
fn load_miden_program(guest_name: &str) -> MidenProgram {
@@ -212,7 +219,7 @@ mod tests {
#[test]
fn test_prove_and_verify_add() {
let program = load_miden_program("add");
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreMiden::new(program, ProverResource::Cpu).unwrap();
let const_a = -Felt::ONE;
let const_b = Felt::ONE / Felt::ONE.double();
@@ -237,7 +244,7 @@ mod tests {
#[test]
fn test_prove_and_verify_fib() {
let program = load_miden_program("fib");
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreMiden::new(program, ProverResource::Cpu).unwrap();
let n_iterations = 50u32;
let expected_fib = Felt::try_from(12_586_269_025u64).unwrap();
@@ -261,7 +268,7 @@ mod tests {
#[test]
fn test_invalid_test_case() {
let program = load_miden_program("add");
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreMiden::new(program, ProverResource::Cpu).unwrap();
let empty_inputs = Input::new();
assert!(zkvm.execute(&empty_inputs).is_err());

View File

@@ -10,13 +10,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//! | `Cluster` | No |
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),

View File

@@ -2,7 +2,7 @@ use crate::program::NexusProgram;
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM,
ProverResource, ProverResourceKind, PublicValues, zkVM,
};
use nexus_core::nvm::{self, ElfFile, internals::LinearMemoryLayout};
use nexus_sdk::{CheckedView, KnownExitCodes, Viewable};
@@ -42,17 +42,20 @@ pub struct EreNexus {
}
impl EreNexus {
pub fn new(program: NexusProgram, resource: ProverResourceType) -> Result<Self, Error> {
pub fn new(program: NexusProgram, resource: ProverResource) -> Result<Self, Error> {
Self::with_extensions(program, resource, vec![])
}
pub fn with_extensions(
program: NexusProgram,
resource: ProverResourceType,
resource: ProverResource,
extensions: Vec<NexusExtension>,
) -> Result<Self, Error> {
if !matches!(resource, ProverResourceType::Cpu) {
panic!("Network or GPU proving not yet implemented for Nexus. Use CPU resource type.");
if !matches!(resource, ProverResource::Cpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu],
))?;
}
Ok(Self {
@@ -65,7 +68,9 @@ impl EreNexus {
impl zkVM for EreNexus {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let elf = ElfFile::from_bytes(self.program.elf()).map_err(Error::ParseElf)?;
@@ -102,13 +107,15 @@ impl zkVM for EreNexus {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let elf = ElfFile::from_bytes(self.program.elf()).map_err(Error::ParseElf)?;
@@ -155,10 +162,10 @@ impl zkVM for EreNexus {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
info!("Verifying proof...");
@@ -244,7 +251,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProofKind, ProverResourceType, zkVM},
zkvm::{ProofKind, ProverResource, zkVM},
};
use std::sync::OnceLock;
@@ -262,7 +269,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreNexus::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -271,7 +278,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreNexus::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -284,7 +291,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreNexus::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
@@ -293,7 +300,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreNexus::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),

View File

@@ -63,7 +63,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProverResourceType, zkVM},
zkvm::{ProverResource, zkVM},
};
#[test]
@@ -77,7 +77,7 @@ mod tests {
fn test_execute() {
let guest_directory = testing_guest_directory("openvm", "stock_nightly_no_std");
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreOpenVM::new(program, ProverResource::Cpu).unwrap();
zkvm.execute(&Input::new()).unwrap();
}

View File

@@ -25,13 +25,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//! | `Cluster` | No |
//!
//! [`install_openvm_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_openvm_sdk.sh

View File

@@ -2,7 +2,7 @@ use crate::program::OpenVMProgram;
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, ProverResourceKind, PublicValues, zkVM, zkVMProgramDigest,
};
use openvm_circuit::arch::instructions::exe::VmExe;
use openvm_continuations::verifier::internal::types::VmStarkProof;
@@ -31,22 +31,21 @@ pub struct EreOpenVM {
agg_pk: AggProvingKey,
agg_vk: AggVerifyingKey,
app_commit: AppExecutionCommit,
resource: ProverResourceType,
resource: ProverResource,
}
impl EreOpenVM {
pub fn new(program: OpenVMProgram, resource: ProverResourceType) -> Result<Self, Error> {
match resource {
#[cfg(not(feature = "cuda"))]
ProverResourceType::Gpu => {
panic!("Feature `cuda` is disabled. Enable `cuda` to use GPU resource type")
}
ProverResourceType::Network(_) => {
panic!(
"Network proving not yet implemented for OpenVM. Use CPU or GPU resource type."
);
}
_ => {}
pub fn new(program: OpenVMProgram, resource: ProverResource) -> Result<Self, Error> {
#[cfg(not(feature = "cuda"))]
if resource.is_gpu() {
return Err(Error::CudaFeatureDisabled);
}
if !matches!(resource, ProverResource::Cpu | ProverResource::Gpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu, ProverResourceKind::Gpu],
))?;
}
let app_config = if let Some(value) = program.app_config() {
@@ -124,7 +123,9 @@ impl EreOpenVM {
impl zkVM for EreOpenVM {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let mut stdin = StdIn::default();
@@ -151,13 +152,15 @@ impl zkVM for EreOpenVM {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let mut stdin = StdIn::default();
@@ -165,18 +168,15 @@ impl zkVM for EreOpenVM {
let now = std::time::Instant::now();
let (proof, app_commit) = match self.resource {
ProverResourceType::Cpu => self.cpu_sdk()?.prove(self.app_exe.clone(), stdin),
ProverResource::Cpu => self.cpu_sdk()?.prove(self.app_exe.clone(), stdin),
#[cfg(feature = "cuda")]
ProverResourceType::Gpu => self.gpu_sdk()?.prove(self.app_exe.clone(), stdin),
ProverResource::Gpu => self.gpu_sdk()?.prove(self.app_exe.clone(), stdin),
#[cfg(not(feature = "cuda"))]
ProverResourceType::Gpu => {
panic!("Feature `cuda` is disabled. Enable `cuda` to use GPU resource type")
}
ProverResourceType::Network(_) => {
panic!(
"Network proving not yet implemented for OpenVM. Use CPU or GPU resource type."
);
}
ProverResource::Gpu => bail!(Error::CudaFeatureDisabled),
_ => bail!(Error::from(CommonError::unsupported_prover_resource_kind(
self.resource.kind(),
[ProverResourceKind::Cpu, ProverResourceKind::Gpu],
))),
}
.map_err(Error::Prove)?;
let elapsed = now.elapsed();
@@ -205,10 +205,10 @@ impl zkVM for EreOpenVM {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
let proof = VmStarkProof::<SC>::decode(&mut proof.as_slice())
@@ -265,7 +265,7 @@ mod tests {
};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::sync::OnceLock;
@@ -283,7 +283,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreOpenVM::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case().into_output_sha256();
run_zkvm_execute(&zkvm, &test_case);
@@ -292,7 +292,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreOpenVM::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -305,7 +305,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreOpenVM::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case().into_output_sha256();
run_zkvm_prove(&zkvm, &test_case);
@@ -314,7 +314,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreOpenVM::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),

View File

@@ -8,6 +8,9 @@ pub enum Error {
CommonError(#[from] CommonError),
// Common
#[error("Enable `cuda` feature to enable `ProverResource::Gpu`")]
CudaFeatureDisabled,
#[error("Invalid AppConfig: {0}")]
InvalidAppConfig(toml::de::Error),

View File

@@ -54,7 +54,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProverResourceType, zkVM},
zkvm::{ProverResource, zkVM},
};
#[test]
@@ -68,7 +68,7 @@ mod tests {
fn test_execute() {
let guest_directory = testing_guest_directory("pico", "stock_nightly_no_std");
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = ErePico::new(program, ProverResource::Cpu).unwrap();
zkvm.execute(&Input::new()).unwrap();
}

View File

@@ -20,13 +20,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//! | `Cluster` | No |
//!
//! [`install_pico_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_pico_sdk.sh

View File

@@ -5,7 +5,7 @@ use crate::{
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, ProverResourceKind, PublicValues, zkVM, zkVMProgramDigest,
};
use pico_p3_field::PrimeField32;
use pico_vm::emulator::stdin::EmulatorStdinBuilder;
@@ -31,9 +31,12 @@ pub struct ErePico {
}
impl ErePico {
pub fn new(program: PicoProgram, resource: ProverResourceType) -> Result<Self, Error> {
if !matches!(resource, ProverResourceType::Cpu) {
panic!("Network or GPU proving not yet implemented for Pico. Use CPU resource type.");
pub fn new(program: PicoProgram, resource: ProverResource) -> Result<Self, Error> {
if !matches!(resource, ProverResource::Cpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu],
))?;
}
Ok(ErePico { program })
}
@@ -46,7 +49,9 @@ impl ErePico {
impl zkVM for ErePico {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let mut stdin = EmulatorStdinBuilder::default();
@@ -77,13 +82,15 @@ impl zkVM for ErePico {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let mut stdin = EmulatorStdinBuilder::default();
@@ -116,10 +123,10 @@ impl zkVM for ErePico {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
let client = self.client();
@@ -195,7 +202,7 @@ mod tests {
};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::sync::OnceLock;
@@ -213,7 +220,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = ErePico::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -222,7 +229,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = ErePico::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -235,7 +242,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = ErePico::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
@@ -244,7 +251,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = ErePico::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),

View File

@@ -66,7 +66,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProverResourceType, zkVM},
zkvm::{ProverResource, zkVM},
};
#[test]
@@ -80,7 +80,7 @@ mod tests {
fn test_execute() {
let guest_directory = testing_guest_directory("risc0", "stock_nightly_no_std");
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreRisc0::new(program, ProverResource::Cpu).unwrap();
zkvm.execute(&Input::new()).unwrap();
}

View File

@@ -17,7 +17,7 @@
//!
//! - [`rzup`]
//! - Installation via `rzup install`
//! - `r0vm-cuda` - Used by `zkVM::prove` if `ProverResourceType::Gpu` is
//! - `r0vm-cuda` - Used by `zkVM::prove` if `ProverResource::Gpu` is
//! selected
//! - `docker` - Used by `zkVM::prove` if `ProofKind::Groth16` is selected
//!
@@ -32,13 +32,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//! | `Cluster` | No |
//!
//! [`install_risc0_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_risc0_sdk.sh
//! [`rzup`]: https://risczero.com/install

View File

@@ -2,7 +2,7 @@ use crate::program::Risc0Program;
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, ProverResourceKind, PublicValues, zkVM, zkVMProgramDigest,
};
use risc0_zkvm::{
AssumptionReceipt, DEFAULT_MAX_PO2, DefaultProver, Digest, ExecutorEnv, ExternalProver,
@@ -44,33 +44,41 @@ const KECCAK_PO2_RANGE: RangeInclusive<usize> = 14..=18;
pub struct EreRisc0 {
program: Risc0Program,
resource: ProverResourceType,
resource: ProverResource,
segment_po2: usize,
keccak_po2: usize,
}
impl EreRisc0 {
pub fn new(program: Risc0Program, resource: ProverResourceType) -> Result<Self, Error> {
if matches!(resource, ProverResourceType::Network(_)) {
panic!(
"Network proving not yet implemented for RISC Zero. Use CPU or GPU resource type."
);
pub fn new(program: Risc0Program, resource: ProverResource) -> Result<Self, Error> {
if !matches!(resource, ProverResource::Cpu | ProverResource::Gpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu, ProverResourceKind::Gpu],
))?;
}
let [segment_po2, keccak_po2] = [
("RISC0_SEGMENT_PO2", DEFAULT_SEGMENT_PO2, SEGMENT_PO2_RANGE),
("RISC0_KECCAK_PO2", DEFAULT_KECCAK_PO2, KECCAK_PO2_RANGE),
]
.map(|(key, default, range)| {
let val = env::var(key)
.ok()
.and_then(|po2| po2.parse::<usize>().ok())
.unwrap_or(default);
if !range.contains(&val) {
panic!("Unsupported po2 value {val} of {key}, expected in range {range:?}")
let parse_env = |key: &str, default: usize, range: RangeInclusive<usize>| {
let Ok(val) = env::var(key) else {
return Ok(default);
};
match val.parse() {
Ok(val) if range.contains(&val) => Ok(val),
_ => Err(Error::UnsupportedPo2Value {
key: key.to_string(),
val,
range,
}),
}
val
});
};
let segment_po2 = parse_env(
"ERE_RISC0_SEGMENT_PO2",
DEFAULT_SEGMENT_PO2,
SEGMENT_PO2_RANGE,
)?;
let keccak_po2 = parse_env("ERE_RISC0_KECCAK_PO2", DEFAULT_KECCAK_PO2, KECCAK_PO2_RANGE)?;
Ok(Self {
program,
@@ -112,8 +120,8 @@ impl zkVM for EreRisc0 {
let env = self.input_to_env(input)?;
let prover = match self.resource {
ProverResourceType::Cpu => Rc::new(ExternalProver::new("ipc", "r0vm")),
ProverResourceType::Gpu => {
ProverResource::Cpu => Rc::new(ExternalProver::new("ipc", "r0vm")),
ProverResource::Gpu => {
if cfg!(feature = "metal") {
// When `metal` is enabled, we use the `LocalProver` to do
// proving. but it's not public so we use `default_prover`
@@ -127,11 +135,10 @@ impl zkVM for EreRisc0 {
Rc::new(DefaultProver::new("r0vm-cuda").map_err(Error::InitializeCudaProver)?)
}
}
ProverResourceType::Network(_) => {
panic!(
"Network proving not yet implemented for RISC Zero. Use CPU or GPU resource type."
);
}
_ => bail!(Error::from(CommonError::unsupported_prover_resource_kind(
self.resource.kind(),
[ProverResourceKind::Cpu, ProverResourceKind::Gpu],
))),
};
let opts = match proof_kind {
@@ -234,7 +241,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProofKind, ProverResourceType, zkVM},
zkvm::{ProofKind, ProverResource, zkVM},
};
use std::sync::OnceLock;
@@ -252,7 +259,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreRisc0::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -261,7 +268,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreRisc0::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -274,7 +281,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreRisc0::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
@@ -283,7 +290,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreRisc0::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -300,7 +307,7 @@ mod tests {
.unwrap();
for i in 1..=16_u32 {
let zkvm = EreRisc0::new(program.clone(), ProverResourceType::Cpu).unwrap();
let zkvm = EreRisc0::new(program.clone(), ProverResource::Cpu).unwrap();
let input = Input::new().with_stdin(i.to_le_bytes().to_vec());

View File

@@ -1,12 +1,23 @@
use ere_zkvm_interface::zkvm::ProofKind;
use core::ops::RangeInclusive;
use ere_zkvm_interface::zkvm::{CommonError, ProofKind};
use risc0_zkp::verify::VerificationError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
CommonError(#[from] CommonError),
#[error("Deserialize proofs in Input failed: {0:?}")]
DeserializeInputProofs(bincode::error::DecodeError),
#[error("Unsupported power of 2 value {val} of {key}, expected in range {range:?}")]
UnsupportedPo2Value {
key: String,
val: String,
range: RangeInclusive<usize>,
},
// Execute
#[error("Failed to build `ExecutorEnv`: {0}")]
BuildExecutorEnv(anyhow::Error),

View File

@@ -54,7 +54,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{ProverResourceType, zkVM},
zkvm::{ProverResource, zkVM},
};
#[test]
@@ -68,7 +68,7 @@ mod tests {
fn test_execute() {
let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std");
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreSP1::new(program, ProverResource::Cpu).unwrap();
zkvm.execute(&Input::new()).unwrap();
}

View File

@@ -12,7 +12,7 @@
//!
//! ## `zkVM` requirements
//!
//! - `docker` - Used by `zkVM::prove` if `ProverResourceType::Gpu` is selected
//! - `docker` - Used by `zkVM::prove` if `ProverResource::Gpu` is selected
//!
//! # `Compiler` implementation
//!
@@ -25,13 +25,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | Yes |
//! | `Cluster` | No |
//!
//! [`install_sp1_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_sp1_sdk.sh
//! [`sp1up`]: https://sp1up.succinct.xyz

View File

@@ -2,7 +2,7 @@ use crate::{program::SP1Program, zkvm::sdk::Prover};
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, PublicValues, zkVM, zkVMProgramDigest,
};
use sp1_sdk::{SP1ProofMode, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey};
use std::{
@@ -23,7 +23,7 @@ include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
pub struct EreSP1 {
program: SP1Program,
/// Prover resource configuration for creating clients
resource: ProverResourceType,
resource: ProverResource,
/// Proving key
pk: SP1ProvingKey,
/// Verification key
@@ -39,7 +39,7 @@ pub struct EreSP1 {
}
impl EreSP1 {
pub fn new(program: SP1Program, resource: ProverResourceType) -> Result<Self, Error> {
pub fn new(program: SP1Program, resource: ProverResource) -> Result<Self, Error> {
let prover = Prover::new(&resource)?;
let (pk, vk) = prover.setup(&program.elf)?;
Ok(Self {
@@ -97,7 +97,7 @@ impl zkVM for EreSP1 {
let mut prover = self.prover_mut()?;
// Restart GPU prover if the prover is dropped before.
if matches!(self.resource, ProverResourceType::Gpu) && matches!(&*prover, Prover::Cpu(_)) {
if matches!(self.resource, ProverResource::Gpu) && matches!(&*prover, Prover::Cpu(_)) {
*prover = Prover::new(&self.resource).and_then(|prover| {
prover.setup(&self.program.elf)?;
Ok(prover)
@@ -107,7 +107,7 @@ impl zkVM for EreSP1 {
let start = Instant::now();
let proof =
panic::catch_unwind(|| prover.prove(&self.pk, &stdin, mode)).map_err(|err| {
if matches!(self.resource, ProverResourceType::Gpu) {
if matches!(self.resource, ProverResource::Gpu) {
// Drop the panicked GPU prover and replace it with CPU one,
// next prove call will try to restart it.
take(&mut *prover);
@@ -201,7 +201,7 @@ mod tests {
use ere_zkvm_interface::{
Input,
compiler::Compiler,
zkvm::{NetworkProverConfig, ProofKind, ProverResourceType, zkVM},
zkvm::{ProofKind, ProverResource, RemoteProverConfig, zkVM},
};
use std::sync::OnceLock;
@@ -219,7 +219,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreSP1::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -228,7 +228,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreSP1::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -241,7 +241,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreSP1::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
@@ -250,7 +250,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreSP1::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -269,13 +269,13 @@ mod tests {
return;
}
// Create a network prover configuration
let network_config = NetworkProverConfig {
// Create a remote prover configuration
let config = RemoteProverConfig {
endpoint: std::env::var("NETWORK_RPC_URL").unwrap_or_default(),
api_key: std::env::var("NETWORK_PRIVATE_KEY").ok(),
};
let program = basic_program();
let zkvm = EreSP1::new(program, ProverResourceType::Network(network_config)).unwrap();
let zkvm = EreSP1::new(program, ProverResource::Network(config)).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);

View File

@@ -19,6 +19,9 @@ pub enum Error {
#[error("Deserialize proofs in Input failed: {0:?}")]
DeserializeInputProofs(bincode::error::DecodeError),
#[error("Missing `api_key` in `RemoteProverConfig`")]
MissingApiKey,
// Execute
#[error("SP1 execution failed: {0}")]
Execute(#[source] anyhow::Error),

View File

@@ -1,7 +1,7 @@
use crate::zkvm::{Error, panic_msg};
use ere_zkvm_interface::{
CommonError,
zkvm::{NetworkProverConfig, ProverResourceType},
CommonError, RemoteProverConfig,
zkvm::{ProverResource, ProverResourceKind},
};
use sp1_sdk::{
CpuProver, NetworkProver, Prover as _, ProverClient, SP1ProofMode, SP1ProofWithPublicValues,
@@ -27,16 +27,24 @@ pub enum Prover {
impl Default for Prover {
fn default() -> Self {
Self::new(&ProverResourceType::Cpu).unwrap()
Self::new(&ProverResource::Cpu).unwrap()
}
}
impl Prover {
pub fn new(resource: &ProverResourceType) -> Result<Self, Error> {
pub fn new(resource: &ProverResource) -> Result<Self, Error> {
Ok(match resource {
ProverResourceType::Cpu => Self::Cpu(ProverClient::builder().cpu().build()),
ProverResourceType::Gpu => Self::Gpu(CudaProver::new()?),
ProverResourceType::Network(config) => Self::Network(build_network_prover(config)),
ProverResource::Cpu => Self::Cpu(ProverClient::builder().cpu().build()),
ProverResource::Gpu => Self::Gpu(CudaProver::new()?),
ProverResource::Network(config) => Self::Network(build_network_prover(config)?),
_ => Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[
ProverResourceKind::Cpu,
ProverResourceKind::Gpu,
ProverResourceKind::Network,
],
))?,
})
}
@@ -168,7 +176,7 @@ impl CudaProver {
}
}
fn build_network_prover(config: &NetworkProverConfig) -> NetworkProver {
fn build_network_prover(config: &RemoteProverConfig) -> Result<NetworkProver, Error> {
let mut builder = ProverClient::builder().network();
// Check if we have a private key in the config or environment
if let Some(api_key) = &config.api_key {
@@ -176,9 +184,7 @@ fn build_network_prover(config: &NetworkProverConfig) -> NetworkProver {
} else if let Ok(private_key) = env::var("NETWORK_PRIVATE_KEY") {
builder = builder.private_key(&private_key);
} else {
panic!(
"Network proving requires a private key. Set NETWORK_PRIVATE_KEY environment variable or provide api_key in NetworkProverConfig"
);
return Err(Error::MissingApiKey);
}
// Set the RPC URL if provided
if !config.endpoint.is_empty() {
@@ -187,5 +193,5 @@ fn build_network_prover(config: &NetworkProverConfig) -> NetworkProver {
builder = builder.rpc_url(&rpc_url);
}
// Otherwise SP1 SDK will use its default RPC URL
builder.build()
Ok(builder.build())
}

View File

@@ -19,13 +19,14 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//! | `Cluster` | No |
//!
//! [`install_ziren_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_ziren_sdk.sh

View File

@@ -2,7 +2,7 @@ use crate::program::ZirenProgram;
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, ProverResourceKind, PublicValues, zkVM, zkVMProgramDigest,
};
use std::{panic, time::Instant};
use tracing::info;
@@ -24,12 +24,12 @@ pub struct EreZiren {
}
impl EreZiren {
pub fn new(program: ZirenProgram, resource: ProverResourceType) -> Result<Self, Error> {
if matches!(
resource,
ProverResourceType::Gpu | ProverResourceType::Network(_)
) {
panic!("Network or Gpu proving not yet implemented for ZKM. Use CPU resource type.");
pub fn new(program: ZirenProgram, resource: ProverResource) -> Result<Self, Error> {
if !matches!(resource, ProverResource::Cpu) {
Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[ProverResourceKind::Cpu],
))?;
}
let (pk, vk) = CpuProver::new().setup(program.elf());
Ok(Self { program, pk, vk })
@@ -160,7 +160,7 @@ mod tests {
};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::sync::OnceLock;
@@ -178,7 +178,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZiren::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -187,7 +187,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZiren::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -200,7 +200,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZiren::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
@@ -209,7 +209,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZiren::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),

View File

@@ -1,9 +1,12 @@
use ere_zkvm_interface::zkvm::ProofKind;
use ere_zkvm_interface::zkvm::{CommonError, ProofKind};
use thiserror::Error;
use zkm_sdk::{ZKMProofKind, ZKMVerificationError};
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
CommonError(#[from] CommonError),
// Execute
#[error("Ziren execution failed: {0}")]
Execute(#[source] anyhow::Error),

View File

@@ -8,18 +8,28 @@ license.workspace = true
[dependencies]
anyhow.workspace = true
blake3.workspace = true
bytemuck.workspace = true
bytemuck = { workspace = true, features = ["extern_crate_alloc"] }
futures-util.workspace = true
http.workspace = true
parking_lot.workspace = true
serde.workspace = true
strum = { workspace = true, features = ["derive"] }
tempfile.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread"] }
tonic.workspace = true
tracing.workspace = true
uuid = { workspace = true, features = ["v4"] }
wait-timeout.workspace = true
# Local dependencies
ere-compile-utils = { workspace = true, optional = true }
ere-zkvm-interface.workspace = true
# Zisk dependencies
zisk-distributed-grpc-api.workspace = true
zisk-proofman-verifier.workspace = true
[dev-dependencies]
ere-test-utils = { workspace = true, features = ["host"] }

View File

@@ -68,7 +68,7 @@ mod tests {
io::serde::cbor::Cbor,
program::basic::BasicProgram,
};
use ere_zkvm_interface::{ProverResourceType, compiler::Compiler};
use ere_zkvm_interface::{ProverResource, compiler::Compiler};
#[test]
fn test_compile() {
@@ -81,7 +81,7 @@ mod tests {
fn test_execute() {
let guest_directory = testing_guest_directory("zisk", "basic_go");
let program = GoCustomized.compile(&guest_directory).unwrap();
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<Cbor>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);

View File

@@ -16,7 +16,7 @@
//! ## `zkVM` requirements
//!
//! - Installation via [`ziskup`]
//! - `cargo-zisk-cuda` - Used by `zkVM::prove` if `ProverResourceType::Gpu` is
//! - `cargo-zisk-cuda` - Used by `zkVM::prove` if `ProverResource::Gpu` is
//! selected
//!
//! # `Compiler` implementation
@@ -30,13 +30,31 @@
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//! ## Supported `ProverResource`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//! | `Cluster` | Yes |
//!
//! ## Environment variables
//!
//! | Variable | Type | Default | Description |
//! | -------------------------------------- | ----- | ------- | ---------------------------------------------------------------------- |
//! | `ERE_ZISK_SETUP_ON_INIT` | Flag | | Trigger ROM setup at initialization instead of lazily |
//! | `ERE_ZISK_PORT` | Value | | Pass `--port {port}` to the server |
//! | `ERE_ZISK_UNLOCK_MAPPED_MEMORY` | Flag | | Pass `--unlock-mapped-memory` to the server |
//! | `ERE_ZISK_MINIMAL_MEMORY` | Flag | | Pass `--minimal_memory` to the server |
//! | `ERE_ZISK_PREALLOCATE` | Flag | | Pass `--preallocate` to the server |
//! | `ERE_ZISK_SHARED_TABLES` | Flag | | Pass `--shared-tables` to the server |
//! | `ERE_ZISK_MAX_STREAMS` | Value | | Pass `--max-streams {max_streams}` to the server |
//! | `ERE_ZISK_NUMBER_THREADS_WITNESS` | Value | | Pass `--number-threads-witness {number_threads_witness}` to the server |
//! | `ERE_ZISK_MAX_WITNESS_STORED` | Value | | Pass `--max-witness-stored {max_witness_stored}` to the server |
//! | `ERE_ZISK_START_SERVER_TIMEOUT_SEC` | Value | `120` | Timeout waiting for server to start |
//! | `ERE_ZISK_SHUTDOWN_SERVER_TIMEOUT_SEC` | Value | `30` | Timeout for server shutdown |
//! | `ERE_ZISK_PROVE_TIMEOUT_SEC` | Value | `3600` | Timeout for proof generation |
//!
//! [`install_zisk_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_zisk_sdk.sh
//! [`install_tamago.sh`]: https://github.com/eth-act/ere/blob/master/scripts/install_tamago.sh

View File

@@ -1,20 +1,18 @@
use crate::{
program::ZiskProgram,
zkvm::sdk::{RomDigest, START_SERVER_TIMEOUT, ZiskOptions, ZiskSdk, ZiskServer},
zkvm::sdk::{RomDigest, ZiskSdk},
};
use anyhow::bail;
use ere_zkvm_interface::zkvm::{
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMProgramDigest,
ProverResource, PublicValues, zkVM, zkVMProgramDigest,
};
use std::{
sync::{Mutex, MutexGuard},
time::Instant,
};
use tracing::error;
use std::time::Instant;
mod cluster_client;
mod error;
mod sdk;
mod server;
pub use error::Error;
@@ -22,57 +20,21 @@ include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
pub struct EreZisk {
sdk: ZiskSdk,
/// Use `Mutex` because the server can only handle signle proving task at a
/// time.
///
/// Use `Option` inside to lazily initialize only when `prove` is called.
server: Mutex<Option<ZiskServer>>,
}
impl EreZisk {
pub fn new(program: ZiskProgram, resource: ProverResourceType) -> Result<Self, Error> {
if matches!(resource, ProverResourceType::Network(_)) {
panic!("Network proving not yet implemented for ZisK. Use CPU or GPU resource type.");
}
let sdk = ZiskSdk::new(program.elf, resource, ZiskOptions::from_env())?;
Ok(Self {
sdk,
server: Mutex::new(None),
})
}
fn server(&'_ self) -> Result<MutexGuard<'_, Option<ZiskServer>>, Error> {
let mut server = self.server.lock().map_err(|_| Error::MutexPoisoned)?;
if server
.as_ref()
.is_none_or(|server| server.status(START_SERVER_TIMEOUT).is_err())
{
const MAX_RETRY: usize = 3;
let mut retry = 0;
*server = loop {
drop(server.take());
match self.sdk.server() {
Ok(server) => break Some(server),
Err(Error::TimeoutWaitingServerReady) if retry < MAX_RETRY => {
error!("Timeout waiting server ready, restarting...");
retry += 1;
continue;
}
Err(err) => return Err(err),
}
}
}
// FIXME: Use `MutexGuard::map` to unwrap the inner `Option` when it's stabilized.
Ok(server)
pub fn new(program: ZiskProgram, resource: ProverResource) -> Result<Self, Error> {
let sdk = ZiskSdk::new(program.elf, resource)?;
Ok(Self { sdk })
}
}
impl zkVM for EreZisk {
fn execute(&self, input: &Input) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
let start = Instant::now();
@@ -95,21 +57,18 @@ impl zkVM for EreZisk {
proof_kind: ProofKind,
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
if input.proofs.is_some() {
bail!(CommonError::unsupported_input("no dedicated proofs stream"))
bail!(Error::from(CommonError::unsupported_input(
"no dedicated proofs stream"
)))
}
if proof_kind != ProofKind::Compressed {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof_kind,
[ProofKind::Compressed]
))
)))
}
let mut server = self.server()?;
let server = server.as_mut().expect("server initialized");
let start = Instant::now();
let (public_values, proof) = server.prove(input.stdin())?;
let proving_time = start.elapsed();
let (public_values, proof, proving_time) = self.sdk.prove(input.stdin())?;
Ok((
public_values,
@@ -120,10 +79,10 @@ impl zkVM for EreZisk {
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
let Proof::Compressed(proof) = proof else {
bail!(CommonError::unsupported_proof_kind(
bail!(Error::from(CommonError::unsupported_proof_kind(
proof.kind(),
[ProofKind::Compressed]
))
)))
};
Ok(self.sdk.verify(proof)?)
@@ -155,8 +114,9 @@ mod tests {
program::basic::BasicProgram,
};
use ere_zkvm_interface::{
RemoteProverConfig,
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
zkvm::{Input, ProofKind, ProverResource, zkVM},
};
use std::sync::{Mutex, OnceLock};
@@ -178,7 +138,7 @@ mod tests {
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_execute(&zkvm, &test_case);
@@ -187,7 +147,7 @@ mod tests {
#[test]
fn test_execute_invalid_test_case() {
let program = basic_program();
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
for input in [
Input::new(),
@@ -200,7 +160,7 @@ mod tests {
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
let _guard = PROVE_LOCK.lock().unwrap();
@@ -211,7 +171,7 @@ mod tests {
#[test]
fn test_prove_invalid_test_case() {
let program = basic_program();
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
let _guard = PROVE_LOCK.lock().unwrap();
@@ -222,4 +182,23 @@ mod tests {
zkvm.prove(&input, ProofKind::default()).unwrap_err();
}
}
#[test]
#[ignore = "Requires ZisK cluster running"]
fn test_cluster_prove() {
let program = basic_program();
let zkvm = EreZisk::new(
program,
ProverResource::Cluster(RemoteProverConfig {
endpoint: "http://127.0.0.1:50051".to_string(),
..Default::default()
}),
)
.unwrap();
let _guard = PROVE_LOCK.lock().unwrap();
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
run_zkvm_prove(&zkvm, &test_case);
}
}

View File

@@ -0,0 +1,183 @@
//! Remote ZisK cluster proving.
use crate::zkvm::Error;
use ere_zkvm_interface::zkvm::RemoteProverConfig;
use futures_util::StreamExt;
use std::sync::OnceLock;
use std::time::Duration;
use tonic::transport::Channel;
use tracing::debug;
use zisk_distributed_grpc_api::{
ErrorResponse, InputMode, LaunchProofRequest, ProofStatusType, SubscribeToProofRequest,
SystemStatusRequest, launch_proof_response, system_status_response,
zisk_distributed_api_client::ZiskDistributedApiClient,
};
/// Wrapper for the ZisK cluster client.
///
/// Connects to the ZisK cluster via gRPC and submits proof jobs.
pub struct ClusterClient {
client: ZiskDistributedApiClient<Channel>,
}
impl ClusterClient {
/// Create a new ClusterClient that connects to the cluster.
pub fn new(config: &RemoteProverConfig) -> Result<Self, Error> {
let client = block_on(connect(&config.endpoint))?;
Ok(Self { client })
}
/// Sync wrapper for [`Self::prove_async`].
pub fn prove(&self, input: &[u8]) -> Result<(Vec<u8>, Duration), Error> {
block_on(self.prove_async(input))
}
/// Send proof request to cluster and wait for completion.
///
/// Returns the proof and proving time reported by the cluster.
async fn prove_async(&self, input: &[u8]) -> Result<(Vec<u8>, Duration), Error> {
let mut client = self.client.clone();
// Check system status to get available compute capacity
debug!("Checking system status...");
let status_response = client.system_status(SystemStatusRequest {}).await?;
let compute_capacity = match status_response.into_inner().result {
Some(system_status_response::Result::Status(status)) => {
debug!(
total_workers = status.total_workers,
compute_capacity = status.compute_capacity,
idle_workers = status.idle_workers,
busy_workers = status.busy_workers,
active_jobs = status.active_jobs,
"System status",
);
if status.total_workers == 0 || status.compute_capacity == 0 {
return Err(cluster_error("No worker available in the cluster"));
}
if status.active_jobs != 0 {
return Err(cluster_error("Cluster is busy with another proof job"));
}
status.compute_capacity
}
Some(system_status_response::Result::Error(res)) => {
return Err(cluster_error_from_response("System status error", res));
}
None => {
return Err(cluster_error("Received empty system status response"));
}
};
// Launch proof
let data_id = uuid::Uuid::new_v4().to_string();
debug!(data_id = data_id, "Launching proof...");
let launch_request = LaunchProofRequest {
data_id,
compute_capacity,
input_mode: InputMode::Data.into(),
input_path: None,
input_data: Some(input.to_vec()),
simulated_node: None,
};
let launch_response = client.launch_proof(launch_request).await?;
let job_id = match launch_response.into_inner().result {
Some(launch_proof_response::Result::JobId(job_id)) => {
debug!(job_id = job_id, "Proof launched successfully");
job_id
}
Some(launch_proof_response::Result::Error(res)) => {
return Err(cluster_error_from_response("Launch proof error", res));
}
None => {
return Err(cluster_error("Received empty launch proof response"));
}
};
// Subscribe to proof status updates
debug!(job_id = job_id, "Subscribing to proof status updates...");
let stream = client
.subscribe_to_proof(SubscribeToProofRequest { job_id })
.await?;
// Wait for proof status update (completion or failure)
debug!("Waiting for proof status update (completion or failure)...");
if let Some(update) = stream.into_inner().next().await {
let update = update.map_err(cluster_error)?;
match ProofStatusType::try_from(update.status) {
Ok(ProofStatusType::ProofStatusCompleted) => match update.final_proof {
Some(final_proof) => {
let proof = bytemuck::cast_slice(&final_proof.values).to_vec();
let proving_time = Duration::from_millis(update.duration_ms);
debug!(
proof_size = proof.len(),
proving_time = ?proving_time,
"Proof generated successfully"
);
Ok((proof, proving_time))
}
None => Err(cluster_error("Missing final proof")),
},
Ok(ProofStatusType::ProofStatusFailed) => Err(update
.error
.map(|res| cluster_error_from_response("Proof generation error", res))
.unwrap_or_else(|| cluster_error("Unknown error"))),
Err(err) => Err(cluster_error(err)),
}
} else {
Err(cluster_error("Stream ended without completion status"))
}
}
}
/// Connect to the ZisK cluster at the given gRPC endpoint.
async fn connect(endpoint: &str) -> Result<ZiskDistributedApiClient<Channel>, Error> {
let channel = Channel::from_shared(endpoint.to_string())?
.connect()
.await?;
Ok(ZiskDistributedApiClient::new(channel))
}
/// Run a future to completion, reusing the current tokio runtime or creating one.
fn block_on<T>(future: impl Future<Output = T>) -> T {
match tokio::runtime::Handle::try_current() {
Ok(handle) => tokio::task::block_in_place(|| handle.block_on(future)),
Err(_) => {
static FALLBACK_RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
FALLBACK_RT
.get_or_init(|| tokio::runtime::Runtime::new().expect("Failed to create runtime"))
.block_on(future)
}
}
}
/// Returns `Error::ClusterError`.
fn cluster_error(s: impl ToString) -> Error {
Error::ClusterError(s.to_string())
}
/// Returns `Error::ClusterError` formatted with error code and message.
fn cluster_error_from_response(s: impl ToString, res: ErrorResponse) -> Error {
Error::ClusterError(format!(
"{}, code: {}, message: {}",
s.to_string(),
res.code,
res.message
))
}

View File

@@ -1,5 +1,4 @@
use crate::zkvm::sdk::RomDigest;
use bytemuck::PodCastError;
use ere_zkvm_interface::zkvm::CommonError;
use thiserror::Error;
@@ -20,9 +19,6 @@ pub enum Error {
RomSetupFailedBefore,
// Prove
#[error("Mutex of ZiskServer is poisoned")]
MutexPoisoned,
#[error("Server crashed")]
ServerCrashed,
@@ -32,15 +28,28 @@ pub enum Error {
#[error("Timeout waiting for server ready")]
TimeoutWaitingServerReady,
#[error("Uknown server status, stdout: {stdout}")]
#[error("Unknown server status, stdout: {stdout}")]
UnknownServerStatus { stdout: String },
// Verify
#[error("Invalid proof: {0}")]
InvalidProof(String),
// Cluster
#[error("Invalid cluster endpoint: {0}")]
InvalidClusterEndpoint(#[from] http::uri::InvalidUri),
#[error("Cast proof to `u64` slice failed: {0}")]
CastProofBytesToU64s(PodCastError),
#[error("Cluster gRPC error: {0}")]
ClusterGrpcError(#[from] tonic::Status),
#[error("Failed to connect to cluster: {0}")]
ClusterConnectionFailed(#[from] tonic::transport::Error),
#[error("Cluster error: {0}")]
ClusterError(String),
// Verify
#[error("Invalid proof")]
InvalidProof,
#[error("Invalid proof size {0}, expected a multiple of 8")]
InvalidProofSize(usize),
#[error("Invalid public value format")]
InvalidPublicValue,

View File

@@ -1,162 +1,59 @@
use crate::zkvm::Error;
use ere_zkvm_interface::zkvm::{CommonError, ProverResourceType, PublicValues};
use crate::zkvm::cluster_client::ClusterClient;
use crate::zkvm::server::{ZiskServer, ZiskServerOptions};
use ere_zkvm_interface::zkvm::{CommonError, ProverResource, ProverResourceKind, PublicValues};
use std::borrow::Cow;
use std::path::Path;
use std::process::Command;
use std::{
collections::BTreeMap,
env, fs,
io::{BufRead, Write},
iter,
net::{Ipv4Addr, TcpStream},
path::{Path, PathBuf},
process::{Child, Command, Stdio},
io::BufRead,
mem::transmute,
path::PathBuf,
sync::OnceLock,
thread,
time::{Duration, Instant},
};
use strum::{EnumIter, IntoEnumIterator};
use tempfile::tempdir;
use tracing::{error, info};
use wait_timeout::ChildExt;
use tracing::info;
pub const START_SERVER_TIMEOUT: Duration = Duration::from_secs(120); // 2 mins
pub const SHUTDOWN_SERVER_TIMEOUT: Duration = Duration::from_secs(30); // 30 secs
pub const DEFAULT_PROVE_TIMEOUT: Duration = Duration::from_secs(3600); // 1 hour
/// Verifying key of the aggregation proof.
///
/// Extracted from `$HOME/.zisk/provingKey/zisk/vadcop_final/vadcop_final.verkey.json`.
pub const VADCOP_FINAL_VK: [u8; 32] = unsafe {
// Use `transmute` to keep the endianness because `zisk_proofman_verifier::verify` will
// use `bytemuck` to cast it to `[u64; 4]`.
transmute([
723851053263266420u64,
2272245643171245153u64,
9868173762158752255u64,
6004219199197288727u64,
])
};
/// Merkle root of ROM trace generated by `cargo-zisk rom-setup`.
pub type RomDigest = [u64; 4];
/// Options of `cargo-zisk` commands.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumIter)]
pub enum ZiskOption {
Port,
UnlockMappedMemory, // Should be set if locked memory is not enough
MinimalMemory,
// GPU options
Preallocate, // Should be set only if GPU memory is enough
SharedTables,
MaxStreams,
NumberThreadsWitness,
MaxWitnessStored,
}
impl ZiskOption {
/// The key of the env variable to read from.
fn env_var_key(&self) -> &'static str {
match self {
Self::Port => "ZISK_PORT",
Self::UnlockMappedMemory => "ZISK_UNLOCK_MAPPED_MEMORY",
Self::MinimalMemory => "ZISK_MINIMAL_MEMORY",
Self::Preallocate => "ZISK_PREALLOCATE",
Self::SharedTables => "ZISK_SHARED_TABLES",
Self::MaxStreams => "ZISK_MAX_STREAMS",
Self::NumberThreadsWitness => "ZISK_NUMBER_THREADS_WITNESS",
Self::MaxWitnessStored => "ZISK_MAX_WITNESS_STORED",
}
}
/// Whether the option is a flag (false-by-default boolean option) or not.
///
/// When we read the option from env variable, if the option is a flag,
/// we only check if the env variable is set or not.
fn is_flag(&self) -> bool {
match self {
Self::UnlockMappedMemory
| Self::MinimalMemory
| Self::Preallocate
| Self::SharedTables => true,
Self::Port | Self::MaxStreams | Self::NumberThreadsWitness | Self::MaxWitnessStored => {
false
}
}
}
/// The option key to be appended to `cargo-zisk` command arguments.
fn key(&self) -> &'static str {
match self {
Self::Port => "--port",
Self::UnlockMappedMemory => "--unlock-mapped-memory",
// NOTE: Use snake case for `prove-client` command
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
Self::MinimalMemory => "--minimal_memory",
Self::Preallocate => "--preallocate",
Self::SharedTables => "--shared-tables",
Self::MaxStreams => "--max-streams",
Self::NumberThreadsWitness => "--number-threads-witness",
Self::MaxWitnessStored => "--max-witness-stored",
}
}
}
#[derive(Clone)]
pub struct ZiskOptions(BTreeMap<ZiskOption, String>);
impl ZiskOptions {
/// Read options from env variables.
pub fn from_env() -> Self {
Self(
ZiskOption::iter()
.flat_map(|option| env::var(option.env_var_key()).ok().map(|val| (option, val)))
.collect(),
)
}
/// Returns `cargo-zisk` command arguments by given options that have been
/// set.
fn args(&self, options: impl IntoIterator<Item = ZiskOption>) -> impl Iterator<Item = &str> {
options
.into_iter()
.filter(|option| self.0.contains_key(option))
.flat_map(|option| {
iter::once(option.key())
.chain((!option.is_flag()).then(|| self.0[&option].as_str()))
})
}
/// Returns `cargo-zisk server` command arguments.
fn server_args(&self) -> impl Iterator<Item = &str> {
self.args([
ZiskOption::Port,
ZiskOption::UnlockMappedMemory,
ZiskOption::Preallocate,
ZiskOption::SharedTables,
ZiskOption::MaxStreams,
ZiskOption::NumberThreadsWitness,
ZiskOption::MaxWitnessStored,
])
}
/// Returns `cargo-zisk prove-client` command arguments.
fn prove_client_args(&self) -> impl Iterator<Item = &str> {
self.args([ZiskOption::Port])
}
/// Returns `cargo-zisk prove-client prove` command arguments.
fn prove_args(&self) -> impl Iterator<Item = &str> {
self.prove_client_args()
.chain(self.args([ZiskOption::MinimalMemory]))
}
pub enum ZiskProver {
Server(ZiskServer),
Cluster(ClusterClient),
}
pub struct ZiskSdk {
elf_path: PathBuf,
cuda: bool,
options: ZiskOptions,
/// ROM digest will be setup only when `ZiskSdk::server` is called.
resource: ProverResource,
/// ROM digest will be setup when `ZiskSdk::prove` or `ZiskSdk::verify`
/// is called, or if env variable `ERE_ZISK_SETUP_ON_INIT` is set.
///
/// Use `Option` inside because ROM setup might fail, we can get rid of
/// it if `OnceLock::get_or_try_init` is stabilized.
rom_digest: OnceLock<Option<RomDigest>>,
/// Prover instance, either a local server or a cluster client.
prover: ZiskProver,
}
impl ZiskSdk {
/// Returns SDK for the ELF.
pub fn new(
elf: Vec<u8>,
resource: ProverResourceType,
options: ZiskOptions,
) -> Result<Self, Error> {
if matches!(resource, ProverResourceType::Network(_)) {
panic!("Network proving not yet implemented for ZisK. Use CPU or GPU resource type.");
}
pub fn new(elf: Vec<u8>, resource: ProverResource) -> Result<Self, Error> {
// Save ELF to `~/.zisk/cache` along with the ROM binaries, to avoid it
// been cleaned up during a long run process.
let cache_dir_path = dot_zisk_dir_path().join("cache");
@@ -170,12 +67,39 @@ impl ZiskSdk {
fs::write(&elf_path, elf).map_err(|err| CommonError::write_file("elf", &elf_path, err))?;
Ok(Self {
let prover = match &resource {
ProverResource::Cpu | ProverResource::Gpu => ZiskProver::Server(ZiskServer::new(
&elf_path,
resource.is_gpu(),
ZiskServerOptions::from_env(),
)),
ProverResource::Cluster(config) => ZiskProver::Cluster(ClusterClient::new(config)?),
_ => Err(CommonError::unsupported_prover_resource_kind(
resource.kind(),
[
ProverResourceKind::Cpu,
ProverResourceKind::Gpu,
ProverResourceKind::Cluster,
],
))?,
};
let sdk = Self {
elf_path,
cuda: matches!(resource, ProverResourceType::Gpu),
options,
resource,
rom_digest: OnceLock::new(),
})
prover,
};
if env::var_os("ERE_ZISK_SETUP_ON_INIT").is_some() {
sdk.rom_digest()?;
if let ZiskProver::Server(server) = &sdk.prover {
server.ensure_ready()?;
}
}
Ok(sdk)
}
/// Execute the ELF with the given `input`.
@@ -233,7 +157,7 @@ impl ZiskSdk {
// FIXME: Use `get_or_try_init` when it is stabilized
let mut result = Ok(());
let rom_digest = *self.rom_digest.get_or_init(|| {
check_setup(self.cuda)
check_setup(self.resource.is_gpu())
.and_then(|_| rom_setup(&self.elf_path))
.map_err(|err| result = Err(err))
.ok()
@@ -242,75 +166,51 @@ impl ZiskSdk {
rom_digest.ok_or(Error::RomSetupFailedBefore)
}
/// Start a server of the ELF.
pub fn server(&self) -> Result<ZiskServer, Error> {
// Setup ROM and get ROM digest if it's not done yet.
let rom_digest = self.rom_digest()?;
/// Prove the ELF with the given input.
///
/// Returns the public values, proof, and proving time.
pub fn prove(&self, input: &[u8]) -> Result<(PublicValues, Vec<u8>, Duration), Error> {
let (proof, proving_time) = match &self.prover {
ZiskProver::Cluster(client) => client.prove(input)?,
ZiskProver::Server(server) => {
self.rom_digest()?;
let (cargo_zisk, witness_lib_path) = if self.cuda {
let witness_lib_path = dot_zisk_dir_path()
.join("bin")
.join("libzisk_witness_cuda.so");
("cargo-zisk-cuda", Some(witness_lib_path))
} else {
("cargo-zisk", None)
let start = Instant::now();
let proof = server.prove(input)?;
let proving_time = start.elapsed();
(proof, proving_time)
}
};
info!("Starting ZisK server...");
let mut cmd = Command::new(cargo_zisk);
cmd.arg("server")
.args(self.options.server_args())
.arg("--elf")
.arg(&self.elf_path)
.arg("--aggregation");
if let Some(witness_lib_path) = witness_lib_path {
cmd.arg("--witness-lib").arg(witness_lib_path);
}
let child = cmd.spawn().map_err(|err| CommonError::command(&cmd, err))?;
let server = ZiskServer {
options: self.options.clone(),
rom_digest,
child,
};
server.wait_until_ready()?;
Ok(server)
}
/// Verify the proof of the ELF, and returns public values.
pub fn verify(&self, proof: &[u8]) -> Result<PublicValues, Error> {
let rom_digest = self.rom_digest()?;
let tempdir = tempdir().map_err(CommonError::tempdir)?;
let proof_path = tempdir.path().join("proof");
fs::write(&proof_path, proof)
.map_err(|err| CommonError::write_file("proof", &proof_path, err))?;
let mut cmd = Command::new("cargo-zisk");
let output = cmd
.arg("verify")
.arg("--proof")
.arg(&proof_path)
.output()
.map_err(|err| CommonError::command(&cmd, err))?;
if !output.status.success() {
Err(Error::InvalidProof(
String::from_utf8_lossy(&output.stderr).to_string(),
))?
}
let proof = fs::read(&proof_path)
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
// Deserialize public values.
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
// The proved ROM digest should be equal to preprocessed one.
let rom_digest = self.rom_digest()?;
if proved_rom_digest != rom_digest {
return Err(Error::UnexpectedRomDigest {
preprocessed: rom_digest,
proved: proved_rom_digest,
});
}
Ok((public_values, proof, proving_time))
}
/// Verify the proof of the ELF, and returns public values.
pub fn verify(&self, proof: &[u8]) -> Result<PublicValues, Error> {
let proof = align_to_u64(proof)?;
if !zisk_proofman_verifier::verify(&proof, &VADCOP_FINAL_VK) {
Err(Error::InvalidProof)?
}
// Deserialize public values.
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
// The proved ROM digest should be equal to preprocessed one.
let rom_digest = self.rom_digest()?;
if proved_rom_digest != rom_digest {
return Err(Error::UnexpectedRomDigest {
preprocessed: rom_digest,
@@ -322,202 +222,6 @@ impl ZiskSdk {
}
}
/// ZisK server status returned from `cargo-zisk prove-client status`.
#[derive(Debug)]
pub enum ZiskServerStatus {
Idle,
Working,
}
/// Wrapper for ZisK server child process.
pub struct ZiskServer {
options: ZiskOptions,
rom_digest: RomDigest,
child: Child,
}
impl Drop for ZiskServer {
fn drop(&mut self) {
info!("Shutting down ZisK server");
let mut cmd = Command::new("cargo-zisk");
let result = cmd
.args(["prove-client", "shutdown"])
.args(self.options.prove_client_args())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.and_then(
|mut child| match child.wait_timeout(SHUTDOWN_SERVER_TIMEOUT)? {
Some(_) => child.wait_with_output(),
None => {
child.kill().ok();
Err(std::io::Error::other("shutdown command timed out"))
}
},
);
if result.as_ref().is_ok_and(|output| output.status.success()) {
info!("Shutdown ZisK server");
} else {
error!(
"Failed to shutdown ZisK server: {}",
result
.map(|output| String::from_utf8_lossy(&output.stderr).to_string())
.unwrap_or_else(|err| err.to_string())
);
error!("Shutdown server child process and asm services manually...");
let _ = self.child.kill();
shutdown_asm_service(23115);
shutdown_asm_service(23116);
shutdown_asm_service(23117);
remove_shm_files();
}
}
}
impl ZiskServer {
/// Get status of server.
pub fn status(&self, timeout: Duration) -> Result<ZiskServerStatus, Error> {
let mut cmd = Command::new("cargo-zisk");
let mut child = cmd
.args(["prove-client", "status"])
.args(self.options.prove_client_args())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|err| CommonError::command(&cmd, err))?;
if child
.wait_timeout(timeout)
.map_err(|err| CommonError::command(&cmd, err))?
.is_none()
{
// Timeout reached, kill the process
child.kill().ok();
return Err(Error::TimeoutWaitingServerReady);
}
let output = child
.wait_with_output()
.map_err(|err| CommonError::command(&cmd, err))?;
if !output.status.success() {
return Err(CommonError::command_exit_non_zero(
&cmd,
output.status,
Some(&output),
))?;
}
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.contains("idle") {
Ok(ZiskServerStatus::Idle)
} else if stdout.contains("working") {
Ok(ZiskServerStatus::Working)
} else {
Err(Error::UnknownServerStatus {
stdout: stdout.to_string(),
})
}
}
/// Send prove request to server and wait for proof to be created.
pub fn prove(&mut self, input: &[u8]) -> Result<(PublicValues, Vec<u8>), Error> {
// Prefix that ZisK server will add to the file name of the proof.
// We use constant because the file will be save to a temporary dir,
// so there will be no conflict.
const PREFIX: &str = "ere";
let tempdir = tempdir().map_err(CommonError::tempdir)?;
let input_path = tempdir.path().join("input");
let output_path = tempdir.path().join("output");
let proof_path = output_path.join(format!("{PREFIX}-vadcop_final_proof.bin"));
fs::write(&input_path, input)
.map_err(|err| CommonError::write_file("input", &input_path, err))?;
// NOTE: Use snake case for `prove-client` command
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
let mut cmd = Command::new("cargo-zisk");
let output = cmd
.args(["prove-client", "prove"])
.arg("--input")
.arg(input_path)
.arg("--output_dir")
.arg(&output_path)
.args(["-p", PREFIX])
.args(["--aggregation", "--verify_proofs"])
.args(self.options.prove_args())
.output()
.map_err(|err| CommonError::command(&cmd, err))?;
if !output.status.success() {
return Err(CommonError::command_exit_non_zero(
&cmd,
output.status,
Some(&output),
))?;
}
// By default set 1 hour timeout for prove.
let prove_timeout = env::var("ZISK_PROVE_TIMEOUT_SEC")
.ok()
.and_then(|timeout| timeout.parse::<u64>().ok())
.map(Duration::from_secs)
.unwrap_or(DEFAULT_PROVE_TIMEOUT);
// ZisK server will finish the `prove` requested above then respond the
// following `status`. So if the following `status` succeeds, the proof
// should also be ready.
self.status(prove_timeout).map_err(|err| {
if matches!(err, Error::TimeoutWaitingServerReady) {
Error::TimeoutWaitingServerProving
} else if err.to_string().contains("EOF") {
Error::ServerCrashed
} else {
err
}
})?;
let proof = fs::read(&proof_path)
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
// Deserialize public values.
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
// The proved ROM digest should be equal to preprocessed one.
if proved_rom_digest != self.rom_digest {
return Err(Error::UnexpectedRomDigest {
preprocessed: self.rom_digest,
proved: proved_rom_digest,
});
}
Ok((public_values, proof))
}
/// Wait until the server status to be idle.
fn wait_until_ready(&self) -> Result<(), Error> {
const INTERVAL: Duration = Duration::from_secs(1);
info!("Waiting until server is ready...");
let start = Instant::now();
while !matches!(
self.status(START_SERVER_TIMEOUT),
Ok(ZiskServerStatus::Idle)
) {
if start.elapsed() > START_SERVER_TIMEOUT {
return Err(Error::TimeoutWaitingServerReady);
}
thread::sleep(INTERVAL);
}
Ok(())
}
}
/// Does global setup if it is not done yet.
fn check_setup(cuda: bool) -> Result<(), Error> {
info!("Running command `cargo-zisk check-setup --aggregation`...");
@@ -588,42 +292,11 @@ fn rom_setup(elf_path: &Path) -> Result<RomDigest, Error> {
Ok(rom_digest)
}
/// Send shutdown request to ZisK asm services.
fn shutdown_asm_service(port: u16) {
// According to https://github.com/0xPolygonHermez/zisk/blob/v0.15.0/emulator-asm/asm-runner/src/asm_services/mod.rs#L34.
const CMD_SHUTDOWN_REQUEST_ID: u64 = 1000000;
if let Ok(mut stream) = TcpStream::connect((Ipv4Addr::LOCALHOST, port)) {
let _ = stream.write_all(
&[CMD_SHUTDOWN_REQUEST_ID, 0, 0, 0, 0]
.into_iter()
.flat_map(|word| word.to_le_bytes())
.collect::<Vec<_>>(),
);
}
}
/// Remove shared memory created by ZisK.
fn remove_shm_files() {
let Ok(shm_dir) = fs::read_dir(Path::new("/dev/shm")) else {
return;
};
for entry in shm_dir.flatten() {
let path = entry.path();
if path
.file_name()
.and_then(|n| n.to_str())
.is_some_and(|name| name.starts_with("ZISK") || name.starts_with("sem"))
{
let _ = fs::remove_file(&path);
}
}
}
/// Deserialize public values as json string sequence, and parse the `RomDigest`
/// and user set public values as `Vec<u8>`.
fn deserialize_public_values(proof: &[u8]) -> Result<(RomDigest, Vec<u8>), Error> {
let proof = bytemuck::try_cast_slice::<_, u64>(proof).map_err(Error::CastProofBytesToU64s)?;
let proof = align_to_u64(proof)?;
let proof = bytemuck::cast_slice::<u8, u64>(&proof);
// The public values contain at least the the total number of public values,
// `RomDigest`, and the number of user set public values.
@@ -653,7 +326,23 @@ fn deserialize_public_values(proof: &[u8]) -> Result<(RomDigest, Vec<u8>), Error
Ok((rom_digest, public_values))
}
/// Returns u64-aligned bytes.
///
/// Returns an error if `data.len()` is not a multiple of 8.
fn align_to_u64(data: &[u8]) -> Result<Cow<'_, [u8]>, Error> {
if !data.len().is_multiple_of(8) {
return Err(Error::InvalidProofSize(data.len()));
}
Ok(if data.as_ptr().cast::<u64>().is_aligned() {
Cow::Borrowed(data)
} else {
let mut aligned: Vec<u64> = vec![0; data.len() / 8];
bytemuck::cast_slice_mut(&mut aligned).copy_from_slice(data);
Cow::Owned(bytemuck::cast_slice(&aligned).to_vec())
})
}
/// Returns path to `~/.zisk` directory.
fn dot_zisk_dir_path() -> PathBuf {
pub(crate) fn dot_zisk_dir_path() -> PathBuf {
PathBuf::from(env::var("HOME").expect("env `$HOME` should be set")).join(".zisk")
}

View File

@@ -0,0 +1,461 @@
//! Local ZisK server management via `cargo-zisk` commands.
use crate::zkvm::{Error, sdk::dot_zisk_dir_path};
use ere_zkvm_interface::zkvm::CommonError;
use parking_lot::Mutex;
use std::{
collections::BTreeMap,
env, fs,
io::Write,
iter,
net::{Ipv4Addr, TcpStream},
path::{Path, PathBuf},
process::{Child, Command, Stdio},
thread,
time::{Duration, Instant},
};
use strum::{EnumIter, IntoEnumIterator};
use tempfile::tempdir;
use tracing::{error, info};
use wait_timeout::ChildExt;
pub const DEFAULT_START_SERVER_TIMEOUT_SEC: u64 = 120; // 2 mins
pub const DEFAULT_SHUTDOWN_SERVER_TIMEOUT_SEC: u64 = 30; // 30 secs
pub const DEFAULT_PROVE_TIMEOUT_SEC: u64 = 3600; // 1 hour
/// ZisK server status returned from `cargo-zisk prove-client status`.
#[derive(Debug)]
pub enum ZiskServerStatus {
Idle,
Working,
}
/// Options of `cargo-zisk` commands.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumIter)]
pub enum ZiskServerOption {
Port,
UnlockMappedMemory, // Should be set if locked memory is not enough
MinimalMemory,
// GPU options
Preallocate, // Should be set only if GPU memory is enough
SharedTables,
MaxStreams,
NumberThreadsWitness,
MaxWitnessStored,
}
impl ZiskServerOption {
/// The key of the env variable to read from.
fn env_var_key(&self) -> &'static str {
match self {
Self::Port => "ERE_ZISK_PORT",
Self::UnlockMappedMemory => "ERE_ZISK_UNLOCK_MAPPED_MEMORY",
Self::MinimalMemory => "ERE_ZISK_MINIMAL_MEMORY",
Self::Preallocate => "ERE_ZISK_PREALLOCATE",
Self::SharedTables => "ERE_ZISK_SHARED_TABLES",
Self::MaxStreams => "ERE_ZISK_MAX_STREAMS",
Self::NumberThreadsWitness => "ERE_ZISK_NUMBER_THREADS_WITNESS",
Self::MaxWitnessStored => "ERE_ZISK_MAX_WITNESS_STORED",
}
}
/// Whether the option is a flag (false-by-default boolean option) or not.
///
/// When we read the option from env variable, if the option is a flag,
/// we only check if the env variable is set or not.
fn is_flag(&self) -> bool {
match self {
Self::UnlockMappedMemory
| Self::MinimalMemory
| Self::Preallocate
| Self::SharedTables => true,
Self::Port | Self::MaxStreams | Self::NumberThreadsWitness | Self::MaxWitnessStored => {
false
}
}
}
/// The option key to be appended to `cargo-zisk` command arguments.
fn key(&self) -> &'static str {
match self {
Self::Port => "--port",
Self::UnlockMappedMemory => "--unlock-mapped-memory",
// NOTE: Use snake case for `prove-client` command
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
Self::MinimalMemory => "--minimal_memory",
Self::Preallocate => "--preallocate",
Self::SharedTables => "--shared-tables",
Self::MaxStreams => "--max-streams",
Self::NumberThreadsWitness => "--number-threads-witness",
Self::MaxWitnessStored => "--max-witness-stored",
}
}
}
/// Configurable options for `cargo-zisk server` and `cargo-zisk prove-client` commands.
#[derive(Clone)]
pub struct ZiskServerOptions(BTreeMap<ZiskServerOption, String>);
impl ZiskServerOptions {
/// Read options from env variables.
pub fn from_env() -> Self {
Self(
ZiskServerOption::iter()
.flat_map(|option| env::var(option.env_var_key()).ok().map(|val| (option, val)))
.collect(),
)
}
/// Returns `cargo-zisk` command arguments by given options that have been
/// set.
fn args(
&self,
options: impl IntoIterator<Item = ZiskServerOption>,
) -> impl Iterator<Item = &str> {
options
.into_iter()
.filter(|option| self.0.contains_key(option))
.flat_map(|option| {
iter::once(option.key())
.chain((!option.is_flag()).then(|| self.0[&option].as_str()))
})
}
/// Returns `cargo-zisk server` command arguments.
pub(crate) fn server_args(&self) -> impl Iterator<Item = &str> {
self.args([
ZiskServerOption::Port,
ZiskServerOption::UnlockMappedMemory,
ZiskServerOption::Preallocate,
ZiskServerOption::SharedTables,
ZiskServerOption::MaxStreams,
ZiskServerOption::NumberThreadsWitness,
ZiskServerOption::MaxWitnessStored,
])
}
/// Returns `cargo-zisk prove-client` command arguments.
pub(crate) fn prove_client_args(&self) -> impl Iterator<Item = &str> {
self.args([ZiskServerOption::Port])
}
/// Returns `cargo-zisk prove-client prove` command arguments.
pub(crate) fn prove_args(&self) -> impl Iterator<Item = &str> {
self.prove_client_args()
.chain(self.args([ZiskServerOption::MinimalMemory]))
}
}
/// Wrapper for ZisK server child process.
pub struct ZiskServer {
options: ZiskServerOptions,
elf_path: PathBuf,
cuda: bool,
child: Mutex<Option<Child>>,
}
impl Drop for ZiskServer {
fn drop(&mut self) {
self.shutdown();
}
}
impl ZiskServer {
/// Create a new ZisK server for the given ELF.
///
/// The server process is lazily started on the first call to [`prove`](ZiskServer::prove).
pub fn new(elf_path: &Path, cuda: bool, options: ZiskServerOptions) -> Self {
Self {
elf_path: elf_path.to_path_buf(),
cuda,
options,
child: Mutex::new(None),
}
}
/// Send prove request to server and wait for proof to be created.
///
/// Returns the proof.
pub fn prove(&self, input: &[u8]) -> Result<Vec<u8>, Error> {
self.ensure_ready()?;
// Prefix that ZisK server will add to the file name of the proof.
// We use constant because the file will be save to a temporary dir,
// so there will be no conflict.
const PREFIX: &str = "ere";
let tempdir = tempdir().map_err(CommonError::tempdir)?;
let input_path = tempdir.path().join("input");
let output_path = tempdir.path().join("output");
let proof_path = output_path.join(format!("{PREFIX}-vadcop_final_proof.bin"));
fs::write(&input_path, input)
.map_err(|err| CommonError::write_file("input", &input_path, err))?;
// NOTE: Use snake case for `prove-client` command
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
let mut cmd = Command::new("cargo-zisk");
let output = cmd
.args(["prove-client", "prove"])
.arg("--input")
.arg(input_path)
.arg("--output_dir")
.arg(&output_path)
.args(["-p", PREFIX])
.args(["--aggregation", "--verify_proofs"])
.args(self.options.prove_args())
.output()
.map_err(|err| CommonError::command(&cmd, err))?;
if !output.status.success() {
return Err(CommonError::command_exit_non_zero(
&cmd,
output.status,
Some(&output),
))?;
}
// ZisK server will finish the `prove` requested above then respond the
// following `status`. So if the following `status` succeeds, the proof
// should also be ready.
self.status(prove_timeout()).map_err(|err| {
if matches!(err, Error::TimeoutWaitingServerReady) {
Error::TimeoutWaitingServerProving
} else if err.to_string().contains("EOF") {
Error::ServerCrashed
} else {
err
}
})?;
let proof = fs::read(&proof_path)
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
Ok(proof)
}
/// Ensure the server is running and responsive, restarting it if needed.
pub fn ensure_ready(&self) -> Result<(), Error> {
if self.child.lock().is_some() && self.status(start_server_timeout()).is_ok() {
return Ok(());
}
const MAX_RETRY: usize = 3;
let mut attempt = 0;
loop {
self.shutdown();
match self.start() {
Ok(()) => return Ok(()),
Err(Error::TimeoutWaitingServerReady) if attempt < MAX_RETRY => {
error!("Timeout waiting server ready, restarting...");
attempt += 1;
continue;
}
Err(err) => return Err(err),
}
}
}
/// Spawn the server process and wait until it's ready.
fn start(&self) -> Result<(), Error> {
info!("Starting ZisK server...");
let (cargo_zisk, witness_lib_name) = if self.cuda {
("cargo-zisk-cuda", "libzisk_witness_cuda.so")
} else {
("cargo-zisk", "libzisk_witness.so")
};
let witness_lib_path = dot_zisk_dir_path().join("bin").join(witness_lib_name);
let mut cmd = Command::new(cargo_zisk);
cmd.arg("server")
.args(self.options.server_args())
.arg("--elf")
.arg(&self.elf_path)
.arg("--witness-lib")
.arg(witness_lib_path)
.arg("--aggregation");
let child = cmd.spawn().map_err(|err| CommonError::command(&cmd, err))?;
{
let mut guard = self.child.lock();
*guard = Some(child);
}
self.wait_until_ready()?;
Ok(())
}
/// Wait until the server status to be idle.
pub fn wait_until_ready(&self) -> Result<(), Error> {
const INTERVAL: Duration = Duration::from_secs(1);
let timeout = start_server_timeout();
info!("Waiting until server is ready...");
let start = Instant::now();
while !matches!(self.status(timeout), Ok(ZiskServerStatus::Idle)) {
if start.elapsed() > timeout {
return Err(Error::TimeoutWaitingServerReady);
}
thread::sleep(INTERVAL);
}
Ok(())
}
/// Gracefully shut down the server, falling back to force-kill on failure.
fn shutdown(&self) {
let mut guard = self.child.lock();
let Some(mut child) = guard.take() else {
return;
};
info!("Shutting down ZisK server");
let mut cmd = Command::new("cargo-zisk");
let result = cmd
.args(["prove-client", "shutdown"])
.args(self.options.prove_client_args())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.and_then(
|mut child| match child.wait_timeout(shutdown_server_timeout())? {
Some(_) => child.wait_with_output(),
None => {
child.kill().ok();
Err(std::io::Error::other("shutdown command timed out"))
}
},
);
if result.as_ref().is_ok_and(|output| output.status.success()) {
info!("Shutdown ZisK server");
} else {
error!(
"Failed to shutdown ZisK server: {}",
result
.map(|output| String::from_utf8_lossy(&output.stderr).to_string())
.unwrap_or_else(|err| err.to_string())
);
error!("Shutdown server child process and asm services manually...");
let _ = child.kill();
shutdown_asm_service(23115);
shutdown_asm_service(23116);
shutdown_asm_service(23117);
remove_shm_files();
}
}
/// Get status of server.
fn status(&self, timeout: Duration) -> Result<ZiskServerStatus, Error> {
let mut cmd = Command::new("cargo-zisk");
let mut child = cmd
.args(["prove-client", "status"])
.args(self.options.prove_client_args())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|err| CommonError::command(&cmd, err))?;
if child
.wait_timeout(timeout)
.map_err(|err| CommonError::command(&cmd, err))?
.is_none()
{
// Timeout reached, kill the process
child.kill().ok();
return Err(Error::TimeoutWaitingServerReady);
}
let output = child
.wait_with_output()
.map_err(|err| CommonError::command(&cmd, err))?;
if !output.status.success() {
return Err(CommonError::command_exit_non_zero(
&cmd,
output.status,
Some(&output),
))?;
}
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.contains("idle") {
Ok(ZiskServerStatus::Idle)
} else if stdout.contains("working") {
Ok(ZiskServerStatus::Working)
} else {
Err(Error::UnknownServerStatus {
stdout: stdout.to_string(),
})
}
}
}
/// Send shutdown request to ZisK asm services.
fn shutdown_asm_service(port: u16) {
// According to https://github.com/0xPolygonHermez/zisk/blob/v0.15.0/emulator-asm/asm-runner/src/asm_services/mod.rs#L34.
const CMD_SHUTDOWN_REQUEST_ID: u64 = 1000000;
if let Ok(mut stream) = TcpStream::connect((Ipv4Addr::LOCALHOST, port)) {
let _ = stream.write_all(
&[CMD_SHUTDOWN_REQUEST_ID, 0, 0, 0, 0]
.into_iter()
.flat_map(|word| word.to_le_bytes())
.collect::<Vec<_>>(),
);
}
}
/// Remove shared memory created by ZisK.
fn remove_shm_files() {
let Ok(shm_dir) = fs::read_dir(Path::new("/dev/shm")) else {
return;
};
for entry in shm_dir.flatten() {
let path = entry.path();
if path
.file_name()
.and_then(|n| n.to_str())
.is_some_and(|name| name.starts_with("ZISK") || name.starts_with("sem"))
{
let _ = fs::remove_file(&path);
}
}
}
/// Returns the server start timeout, configurable via `ERE_ZISK_START_SERVER_TIMEOUT_SEC`.
fn start_server_timeout() -> Duration {
timeout(
"ERE_ZISK_START_SERVER_TIMEOUT_SEC",
DEFAULT_START_SERVER_TIMEOUT_SEC,
)
}
/// Returns the server shutdown timeout, configurable via `ERE_ZISK_SHUTDOWN_SERVER_TIMEOUT_SEC`.
fn shutdown_server_timeout() -> Duration {
timeout(
"ERE_ZISK_SHUTDOWN_SERVER_TIMEOUT_SEC",
DEFAULT_SHUTDOWN_SERVER_TIMEOUT_SEC,
)
}
/// Returns the prove timeout, configurable via `ERE_ZISK_PROVE_TIMEOUT_SEC`.
fn prove_timeout() -> Duration {
timeout("ERE_ZISK_PROVE_TIMEOUT_SEC", DEFAULT_PROVE_TIMEOUT_SEC)
}
/// Read a timeout from the given env variable key, falling back to `default`.
fn timeout(key: &str, default: u64) -> Duration {
let sec = env::var(key)
.ok()
.and_then(|timeout| timeout.parse::<u64>().ok())
.unwrap_or(default);
Duration::from_secs(sec)
}

View File

@@ -0,0 +1,114 @@
ARG BASE_IMAGE=ubuntu:24.04
ARG BASE_CUDA_IMAGE=nvidia/cuda:12.9.1-devel-ubuntu24.04
ARG RUNTIME_IMAGE=ubuntu:24.04
ARG RUNTIME_CUDA_IMAGE=nvidia/cuda:12.9.1-runtime-ubuntu24.04
# Whether to enable CUDA feature or not.
ARG CUDA
FROM $BASE_IMAGE AS base
FROM $BASE_CUDA_IMAGE AS base_cuda
FROM base${CUDA:+_cuda} AS build_stage
# Taken from https://0xpolygonhermez.github.io/zisk/getting_started/installation.html
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
xz-utils \
jq \
curl \
build-essential \
qemu-system \
libomp-dev \
libgmp-dev \
nlohmann-json3-dev \
protobuf-compiler \
uuid-dev \
libgrpc++-dev \
libsecp256k1-dev \
libsodium-dev \
libpqxx-dev \
nasm \
libopenmpi-dev \
openmpi-bin \
openmpi-common \
libclang-dev \
clang \
gcc-riscv64-unknown-elf \
libprotobuf-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install rustup
ARG RUST_VERSION=1.88.0
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain ${RUST_VERSION} --no-modify-path
# Clone repo
WORKDIR /app
RUN git clone https://github.com/han0110/zisk.git --depth 1 --branch patch/v0.15.0-cluster /app
# Whether to enable CUDA feature or not.
ARG CUDA
# Default to build for RTX 50 series
ARG CUDA_ARCH=sm_120
# Build binaries
RUN cargo build --release ${CUDA:+--features gpu}
FROM $RUNTIME_IMAGE AS runtime
FROM $RUNTIME_CUDA_IMAGE AS runtime_cuda
FROM runtime${CUDA:+_cuda} AS runtime_stage
# Taken from https://0xpolygonhermez.github.io/zisk/getting_started/installation.html
RUN apt-get update && apt-get install -y --no-install-recommends \
xz-utils \
jq \
curl \
build-essential \
qemu-system \
libomp-dev \
libgmp-dev \
nlohmann-json3-dev \
protobuf-compiler \
uuid-dev \
libgrpc++-dev \
libsecp256k1-dev \
libsodium-dev \
libpqxx-dev \
nasm \
libopenmpi-dev \
openmpi-bin \
openmpi-common \
libclang-dev \
clang \
gcc-riscv64-unknown-elf \
libprotobuf-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.9.3/grpcurl_1.9.3_linux_x86_64.tar.gz \
| tar -xz -C /usr/local/bin grpcurl
WORKDIR /app
# Copy script
COPY --from=build_stage /app/ziskup/ziskup /app/ziskup/ziskup
# Copy binaries (to /usr/local/bin instead of ~/.zisk/bin because we want to mount ~/.zisk later)
COPY --from=build_stage /app/target/release/cargo-zisk /usr/local/bin/cargo-zisk
COPY --from=build_stage /app/target/release/libzisk_witness.so /usr/local/bin/libzisk_witness.so
COPY --from=build_stage /app/target/release/zisk-coordinator /usr/local/bin/zisk-coordinator
COPY --from=build_stage /app/target/release/zisk-worker /usr/local/bin/zisk-worker
# Copy proto for health check
COPY --from=build_stage /app/distributed/crates/grpc-api/proto/zisk_distributed_api.proto /app/proto/zisk_distributed_api.proto
# Copy configs
COPY --from=build_stage /app/distributed/crates/coordinator/config/ /app/config/coordinator/
COPY --from=build_stage /app/distributed/crates/worker/config/ /app/config/worker/
CMD ["/bin/bash"]

View File

@@ -0,0 +1,131 @@
services:
zisk-setup:
image: ere-cluster-zisk:local-cuda
entrypoint: ["/bin/bash", "-c"]
command:
- |
set -e
if [ ! -d "/root/.zisk/provingKey" ]; then
echo "Install rustup temporarily for ziskup..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none
source "$$HOME/.cargo/env"
echo "Download proving key..."
SETUP_KEY=proving-no-consttree /app/ziskup/ziskup
echo "Cleaning up rustup, cargo, toolchains and binaries..."
rustup self uninstall -y
rm -rf /root/.zisk/toolchains
rm /root/.zisk/bin/cargo-zisk
rm /root/.zisk/bin/libzisk_witness.so
rm /root/.zisk/bin/riscv2zisk
rm /root/.zisk/bin/zisk-coordinator
rm /root/.zisk/bin/ziskemu
rm /root/.zisk/bin/ziskup
rm /root/.zisk/bin/zisk-worker
echo "Generating constant tree files (GPU). This may take a while..."
cargo-zisk check-setup -a
else
echo "Proving key already exists, skipping"
fi
echo "Running rom-setup..."
cargo-zisk rom-setup -e /app/elf
echo "Setup complete"
volumes:
- zisk_setup:/root/.zisk
- ${ELF_PATH}:/app/elf:ro
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['0']
capabilities: [gpu]
zisk-coordinator:
image: ere-cluster-zisk:local-cuda
command:
- "zisk-coordinator"
- "--config"
- "/app/config/coordinator/prod.toml"
ports:
- "50051:50051"
# volumes:
# Uncomment to override config
# - ./coordinator-config:/app/config/coordinator/prod.toml:ro
environment:
- RUST_LOG=info
restart: unless-stopped
healthcheck:
test: ["CMD", "grpcurl", "-plaintext", "-import-path", "/app/proto", "-proto", "zisk_distributed_api.proto", "localhost:50051", "zisk.distributed.api.v1.ZiskDistributedApi/HealthCheck"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
zisk-worker-0:
image: ere-cluster-zisk:local-cuda
command:
- "zisk-worker"
- "--config"
- "/app/config/worker/prod.toml"
- "--coordinator-url"
- "http://zisk-coordinator:50051"
- "--elf"
- "/app/elf"
- "--witness-lib"
- "/usr/local/bin/libzisk_witness.so"
volumes:
# Mount proving key
- zisk_setup:/root/.zisk:ro
# Mount ELF
- ${ELF_PATH}:/app/elf:ro
# Uncomment to override config
# - ./worker-config:/app/config/worker/prod.toml:ro
environment:
- RUST_LOG=info
restart: unless-stopped
depends_on:
zisk-setup:
condition: service_completed_successfully
zisk-coordinator:
condition: service_healthy
shm_size: 32G
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test: ["CMD", "pgrep", "-f", "zisk-worker"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['0']
capabilities: [gpu]
# Uncomment to add more workers if more GPUs are available.
# zisk-worker-x:
# extends: zisk-worker-0
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# device_ids: ['x']
# capabilities: [gpu]
volumes:
zisk_setup:
networks:
default: