cargo_openvm/commands/
verify.rs

1use std::path::{Path, PathBuf};
2
3use clap::Parser;
4use eyre::{Context, Result};
5use openvm_sdk::{
6    fs::{decode_from_file, read_from_file_json, read_object_from_file},
7    prover::verify_app_proof,
8    types::VersionedVmStarkProof,
9    Sdk, OPENVM_VERSION,
10};
11
12use super::KeygenCargoArgs;
13#[cfg(feature = "evm-verify")]
14use crate::default::default_evm_halo2_verifier_path;
15use crate::{
16    default::default_agg_stark_vk_path,
17    util::{
18        get_app_commit_path, get_app_vk_path, get_files_with_ext, get_manifest_path_and_dir,
19        get_single_target_name_raw, get_target_dir, get_target_output_dir,
20    },
21};
22
23#[derive(Parser)]
24#[command(name = "verify", about = "Verify a proof")]
25pub struct VerifyCmd {
26    #[command(subcommand)]
27    command: VerifySubCommand,
28}
29
30#[derive(Parser)]
31enum VerifySubCommand {
32    App {
33        #[arg(
34            long,
35            action,
36            help = "Path to app verifying key, by default will search for it in ${target_dir}/openvm/app.vk",
37            help_heading = "OpenVM Options"
38        )]
39        app_vk: Option<PathBuf>,
40
41        #[arg(
42            long,
43            action,
44            help = "Path to app proof, by default will search the working directory for a file with extension .app.proof",
45            help_heading = "OpenVM Options"
46        )]
47        proof: Option<PathBuf>,
48
49        #[command(flatten)]
50        cargo_args: KeygenCargoArgs,
51    },
52    Stark {
53        /// NOTE: if `openvm commit` was called with the `--exe` option, then `--app-commit` must
54        /// be specified so the command knows where to find the app commit.
55        #[arg(
56            long,
57            action,
58            help = "Path to app commit, by default will search for it using the binary target name",
59            help_heading = "OpenVM Options"
60        )]
61        app_commit: Option<PathBuf>,
62
63        #[arg(
64            long,
65            action,
66            help = "Path to STARK proof, by default will search the working directory for a file with extension .stark.proof",
67            help_heading = "OpenVM Options"
68        )]
69        proof: Option<PathBuf>,
70
71        #[command(flatten)]
72        cargo_args: SingleTargetCargoArgs,
73    },
74    #[cfg(feature = "evm-verify")]
75    Evm {
76        #[arg(
77            long,
78            action,
79            help = "Path to EVM proof, by default will search the working directory for a file with extension .evm.proof",
80            help_heading = "OpenVM Options"
81        )]
82        proof: Option<PathBuf>,
83    },
84}
85
86#[derive(Parser)]
87pub struct SingleTargetCargoArgs {
88    #[arg(
89        long,
90        short = 'p',
91        value_name = "PACKAGES",
92        help = "The package to run; by default is the package in the current workspace",
93        help_heading = "Package Selection"
94    )]
95    pub package: Option<String>,
96
97    #[arg(
98        long,
99        value_name = "BIN",
100        help = "Run the specified binary",
101        help_heading = "Target Selection"
102    )]
103    pub bin: Vec<String>,
104
105    #[arg(
106        long,
107        value_name = "EXAMPLE",
108        help = "Run the specified example",
109        help_heading = "Target Selection"
110    )]
111    pub example: Vec<String>,
112
113    #[arg(
114        long,
115        value_name = "NAME",
116        default_value = "release",
117        help = "Run with the given profile",
118        help_heading = "Compilation Options"
119    )]
120    pub profile: String,
121
122    #[arg(
123        long,
124        value_name = "DIR",
125        help = "Directory for all generated artifacts and intermediate files",
126        help_heading = "Output Options"
127    )]
128    pub target_dir: Option<PathBuf>,
129
130    #[arg(
131        long,
132        value_name = "PATH",
133        help = "Path to the Cargo.toml file, by default searches for the file in the current or any parent directory",
134        help_heading = "Manifest Options"
135    )]
136    pub manifest_path: Option<PathBuf>,
137}
138
139impl VerifyCmd {
140    pub fn run(&self) -> Result<()> {
141        match &self.command {
142            VerifySubCommand::App {
143                app_vk,
144                proof,
145                cargo_args,
146            } => {
147                let app_vk_path = if let Some(app_vk) = app_vk {
148                    app_vk.to_path_buf()
149                } else {
150                    let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?;
151                    let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path);
152                    get_app_vk_path(&target_dir)
153                };
154                let app_vk = read_object_from_file(app_vk_path)?;
155
156                let proof_path = if let Some(proof) = proof {
157                    proof.clone()
158                } else {
159                    let files = get_files_with_ext(Path::new("."), "app.proof")?;
160                    if files.len() > 1 {
161                        return Err(eyre::eyre!("multiple .app.proof files found, please specify the path using option --proof"));
162                    } else if files.is_empty() {
163                        return Err(eyre::eyre!("no .app.proof file found, please specify the path using option --proof"));
164                    }
165                    files[0].clone()
166                };
167                println!("Verifying application proof at {}", proof_path.display());
168                let app_proof = decode_from_file(proof_path)?;
169                verify_app_proof(&app_vk, &app_proof)?;
170            }
171            VerifySubCommand::Stark {
172                app_commit,
173                proof,
174                cargo_args,
175            } => {
176                let agg_vk = read_object_from_file(default_agg_stark_vk_path())
177                    .map_err(|e| {
178                        eyre::eyre!(
179                        "Failed to read aggregation STARK verifying key: {e}\nPlease run 'cargo openvm setup' first",
180                    )
181                    })?;
182                let app_commit_path = if let Some(app_commit) = app_commit {
183                    app_commit.to_path_buf()
184                } else {
185                    let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?;
186                    let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path);
187                    let target_output_dir = get_target_output_dir(&target_dir, &cargo_args.profile);
188                    let target_name = get_single_target_name_raw(
189                        &cargo_args.bin,
190                        &cargo_args.example,
191                        &cargo_args.manifest_path,
192                        &cargo_args.package,
193                    )?;
194                    get_app_commit_path(&target_output_dir, target_name)
195                };
196                let expected_app_commit = read_from_file_json(app_commit_path)?;
197
198                let proof_path = if let Some(proof) = proof {
199                    proof.clone()
200                } else {
201                    let files = get_files_with_ext(Path::new("."), "stark.proof")?;
202                    if files.len() > 1 {
203                        return Err(eyre::eyre!("multiple .stark.proof files found, please specify the path using option --proof"));
204                    } else if files.is_empty() {
205                        return Err(eyre::eyre!("no .stark.proof file found, please specify the path using option --proof"));
206                    }
207                    files[0].clone()
208                };
209                println!("Verifying STARK proof at {}", proof_path.display());
210                let stark_proof: VersionedVmStarkProof = read_from_file_json(proof_path)
211                    .with_context(|| {
212                        format!("Proof needs to be compatible with openvm v{OPENVM_VERSION}",)
213                    })?;
214                if stark_proof.version != format!("v{OPENVM_VERSION}") {
215                    eprintln!("Attempting to verify proof generated with openvm {}, but the verifier is on openvm v{OPENVM_VERSION}", stark_proof.version);
216                }
217                Sdk::verify_proof(&agg_vk, expected_app_commit, &stark_proof.try_into()?)?;
218            }
219            #[cfg(feature = "evm-verify")]
220            VerifySubCommand::Evm { proof } => {
221                use openvm_sdk::{fs::read_evm_halo2_verifier_from_folder, types::EvmProof};
222
223                let evm_verifier =
224                    read_evm_halo2_verifier_from_folder(default_evm_halo2_verifier_path())
225                        .map_err(|e| {
226                            eyre::eyre!(
227                        "Failed to read EVM verifier: {e}\nPlease run 'cargo openvm setup' first"
228                    )
229                        })?;
230
231                let proof_path = if let Some(proof) = proof {
232                    proof.clone()
233                } else {
234                    let files = get_files_with_ext(Path::new("."), "evm.proof")?;
235                    if files.len() > 1 {
236                        return Err(eyre::eyre!("multiple .evm.proof files found, please specify the path using option --proof"));
237                    } else if files.is_empty() {
238                        return Err(eyre::eyre!("no .evm.proof file found, please specify the path using option --proof"));
239                    }
240                    files[0].clone()
241                };
242                // The app config used here doesn't matter, it is ignored in verification
243                println!("Verifying EVM proof at {}", proof_path.display());
244                let evm_proof: EvmProof = read_from_file_json(proof_path).with_context(|| {
245                    format!("Proof needs to be compatible with openvm v{OPENVM_VERSION}",)
246                })?;
247                if evm_proof.version != format!("v{OPENVM_VERSION}") {
248                    eprintln!("Attempting to verify proof generated with openvm {}, but the verifier is on openvm v{OPENVM_VERSION}", evm_proof.version);
249                }
250                Sdk::verify_evm_halo2_proof(&evm_verifier, evm_proof)?;
251            }
252        }
253        println!("Proof verified successfully!");
254        Ok(())
255    }
256}