1use std::{path::PathBuf, sync::Arc};
2
3use clap::Parser;
4use eyre::Result;
5use openvm_circuit::arch::{
6 execution_mode::metered::segment_ctx::{
7 SegmentationLimits, DEFAULT_MAX_CELLS, DEFAULT_MAX_TRACE_HEIGHT_BITS,
8 },
9 instructions::exe::VmExe,
10};
11use openvm_sdk::{
12 config::{AggregationTreeConfig, AppConfig, SdkVmConfig},
13 fs::{encode_to_file, read_object_from_file, write_to_file_json},
14 keygen::AppProvingKey,
15 types::VersionedVmStarkProof,
16 Sdk, F,
17};
18
19use super::{RunArgs, RunCargoArgs};
20#[cfg(feature = "evm-prove")]
21use crate::util::read_default_agg_and_halo2_pk;
22use crate::{
23 commands::build,
24 default::default_agg_stark_pk_path,
25 input::read_to_stdin,
26 util::{get_app_pk_path, get_manifest_path_and_dir, get_single_target_name, get_target_dir},
27};
28
29#[derive(Parser)]
30#[command(name = "prove", about = "Generate a program proof")]
31pub struct ProveCmd {
32 #[command(subcommand)]
33 command: ProveSubCommand,
34}
35
36#[derive(Parser)]
37enum ProveSubCommand {
38 App {
39 #[arg(
40 long,
41 action,
42 help = "Path to app proof output, by default will be ./${bin_name}.app.proof",
43 help_heading = "Output"
44 )]
45 proof: Option<PathBuf>,
46
47 #[arg(
48 long,
49 action,
50 help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
51 help_heading = "OpenVM Options"
52 )]
53 app_pk: Option<PathBuf>,
54
55 #[command(flatten)]
56 run_args: RunArgs,
57
58 #[command(flatten)]
59 cargo_args: RunCargoArgs,
60
61 #[command(flatten)]
62 segmentation_args: SegmentationArgs,
63 },
64 Stark {
65 #[arg(
66 long,
67 action,
68 help = "Path to STARK proof output, by default will be ./${bin_name}.stark.proof",
69 help_heading = "Output"
70 )]
71 proof: Option<PathBuf>,
72
73 #[arg(
74 long,
75 action,
76 help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
77 help_heading = "OpenVM Options"
78 )]
79 app_pk: Option<PathBuf>,
80
81 #[arg(
82 long,
83 action,
84 help = "Path to aggregation proving key, by default will be ~/.openvm/agg.pk",
85 help_heading = "OpenVM Options"
86 )]
87 agg_pk: Option<PathBuf>,
88
89 #[command(flatten)]
90 run_args: RunArgs,
91
92 #[command(flatten)]
93 cargo_args: RunCargoArgs,
94
95 #[command(flatten)]
96 segmentation_args: SegmentationArgs,
97
98 #[command(flatten)]
99 agg_tree_config: AggregationTreeConfig,
100 },
101 #[cfg(feature = "evm-prove")]
102 Evm {
103 #[arg(
104 long,
105 action,
106 help = "Path to EVM proof output, by default will be ./${bin_name}.evm.proof",
107 help_heading = "Output"
108 )]
109 proof: Option<PathBuf>,
110
111 #[arg(
112 long,
113 action,
114 help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
115 help_heading = "OpenVM Options"
116 )]
117 app_pk: Option<PathBuf>,
118
119 #[command(flatten)]
120 run_args: RunArgs,
121
122 #[command(flatten)]
123 cargo_args: RunCargoArgs,
124
125 #[command(flatten)]
126 segmentation_args: SegmentationArgs,
127
128 #[command(flatten)]
129 agg_tree_config: AggregationTreeConfig,
130 },
131}
132
133#[derive(Clone, Copy, Parser)]
134pub struct SegmentationArgs {
135 #[arg(
139 long,
140 default_value_t = DEFAULT_MAX_TRACE_HEIGHT_BITS,
141 help_heading = "OpenVM Options"
142 )]
143 pub segment_max_height_bits: u8,
144 #[arg(
147 long,
148 default_value_t = DEFAULT_MAX_CELLS,
149 help_heading = "OpenVM Options"
150 )]
151 pub segment_max_cells: usize,
152}
153
154impl ProveCmd {
155 pub fn run(&self) -> Result<()> {
156 match &self.command {
157 ProveSubCommand::App {
158 app_pk,
159 proof,
160 run_args,
161 cargo_args,
162 segmentation_args,
163 } => {
164 let mut app_pk = load_app_pk(app_pk, cargo_args)?;
165 let app_config = get_app_config(&mut app_pk, segmentation_args);
166 let sdk = Sdk::new(app_config)?.with_app_pk(app_pk);
167 let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
168
169 let app_proof = sdk
170 .app_prover(exe)?
171 .prove(read_to_stdin(&run_args.input)?)?;
172
173 let proof_path = if let Some(proof) = proof {
174 proof
175 } else {
176 &PathBuf::from(target_name).with_extension("app.proof")
177 };
178 println!(
179 "App proof completed! Writing App proof to {}",
180 proof_path.display()
181 );
182 encode_to_file(proof_path, app_proof)?;
183 }
184 ProveSubCommand::Stark {
185 app_pk,
186 agg_pk,
187 proof,
188 run_args,
189 cargo_args,
190 segmentation_args,
191 agg_tree_config,
192 } => {
193 let mut app_pk = load_app_pk(app_pk, cargo_args)?;
194 let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
195
196 let agg_pk_path = agg_pk
197 .clone()
198 .unwrap_or_else(|| PathBuf::from(default_agg_stark_pk_path()));
199 let agg_pk = read_object_from_file(agg_pk_path).map_err(|e| {
200 eyre::eyre!("Failed to read aggregation proving key: {e}\nPlease run 'cargo openvm setup' first")
201 })?;
202 let app_config = get_app_config(&mut app_pk, segmentation_args);
203 let sdk = Sdk::new(app_config)?
204 .with_agg_tree_config(*agg_tree_config)
205 .with_app_pk(app_pk)
206 .with_agg_pk(agg_pk);
207 let mut prover = sdk.prover(exe)?;
208 let app_commit = prover.app_commit();
209 println!("exe commit: {:?}", app_commit.app_exe_commit.to_bn254());
210 println!("vm commit: {:?}", app_commit.app_vm_commit.to_bn254());
211
212 let stark_proof = prover.prove(read_to_stdin(&run_args.input)?)?;
213 let stark_proof_bytes = VersionedVmStarkProof::new(stark_proof)?;
214
215 let proof_path = if let Some(proof) = proof {
216 proof
217 } else {
218 &PathBuf::from(target_name).with_extension("stark.proof")
219 };
220 println!(
221 "STARK proof completed! Writing STARK proof to {}",
222 proof_path.display()
223 );
224 write_to_file_json(proof_path, stark_proof_bytes)?;
225 }
226 #[cfg(feature = "evm-prove")]
227 ProveSubCommand::Evm {
228 app_pk,
229 proof,
230 run_args,
231 cargo_args,
232 segmentation_args,
233 agg_tree_config,
234 } => {
235 let mut app_pk = load_app_pk(app_pk, cargo_args)?;
236 let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
237
238 println!("Generating EVM proof, this may take a lot of compute and memory...");
239 let (agg_pk, halo2_pk) = read_default_agg_and_halo2_pk().map_err(|e| {
240 eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e)
241 })?;
242 let app_config = get_app_config(&mut app_pk, segmentation_args);
243 let sdk = Sdk::new(app_config)?
244 .with_agg_tree_config(*agg_tree_config)
245 .with_app_pk(app_pk)
246 .with_agg_pk(agg_pk)
247 .with_halo2_pk(halo2_pk);
248 let mut prover = sdk.evm_prover(exe)?;
249 let app_commit = prover.stark_prover.app_commit();
250 println!("exe commit: {:?}", app_commit.app_exe_commit.to_bn254());
251 println!("vm commit: {:?}", app_commit.app_vm_commit.to_bn254());
252 let evm_proof = prover.prove_evm(read_to_stdin(&run_args.input)?)?;
253
254 let proof_path = if let Some(proof) = proof {
255 proof
256 } else {
257 &PathBuf::from(target_name).with_extension("evm.proof")
258 };
259 println!(
260 "EVM proof completed! Writing EVM proof to {}",
261 proof_path.display()
262 );
263 write_to_file_json(proof_path, evm_proof)?;
264 }
265 }
266 Ok(())
267 }
268}
269
270pub(crate) fn load_app_pk(
271 app_pk: &Option<PathBuf>,
272 cargo_args: &RunCargoArgs,
273) -> Result<AppProvingKey<SdkVmConfig>> {
274 let app_pk_path = if let Some(app_pk) = app_pk {
275 app_pk.to_path_buf()
276 } else {
277 let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?;
278 let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path);
279 get_app_pk_path(&target_dir)
280 };
281
282 read_object_from_file(app_pk_path)
283}
284
285pub(crate) fn load_or_build_exe(
288 run_args: &RunArgs,
289 cargo_args: &RunCargoArgs,
290) -> Result<(VmExe<F>, String)> {
291 let exe_path = if let Some(exe) = &run_args.exe {
292 exe
293 } else {
294 let target_name = get_single_target_name(cargo_args)?;
296 let build_args = run_args.clone().into();
297 let cargo_args = cargo_args.clone().into();
298 let output_dir = build(&build_args, &cargo_args)?;
299 &output_dir.join(target_name.with_extension("vmexe"))
300 };
301
302 let app_exe = read_object_from_file(exe_path)?;
303 Ok((
304 app_exe,
305 exe_path.file_stem().unwrap().to_string_lossy().into_owned(),
306 ))
307}
308
309fn get_app_config(
312 app_pk: &mut AppProvingKey<SdkVmConfig>,
313 segmentation_args: &SegmentationArgs,
314) -> AppConfig<SdkVmConfig> {
315 Arc::get_mut(&mut app_pk.app_vm_pk)
316 .unwrap()
317 .vm_config
318 .system
319 .config
320 .set_segmentation_limits((*segmentation_args).into());
321 app_pk.app_config()
322}
323
324impl From<SegmentationArgs> for SegmentationLimits {
325 fn from(args: SegmentationArgs) -> Self {
326 SegmentationLimits {
327 max_trace_height: 1u32
328 .checked_shl(args.segment_max_height_bits as u32)
329 .expect("segment_max_height_bits too large"),
330 max_cells: args.segment_max_cells,
331 ..Default::default()
332 }
333 }
334}