Skip to content

Commit f3253c3

Browse files
authored
refactor: improve evm verifier generation in build-guest (#155)
* refactor * lint
1 parent 762755d commit f3253c3

File tree

2 files changed

+148
-52
lines changed

2 files changed

+148
-52
lines changed

crates/build-guest/src/main.rs

Lines changed: 15 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,18 @@ use dotenv::dotenv;
2323
use eyre::Result;
2424
use openvm_build::GuestOptions;
2525
use openvm_native_compiler::ir::DIGEST_SIZE;
26-
use openvm_native_recursion::halo2::utils::{CacheHalo2ParamsReader, Halo2ParamsReader};
2726
use openvm_sdk::{
28-
DefaultStaticVerifierPvHandler, F, Sdk,
27+
F, Sdk,
2928
commit::CommitBytes,
30-
config::{AggConfig, AppConfig, SdkVmConfig},
29+
config::{AppConfig, SdkVmConfig},
3130
fs::write_exe_to_file,
3231
};
3332
use openvm_stark_sdk::{openvm_stark_backend::p3_field::PrimeField32, p3_bn254_fr::Bn254Fr};
3433
use snark_verifier_sdk::snark_verifier::loader::evm::compile_solidity;
3534

36-
const LOG_PREFIX: &str = "[build-guest]";
35+
mod verifier;
3736

38-
/// The default directory to locate openvm's halo2 SRS parameters.
39-
const DEFAULT_PARAMS_DIR: &str = concat!(env!("HOME"), "/.openvm/params/");
37+
pub(crate) const LOG_PREFIX: &str = "[build-guest]";
4038

4139
/// File descriptor for app openvm config.
4240
const FD_APP_CONFIG: &str = "openvm.toml";
@@ -336,54 +334,18 @@ fn run_stage5_dump_evm_verifier(verifier_output_dir: &PathBuf, recompute_mode: b
336334
if let Some(parent) = path_verifier_bin.parent() {
337335
std::fs::create_dir_all(parent)?;
338336
}
339-
let verifier_contract = if recompute_mode {
340-
let dir_halo2_params = Path::new(DEFAULT_PARAMS_DIR).to_path_buf();
341-
let halo2_params_reader = CacheHalo2ParamsReader::new(&dir_halo2_params);
342-
let agg_pk = Sdk::new().agg_keygen(
343-
AggConfig::default(),
344-
&halo2_params_reader,
345-
&DefaultStaticVerifierPvHandler,
346-
)?;
347-
let halo2_params = halo2_params_reader
348-
.read_params(agg_pk.halo2_pk.wrapper.pinning.metadata.config_params.k);
349-
snark_verifier_sdk::evm::gen_evm_verifier_shplonk::<
350-
snark_verifier_sdk::halo2::aggregation::AggregationCircuit,
351-
>(
352-
&halo2_params,
353-
agg_pk.halo2_pk.wrapper.pinning.pk.get_vk(),
354-
agg_pk.halo2_pk.wrapper.pinning.metadata.num_pvs.clone(),
355-
Some(&path_verifier_sol),
356-
)
357-
} else {
358-
println!("{LOG_PREFIX} Downloading pre-built verifier from openvm-solidity-sdk...");
359-
let verifier_url = "https://github.com/openvm-org/openvm-solidity-sdk/raw/refs/heads/main/src/v1.3/Halo2Verifier.sol";
360-
361-
let output = std::process::Command::new("wget")
362-
.arg("-q")
363-
.arg("-O")
364-
.arg("-")
365-
.arg(verifier_url)
366-
.output()?;
367-
368-
if !output.status.success() {
369-
return Err(eyre::eyre!(
370-
"Failed to download verifier from {}: wget exited with code {:?}",
371-
verifier_url,
372-
output.status.code()
373-
));
374-
}
375337

376-
let sol_code = String::from_utf8(output.stdout)?;
377-
std::fs::write(&path_verifier_sol, &sol_code)?;
378-
println!(
379-
"{LOG_PREFIX} Downloaded verifier.sol to {}",
380-
path_verifier_sol.display()
381-
);
382-
383-
compile_solidity(&sol_code)
338+
let verifier_sol = if recompute_mode {
339+
verifier::generate_evm_verifier()?
340+
} else {
341+
verifier::download_evm_verifier()?
384342
};
385-
std::fs::write(&path_verifier_bin, &verifier_contract)?;
386-
println!("{LOG_PREFIX} verifier_contract written to {path_verifier_bin:?}");
343+
std::fs::write(&path_verifier_sol, &verifier_sol)?;
344+
println!("{LOG_PREFIX} verifier_sol written to {path_verifier_sol:?}");
345+
346+
let verifier_bin = compile_solidity(&verifier_sol);
347+
std::fs::write(&path_verifier_bin, &verifier_bin)?;
348+
println!("{LOG_PREFIX} verifier_bin written to {path_verifier_bin:?}");
387349

388350
println!("{LOG_PREFIX} === Stage 5 Finished ===");
389351
Ok(())
@@ -470,6 +432,7 @@ pub fn main() -> Result<()> {
470432

471433
run_stage4_dump_vk_json(&release_output_dir, leaf_commitments, exe_commitments)?;
472434

435+
env::set_current_dir(&workspace_dir)?;
473436
run_stage5_dump_evm_verifier(
474437
&release_output_dir.join("verifier"),
475438
stages_to_run.contains("stage5"),

crates/build-guest/src/verifier.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use std::path::Path;
2+
3+
use super::LOG_PREFIX;
4+
use eyre::Result;
5+
use openvm_native_recursion::halo2::utils::{CacheHalo2ParamsReader, Halo2ParamsReader};
6+
use openvm_sdk::{DefaultStaticVerifierPvHandler, Sdk, config::AggConfig};
7+
use snark_verifier_sdk::SHPLONK;
8+
9+
/// The default directory to locate openvm's halo2 SRS parameters.
10+
const DEFAULT_PARAMS_DIR: &str = concat!(env!("HOME"), "/.openvm/params/");
11+
12+
pub fn generate_evm_verifier() -> Result<String> {
13+
let dir_halo2_params = Path::new(DEFAULT_PARAMS_DIR).to_path_buf();
14+
let halo2_params_reader = CacheHalo2ParamsReader::new(&dir_halo2_params);
15+
let agg_pk = Sdk::new().agg_keygen(
16+
AggConfig::default(),
17+
&halo2_params_reader,
18+
&DefaultStaticVerifierPvHandler,
19+
)?;
20+
let halo2_params =
21+
halo2_params_reader.read_params(agg_pk.halo2_pk.wrapper.pinning.metadata.config_params.k);
22+
let sol_code = snark_verifier_sdk::evm::gen_evm_verifier_sol_code::<
23+
snark_verifier_sdk::halo2::aggregation::AggregationCircuit,
24+
SHPLONK,
25+
>(
26+
&halo2_params,
27+
agg_pk.halo2_pk.wrapper.pinning.pk.get_vk(),
28+
agg_pk.halo2_pk.wrapper.pinning.metadata.num_pvs.clone(),
29+
);
30+
31+
// 1. write sol_code to a tmp file
32+
// 2. use `forge fmt $tmpfile` to format it
33+
// 3. read it out again, and assign the String as `sol_code`
34+
let temp_file = std::env::temp_dir().join("Halo2Verifier.sol");
35+
std::fs::write(&temp_file, &sol_code)?;
36+
37+
let format_output = std::process::Command::new("forge")
38+
.arg("fmt")
39+
.arg(&temp_file)
40+
.output();
41+
42+
let sol_code = match format_output {
43+
Ok(output) if output.status.success() => {
44+
println!("{LOG_PREFIX} Formatted verifier with forge fmt");
45+
std::fs::read_to_string(&temp_file)?
46+
}
47+
_ => {
48+
println!("{LOG_PREFIX} Warning: forge fmt failed, using unformatted code");
49+
sol_code
50+
}
51+
};
52+
53+
// Clean up temp file
54+
let _ = std::fs::remove_file(&temp_file);
55+
56+
Ok(sol_code)
57+
}
58+
59+
pub fn download_evm_verifier() -> Result<String> {
60+
let verifier_url = "https://github.com/openvm-org/openvm-solidity-sdk/raw/refs/heads/main/src/v1.3/Halo2Verifier.sol";
61+
println!("{LOG_PREFIX} Downloading pre-built verifier from openvm-solidity-sdk...");
62+
63+
let output = std::process::Command::new("wget")
64+
.arg("-q")
65+
.arg("-O")
66+
.arg("-")
67+
.arg(verifier_url)
68+
.output()?;
69+
70+
if !output.status.success() {
71+
return Err(eyre::eyre!(
72+
"Failed to download verifier from {}: wget exited with code {:?}",
73+
verifier_url,
74+
output.status.code()
75+
));
76+
}
77+
78+
println!("{LOG_PREFIX} Downloaded Halo2Verifier.sol");
79+
80+
let sol_code = String::from_utf8(output.stdout)?;
81+
82+
Ok(sol_code)
83+
}
84+
#[cfg(test)]
85+
mod tests {
86+
use super::*;
87+
88+
use std::fs;
89+
90+
#[test]
91+
fn test_verifier() {
92+
// assert `generate_evm_verifier` vs `download_evm_verifier` result are equal
93+
// if not, dump them to 2 files, and let user use vimdiff to check
94+
95+
let num_preview_lines = 10;
96+
let print_verifier_info = |name: &str, code: &str| {
97+
println!("{} verifier length: {} bytes", name, code.len());
98+
let lines: Vec<&str> = code.lines().collect();
99+
for line in lines.iter().take(num_preview_lines) {
100+
println!("{}", line);
101+
}
102+
println!("...");
103+
for line in lines.iter().rev().take(num_preview_lines).rev() {
104+
println!("{}", line);
105+
}
106+
};
107+
108+
let downloaded = download_evm_verifier().expect("Failed to download EVM verifier");
109+
print_verifier_info("Downloaded", &downloaded);
110+
111+
let generated = generate_evm_verifier().expect("Failed to generate EVM verifier");
112+
print_verifier_info("Generated", &generated);
113+
114+
if generated != downloaded {
115+
let temp_dir = std::env::temp_dir();
116+
let generated_file = temp_dir.join("generated_verifier.sol");
117+
let downloaded_file = temp_dir.join("downloaded_verifier.sol");
118+
119+
fs::write(&generated_file, &generated).expect("Failed to write generated verifier");
120+
fs::write(&downloaded_file, &downloaded).expect("Failed to write downloaded verifier");
121+
122+
panic!(
123+
"Verifiers are different! Compare files:\n Generated: {}\n Downloaded: {}\nUse: vimdiff {} {}",
124+
generated_file.display(),
125+
downloaded_file.display(),
126+
generated_file.display(),
127+
downloaded_file.display()
128+
);
129+
} else {
130+
println!("Verifiers match successfully!");
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)