cargo_openvm/commands/
verify.rs

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        /// NOTE: if `openvm commit` was called with the `--exe` option, then `--app-baseline` must
68        /// be specified so the command knows where to find the baseline file.
69        #[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                // The app config used here doesn't matter, it is ignored in verification
260                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}