Skip to content

Commit 0c9fff0

Browse files
HrikBmanh9203
authored andcommitted
feat: OpenVmHalo2Verifier (#1549)
This PR adds a new verifier contract generation that wraps the original `snark-verifier` output (via inheritance). The goal of this wrapper `OpenVmHalo2Verifier` is to expose a more friendly interface to users that cleanly separates out the guest program public values. `OpenVmHalo2Verifier` exposes the following interface: ```solidity interface IOpenVmHalo2Verifier { function verify(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit, bytes32 appVmCommit) external view; } ``` - `publicValues`: The bytes revealed in the OpenVM guest program packed together. - `proofData`: Defined as `abi.encodePacked(KZG accumulators, publicValuesSuffix)` - `appExeCommit`: The commitment to the OpenVM application executable whose execution is being verified. - `appVmCommit`: The commitment to the VM configuration (aka `leaf_exe_commit`) Once received, the proof is constructed into the format expected by `snark-verifier`. The expected format is `abi.encodePacked(proofData[0:0x180], appExeCommit, appVmExeCommit, publicValuesPayload, proofData[0x180:])` where `publicValuesPayload` is a memory payload with each byte in `publicValues` separated into its own `bytes32` word. Since `OpenVmHalo2Verifier` inherits the `snark-verifier` output, the proof is forwarded via self-call. ## Verifier Generation The smart contract is written as a template that does not compile in isolation. During generation, the OpenVM SDK will fill out the maximum amount of public values and the OpenVM version with which the generation happened. Given an output folder, the relevant contracts are written into the following folder structure: ``` halo2/ ├── interfaces/ │ └── IOpenVmHalo2Verifier.sol ├── OpenVmHalo2Verifier.sol ├── Halo2Verifier.sol ``` `cargo openvm setup` will now generate this output directly into the `~/.openvm/` dir. Closes INT-3710
1 parent a0ae88f commit 0c9fff0

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

crates/sdk/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ repository.workspace = true
88
license.workspace = true
99

1010
[dependencies]
11+
1112
p3-fri = { workspace = true }
1213
openvm-algebra-circuit = { workspace = true }
1314
openvm-algebra-transpiler = { workspace = true }

crates/sdk/src/lib.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
use std::{fs::read, marker::PhantomData, path::Path, sync::Arc};
1+
use std::{
2+
env,
3+
fs::{create_dir_all, read, write},
4+
marker::PhantomData,
5+
path::Path,
6+
process::Command,
7+
sync::Arc,
8+
};
29

310
#[cfg(feature = "evm-verify")]
411
use alloy_sol_types::sol;
@@ -43,6 +50,10 @@ use snark_verifier_sdk::{evm::gen_evm_verifier_sol_code, halo2::aggregation::Agg
4350

4451
use crate::{
4552
config::AggConfig,
53+
fs::{
54+
EVM_HALO2_VERIFIER_BASE_NAME, EVM_HALO2_VERIFIER_INTERFACE_NAME,
55+
EVM_HALO2_VERIFIER_PARENT_NAME,
56+
},
4657
keygen::{AggProvingKey, AggStarkProvingKey},
4758
prover::{AppProver, StarkProver},
4859
};
@@ -540,3 +551,48 @@ impl<E: StarkFriEngine<SC>> GenericSdk<E> {
540551
Ok(gas_cost)
541552
}
542553
}
554+
555+
/// We will split the output by whitespace and look for the following
556+
/// sequence:
557+
/// [
558+
/// ...
559+
/// "=======",
560+
/// "OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier",
561+
/// "=======",
562+
/// "Binary:"
563+
/// "[compiled bytecode]"
564+
/// ...
565+
/// ]
566+
///
567+
/// Once we find "OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier," we can skip
568+
/// to the appropriate offset to get the compiled bytecode.
569+
fn extract_binary(output: &[u8], contract_name: &str) -> Vec<u8> {
570+
let split = split_by_ascii_whitespace(output);
571+
let contract_name_bytes = contract_name.as_bytes();
572+
573+
for i in 0..split.len().saturating_sub(3) {
574+
if split[i] == contract_name_bytes {
575+
return hex::decode(split[i + 3]).expect("Invalid hex in Binary");
576+
}
577+
}
578+
579+
panic!("Contract '{}' not found", contract_name);
580+
}
581+
582+
fn split_by_ascii_whitespace(bytes: &[u8]) -> Vec<&[u8]> {
583+
let mut split = Vec::new();
584+
let mut start = None;
585+
for (idx, byte) in bytes.iter().enumerate() {
586+
if byte.is_ascii_whitespace() {
587+
if let Some(start) = start.take() {
588+
split.push(&bytes[start..idx]);
589+
}
590+
} else if start.is_none() {
591+
start = Some(idx);
592+
}
593+
}
594+
if let Some(last) = start {
595+
split.push(&bytes[last..]);
596+
}
597+
split
598+
}

0 commit comments

Comments
 (0)