Compile. Execute. Prove. Verify.
One ergonomic Rust API, multiple zero‑knowledge virtual machines.
The current MSRV (minimum supported rust version) is 1.91.
This repository contains the following crates:
ere-compiler-core] - Compiler trait and Elf type for compiling guest programsere-prover-core] - zkVMProver trait, Input, ProverResource, and execution/proving reportsere-platform-core] - Platform trait for guest programere-verifier-core] - zkVMVerifier trait and PublicValuesere-compiler-core] (host)ere-compiler-airbender]ere-compiler-openvm]ere-compiler-risc0]ere-compiler-sp1]ere-compiler-zisk]ere-prover-core] (host)ere-prover-airbender]ere-prover-openvm]ere-prover-risc0]ere-prover-sp1]ere-prover-zisk]ere-platform-core] (guest)ere-platform-airbender]ere-platform-openvm]ere-platform-risc0]ere-platform-sp1]ere-platform-zisk]ere-verifier-core] (lightweight host verifier)ere-verifier-airbender]ere-verifier-openvm]ere-verifier-risc0]ere-verifier-sp1]ere-verifier-zisk]ere-dockerized] - Docker wrapper that spawns [ere-server] containers to run zkVM operations without local SDK installationere-cluster-client-zisk] - ZisK distributed-cluster client used by [ere-prover-zisk] when ProverResource::Cluster is selectedere-codec] - Canonical byte codec (Encode/Decode + macros) shared across cratesere-catalog] - Catalog of supported zkVMs and compilers (zkVMKind, CompilerKind, SDK versions, Docker image tag)ere-compiler] - CLI binary to run Compiler used by [ere-dockerized]ere-server] - Server binary that exposes zkVMProver operations over gRPC (also provides a keygen subcommand)ere-server-api] - gRPC wire contract (proto/api.proto and generated prost/twirp types) shared by [ere-server] and [ere-server-client]ere-server-client] - Client library for [ere-server], used by [ere-dockerized]ere-util-build] - Build-time utilities (SDK version + Docker image tag detection)ere-util-compile] - Cross-compilation utilities (CargoBuildCmd, RustTarget, toolchain management)ere-util-test] - Testing utilities (Program, TestCase, BasicProgram, codec markers)ere-util-tokio] - Tokio runtime bridge (block_on) used by sync constructors that call async SDK APIsHost-side traits:
Compiler (from ere-compiler-core)Compile a guest program into an Elf.
zkVMProver (from ere-prover-core)Execute, prove and verify. A zkVM prover instance is created for an Elf produced by a Compiler. Elf specific verifying key generation happens in the constructor.
zkVMVerifier (from ere-verifier-core)zkVM verifier that is created by a succinct ProgramVk for specific Elf produced by zkVMProver. A zkVM verifier instance verifies a Proof and returns PublicValues. Pulled in standalone by verify-only consumers without the prover deps if upstream zkVM SDK provides verifier-only crate.
Guest-side trait (ere-platform-core):
PlatformProvides platform-dependent methods for IO read/write and cycle tracking. It also re-exports the runtime SDK of the zkVM, guaranteed to match the host when ere-prover-{zkvm} and ere-platform-{zkvm} share the same version.
Host and guest communicate through raw bytes. Serialization/deserialization can be done in any way as long as they agree with each other.
The Input structure holds stdin as raw bytes. Set them with Input::new().with_stdin(data), and the guest reads them back via Platform::read_input().
For Airbender and RISC Zero, the prover internally prepends a u32 LE byte-length prefix because their guest input APIs read u32 words and need a length to stop. SP1, OpenVM, and ZisK pass bytes through.
zkVM-specific stdin APIs (e.g., sp1_zkvm::io::read, risc0_zkvm::guest::env::read) can also be used directly when finer-grained control is needed.
Public values written in the guest program (via Platform::write_output() or zkVM-specific output APIs) are returned as raw bytes to the host after zkVMProver::execute, zkVMProver::prove and zkVMProver::verify methods.
Different zkVMs handles public values in different approaches:
| zkVM | Size Limit | Note |
|---|---|---|
| Airbender | 32 bytes | Padded to 32 bytes with zeros |
| OpenVM | 256 bytes | Padded to 256 bytes with zeros |
| RISC Zero | unlimited | Hashed internally |
| SP1 | unlimited | Hashed internally |
| ZisK | 256 bytes |
| zkVM | Version | ISA | GPU | Multi GPU | Cluster |
|---|---|---|---|---|---|
| Airbender | 73d69b5 |
RV32IMA |
V | V | |
| OpenVM | 2.0.0-rc.3 |
RV32IMA |
V | ||
| RISC Zero | 3.0.5 |
RV32IMA |
V | V | |
| SP1 | 6.3.0 |
RV64IMA |
V | ||
| ZisK | 1.0.0-alpha |
RV64IMA |
V | V | V |
Install the required zkVM SDKs locally for better performance and debugging.
Install the SP1 SDK as an example
bash scripts/sdk_installers/install_sp1_sdk.sh
# guest/Cargo.toml
[workspace]
[package]
name = "guest"
edition = "2024"
[dependencies]
ere-platform-sp1 = { git = "https://github.com/eth-act/ere.git" }
// guest/src/main.rs
#![no_main]
use ere_platform_sp1::{sp1_zkvm, Platform, SP1Platform};
sp1_zkvm::entrypoint!(main);
type P = SP1Platform;
pub fn main() {
// Read serialized input and deserialize it.
let input = P::read_input();
let n = u64::from_le_bytes(input.as_slice().try_into().unwrap());
// Compute nth fib.
let fib_n = fib(n);
// Write serialized output.
let output = [input.as_slice(), &fib_n.to_le_bytes()].concat();
P::write_output(&output);
}
fn fib(n: u64) -> u64 {
let mut a = 0;
let mut b = 1;
for _ in 0..n {
let c = a + b;
a = b;
b = c;
}
a
}
# host/Cargo.toml
[workspace]
[package]
name = "host"
edition = "2024"
[dependencies]
ere-prover-core = { git = "https://github.com/eth-act/ere.git" }
ere-prover-sp1 = { git = "https://github.com/eth-act/ere.git" }
// host/src/main.rs
use ere_compiler_core::Compiler;
use ere_compiler_sp1::SP1RustRv64imaCustomized;
use ere_prover_core::{Input, ProverResource, zkVMProver};
use ere_prover_sp1::SP1Prover;
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let guest_directory = Path::new("path/to/guest");
// Compile guest program with SP1 customized toolchain
let compiler = SP1RustRv64imaCustomized;
let elf = compiler.compile(guest_directory, &[])?;
// Create zkVM instance (setup/preprocessing happens here)
let zkvm = SP1Prover::new(elf, ProverResource::Cpu)?;
// Prepare input as raw bytes. The prover handles any framing needed by the SDK.
let stdin = 10u64.to_le_bytes().to_vec();
let input = Input::new().with_stdin(stdin.clone());
let expected_output = [stdin, 55u64.to_le_bytes().to_vec()].concat();
// Execute
let (public_values, report) = zkvm.execute(&input)?;
assert_eq!(public_values, expected_output);
println!("Execution cycles: {}", report.total_num_cycles);
// Prove
let (public_values, proof, report) = zkvm.prove(&input)?;
assert_eq!(public_values, expected_output);
println!("Proving time: {:?}", report.proving_time);
// Verify
let public_values = zkvm.verify(&proof)?;
assert_eq!(public_values, expected_output);
println!("Proof verified successfully!");
Ok(())
}
Use Docker for zkVM operations without installing SDKs locally. Only requires Docker to be installed.
We use the same guest program created above.
# host/Cargo.toml
[workspace]
[package]
name = "host"
edition = "2024"
[dependencies]
ere-prover-core = { git = "https://github.com/eth-act/ere.git" }
ere-dockerized = { git = "https://github.com/eth-act/ere.git" }
```rust // host/src/main.rs
use ere_compiler_core::Compiler; use ere_dockerized::{ CompilerKind, DockerizedCompiler, DockerizedzkVM, DockerizedzkVMConfig, zkVMKind, }; use ere_prover_core::{Input, ProverResource, zkVMProver}; use std::path::Path;
fn main() -> Result<(), Box> { let guest_directory = Path::new(