mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
Refactor docs and make Input API more explicit (#235)
This commit is contained in:
34
.github/workflows/test-common.yml
vendored
34
.github/workflows/test-common.yml
vendored
@@ -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 }}
|
||||
|
||||
5
.github/workflows/test-zkvm.yml
vendored
5
.github/workflows/test-zkvm.yml
vendored
@@ -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
1
Cargo.lock
generated
@@ -3969,6 +3969,7 @@ dependencies = [
|
||||
"prost 0.13.5",
|
||||
"prost-build 0.13.5",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
|
||||
@@ -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
401
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<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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
278
crates/dockerized/server/src/api.rs
Normal file
278
crates/dockerized/server/src/api.rs
Normal 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()
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
28
crates/dockerized/server/src/test.rs
Normal file
28
crates/dockerized/server/src/test.rs
Normal 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, ¤t).unwrap();
|
||||
}
|
||||
assert_eq!(fs::read(&latest).unwrap(), fs::read(¤t).unwrap());
|
||||
}
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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()
|
||||
]
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub use sha2::Sha256;
|
||||
@@ -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]) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod guest;
|
||||
pub mod program;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
|
||||
@@ -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>>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) };
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user