Compile. Execute. Prove. Verify.
One ergonomic Rust API, multiple zero‑knowledge virtual machines.
- Table of Contents
- Supported Rust Versions (MSRV)
- Overview
- Architecture
- Supported zkVMs
- Examples
- Directory Layout
- Contributing
- Disclaimer
- License
The current MSRV (minimum supported rust version) is 1.88.
This repository contains the following crates:
- Traits
ere-zkvm-interface-CompilerandzkVMtraits for zkVM host operationsere-platform-trait-Platformtrait for guest program
- Per-zkVM implementations for
ere-zkvm-interface(host) - Per-zkVM implementations for
ere-platform-trait(guest) ere-dockerized- Docker wrapper implementation forere-zkvm-interfaceof all zkVMsere-io- Serialization utilities for host/guest IO communication- Internal crates
ere-compiler- Cli to runCompilerused byere-dockerizedere-server- Server and client forzkVMoperations used byere-dockerizedere-build-utils- Build-time utilitiesere-compile-utils- Compilation utilitiesere-test-utils- Testing utilities
ere-zkvm-interface provides traits for host:
-
CompilerCompile a guest program into a zkVM-specific artifact (typically a RISC-V ELF, or a wrapper with preprocessing data).
-
zkVMExecute, prove and verify that artifact. A zkVM instance is created for specific artifact that comes from the
Compiler.
ere-platform-trait provides traits for guest program:
-
PlatformProvides platform dependent methods for IO read/write and cycle tracking. It also re-exports the runtime SDK of the zkVM, that is guaranteed to match the same version with the zkVM host when
ere-{zkvm}andere-platform-{zkvm}have 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. There are 2 ways to use it:
-
Input::new().with_prefixed_stdin(data)forPlatform::read_whole_input()The
Platformtrait provides a unified interface to read the whole stdin. However, some zkVMs don't provide access to the stdin length, so we require it to have a length prefix.The method
Input::with_prefixed_stdinautomatically adds a LE u32 length prefix to the stdin. In the guest,Platform::read_whole_inputwill return only the actual data.Without the length prefix, the
Platform::read_whole_inputwill cause guest panic at runtime. -
Input::new().with_stdin(data)for zkVM-specific stdin APIsThe method
Input::with_stdinsets stdin without modification. Use this when you need direct access to zkVM-specific stdin APIs (e.g.,sp1_zkvm::io::read,risc0_zkvm::guest::env::read), such as streaming reads or partial data consumption.
Public values written in the guest program (via Platform::write_whole_output() or zkVM-specific output APIs) are returned as raw bytes to the host after zkVM::execute, zkVM::prove and zkVM::verify methods.
Different zkVMs handles public values in different approaches:
| zkVM | Size Limit | Note |
|---|---|---|
| Airbender | 32 bytes | Padded to 32 bytes with zeros |
| Jolt | 4096 bytes (Configurable) | |
| Miden | 16 words | Word = Goldilocks field element |
| Nexus | unlimited | Size configured automatically |
| OpenVM | 32 bytes | Padded to 32 bytes with zeros |
| Pico | unlimited | Hashed internally |
| Risc0 | unlimited | Hashed internally |
| SP1 | unlimited | Hashed internally |
| Ziren | unlimited | Hashed internally |
| Zisk | 256 bytes |
For zkVMs with size limits on public values, OutputHashedPlatform<P, D> serves as a wrapper that hashes outputs before calling the inner P::write_whole_output. This enables the same guest program to run across all zkVMs regardless of their size constraints:
OutputHashedPlatform::<OpenVMPlatform, Sha256>::write_whole_output(&large_output);| zkVM | Version | GPU |
|---|---|---|
| Airbender | 0.5.1 |
Yes |
| Jolt | 0.3.0-alpha |
No |
| Miden | 0.19.1 |
No |
| Nexus | 0.3.5 |
No |
| OpenVM | 1.4.2 |
Yes |
| Pico | 1.1.10 |
No |
| Risc0 | 3.0.4 |
Yes |
| SP1 | 5.2.3 |
Yes |
| Ziren | 1.2.2 |
No |
| Zisk | 0.13.0 |
Yes |
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_whole_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, fib_n.to_le_bytes().to_vec()].concat();
P::write_whole_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-zkvm-interface = { git = "https://github.com/eth-act/ere.git" }
ere-sp1 = { git = "https://github.com/eth-act/ere.git" }// host/src/main.rs
use ere_sp1::{compiler::RustRv32imaCustomized, zkvm::EreSP1};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
};
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 = RustRv32imaCustomized;
let program = compiler.compile(guest_directory)?;
// Create zkVM instance (setup/preprocessing happens here)
let zkvm = EreSP1::new(program, ProverResourceType::Cpu)?;
// Prepare input
// Use `with_prefixed_stdin` when guest uses `Platform::read_whole_input()`
let input = Input::new().with_prefixed_stdin(10u64.to_le_bytes().to_vec());
let expected_output = [input, 55u64.to_le_bytes()].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, ProofKind::default())?;
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-zkvm-interface = { git = "https://github.com/eth-act/ere.git" }
ere-dockerized = { git = "https://github.com/eth-act/ere.git" }// host/src/main.rs
use ere_dockerized::{CompilerKind, DockerizedCompiler, DockerizedzkVM, zkVMKind};
use ere_zkvm_interface::{
compiler::Compiler,
zkvm::{Input, ProofKind, ProverResourceType, zkVM},
};
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 (builds Docker images if needed)
let compiler =
DockerizedCompiler::new(zkVMKind::SP1, CompilerKind::RustCustomized, guest_directory)?;
let program = compiler.compile(guest_directory)?;
// 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)?;
// Prepare input
// Use `with_prefixed_stdin` when guest uses `Platform::read_whole_input()`
let input = Input::new().with_prefixed_stdin(10u64.to_le_bytes().to_vec());
let expected_output = [input, 55u64.to_le_bytes()].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, ProofKind::default())?;
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(())
}ere/
├── crates/ # Rust crates
│ ├── zkvm-interface/ # ere-zkvm-interface
│ │ └── platform/ # ere-platform-trait
│ ├── zkvm/
│ │ └── {zkvm}/ # ere-{zkvm}
│ │ └── platform/ # ere-platform-{zkvm}
│ ├── dockerized/ # ere-dockerized
│ │ ├── compiler/ # ere-compiler
│ │ └── server/ # ere-server
│ ├── io/ # ere-io
│ ├── build-utils/ # ere-build-utils
│ ├── compile-utils/ # ere-compile-utils
│ └── test-utils/ # ere-test-utils
│
├── docker/ # Dockerfile used by ere-dockerized
│ ├── Dockerfile.base # ere-base
│ └── {zkvm}/
│ ├── Dockerfile.base # ere-base-{zkvm}
│ ├── Dockerfile.compiler # ere-compiler-{zkvm}
│ └── Dockerfile.server # ere-server-{zkvm}
│
├── scripts/ # SDK installation scripts per zkVM
└── tests/ # Guest programs per zkVM for integration test
PRs and issues are welcome!
zkVMs evolve quickly; expect breaking changes. Although the API is generic, its primary target is zkEVMs, which may for example, guide the default set of precompiles.
Licensed under either of
- MIT license (LICENSE‑MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE‑APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.