From daea3f4fffecaf7523faed9fd68623d98a73522f Mon Sep 17 00:00:00 2001 From: Han Date: Tue, 25 Nov 2025 10:59:48 +0900 Subject: [PATCH] Introduce `CompilerKind` and refactor `ere-dockerized` (#221) --- .github/workflows/test-zkvm.yml | 8 +- Cargo.lock | 58 +- Cargo.toml | 7 +- README.md | 10 +- crates/compile-utils/src/error.rs | 4 +- crates/dockerized/{dockerized => }/Cargo.toml | 4 + crates/dockerized/{dockerized => }/build.rs | 2 +- crates/dockerized/compiler/src/lib.rs | 48 ++ crates/dockerized/compiler/src/main.rs | 148 +++- crates/dockerized/dockerized/src/error.rs | 54 -- crates/dockerized/dockerized/src/lib.rs | 770 ------------------ crates/dockerized/src/compiler.rs | 236 ++++++ crates/dockerized/src/compiler/error.rs | 16 + crates/dockerized/src/lib.rs | 179 ++++ crates/dockerized/src/util.rs | 15 + .../{dockerized/src => src/util}/cuda.rs | 0 .../{dockerized/src => src/util}/docker.rs | 85 +- crates/dockerized/src/zkvm.rs | 467 +++++++++++ crates/dockerized/src/zkvm/error.rs | 26 + crates/zkvm-interface/src/zkvm/error.rs | 4 +- crates/zkvm/openvm/Cargo.toml | 18 +- crates/zkvm/openvm/src/compiler.rs | 38 +- crates/zkvm/openvm/src/program.rs | 7 +- crates/zkvm/openvm/src/zkvm.rs | 32 +- crates/zkvm/openvm/src/zkvm/error.rs | 3 + docker/{base => }/Dockerfile.base | 7 - docker/airbender/Dockerfile.base | 9 +- docker/airbender/Dockerfile.compiler | 2 +- docker/airbender/Dockerfile.server | 7 +- docker/jolt/Dockerfile.base | 3 - docker/jolt/Dockerfile.compiler | 2 +- docker/jolt/Dockerfile.server | 2 +- docker/miden/Dockerfile.compiler | 2 +- docker/miden/Dockerfile.server | 2 +- docker/nexus/Dockerfile.compiler | 2 +- docker/nexus/Dockerfile.server | 2 +- docker/openvm/Dockerfile.base | 9 +- docker/openvm/Dockerfile.compiler | 2 +- docker/openvm/Dockerfile.server | 7 +- docker/pico/Dockerfile.compiler | 2 +- docker/pico/Dockerfile.server | 2 +- docker/risc0/Dockerfile.base | 8 +- docker/risc0/Dockerfile.compiler | 2 +- docker/risc0/Dockerfile.server | 7 +- docker/sp1/Dockerfile.compiler | 2 +- docker/sp1/Dockerfile.server | 2 +- docker/ziren/Dockerfile.compiler | 2 +- docker/ziren/Dockerfile.server | 2 +- docker/zisk/Dockerfile.base | 16 +- docker/zisk/Dockerfile.compiler | 8 +- docker/zisk/Dockerfile.server | 7 +- 51 files changed, 1315 insertions(+), 1042 deletions(-) rename crates/dockerized/{dockerized => }/Cargo.toml (89%) rename crates/dockerized/{dockerized => }/build.rs (98%) create mode 100644 crates/dockerized/compiler/src/lib.rs delete mode 100644 crates/dockerized/dockerized/src/error.rs delete mode 100644 crates/dockerized/dockerized/src/lib.rs create mode 100644 crates/dockerized/src/compiler.rs create mode 100644 crates/dockerized/src/compiler/error.rs create mode 100644 crates/dockerized/src/lib.rs create mode 100644 crates/dockerized/src/util.rs rename crates/dockerized/{dockerized/src => src/util}/cuda.rs (100%) rename crates/dockerized/{dockerized/src => src/util}/docker.rs (75%) create mode 100644 crates/dockerized/src/zkvm.rs create mode 100644 crates/dockerized/src/zkvm/error.rs rename docker/{base => }/Dockerfile.base (88%) diff --git a/.github/workflows/test-zkvm.yml b/.github/workflows/test-zkvm.yml index 481b268..df6a0c9 100644 --- a/.github/workflows/test-zkvm.yml +++ b/.github/workflows/test-zkvm.yml @@ -59,7 +59,7 @@ jobs: uses: tj-actions/changed-files@v46 with: files: | - docker/base/Dockerfile.base + docker/Dockerfile.base docker/${{ inputs.zkvm }}/** scripts/sdk_installers/install_${{ inputs.zkvm }}_sdk.sh @@ -97,7 +97,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: docker/base/Dockerfile.base + file: docker/Dockerfile.base push: true tags: ${{ steps.image_meta.outputs.base_image }} @@ -123,7 +123,7 @@ jobs: tags: ${{ steps.image_meta.outputs.compiler_zkvm_image }} build-args: | BASE_ZKVM_IMAGE=${{ steps.image_meta.outputs.base_zkvm_image }} - + - name: Build ere-server-${{ inputs.zkvm }} image if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: docker/build-push-action@v6 @@ -290,4 +290,4 @@ jobs: - name: Run cargo test for ere-${{ inputs.zkvm }} via ere-dockerized run: | cargo test --release --package ere-dockerized \ - -- ${{ inputs.zkvm }} ${{ inputs.skip_prove_test && '--skip prove' || '' }} + -- ${{ inputs.zkvm }} ${{ inputs.skip_prove_test && '--skip prove' || '' }} --test-threads=1 diff --git a/Cargo.lock b/Cargo.lock index 84f4907..fd6b527 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3622,7 +3622,7 @@ dependencies = [ [[package]] name = "ere-airbender" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3639,14 +3639,14 @@ dependencies = [ [[package]] name = "ere-build-utils" -version = "0.0.14" +version = "0.0.15" dependencies = [ "cargo_metadata 0.19.2", ] [[package]] name = "ere-compile-utils" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "cargo_metadata 0.19.2", @@ -3656,7 +3656,7 @@ dependencies = [ [[package]] name = "ere-compiler" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3678,13 +3678,15 @@ dependencies = [ [[package]] name = "ere-dockerized" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "ere-build-utils", + "ere-compiler", "ere-server", "ere-test-utils", "ere-zkvm-interface", + "paste", "serde", "tempfile", "thiserror 2.0.12", @@ -3694,7 +3696,7 @@ dependencies = [ [[package]] name = "ere-io-serde" -version = "0.0.14" +version = "0.0.15" dependencies = [ "bincode 2.0.1", "serde", @@ -3702,7 +3704,7 @@ dependencies = [ [[package]] name = "ere-jolt" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "ark-serialize 0.5.0", @@ -3720,7 +3722,7 @@ dependencies = [ [[package]] name = "ere-miden" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "ere-build-utils", @@ -3738,7 +3740,7 @@ dependencies = [ [[package]] name = "ere-nexus" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3757,7 +3759,7 @@ dependencies = [ [[package]] name = "ere-openvm" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "ere-build-utils", @@ -3778,7 +3780,7 @@ dependencies = [ [[package]] name = "ere-pico" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3796,7 +3798,7 @@ dependencies = [ [[package]] name = "ere-platform-airbender" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "riscv_common", @@ -3804,7 +3806,7 @@ dependencies = [ [[package]] name = "ere-platform-jolt" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "jolt-sdk", @@ -3813,7 +3815,7 @@ dependencies = [ [[package]] name = "ere-platform-nexus" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "nexus-rt", @@ -3821,7 +3823,7 @@ dependencies = [ [[package]] name = "ere-platform-openvm" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "openvm", @@ -3829,7 +3831,7 @@ dependencies = [ [[package]] name = "ere-platform-pico" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "pico-sdk", @@ -3837,7 +3839,7 @@ dependencies = [ [[package]] name = "ere-platform-risc0" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "risc0-zkvm", @@ -3845,7 +3847,7 @@ dependencies = [ [[package]] name = "ere-platform-sp1" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "sp1-zkvm", @@ -3853,14 +3855,14 @@ dependencies = [ [[package]] name = "ere-platform-trait" -version = "0.0.14" +version = "0.0.15" dependencies = [ "digest 0.10.7", ] [[package]] name = "ere-platform-ziren" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "zkm-zkvm", @@ -3868,7 +3870,7 @@ dependencies = [ [[package]] name = "ere-platform-zisk" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-platform-trait", "ziskos", @@ -3876,7 +3878,7 @@ dependencies = [ [[package]] name = "ere-risc0" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "borsh", @@ -3896,7 +3898,7 @@ dependencies = [ [[package]] name = "ere-server" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3926,7 +3928,7 @@ dependencies = [ [[package]] name = "ere-sp1" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3943,7 +3945,7 @@ dependencies = [ [[package]] name = "ere-test-utils" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ere-io-serde", "ere-platform-trait", @@ -3955,7 +3957,7 @@ dependencies = [ [[package]] name = "ere-ziren" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "bincode 2.0.1", @@ -3971,7 +3973,7 @@ dependencies = [ [[package]] name = "ere-zisk" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "blake3", @@ -3989,7 +3991,7 @@ dependencies = [ [[package]] name = "ere-zkvm-interface" -version = "0.0.14" +version = "0.0.15" dependencies = [ "anyhow", "auto_impl", diff --git a/Cargo.toml b/Cargo.toml index 1d57b68..f8e8e03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,8 @@ members = [ "crates/zkvm/zisk", "crates/zkvm/zisk/platform", # Dockerized zkVM + "crates/dockerized", "crates/dockerized/compiler", - "crates/dockerized/dockerized", "crates/dockerized/server", # Utils "crates/io-serde", @@ -36,7 +36,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.0.14" +version = "0.0.15" edition = "2024" rust-version = "1.85" license = "MIT OR Apache-2.0" @@ -56,6 +56,7 @@ dashmap = "6.1.0" digest = { version = "0.10.7", default-features = false } eyre = "0.6.12" indexmap = "2.10.0" +paste = "1.0.15" postcard = { version = "1.0.8", default-features = false } prost = "0.13" prost-build = "0.13" @@ -155,8 +156,8 @@ ere-platform-sp1 = { path = "crates/zkvm/sp1/platform" } ere-platform-ziren = { path = "crates/zkvm/ziren/platform" } ere-platform-zisk = { path = "crates/zkvm/zisk/platform" } +ere-dockerized = { path = "crates/dockerized" } ere-compiler = { path = "crates/dockerized/compiler" } -ere-dockerized = { path = "crates/dockerized/dockerized" } ere-server = { path = "crates/dockerized/server" } ere-io-serde = { path = "crates/io-serde" } diff --git a/README.md b/README.md index ee0849d..28afe37 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ ere-dockerized = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" } ```rust // main.rs -use ere_dockerized::{EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM}; +use ere_dockerized::{CompilerKind, DockerizedCompiler, DockerizedzkVM, zkVMKind}; use ere_zkvm_interface::{ compiler::Compiler, zkvm::{ProofKind, ProverResourceType, zkVM}, @@ -140,11 +140,15 @@ fn main() -> Result<(), Box> { let guest_directory = std::path::Path::new("workspace/guest"); // Compile guest - let compiler = EreDockerizedCompiler::new(ErezkVM::SP1, std::path::Path::new("workspace")); + let compiler = DockerizedCompiler::new( + zkVMKind::SP1, + CompilerKind::RustCustomized, + std::path::Path::new("workspace"), + ); let program = compiler.compile(guest_directory)?; // Create zkVM instance - let zkvm = EreDockerizedzkVM::new(ErezkVM::SP1, program, ProverResourceType::Cpu)?; + let zkvm = DockerizedzkVM::new(zkVMKind::SP1, program, ProverResourceType::Cpu)?; // Serialize input let input = 42u32.to_le_bytes(); diff --git a/crates/compile-utils/src/error.rs b/crates/compile-utils/src/error.rs index 1226a42..cb65625 100644 --- a/crates/compile-utils/src/error.rs +++ b/crates/compile-utils/src/error.rs @@ -73,12 +73,12 @@ impl CommonError { pub fn read_file(id: impl AsRef, path: impl AsRef, err: io::Error) -> Self { let (id, path) = (id.as_ref(), path.as_ref().display()); - Self::io(format!("Failed to write {id} to {path}"), err) + Self::io(format!("Failed to read {id} from {path}"), err) } pub fn write_file(id: impl AsRef, path: impl AsRef, err: io::Error) -> Self { let (id, path) = (id.as_ref(), path.as_ref().display()); - Self::io(format!("Failed to read {id} from {path}"), err) + Self::io(format!("Failed to write {id} to {path}"), err) } pub fn deserialize( diff --git a/crates/dockerized/dockerized/Cargo.toml b/crates/dockerized/Cargo.toml similarity index 89% rename from crates/dockerized/dockerized/Cargo.toml rename to crates/dockerized/Cargo.toml index b07d6ff..8b63828 100644 --- a/crates/dockerized/dockerized/Cargo.toml +++ b/crates/dockerized/Cargo.toml @@ -16,8 +16,12 @@ tracing.workspace = true # Local dependencies ere-zkvm-interface = { workspace = true, features = ["clap"] } ere-server.workspace = true +ere-compiler.workspace = true [dev-dependencies] +paste.workspace = true + +# Local dependencies ere-test-utils = { workspace = true, features = ["host"] } [build-dependencies] diff --git a/crates/dockerized/dockerized/build.rs b/crates/dockerized/build.rs similarity index 98% rename from crates/dockerized/dockerized/build.rs rename to crates/dockerized/build.rs index 2132863..2163377 100644 --- a/crates/dockerized/dockerized/build.rs +++ b/crates/dockerized/build.rs @@ -45,7 +45,7 @@ fn generate_zkvm_sdk_version_impl() { .map(detect_sdk_version); let zkvm_sdk_version_impl = format!( - r#"impl crate::ErezkVM {{ + r#"impl crate::zkVMKind {{ pub fn sdk_version(&self) -> &'static str {{ match self {{ Self::Airbender => "{airbender_version}", diff --git a/crates/dockerized/compiler/src/lib.rs b/crates/dockerized/compiler/src/lib.rs new file mode 100644 index 0000000..476a2c4 --- /dev/null +++ b/crates/dockerized/compiler/src/lib.rs @@ -0,0 +1,48 @@ +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; + +/// Compiler kind to use to compile the guest. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum CompilerKind { + /// Stock Rust compiler + Rust, + /// Rust compiler with customized toolchain + RustCustomized, + /// Go compiler with customized toolchain + GoCustomized, + /// Miden assembly compiler + MidenAsm, +} + +impl CompilerKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::Rust => "rust", + Self::RustCustomized => "rust-customized", + Self::GoCustomized => "go-customized", + Self::MidenAsm => "miden-asm", + } + } +} + +impl FromStr for CompilerKind { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "rust" => Self::Rust, + "rust-customized" => Self::RustCustomized, + "go-customized" => Self::GoCustomized, + "miden-asm" => Self::MidenAsm, + _ => return Err(format!("Unsupported compiler kind {s}")), + }) + } +} + +impl Display for CompilerKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/crates/dockerized/compiler/src/main.rs b/crates/dockerized/compiler/src/main.rs index d8fae5f..5b6a881 100644 --- a/crates/dockerized/compiler/src/main.rs +++ b/crates/dockerized/compiler/src/main.rs @@ -1,8 +1,9 @@ -use anyhow::{Context, Error}; +use anyhow::{Context, Error, bail}; use clap::Parser; +use ere_compiler::CompilerKind; use ere_zkvm_interface::compiler::Compiler; use serde::Serialize; -use std::{env, fs::File, path::PathBuf}; +use std::{fs::File, path::PathBuf}; use tracing_subscriber::EnvFilter; // Compile-time check to ensure exactly one zkVM feature is enabled for `ere-compiler` @@ -26,6 +27,9 @@ const _: () = { #[derive(Parser)] #[command(author, version)] struct Args { + /// Compiler kind to use + #[arg(long, value_parser = ::from_str)] + compiler_kind: CompilerKind, /// Path to the guest program #[arg(long)] guest_path: PathBuf, @@ -41,7 +45,7 @@ fn main() -> Result<(), Error> { let args = Args::parse(); - let program = compile(args.guest_path)?; + let program = compile(args.guest_path, args.compiler_kind)?; let mut output = File::create(args.output_path).with_context(|| "Failed to create output")?; bincode::serde::encode_into_std_write(&program, &mut output, bincode::config::legacy()) @@ -50,62 +54,140 @@ fn main() -> Result<(), Error> { Ok(()) } -fn compile(guest_path: PathBuf) -> Result { +fn compile(guest_path: PathBuf, compiler_kind: CompilerKind) -> Result { #[cfg(feature = "airbender")] - let result = ere_airbender::compiler::RustRv32ima.compile(&guest_path); + let result = { + use ere_airbender::compiler::*; + match compiler_kind { + CompilerKind::Rust | CompilerKind::RustCustomized => RustRv32ima.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } + }; #[cfg(feature = "jolt")] - let result = if use_stock_rust() { - ere_jolt::compiler::RustRv64imac.compile(&guest_path) - } else { - ere_jolt::compiler::RustRv64imacCustomized.compile(&guest_path) + let result = { + use ere_jolt::compiler::*; + match compiler_kind { + CompilerKind::Rust => RustRv64imac.compile(&guest_path), + CompilerKind::RustCustomized => RustRv64imacCustomized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } }; #[cfg(feature = "miden")] - let result = ere_miden::compiler::MidenAsm.compile(&guest_path); + let result = { + use ere_miden::compiler::*; + match compiler_kind { + CompilerKind::MidenAsm => MidenAsm.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::MidenAsm] + )), + } + }; #[cfg(feature = "nexus")] - let result = ere_nexus::compiler::RustRv32i.compile(&guest_path); + let result = { + use ere_nexus::compiler::*; + match compiler_kind { + CompilerKind::Rust | CompilerKind::RustCustomized => RustRv32i.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } + }; #[cfg(feature = "openvm")] - let result = if use_stock_rust() { - ere_openvm::compiler::RustRv32ima.compile(&guest_path) - } else { - ere_openvm::compiler::RustRv32imaCustomized.compile(&guest_path) + let result = { + use ere_openvm::compiler::*; + match compiler_kind { + CompilerKind::Rust => RustRv32ima.compile(&guest_path), + CompilerKind::RustCustomized => RustRv32imaCustomized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } }; #[cfg(feature = "pico")] - let result = if use_stock_rust() { - ere_pico::compiler::RustRv32ima.compile(&guest_path) - } else { - ere_pico::compiler::RustRv32imaCustomized.compile(&guest_path) + let result = { + use ere_pico::compiler::*; + match compiler_kind { + CompilerKind::Rust => RustRv32ima.compile(&guest_path), + CompilerKind::RustCustomized => RustRv32imaCustomized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } }; #[cfg(feature = "risc0")] - let result = if use_stock_rust() { - ere_risc0::compiler::RustRv32ima.compile(&guest_path) - } else { - ere_risc0::compiler::RustRv32imaCustomized.compile(&guest_path) + let result = { + use ere_risc0::compiler::*; + match compiler_kind { + CompilerKind::Rust => RustRv32ima.compile(&guest_path), + CompilerKind::RustCustomized => RustRv32imaCustomized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } }; #[cfg(feature = "sp1")] - let result = if use_stock_rust() { - ere_sp1::compiler::RustRv32ima.compile(&guest_path) - } else { - ere_sp1::compiler::RustRv32imaCustomized.compile(&guest_path) + let result = { + use ere_sp1::compiler::*; + match compiler_kind { + CompilerKind::Rust => RustRv32ima.compile(&guest_path), + CompilerKind::RustCustomized => RustRv32imaCustomized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::Rust, CompilerKind::RustCustomized] + )), + } }; #[cfg(feature = "ziren")] - let result = ere_ziren::compiler::RustMips32r2Customized.compile(&guest_path); + let result = { + use ere_ziren::compiler::*; + match compiler_kind { + CompilerKind::RustCustomized => RustMips32r2Customized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::RustCustomized] + )), + } + }; #[cfg(feature = "zisk")] - let result = ere_zisk::compiler::RustRv64imaCustomized.compile(&guest_path); + let result = { + use ere_zisk::compiler::*; + match compiler_kind { + CompilerKind::RustCustomized => RustRv64imaCustomized.compile(&guest_path), + CompilerKind::GoCustomized => GoCustomized.compile(&guest_path), + _ => bail!(unsupported_compiler_kind_err( + compiler_kind, + [CompilerKind::RustCustomized, CompilerKind::GoCustomized] + )), + } + }; result.with_context(|| "Failed to compile program") } -#[allow(dead_code)] -/// Returns whether to use stock Rust compiler instead of customized compiler. -fn use_stock_rust() -> bool { - env::var_os("ERE_RUST_TOOLCHAIN").is_some() +fn unsupported_compiler_kind_err( + compiler_kind: CompilerKind, + supported: impl IntoIterator, +) -> anyhow::Error { + let supported = supported.into_iter().collect::>(); + anyhow::anyhow!("Unsupported compiler kind {compiler_kind:?}, expect one of {supported:?}",) } diff --git a/crates/dockerized/dockerized/src/error.rs b/crates/dockerized/dockerized/src/error.rs deleted file mode 100644 index ea359df..0000000 --- a/crates/dockerized/dockerized/src/error.rs +++ /dev/null @@ -1,54 +0,0 @@ -use ere_server::client::{self, TwirpErrorResponse}; -use std::{io, path::PathBuf}; -use thiserror::Error; - -impl From for Error { - fn from(value: client::Error) -> Self { - match value { - client::Error::zkVM(err) => Self::zkVM(err), - client::Error::ConnectionTimeout => Self::ConnectionTimeout, - client::Error::Rpc(err) => Self::Rpc(err), - } - } -} - -#[derive(Debug, Error)] -#[allow(non_camel_case_types)] -pub enum Error { - #[error( - "Guest directory must be in mounting directory, mounting_directory: {mounting_directory}, guest_directory: {guest_directory}" - )] - GuestNotInMountingDirecty { - mounting_directory: PathBuf, - guest_directory: PathBuf, - }, - #[error("{context}: {source}")] - Io { - #[source] - source: io::Error, - context: String, - }, - #[error("Failed to execute `docker image`: {0}")] - DockerImageCmd(io::Error), - #[error("Failed to execute `docker build`: {0}")] - DockerBuildCmd(io::Error), - #[error("Failed to execute `docker run`: {0}")] - DockerRunCmd(io::Error), - #[error("Failed to execute `docker container`: {0}")] - DockerContainerCmd(io::Error), - #[error("zkVM method error: {0}")] - zkVM(String), - #[error("Connection to zkVM server timeout after 5 minutes")] - ConnectionTimeout, - #[error("RPC to zkVM server error: {0}")] - Rpc(TwirpErrorResponse), -} - -impl Error { - pub fn io(source: io::Error, context: impl ToString) -> Self { - Self::Io { - source, - context: context.to_string(), - } - } -} diff --git a/crates/dockerized/dockerized/src/lib.rs b/crates/dockerized/dockerized/src/lib.rs deleted file mode 100644 index 3f0ce1b..0000000 --- a/crates/dockerized/dockerized/src/lib.rs +++ /dev/null @@ -1,770 +0,0 @@ -//! # Ere Dockerized -//! -//! A Docker-based wrapper for other zkVM crates `ere-{zkvm}`. -//! -//! This crate provides a unified interface to dockerize the `Compiler` and -//! `zkVM` implementation of other zkVM crates `ere-{zkvm}`, it requires only -//! `docker` to be installed, but no zkVM specific SDK. -//! -//! ## Docker image building -//! -//! It builds 4 Docker images in sequence if they don't exist: -//! 1. `ere-base:{version}` - Base image with common dependencies -//! 2. `ere-base-{zkvm}:{version}` - zkVM-specific base image with the zkVM SDK -//! 3. `ere-compiler-{zkvm}:{version}` - Compiler image with the `ere-compiler` -//! binary built with the selected zkVM feature -//! 4. `ere-server-{zkvm}:{version}` - Server image with the `ere-server` binary -//! built with the selected zkVM feature -//! -//! When [`ProverResourceType::Gpu`] is selected, the image with GPU support -//! will be built and tagged with specific suffix. -//! -//! To force rebuild all images, set the environment variable -//! `ERE_FORCE_REBUILD_DOCKER_IMAGE` to non-empty value. -//! -//! ## Example -//! -//! ```rust,no_run -//! # fn main() -> Result<(), Box> { -//! use ere_dockerized::{EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM}; -//! use ere_zkvm_interface::{ -//! compiler::Compiler, -//! zkvm::{ProofKind, ProverResourceType, zkVM}, -//! }; -//! use std::path::Path; -//! -//! // The zkVM we plan to use -//! let zkvm = ErezkVM::SP1; -//! -//! // Compile a guest program -//! let compiler = EreDockerizedCompiler::new(zkvm, "mounting/directory")?; -//! let guest_path = Path::new("relative/path/to/guest/program"); -//! let program = compiler.compile(&guest_path)?; -//! -//! // Create zkVM instance -//! let resource = ProverResourceType::Cpu; -//! let zkvm = EreDockerizedzkVM::new(zkvm, program, resource)?; -//! -//! // Serialize input -//! let input = 42u32.to_le_bytes(); -//! -//! // Execute program -//! let (public_values, execution_report) = zkvm.execute(&input)?; -//! println!("Execution cycles: {}", execution_report.total_num_cycles); -//! -//! // Generate proof -//! let (public_values, proof, proving_report) = zkvm.prove(&input, ProofKind::Compressed)?; -//! println!("Proof generated in: {:?}", proving_report.proving_time); -//! -//! // Verify proof -//! let public_values = zkvm.verify(&proof)?; -//! println!("Proof verified successfully!"); -//! # Ok(()) -//! # } -//! ``` - -#![cfg_attr(not(test), warn(unused_crate_dependencies))] - -use crate::{ - cuda::cuda_arch, - docker::{DockerBuildCmd, DockerRunCmd, docker_image_exists, stop_docker_container}, -}; -use ere_server::client::{Url, zkVMClient}; -use ere_zkvm_interface::{ - compiler::Compiler, - zkvm::{ - ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType, - PublicValues, zkVM, - }, -}; -use serde::{Deserialize, Serialize}; -use std::{ - env, - fmt::{self, Display, Formatter}, - fs, iter, - path::{Path, PathBuf}, - str::FromStr, -}; -use tempfile::TempDir; -use tracing::error; - -mod cuda; -mod docker; -mod error; - -pub use error::Error; - -include!(concat!(env!("OUT_DIR"), "/crate_version.rs")); -include!(concat!(env!("OUT_DIR"), "/zkvm_sdk_version_impl.rs")); - -/// Offset of port used for `ere-server` for [`ErezkVM`]s. -const ERE_SERVER_PORT_OFFSET: u16 = 4174; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ErezkVM { - Airbender, - Jolt, - Miden, - Nexus, - OpenVM, - Pico, - Risc0, - SP1, - Ziren, - Zisk, -} - -impl ErezkVM { - pub fn as_str(&self) -> &'static str { - match self { - Self::Airbender => "airbender", - Self::Jolt => "jolt", - Self::Miden => "miden", - Self::Nexus => "nexus", - Self::OpenVM => "openvm", - Self::Pico => "pico", - Self::Risc0 => "risc0", - Self::SP1 => "sp1", - Self::Ziren => "ziren", - Self::Zisk => "zisk", - } - } - - /// Tag of images in format of `{version}{suffix}`. - fn image_tag(&self, version: &str, gpu: bool) -> String { - let suffix = match (gpu, self) { - // Only the following zkVMs requires CUDA setup in the base image - // when GPU support is required. - (true, Self::Airbender | Self::OpenVM | Self::Risc0 | Self::Zisk) => "-cuda", - _ => "", - }; - format!("{version}{suffix}") - } - - pub fn base_image(&self, version: &str, gpu: bool) -> String { - format!("ere-base:{}", self.image_tag(version, gpu)) - } - - pub fn base_zkvm_image(&self, version: &str, gpu: bool) -> String { - format!("ere-base-{self}:{}", self.image_tag(version, gpu)) - } - - pub fn compiler_zkvm_image(&self, version: &str) -> String { - format!("ere-compiler-{self}:{}", self.image_tag(version, false)) - } - - pub fn server_zkvm_image(&self, version: &str, gpu: bool) -> String { - format!("ere-server-{self}:{}", self.image_tag(version, gpu)) - } - - /// This method builds 4 Docker images in sequence: - /// 1. `ere-base:{version}`: Base image with common dependencies - /// 2. `ere-base-{zkvm}:{version}`: zkVM-specific base image with the zkVM SDK - /// 3. `ere-compiler-{zkvm}:{version}` - Compiler image with the `ere-compiler` - /// binary built with the selected zkVM feature - /// 4. `ere-server-{zkvm}:{version}` - Server image with the `ere-server` binary - /// built with the selected zkVM feature - /// - /// When [`ProverResourceType::Gpu`] is selected, the image with GPU support - /// will be built and tagged with specific suffix. - /// - /// Images are cached and only rebuilt if they don't exist or if the - /// `ERE_FORCE_REBUILD_DOCKER_IMAGE` environment variable is set. - pub fn build_docker_image(&self, gpu: bool) -> Result<(), Error> { - let workspace_dir = workspace_dir(); - let docker_dir = workspace_dir.join("docker"); - - let force_rebuild = env::var_os("ERE_FORCE_REBUILD_DOCKER_IMAGE").is_some(); - - // Build `ere-base` - if force_rebuild || !docker_image_exists(self.base_image(CRATE_VERSION, gpu))? { - let mut cmd = DockerBuildCmd::new() - .file(docker_dir.join("base").join("Dockerfile.base")) - .tag(self.base_image(CRATE_VERSION, gpu)) - .tag(self.base_image("latest", gpu)); - - if gpu { - cmd = cmd.build_arg("CUDA", "1"); - } - - cmd.exec(&workspace_dir).map_err(Error::DockerBuildCmd)?; - } - - // Build `ere-base-{zkvm}` - if force_rebuild || !docker_image_exists(self.base_zkvm_image(CRATE_VERSION, gpu))? { - let mut cmd = DockerBuildCmd::new() - .file(docker_dir.join(self.as_str()).join("Dockerfile.base")) - .tag(self.base_zkvm_image(CRATE_VERSION, gpu)) - .tag(self.base_zkvm_image("latest", gpu)) - .build_arg("BASE_IMAGE", self.base_image(CRATE_VERSION, gpu)) - .build_arg_from_env("RUSTFLAGS"); - - if gpu { - cmd = cmd.build_arg("CUDA", "1"); - - let cuda_arch = cuda_arch(); - match self { - Self::Airbender | Self::OpenVM | Self::Risc0 | Self::Zisk => { - if let Some(cuda_arch) = cuda_arch { - cmd = cmd.build_arg("CUDA_ARCH", cuda_arch) - } - } - _ => {} - } - } - - cmd.exec(&workspace_dir).map_err(Error::DockerBuildCmd)?; - } - - // Build `ere-compiler-{zkvm}` - if force_rebuild || !docker_image_exists(self.compiler_zkvm_image(CRATE_VERSION))? { - DockerBuildCmd::new() - .file(docker_dir.join(self.as_str()).join("Dockerfile.compiler")) - .tag(self.compiler_zkvm_image(CRATE_VERSION)) - .tag(self.compiler_zkvm_image("latest")) - .build_arg("BASE_ZKVM_IMAGE", self.base_zkvm_image(CRATE_VERSION, gpu)) - .exec(&workspace_dir) - .map_err(Error::DockerBuildCmd)?; - } - - // Build `ere-server-{zkvm}` - if force_rebuild || !docker_image_exists(self.server_zkvm_image(CRATE_VERSION, gpu))? { - let mut cmd = DockerBuildCmd::new() - .file(docker_dir.join(self.as_str()).join("Dockerfile.server")) - .tag(self.server_zkvm_image(CRATE_VERSION, gpu)) - .tag(self.server_zkvm_image("latest", gpu)) - .build_arg("BASE_ZKVM_IMAGE", self.base_zkvm_image(CRATE_VERSION, gpu)) - .build_arg_from_env("RUSTFLAGS"); - - if gpu { - cmd = cmd.build_arg("CUDA", "1"); - } - - cmd.exec(&workspace_dir).map_err(Error::DockerBuildCmd)?; - } - - Ok(()) - } - - fn server_port(&self) -> u16 { - ERE_SERVER_PORT_OFFSET + *self as u16 - } - - fn spawn_server( - &self, - program: &SerializedProgram, - resource: &ProverResourceType, - ) -> Result { - let port = self.server_port().to_string(); - let name = format!("ere-server-{self}-{port}"); - let gpu = matches!(resource, ProverResourceType::Gpu); - let mut cmd = DockerRunCmd::new(self.server_zkvm_image(CRATE_VERSION, gpu)) - .rm() - .inherit_env("RUST_LOG") - .inherit_env("NO_COLOR") - .publish(&port, &port) - .name(&name); - - // zkVM specific options - cmd = match self { - Self::Risc0 => cmd - .inherit_env("RISC0_SEGMENT_PO2") - .inherit_env("RISC0_KECCAK_PO2"), - // ZisK uses shared memory to exchange data between processes, it - // requires at least 8G shared memory, here we set 16G for safety. - Self::Zisk => cmd - .option("shm-size", "16G") - .option("ulimit", "memlock=-1:-1") - .inherit_env("ZISK_PORT") - .inherit_env("ZISK_CHUNK_SIZE_BITS") - .inherit_env("ZISK_UNLOCK_MAPPED_MEMORY") - .inherit_env("ZISK_MINIMAL_MEMORY") - .inherit_env("ZISK_PREALLOCATE") - .inherit_env("ZISK_SHARED_TABLES") - .inherit_env("ZISK_MAX_STREAMS") - .inherit_env("ZISK_NUMBER_THREADS_WITNESS") - .inherit_env("ZISK_MAX_WITNESS_STORED"), - _ => cmd, - }; - - // zkVM specific options when using GPU - if gpu { - cmd = match self { - Self::Airbender => cmd.gpus("all"), - Self::OpenVM => cmd.gpus("all"), - // SP1 runs docker command to spin up the server to do GPU - // proving, to give the client access to the prover service, we - // need to use the host networking driver. - Self::SP1 => cmd.mount_docker_socket().network("host"), - Self::Risc0 => cmd.gpus("all").inherit_env("RISC0_DEFAULT_PROVER_NUM_GPUS"), - Self::Zisk => cmd.gpus("all"), - _ => cmd, - } - } - - let tempdir = - TempDir::new().map_err(|err| Error::io(err, "Failed to create temporary directory"))?; - - // zkVM specific options needed for proving Groth16 proof. - cmd = match self { - // Risc0 and SP1 runs docker command to prove Groth16 proof, and - // they pass the input by mounting temporary directory. Here we - // create a temporary directory and mount it on the top level, so - // the volume could be shared, and override `TMPDIR` so we don't - // need to mount the whole `/tmp`. - Self::Risc0 => cmd - .mount_docker_socket() - .env("TMPDIR", tempdir.path().to_string_lossy()) - .volume(tempdir.path(), tempdir.path()), - Self::SP1 => { - let groth16_circuit_path = home_dir().join(".sp1").join("circuits").join("groth16"); - cmd.mount_docker_socket() - .env( - "SP1_GROTH16_CIRCUIT_PATH", - groth16_circuit_path.to_string_lossy(), - ) - .env("TMPDIR", tempdir.path().to_string_lossy()) - .volume(tempdir.path(), tempdir.path()) - .volume(&groth16_circuit_path, &groth16_circuit_path) - } - _ => cmd, - }; - - let args = iter::empty() - .chain(["--port", &port]) - .chain(resource.to_args()); - cmd.spawn(args, &program.0).map_err(Error::DockerRunCmd)?; - - Ok(ServerContainer { name, tempdir }) - } -} - -impl FromStr for ErezkVM { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "airbender" => Self::Airbender, - "jolt" => Self::Jolt, - "miden" => Self::Miden, - "nexus" => Self::Nexus, - "openvm" => Self::OpenVM, - "pico" => Self::Pico, - "risc0" => Self::Risc0, - "sp1" => Self::SP1, - "ziren" => Self::Ziren, - "zisk" => Self::Zisk, - _ => return Err(format!("Unsupported zkvm {s}")), - }) - } -} - -impl Display for ErezkVM { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -pub struct EreDockerizedCompiler { - zkvm: ErezkVM, - mount_directory: PathBuf, -} - -impl EreDockerizedCompiler { - pub fn new(zkvm: ErezkVM, mount_directory: impl AsRef) -> Result { - zkvm.build_docker_image(false)?; - Ok(Self { - zkvm, - mount_directory: mount_directory.as_ref().to_path_buf(), - }) - } - - pub fn zkvm(&self) -> ErezkVM { - self.zkvm - } -} - -/// Wrapper for serialized program. -#[derive(Clone, Serialize, Deserialize)] -pub struct SerializedProgram(Vec); - -impl Compiler for EreDockerizedCompiler { - type Error = Error; - type Program = SerializedProgram; - - fn compile(&self, guest_directory: &Path) -> Result { - let guest_relative_path = guest_directory - .strip_prefix(&self.mount_directory) - .map_err(|_| Error::GuestNotInMountingDirecty { - mounting_directory: self.mount_directory.to_path_buf(), - guest_directory: guest_directory.to_path_buf(), - })?; - let guest_path_in_docker = PathBuf::from("/guest").join(guest_relative_path); - - let tempdir = - TempDir::new().map_err(|err| Error::io(err, "Failed to create temporary directory"))?; - - let mut cmd = DockerRunCmd::new(self.zkvm.compiler_zkvm_image(CRATE_VERSION)) - .rm() - .inherit_env("RUST_LOG") - .inherit_env("NO_COLOR") - .inherit_env("ERE_RUST_TOOLCHAIN") - .volume(&self.mount_directory, "/guest") - .volume(tempdir.path(), "/output"); - - cmd = match self.zkvm { - // OpenVM allows to select Rust toolchain for guest compilation. - ErezkVM::OpenVM => cmd.inherit_env("OPENVM_RUST_TOOLCHAIN"), - _ => cmd, - }; - - cmd.exec([ - "--guest-path", - guest_path_in_docker.to_string_lossy().as_ref(), - "--output-path", - "/output/program", - ]) - .map_err(Error::DockerRunCmd)?; - - let program_path = tempdir.path().join("program"); - let program = fs::read(&program_path).map_err(|err| { - Error::io( - err, - format!( - "Failed to read compiled program at {}", - program_path.display() - ), - ) - })?; - Ok(SerializedProgram(program)) - } -} - -struct ServerContainer { - name: String, - #[allow(dead_code)] - tempdir: TempDir, -} - -impl Drop for ServerContainer { - fn drop(&mut self) { - if let Err(err) = stop_docker_container(&self.name) { - error!("{err}"); - } - } -} - -pub struct EreDockerizedzkVM { - zkvm: ErezkVM, - program: SerializedProgram, - resource: ProverResourceType, - #[allow(dead_code)] - server_container: ServerContainer, - client: zkVMClient, -} - -impl EreDockerizedzkVM { - pub fn new( - zkvm: ErezkVM, - program: SerializedProgram, - resource: ProverResourceType, - ) -> Result { - zkvm.build_docker_image(matches!(resource, ProverResourceType::Gpu))?; - - let server_container = zkvm.spawn_server(&program, &resource)?; - - let url = Url::parse(&format!("http://127.0.0.1:{}", zkvm.server_port())).unwrap(); - let client = block_on(zkVMClient::new(url))?; - - Ok(Self { - zkvm, - program, - resource, - server_container, - client, - }) - } - - pub fn zkvm(&self) -> ErezkVM { - self.zkvm - } - - pub fn program(&self) -> &SerializedProgram { - &self.program - } - - pub fn resource(&self) -> &ProverResourceType { - &self.resource - } -} - -impl zkVM for EreDockerizedzkVM { - fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> { - let (public_values, report) = - block_on(self.client.execute(input.to_vec())).map_err(Error::from)?; - - Ok((public_values, report)) - } - - fn prove( - &self, - input: &[u8], - proof_kind: ProofKind, - ) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> { - let (public_values, proof, report) = - block_on(self.client.prove(input.to_vec(), proof_kind)).map_err(Error::from)?; - - Ok((public_values, proof, report)) - } - - fn verify(&self, proof: &Proof) -> anyhow::Result { - let public_values = block_on(self.client.verify(proof)).map_err(Error::from)?; - - Ok(public_values) - } - - fn name(&self) -> &'static str { - self.zkvm.as_str() - } - - fn sdk_version(&self) -> &'static str { - self.zkvm.sdk_version() - } -} - -fn block_on(future: impl Future) -> T { - match tokio::runtime::Handle::try_current() { - Ok(handle) => tokio::task::block_in_place(|| handle.block_on(future)), - Err(_) => tokio::runtime::Runtime::new().unwrap().block_on(future), - } -} - -fn workspace_dir() -> PathBuf { - let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - dir.pop(); - dir.pop(); - dir.pop(); - dir.canonicalize().unwrap() -} - -fn home_dir() -> PathBuf { - PathBuf::from(std::env::var("HOME").expect("env `$HOME` should be set")) -} - -#[cfg(test)] -mod test { - use crate::{ - EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM, Error, SerializedProgram, workspace_dir, - }; - use ere_test_utils::{host::*, program::basic::BasicProgramInput}; - use ere_zkvm_interface::{ - compiler::Compiler, - zkvm::{ProofKind, ProverResourceType, zkVM}, - }; - use std::sync::{Mutex, MutexGuard, OnceLock}; - - macro_rules! test_compile { - ($zkvm:ident, $program:literal) => { - use super::*; - - fn program() -> &'static SerializedProgram { - static PROGRAM: OnceLock = OnceLock::new(); - PROGRAM.get_or_init(|| { - let zkvm = ErezkVM::$zkvm; - let guest_directory = testing_guest_directory(zkvm.as_str(), $program); - EreDockerizedCompiler::new(zkvm, workspace_dir()) - .unwrap() - .compile(&guest_directory) - .unwrap() - }) - } - - #[allow(dead_code)] - fn zkvm() -> (MutexGuard<'static, ()>, EreDockerizedzkVM) { - static LOCK: Mutex<()> = Mutex::new(()); - let guard = LOCK.lock().unwrap(); - let zkvm = ErezkVM::$zkvm; - let zkvm = EreDockerizedzkVM::new(zkvm, program().clone(), ProverResourceType::Cpu) - .unwrap(); - (guard, zkvm) - } - - #[test] - fn test_compile() { - let program = program(); - - assert!(!program.0.is_empty(), "Program should not be empty"); - } - }; - } - - macro_rules! test_execute { - ($zkvm:ident, $valid_test_case:expr, $invalid_test_cases:expr) => { - #[test] - fn test_execute() { - let (_guard, zkvm) = zkvm(); - - // Valid test case - run_zkvm_execute(&zkvm, &$valid_test_case); - - // Invalid test cases - for input in $invalid_test_cases { - let err = zkvm.execute(&input).unwrap_err(); - assert!(matches!(err.downcast::().unwrap(), Error::zkVM(_)),); - } - - drop(zkvm); - } - }; - } - - macro_rules! test_prove { - ($zkvm:ident, $valid_test_case:expr, $invalid_test_cases:expr) => { - #[test] - fn test_prove() { - let (_guard, zkvm) = zkvm(); - - // Valid test case - run_zkvm_prove(&zkvm, &$valid_test_case); - - // Invalid test cases - for input in $invalid_test_cases { - let err = zkvm.prove(&input, ProofKind::default()).unwrap_err(); - assert!(matches!(err.downcast::().unwrap(), Error::zkVM(_)),); - } - - drop(zkvm); - } - }; - } - - mod airbender { - test_compile!(Airbender, "basic"); - test_execute!( - Airbender, - BasicProgramInput::valid().into_output_sha256(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Airbender, - BasicProgramInput::valid().into_output_sha256(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod jolt { - test_compile!(Jolt, "basic"); - test_execute!( - Jolt, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Jolt, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod miden { - test_compile!(Miden, "fib"); - } - - mod nexus { - test_compile!(Nexus, "basic"); - test_execute!( - Nexus, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Nexus, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod openvm { - test_compile!(OpenVM, "basic"); - test_execute!( - OpenVM, - BasicProgramInput::valid().into_output_sha256(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - OpenVM, - BasicProgramInput::valid().into_output_sha256(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod pico { - test_compile!(Pico, "basic"); - test_execute!( - Pico, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Pico, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod risc0 { - test_compile!(Risc0, "basic"); - test_execute!( - Risc0, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Risc0, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod sp1 { - test_compile!(SP1, "basic"); - test_execute!( - SP1, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - SP1, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod ziren { - test_compile!(Ziren, "basic"); - test_execute!( - Ziren, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Ziren, - BasicProgramInput::valid(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } - - mod zisk { - test_compile!(Zisk, "basic_rust"); - test_execute!( - Zisk, - BasicProgramInput::valid().into_output_sha256(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - test_prove!( - Zisk, - BasicProgramInput::valid().into_output_sha256(), - [Vec::new(), BasicProgramInput::invalid().serialized_input()] - ); - } -} diff --git a/crates/dockerized/src/compiler.rs b/crates/dockerized/src/compiler.rs new file mode 100644 index 0000000..fac3629 --- /dev/null +++ b/crates/dockerized/src/compiler.rs @@ -0,0 +1,236 @@ +use crate::{ + CompilerKind, base_image, base_zkvm_image, compiler_zkvm_image, + util::{ + docker::{DockerBuildCmd, DockerRunCmd, docker_image_exists, force_rebuild}, + workspace_dir, + }, + zkVMKind, +}; +use ere_zkvm_interface::{CommonError, compiler::Compiler}; +use serde::{Deserialize, Serialize}; +use std::{ + fs, + path::{Path, PathBuf}, +}; +use tempfile::TempDir; +use tracing::info; + +mod error; + +pub use error::Error; + +/// This method builds 3 Docker images in sequence: +/// 1. `ere-base:{version}` - Base image with common dependencies +/// 2. `ere-base-{zkvm}:{version}` - zkVM-specific base image with the zkVM SDK +/// 3. `ere-compiler-{zkvm}:{version}` - Compiler image with the `ere-compiler` +/// binary built with the selected zkVM feature +/// +/// Images are cached and only rebuilt if they don't exist or if the +/// `ERE_FORCE_REBUILD_DOCKER_IMAGE` environment variable is set. +fn build_compiler_image(zkvm_kind: zkVMKind) -> Result<(), Error> { + let workspace_dir = workspace_dir(); + let docker_dir = workspace_dir.join("docker"); + let docker_zkvm_dir = docker_dir.join(zkvm_kind.as_str()); + + let force_rebuild = force_rebuild(); + let base_image = base_image(zkvm_kind, false); + let base_zkvm_image = base_zkvm_image(zkvm_kind, false); + let compiler_zkvm_image = compiler_zkvm_image(zkvm_kind); + + // Build `ere-base` + if force_rebuild || !docker_image_exists(&base_image)? { + info!("Building image {base_image}..."); + + DockerBuildCmd::new() + .file(docker_dir.join("Dockerfile.base")) + .tag(&base_image) + .exec(&workspace_dir)?; + } + + // Build `ere-base-{zkvm_kind}` + if force_rebuild || !docker_image_exists(&base_zkvm_image)? { + info!("Building image {base_zkvm_image}..."); + + DockerBuildCmd::new() + .file(docker_zkvm_dir.join("Dockerfile.base")) + .tag(&base_zkvm_image) + .build_arg("BASE_IMAGE", &base_image) + .build_arg_from_env("RUSTFLAGS") + .exec(&workspace_dir)?; + } + + // Build `ere-compiler-{zkvm_kind}` + if force_rebuild || !docker_image_exists(&compiler_zkvm_image)? { + info!("Building image {compiler_zkvm_image}..."); + + DockerBuildCmd::new() + .file(docker_zkvm_dir.join("Dockerfile.compiler")) + .tag(&compiler_zkvm_image) + .build_arg("BASE_ZKVM_IMAGE", &base_zkvm_image) + .exec(&workspace_dir)?; + } + + Ok(()) +} + +/// Wrapper for serialized program. +#[derive(Clone, Serialize, Deserialize)] +pub struct SerializedProgram(pub(crate) Vec); + +pub struct DockerizedCompiler { + zkvm_kind: zkVMKind, + compiler_kind: CompilerKind, + mount_directory: PathBuf, +} + +impl DockerizedCompiler { + pub fn new( + zkvm_kind: zkVMKind, + compiler_kind: CompilerKind, + mount_directory: impl AsRef, + ) -> Result { + build_compiler_image(zkvm_kind)?; + Ok(Self { + zkvm_kind, + compiler_kind, + mount_directory: mount_directory.as_ref().to_path_buf(), + }) + } + + pub fn zkvm_kind(&self) -> zkVMKind { + self.zkvm_kind + } + + pub fn compiler_kind(&self) -> CompilerKind { + self.compiler_kind + } +} + +impl Compiler for DockerizedCompiler { + type Error = Error; + type Program = SerializedProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + let guest_relative_path = guest_directory + .strip_prefix(&self.mount_directory) + .map_err(|_| Error::GuestNotInMountingDirecty { + mounting_directory: self.mount_directory.to_path_buf(), + guest_directory: guest_directory.to_path_buf(), + })?; + let guest_path_in_docker = PathBuf::from("/guest").join(guest_relative_path); + + let tempdir = TempDir::new().map_err(CommonError::tempdir)?; + + let mut cmd = DockerRunCmd::new(compiler_zkvm_image(self.zkvm_kind)) + .rm() + .inherit_env("RUST_LOG") + .inherit_env("NO_COLOR") + .inherit_env("ERE_RUST_TOOLCHAIN") + .volume(&self.mount_directory, "/guest") + .volume(tempdir.path(), "/output"); + + cmd = match self.zkvm_kind { + // OpenVM allows to select Rust toolchain for guest compilation. + zkVMKind::OpenVM => cmd.inherit_env("OPENVM_RUST_TOOLCHAIN"), + _ => cmd, + }; + + cmd.exec([ + "--compiler-kind", + self.compiler_kind.as_str(), + "--guest-path", + guest_path_in_docker.to_string_lossy().as_ref(), + "--output-path", + "/output/program", + ])?; + + let program_path = tempdir.path().join("program"); + let program = fs::read(&program_path) + .map_err(|err| CommonError::read_file("program", &program_path, err))?; + Ok(SerializedProgram(program)) + } +} + +#[cfg(test)] +pub(crate) mod test { + use crate::{ + CompilerKind, + compiler::{DockerizedCompiler, SerializedProgram}, + util::workspace_dir, + zkVMKind, + }; + use ere_test_utils::host::testing_guest_directory; + use ere_zkvm_interface::compiler::Compiler; + + pub fn compile( + zkvm_kind: zkVMKind, + compiler_kind: CompilerKind, + program: &'static str, + ) -> SerializedProgram { + DockerizedCompiler::new(zkvm_kind, compiler_kind, workspace_dir()) + .unwrap() + .compile(&testing_guest_directory(zkvm_kind.as_str(), program)) + .unwrap() + } + + macro_rules! test_compile { + ($zkvm_kind:ident, $compiler_kind:ident, $program:literal) => { + paste::paste! { + #[test] + fn []() { + let zkvm_kind = crate::zkVMKind::$zkvm_kind; + let compiler_kind = crate::CompilerKind::$compiler_kind; + let program = crate::compiler::test::compile(zkvm_kind, compiler_kind, $program); + + assert!(!program.0.is_empty(), "Program should not be empty"); + } + } + }; + } + + mod airbender { + test_compile!(Airbender, Rust, "basic"); + } + + mod jolt { + test_compile!(Jolt, RustCustomized, "basic"); + test_compile!(Jolt, Rust, "stock_nightly_no_std"); + } + + mod miden { + test_compile!(Miden, MidenAsm, "fib"); + } + + mod nexus { + test_compile!(Nexus, Rust, "basic"); + } + + mod openvm { + test_compile!(OpenVM, RustCustomized, "basic"); + test_compile!(OpenVM, Rust, "stock_nightly_no_std"); + } + + mod pico { + test_compile!(Pico, RustCustomized, "basic"); + test_compile!(Pico, Rust, "stock_nightly_no_std"); + } + + mod risc0 { + test_compile!(Risc0, RustCustomized, "basic"); + test_compile!(Risc0, Rust, "stock_nightly_no_std"); + } + + mod sp1 { + test_compile!(SP1, RustCustomized, "basic"); + test_compile!(SP1, Rust, "stock_nightly_no_std"); + } + + mod ziren { + test_compile!(Ziren, RustCustomized, "basic"); + } + + mod zisk { + test_compile!(Zisk, RustCustomized, "basic_rust"); + test_compile!(Zisk, GoCustomized, "basic_go"); + } +} diff --git a/crates/dockerized/src/compiler/error.rs b/crates/dockerized/src/compiler/error.rs new file mode 100644 index 0000000..92ab6b0 --- /dev/null +++ b/crates/dockerized/src/compiler/error.rs @@ -0,0 +1,16 @@ +use ere_zkvm_interface::CommonError; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error(transparent)] + CommonError(#[from] CommonError), + #[error( + "Guest directory must be in mounting directory, mounting_directory: {mounting_directory}, guest_directory: {guest_directory}" + )] + GuestNotInMountingDirecty { + mounting_directory: PathBuf, + guest_directory: PathBuf, + }, +} diff --git a/crates/dockerized/src/lib.rs b/crates/dockerized/src/lib.rs new file mode 100644 index 0000000..e695cd0 --- /dev/null +++ b/crates/dockerized/src/lib.rs @@ -0,0 +1,179 @@ +//! # Ere Dockerized +//! +//! A Docker-based wrapper for other zkVM crates `ere-{zkvm}`. +//! +//! This crate provides a unified interface to dockerize the `Compiler` and +//! `zkVM` implementation of other zkVM crates `ere-{zkvm}`, it requires only +//! `docker` to be installed, but no zkVM specific SDK. +//! +//! ## Docker image building +//! +//! It builds 4 Docker images in sequence if they don't exist: +//! 1. `ere-base:{version}` - Base image with common dependencies +//! 2. `ere-base-{zkvm}:{version}` - zkVM-specific base image with the zkVM SDK +//! 3. `ere-compiler-{zkvm}:{version}` - Compiler image with the `ere-compiler` +//! binary built with the selected zkVM feature +//! 4. `ere-server-{zkvm}:{version}` - Server image with the `ere-server` binary +//! built with the selected zkVM feature +//! +//! When [`ProverResourceType::Gpu`] is selected, the image with GPU support +//! will be built and tagged with specific suffix. +//! +//! To force rebuild all images, set the environment variable +//! `ERE_FORCE_REBUILD_DOCKER_IMAGE` to non-empty value. +//! +//! ## Example +//! +//! ```rust,no_run +//! # fn main() -> Result<(), Box> { +//! use ere_dockerized::{CompilerKind, DockerizedCompiler, DockerizedzkVM, zkVMKind}; +//! use ere_zkvm_interface::{ +//! compiler::Compiler, +//! zkvm::{ProofKind, ProverResourceType, zkVM}, +//! }; +//! use std::path::Path; +//! +//! // The zkVM we plan to use +//! let zkvm_kind = zkVMKind::SP1; +//! +//! // The compiler we plan to use +//! let compiler_kind = CompilerKind::RustCustomized; +//! +//! // Compile a guest program +//! let compiler = DockerizedCompiler::new(zkvm_kind, compiler_kind, "mounting/directory")?; +//! let guest_path = Path::new("relative/path/to/guest/program"); +//! let program = compiler.compile(&guest_path)?; +//! +//! // Create zkVM instance +//! let resource = ProverResourceType::Cpu; +//! let zkvm = DockerizedzkVM::new(zkvm_kind, program, resource)?; +//! +//! // Serialize input +//! let input = 42u32.to_le_bytes(); +//! +//! // Execute program +//! let (public_values, execution_report) = zkvm.execute(&input)?; +//! println!("Execution cycles: {}", execution_report.total_num_cycles); +//! +//! // Generate proof +//! let (public_values, proof, proving_report) = zkvm.prove(&input, ProofKind::Compressed)?; +//! println!("Proof generated in: {:?}", proving_report.proving_time); +//! +//! // Verify proof +//! let public_values = zkvm.verify(&proof)?; +//! println!("Proof verified successfully!"); +//! # Ok(()) +//! # } +//! ``` + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; + +mod util; + +pub mod compiler; +pub mod zkvm; + +pub use crate::{ + compiler::{DockerizedCompiler, SerializedProgram}, + zkvm::DockerizedzkVM, +}; +pub use ere_compiler::CompilerKind; + +include!(concat!(env!("OUT_DIR"), "/crate_version.rs")); +include!(concat!(env!("OUT_DIR"), "/zkvm_sdk_version_impl.rs")); + +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum zkVMKind { + Airbender, + Jolt, + Miden, + Nexus, + OpenVM, + Pico, + Risc0, + SP1, + Ziren, + Zisk, +} + +impl zkVMKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::Airbender => "airbender", + Self::Jolt => "jolt", + Self::Miden => "miden", + Self::Nexus => "nexus", + Self::OpenVM => "openvm", + Self::Pico => "pico", + Self::Risc0 => "risc0", + Self::SP1 => "sp1", + Self::Ziren => "ziren", + Self::Zisk => "zisk", + } + } +} + +impl FromStr for zkVMKind { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "airbender" => Self::Airbender, + "jolt" => Self::Jolt, + "miden" => Self::Miden, + "nexus" => Self::Nexus, + "openvm" => Self::OpenVM, + "pico" => Self::Pico, + "risc0" => Self::Risc0, + "sp1" => Self::SP1, + "ziren" => Self::Ziren, + "zisk" => Self::Zisk, + _ => return Err(format!("Unsupported zkvm kind {s}")), + }) + } +} + +impl Display for zkVMKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +/// Tag of images in format of `{version}{suffix}`. +fn image_tag(zkvm_kind: zkVMKind, gpu: bool) -> String { + let suffix = match (zkvm_kind, gpu) { + // Only the following zkVMs requires CUDA setup in the base image + // when GPU support is required. + (zkVMKind::Airbender | zkVMKind::OpenVM | zkVMKind::Risc0 | zkVMKind::Zisk, true) => { + "-cuda" + } + _ => "", + }; + format!("{CRATE_VERSION}{suffix}") +} + +fn base_image(zkvm_kind: zkVMKind, gpu: bool) -> String { + let image_tag = image_tag(zkvm_kind, gpu); + format!("ere-base:{image_tag}") +} + +fn base_zkvm_image(zkvm_kind: zkVMKind, gpu: bool) -> String { + let image_tag = image_tag(zkvm_kind, gpu); + format!("ere-base-{zkvm_kind}:{image_tag}") +} + +fn server_zkvm_image(zkvm_kind: zkVMKind, gpu: bool) -> String { + let image_tag = image_tag(zkvm_kind, gpu); + format!("ere-server-{zkvm_kind}:{image_tag}") +} + +fn compiler_zkvm_image(zkvm_kind: zkVMKind) -> String { + let image_tag = image_tag(zkvm_kind, false); + format!("ere-compiler-{zkvm_kind}:{image_tag}") +} diff --git a/crates/dockerized/src/util.rs b/crates/dockerized/src/util.rs new file mode 100644 index 0000000..655a278 --- /dev/null +++ b/crates/dockerized/src/util.rs @@ -0,0 +1,15 @@ +use std::path::PathBuf; + +pub mod cuda; +pub mod docker; + +pub fn workspace_dir() -> PathBuf { + let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + dir.pop(); + dir.pop(); + dir.canonicalize().unwrap() +} + +pub fn home_dir() -> PathBuf { + PathBuf::from(std::env::var("HOME").expect("env `$HOME` should be set")) +} diff --git a/crates/dockerized/dockerized/src/cuda.rs b/crates/dockerized/src/util/cuda.rs similarity index 100% rename from crates/dockerized/dockerized/src/cuda.rs rename to crates/dockerized/src/util/cuda.rs diff --git a/crates/dockerized/dockerized/src/docker.rs b/crates/dockerized/src/util/docker.rs similarity index 75% rename from crates/dockerized/dockerized/src/docker.rs rename to crates/dockerized/src/util/docker.rs index 62075b2..96d9971 100644 --- a/crates/dockerized/dockerized/src/docker.rs +++ b/crates/dockerized/src/util/docker.rs @@ -1,16 +1,17 @@ -use crate::error::Error; +use ere_zkvm_interface::CommonError; use std::{ env, fmt::{self, Display, Formatter}, - io::{self, Write}, + io::Write, path::Path, process::{Child, Command, Stdio}, }; +use tracing::debug; pub const DOCKER_SOCKET: &str = "/var/run/docker.sock"; #[derive(Clone)] -pub struct CmdOption(String, Option); +struct CmdOption(String, Option); impl CmdOption { pub fn new(key: impl AsRef, value: impl AsRef) -> Self { @@ -78,7 +79,7 @@ impl DockerBuildCmd { } } - pub fn exec(self, context: impl AsRef) -> Result<(), io::Error> { + pub fn exec(self, context: impl AsRef) -> Result<(), CommonError> { let mut cmd = Command::new("docker"); cmd.arg("build"); for option in self.options { @@ -86,12 +87,14 @@ impl DockerBuildCmd { } cmd.arg(context.as_ref().to_string_lossy().to_string()); - let status = cmd.status()?; + debug!("Docker build with command: {cmd:?}"); + + let status = cmd + .status() + .map_err(|err| CommonError::command(&cmd, err))?; if !status.success() { - return Err(io::Error::other(format!( - "Command {cmd:?} failed with status: {status}", - ))); + Err(CommonError::command_exit_non_zero(&cmd, status, None))? } Ok(()) @@ -177,7 +180,7 @@ impl DockerRunCmd { mut self, commands: impl IntoIterator>, stdin: &[u8], - ) -> Result { + ) -> Result { self = self.flag("interactive"); let mut cmd = Command::new("docker"); @@ -190,15 +193,25 @@ impl DockerRunCmd { cmd.arg(command.as_ref()); } - let mut child = cmd.stdin(Stdio::piped()).spawn()?; + debug!("Docker run with command: {cmd:?}"); + + let mut child = cmd + .stdin(Stdio::piped()) + .spawn() + .map_err(|err| CommonError::command(&cmd, err))?; // Write all to stdin then drop to close the pipe. - child.stdin.take().unwrap().write_all(stdin)?; + child + .stdin + .take() + .unwrap() + .write_all(stdin) + .map_err(|err| CommonError::command(&cmd, err))?; Ok(child) } - pub fn exec(self, commands: impl IntoIterator>) -> Result<(), io::Error> { + pub fn exec(self, commands: impl IntoIterator>) -> Result<(), CommonError> { let mut cmd = Command::new("docker"); cmd.arg("run"); for option in self.options { @@ -209,43 +222,61 @@ impl DockerRunCmd { cmd.arg(command.as_ref()); } - let status = cmd.status()?; + debug!("Docker run with command: {cmd:?}"); + + let status = cmd + .status() + .map_err(|err| CommonError::command(&cmd, err))?; if !status.success() { - return Err(io::Error::other(format!( - "Command {cmd:?} failed with status: {status}", - ))); + Err(CommonError::command_exit_non_zero(&cmd, status, None))? } Ok(()) } } -pub fn stop_docker_container(container_name: impl AsRef) -> Result<(), Error> { - let output = Command::new("docker") +pub fn stop_docker_container(container_name: impl AsRef) -> Result<(), CommonError> { + let mut cmd = Command::new("docker"); + let output = cmd .args(["container", "stop", container_name.as_ref()]) .output() - .map_err(Error::DockerContainerCmd)?; + .map_err(|err| CommonError::command(&cmd, err))?; - if String::from_utf8_lossy(&output.stdout).starts_with("Error") { - return Err(Error::DockerContainerCmd(io::Error::other(format!( - "Failed to stop container {}", - container_name.as_ref() - )))); + if !output.status.success() { + Err(CommonError::command_exit_non_zero( + &cmd, + output.status, + Some(&output), + ))? } Ok(()) } -pub fn docker_image_exists(image: impl AsRef) -> Result { - let output = Command::new("docker") +pub fn docker_image_exists(image: impl AsRef) -> Result { + let mut cmd = Command::new("docker"); + let output = cmd .args(["images", "--quiet", image.as_ref()]) .output() - .map_err(Error::DockerImageCmd)?; + .map_err(|err| CommonError::command(&cmd, err))?; + + if !output.status.success() { + Err(CommonError::command_exit_non_zero( + &cmd, + output.status, + Some(&output), + ))? + } + // If image exists, image id will be printed hence stdout will be non-empty. Ok(!output.stdout.is_empty()) } +pub fn force_rebuild() -> bool { + env::var_os("ERE_FORCE_REBUILD_DOCKER_IMAGE").is_some() +} + fn to_string(s: impl AsRef) -> String { s.as_ref().to_string() } diff --git a/crates/dockerized/src/zkvm.rs b/crates/dockerized/src/zkvm.rs new file mode 100644 index 0000000..87cb81f --- /dev/null +++ b/crates/dockerized/src/zkvm.rs @@ -0,0 +1,467 @@ +use crate::{ + base_image, base_zkvm_image, + compiler::SerializedProgram, + server_zkvm_image, + util::{ + cuda::cuda_arch, + docker::{ + DockerBuildCmd, DockerRunCmd, docker_image_exists, force_rebuild, stop_docker_container, + }, + home_dir, workspace_dir, + }, + zkVMKind, +}; +use ere_server::client::{Url, zkVMClient}; +use ere_zkvm_interface::{ + CommonError, + zkvm::{ + ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType, + PublicValues, zkVM, + }, +}; +use std::iter; +use tempfile::TempDir; +use tracing::{error, info}; + +mod error; + +pub use error::Error; + +/// This method builds 3 Docker images in sequence: +/// 1. `ere-base:{version}` - Base image with common dependencies +/// 2. `ere-base-{zkvm}:{version}` - zkVM-specific base image with the zkVM SDK +/// 3. `ere-server-{zkvm}:{version}` - Server image with the `ere-server` binary +/// built with the selected zkVM feature +/// +/// When [`ProverResourceType::Gpu`] is selected, the image with GPU support +/// will be built and tagged with specific suffix. +/// +/// Images are cached and only rebuilt if they don't exist or if the +/// `ERE_FORCE_REBUILD_DOCKER_IMAGE` environment variable is set. +fn build_server_image(zkvm_kind: zkVMKind, gpu: bool) -> Result<(), Error> { + let workspace_dir = workspace_dir(); + let docker_dir = workspace_dir.join("docker"); + let docker_zkvm_dir = docker_dir.join(zkvm_kind.as_str()); + + let force_rebuild = force_rebuild(); + let base_image = base_image(zkvm_kind, gpu); + let base_zkvm_image = base_zkvm_image(zkvm_kind, gpu); + let server_zkvm_image = server_zkvm_image(zkvm_kind, gpu); + + // Build `ere-base` + if force_rebuild || !docker_image_exists(&base_image)? { + info!("Building image {base_image}..."); + + let mut cmd = DockerBuildCmd::new() + .file(docker_dir.join("Dockerfile.base")) + .tag(&base_image); + + if gpu { + cmd = cmd.build_arg("CUDA", "1"); + } + + cmd.exec(&workspace_dir)?; + } + + // Build `ere-base-{zkvm_kind}` + if force_rebuild || !docker_image_exists(&base_zkvm_image)? { + info!("Building image {base_zkvm_image}..."); + + let mut cmd = DockerBuildCmd::new() + .file(docker_zkvm_dir.join("Dockerfile.base")) + .tag(&base_zkvm_image) + .build_arg("BASE_IMAGE", &base_image) + .build_arg_from_env("RUSTFLAGS"); + + if gpu { + cmd = cmd.build_arg("CUDA", "1"); + + match zkvm_kind { + zkVMKind::Airbender | zkVMKind::OpenVM | zkVMKind::Risc0 | zkVMKind::Zisk => { + if let Some(cuda_arch) = cuda_arch() { + cmd = cmd.build_arg("CUDA_ARCH", cuda_arch) + } + } + _ => {} + } + } + + cmd.exec(&workspace_dir)?; + } + + // Build `ere-server-{zkvm_kind}` + if force_rebuild || !docker_image_exists(&server_zkvm_image)? { + info!("Building image {server_zkvm_image}..."); + + let mut cmd = DockerBuildCmd::new() + .file(docker_zkvm_dir.join("Dockerfile.server")) + .tag(&server_zkvm_image) + .build_arg("BASE_ZKVM_IMAGE", &base_zkvm_image) + .build_arg_from_env("RUSTFLAGS"); + + if gpu { + cmd = cmd.build_arg("CUDA", "1"); + } + + cmd.exec(&workspace_dir)?; + } + + Ok(()) +} + +struct ServerContainer { + name: String, + port: u16, + #[allow(dead_code)] + tempdir: TempDir, +} + +impl Drop for ServerContainer { + fn drop(&mut self) { + if let Err(err) = stop_docker_container(&self.name) { + error!("Failed to stop docker container: {err}"); + } + } +} + +impl ServerContainer { + /// Offset of port used for `ere-server`. + const PORT_OFFSET: u16 = 4174; + + fn new( + zkvm_kind: zkVMKind, + program: &SerializedProgram, + resource: &ProverResourceType, + ) -> Result { + let port = Self::PORT_OFFSET + zkvm_kind as u16; + + let name = format!("ere-server-{zkvm_kind}-{port}"); + let gpu = matches!(resource, ProverResourceType::Gpu); + let mut cmd = DockerRunCmd::new(server_zkvm_image(zkvm_kind, gpu)) + .rm() + .inherit_env("RUST_LOG") + .inherit_env("NO_COLOR") + .publish(port.to_string(), port.to_string()) + .name(&name); + + // zkVM specific options + cmd = match zkvm_kind { + zkVMKind::Risc0 => cmd + .inherit_env("RISC0_SEGMENT_PO2") + .inherit_env("RISC0_KECCAK_PO2"), + // ZisK uses shared memory to exchange data between processes, it + // requires at least 8G shared memory, here we set 16G for safety. + zkVMKind::Zisk => cmd + .option("shm-size", "16G") + .option("ulimit", "memlock=-1:-1") + .inherit_env("ZISK_PORT") + .inherit_env("ZISK_CHUNK_SIZE_BITS") + .inherit_env("ZISK_UNLOCK_MAPPED_MEMORY") + .inherit_env("ZISK_MINIMAL_MEMORY") + .inherit_env("ZISK_PREALLOCATE") + .inherit_env("ZISK_SHARED_TABLES") + .inherit_env("ZISK_MAX_STREAMS") + .inherit_env("ZISK_NUMBER_THREADS_WITNESS") + .inherit_env("ZISK_MAX_WITNESS_STORED"), + _ => cmd, + }; + + // zkVM specific options when using GPU + if gpu { + cmd = match zkvm_kind { + zkVMKind::Airbender => cmd.gpus("all"), + zkVMKind::OpenVM => cmd.gpus("all"), + // SP1 runs docker command to spin up the server to do GPU + // proving, to give the client access to the prover service, we + // need to use the host networking driver. + zkVMKind::SP1 => cmd.mount_docker_socket().network("host"), + zkVMKind::Risc0 => cmd.gpus("all").inherit_env("RISC0_DEFAULT_PROVER_NUM_GPUS"), + zkVMKind::Zisk => cmd.gpus("all"), + _ => cmd, + } + } + + let tempdir = TempDir::new().map_err(CommonError::tempdir)?; + + // zkVM specific options needed for proving Groth16 proof. + cmd = match zkvm_kind { + // Risc0 and SP1 runs docker command to prove Groth16 proof, and + // they pass the input by mounting temporary directory. Here we + // create a temporary directory and mount it on the top level, so + // the volume could be shared, and override `TMPDIR` so we don't + // need to mount the whole `/tmp`. + zkVMKind::Risc0 => cmd + .mount_docker_socket() + .env("TMPDIR", tempdir.path().to_string_lossy()) + .volume(tempdir.path(), tempdir.path()), + zkVMKind::SP1 => { + let groth16_circuit_path = home_dir().join(".sp1").join("circuits").join("groth16"); + cmd.mount_docker_socket() + .env( + "SP1_GROTH16_CIRCUIT_PATH", + groth16_circuit_path.to_string_lossy(), + ) + .env("TMPDIR", tempdir.path().to_string_lossy()) + .volume(tempdir.path(), tempdir.path()) + .volume(&groth16_circuit_path, &groth16_circuit_path) + } + _ => cmd, + }; + + cmd.spawn( + iter::empty() + .chain(["--port", &port.to_string()]) + .chain(resource.to_args()), + &program.0, + )?; + + Ok(ServerContainer { + name, + port, + tempdir, + }) + } + + fn endpoint(&self) -> Url { + Url::parse(&format!("http://127.0.0.1:{}", self.port)).unwrap() + } +} + +pub struct DockerizedzkVM { + zkvm_kind: zkVMKind, + program: SerializedProgram, + resource: ProverResourceType, + #[allow(dead_code)] + server_container: ServerContainer, + client: zkVMClient, +} + +impl DockerizedzkVM { + pub fn new( + zkvm_kind: zkVMKind, + program: SerializedProgram, + resource: ProverResourceType, + ) -> Result { + build_server_image(zkvm_kind, matches!(resource, ProverResourceType::Gpu))?; + + let server_container = ServerContainer::new(zkvm_kind, &program, &resource)?; + let client = block_on(zkVMClient::new(server_container.endpoint()))?; + + Ok(Self { + zkvm_kind, + program, + resource, + server_container, + client, + }) + } + + pub fn zkvm_kind(&self) -> zkVMKind { + self.zkvm_kind + } + + pub fn program(&self) -> &SerializedProgram { + &self.program + } + + pub fn resource(&self) -> &ProverResourceType { + &self.resource + } +} + +impl zkVM for DockerizedzkVM { + fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> { + let (public_values, report) = + block_on(self.client.execute(input.to_vec())).map_err(Error::from)?; + + Ok((public_values, report)) + } + + fn prove( + &self, + input: &[u8], + proof_kind: ProofKind, + ) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> { + let (public_values, proof, report) = + block_on(self.client.prove(input.to_vec(), proof_kind)).map_err(Error::from)?; + + Ok((public_values, proof, report)) + } + + fn verify(&self, proof: &Proof) -> anyhow::Result { + let public_values = block_on(self.client.verify(proof)).map_err(Error::from)?; + + Ok(public_values) + } + + fn name(&self) -> &'static str { + self.zkvm_kind.as_str() + } + + fn sdk_version(&self) -> &'static str { + self.zkvm_kind.sdk_version() + } +} + +fn block_on(future: impl Future) -> T { + match tokio::runtime::Handle::try_current() { + Ok(handle) => tokio::task::block_in_place(|| handle.block_on(future)), + Err(_) => tokio::runtime::Runtime::new().unwrap().block_on(future), + } +} + +#[cfg(test)] +mod test { + use crate::{ + CompilerKind, + compiler::test::compile, + zkVMKind, + zkvm::{DockerizedzkVM, Error}, + }; + use ere_test_utils::{host::*, program::basic::BasicProgramInput}; + use ere_zkvm_interface::zkvm::{ProofKind, ProverResourceType, zkVM}; + + fn zkvm( + zkvm_kind: zkVMKind, + compiler_kind: CompilerKind, + program: &'static str, + ) -> DockerizedzkVM { + let program = compile(zkvm_kind, compiler_kind, program).clone(); + DockerizedzkVM::new(zkvm_kind, program, ProverResourceType::Cpu).unwrap() + } + + macro_rules! test { + ($zkvm_kind:ident, $compiler_kind:ident, $program:literal, $valid_test_cases:expr, $invalid_test_cases:expr) => { + #[test] + fn test_execute() { + let zkvm = zkvm(zkVMKind::$zkvm_kind, CompilerKind::$compiler_kind, $program); + + // Valid test cases + for test_case in $valid_test_cases { + run_zkvm_execute(&zkvm, &test_case); + } + + // Invalid test cases + for input in $invalid_test_cases { + let err = zkvm.execute(&input).unwrap_err(); + assert!(matches!(err.downcast::().unwrap(), Error::zkVM(_))); + } + } + + #[test] + fn test_prove() { + let zkvm = zkvm(zkVMKind::$zkvm_kind, CompilerKind::$compiler_kind, $program); + + // Valid test cases + for test_case in $valid_test_cases { + run_zkvm_prove(&zkvm, &test_case); + } + + // Invalid test cases + for input in $invalid_test_cases { + let err = zkvm.prove(&input, ProofKind::default()).unwrap_err(); + assert!(matches!(err.downcast::().unwrap(), Error::zkVM(_))); + } + } + }; + } + + mod airbender { + use super::*; + test!( + Airbender, + Rust, + "basic", + [BasicProgramInput::valid().into_output_sha256()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod jolt { + use super::*; + test!( + Jolt, + RustCustomized, + "basic", + [BasicProgramInput::valid()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod nexus { + use super::*; + test!( + Nexus, + Rust, + "basic", + [BasicProgramInput::valid()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod openvm { + use super::*; + test!( + OpenVM, + RustCustomized, + "basic", + [BasicProgramInput::valid().into_output_sha256()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod pico { + use super::*; + test!( + Pico, + RustCustomized, + "basic", + [BasicProgramInput::valid()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod risc0 { + use super::*; + test!( + Risc0, + RustCustomized, + "basic", + [BasicProgramInput::valid()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod sp1 { + use super::*; + test!( + SP1, + RustCustomized, + "basic", + [BasicProgramInput::valid()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod ziren { + use super::*; + test!( + Ziren, + RustCustomized, + "basic", + [BasicProgramInput::valid()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } + + mod zisk { + use super::*; + test!( + Zisk, + RustCustomized, + "basic_rust", + [BasicProgramInput::valid().into_output_sha256()], + [Vec::new(), BasicProgramInput::invalid().serialized_input()] + ); + } +} diff --git a/crates/dockerized/src/zkvm/error.rs b/crates/dockerized/src/zkvm/error.rs new file mode 100644 index 0000000..58aa117 --- /dev/null +++ b/crates/dockerized/src/zkvm/error.rs @@ -0,0 +1,26 @@ +use ere_server::client::{self, TwirpErrorResponse}; +use ere_zkvm_interface::CommonError; +use thiserror::Error; + +impl From for Error { + fn from(value: client::Error) -> Self { + match value { + client::Error::zkVM(err) => Self::zkVM(err), + client::Error::ConnectionTimeout => Self::ConnectionTimeout, + client::Error::Rpc(err) => Self::Rpc(err), + } + } +} + +#[derive(Debug, Error)] +#[allow(non_camel_case_types)] +pub enum Error { + #[error(transparent)] + CommonError(#[from] CommonError), + #[error("zkVM method error: {0}")] + zkVM(String), + #[error("Connection to zkVM server timeout after 5 minutes")] + ConnectionTimeout, + #[error("RPC to zkVM server error: {0}")] + Rpc(TwirpErrorResponse), +} diff --git a/crates/zkvm-interface/src/zkvm/error.rs b/crates/zkvm-interface/src/zkvm/error.rs index f4c6d4b..a2ed7f1 100644 --- a/crates/zkvm-interface/src/zkvm/error.rs +++ b/crates/zkvm-interface/src/zkvm/error.rs @@ -80,12 +80,12 @@ impl CommonError { pub fn read_file(id: impl AsRef, path: impl AsRef, err: io::Error) -> Self { let (id, path) = (id.as_ref(), path.as_ref().display()); - Self::io(format!("Failed to write {id} to {path}"), err) + Self::io(format!("Failed to read {id} from {path}"), err) } pub fn write_file(id: impl AsRef, path: impl AsRef, err: io::Error) -> Self { let (id, path) = (id.as_ref(), path.as_ref().display()); - Self::io(format!("Failed to read {id} from {path}"), err) + Self::io(format!("Failed to write {id} to {path}"), err) } pub fn serialize( diff --git a/crates/zkvm/openvm/Cargo.toml b/crates/zkvm/openvm/Cargo.toml index c2ae62a..cb2b06d 100644 --- a/crates/zkvm/openvm/Cargo.toml +++ b/crates/zkvm/openvm/Cargo.toml @@ -14,11 +14,11 @@ toml.workspace = true # OpenVM dependencies openvm-build.workspace = true -openvm-circuit.workspace = true -openvm-continuations.workspace = true -openvm-sdk = { workspace = true, features = ["nightly-features"] } -openvm-stark-sdk.workspace = true -openvm-transpiler.workspace = true +openvm-circuit = { workspace = true, optional = true } +openvm-continuations = { workspace = true, optional = true } +openvm-sdk = { workspace = true, features = ["nightly-features"], optional = true } +openvm-stark-sdk = { workspace = true, optional = true } +openvm-transpiler = { workspace = true, optional = true } # Local dependencies ere-compile-utils = { workspace = true, optional = true } @@ -33,7 +33,13 @@ ere-build-utils.workspace = true [features] default = ["compiler", "zkvm"] compiler = ["dep:ere-compile-utils"] -zkvm = [] +zkvm = [ + "dep:openvm-circuit", + "dep:openvm-continuations", + "dep:openvm-sdk", + "dep:openvm-stark-sdk", + "dep:openvm-transpiler", +] cuda = ["openvm-sdk/cuda"] [lints] diff --git a/crates/zkvm/openvm/src/compiler.rs b/crates/zkvm/openvm/src/compiler.rs index b24522f..51864ba 100644 --- a/crates/zkvm/openvm/src/compiler.rs +++ b/crates/zkvm/openvm/src/compiler.rs @@ -1,6 +1,4 @@ use ere_compile_utils::CommonError; -use openvm_sdk::config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig}; -use openvm_stark_sdk::config::FriParameters; use std::{fs, path::Path}; mod error; @@ -11,30 +9,14 @@ pub use error::Error; pub use rust_rv32ima::RustRv32ima; pub use rust_rv32ima_customized::RustRv32imaCustomized; -fn read_app_config(app_config_path: impl AsRef) -> Result, Error> { - Ok(if app_config_path.as_ref().exists() { - let toml = fs::read_to_string(app_config_path.as_ref()) - .map_err(|err| CommonError::read_file("app_config", &app_config_path, err))?; - toml::from_str(&toml).map_err(|err| CommonError::deserialize("app_config", "toml", err))? - } else { - // The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/v1.4.1/crates/cli/src/default.rs#L35. - AppConfig { - app_fri_params: FriParameters::standard_with_100_bits_conjectured_security( - DEFAULT_APP_LOG_BLOWUP, - ) - .into(), - // By default it supports RISCV32IM with IO but no precompiles. - app_vm_config: SdkVmConfig::builder() - .system(Default::default()) - .rv32i(Default::default()) - .rv32m(Default::default()) - .io(Default::default()) - .build(), - leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security( - DEFAULT_LEAF_LOG_BLOWUP, - ) - .into(), - compiler_options: Default::default(), - } - }) +fn read_app_config(app_config_path: impl AsRef) -> Result, Error> { + if !app_config_path.as_ref().exists() { + return Ok(None); + } + + let value = fs::read_to_string(app_config_path.as_ref()) + .map_err(|err| CommonError::read_file("app_config", &app_config_path, err))?; + toml::from_str::(&value) + .map_err(|err| CommonError::deserialize("app_config", "toml", err))?; + Ok(Some(value)) } diff --git a/crates/zkvm/openvm/src/program.rs b/crates/zkvm/openvm/src/program.rs index b86827f..77d0c41 100644 --- a/crates/zkvm/openvm/src/program.rs +++ b/crates/zkvm/openvm/src/program.rs @@ -1,11 +1,10 @@ -use openvm_sdk::config::{AppConfig, SdkVmConfig}; use serde::{Deserialize, Serialize}; /// OpenVM program that contains ELF of compiled guest and app config. #[derive(Clone, Serialize, Deserialize)] pub struct OpenVMProgram { pub(crate) elf: Vec, - pub(crate) app_config: AppConfig, + pub(crate) app_config: Option, } impl OpenVMProgram { @@ -13,7 +12,7 @@ impl OpenVMProgram { &self.elf } - pub fn app_config(&self) -> &AppConfig { - &self.app_config + pub fn app_config(&self) -> Option<&str> { + self.app_config.as_deref() } } diff --git a/crates/zkvm/openvm/src/zkvm.rs b/crates/zkvm/openvm/src/zkvm.rs index f694950..48f7f5e 100644 --- a/crates/zkvm/openvm/src/zkvm.rs +++ b/crates/zkvm/openvm/src/zkvm.rs @@ -10,11 +10,11 @@ use openvm_sdk::{ CpuSdk, F, SC, StdIn, codec::{Decode, Encode}, commit::AppExecutionCommit, - config::{AppConfig, SdkVmConfig}, + config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig}, fs::read_object_from_file, keygen::{AggProvingKey, AggVerifyingKey, AppProvingKey}, }; -use openvm_stark_sdk::openvm_stark_backend::p3_field::PrimeField32; +use openvm_stark_sdk::{config::FriParameters, openvm_stark_backend::p3_field::PrimeField32}; use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; use std::{env, path::PathBuf, sync::Arc, time::Instant}; @@ -49,7 +49,31 @@ impl EreOpenVM { _ => {} } - let sdk = CpuSdk::new(program.app_config().clone()).map_err(Error::SdkInit)?; + let app_config = if let Some(value) = program.app_config() { + toml::from_str(value).map_err(Error::InvalidAppConfig)? + } else { + // The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/v1.4.1/crates/cli/src/default.rs#L35. + AppConfig { + app_fri_params: FriParameters::standard_with_100_bits_conjectured_security( + DEFAULT_APP_LOG_BLOWUP, + ) + .into(), + // By default it supports RISCV32IM with IO but no precompiles. + app_vm_config: SdkVmConfig::builder() + .system(Default::default()) + .rv32i(Default::default()) + .rv32m(Default::default()) + .io(Default::default()) + .build(), + leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security( + DEFAULT_LEAF_LOG_BLOWUP, + ) + .into(), + compiler_options: Default::default(), + } + }; + + let sdk = CpuSdk::new(app_config.clone()).map_err(Error::SdkInit)?; let elf = Elf::decode(program.elf(), MEM_SIZE as u32).map_err(Error::ElfDecode)?; @@ -69,7 +93,7 @@ impl EreOpenVM { .app_commit(); Ok(Self { - app_config: program.app_config, + app_config, app_exe, app_pk, agg_pk, diff --git a/crates/zkvm/openvm/src/zkvm/error.rs b/crates/zkvm/openvm/src/zkvm/error.rs index 1bcdd0b..3f758e0 100644 --- a/crates/zkvm/openvm/src/zkvm/error.rs +++ b/crates/zkvm/openvm/src/zkvm/error.rs @@ -8,6 +8,9 @@ pub enum Error { CommonError(#[from] CommonError), // Common + #[error("Invalid AppConfig: {0}")] + InvalidAppConfig(toml::de::Error), + #[error("Initialize SDK failed: {0}")] SdkInit(SdkError), diff --git a/docker/base/Dockerfile.base b/docker/Dockerfile.base similarity index 88% rename from docker/base/Dockerfile.base rename to docker/Dockerfile.base index a442b86..e989921 100644 --- a/docker/base/Dockerfile.base +++ b/docker/Dockerfile.base @@ -44,13 +44,6 @@ ENV RUSTUP_HOME=/usr/local/rustup \ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain ${RUST_VERSION} --no-modify-path -# Copy the TamaGo installer script from the workspace context -COPY --chmod=755 scripts/install_tamago.sh /tmp/install_tamago.sh - -# Run the TamaGo installation script. -RUN /tmp/install_tamago.sh && \ - rm /tmp/install_tamago.sh - # Add a non-root user for subsequent stages or use in derived images # This is generally best practice. ARG USERNAME=ere_user diff --git a/docker/airbender/Dockerfile.base b/docker/airbender/Dockerfile.base index 6cfc08c..6742073 100644 --- a/docker/airbender/Dockerfile.base +++ b/docker/airbender/Dockerfile.base @@ -1,16 +1,11 @@ ARG BASE_IMAGE=ere-base:latest -ARG BASE_CUDA_IMAGE=ere-base:latest-cuda -# Whether to enable CUDA feature or not. -ARG CUDA - -FROM $BASE_IMAGE AS base -FROM $BASE_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} +FROM $BASE_IMAGE # Set default toolchain to nightly RUN rustup default nightly +# Whether to enable CUDA feature or not. ARG CUDA # Default to build for RTX 50 series diff --git a/docker/airbender/Dockerfile.compiler b/docker/airbender/Dockerfile.compiler index 506e8d9..23c2114 100644 --- a/docker/airbender/Dockerfile.compiler +++ b/docker/airbender/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features airbender \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/airbender/Dockerfile.server b/docker/airbender/Dockerfile.server index 10ba50b..60ed9ba 100644 --- a/docker/airbender/Dockerfile.server +++ b/docker/airbender/Dockerfile.server @@ -1,14 +1,11 @@ ARG BASE_ZKVM_IMAGE=ere-base-airbender:latest -ARG BASE_ZKVM_CUDA_IMAGE=ere-base-airbender:latest-cuda ARG RUNTIME_IMAGE=ubuntu:24.04 ARG RUNTIME_CUDA_IMAGE=nvidia/cuda:12.9.1-runtime-ubuntu24.04 # Whether to enable CUDA feature or not. ARG CUDA -FROM $BASE_ZKVM_IMAGE AS base -FROM $BASE_ZKVM_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} AS build_stage +FROM $BASE_ZKVM_IMAGE AS build_stage COPY . /ere @@ -19,7 +16,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features airbender${CUDA:+,cuda} \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime FROM $RUNTIME_CUDA_IMAGE AS runtime_cuda diff --git a/docker/jolt/Dockerfile.base b/docker/jolt/Dockerfile.base index 5ebf33a..2c29793 100644 --- a/docker/jolt/Dockerfile.base +++ b/docker/jolt/Dockerfile.base @@ -5,9 +5,6 @@ FROM $BASE_IMAGE # The ere-base image provides Rust, Cargo (with a default nightly), and common tools. # We operate as root for SDK installation. -# Set default toolchain to 1.88.0 -RUN rustup default 1.88.0 - # Copy the Jolt SDK (CLI) installer script from the workspace context COPY --chmod=755 scripts/sdk_installers/install_jolt_sdk.sh /tmp/install_jolt_sdk.sh diff --git a/docker/jolt/Dockerfile.compiler b/docker/jolt/Dockerfile.compiler index 62573ff..eca6dd7 100644 --- a/docker/jolt/Dockerfile.compiler +++ b/docker/jolt/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features jolt \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/jolt/Dockerfile.server b/docker/jolt/Dockerfile.server index 34c17ff..579608a 100644 --- a/docker/jolt/Dockerfile.server +++ b/docker/jolt/Dockerfile.server @@ -11,7 +11,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features jolt \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/miden/Dockerfile.compiler b/docker/miden/Dockerfile.compiler index ec4a3bd..462d534 100644 --- a/docker/miden/Dockerfile.compiler +++ b/docker/miden/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features miden \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/miden/Dockerfile.server b/docker/miden/Dockerfile.server index e20b2e7..4f0a99c 100644 --- a/docker/miden/Dockerfile.server +++ b/docker/miden/Dockerfile.server @@ -11,7 +11,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features miden \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/nexus/Dockerfile.compiler b/docker/nexus/Dockerfile.compiler index f67ebe7..5306b12 100644 --- a/docker/nexus/Dockerfile.compiler +++ b/docker/nexus/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features nexus \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/nexus/Dockerfile.server b/docker/nexus/Dockerfile.server index 8f115a2..99794dc 100644 --- a/docker/nexus/Dockerfile.server +++ b/docker/nexus/Dockerfile.server @@ -11,7 +11,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features nexus \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/openvm/Dockerfile.base b/docker/openvm/Dockerfile.base index 994d0a2..b9025de 100644 --- a/docker/openvm/Dockerfile.base +++ b/docker/openvm/Dockerfile.base @@ -1,12 +1,6 @@ ARG BASE_IMAGE=ere-base:latest -ARG BASE_CUDA_IMAGE=ere-base:latest-cuda -# Whether to enable CUDA feature or not. -ARG CUDA - -FROM $BASE_IMAGE AS base -FROM $BASE_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} +FROM $BASE_IMAGE # The ere-base image provides Rust, Cargo, and common tools. # We operate as root for SDK installation. @@ -14,6 +8,7 @@ FROM base${CUDA:+_cuda} # Set default toolchain to nightly RUN rustup default nightly +# Whether to enable CUDA feature or not. ARG CUDA # Default to build for RTX 50 series diff --git a/docker/openvm/Dockerfile.compiler b/docker/openvm/Dockerfile.compiler index 4caef4d..8e4da1b 100644 --- a/docker/openvm/Dockerfile.compiler +++ b/docker/openvm/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features openvm \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/openvm/Dockerfile.server b/docker/openvm/Dockerfile.server index cb02502..48e61f4 100644 --- a/docker/openvm/Dockerfile.server +++ b/docker/openvm/Dockerfile.server @@ -1,14 +1,11 @@ ARG BASE_ZKVM_IMAGE=ere-base-openvm:latest -ARG BASE_ZKVM_CUDA_IMAGE=ere-base-openvm:latest-cuda ARG RUNTIME_IMAGE=ubuntu:24.04 ARG RUNTIME_CUDA_IMAGE=nvidia/cuda:12.9.1-runtime-ubuntu24.04 # Whether to enable CUDA feature or not. ARG CUDA -FROM $BASE_ZKVM_IMAGE AS base -FROM $BASE_ZKVM_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} AS build_stage +FROM $BASE_ZKVM_IMAGE AS build_stage COPY . /ere @@ -19,7 +16,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features openvm${CUDA:+,cuda} \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime FROM $RUNTIME_CUDA_IMAGE AS runtime_cuda diff --git a/docker/pico/Dockerfile.compiler b/docker/pico/Dockerfile.compiler index 8653d6c..423cb98 100644 --- a/docker/pico/Dockerfile.compiler +++ b/docker/pico/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features pico \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/pico/Dockerfile.server b/docker/pico/Dockerfile.server index ea595d4..4671021 100644 --- a/docker/pico/Dockerfile.server +++ b/docker/pico/Dockerfile.server @@ -11,7 +11,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features pico \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/risc0/Dockerfile.base b/docker/risc0/Dockerfile.base index f08f146..1a068ef 100644 --- a/docker/risc0/Dockerfile.base +++ b/docker/risc0/Dockerfile.base @@ -1,14 +1,10 @@ ARG BASE_IMAGE=ere-base:latest -ARG BASE_CUDA_IMAGE=ere-base:latest-cuda + +FROM $BASE_IMAGE # Whether to enable CUDA feature or not. ARG CUDA -FROM $BASE_IMAGE AS base -FROM $BASE_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} - -ARG CUDA ARG RUSTFLAGS # Install protoc with same version as https://github.com/risc0/risc0/blob/v3.0.3/bento/dockerfiles/agent.dockerfile#L24-L26. diff --git a/docker/risc0/Dockerfile.compiler b/docker/risc0/Dockerfile.compiler index eb8a031..7776354 100644 --- a/docker/risc0/Dockerfile.compiler +++ b/docker/risc0/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features risc0 \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/risc0/Dockerfile.server b/docker/risc0/Dockerfile.server index 7229159..16175ca 100644 --- a/docker/risc0/Dockerfile.server +++ b/docker/risc0/Dockerfile.server @@ -1,14 +1,11 @@ ARG BASE_ZKVM_IMAGE=ere-base-risc0:latest -ARG BASE_ZKVM_CUDA_IMAGE=ere-base-risc0:latest-cuda ARG RUNTIME_IMAGE=ubuntu:24.04 ARG RUNTIME_CUDA_IMAGE=nvidia/cuda:12.9.1-runtime-ubuntu24.04 # Whether to enable CUDA feature or not. ARG CUDA -FROM $BASE_ZKVM_IMAGE AS base -FROM $BASE_ZKVM_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} AS build_stage +FROM $BASE_ZKVM_IMAGE AS build_stage COPY . /ere @@ -19,7 +16,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features risc0${CUDA:+,cuda} \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime FROM $RUNTIME_CUDA_IMAGE AS runtime_cuda diff --git a/docker/sp1/Dockerfile.compiler b/docker/sp1/Dockerfile.compiler index b99de8f..b1f0505 100644 --- a/docker/sp1/Dockerfile.compiler +++ b/docker/sp1/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features sp1 \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/sp1/Dockerfile.server b/docker/sp1/Dockerfile.server index d894d21..9ddd755 100644 --- a/docker/sp1/Dockerfile.server +++ b/docker/sp1/Dockerfile.server @@ -11,7 +11,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features sp1 \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/ziren/Dockerfile.compiler b/docker/ziren/Dockerfile.compiler index 45b3425..911f5a4 100644 --- a/docker/ziren/Dockerfile.compiler +++ b/docker/ziren/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features ziren \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/ziren/Dockerfile.server b/docker/ziren/Dockerfile.server index 4f70c30..649a617 100644 --- a/docker/ziren/Dockerfile.server +++ b/docker/ziren/Dockerfile.server @@ -11,7 +11,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features ziren \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage diff --git a/docker/zisk/Dockerfile.base b/docker/zisk/Dockerfile.base index 8f108b5..6ad5bc1 100644 --- a/docker/zisk/Dockerfile.base +++ b/docker/zisk/Dockerfile.base @@ -1,17 +1,12 @@ ARG BASE_IMAGE=ere-base:latest -ARG BASE_CUDA_IMAGE=ere-base:latest-cuda -# Whether to enable CUDA feature or not. -ARG CUDA - -FROM $BASE_IMAGE AS base -FROM $BASE_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} +FROM $BASE_IMAGE # The ere-base image provides Rust, Cargo, and common tools. # ZisK requires Ubuntu 22.04 or higher (ere-base uses 24.04 by default). # We operate as root for SDK and dependency installation. +# Whether to enable CUDA feature or not. ARG CUDA # Install ZisK system dependencies (for Ubuntu) @@ -68,4 +63,11 @@ ENV PATH=/root/.zisk/bin:$PATH # Verify cargo-zisk is accessible RUN echo "Verifying ZisK installation in Dockerfile ..." && cargo-zisk --version +# Copy the TamaGo installer script from the workspace context +COPY --chmod=755 scripts/install_tamago.sh /tmp/install_tamago.sh + +# Run the TamaGo installation script. +RUN /tmp/install_tamago.sh && \ + rm /tmp/install_tamago.sh + CMD ["/bin/bash"] diff --git a/docker/zisk/Dockerfile.compiler b/docker/zisk/Dockerfile.compiler index 0c6b320..b885d8f 100644 --- a/docker/zisk/Dockerfile.compiler +++ b/docker/zisk/Dockerfile.compiler @@ -9,7 +9,7 @@ WORKDIR /ere RUN cargo build --release --package ere-compiler --bin ere-compiler --features zisk \ && mkdir bin && mv target/release/ere-compiler bin/ere-compiler \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime_stage @@ -28,6 +28,12 @@ ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ PATH=/usr/local/cargo/bin:$PATH +# Copy TamaGo +COPY --from=build_stage /root/.tamago /root/.tamago + +# Add TamaGo to path +ENV PATH=/root/.tamago/bin:$PATH + # Copy ZisK SDK COPY --from=build_stage /root/.zisk/toolchains /root/.zisk/toolchains diff --git a/docker/zisk/Dockerfile.server b/docker/zisk/Dockerfile.server index 0c96733..9fa9499 100644 --- a/docker/zisk/Dockerfile.server +++ b/docker/zisk/Dockerfile.server @@ -1,14 +1,11 @@ ARG BASE_ZKVM_IMAGE=ere-base-zisk:latest -ARG BASE_ZKVM_CUDA_IMAGE=ere-base-zisk:latest-cuda ARG RUNTIME_IMAGE=ubuntu:24.04 ARG RUNTIME_CUDA_IMAGE=nvidia/cuda:12.9.1-runtime-ubuntu24.04 # Whether to enable CUDA feature or not. ARG CUDA -FROM $BASE_ZKVM_IMAGE AS base -FROM $BASE_ZKVM_CUDA_IMAGE AS base_cuda -FROM base${CUDA:+_cuda} AS build_stage +FROM $BASE_ZKVM_IMAGE AS build_stage COPY . /ere @@ -19,7 +16,7 @@ ARG RUSTFLAGS RUN cargo build --release --package ere-server --bin ere-server --features zisk${CUDA:+,cuda} \ && mkdir bin && mv target/release/ere-server bin/ere-server \ - && cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache + && cargo clean && rm -rf $CARGO_HOME/registry/ FROM $RUNTIME_IMAGE AS runtime FROM $RUNTIME_CUDA_IMAGE AS runtime_cuda