cargo_openvm/commands/
prove.rs

1use std::path::PathBuf;
2
3use clap::Parser;
4use eyre::Result;
5use openvm_circuit::arch::instructions::exe::VmExe;
6use openvm_sdk::{
7    config::{AggregationTreeConfig, SdkVmConfig},
8    fs::{encode_to_file, read_object_from_file, write_to_file_json},
9    keygen::AppProvingKey,
10    types::VersionedVmStarkProof,
11    Sdk, F,
12};
13
14use super::{RunArgs, RunCargoArgs};
15#[cfg(feature = "evm-prove")]
16use crate::util::read_default_agg_and_halo2_pk;
17use crate::{
18    commands::build,
19    default::default_agg_stark_pk_path,
20    input::read_to_stdin,
21    util::{get_app_pk_path, get_manifest_path_and_dir, get_single_target_name, get_target_dir},
22};
23
24#[derive(Parser)]
25#[command(name = "prove", about = "Generate a program proof")]
26pub struct ProveCmd {
27    #[command(subcommand)]
28    command: ProveSubCommand,
29}
30
31#[derive(Parser)]
32enum ProveSubCommand {
33    App {
34        #[arg(
35            long,
36            action,
37            help = "Path to app proof output, by default will be ./${bin_name}.app.proof",
38            help_heading = "Output"
39        )]
40        proof: Option<PathBuf>,
41
42        #[arg(
43            long,
44            action,
45            help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
46            help_heading = "OpenVM Options"
47        )]
48        app_pk: Option<PathBuf>,
49
50        #[command(flatten)]
51        run_args: RunArgs,
52
53        #[command(flatten)]
54        cargo_args: RunCargoArgs,
55    },
56    Stark {
57        #[arg(
58            long,
59            action,
60            help = "Path to STARK proof output, by default will be ./${bin_name}.stark.proof",
61            help_heading = "Output"
62        )]
63        proof: Option<PathBuf>,
64
65        #[arg(
66            long,
67            action,
68            help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
69            help_heading = "OpenVM Options"
70        )]
71        app_pk: Option<PathBuf>,
72
73        #[command(flatten)]
74        run_args: RunArgs,
75
76        #[command(flatten)]
77        cargo_args: RunCargoArgs,
78
79        #[command(flatten)]
80        agg_tree_config: AggregationTreeConfig,
81    },
82    #[cfg(feature = "evm-prove")]
83    Evm {
84        #[arg(
85            long,
86            action,
87            help = "Path to EVM proof output, by default will be ./${bin_name}.evm.proof",
88            help_heading = "Output"
89        )]
90        proof: Option<PathBuf>,
91
92        #[arg(
93            long,
94            action,
95            help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
96            help_heading = "OpenVM Options"
97        )]
98        app_pk: Option<PathBuf>,
99
100        #[command(flatten)]
101        run_args: RunArgs,
102
103        #[command(flatten)]
104        cargo_args: RunCargoArgs,
105
106        #[command(flatten)]
107        agg_tree_config: AggregationTreeConfig,
108    },
109}
110
111impl ProveCmd {
112    pub fn run(&self) -> Result<()> {
113        match &self.command {
114            ProveSubCommand::App {
115                app_pk,
116                proof,
117                run_args,
118                cargo_args,
119            } => {
120                let app_pk = load_app_pk(app_pk, cargo_args)?;
121                let sdk = Sdk::new(app_pk.app_config())?.with_app_pk(app_pk);
122                let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
123
124                let app_proof = sdk
125                    .app_prover(exe)?
126                    .prove(read_to_stdin(&run_args.input)?)?;
127
128                let proof_path = if let Some(proof) = proof {
129                    proof
130                } else {
131                    &PathBuf::from(target_name).with_extension("app.proof")
132                };
133                println!(
134                    "App proof completed! Writing App proof to {}",
135                    proof_path.display()
136                );
137                encode_to_file(proof_path, app_proof)?;
138            }
139            ProveSubCommand::Stark {
140                app_pk,
141                proof,
142                run_args,
143                cargo_args,
144                agg_tree_config,
145            } => {
146                let app_pk = load_app_pk(app_pk, cargo_args)?;
147                let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
148
149                let agg_pk = read_object_from_file(default_agg_stark_pk_path()).map_err(|e| {
150                    eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e)
151                })?;
152                let sdk = Sdk::new(app_pk.app_config())?
153                    .with_agg_tree_config(*agg_tree_config)
154                    .with_app_pk(app_pk)
155                    .with_agg_pk(agg_pk);
156                let mut prover = sdk.prover(exe)?;
157                let app_commit = prover.app_commit();
158                println!("exe commit: {:?}", app_commit.app_exe_commit.to_bn254());
159                println!("vm commit: {:?}", app_commit.app_vm_commit.to_bn254());
160
161                let stark_proof = prover.prove(read_to_stdin(&run_args.input)?)?;
162                let stark_proof_bytes = VersionedVmStarkProof::new(stark_proof)?;
163
164                let proof_path = if let Some(proof) = proof {
165                    proof
166                } else {
167                    &PathBuf::from(target_name).with_extension("stark.proof")
168                };
169                println!(
170                    "STARK proof completed! Writing STARK proof to {}",
171                    proof_path.display()
172                );
173                write_to_file_json(proof_path, stark_proof_bytes)?;
174            }
175            #[cfg(feature = "evm-prove")]
176            ProveSubCommand::Evm {
177                app_pk,
178                proof,
179                run_args,
180                cargo_args,
181                agg_tree_config,
182            } => {
183                let app_pk = load_app_pk(app_pk, cargo_args)?;
184                let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
185
186                println!("Generating EVM proof, this may take a lot of compute and memory...");
187                let (agg_pk, halo2_pk) = read_default_agg_and_halo2_pk().map_err(|e| {
188                    eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e)
189                })?;
190                let sdk = Sdk::new(app_pk.app_config())?
191                    .with_agg_tree_config(*agg_tree_config)
192                    .with_app_pk(app_pk)
193                    .with_agg_pk(agg_pk)
194                    .with_halo2_pk(halo2_pk);
195                let mut prover = sdk.evm_prover(exe)?;
196                let app_commit = prover.stark_prover.app_commit();
197                println!("exe commit: {:?}", app_commit.app_exe_commit.to_bn254());
198                println!("vm commit: {:?}", app_commit.app_vm_commit.to_bn254());
199                let evm_proof = prover.prove_evm(read_to_stdin(&run_args.input)?)?;
200
201                let proof_path = if let Some(proof) = proof {
202                    proof
203                } else {
204                    &PathBuf::from(target_name).with_extension("evm.proof")
205                };
206                println!(
207                    "EVM proof completed! Writing EVM proof to {}",
208                    proof_path.display()
209                );
210                write_to_file_json(proof_path, evm_proof)?;
211            }
212        }
213        Ok(())
214    }
215}
216
217pub(crate) fn load_app_pk(
218    app_pk: &Option<PathBuf>,
219    cargo_args: &RunCargoArgs,
220) -> Result<AppProvingKey<SdkVmConfig>> {
221    let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?;
222    let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path);
223
224    let app_pk_path = if let Some(app_pk) = app_pk {
225        app_pk.to_path_buf()
226    } else {
227        get_app_pk_path(&target_dir)
228    };
229
230    read_object_from_file(app_pk_path)
231}
232
233/// Returns `(exe, target_name.file_stem())` where target_name has no extension and only contains
234/// the file stem (in particular it does not include `examples/` if the target was an example)
235pub(crate) fn load_or_build_exe(
236    run_args: &RunArgs,
237    cargo_args: &RunCargoArgs,
238) -> Result<(VmExe<F>, String)> {
239    let exe_path = if let Some(exe) = &run_args.exe {
240        exe
241    } else {
242        // Build and get the executable name
243        let target_name = get_single_target_name(cargo_args)?;
244        let build_args = run_args.clone().into();
245        let cargo_args = cargo_args.clone().into();
246        let output_dir = build(&build_args, &cargo_args)?;
247        &output_dir.join(target_name.with_extension("vmexe"))
248    };
249
250    let app_exe = read_object_from_file(exe_path)?;
251    Ok((
252        app_exe,
253        exe_path.file_stem().unwrap().to_string_lossy().into_owned(),
254    ))
255}