mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
Do preprocess when instantiating EreOpenVM (#67)
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2320,7 +2320,9 @@ dependencies = [
|
||||
"openvm-sdk",
|
||||
"openvm-stark-sdk",
|
||||
"openvm-transpiler",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.8.22",
|
||||
"zkvm-interface",
|
||||
]
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", ta
|
||||
openvm-build = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.2.0", default-features = false }
|
||||
openvm-transpiler = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.2.0", default-features = false }
|
||||
|
||||
toml = "0.8"
|
||||
thiserror = "2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[build-dependencies]
|
||||
build-utils = { workspace = true }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::{io, path::PathBuf};
|
||||
use thiserror::Error;
|
||||
use zkvm_interface::zkVMError;
|
||||
|
||||
@@ -12,13 +13,45 @@ pub enum OpenVMError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("OpenVM execution failed: {0}")]
|
||||
#[error("Failed to build guest, code: {0}")]
|
||||
BuildFailed(i32),
|
||||
#[error("Guest building skipped (OPENVM_SKIP_BUILD is set)")]
|
||||
BuildSkipped,
|
||||
#[error("Missing to find unique elf: {0}")]
|
||||
UniqueElfNotFound(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Failed to read elf at {path}: {source}")]
|
||||
ReadElfFailed { source: io::Error, path: PathBuf },
|
||||
#[error("Failed to read OpenVM's config file at {path}: {source}")]
|
||||
ReadConfigFailed { source: io::Error, path: PathBuf },
|
||||
#[error("Failed to deserialize OpenVM's config file: {0}")]
|
||||
DeserializeConfigFailed(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Failed to decode elf: {0}")]
|
||||
DecodeFailed(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Failed to transpile elf: {0}")]
|
||||
TranspileFailed(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
#[error("OpenVM execute failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("OpenVM prove failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
use std::{path::Path, time::Instant};
|
||||
|
||||
use crate::error::{CompileError, ExecuteError, OpenVMError, VerifyError};
|
||||
use openvm_build::GuestOptions;
|
||||
use openvm_circuit::arch::ContinuationVmProof;
|
||||
use openvm_circuit::{
|
||||
arch::{ContinuationVmProof, instructions::exe::VmExe},
|
||||
system::program::trace::VmCommittedExe,
|
||||
};
|
||||
use openvm_sdk::{
|
||||
Sdk, StdIn,
|
||||
F, SC, Sdk, StdIn,
|
||||
codec::{Decode, Encode},
|
||||
config::{AppConfig, SdkVmConfig},
|
||||
prover::AppProver,
|
||||
config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig},
|
||||
keygen::AppProvingKey,
|
||||
};
|
||||
use openvm_stark_sdk::config::{
|
||||
FriParameters, baby_bear_poseidon2::BabyBearPoseidon2Config,
|
||||
baby_bear_poseidon2::BabyBearPoseidon2Engine,
|
||||
};
|
||||
use openvm_transpiler::elf::Elf;
|
||||
use openvm_stark_sdk::config::FriParameters;
|
||||
use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::Path, sync::Arc, time::Instant};
|
||||
use zkvm_interface::{
|
||||
Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, ProverResourceType,
|
||||
zkVM, zkVMError,
|
||||
@@ -20,59 +21,115 @@ use zkvm_interface::{
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
mod error;
|
||||
use error::{CompileError, OpenVMError, VerifyError};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct OPENVM_TARGET;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct OpenVMProgram {
|
||||
elf: Vec<u8>,
|
||||
app_config: AppConfig<SdkVmConfig>,
|
||||
}
|
||||
|
||||
impl Compiler for OPENVM_TARGET {
|
||||
type Error = OpenVMError;
|
||||
|
||||
type Program = Elf;
|
||||
type Program = OpenVMProgram;
|
||||
|
||||
// Inlining `openvm_sdk::Sdk::build` in order to get raw elf bytes.
|
||||
fn compile(workspace_path: &Path, guest_relative: &Path) -> Result<Self::Program, Self::Error> {
|
||||
let sdk = Sdk::new();
|
||||
let guest_directory = workspace_path.join(guest_relative);
|
||||
let pkg = openvm_build::get_package(&guest_directory);
|
||||
let guest_opts = GuestOptions::default().with_profile("release".to_string());
|
||||
let target_dir = match openvm_build::build_guest_package(&pkg, &guest_opts, None, &None) {
|
||||
Ok(target_dir) => target_dir,
|
||||
Err(Some(code)) => return Err(CompileError::BuildFailed(code).into()),
|
||||
Err(None) => return Err(CompileError::BuildSkipped.into()),
|
||||
};
|
||||
|
||||
// Build the guest crate
|
||||
let elf: Elf = sdk
|
||||
.build(
|
||||
GuestOptions::default(),
|
||||
workspace_path.join(guest_relative),
|
||||
&Default::default(),
|
||||
)
|
||||
.map_err(|e| CompileError::Client(e.into()))?;
|
||||
// TODO: note that this does not transpile (check to see how expensive that is)
|
||||
let elf_path = openvm_build::find_unique_executable(&guest_directory, target_dir, &None)
|
||||
.map_err(|e| CompileError::UniqueElfNotFound(e.into()))?;
|
||||
let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadElfFailed {
|
||||
source,
|
||||
path: elf_path.to_path_buf(),
|
||||
})?;
|
||||
|
||||
Ok(elf)
|
||||
let app_config_path = guest_directory.join("openvm.toml");
|
||||
let app_config = if app_config_path.exists() {
|
||||
let toml = fs::read_to_string(&app_config_path).map_err(|source| {
|
||||
CompileError::ReadConfigFailed {
|
||||
source,
|
||||
path: app_config_path.to_path_buf(),
|
||||
}
|
||||
})?;
|
||||
toml::from_str(&toml)
|
||||
.map_err(|err| CompileError::DeserializeConfigFailed(err.into()))?
|
||||
} else {
|
||||
// The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/ca36de3/crates/cli/src/default.rs#L31.
|
||||
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(),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(OpenVMProgram { elf, app_config })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EreOpenVM {
|
||||
program: <OPENVM_TARGET as Compiler>::Program,
|
||||
app_config: AppConfig<SdkVmConfig>,
|
||||
app_exe: VmExe<F>,
|
||||
app_committed_exe: Arc<VmCommittedExe<SC>>,
|
||||
app_pk: Arc<AppProvingKey<SdkVmConfig>>,
|
||||
_resource: ProverResourceType,
|
||||
}
|
||||
|
||||
impl EreOpenVM {
|
||||
pub fn new(
|
||||
program: <OPENVM_TARGET as Compiler>::Program,
|
||||
_resource_type: ProverResourceType,
|
||||
) -> Self {
|
||||
Self { program }
|
||||
pub fn new(program: OpenVMProgram, _resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
let sdk = Sdk::new();
|
||||
|
||||
let elf = Elf::decode(&program.elf, MEM_SIZE as u32)
|
||||
.map_err(|e| OpenVMError::from(CompileError::DecodeFailed(e.into())))?;
|
||||
|
||||
let app_exe = sdk
|
||||
.transpile(elf, program.app_config.app_vm_config.transpiler())
|
||||
.map_err(|e| OpenVMError::from(CompileError::TranspileFailed(e.into())))?;
|
||||
|
||||
let app_pk = sdk
|
||||
.app_keygen(program.app_config.clone())
|
||||
.map_err(|e| zkVMError::Other(e.into()))?;
|
||||
|
||||
let app_committed_exe = sdk
|
||||
.commit_app_exe(app_pk.app_fri_params(), app_exe.clone())
|
||||
.map_err(|e| zkVMError::Other(e.into()))?;
|
||||
|
||||
Ok(Self {
|
||||
app_config: program.app_config,
|
||||
app_exe,
|
||||
app_committed_exe,
|
||||
app_pk: Arc::new(app_pk),
|
||||
_resource,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreOpenVM {
|
||||
fn execute(&self, inputs: &Input) -> Result<zkvm_interface::ProgramExecutionReport, zkVMError> {
|
||||
let sdk = Sdk::new();
|
||||
let vm_cfg = SdkVmConfig::builder()
|
||||
.system(Default::default())
|
||||
.rv32i(Default::default())
|
||||
.rv32m(Default::default())
|
||||
.io(Default::default())
|
||||
.build();
|
||||
|
||||
let exe = sdk
|
||||
.transpile(self.program.clone(), vm_cfg.transpiler())
|
||||
.map_err(|e| CompileError::Client(e.into()))
|
||||
.map_err(OpenVMError::from)?;
|
||||
|
||||
let mut stdin = StdIn::default();
|
||||
for input in inputs.iter() {
|
||||
@@ -84,9 +141,12 @@ impl zkVM for EreOpenVM {
|
||||
|
||||
let start = Instant::now();
|
||||
let _outputs = sdk
|
||||
.execute(exe.clone(), vm_cfg.clone(), stdin)
|
||||
.map_err(|e| CompileError::Client(e.into()))
|
||||
.map_err(OpenVMError::from)?;
|
||||
.execute(
|
||||
self.app_exe.clone(),
|
||||
self.app_config.app_vm_config.clone(),
|
||||
stdin,
|
||||
)
|
||||
.map_err(|e| OpenVMError::from(ExecuteError::Client(e.into())))?;
|
||||
|
||||
Ok(ProgramExecutionReport {
|
||||
execution_duration: start.elapsed(),
|
||||
@@ -98,21 +158,7 @@ impl zkVM for EreOpenVM {
|
||||
&self,
|
||||
inputs: &Input,
|
||||
) -> Result<(Vec<u8>, zkvm_interface::ProgramProvingReport), zkVMError> {
|
||||
// TODO: We need a stateful version in order to not spend a lot of time
|
||||
// TODO doing things like computing the pk and vk.
|
||||
|
||||
let sdk = Sdk::new();
|
||||
let vm_cfg = SdkVmConfig::builder()
|
||||
.system(Default::default())
|
||||
.rv32i(Default::default())
|
||||
.rv32m(Default::default())
|
||||
.io(Default::default())
|
||||
.build();
|
||||
|
||||
let app_exe = sdk
|
||||
.transpile(self.program.clone(), vm_cfg.transpiler())
|
||||
.map_err(|e| CompileError::Client(e.into()))
|
||||
.map_err(OpenVMError::from)?;
|
||||
|
||||
let mut stdin = StdIn::default();
|
||||
for input in inputs.iter() {
|
||||
@@ -122,20 +168,10 @@ impl zkVM for EreOpenVM {
|
||||
}
|
||||
}
|
||||
|
||||
let app_config = AppConfig::new(FriParameters::standard_fast(), vm_cfg);
|
||||
|
||||
let app_pk = sdk.app_keygen(app_config).unwrap();
|
||||
|
||||
let app_committed_exe = sdk
|
||||
.commit_app_exe(app_pk.app_fri_params(), app_exe)
|
||||
.unwrap();
|
||||
|
||||
let prover = AppProver::<_, BabyBearPoseidon2Engine>::new(
|
||||
app_pk.app_vm_pk.clone(),
|
||||
app_committed_exe,
|
||||
);
|
||||
let now = std::time::Instant::now();
|
||||
let proof = prover.generate_app_proof(stdin);
|
||||
let proof = sdk
|
||||
.generate_app_proof(self.app_pk.clone(), self.app_committed_exe.clone(), stdin)
|
||||
.unwrap();
|
||||
let elapsed = now.elapsed();
|
||||
|
||||
let proof_bytes = proof.encode_to_vec().unwrap();
|
||||
@@ -145,20 +181,10 @@ impl zkVM for EreOpenVM {
|
||||
|
||||
fn verify(&self, mut proof: &[u8]) -> Result<(), zkVMError> {
|
||||
let sdk = Sdk::new();
|
||||
let vm_cfg = SdkVmConfig::builder()
|
||||
.system(Default::default())
|
||||
.rv32i(Default::default())
|
||||
.rv32m(Default::default())
|
||||
.io(Default::default())
|
||||
.build();
|
||||
|
||||
let app_config = AppConfig::new(FriParameters::standard_fast(), vm_cfg);
|
||||
let proof = ContinuationVmProof::<SC>::decode(&mut proof).unwrap();
|
||||
|
||||
let app_pk = sdk.app_keygen(app_config).unwrap();
|
||||
|
||||
let proof = ContinuationVmProof::<BabyBearPoseidon2Config>::decode(&mut proof).unwrap();
|
||||
|
||||
let app_vk = app_pk.get_app_vk();
|
||||
let app_vk = self.app_pk.get_app_vk();
|
||||
sdk.verify_app_proof(&app_vk, &proof)
|
||||
.map(|_payload| ())
|
||||
.map_err(|e| OpenVMError::Verify(VerifyError::Client(e.into())))
|
||||
@@ -199,12 +225,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf =
|
||||
let program =
|
||||
OPENVM_TARGET::compile(&test_guest_path, Path::new("")).expect("compilation failed");
|
||||
assert!(
|
||||
!elf.instructions.is_empty(),
|
||||
"ELF bytes should not be empty."
|
||||
);
|
||||
assert!(!program.elf.is_empty(), "ELF bytes should not be empty.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -212,10 +235,10 @@ mod tests {
|
||||
fn test_execute_empty_input_panic() {
|
||||
// Panics because the program expects input arguments, but we supply none
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf =
|
||||
let program =
|
||||
OPENVM_TARGET::compile(&test_guest_path, Path::new("")).expect("compilation failed");
|
||||
let empty_input = Input::new();
|
||||
let zkvm = EreOpenVM::new(elf, ProverResourceType::Cpu);
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&empty_input).unwrap();
|
||||
}
|
||||
@@ -223,24 +246,24 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf =
|
||||
let program =
|
||||
OPENVM_TARGET::compile(&test_guest_path, Path::new("")).expect("compilation failed");
|
||||
let mut input = Input::new();
|
||||
input.write(10u64);
|
||||
|
||||
let zkvm = EreOpenVM::new(elf, ProverResourceType::Cpu);
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
zkvm.execute(&input).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_verify() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf =
|
||||
let program =
|
||||
OPENVM_TARGET::compile(&test_guest_path, Path::new("")).expect("compilation failed");
|
||||
let mut input = Input::new();
|
||||
input.write(10u64);
|
||||
|
||||
let zkvm = EreOpenVM::new(elf, ProverResourceType::Cpu);
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
let (proof, _) = zkvm.prove(&input).unwrap();
|
||||
zkvm.verify(&proof).expect("proof should verify");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user