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 #[command(flatten)]
82 run_args: RunArgs,
83
84 #[command(flatten)]
85 cargo_args: RunCargoArgs,
86
87 #[command(flatten)]
88 segmentation_args: SegmentationArgs,
89
90 #[command(flatten)]
91 agg_tree_config: AggregationTreeConfig,
92 },
93 #[cfg(feature = "evm-prove")]
94 Evm {
95 #[arg(
96 long,
97 action,
98 help = "Path to EVM proof output, by default will be ./${bin_name}.evm.proof",
99 help_heading = "Output"
100 )]
101 proof: Option<PathBuf>,
102
103 #[arg(
104 long,
105 action,
106 help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk",
107 help_heading = "OpenVM Options"
108 )]
109 app_pk: Option<PathBuf>,
110
111 #[command(flatten)]
112 run_args: RunArgs,
113
114 #[command(flatten)]
115 cargo_args: RunCargoArgs,
116
117 #[command(flatten)]
118 segmentation_args: SegmentationArgs,
119
120 #[command(flatten)]
121 agg_tree_config: AggregationTreeConfig,
122 },
123}
124
125#[derive(Clone, Copy, Parser)]
126pub struct SegmentationArgs {
127 #[arg(
131 long,
132 default_value_t = DEFAULT_MAX_TRACE_HEIGHT_BITS,
133 help_heading = "OpenVM Options"
134 )]
135 pub segment_max_height_bits: u8,
136 #[arg(
139 long,
140 default_value_t = DEFAULT_MAX_CELLS,
141 help_heading = "OpenVM Options"
142 )]
143 pub segment_max_cells: usize,
144}
145
146impl ProveCmd {
147 pub fn run(&self) -> Result<()> {
148 match &self.command {
149 ProveSubCommand::App {
150 app_pk,
151 proof,
152 run_args,
153 cargo_args,
154 segmentation_args,
155 } => {
156 let mut app_pk = load_app_pk(app_pk, cargo_args)?;
157 let app_config = get_app_config(&mut app_pk, segmentation_args);
158 let sdk = Sdk::new(app_config)?.with_app_pk(app_pk);
159 let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
160
161 let app_proof = sdk
162 .app_prover(exe)?
163 .prove(read_to_stdin(&run_args.input)?)?;
164
165 let proof_path = if let Some(proof) = proof {
166 proof
167 } else {
168 &PathBuf::from(target_name).with_extension("app.proof")
169 };
170 println!(
171 "App proof completed! Writing App proof to {}",
172 proof_path.display()
173 );
174 encode_to_file(proof_path, app_proof)?;
175 }
176 ProveSubCommand::Stark {
177 app_pk,
178 proof,
179 run_args,
180 cargo_args,
181 segmentation_args,
182 agg_tree_config,
183 } => {
184 let mut app_pk = load_app_pk(app_pk, cargo_args)?;
185 let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
186
187 let agg_pk = read_object_from_file(default_agg_stark_pk_path()).map_err(|e| {
188 eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e)
189 })?;
190 let app_config = get_app_config(&mut app_pk, segmentation_args);
191 let sdk = Sdk::new(app_config)?
192 .with_agg_tree_config(*agg_tree_config)
193 .with_app_pk(app_pk)
194 .with_agg_pk(agg_pk);
195 let mut prover = sdk.prover(exe)?;
196 let app_commit = 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
200 let stark_proof = prover.prove(read_to_stdin(&run_args.input)?)?;
201 let stark_proof_bytes = VersionedVmStarkProof::new(stark_proof)?;
202
203 let proof_path = if let Some(proof) = proof {
204 proof
205 } else {
206 &PathBuf::from(target_name).with_extension("stark.proof")
207 };
208 println!(
209 "STARK proof completed! Writing STARK proof to {}",
210 proof_path.display()
211 );
212 write_to_file_json(proof_path, stark_proof_bytes)?;
213 }
214 #[cfg(feature = "evm-prove")]
215 ProveSubCommand::Evm {
216 app_pk,
217 proof,
218 run_args,
219 cargo_args,
220 segmentation_args,
221 agg_tree_config,
222 } => {
223 let mut app_pk = load_app_pk(app_pk, cargo_args)?;
224 let (exe, target_name) = load_or_build_exe(run_args, cargo_args)?;
225
226 println!("Generating EVM proof, this may take a lot of compute and memory...");
227 let (agg_pk, halo2_pk) = read_default_agg_and_halo2_pk().map_err(|e| {
228 eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e)
229 })?;
230 let app_config = get_app_config(&mut app_pk, segmentation_args);
231 let sdk = Sdk::new(app_config)?
232 .with_agg_tree_config(*agg_tree_config)
233 .with_app_pk(app_pk)
234 .with_agg_pk(agg_pk)
235 .with_halo2_pk(halo2_pk);
236 let mut prover = sdk.evm_prover(exe)?;
237 let app_commit = prover.stark_prover.app_commit();
238 println!("exe commit: {:?}", app_commit.app_exe_commit.to_bn254());
239 println!("vm commit: {:?}", app_commit.app_vm_commit.to_bn254());
240 let evm_proof = prover.prove_evm(read_to_stdin(&run_args.input)?)?;
241
242 let proof_path = if let Some(proof) = proof {
243 proof
244 } else {
245 &PathBuf::from(target_name).with_extension("evm.proof")
246 };
247 println!(
248 "EVM proof completed! Writing EVM proof to {}",
249 proof_path.display()
250 );
251 write_to_file_json(proof_path, evm_proof)?;
252 }
253 }
254 Ok(())
255 }
256}
257
258pub(crate) fn load_app_pk(
259 app_pk: &Option<PathBuf>,
260 cargo_args: &RunCargoArgs,
261) -> Result<AppProvingKey<SdkVmConfig>> {
262 let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?;
263 let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path);
264
265 let app_pk_path = if let Some(app_pk) = app_pk {
266 app_pk.to_path_buf()
267 } else {
268 get_app_pk_path(&target_dir)
269 };
270
271 read_object_from_file(app_pk_path)
272}
273
274pub(crate) fn load_or_build_exe(
277 run_args: &RunArgs,
278 cargo_args: &RunCargoArgs,
279) -> Result<(VmExe<F>, String)> {
280 let exe_path = if let Some(exe) = &run_args.exe {
281 exe
282 } else {
283 let target_name = get_single_target_name(cargo_args)?;
285 let build_args = run_args.clone().into();
286 let cargo_args = cargo_args.clone().into();
287 let output_dir = build(&build_args, &cargo_args)?;
288 &output_dir.join(target_name.with_extension("vmexe"))
289 };
290
291 let app_exe = read_object_from_file(exe_path)?;
292 Ok((
293 app_exe,
294 exe_path.file_stem().unwrap().to_string_lossy().into_owned(),
295 ))
296}
297
298fn get_app_config(
301 app_pk: &mut AppProvingKey<SdkVmConfig>,
302 segmentation_args: &SegmentationArgs,
303) -> AppConfig<SdkVmConfig> {
304 Arc::get_mut(&mut app_pk.app_vm_pk)
305 .unwrap()
306 .vm_config
307 .system
308 .config
309 .set_segmentation_limits((*segmentation_args).into());
310 app_pk.app_config()
311}
312
313impl From<SegmentationArgs> for SegmentationLimits {
314 fn from(args: SegmentationArgs) -> Self {
315 SegmentationLimits {
316 max_trace_height: 1u32
317 .checked_shl(args.segment_max_height_bits as u32)
318 .expect("segment_max_height_bits too large"),
319 max_cells: args.segment_max_cells,
320 ..Default::default()
321 }
322 }
323}