mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
add initial openvm code
This commit is contained in:
2130
Cargo.lock
generated
2130
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,8 @@ members = [
|
||||
# zkVM interface
|
||||
"crates/zkvm-interface",
|
||||
# zkVMs
|
||||
"crates/ere-sp1", "crates/ere-risczero",
|
||||
"crates/ere-sp1",
|
||||
"crates/ere-risczero", "crates/ere-openvm",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
|
||||
20
crates/ere-openvm/Cargo.toml
Normal file
20
crates/ere-openvm/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "ere-openvm"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
zkvm-interface = { workspace = true }
|
||||
|
||||
openvm-sdk = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.1.1", default-features = false }
|
||||
openvm-circuit = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.1.1", default-features = false }
|
||||
openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.1" }
|
||||
openvm-build = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.1.1", default-features = false }
|
||||
openvm-transpiler = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.1.1", default-features = false }
|
||||
|
||||
thiserror = "2"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
22
crates/ere-openvm/src/error.rs
Normal file
22
crates/ere-openvm/src/error.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OpenVMError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("OpenVM execution failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("OpenVM verification failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
208
crates/ere-openvm/src/lib.rs
Normal file
208
crates/ere-openvm/src/lib.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
use openvm_build::GuestOptions;
|
||||
use openvm_circuit::arch::ContinuationVmProof;
|
||||
use openvm_sdk::{
|
||||
Sdk, StdIn,
|
||||
codec::{Decode, Encode},
|
||||
config::{AppConfig, SdkVmConfig},
|
||||
prover::AppProver,
|
||||
};
|
||||
use openvm_stark_sdk::config::{
|
||||
FriParameters, baby_bear_poseidon2::BabyBearPoseidon2Config,
|
||||
baby_bear_poseidon2::BabyBearPoseidon2Engine,
|
||||
};
|
||||
use openvm_transpiler::elf::Elf;
|
||||
use zkvm_interface::{Compiler, ProgramExecutionReport, ProgramProvingReport, zkVM};
|
||||
|
||||
mod error;
|
||||
use error::{CompileError, OpenVMError, VerifyError};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct OPENVM_TARGET;
|
||||
|
||||
impl Compiler for OPENVM_TARGET {
|
||||
type Error = OpenVMError;
|
||||
|
||||
type Program = Elf;
|
||||
|
||||
fn compile(path_to_program: &std::path::Path) -> Result<Self::Program, Self::Error> {
|
||||
let sdk = Sdk::new();
|
||||
|
||||
// Build the guest crate
|
||||
let elf: Elf = sdk
|
||||
.build(
|
||||
GuestOptions::default(),
|
||||
path_to_program,
|
||||
&Default::default(),
|
||||
)
|
||||
.map_err(|e| CompileError::Client(e.into()))?;
|
||||
// TODO: note that this does not transpile (check to see how expensive that is)
|
||||
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EreOpenVM;
|
||||
|
||||
impl zkVM<OPENVM_TARGET> for EreOpenVM {
|
||||
type Error = OpenVMError;
|
||||
|
||||
fn execute(
|
||||
program: &<OPENVM_TARGET as Compiler>::Program,
|
||||
inputs: &zkvm_interface::Input,
|
||||
) -> Result<zkvm_interface::ProgramExecutionReport, Self::Error> {
|
||||
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(program.clone(), vm_cfg.transpiler())
|
||||
.map_err(|e| CompileError::Client(e.into()))?;
|
||||
|
||||
let mut stdin = StdIn::default();
|
||||
for input in inputs.chunked_iter() {
|
||||
stdin.write_bytes(input);
|
||||
}
|
||||
|
||||
let _outputs = sdk
|
||||
.execute(exe.clone(), vm_cfg.clone(), stdin)
|
||||
.map_err(|e| CompileError::Client(e.into()))?;
|
||||
|
||||
Ok(ProgramExecutionReport::default())
|
||||
}
|
||||
|
||||
fn prove(
|
||||
program: &<OPENVM_TARGET as Compiler>::Program,
|
||||
inputs: &zkvm_interface::Input,
|
||||
) -> Result<(Vec<u8>, zkvm_interface::ProgramProvingReport), Self::Error> {
|
||||
// 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(program.clone(), vm_cfg.transpiler())
|
||||
.map_err(|e| CompileError::Client(e.into()))?;
|
||||
|
||||
let mut stdin = StdIn::default();
|
||||
for input in inputs.chunked_iter() {
|
||||
stdin.write_bytes(input);
|
||||
}
|
||||
|
||||
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 elapsed = now.elapsed();
|
||||
|
||||
let proof_bytes = proof.encode_to_vec().unwrap();
|
||||
|
||||
Ok((proof_bytes, ProgramProvingReport::new(elapsed)))
|
||||
}
|
||||
|
||||
fn verify(
|
||||
_program: &<OPENVM_TARGET as Compiler>::Program,
|
||||
mut proof: &[u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
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 app_pk = sdk.app_keygen(app_config).unwrap();
|
||||
|
||||
let proof = ContinuationVmProof::<BabyBearPoseidon2Config>::decode(&mut proof).unwrap();
|
||||
|
||||
let app_vk = app_pk.get_app_vk();
|
||||
sdk.verify_app_proof(&app_vk, &proof)
|
||||
.map(|_payload| ())
|
||||
.map_err(|e| OpenVMError::Verify(VerifyError::Client(e.into())))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use zkvm_interface::Compiler;
|
||||
|
||||
use crate::OPENVM_TARGET;
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// TODO: for now, we just get one test file
|
||||
// TODO: but this should get the whole directory and compile each test
|
||||
fn get_compile_test_guest_program_path() -> PathBuf {
|
||||
let workspace_dir = env!("CARGO_WORKSPACE_DIR");
|
||||
PathBuf::from(workspace_dir)
|
||||
.join("tests")
|
||||
.join("openvm")
|
||||
.join("compile")
|
||||
.join("basic")
|
||||
.canonicalize()
|
||||
.expect("Failed to find or canonicalize test guest program at <CARGO_WORKSPACE_DIR>/tests/compile/openvm")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf = OPENVM_TARGET::compile(&test_guest_path).expect("compilation failed");
|
||||
assert!(
|
||||
!elf.instructions.is_empty(),
|
||||
"ELF bytes should not be empty."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
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 = OPENVM_TARGET::compile(&test_guest_path).expect("compilation failed");
|
||||
let empty_input = zkvm_interface::Input::new();
|
||||
|
||||
EreOpenVM::execute(&elf, &empty_input).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf = OPENVM_TARGET::compile(&test_guest_path).expect("compilation failed");
|
||||
let mut input = zkvm_interface::Input::new();
|
||||
input.write(&10u64).unwrap();
|
||||
EreOpenVM::execute(&elf, &input).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_verify() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
let elf = OPENVM_TARGET::compile(&test_guest_path).expect("compilation failed");
|
||||
let mut input = zkvm_interface::Input::new();
|
||||
input.write(&10u64).unwrap();
|
||||
let (proof, _) = EreOpenVM::prove(&elf, &input).unwrap();
|
||||
EreOpenVM::verify(&elf, &proof).expect("proof should verify");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user