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
233pub(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 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}