From 8f6cee8a32dc02feea42865c5816678628dc43f4 Mon Sep 17 00:00:00 2001 From: Han Date: Sat, 6 Dec 2025 12:02:14 +0900 Subject: [PATCH] Refactor docs and make `Input` API more explicit (#235) --- .github/workflows/test-common.yml | 34 +- .github/workflows/test-zkvm.yml | 5 - Cargo.lock | 1 + Cargo.toml | 2 +- README.md | 401 ++++++++++++++---- crates/dockerized/server/Cargo.toml | 10 +- crates/dockerized/server/build.rs | 8 - crates/dockerized/server/src/api.rs | 278 ++++++++++++ crates/dockerized/server/src/lib.rs | 12 +- crates/dockerized/server/src/test.rs | 28 ++ crates/dockerized/src/lib.rs | 2 +- crates/dockerized/src/zkvm.rs | 18 +- crates/io/src/lib.rs | 4 +- crates/test-utils/src/guest.rs | 1 - crates/test-utils/src/host.rs | 2 +- crates/test-utils/src/lib.rs | 1 - crates/test-utils/src/program.rs | 7 +- crates/zkvm-interface/platform/src/lib.rs | 84 +++- .../platform/src/output_hasher.rs | 90 ---- crates/zkvm-interface/src/zkvm.rs | 23 +- crates/zkvm/airbender/platform/src/lib.rs | 35 +- crates/zkvm/airbender/src/lib.rs | 45 ++ crates/zkvm/airbender/src/zkvm.rs | 4 +- crates/zkvm/airbender/src/zkvm/sdk.rs | 6 +- crates/zkvm/jolt/platform/src/lib.rs | 44 +- .../zkvm/jolt/src/compiler/rust_rv64imac.rs | 2 +- crates/zkvm/jolt/src/lib.rs | 33 ++ crates/zkvm/jolt/src/zkvm.rs | 4 +- crates/zkvm/jolt/src/zkvm/error.rs | 3 + crates/zkvm/jolt/src/zkvm/sdk.rs | 48 +-- crates/zkvm/miden/src/lib.rs | 20 + crates/zkvm/miden/src/zkvm.rs | 14 +- crates/zkvm/nexus/platform/src/lib.rs | 21 +- crates/zkvm/nexus/src/lib.rs | 20 + crates/zkvm/nexus/src/zkvm.rs | 4 +- crates/zkvm/openvm/platform/src/lib.rs | 33 +- .../zkvm/openvm/src/compiler/rust_rv32ima.rs | 2 +- crates/zkvm/openvm/src/lib.rs | 37 ++ crates/zkvm/openvm/src/zkvm.rs | 4 +- crates/zkvm/pico/platform/src/lib.rs | 22 +- crates/zkvm/pico/src/compiler/rust_rv32ima.rs | 2 +- crates/zkvm/pico/src/lib.rs | 32 ++ crates/zkvm/pico/src/zkvm.rs | 4 +- crates/zkvm/risc0/platform/src/lib.rs | 23 +- .../zkvm/risc0/src/compiler/rust_rv32ima.rs | 2 +- crates/zkvm/risc0/src/lib.rs | 45 ++ crates/zkvm/risc0/src/zkvm.rs | 9 +- crates/zkvm/sp1/platform/src/lib.rs | 22 +- crates/zkvm/sp1/src/compiler/rust_rv32ima.rs | 2 +- crates/zkvm/sp1/src/lib.rs | 38 ++ crates/zkvm/sp1/src/zkvm.rs | 4 +- crates/zkvm/ziren/platform/src/lib.rs | 21 +- crates/zkvm/ziren/src/lib.rs | 31 ++ crates/zkvm/ziren/src/zkvm.rs | 4 +- crates/zkvm/zisk/platform/src/lib.rs | 27 +- crates/zkvm/zisk/src/lib.rs | 44 ++ crates/zkvm/zisk/src/zkvm.rs | 4 +- tests/airbender/basic/src/main.rs | 3 +- tests/openvm/basic/src/main.rs | 3 +- tests/openvm/stock_nightly_no_std/src/main.rs | 4 +- tests/pico/stock_nightly_no_std/src/main.rs | 3 +- tests/risc0/allocs_alignment/src/main.rs | 8 +- tests/risc0/stock_nightly_no_std/src/main.rs | 3 +- tests/sp1/stock_nightly_no_std/src/main.rs | 3 +- 64 files changed, 1297 insertions(+), 456 deletions(-) delete mode 100644 crates/dockerized/server/build.rs create mode 100644 crates/dockerized/server/src/api.rs create mode 100644 crates/dockerized/server/src/test.rs delete mode 100644 crates/test-utils/src/guest.rs delete mode 100644 crates/zkvm-interface/platform/src/output_hasher.rs diff --git a/.github/workflows/test-common.yml b/.github/workflows/test-common.yml index 83f2581..492f478 100644 --- a/.github/workflows/test-common.yml +++ b/.github/workflows/test-common.yml @@ -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 }} diff --git a/.github/workflows/test-zkvm.yml b/.github/workflows/test-zkvm.yml index df6a0c9..da5f50b 100644 --- a/.github/workflows/test-zkvm.yml +++ b/.github/workflows/test-zkvm.yml @@ -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: diff --git a/Cargo.lock b/Cargo.lock index d14d125..a18913b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3969,6 +3969,7 @@ dependencies = [ "prost 0.13.5", "prost-build 0.13.5", "serde", + "tempfile", "thiserror 2.0.12", "tokio", "tower-http", diff --git a/Cargo.toml b/Cargo.toml index 9efc3da..5a2f3c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/README.md b/README.md index 884f42e..56c21da 100644 --- a/README.md +++ b/README.md @@ -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 back‑ends** – easily switch between different zkVMs -* **SDK bootstrap scripts** for every supported zkVM -* **End‑to‑end 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` 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::::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> { - 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> { - 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> { ## 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! diff --git a/crates/dockerized/server/Cargo.toml b/crates/dockerized/server/Cargo.toml index ca3ad7c..6ed4aff 100644 --- a/crates/dockerized/server/Cargo.toml +++ b/crates/dockerized/server/Cargo.toml @@ -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 diff --git a/crates/dockerized/server/build.rs b/crates/dockerized/server/build.rs deleted file mode 100644 index 4c0c556..0000000 --- a/crates/dockerized/server/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() -> Result<(), Box> { - 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(()) -} diff --git a/crates/dockerized/server/src/api.rs b/crates/dockerized/server/src/api.rs new file mode 100644 index 0000000..c070e95 --- /dev/null +++ b/crates/dockerized/server/src/api.rs @@ -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, + #[prost(bytes = "vec", optional, tag = "2")] + pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec>, +} +#[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, +} +/// 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, + #[prost(bytes = "vec", tag = "2")] + pub report: ::prost::alloc::vec::Vec, +} +#[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, + #[prost(bytes = "vec", optional, tag = "2")] + pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec>, + #[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, +} +/// 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, + #[prost(bytes = "vec", tag = "2")] + pub proof: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub report: ::prost::alloc::vec::Vec, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VerifyRequest { + #[prost(bytes = "vec", tag = "1")] + pub proof: ::prost::alloc::vec::Vec, + #[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, +} +/// 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, +} +#[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 { + 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, + ) -> twirp::Result>; + async fn prove( + &self, + req: twirp::Request, + ) -> twirp::Result>; + async fn verify( + &self, + req: twirp::Request, + ) -> twirp::Result>; +} +#[twirp::async_trait::async_trait] +impl ZkvmService for std::sync::Arc +where + T: ZkvmService + Sync + Send, +{ + async fn execute( + &self, + req: twirp::Request, + ) -> twirp::Result> { + T::execute(&*self, req).await + } + async fn prove( + &self, + req: twirp::Request, + ) -> twirp::Result> { + T::prove(&*self, req).await + } + async fn verify( + &self, + req: twirp::Request, + ) -> twirp::Result> { + T::verify(&*self, req).await + } +} +pub fn router(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| async move { + api.execute(req).await + }, + ) + .route( + "/Prove", + |api: T, req: twirp::Request| async move { + api.prove(req).await + }, + ) + .route( + "/Verify", + |api: T, req: twirp::Request| 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, + ) -> twirp::Result> { + self.request("api.ZkvmService/Execute", req).await + } + async fn prove( + &self, + req: twirp::Request, + ) -> twirp::Result> { + self.request("api.ZkvmService/Prove", req).await + } + async fn verify( + &self, + req: twirp::Request, + ) -> twirp::Result> { + self.request("api.ZkvmService/Verify", req).await + } +} +#[allow(dead_code)] +pub mod handler { + use super::*; + pub struct ZkvmServiceHandler { + inner: std::sync::Arc, + } + impl ZkvmServiceHandler { + #[allow(clippy::new_ret_no_self)] + pub fn new(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 { + 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() + ), + ), + ) + } + } + } + } +} diff --git a/crates/dockerized/server/src/lib.rs b/crates/dockerized/server/src/lib.rs index d2cef53..cd102fd 100644 --- a/crates/dockerized/server/src/lib.rs +++ b/crates/dockerized/server/src/lib.rs @@ -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; diff --git a/crates/dockerized/server/src/test.rs b/crates/dockerized/server/src/test.rs new file mode 100644 index 0000000..85d4891 --- /dev/null +++ b/crates/dockerized/server/src/test.rs @@ -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, ¤t).unwrap(); + } + assert_eq!(fs::read(&latest).unwrap(), fs::read(¤t).unwrap()); +} diff --git a/crates/dockerized/src/lib.rs b/crates/dockerized/src/lib.rs index 547e1f8..8d3363a 100644 --- a/crates/dockerized/src/lib.rs +++ b/crates/dockerized/src/lib.rs @@ -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)?; diff --git a/crates/dockerized/src/zkvm.rs b/crates/dockerized/src/zkvm.rs index 8720420..80c20d3 100644 --- a/crates/dockerized/src/zkvm.rs +++ b/crates/dockerized/src/zkvm.rs @@ -406,7 +406,7 @@ mod test { "basic", [BasicProgram::::valid_test_case().into_output_sha256()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -420,7 +420,7 @@ mod test { "basic", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -434,7 +434,7 @@ mod test { "basic", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -448,7 +448,7 @@ mod test { "basic", [BasicProgram::::valid_test_case().into_output_sha256()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -462,7 +462,7 @@ mod test { "basic", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -476,7 +476,7 @@ mod test { "basic", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -490,7 +490,7 @@ mod test { "basic", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -504,7 +504,7 @@ mod test { "basic", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); @@ -518,7 +518,7 @@ mod test { "basic_rust", [BasicProgram::::valid_test_case()], [ - Input::default(), + Input::new(), BasicProgram::::invalid_test_case().input() ] ); diff --git a/crates/io/src/lib.rs b/crates/io/src/lib.rs index 6c603b9..f45efec 100644 --- a/crates/io/src/lib.rs +++ b/crates/io/src/lib.rs @@ -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; diff --git a/crates/test-utils/src/guest.rs b/crates/test-utils/src/guest.rs deleted file mode 100644 index 03670de..0000000 --- a/crates/test-utils/src/guest.rs +++ /dev/null @@ -1 +0,0 @@ -pub use sha2::Sha256; diff --git a/crates/test-utils/src/host.rs b/crates/test-utils/src/host.rs index 2c6a876..7fdbecc 100644 --- a/crates/test-utils/src/host.rs +++ b/crates/test-utils/src/host.rs @@ -84,7 +84,7 @@ impl Deref for ProgramTestCase

{ impl TestCase for ProgramTestCase

{ 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]) { diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index fc23969..ff5c2f2 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -2,7 +2,6 @@ extern crate alloc; -pub mod guest; pub mod program; #[cfg(feature = "host")] diff --git a/crates/test-utils/src/program.rs b/crates/test-utils/src/program.rs index 5a8a47e..6f6dd27 100644 --- a/crates/test-utils/src/program.rs +++ b/crates/test-utils/src/program.rs @@ -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() { + Self::run::>() + } } diff --git a/crates/zkvm-interface/platform/src/lib.rs b/crates/zkvm-interface/platform/src/lib.rs index 2d17ce4..30365dd 100644 --- a/crates/zkvm-interface/platform/src/lib.rs +++ b/crates/zkvm-interface/platform/src/lib.rs @@ -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; + fn read_whole_input() -> impl Deref; /// 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(PhantomData<(P, D)>); + +impl Platform for OutputHashedPlatform +where + P: Platform, + D: Digest, +{ + #[inline] + fn read_whole_input() -> impl Deref { + 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(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); + +impl Deref for LengthPrefixedStdin { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0[4..] + } +} + +impl LengthPrefixedStdin { + pub fn new(stdin: Vec) -> 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) + } +} diff --git a/crates/zkvm-interface/platform/src/output_hasher.rs b/crates/zkvm-interface/platform/src/output_hasher.rs deleted file mode 100644 index 58c4b7b..0000000 --- a/crates/zkvm-interface/platform/src/output_hasher.rs +++ /dev/null @@ -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; - - 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 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(PhantomData); - -impl OutputHasher for IdentityOutput { - type Hash<'a> = &'a [u8]; - - fn output_hash(output: &[u8]) -> Self::Hash<'_> { - output - } -} - -impl + 'static> OutputSizeUser for IdentityOutput { - type OutputSize = U; -} - -impl + 'static> OutputHasher for IdentityOutput { - type Hash<'a> = GenericArray; - - 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::::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(PhantomData); - -impl + 'static> OutputSizeUser for PaddedOutput { - type OutputSize = U; -} - -impl + 'static> OutputHasher for PaddedOutput { - type Hash<'a> = GenericArray; - - 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::::default(); - hash[..output.len()].copy_from_slice(output); - hash - } -} - -impl OutputHasher for D { - type Hash<'a> = Output; - - fn output_hash(output: &[u8]) -> Self::Hash<'_> { - D::digest(output) - } -} diff --git a/crates/zkvm-interface/src/zkvm.rs b/crates/zkvm-interface/src/zkvm.rs index f973d64..0b48e33 100644 --- a/crates/zkvm-interface/src/zkvm.rs +++ b/crates/zkvm-interface/src/zkvm.rs @@ -22,10 +22,10 @@ pub struct Input { } impl Input { - /// Creates a new `Input` with the given stdin. - pub fn new(stdin: Vec) -> 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) -> 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) -> 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. diff --git a/crates/zkvm/airbender/platform/src/lib.rs b/crates/zkvm/airbender/platform/src/lib.rs index d405c2f..4fdd496 100644 --- a/crates/zkvm/airbender/platform/src/lib.rs +++ b/crates/zkvm/airbender/platform/src/lib.rs @@ -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(PhantomData); +/// 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> Platform for AirbenderPlatform { - fn read_whole_input() -> Vec { +impl Platform for AirbenderPlatform { + fn read_whole_input() -> impl Deref { 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::>() } 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(); } } diff --git a/crates/zkvm/airbender/src/lib.rs b/crates/zkvm/airbender/src/lib.rs index a53c4ec..30576af 100644 --- a/crates/zkvm/airbender/src/lib.rs +++ b/crates/zkvm/airbender/src/lib.rs @@ -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) diff --git a/crates/zkvm/airbender/src/zkvm.rs b/crates/zkvm/airbender/src/zkvm.rs index bb03d38..501b513 100644 --- a/crates/zkvm/airbender/src/zkvm.rs +++ b/crates/zkvm/airbender/src/zkvm.rs @@ -149,7 +149,7 @@ mod tests { let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/airbender/src/zkvm/sdk.rs b/crates/zkvm/airbender/src/zkvm/sdk.rs index d14d202..59675a2 100644 --- a/crates/zkvm/airbender/src/zkvm/sdk.rs +++ b/crates/zkvm/airbender/src/zkvm/sdk.rs @@ -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); diff --git a/crates/zkvm/jolt/platform/src/lib.rs b/crates/zkvm/jolt/platform/src/lib.rs index 6f23650..8fc441a 100644 --- a/crates/zkvm/jolt/platform/src/lib.rs +++ b/crates/zkvm/jolt/platform/src/lib.rs @@ -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(PhantomData<(C, H)>); +pub struct JoltPlatform(PhantomData); -impl Platform for JoltPlatform { - fn read_whole_input() -> Vec { +impl Platform for JoltPlatform { + fn read_whole_input() -> impl Deref { 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) { diff --git a/crates/zkvm/jolt/src/compiler/rust_rv64imac.rs b/crates/zkvm/jolt/src/compiler/rust_rv64imac.rs index c350dc5..3099aed 100644 --- a/crates/zkvm/jolt/src/compiler/rust_rv64imac.rs +++ b/crates/zkvm/jolt/src/compiler/rust_rv64imac.rs @@ -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(); } } diff --git a/crates/zkvm/jolt/src/lib.rs b/crates/zkvm/jolt/src/lib.rs index a53c4ec..fe4f694 100644 --- a/crates/zkvm/jolt/src/lib.rs +++ b/crates/zkvm/jolt/src/lib.rs @@ -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) diff --git a/crates/zkvm/jolt/src/zkvm.rs b/crates/zkvm/jolt/src/zkvm.rs index 6f37bd5..14d99fb 100644 --- a/crates/zkvm/jolt/src/zkvm.rs +++ b/crates/zkvm/jolt/src/zkvm.rs @@ -153,7 +153,7 @@ mod tests { let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/jolt/src/zkvm/error.rs b/crates/zkvm/jolt/src/zkvm/error.rs index f6a7f3e..58c6c5f 100644 --- a/crates/zkvm/jolt/src/zkvm/error.rs +++ b/crates/zkvm/jolt/src/zkvm/error.rs @@ -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, diff --git a/crates/zkvm/jolt/src/zkvm/sdk.rs b/crates/zkvm/jolt/src/zkvm/sdk.rs index b48d6c3..30b5614 100644 --- a/crates/zkvm/jolt/src/zkvm/sdk.rs +++ b/crates/zkvm/jolt/src/zkvm/sdk.rs @@ -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, Error> { - Ok(postcard::to_stdvec(bytes) - .map_err(|err| CommonError::serialize("input", "postcard", err))?) -} - -fn deserialize_output(output: &[u8]) -> Result, 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, Error> { Ok(if output.is_empty() { Vec::new() } else { - let (len, bytes) = postcard::take_from_bytes::(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 }) } diff --git a/crates/zkvm/miden/src/lib.rs b/crates/zkvm/miden/src/lib.rs index a53c4ec..8f342df 100644 --- a/crates/zkvm/miden/src/lib.rs +++ b/crates/zkvm/miden/src/lib.rs @@ -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) diff --git a/crates/zkvm/miden/src/zkvm.rs b/crates/zkvm/miden/src/zkvm.rs index 20116ca..64d3f89 100644 --- a/crates/zkvm/miden/src/zkvm.rs +++ b/crates/zkvm/miden/src/zkvm.rs @@ -175,7 +175,7 @@ pub fn felts_to_bytes(felts: &[Felt]) -> Vec { /// Convert bytes into Miden field elements. pub fn bytes_to_felts(bytes: &[u8]) -> Result, 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()); } } diff --git a/crates/zkvm/nexus/platform/src/lib.rs b/crates/zkvm/nexus/platform/src/lib.rs index 872be8a..7053a1b 100644 --- a/crates/zkvm/nexus/platform/src/lib.rs +++ b/crates/zkvm/nexus/platform/src/lib.rs @@ -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(PhantomData); +pub struct NexusPlatform; -impl Platform for NexusPlatform { - fn read_whole_input() -> Vec { - nexus_rt::read_private_input().unwrap() +impl Platform for NexusPlatform { + fn read_whole_input() -> impl Deref { + 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) { diff --git a/crates/zkvm/nexus/src/lib.rs b/crates/zkvm/nexus/src/lib.rs index a53c4ec..9883687 100644 --- a/crates/zkvm/nexus/src/lib.rs +++ b/crates/zkvm/nexus/src/lib.rs @@ -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) diff --git a/crates/zkvm/nexus/src/zkvm.rs b/crates/zkvm/nexus/src/zkvm.rs index f2eb61b..fd14799 100644 --- a/crates/zkvm/nexus/src/zkvm.rs +++ b/crates/zkvm/nexus/src/zkvm.rs @@ -205,7 +205,7 @@ mod tests { let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/openvm/platform/src/lib.rs b/crates/zkvm/openvm/platform/src/lib.rs index f3bac3d..2871417 100644 --- a/crates/zkvm/openvm/platform/src/lib.rs +++ b/crates/zkvm/openvm/platform/src/lib.rs @@ -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(PhantomData); +/// 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> Platform for OpenVMPlatform { - fn read_whole_input() -> Vec { - openvm::io::read_vec() +impl Platform for OpenVMPlatform { + fn read_whole_input() -> impl Deref { + 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) { diff --git a/crates/zkvm/openvm/src/compiler/rust_rv32ima.rs b/crates/zkvm/openvm/src/compiler/rust_rv32ima.rs index 7cec88c..99eda60 100644 --- a/crates/zkvm/openvm/src/compiler/rust_rv32ima.rs +++ b/crates/zkvm/openvm/src/compiler/rust_rv32ima.rs @@ -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(); } } diff --git a/crates/zkvm/openvm/src/lib.rs b/crates/zkvm/openvm/src/lib.rs index a53c4ec..1db3bb0 100644 --- a/crates/zkvm/openvm/src/lib.rs +++ b/crates/zkvm/openvm/src/lib.rs @@ -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) diff --git a/crates/zkvm/openvm/src/zkvm.rs b/crates/zkvm/openvm/src/zkvm.rs index f4d53b4..cbd6a5b 100644 --- a/crates/zkvm/openvm/src/zkvm.rs +++ b/crates/zkvm/openvm/src/zkvm.rs @@ -292,7 +292,7 @@ mod tests { let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/pico/platform/src/lib.rs b/crates/zkvm/pico/platform/src/lib.rs index ad6f451..9684382 100644 --- a/crates/zkvm/pico/platform/src/lib.rs +++ b/crates/zkvm/pico/platform/src/lib.rs @@ -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(PhantomData); +pub struct PicoPlatform; -impl Platform for PicoPlatform { - fn read_whole_input() -> Vec { - pico_sdk::io::read_vec() +impl Platform for PicoPlatform { + fn read_whole_input() -> impl Deref { + 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) { diff --git a/crates/zkvm/pico/src/compiler/rust_rv32ima.rs b/crates/zkvm/pico/src/compiler/rust_rv32ima.rs index 920e22c..08ab6ec 100644 --- a/crates/zkvm/pico/src/compiler/rust_rv32ima.rs +++ b/crates/zkvm/pico/src/compiler/rust_rv32ima.rs @@ -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(); } } diff --git a/crates/zkvm/pico/src/lib.rs b/crates/zkvm/pico/src/lib.rs index a53c4ec..45b7415 100644 --- a/crates/zkvm/pico/src/lib.rs +++ b/crates/zkvm/pico/src/lib.rs @@ -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) diff --git a/crates/zkvm/pico/src/zkvm.rs b/crates/zkvm/pico/src/zkvm.rs index 0556be8..98eab7c 100644 --- a/crates/zkvm/pico/src/zkvm.rs +++ b/crates/zkvm/pico/src/zkvm.rs @@ -225,7 +225,7 @@ mod tests { let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/risc0/platform/src/lib.rs b/crates/zkvm/risc0/platform/src/lib.rs index ea7295b..a345663 100644 --- a/crates/zkvm/risc0/platform/src/lib.rs +++ b/crates/zkvm/risc0/platform/src/lib.rs @@ -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(PhantomData); +pub struct Risc0Platform; -impl Platform for Risc0Platform { - fn read_whole_input() -> Vec { +impl Platform for Risc0Platform { + fn read_whole_input() -> impl Deref { 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) { diff --git a/crates/zkvm/risc0/src/compiler/rust_rv32ima.rs b/crates/zkvm/risc0/src/compiler/rust_rv32ima.rs index b1f0858..e02dbf3 100644 --- a/crates/zkvm/risc0/src/compiler/rust_rv32ima.rs +++ b/crates/zkvm/risc0/src/compiler/rust_rv32ima.rs @@ -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(); } } diff --git a/crates/zkvm/risc0/src/lib.rs b/crates/zkvm/risc0/src/lib.rs index a53c4ec..c349271 100644 --- a/crates/zkvm/risc0/src/lib.rs +++ b/crates/zkvm/risc0/src/lib.rs @@ -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) diff --git a/crates/zkvm/risc0/src/zkvm.rs b/crates/zkvm/risc0/src/zkvm.rs index ce6490a..35f2a2a 100644 --- a/crates/zkvm/risc0/src/zkvm.rs +++ b/crates/zkvm/risc0/src/zkvm.rs @@ -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::::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::::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) diff --git a/crates/zkvm/sp1/platform/src/lib.rs b/crates/zkvm/sp1/platform/src/lib.rs index 83d1802..bf5b26a 100644 --- a/crates/zkvm/sp1/platform/src/lib.rs +++ b/crates/zkvm/sp1/platform/src/lib.rs @@ -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(PhantomData); +pub struct SP1Platform; -impl Platform for SP1Platform { - fn read_whole_input() -> Vec { - sp1_zkvm::io::read_vec() +impl Platform for SP1Platform { + fn read_whole_input() -> impl Deref { + 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) { diff --git a/crates/zkvm/sp1/src/compiler/rust_rv32ima.rs b/crates/zkvm/sp1/src/compiler/rust_rv32ima.rs index ebc73e8..8350c6d 100644 --- a/crates/zkvm/sp1/src/compiler/rust_rv32ima.rs +++ b/crates/zkvm/sp1/src/compiler/rust_rv32ima.rs @@ -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(); } } diff --git a/crates/zkvm/sp1/src/lib.rs b/crates/zkvm/sp1/src/lib.rs index a53c4ec..ebabc57 100644 --- a/crates/zkvm/sp1/src/lib.rs +++ b/crates/zkvm/sp1/src/lib.rs @@ -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) diff --git a/crates/zkvm/sp1/src/zkvm.rs b/crates/zkvm/sp1/src/zkvm.rs index b5b419c..5c3307e 100644 --- a/crates/zkvm/sp1/src/zkvm.rs +++ b/crates/zkvm/sp1/src/zkvm.rs @@ -225,7 +225,7 @@ mod tests { let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/ziren/platform/src/lib.rs b/crates/zkvm/ziren/platform/src/lib.rs index 14d3c5a..8506c02 100644 --- a/crates/zkvm/ziren/platform/src/lib.rs +++ b/crates/zkvm/ziren/platform/src/lib.rs @@ -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(PhantomData); +pub struct ZirenPlatform; -impl Platform for ZirenPlatform { - fn read_whole_input() -> Vec { - zkm_zkvm::io::read_vec() +impl Platform for ZirenPlatform { + fn read_whole_input() -> impl Deref { + 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) { diff --git a/crates/zkvm/ziren/src/lib.rs b/crates/zkvm/ziren/src/lib.rs index a53c4ec..922540f 100644 --- a/crates/zkvm/ziren/src/lib.rs +++ b/crates/zkvm/ziren/src/lib.rs @@ -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) diff --git a/crates/zkvm/ziren/src/zkvm.rs b/crates/zkvm/ziren/src/zkvm.rs index 00e7a2f..dd4ea3a 100644 --- a/crates/zkvm/ziren/src/zkvm.rs +++ b/crates/zkvm/ziren/src/zkvm.rs @@ -190,7 +190,7 @@ mod tests { let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/crates/zkvm/zisk/platform/src/lib.rs b/crates/zkvm/zisk/platform/src/lib.rs index 5edb89c..a798c2e 100644 --- a/crates/zkvm/zisk/platform/src/lib.rs +++ b/crates/zkvm/zisk/platform/src/lib.rs @@ -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(PhantomData); +pub struct ZiskPlatform; -impl Platform for ZiskPlatform { - fn read_whole_input() -> Vec { - ziskos::read_input() +impl Platform for ZiskPlatform { + fn read_whole_input() -> impl Deref { + 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) }); diff --git a/crates/zkvm/zisk/src/lib.rs b/crates/zkvm/zisk/src/lib.rs index a53c4ec..87288ed 100644 --- a/crates/zkvm/zisk/src/lib.rs +++ b/crates/zkvm/zisk/src/lib.rs @@ -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) diff --git a/crates/zkvm/zisk/src/zkvm.rs b/crates/zkvm/zisk/src/zkvm.rs index 66803c6..90030be 100644 --- a/crates/zkvm/zisk/src/zkvm.rs +++ b/crates/zkvm/zisk/src/zkvm.rs @@ -188,7 +188,7 @@ mod tests { let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap(); for input in [ - Input::default(), + Input::new(), BasicProgram::::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::::invalid_test_case().input(), ] { zkvm.prove(&input, ProofKind::default()).unwrap_err(); diff --git a/tests/airbender/basic/src/main.rs b/tests/airbender/basic/src/main.rs index ec5d8fb..cddc3bf 100644 --- a/tests/airbender/basic/src/main.rs +++ b/tests/airbender/basic/src/main.rs @@ -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::::run::>(); + BasicProgram::::run_output_sha256::(); } diff --git a/tests/openvm/basic/src/main.rs b/tests/openvm/basic/src/main.rs index 45a4dfb..2f85037 100644 --- a/tests/openvm/basic/src/main.rs +++ b/tests/openvm/basic/src/main.rs @@ -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::::run::>(); + BasicProgram::::run_output_sha256::(); } diff --git a/tests/openvm/stock_nightly_no_std/src/main.rs b/tests/openvm/stock_nightly_no_std/src/main.rs index 416cedc..3d4155a 100644 --- a/tests/openvm/stock_nightly_no_std/src/main.rs +++ b/tests/openvm/stock_nightly_no_std/src/main.rs @@ -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() { diff --git a/tests/pico/stock_nightly_no_std/src/main.rs b/tests/pico/stock_nightly_no_std/src/main.rs index 920e413..2b3aa9f 100644 --- a/tests/pico/stock_nightly_no_std/src/main.rs +++ b/tests/pico/stock_nightly_no_std/src/main.rs @@ -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; diff --git a/tests/risc0/allocs_alignment/src/main.rs b/tests/risc0/allocs_alignment/src/main.rs index c2b3d16..2b8a33e 100644 --- a/tests/risc0/allocs_alignment/src/main.rs +++ b/tests/risc0/allocs_alignment/src/main.rs @@ -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) }; diff --git a/tests/risc0/stock_nightly_no_std/src/main.rs b/tests/risc0/stock_nightly_no_std/src/main.rs index 338be42..717ed0c 100644 --- a/tests/risc0/stock_nightly_no_std/src/main.rs +++ b/tests/risc0/stock_nightly_no_std/src/main.rs @@ -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; diff --git a/tests/sp1/stock_nightly_no_std/src/main.rs b/tests/sp1/stock_nightly_no_std/src/main.rs index 73a2e98..1f39bce 100644 --- a/tests/sp1/stock_nightly_no_std/src/main.rs +++ b/tests/sp1/stock_nightly_no_std/src/main.rs @@ -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;