Refactor docs and make Input API more explicit (#235)

This commit is contained in:
Han
2025-12-06 12:02:14 +09:00
committed by GitHub
parent 3d844b9f8b
commit 8f6cee8a32
64 changed files with 1297 additions and 456 deletions

View File

@@ -16,23 +16,29 @@ jobs:
strategy:
fail-fast: false
matrix:
crate:
- ere-zkvm-interface
- ere-io
- ere-dockerized
- ere-compiler
- ere-server
- ere-build-utils
- ere-compile-utils
- ere-test-utils
include:
- crate: ere-zkvm-interface
run_test: true
# Test disabled (Test in per-zkVM workflows)
- crate: ere-dockerized
run_test: false # They are run in per-zkVM workflows
- crate: ere-io
run_test: true
- crate: ere-build-utils
run_test: true
- crate: ere-compile-utils
run_test: true
- crate: ere-test-utils
run_test: true
skip_test: true
# Clippy only lib (Binary clippy in per-zkVM workflows)
- crate: ere-compiler
clippy_only_lib: true
- crate: ere-server
clippy_only_lib: true
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Needed to run test of ere-server
- name: Install protoc
run: |
sudo apt-get update
@@ -47,11 +53,11 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Run cargo clippy
run: cargo clippy --all-targets --package ${{ matrix.crate }} -- -D warnings
run: cargo clippy ${{ matrix.clippy_only_lib == true && '--lib' || '--all-targets' }} --package ${{ matrix.crate }} -- -D warnings
- name: Run cargo test on documentation
run: cargo test --doc --package ${{ matrix.crate }}
- name: Run cargo test
if: ${{ matrix.run_test }}
run: cargo test --release --package ${{ matrix.crate }}
if: ${{ matrix.skip_test != true }}
run: cargo test --lib --release --package ${{ matrix.crate }}

View File

@@ -243,11 +243,6 @@ jobs:
- name: Free up disk space
run: bash .github/scripts/free-up-disk-space.sh
- name: Install protoc
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:

1
Cargo.lock generated
View File

@@ -3969,6 +3969,7 @@ dependencies = [
"prost 0.13.5",
"prost-build 0.13.5",
"serde",
"tempfile",
"thiserror 2.0.12",
"tokio",
"tower-http",

View File

@@ -38,7 +38,7 @@ resolver = "2"
[workspace.package]
version = "0.0.15"
edition = "2024"
rust-version = "1.85"
rust-version = "1.88"
license = "MIT OR Apache-2.0"
[workspace.lints]

401
README.md
View File

@@ -13,154 +13,368 @@
## Table of Contents
* [Features](#features)
* [Supported zkVMs](#supported-zkvms)
* [Quick Start](#quick-start)
- [Table of Contents](#table-of-contents)
- [Supported Rust Versions (MSRV)](#supported-rust-versions-msrv)
- [Overview](#overview)
- [Architecture](#architecture)
- [The Interface](#the-interface)
- [Communication between Host and Guest](#communication-between-host-and-guest)
- [Reading Private Values from Host](#reading-private-values-from-host)
- [Writing Public Values to Host](#writing-public-values-to-host)
- [Supported zkVMs](#supported-zkvms)
- [Examples](#examples)
- [With SDK Installation](#with-sdk-installation)
- [1. Install SDKs](#1-install-sdks)
- [2. Create Guest Program](#2-create-guest-program)
- [3. Create Host](#3-create-host)
- [Docker-Only Setup](#docker-only-setup)
- [1. Create Guest Program](#1-create-guest-program)
- [2. Create Host](#2-create-host)
- [Directory Layout](#directory-layout)
- [Contributing](#contributing)
- [Disclaimer](#disclaimer)
- [License](#license)
* [Option 1: With SDK Installation](#option-1-with-sdk-installation)
* [Option 2: Docker-Only Setup](#option-2-docker-only-setup)
* [Directory Layout](#directory-layout)
* [Architecture](#architecture)
## Supported Rust Versions (MSRV)
* [The Interface](#the-interface)
* [Backend Crates](#backend-crates)
* [Input Handling](#input-handling)
* [Contributing](#contributing)
* [Disclaimer](#disclaimer)
* [License](#license)
The current MSRV (minimum supported rust version) is 1.88.
## Features
## Overview
* **Unified Rust API** for compiling, executing, proving & verifying zkVM programs
* **Pluggable backends** easily switch between different zkVMs
* **SDK bootstrap scripts** for every supported zkVM
* **Endtoend test suite** covering compilation → proof → verification for each backend
This repository contains the following crates:
- Traits
- [`ere-zkvm-interface`] - `Compiler` and `zkVM` traits for zkVM host operations
- [`ere-platform-trait`] - `Platform` trait for guest program
- Per-zkVM implementations for [`ere-zkvm-interface`] (host)
- [`ere-airbender`]
- [`ere-jolt`]
- [`ere-miden`]
- [`ere-nexus`]
- [`ere-openvm`]
- [`ere-pico`]
- [`ere-risc0`]
- [`ere-sp1`]
- [`ere-ziren`]
- [`ere-zisk`]
- Per-zkVM implementations for [`ere-platform-trait`] (guest)
- [`ere-platform-airbender`]
- [`ere-platform-jolt`]
- [`ere-platform-nexus`]
- [`ere-platform-openvm`]
- [`ere-platform-pico`]
- [`ere-platform-risc0`]
- [`ere-platform-sp1`]
- [`ere-platform-ziren`]
- [`ere-platform-zisk`]
- [`ere-dockerized`] - Docker wrapper implementation for [`ere-zkvm-interface`] of all zkVMs
- [`ere-io`] - Serialization utilities for host/guest IO communication
- Internal crates
- [`ere-compiler`] - Cli to run `Compiler` used by [`ere-dockerized`]
- [`ere-server`] - Server and client for `zkVM` operations used by [`ere-dockerized`]
- [`ere-build-utils`] - Build-time utilities
- [`ere-compile-utils`] - Compilation utilities
- [`ere-test-utils`] - Testing utilities
[`ere-zkvm-interface`]: https://github.com/eth-act/ere/tree/master/crates/zkvm-interface
[`ere-platform-trait`]: https://github.com/eth-act/ere/tree/master/crates/zkvm-interface/platform
[`ere-airbender`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/airbender
[`ere-platform-airbender`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/airbender/platform
[`ere-jolt`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/jolt
[`ere-platform-jolt`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/jolt/platform
[`ere-miden`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/miden
[`ere-nexus`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/nexus
[`ere-platform-nexus`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/nexus/platform
[`ere-openvm`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/openvm
[`ere-platform-openvm`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/openvm/platform
[`ere-pico`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/pico
[`ere-platform-pico`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/pico/platform
[`ere-risc0`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/risc0
[`ere-platform-risc0`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/risc0/platform
[`ere-sp1`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/sp1
[`ere-platform-sp1`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/sp1/platform
[`ere-ziren`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/ziren
[`ere-platform-ziren`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/ziren/platform
[`ere-zisk`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/zisk
[`ere-platform-zisk`]: https://github.com/eth-act/ere/tree/master/crates/zkvm/zisk/platform
[`ere-dockerized`]: https://github.com/eth-act/ere/tree/master/crates/dockerized
[`ere-compiler`]: https://github.com/eth-act/ere/tree/master/crates/dockerized/compiler
[`ere-server`]: https://github.com/eth-act/ere/tree/master/crates/dockerized/server
[`ere-io`]: https://github.com/eth-act/ere/tree/master/crates/io
[`ere-build-utils`]: https://github.com/eth-act/ere/tree/master/crates/build-utils
[`ere-compile-utils`]: https://github.com/eth-act/ere/tree/master/crates/compile-utils
[`ere-test-utils`]: https://github.com/eth-act/ere/tree/master/crates/test-utils
## Architecture
### The Interface
`ere-zkvm-interface` provides traits for host:
- `Compiler`
Compile a guest program into a zkVM-specific artifact (typically a RISC-V ELF, or a wrapper with preprocessing data).
- `zkVM`
Execute, 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:
- `Platform`
Provides 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}` and `ere-platform-{zkvm}` have the same version.
### Communication between Host and Guest
Host and guest communicate through raw bytes. Serialization/deserialization can be done in any way as long as they agree with each other.
#### Reading Private Values from Host
The `Input` structure holds stdin as raw bytes. There are 2 ways to use it:
1. `Input::new().with_prefixed_stdin(data)` for `Platform::read_whole_input()`
The `Platform` trait 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_stdin` automatically adds a LE u32 length prefix to the stdin. In the guest, `Platform::read_whole_input` will return only the actual data.
Without the length prefix, the `Platform::read_whole_input` will cause guest panic at runtime.
2. `Input::new().with_stdin(data)` for zkVM-specific stdin APIs
The method `Input::with_stdin` sets 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.
#### Writing Public Values to Host
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:
```rust
OutputHashedPlatform::<OpenVMPlatform, Sha256>::write_whole_output(&large_output);
```
## Supported zkVMs
- SP1
- OpenVM
- Risc Zero
- Jolt
- Pico
- Zisk
- Nexus
- Miden
| zkVM | Version | GPU |
| --------- | ---------------------------------------------------------------------- | --- |
| Airbender | [`0.5.1`](https://github.com/matter-labs/zksync-airbender/tree/v0.5.1) | Yes |
| Jolt | [`0.3.0-alpha`](https://github.com/a16z/jolt/tree/v0.3.0-alpha) | No |
| Miden | [`0.19.1`](https://github.com/0xMiden/miden-vm/tree/v0.19.1) | No |
| Nexus | [`0.3.5`](https://github.com/nexus-xyz/nexus-zkvm/tree/v0.3.5) | No |
| OpenVM | [`1.4.1`](https://github.com/openvm-org/openvm/tree/v1.4.1) | Yes |
| Pico | [`1.1.8`](https://github.com/brevis-network/pico/tree/v1.1.8) | No |
| Risc0 | [`3.0.4`](https://github.com/risc0/risc0/tree/v3.0.4) | Yes |
| SP1 | [`5.2.3`](https://github.com/succinctlabs/sp1/tree/v5.2.3) | Yes |
| Ziren | [`1.2.2`](https://github.com/ProjectZKM/Ziren/tree/v1.2.2) | No |
| Zisk | [`0.13.0`](https://github.com/0xPolygonHermez/zisk/tree/v0.13.0) | Yes |
## Prerequisites
## Examples
The following dependencies are required:
- `protobuf-compiler`
## Quick Start
This guide assumes you have Rust and Cargo installed. If not, please refer to the [Rust installation guide](https://www.rust-lang.org/tools/install).
Choose your setup approach:
### Option 1: With SDK Installation
### With SDK Installation
Install the required zkVM SDKs locally for better performance and debugging.
#### 1. Install SDKs
Install the SP1 SDK as an example
```bash
bash scripts/sdk_installers/install_sp1_sdk.sh
```
#### 2. Add Dependencies
#### 2. Create Guest Program
```toml
# Cargo.toml
# guest/Cargo.toml
[workspace]
[package]
name = "guest"
edition = "2024"
[dependencies]
ere-zkvm-interface = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" }
ere-sp1 = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" }
ere-platform-sp1 = { git = "https://github.com/eth-act/ere.git" }
```
#### 3. Compile & Prove Example
```rust
// 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
}
```
#### 3. Create Host
```toml
# 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" }
```
```rust
// main.rs
use ere_sp1::{EreSP1, RV32_IM_SUCCINCT_ZKVM_ELF};
// 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 = std::path::Path::new("workspace/guest");
let guest_directory = Path::new("path/to/guest");
// Compile guest
let compiler = RV32_IM_SUCCINCT_ZKVM_ELF;
// Compile guest program with SP1 customized toolchain
let compiler = RustRv32imaCustomized;
let program = compiler.compile(guest_directory)?;
// Create zkVM instance
// Create zkVM instance (setup/preprocessing happens here)
let zkvm = EreSP1::new(program, ProverResourceType::Cpu)?;
// Serialize input
let input = Input::new(42u32.to_le_bytes().to_vec());
// 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::Compressed)?;
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(())
}
```
### Option 2: Docker-Only Setup
### Docker-Only Setup
Use Docker for zkVM operations without installing SDKs locally. Only requires Docker to be installed.
#### 1. Add Dependencies
#### 1. Create Guest Program
We use the same guest program created above.
#### 2. Create Host
```toml
# Cargo.toml
# host/Cargo.toml
[workspace]
[package]
name = "host"
edition = "2024"
[dependencies]
ere-zkvm-interface = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" }
ere-dockerized = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" }
ere-zkvm-interface = { git = "https://github.com/eth-act/ere.git" }
ere-dockerized = { git = "https://github.com/eth-act/ere.git" }
```
#### 2. Compile & Prove Example
```rust
// main.rs
// 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 = std::path::Path::new("workspace/guest");
let guest_directory = Path::new("path/to/guest");
// Compile guest
let compiler = DockerizedCompiler::new(
zkVMKind::SP1,
CompilerKind::RustCustomized,
std::path::Path::new("workspace"),
);
// 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
// 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)?;
// Serialize input
let input = Input::new(42u32.to_le_bytes().to_vec());
// 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::Compressed)?;
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(())
}
@@ -169,31 +383,32 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
## Directory Layout
```
crates/
zkvm-interface/ ← core traits & types
zkvm/{backend}/ ← backend adapters (sp1, openvm, …)
tests/ ← guest programs & integration tests
scripts/sdk_installers/ ← SDK install helpers
docker/ ← Dockerfiles & build contexts
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
```
## Architecture
### The Interface
`ere-zkvm-interface` exposes two core traits:
* **Compiler** compile a guest project into the correct zkVM artifact. For most this will be a RISCV ELF binary or some type that wraps it and includes extra metadata such as a proving and verifying key.
* **zkVM** execute, prove & verify that artifact. A zkVM instance is created for specific `program`, where the `program` comes from the `Compiler`.
### Backend Crates
Each `ere-{backend}` crate implements the above traits for its zkVM.
### Input Handling
The input is opaque to `zkVM` and will be passed as is, de/serialization needed to be handled by guest/host themselves.
## Contributing
PRs and issues are welcome!

View File

@@ -34,16 +34,16 @@ ere-zisk = { workspace = true, features = ["zkvm"], optional = true }
ere-zkvm-interface = { workspace = true, features = ["clap"] }
[dev-dependencies]
prost-build.workspace = true
tempfile.workspace = true
twirp-build.workspace = true
[lints]
workspace = true
[build-dependencies]
prost-build.workspace = true
twirp-build.workspace = true
[features]
default = []
default = ["client"]
client = []
server = ["dep:clap", "dep:tower-http", "dep:tracing", "dep:tracing-subscriber", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
# zkVM

View File

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

View File

@@ -0,0 +1,278 @@
// This file is @generated by prost-build.
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteRequest {
#[prost(bytes = "vec", tag = "1")]
pub input_stdin: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", optional, tag = "2")]
pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteResponse {
#[prost(oneof = "execute_response::Result", tags = "1, 2")]
pub result: ::core::option::Option<execute_response::Result>,
}
/// Nested message and enum types in `ExecuteResponse`.
pub mod execute_response {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Ok(super::ExecuteOk),
#[prost(string, tag = "2")]
Err(::prost::alloc::string::String),
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteOk {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveRequest {
#[prost(bytes = "vec", tag = "1")]
pub input_stdin: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", optional, tag = "2")]
pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
#[prost(enumeration = "ProofKind", tag = "3")]
pub proof_kind: i32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveResponse {
#[prost(oneof = "prove_response::Result", tags = "1, 2")]
pub result: ::core::option::Option<prove_response::Result>,
}
/// Nested message and enum types in `ProveResponse`.
pub mod prove_response {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Ok(super::ProveOk),
#[prost(string, tag = "2")]
Err(::prost::alloc::string::String),
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveOk {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub proof: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "3")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyRequest {
#[prost(bytes = "vec", tag = "1")]
pub proof: ::prost::alloc::vec::Vec<u8>,
#[prost(enumeration = "ProofKind", tag = "2")]
pub proof_kind: i32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyResponse {
#[prost(oneof = "verify_response::Result", tags = "1, 2")]
pub result: ::core::option::Option<verify_response::Result>,
}
/// Nested message and enum types in `VerifyResponse`.
pub mod verify_response {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Ok(super::VerifyOk),
#[prost(string, tag = "2")]
Err(::prost::alloc::string::String),
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyOk {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ProofKind {
Compressed = 0,
Groth16 = 1,
}
impl ProofKind {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Compressed => "Compressed",
Self::Groth16 => "Groth16",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"Compressed" => Some(Self::Compressed),
"Groth16" => Some(Self::Groth16),
_ => None,
}
}
}
pub use twirp;
#[twirp::async_trait::async_trait]
pub trait ZkvmService: Send + Sync {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>>;
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>>;
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>>;
}
#[twirp::async_trait::async_trait]
impl<T> ZkvmService for std::sync::Arc<T>
where
T: ZkvmService + Sync + Send,
{
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
T::execute(&*self, req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
T::prove(&*self, req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
T::verify(&*self, req).await
}
}
pub fn router<T>(api: T) -> twirp::Router
where
T: ZkvmService + Clone + Send + Sync + 'static,
{
twirp::details::TwirpRouterBuilder::new("/api.ZkvmService", api)
.route(
"/Execute",
|api: T, req: twirp::Request<ExecuteRequest>| async move {
api.execute(req).await
},
)
.route(
"/Prove",
|api: T, req: twirp::Request<ProveRequest>| async move {
api.prove(req).await
},
)
.route(
"/Verify",
|api: T, req: twirp::Request<VerifyRequest>| async move {
api.verify(req).await
},
)
.build()
}
#[twirp::async_trait::async_trait]
impl ZkvmService for twirp::client::Client {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
self.request("api.ZkvmService/Execute", req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
self.request("api.ZkvmService/Prove", req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
self.request("api.ZkvmService/Verify", req).await
}
}
#[allow(dead_code)]
pub mod handler {
use super::*;
pub struct ZkvmServiceHandler {
inner: std::sync::Arc<dyn ZkvmService>,
}
impl ZkvmServiceHandler {
#[allow(clippy::new_ret_no_self)]
pub fn new<M: ZkvmService + 'static>(inner: M) -> Self {
Self {
inner: std::sync::Arc::new(inner),
}
}
}
#[twirp::async_trait::async_trait]
impl twirp::client::DirectHandler for ZkvmServiceHandler {
fn service(&self) -> &str {
"api.ZkvmService"
}
async fn handle(
&self,
method: &str,
req: twirp::reqwest::Request,
) -> twirp::Result<twirp::reqwest::Response> {
match method {
"Execute" => {
twirp::details::encode_response(
self
.inner
.execute(twirp::details::decode_request(req).await?)
.await?,
)
}
"Prove" => {
twirp::details::encode_response(
self
.inner
.prove(twirp::details::decode_request(req).await?)
.await?,
)
}
"Verify" => {
twirp::details::encode_response(
self
.inner
.verify(twirp::details::decode_request(req).await?)
.await?,
)
}
_ => {
Err(
twirp::bad_route(
format!(
"unknown rpc `{method}` for service `{}`, url: {:?}",
"api.ZkvmService", req.url()
),
),
)
}
}
}
}
}

View File

@@ -1,9 +1,11 @@
pub mod client;
#[rustfmt::skip]
pub mod api;
#[allow(dead_code)]
pub(crate) mod api {
include!(concat!(env!("OUT_DIR"), "/api.rs"));
}
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "server")]
pub mod server;
#[cfg(test)]
mod test;

View File

@@ -0,0 +1,28 @@
use std::{env, fs, path::PathBuf};
/// To sync generated `api.rs`, run:
///
/// ```
/// cargo test --package ere-server --no-default-features --lib -- test::api_generation --exact
/// ```
#[test]
fn api_generation() {
let tempdir = tempfile::tempdir().unwrap();
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
prost_build::Config::new()
.out_dir(tempdir.path())
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") // enable support for JSON encoding
.service_generator(twirp_build::service_generator())
.compile_protos(&[dir.join("proto").join("api.proto")], &[dir.join("proto")])
.unwrap();
let latest = tempdir.path().join("api.rs");
let current = dir.join("src").join("api.rs");
// If it's in CI env, don't overwrite but only check if it's up-to-date.
if env::var_os("GITHUB_ACTIONS").is_none() {
fs::copy(&latest, &current).unwrap();
}
assert_eq!(fs::read(&latest).unwrap(), fs::read(&current).unwrap());
}

View File

@@ -49,7 +49,7 @@
//! let zkvm = DockerizedzkVM::new(zkvm_kind, program, resource)?;
//!
//! // Serialize input
//! let input = Input::new(42u32.to_le_bytes().to_vec());
//! let input = Input::new().with_stdin(42u32.to_le_bytes().to_vec());
//!
//! // Execute program
//! let (public_values, execution_report) = zkvm.execute(&input)?;

View File

@@ -406,7 +406,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case().into_output_sha256()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -420,7 +420,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -434,7 +434,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -448,7 +448,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case().into_output_sha256()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -462,7 +462,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -476,7 +476,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -490,7 +490,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -504,7 +504,7 @@ mod test {
"basic",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);
@@ -518,7 +518,7 @@ mod test {
"basic_rust",
[BasicProgram::<BincodeLegacy>::valid_test_case()],
[
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input()
]
);

View File

@@ -4,11 +4,11 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use core::{error::Error, fmt::Debug};
extern crate alloc;
#[cfg(feature = "serde")]
pub mod serde;

View File

@@ -1 +0,0 @@
pub use sha2::Sha256;

View File

@@ -84,7 +84,7 @@ impl<P: Program> Deref for ProgramTestCase<P> {
impl<P: Program> TestCase for ProgramTestCase<P> {
fn input(&self) -> Input {
Input::new(P::Io::serialize_input(&self.input).unwrap())
Input::new().with_prefixed_stdin(P::Io::serialize_input(&self.input).unwrap())
}
fn assert_output(&self, public_values: &[u8]) {

View File

@@ -2,7 +2,6 @@
extern crate alloc;
pub mod guest;
pub mod program;
#[cfg(feature = "host")]

View File

@@ -1,5 +1,6 @@
use ere_io::Io;
use ere_platform_trait::Platform;
use ere_platform_trait::{OutputHashedPlatform, Platform};
use sha2::Sha256;
pub mod basic;
@@ -16,4 +17,8 @@ pub trait Program {
let output_bytes = Self::Io::serialize_output(&output).unwrap();
P::write_whole_output(&output_bytes);
}
fn run_output_sha256<P: Platform>() {
Self::run::<OutputHashedPlatform<P, Sha256>>()
}
}

View File

@@ -3,15 +3,20 @@
extern crate alloc;
use alloc::vec::Vec;
use core::{marker::PhantomData, ops::Deref};
pub mod output_hasher;
pub use digest::Digest;
/// Platform dependent methods.
pub trait Platform {
/// Reads the whole input at once from host.
///
/// The stdin passed must have a LE u32 length prefix, because some zkVMs
/// don't provide access to the stdin length.
/// Use `Input::new().with_prefixed_stdin(stdin)` for convenience.
///
/// Note that this function should only be called once.
fn read_whole_input() -> Vec<u8>;
fn read_whole_input() -> impl Deref<Target = [u8]>;
/// Writes the whole output at once to host.
///
@@ -54,3 +59,78 @@ pub trait Platform {
t
}
}
/// Wrapper for `Platform` implementation that hashes output before calling
/// the inner `P::write_whole_output`.
pub struct OutputHashedPlatform<P, D>(PhantomData<(P, D)>);
impl<P, D> Platform for OutputHashedPlatform<P, D>
where
P: Platform,
D: Digest,
{
#[inline]
fn read_whole_input() -> impl Deref<Target = [u8]> {
P::read_whole_input()
}
#[inline]
fn write_whole_output(output: &[u8]) {
P::write_whole_output(&D::digest(output));
}
#[inline]
fn print(message: &str) {
P::print(message);
}
#[inline]
fn cycle_count() -> u64 {
P::cycle_count()
}
#[inline]
fn cycle_scope_start(name: &str) {
P::cycle_scope_start(name)
}
#[inline]
fn cycle_scope_end(name: &str) {
P::cycle_scope_end(name)
}
#[inline]
fn cycle_scope<T>(name: &str, f: impl FnOnce() -> T) -> T {
P::cycle_scope(name, f)
}
}
/// Stdin with a LE u32 length prefix.
///
/// Dereferencing it returns slice to the actual data.
pub struct LengthPrefixedStdin(Vec<u8>);
impl Deref for LengthPrefixedStdin {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0[4..]
}
}
impl LengthPrefixedStdin {
pub fn new(stdin: Vec<u8>) -> Self {
assert!(
stdin.len() >= 4,
"stdin must have a LE u32 length prefix; use Input::with_prefixed_length(stdin) on the host side"
);
let len = u32::from_le_bytes(stdin[..4].try_into().unwrap()) as usize;
assert_eq!(
len,
stdin.len() - 4,
"Length mismatch: stdin length prefix indicated {len} bytes, but got {} bytes; use Input::with_prefixed_length(stdin) on the host side",
stdin.len() - 4
);
Self(stdin)
}
}

View File

@@ -1,90 +0,0 @@
use core::{marker::PhantomData, ops::Deref};
use digest::{
Digest, Output, OutputSizeUser,
generic_array::{ArrayLength, GenericArray},
};
pub use digest;
/// A hasher that given the output, returns a hash of it.
pub trait OutputHasher {
type Hash<'a>: Deref<Target = [u8]>;
fn output_hash(output: &[u8]) -> Self::Hash<'_>;
}
/// A hasher that given the output, returns a fixed-size hash of it.
pub trait FixedOutputHasher: OutputHasher + OutputSizeUser {}
impl<T: OutputHasher + OutputSizeUser> FixedOutputHasher for T {}
/// A marker used to mark [`IdentityOutput`] to accept unsized output.
pub struct Unsized;
/// [`OutputHasher`] implementation that returns output as is.
///
/// By setting generic `U = Unsized` it takes output with any size.
///
/// By setting generic `U = typenum::U{SIZE}` it expects the output to match
/// the `SIZE`.
pub struct IdentityOutput<U = Unsized>(PhantomData<U>);
impl OutputHasher for IdentityOutput<Unsized> {
type Hash<'a> = &'a [u8];
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
output
}
}
impl<U: ArrayLength<u8> + 'static> OutputSizeUser for IdentityOutput<U> {
type OutputSize = U;
}
impl<U: ArrayLength<u8> + 'static> OutputHasher for IdentityOutput<U> {
type Hash<'a> = GenericArray<u8, U>;
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
assert!(
output.len() == Self::output_size(),
"output length should be equal to {}",
Self::output_size()
);
let mut hash = Output::<Self>::default();
hash.copy_from_slice(output);
hash
}
}
/// [`OutputHasher`] implementation that returns output with 0s padding.
///
/// By setting generic `U = typenum::U{SIZE}` it expects the output to be less
/// than or equal to the `SIZE`.
pub struct PaddedOutput<U>(PhantomData<U>);
impl<U: ArrayLength<u8> + 'static> OutputSizeUser for PaddedOutput<U> {
type OutputSize = U;
}
impl<U: ArrayLength<u8> + 'static> OutputHasher for PaddedOutput<U> {
type Hash<'a> = GenericArray<u8, U>;
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
assert!(
output.len() <= Self::output_size(),
"output length should be less than or equal to {}",
Self::output_size()
);
let mut hash = Output::<Self>::default();
hash[..output.len()].copy_from_slice(output);
hash
}
}
impl<D: Digest> OutputHasher for D {
type Hash<'a> = Output<D>;
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
D::digest(output)
}
}

View File

@@ -22,10 +22,10 @@ pub struct Input {
}
impl Input {
/// Creates a new `Input` with the given stdin.
pub fn new(stdin: Vec<u8>) -> Self {
/// Creates a new `Input` with the empty stdin.
pub fn new() -> Self {
Self {
stdin,
stdin: Vec::new(),
proofs: None,
}
}
@@ -49,6 +49,23 @@ impl Input {
})
}
/// Sets stdin and returns a new `Input`.
pub fn with_stdin(mut self, stdin: Vec<u8>) -> Self {
self.stdin = stdin;
self
}
/// Sets stdin with a LE u32 length prefix and returns a new `Input`.
///
/// The `Platform::read_whole_input` requires stdin to have a LE u32 length
/// prefix for efficiency reason.
pub fn with_prefixed_stdin(mut self, stdin: Vec<u8>) -> Self {
self.stdin = Vec::with_capacity(4 + stdin.len());
self.stdin.extend((stdin.len() as u32).to_le_bytes());
self.stdin.extend(stdin);
self
}
/// Serializes the given proofs and returns a new `Input` with them set.
///
/// Consumes `self` and returns an error if serialization fails.

View File

@@ -3,42 +3,39 @@
extern crate alloc;
use alloc::vec::Vec;
use core::{array, iter::repeat_with, marker::PhantomData};
use ere_platform_trait::output_hasher::FixedOutputHasher;
use core::{array, iter::repeat_with, ops::Deref};
pub use airbender_riscv_common as riscv_common;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum::U32},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
/// Airbender [`Platform`] implementation.
///
/// Because Airbender only support public values up to 32 bytes, so
/// - If the guest has output bytes more than 32 bytes, it should use a
/// cryptographic hash function for the generic `H` (for example `Sha256`).
/// - If the guest has output bytes less than 32 bytes, it should use
/// [`PaddedOutput`] for the generic `H`
pub struct AirbenderPlatform<H>(PhantomData<H>);
/// Note that the maximum output size is 32 bytes, and output less than 32
/// bytes will be padded to 32 bytes.
pub struct AirbenderPlatform;
impl<H: FixedOutputHasher<OutputSize = U32>> Platform for AirbenderPlatform<H> {
fn read_whole_input() -> Vec<u8> {
impl Platform for AirbenderPlatform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
let len = riscv_common::csr_read_word() as usize;
repeat_with(riscv_common::csr_read_word)
.take(len.div_ceil(4))
.flat_map(u32::to_le_bytes)
.take(len)
.collect()
.collect::<Vec<_>>()
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
let words = array::from_fn(|i| u32::from_le_bytes(array::from_fn(|j| hash[4 * i + j])));
assert!(
output.len() <= 32,
"Maximum output size is 32 bytes, got {} bytes",
output.len()
);
let words = array::from_fn(|i| u32::from_le_bytes(array::from_fn(|j| output[4 * i + j])));
riscv_common::zksync_os_finish_success(&words);
}
fn print(message: &str) {
fn print(_message: &str) {
#[cfg(feature = "uart")]
core::fmt::Write::write_str(&mut riscv_common::QuasiUART::new(), message).unwrap();
core::fmt::Write::write_str(&mut riscv_common::QuasiUART::new(), _message).unwrap();
}
}

View File

@@ -1,3 +1,48 @@
//! Airbender [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_airbender_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-airbender` dependency.
//!
//! To install `airbender-cli` with GPU proving support, make sure CUDA 12.9 is
//! installed, and run [`install_airbender_sdk.sh`] with env `CUDA=1` set.
//!
//! ## `Compiler` requirements
//!
//! The `Compiler` implementation requires external tools installed and
//! available in `PATH`:
//!
//! - `rust-objcopy` - Used by compiler to convert ELF to binary
//!
//! ## `zkVM` requirements
//!
//! The `zkVM` implementation requires external tools installed and available in
//! `PATH`:
//!
//! - Installation via [`install_airbender_sdk.sh`] - `airbender-cli` used by
//! `zkVM::execute` and `zkVM::prove`
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target |
//! | ------------- | :------: | ----------------------------- |
//! | `RustRv32ima` | Rust | `riscv32ima-unknown-none-elf` |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//!
//! [`install_airbender_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_airbender_sdk.sh
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -149,7 +149,7 @@ mod tests {
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -171,7 +171,7 @@ mod tests {
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -4,7 +4,7 @@ use airbender_execution_utils::{
universal_circuit_verifier_vk, verify_recursion_log_23_layer,
};
use ere_zkvm_interface::zkvm::{CommonError, PublicValues};
use std::{array, fs, io::BufRead, iter, process::Command};
use std::{array, fs, io::BufRead, process::Command};
use tempfile::tempdir;
/// Verification key hash chain.
@@ -217,8 +217,8 @@ impl AirbenderSdk {
/// Encode input with length prefixed to hex string for `airbender-cli`.
fn encode_input(input: &[u8]) -> String {
iter::once((input.len() as u32).to_le_bytes().as_slice())
.chain(input.chunks(4))
input
.chunks(4)
.map(|chunk| {
let mut bytes = [0u8; 4];
bytes[..chunk.len()].copy_from_slice(chunk);

View File

@@ -2,14 +2,9 @@
extern crate alloc;
use alloc::vec::Vec;
use core::{marker::PhantomData, slice};
use ere_platform_trait::output_hasher::OutputHasher;
use core::{marker::PhantomData, ops::Deref};
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use jolt_sdk as jolt;
// FIXME: Because the crate `jolt-common` is not `no_std` compatible, so we have
@@ -70,30 +65,37 @@ impl JoltMemoryConfig for DefaulJoltMemoryConfig {
}
/// Jolt [`Platform`] implementation.
pub struct JoltPlatform<C = DefaulJoltMemoryConfig, H = IdentityOutput>(PhantomData<(C, H)>);
pub struct JoltPlatform<C = DefaulJoltMemoryConfig>(PhantomData<C>);
impl<C: JoltMemoryConfig, H: OutputHasher> Platform for JoltPlatform<C, H> {
fn read_whole_input() -> Vec<u8> {
impl<C: JoltMemoryConfig> Platform for JoltPlatform<C> {
fn read_whole_input() -> impl Deref<Target = [u8]> {
let memory_layout = C::memory_layout();
let input_ptr = memory_layout.input_start as *const u8;
let max_input_len = memory_layout.max_input_size as usize;
let input_slice = unsafe { slice::from_raw_parts(input_ptr, max_input_len) };
let (input, _) = jolt::postcard::take_from_bytes(input_slice).unwrap();
input
assert!(max_input_len > 4);
let len_bytes = unsafe { core::slice::from_raw_parts(input_ptr, 4) };
let len = u32::from_le_bytes(len_bytes.try_into().unwrap()) as usize;
assert!(
len <= max_input_len - 4,
"Maximum input size is {} bytes, got {len}",
max_input_len - 4,
);
unsafe { core::slice::from_raw_parts(input_ptr.add(4), len) }.to_vec()
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
let memory_layout = C::memory_layout();
let output_ptr = memory_layout.output_start as *mut u8;
let max_output_len = memory_layout.max_output_size as usize;
let output_slice = unsafe { core::slice::from_raw_parts_mut(output_ptr, max_output_len) };
jolt::postcard::to_slice(&*hash, output_slice).unwrap_or_else(|err| match err {
jolt::postcard::Error::SerializeBufferFull => {
panic!("Maximum output size is {max_output_len} bytes")
}
err => panic!("`postcard::to_slice` failed: {err:?}"),
});
let len = output.len();
assert!(
len <= max_output_len - 4,
"Maximum output size is {} bytes, got {len}",
max_output_len - 4,
);
let output_slice = unsafe { core::slice::from_raw_parts_mut(output_ptr, len + 4) };
output_slice[..4].copy_from_slice(&(output.len() as u32).to_le_bytes());
output_slice[4..].copy_from_slice(&output);
}
fn print(message: &str) {

View File

@@ -78,6 +78,6 @@ mod tests {
let program = RustRv64imac.compile(&guest_directory).unwrap();
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
zkvm.execute(&Input::default()).unwrap();
zkvm.execute(&Input::new()).unwrap();
}
}

View File

@@ -1,3 +1,36 @@
//! Jolt [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_jolt_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-jolt` dependency.
//!
//! ## `Compiler` requirements
//!
//! - `jolt`
//! - Install custom Rust toolchain via `jolt install-toolchain` - Used by `RustRv64imaCustomized`
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ------------------------- | :------: | ------------------------------ | ------------------ |
//! | `RustRv64imacCustomized` | Rust | `riscv64imac-jolt-zkvm-elf` | With `std` support |
//! | `RustRv64imac` | Rust | `riscv64imac-unknown-none-elf` | |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//!
//! [`install_jolt_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_jolt_sdk.sh
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -153,7 +153,7 @@ mod tests {
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -179,7 +179,7 @@ mod tests {
let _guard = PROVE_LOCK.lock().unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -7,6 +7,9 @@ pub enum Error {
#[error(transparent)]
CommonError(#[from] CommonError),
#[error("Output is expected to have length prefix")]
InvalidOutput,
// Execute
#[error("Execution panics")]
ExecutionPanic,

View File

@@ -1,6 +1,6 @@
use crate::zkvm::Error;
use core::cmp::min;
use ere_zkvm_interface::zkvm::{CommonError, PublicValues};
use core::{array::from_fn, cmp::min};
use ere_zkvm_interface::zkvm::PublicValues;
use jolt_ark_serialize::{self as ark_serialize, CanonicalDeserialize, CanonicalSerialize};
use jolt_common::constants::{
DEFAULT_MAX_INPUT_SIZE, DEFAULT_MAX_OUTPUT_SIZE, DEFAULT_MAX_TRACE_LENGTH, DEFAULT_MEMORY_SIZE,
@@ -14,7 +14,6 @@ use jolt_sdk::{
F, Jolt, JoltDevice, JoltProverPreprocessing, JoltRV64IMAC, JoltVerifierPreprocessing,
MemoryConfig, MemoryLayout, PCS,
guest::program::{decode, trace},
postcard,
};
#[derive(CanonicalSerialize, CanonicalDeserialize)]
@@ -64,25 +63,20 @@ impl JoltSdk {
}
pub fn execute(&self, input: &[u8]) -> Result<(PublicValues, u64), Error> {
let (cycles, _, io) = trace(
&self.elf,
None,
&serialize_input(input)?,
&self.memory_config,
);
let (cycles, _, io) = trace(&self.elf, None, input, &self.memory_config);
if io.panic {
return Err(Error::ExecutionPanic);
}
let public_values = deserialize_output(&io.outputs)?;
let public_values = extract_public_values(&io.outputs)?;
Ok((public_values, cycles.len() as _))
}
pub fn prove(&self, input: &[u8]) -> Result<(PublicValues, JoltProof), Error> {
let (proof, io, _) = JoltRV64IMAC::prove(&self.pk, &self.elf, &serialize_input(input)?);
let (proof, io, _) = JoltRV64IMAC::prove(&self.pk, &self.elf, input);
if io.panic {
return Err(Error::ExecutionPanic);
}
let public_values = deserialize_output(&io.outputs)?;
let public_values = extract_public_values(&io.outputs)?;
let proof = JoltProof {
proof,
inputs: io.inputs,
@@ -103,28 +97,26 @@ impl JoltSdk {
},
None,
)?;
let public_values = deserialize_output(&proof.outputs)?;
let public_values = extract_public_values(&proof.outputs)?;
Ok(public_values)
}
}
fn serialize_input(bytes: &[u8]) -> Result<Vec<u8>, Error> {
Ok(postcard::to_stdvec(bytes)
.map_err(|err| CommonError::serialize("input", "postcard", err))?)
}
fn deserialize_output(output: &[u8]) -> Result<Vec<u8>, Error> {
// Note taht for execute the bytes are padded to size of multiple of 8, but for
// prove the bytes are truncated.
fn extract_public_values(output: &[u8]) -> Result<Vec<u8>, Error> {
Ok(if output.is_empty() {
Vec::new()
} else {
let (len, bytes) = postcard::take_from_bytes::<u64>(output)
.map_err(|err| CommonError::deserialize("output", "postcard", err))?;
let mut output = vec![0; len as usize];
// For execute the bytes are padded to size of multiple of 8, but for
// prove the bytes are truncated if there are trailing zeros, so here we
// take the min.
let len = min(bytes.len(), output.len());
output[..len].copy_from_slice(&bytes[..len]);
output
let len = u32::from_le_bytes(from_fn(|i| output.get(i).copied().unwrap_or(0))) as usize;
if output.len() > (len + 4).next_multiple_of(8) {
return Err(Error::InvalidOutput);
}
let mut public_values = vec![0; len];
if let Some((_, output)) = output.split_at_checked(4) {
let len = min(len, output.len());
public_values[..len].copy_from_slice(&output[..len]);
}
public_values
})
}

View File

@@ -1,3 +1,23 @@
//! Miden [`Compiler`] and [`zkVM`] implementation.
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target |
//! | ---------- | :------------: | ---------- |
//! | `MidenAsm` | Miden Assembly | Miden MAST |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -175,7 +175,7 @@ pub fn felts_to_bytes(felts: &[Felt]) -> Vec<u8> {
/// Convert bytes into Miden field elements.
pub fn bytes_to_felts(bytes: &[u8]) -> Result<Vec<Felt>, Error> {
if bytes.len() % 8 != 0 {
if !bytes.len().is_multiple_of(8) {
let err = anyhow::anyhow!(
"Invalid bytes length {}, expected multiple of 8",
bytes.len()
@@ -218,11 +218,11 @@ mod tests {
let const_b = Felt::ONE / Felt::ONE.double();
let expected_sum = const_a + const_b;
let input = felts_to_bytes(&[const_a, const_b]);
let stdin = felts_to_bytes(&[const_a, const_b]);
// Prove
let (prover_public_values, proof, _) = zkvm
.prove(&Input::new(input), ProofKind::default())
.prove(&Input::new().with_stdin(stdin), ProofKind::default())
.unwrap();
// Verify
@@ -242,11 +242,11 @@ mod tests {
let n_iterations = 50u32;
let expected_fib = Felt::try_from(12_586_269_025u64).unwrap();
let input = felts_to_bytes(&[Felt::from(0u32), Felt::from(1u32), Felt::from(n_iterations)]);
let stdin = felts_to_bytes(&[Felt::from(0u32), Felt::from(1u32), Felt::from(n_iterations)]);
// Prove
let (prover_public_values, proof, _) = zkvm
.prove(&Input::new(input), ProofKind::default())
.prove(&Input::new().with_stdin(stdin), ProofKind::default())
.unwrap();
// Verify
@@ -263,10 +263,10 @@ mod tests {
let program = load_miden_program("add");
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
let empty_inputs = Input::new(Vec::new());
let empty_inputs = Input::new();
assert!(zkvm.execute(&empty_inputs).is_err());
let insufficient_inputs = Input::new(felts_to_bytes(&[Felt::from(5u32)]));
let insufficient_inputs = Input::new().with_stdin(felts_to_bytes(&[Felt::from(5u32)]));
assert!(zkvm.execute(&insufficient_inputs).is_err());
}
}

View File

@@ -2,27 +2,22 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
use core::ops::Deref;
use ere_platform_trait::LengthPrefixedStdin;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use nexus_rt;
/// Nexus [`Platform`] implementation.
pub struct NexusPlatform<H = IdentityOutput>(PhantomData<H>);
pub struct NexusPlatform;
impl<H: OutputHasher> Platform for NexusPlatform<H> {
fn read_whole_input() -> Vec<u8> {
nexus_rt::read_private_input().unwrap()
impl Platform for NexusPlatform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
LengthPrefixedStdin::new(nexus_rt::read_private_input().unwrap())
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
nexus_rt::write_public_output(&*hash).unwrap()
nexus_rt::write_public_output(output).unwrap()
}
fn print(message: &str) {

View File

@@ -1,3 +1,23 @@
//! Nexus [`Compiler`] and [`zkVM`] implementation.
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target |
//! | ------------ | :------: | --------------------------- |
//! | `RustRv32i` | Rust | `riscv32i-unknown-none-elf` |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -205,7 +205,7 @@ mod tests {
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -227,7 +227,7 @@ mod tests {
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -2,33 +2,30 @@
extern crate alloc;
use alloc::vec::Vec;
use core::{marker::PhantomData, ops::Deref};
use ere_platform_trait::output_hasher::FixedOutputHasher;
use core::{array::from_fn, ops::Deref};
use ere_platform_trait::LengthPrefixedStdin;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum::U32},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use openvm;
/// OpenVM [`Platform`] implementation.
///
/// Because OpenVM only support public values up to 32 bytes, so
/// - If the guest has output bytes more than 32 bytes, it should use a
/// cryptographic hash function for the generic `H` (for example `Sha256`).
/// - If the guest has output bytes less than 32 bytes, it should use
/// [`PaddedOutput`] for the generic `H`
pub struct OpenVMPlatform<H>(PhantomData<H>);
/// Note that the maximum output size is 32 bytes, and output less than 32
/// bytes will be padded to 32 bytes.
pub struct OpenVMPlatform;
impl<H: FixedOutputHasher<OutputSize = U32>> Platform for OpenVMPlatform<H> {
fn read_whole_input() -> Vec<u8> {
openvm::io::read_vec()
impl Platform for OpenVMPlatform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
LengthPrefixedStdin::new(openvm::io::read_vec())
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output).deref().try_into().unwrap();
openvm::io::reveal_bytes32(hash);
assert!(
output.len() <= 32,
"Maximum output size is 32 bytes, got {} bytes",
output.len()
);
openvm::io::reveal_bytes32(from_fn(|i| output.get(i).copied().unwrap_or(0)));
}
fn print(message: &str) {

View File

@@ -79,6 +79,6 @@ mod tests {
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
zkvm.execute(&Input::default()).unwrap();
zkvm.execute(&Input::new()).unwrap();
}
}

View File

@@ -1,3 +1,40 @@
//! OpenVM [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_openvm_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-openvm` dependency.
//!
//! To use with GPU proving support, make sure CUDA 12.9 is installed, and turn
//! on the `cuda` feature.
//!
//! ## `zkVM` requirements
//!
//! - `cargo-openvm`
//! - Setup via `cargo openvm setup` - Setup aggregation keys used by
//! `zkVM::prove`
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ----------------------- | :------: | ----------------------------- | ------------------ |
//! | `RustRv32imaCustomized` | Rust | `riscv32im-risc0-zkvm-elf` | With `std` support |
//! | `RustRv32ima` | Rust | `riscv32ima-unknown-none-elf` | |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//!
//! [`install_openvm_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_openvm_sdk.sh
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -292,7 +292,7 @@ mod tests {
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -314,7 +314,7 @@ mod tests {
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -2,27 +2,23 @@
extern crate alloc;
use alloc::{format, vec::Vec};
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
use alloc::format;
use core::ops::Deref;
use ere_platform_trait::LengthPrefixedStdin;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use pico_sdk;
/// Pico [`Platform`] implementation.
pub struct PicoPlatform<H = IdentityOutput>(PhantomData<H>);
pub struct PicoPlatform;
impl<H: OutputHasher> Platform for PicoPlatform<H> {
fn read_whole_input() -> Vec<u8> {
pico_sdk::io::read_vec()
impl Platform for PicoPlatform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
LengthPrefixedStdin::new(pico_sdk::io::read_vec())
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
pico_sdk::io::commit_bytes(&hash);
pico_sdk::io::commit_bytes(output);
}
fn print(message: &str) {

View File

@@ -70,6 +70,6 @@ mod tests {
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
zkvm.execute(&Input::default()).unwrap();
zkvm.execute(&Input::new()).unwrap();
}
}

View File

@@ -1,3 +1,35 @@
//! Pico [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_pico_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-pico` dependency.
//!
//! ## `Compiler` requirements
//!
//! - `cargo-pico` - Used by `RustRv32imaCustomized`
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ----------------------- | :------: | ----------------------------- | ------------------ |
//! | `RustRv32imaCustomized` | Rust | `riscv32im-risc0-zkvm-elf` | With `std` support |
//! | `RustRv32ima` | Rust | `riscv32ima-unknown-none-elf` | |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//!
//! [`install_pico_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_pico_sdk.sh
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -225,7 +225,7 @@ mod tests {
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -247,7 +247,7 @@ mod tests {
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -2,35 +2,30 @@
extern crate alloc;
use alloc::{vec, vec::Vec};
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
use alloc::vec;
use core::{ops::Deref, slice};
use risc0_zkvm::guest::env::Write;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use risc0_zkvm;
/// Risc0 [`Platform`] implementation.
pub struct Risc0Platform<H = IdentityOutput>(PhantomData<H>);
pub struct Risc0Platform;
impl<H: OutputHasher> Platform for Risc0Platform<H> {
fn read_whole_input() -> Vec<u8> {
impl Platform for Risc0Platform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
let len = {
let mut bytes = [0; 4];
risc0_zkvm::guest::env::read_slice(&mut bytes);
u32::from_le_bytes(bytes)
u32::from_le_bytes(bytes) as usize
};
let mut input = vec![0u8; len as usize];
let mut input = vec![0u8; len];
risc0_zkvm::guest::env::read_slice(&mut input);
input
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
risc0_zkvm::guest::env::commit_slice(&hash);
risc0_zkvm::guest::env::commit_slice(output);
}
fn print(message: &str) {

View File

@@ -82,6 +82,6 @@ mod tests {
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
zkvm.execute(&Input::default()).unwrap();
zkvm.execute(&Input::new()).unwrap();
}
}

View File

@@ -1,3 +1,48 @@
//! Risc0 [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_risc0_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-risc0` dependency.
//!
//! To install `r0vm-cuda` (with GPU proving support), make sure CUDA 12.9 is
//! installed, run [`install_risc0_sdk.sh`] with env `CUDA=1` set.
//!
//! ## `Compiler` requirements
//!
//! - [`rzup`]
//! - Installation via `rzup install` - Custom Rust toolchain used by `RustRv32imaCustomized`
//!
//! ## `zkVM` requirements
//!
//! - [`rzup`]
//! - Installation via `rzup install`
//! - `r0vm-cuda` - Used by `zkVM::prove` if `ProverResourceType::Gpu` is
//! selected
//! - `docker` - Used by `zkVM::prove` if `ProofKind::Groth16` is selected
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ----------------------- | :------: | ----------------------------- | ------------------ |
//! | `RustRv32imaCustomized` | Rust | `riscv32im-risc0-zkvm-elf` | With `std` support |
//! | `RustRv32ima` | Rust | `riscv32ima-unknown-none-elf` | |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | 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
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -213,8 +213,7 @@ impl EreRisc0 {
env.segment_limit_po2(self.segment_po2 as _)
.keccak_max_po2(self.keccak_po2 as _)
.expect("keccak_po2 in valid range");
env.write_slice(&(input.stdin().len() as u32).to_le_bytes())
.write_slice(input.stdin());
env.write_slice(input.stdin());
if let Some(receipts) = input.proofs() {
for receipt in receipts.map_err(Error::DeserializeInputProofs)? {
env.add_assumption(AssumptionReceipt::Proven(receipt));
@@ -265,7 +264,7 @@ mod tests {
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -287,7 +286,7 @@ mod tests {
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();
@@ -303,7 +302,7 @@ mod tests {
for i in 1..=16_u32 {
let zkvm = EreRisc0::new(program.clone(), ProverResourceType::Cpu).unwrap();
let input = Input::new(i.to_le_bytes().to_vec());
let input = Input::new().with_stdin(i.to_le_bytes().to_vec());
if i.is_power_of_two() {
zkvm.execute(&input)

View File

@@ -2,27 +2,23 @@
extern crate alloc;
use alloc::{format, vec::Vec};
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
use alloc::format;
use core::ops::Deref;
use ere_platform_trait::LengthPrefixedStdin;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use sp1_zkvm;
/// SP1 [`Platform`] implementation.
pub struct SP1Platform<H = IdentityOutput>(PhantomData<H>);
pub struct SP1Platform;
impl<H: OutputHasher> Platform for SP1Platform<H> {
fn read_whole_input() -> Vec<u8> {
sp1_zkvm::io::read_vec()
impl Platform for SP1Platform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
LengthPrefixedStdin::new(sp1_zkvm::io::read_vec())
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
sp1_zkvm::io::commit_slice(&hash);
sp1_zkvm::io::commit_slice(output);
}
fn print(message: &str) {

View File

@@ -70,6 +70,6 @@ mod tests {
let program = RustRv32ima.compile(&guest_directory).unwrap();
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
zkvm.execute(&Input::default()).unwrap();
zkvm.execute(&Input::new()).unwrap();
}
}

View File

@@ -1,3 +1,41 @@
//! SP1 [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_sp1_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-sp1` dependency.
//!
//! ## `Compiler` requirements
//!
//! - Installation via [`sp1up`] - Custom Rust toolchain used by `RustRv32imaCustomized`
//! - `cargo-prove` - Used by `RustRv32imaCustomized`
//!
//! ## `zkVM` requirements
//!
//! - `docker` - Used by `zkVM::prove` if `ProverResourceType::Gpu` is selected
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ----------------------- | :------: | ----------------------------- | ------------------ |
//! | `RustRv32imaCustomized` | Rust | `riscv32im-succinct-zkvm-elf` | With `std` support |
//! | `RustRv32ima` | Rust | `riscv32ima-unknown-none-elf` | |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | Yes |
//!
//! [`install_sp1_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_sp1_sdk.sh
//! [`sp1up`]: https://sp1up.succinct.xyz
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -225,7 +225,7 @@ mod tests {
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -247,7 +247,7 @@ mod tests {
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -2,27 +2,22 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
use core::ops::Deref;
use ere_platform_trait::LengthPrefixedStdin;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use zkm_zkvm;
/// Ziren [`Platform`] implementation.
pub struct ZirenPlatform<H = IdentityOutput>(PhantomData<H>);
pub struct ZirenPlatform;
impl<H: OutputHasher> Platform for ZirenPlatform<H> {
fn read_whole_input() -> Vec<u8> {
zkm_zkvm::io::read_vec()
impl Platform for ZirenPlatform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
LengthPrefixedStdin::new(zkm_zkvm::io::read_vec())
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
zkm_zkvm::io::commit_slice(&hash);
zkm_zkvm::io::commit_slice(output);
}
fn print(message: &str) {

View File

@@ -1,3 +1,34 @@
//! Ziren [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_ziren_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-ziren` dependency.
//!
//! ## `Compiler` requirements
//!
//! - Installation via [`install_ziren_sdk.sh`] - Custom Rust toolchain used by `RustMips32r2Customized`
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ------------------------ | :------: | --------------------- | ------------------ |
//! | `RustMips32r2Customized` | Rust | `mipsel-zkm-zkvm-elf` | With `std` support |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | No |
//! | `Network` | No |
//!
//! [`install_ziren_sdk.sh`]: https://github.com/eth-act/ere/blob/master/scripts/sdk_installers/install_ziren_sdk.sh
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -190,7 +190,7 @@ mod tests {
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -212,7 +212,7 @@ mod tests {
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -2,32 +2,31 @@
extern crate alloc;
use alloc::vec::Vec;
use core::{array::from_fn, marker::PhantomData};
use ere_platform_trait::output_hasher::OutputHasher;
use core::{array::from_fn, ops::Deref};
use ere_platform_trait::LengthPrefixedStdin;
use ziskos::ziskos_definitions::ziskos_config::UART_ADDR;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use ere_platform_trait::{Digest, OutputHashedPlatform, Platform};
pub use ziskos;
/// ZisK [`Platform`] implementation.
///
/// Note that the maximum output size is 256 bytes, and output size will be
/// padded to multiple of 4.
pub struct ZiskPlatform<H = IdentityOutput>(PhantomData<H>);
pub struct ZiskPlatform;
impl<H: OutputHasher> Platform for ZiskPlatform<H> {
fn read_whole_input() -> Vec<u8> {
ziskos::read_input()
impl Platform for ZiskPlatform {
fn read_whole_input() -> impl Deref<Target = [u8]> {
LengthPrefixedStdin::new(ziskos::read_input())
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
assert!(hash.len() <= 256, "Maximum output size is 256 bytes");
hash.chunks(4).enumerate().for_each(|(idx, chunk)| {
assert!(
output.len() <= 256,
"Maximum output size is 256 bytes, got {}",
output.len()
);
output.chunks(4).enumerate().for_each(|(idx, chunk)| {
let value = u32::from_le_bytes(from_fn(|i| chunk.get(i).copied().unwrap_or_default()));
ziskos::set_output(idx, value)
});

View File

@@ -1,3 +1,47 @@
//! ZisK [`Compiler`] and [`zkVM`] implementation.
//!
//! # Requirements
//!
//! To install all requirements, run [`install_zisk_sdk.sh`] from the Ere
//! repository at the same git revision as your `ere-zisk` dependency.
//!
//! To install `cargo-zisk-cuda` (with GPU proving support), make sure CUDA 12.9
//! is installed, run [`install_zisk_sdk.sh`] with env `CUDA=1` set.
//!
//! ## `Compiler` requirements
//!
//! - Installation via [`ziskup`] - Custom Rust toolchain used by `RustRv64imaCustomized`
//! - Installation via [`install_tamago.sh`] - Custom Go toolchain used by `GoCustomized`
//!
//! ## `zkVM` requirements
//!
//! - Installation via [`ziskup`]
//! - `cargo-zisk-cuda` - Used by `zkVM::prove` if `ProverResourceType::Gpu` is
//! selected
//!
//! # `Compiler` implementation
//!
//! ## Available compilers
//!
//! | Compiler | Language | Target | Note |
//! | ----------------------- | :------: | -------------------------- | ------------------ |
//! | `RustRv64imaCustomized` | Rust | `riscv64ima-zisk-zkvm-elf` | With `std` support |
//! | `GoCustomized` | Go | `riscv64` | |
//!
//! # `zkVM` implementation
//!
//! ## Supported `ProverResourceType`
//!
//! | Resource | Supported |
//! | --------- | :-------: |
//! | `Cpu` | Yes |
//! | `Gpu` | Yes |
//! | `Network` | No |
//!
//! [`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
//! [`ziskup`]: https://raw.githubusercontent.com/0xPolygonHermez/zisk/main/ziskup/install.sh
#![cfg_attr(
all(not(test), feature = "compiler", feature = "zkvm"),
warn(unused_crate_dependencies)

View File

@@ -188,7 +188,7 @@ mod tests {
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.execute(&input).unwrap_err();
@@ -214,7 +214,7 @@ mod tests {
let _guard = PROVE_LOCK.lock().unwrap();
for input in [
Input::default(),
Input::new(),
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();

View File

@@ -7,7 +7,6 @@
use ere_platform_airbender::AirbenderPlatform;
use ere_test_utils::{
guest::Sha256,
io::serde::bincode::BincodeLegacy,
program::{basic::BasicProgram, Program},
};
@@ -16,5 +15,5 @@ mod airbender_rt;
#[inline(never)]
fn main() {
BasicProgram::<BincodeLegacy>::run::<AirbenderPlatform<Sha256>>();
BasicProgram::<BincodeLegacy>::run_output_sha256::<AirbenderPlatform>();
}

View File

@@ -1,10 +1,9 @@
use ere_platform_openvm::OpenVMPlatform;
use ere_test_utils::{
guest::Sha256,
io::serde::bincode::BincodeLegacy,
program::{basic::BasicProgram, Program},
};
fn main() {
BasicProgram::<BincodeLegacy>::run::<OpenVMPlatform<Sha256>>();
BasicProgram::<BincodeLegacy>::run_output_sha256::<OpenVMPlatform>();
}

View File

@@ -1,10 +1,12 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::vec::Vec;
use core::sync::atomic::Ordering;
use core::sync::atomic::AtomicU16;
use core::sync::atomic::Ordering;
mod openvm_rt;
fn main() {

View File

@@ -1,10 +1,11 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::vec::Vec;
use core::sync::atomic::Ordering;
use core::sync::atomic::AtomicU16;
use core::sync::atomic::Ordering;
mod pico_rt;

View File

@@ -1,9 +1,9 @@
use ere_platform_risc0::{Platform, Risc0Platform};
type P = Risc0Platform;
use ere_platform_risc0::risc0_zkvm::guest::env::read_slice;
fn main() {
let alignment = u32::from_le_bytes(P::read_whole_input().try_into().unwrap()) as usize;
let mut alignment = [0; 4];
read_slice(&mut alignment);
let alignment = u32::from_le_bytes(alignment) as usize;
let layout = std::alloc::Layout::from_size_align(1, alignment).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) };

View File

@@ -1,10 +1,11 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::vec::Vec;
use core::sync::atomic::Ordering;
use core::sync::atomic::AtomicU16;
use core::sync::atomic::Ordering;
mod risc0_rt;

View File

@@ -1,10 +1,11 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::vec::Vec;
use core::sync::atomic::Ordering;
use core::sync::atomic::AtomicU16;
use core::sync::atomic::Ordering;
mod sp1;