1use std::path::PathBuf;
2
3use clap::Parser;
4use eyre::{Context, Result};
5use openvm_sdk::{
6 fs::{read_from_file_json, read_object_from_file},
7 prover::verify_app_proof_with_expected_exe_commit,
8 types::{AppExecutionCommit, VerificationBaselineJson, VersionedVmStarkProof},
9 Sdk, OPENVM_VERSION,
10};
11
12use crate::{
13 args::ManifestArgs,
14 default::{APP_PROOF_EXT, STARK_PROOF_EXT},
15 util::{
16 get_app_baseline_path, get_app_commit_path, get_app_vk_path, get_manifest_path_and_dir,
17 get_single_target_name_raw, get_target_dir, get_target_output_dir, resolve_proof_path,
18 },
19};
20
21#[derive(Parser)]
22#[command(name = "verify", about = "Verify a proof")]
23pub struct VerifyCmd {
24 #[command(subcommand)]
25 command: VerifySubCommand,
26}
27
28#[derive(Parser)]
29enum VerifySubCommand {
30 App {
31 #[arg(
32 long,
33 action,
34 help = "Path to app verifying key, by default will search for it in ${openvm_dir}/app.vk",
35 help_heading = "OpenVM Options"
36 )]
37 app_vk: Option<PathBuf>,
38
39 #[arg(
40 long,
41 action,
42 help = "Path to app proof, by default will search the working directory for a file with extension .app.proof",
43 help_heading = "OpenVM Options"
44 )]
45 proof: Option<PathBuf>,
46
47 #[arg(
48 long,
49 num_args = 0..=1,
50 help = "Check the exe commit recovered from the proof against an expected app commit. With no value, uses the default commit path for the target; or pass an explicit path to a .commit.json",
51 help_heading = "OpenVM Options"
52 )]
53 app_commit: Option<Option<PathBuf>>,
54
55 #[command(flatten)]
56 cargo_args: SingleTargetCargoArgs,
57 },
58 Stark {
59 #[arg(
60 long,
61 action,
62 help = "Path to the aggregation verifying key, by default uses the internal-recursive verifying key generated by 'cargo openvm setup'",
63 help_heading = "OpenVM Options"
64 )]
65 agg_vk: Option<PathBuf>,
66
67 #[arg(
70 long,
71 action,
72 help = "Path to app baseline (.baseline.json), by default will search for it using the binary target name",
73 help_heading = "OpenVM Options"
74 )]
75 app_baseline: Option<PathBuf>,
76
77 #[arg(
78 long,
79 action,
80 help = "Path to STARK proof, by default will search the working directory for a file with extension .stark.proof",
81 help_heading = "OpenVM Options"
82 )]
83 proof: Option<PathBuf>,
84
85 #[command(flatten)]
86 cargo_args: SingleTargetCargoArgs,
87 },
88 #[cfg(feature = "evm-verify")]
89 Evm {
90 #[arg(
91 long,
92 action,
93 help = "Path to EVM halo2 verifier directory, by default uses ~/.openvm/halo2/ (generated by 'cargo openvm setup --evm')",
94 help_heading = "OpenVM Options"
95 )]
96 evm_verifier: Option<PathBuf>,
97
98 #[arg(
99 long,
100 num_args = 0..=1,
101 help = "Check the commit embedded in the proof against an expected app commit. With no value, uses the default commit path for the target; or pass an explicit path to a .commit.json",
102 help_heading = "OpenVM Options"
103 )]
104 app_commit: Option<Option<PathBuf>>,
105
106 #[arg(
107 long,
108 action,
109 help = "Path to EVM proof, by default will search the working directory for a file with extension .evm.proof",
110 help_heading = "OpenVM Options"
111 )]
112 proof: Option<PathBuf>,
113
114 #[command(flatten)]
115 cargo_args: SingleTargetCargoArgs,
116 },
117}
118
119#[derive(Parser)]
120pub struct SingleTargetCargoArgs {
121 #[arg(
122 long,
123 short = 'p',
124 value_name = "PACKAGES",
125 help = "The package to run; by default is the package in the current workspace",
126 help_heading = "Package Selection"
127 )]
128 pub package: Option<String>,
129
130 #[arg(
131 long,
132 value_name = "BIN",
133 help = "Run the specified binary",
134 help_heading = "Target Selection"
135 )]
136 pub bin: Vec<String>,
137
138 #[arg(
139 long,
140 value_name = "EXAMPLE",
141 help = "Run the specified example",
142 help_heading = "Target Selection"
143 )]
144 pub example: Vec<String>,
145
146 #[arg(
147 long,
148 value_name = "NAME",
149 default_value = "release",
150 help = "Run with the given profile",
151 help_heading = "Compilation Options"
152 )]
153 pub profile: String,
154
155 #[clap(flatten)]
156 pub manifest: ManifestArgs,
157}
158
159impl VerifyCmd {
160 pub fn run(&self) -> Result<()> {
161 match &self.command {
162 VerifySubCommand::App {
163 app_vk,
164 proof,
165 app_commit,
166 cargo_args,
167 } => {
168 let app_vk_path = if let Some(app_vk) = app_vk {
169 app_vk.to_path_buf()
170 } else {
171 let (manifest_path, _) =
172 get_manifest_path_and_dir(&cargo_args.manifest.manifest_path)?;
173 let target_dir =
174 get_target_dir(&cargo_args.manifest.target_dir, &manifest_path);
175 get_app_vk_path(&target_dir)
176 };
177 let app_vk: openvm_sdk::keygen::AppVerifyingKey =
178 read_object_from_file(app_vk_path)?;
179
180 let proof_path = resolve_proof_path(proof, APP_PROOF_EXT)?;
181 println!("Verifying application proof at {}", proof_path.display());
182 let app_proof = read_object_from_file(proof_path)?;
183 let exe_commit =
184 read_app_commit(app_commit, cargo_args)?.map(|c| c.app_exe_commit.into());
185 verify_app_proof_with_expected_exe_commit::<openvm_sdk::DefaultStarkEngine>(
186 &app_vk, &app_proof, exe_commit,
187 )?;
188 }
189 VerifySubCommand::Stark {
190 agg_vk,
191 app_baseline,
192 proof,
193 cargo_args,
194 } => {
195 let (manifest_path, _) =
196 get_manifest_path_and_dir(&cargo_args.manifest.manifest_path)?;
197 let target_dir = get_target_dir(&cargo_args.manifest.target_dir, &manifest_path);
198 let agg_vk_path = agg_vk
199 .clone()
200 .unwrap_or_else(|| crate::default::default_internal_recursive_vk_path().into());
201 let agg_vk = read_object_from_file(&agg_vk_path).map_err(|e| {
202 eyre::eyre!(
203 "Failed to read aggregation verifying key from {}: {e}\nRun 'cargo openvm setup' first to generate it",
204 agg_vk_path.display()
205 )
206 })?;
207 let baseline_path = if let Some(app_baseline) = app_baseline {
208 app_baseline.to_path_buf()
209 } else {
210 let target_output_dir = get_target_output_dir(&target_dir, &cargo_args.profile);
211 let target_name = get_single_target_name_raw(
212 &cargo_args.bin,
213 &cargo_args.example,
214 &cargo_args.manifest.manifest_path,
215 &cargo_args.package,
216 )?;
217 get_app_baseline_path(&target_output_dir, target_name)
218 };
219 let baseline_json: VerificationBaselineJson = read_from_file_json(baseline_path)?;
220 let expected_app_commit = baseline_json.into();
221
222 let proof_path = resolve_proof_path(proof, STARK_PROOF_EXT)?;
223 println!("Verifying STARK proof at {}", proof_path.display());
224 let stark_proof: VersionedVmStarkProof = read_from_file_json(proof_path)
225 .with_context(|| {
226 format!("Proof needs to be compatible with openvm v{OPENVM_VERSION}",)
227 })?;
228 if stark_proof.version != format!("v{OPENVM_VERSION}") {
229 eprintln!("Attempting to verify proof generated with openvm {}, but the verifier is on openvm v{OPENVM_VERSION}", stark_proof.version);
230 }
231 Sdk::verify_proof(agg_vk, expected_app_commit, &stark_proof.try_into()?)?;
232 }
233 #[cfg(feature = "evm-verify")]
234 VerifySubCommand::Evm {
235 evm_verifier,
236 app_commit,
237 proof,
238 cargo_args,
239 } => {
240 use openvm_sdk::{fs::read_evm_halo2_verifier_from_folder, types::EvmProof};
241
242 let verifier_path = evm_verifier
243 .clone()
244 .unwrap_or_else(|| crate::default::default_evm_halo2_verifier_path().into());
245 let evm_verifier = read_evm_halo2_verifier_from_folder(
246 &verifier_path,
247 Some(&crate::util::evm_verifier_version_dir()),
248 )
249 .map_err(
250 |e| {
251 eyre::eyre!(
252 "Failed to read EVM verifier from {}: {e}\nRun 'cargo openvm setup --evm' to generate it",
253 verifier_path.display()
254 )
255 },
256 )?;
257
258 let proof_path = resolve_proof_path(proof, crate::default::EVM_PROOF_EXT)?;
259 println!("Verifying EVM proof at {}", proof_path.display());
261 let evm_proof: EvmProof = read_from_file_json(proof_path).with_context(|| {
262 format!("Proof needs to be compatible with openvm v{OPENVM_VERSION}",)
263 })?;
264 if evm_proof.version != format!("v{OPENVM_VERSION}") {
265 eprintln!("Attempting to verify proof generated with openvm {}, but the verifier is on openvm v{OPENVM_VERSION}", evm_proof.version);
266 }
267 let app_commit = read_app_commit(app_commit, cargo_args)?;
268 Sdk::verify_evm_halo2_proof(&evm_verifier, evm_proof, app_commit)?;
269 }
270 }
271 println!("Proof verified successfully!");
272 Ok(())
273 }
274}
275
276fn read_app_commit(
277 app_commit: &Option<Option<PathBuf>>,
278 cargo_args: &SingleTargetCargoArgs,
279) -> Result<Option<AppExecutionCommit>> {
280 let Some(explicit) = app_commit else {
281 return Ok(None);
282 };
283 let commit_path = if let Some(path) = explicit {
284 path.to_path_buf()
285 } else {
286 let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest.manifest_path)?;
287 let target_dir = get_target_dir(&cargo_args.manifest.target_dir, &manifest_path);
288 let target_output_dir = get_target_output_dir(&target_dir, &cargo_args.profile);
289 let target_name = get_single_target_name_raw(
290 &cargo_args.bin,
291 &cargo_args.example,
292 &cargo_args.manifest.manifest_path,
293 &cargo_args.package,
294 )?;
295 get_app_commit_path(&target_output_dir, target_name)
296 };
297 let app_commit: AppExecutionCommit = read_from_file_json(&commit_path).with_context(|| {
298 format!(
299 "Failed to read app commit from {}\nRun 'cargo openvm commit' first to generate it",
300 commit_path.display()
301 )
302 })?;
303 Ok(Some(app_commit))
304}