1use std::{fs, path::Path};
4
5use anyhow::Result;
6use binius_core::constraint_system::{ConstraintSystem, Proof, ValueVec, ValuesData, ValuesRef};
7use binius_frontend::{CircuitBuilder, CircuitStat};
8use binius_hash::{Blake3HashSuite, StdHashSuite};
9use binius_hash_prover::ParallelHashSuite;
10use binius_utils::serialization::{DeserializeBytes, SerializeBytes};
11use clap::{Arg, ArgMatches, Args, Command, FromArgMatches};
12use digest::Output;
13
14use crate::{
15 ExampleCircuit, HashSuiteType, check_proof, check_proof_zk, create_proof, create_proof_zk,
16 prove_verify, setup, setup_verifier, setup_zk, setup_zk_verifier,
17};
18
19fn write_file(path: &str, bytes: &[u8]) -> Result<()> {
24 if let Some(parent) = Path::new(path).parent()
26 && !parent.as_os_str().is_empty()
27 {
28 fs::create_dir_all(parent).map_err(|e| {
29 anyhow::anyhow!("Failed to create directory '{}': {}", parent.display(), e)
30 })?;
31 }
32 fs::write(path, bytes)
33 .map_err(|e| anyhow::anyhow!("Failed to write serialized data to '{}': {}", path, e))
34}
35
36fn write_serialized<T: SerializeBytes>(value: &T, path: &str) -> Result<()> {
38 let mut buf: Vec<u8> = Vec::new();
39 value.serialize(&mut buf)?;
40 write_file(path, &buf)
41}
42
43fn read_deserialized<T: DeserializeBytes>(path: &str) -> Result<T> {
45 let buf =
46 fs::read(path).map_err(|e| anyhow::anyhow!("Failed to read file '{}': {}", path, e))?;
47 T::deserialize(buf.as_slice())
48 .map_err(|e| anyhow::anyhow!("Failed to deserialize data from '{}': {}", path, e))
49}
50
51fn maybe_write_proof(proof_bytes: &[u8], output: Option<&str>) -> Result<()> {
53 tracing::info!("Proof size: {} KiB", proof_bytes.len() / 1024);
54 if let Some(path) = output {
55 use binius_verifier::config::{ChallengerWithName, StdChallenger};
56 let proof = Proof::owned(proof_bytes.to_vec(), StdChallenger::NAME.to_string());
57 write_serialized(&proof, path)?;
58 tracing::info!("Proof written to '{}'", path);
59 }
60 Ok(())
61}
62
63fn prove_with_hash_suite<H>(
65 cs: ConstraintSystem,
66 log_inv_rate: usize,
67 zk: bool,
68 message: Option<&[u8]>,
69 witness: &ValueVec,
70 output: Option<&str>,
71) -> Result<()>
72where
73 H: ParallelHashSuite + Clone,
74 Output<H::LeafHash>: SerializeBytes + DeserializeBytes,
75{
76 if zk {
77 let (verifier, prover) = setup_zk::<H>(cs, log_inv_rate)?;
78 let proof_bytes = create_proof_zk(&prover, witness, message)?;
79 maybe_write_proof(&proof_bytes, output)?;
80 check_proof_zk(&verifier, witness, proof_bytes, message)?;
81 } else {
82 let (verifier, prover) = setup::<H>(cs, log_inv_rate, None)?;
83 let proof_bytes = create_proof(&prover, witness)?;
84 maybe_write_proof(&proof_bytes, output)?;
85 check_proof(&verifier, witness, proof_bytes)?;
86 }
87 Ok(())
88}
89
90fn verify_with_hash_suite<H>(
92 cs: ConstraintSystem,
93 log_inv_rate: usize,
94 zk: bool,
95 message: Option<&[u8]>,
96 witness: &ValueVec,
97 proof_bytes: Vec<u8>,
98) -> Result<()>
99where
100 H: ParallelHashSuite + Clone,
101 Output<H::LeafHash>: SerializeBytes + DeserializeBytes,
102{
103 if zk {
104 let verifier = setup_zk_verifier::<H>(cs, log_inv_rate)?;
105 check_proof_zk(&verifier, witness, proof_bytes, message)?;
106 } else {
107 let verifier = setup_verifier::<H>(cs, log_inv_rate)?;
108 check_proof(&verifier, witness, proof_bytes)?;
109 }
110 Ok(())
111}
112
113fn prove_dispatch(
118 cs: ConstraintSystem,
119 log_inv_rate: usize,
120 hash_suite: HashSuiteType,
121 zk: bool,
122 message: Option<&[u8]>,
123 witness: &ValueVec,
124 output: Option<&str>,
125) -> Result<()> {
126 match hash_suite {
127 HashSuiteType::Sha256 => {
128 tracing::info!("Using SHA-256 hash suite for Merkle tree");
129 prove_with_hash_suite::<StdHashSuite>(cs, log_inv_rate, zk, message, witness, output)
130 }
131 HashSuiteType::Blake3 => {
132 tracing::info!("Using Blake3 hash suite for Merkle tree");
133 prove_with_hash_suite::<Blake3HashSuite>(cs, log_inv_rate, zk, message, witness, output)
134 }
135 }
136}
137
138fn verify_dispatch(
142 cs: ConstraintSystem,
143 log_inv_rate: usize,
144 hash_suite: HashSuiteType,
145 zk: bool,
146 message: Option<&[u8]>,
147 witness: &ValueVec,
148 proof_bytes: Vec<u8>,
149) -> Result<()> {
150 match hash_suite {
151 HashSuiteType::Sha256 => {
152 tracing::info!("Using SHA-256 hash suite for Merkle tree");
153 verify_with_hash_suite::<StdHashSuite>(
154 cs,
155 log_inv_rate,
156 zk,
157 message,
158 witness,
159 proof_bytes,
160 )
161 }
162 HashSuiteType::Blake3 => {
163 tracing::info!("Using Blake3 hash suite for Merkle tree");
164 verify_with_hash_suite::<Blake3HashSuite>(
165 cs,
166 log_inv_rate,
167 zk,
168 message,
169 witness,
170 proof_bytes,
171 )
172 }
173 }
174}
175
176fn save_artifacts(
180 cs: &ConstraintSystem,
181 witness: &ValueVec,
182 cs_path: Option<&str>,
183 pub_witness_path: Option<&str>,
184 non_pub_data_path: Option<&str>,
185 key_collection_path: Option<&str>,
186) -> Result<()> {
187 if let Some(path) = cs_path {
188 write_serialized(cs, path)?;
189 tracing::info!("Constraint system saved to '{}'", path);
190 }
191
192 if let Some(path) = pub_witness_path {
193 write_serialized(&ValuesRef::new(witness.inout()), path)?;
195 tracing::info!("Inout witness saved to '{}'", path);
196 }
197
198 if let Some(path) = non_pub_data_path {
199 write_serialized(&ValuesRef::new(witness.non_public()), path)?;
200 tracing::info!("Non-public witness saved to '{}'", path);
201 }
202
203 if let Some(path) = key_collection_path {
204 let key_collection_scope = tracing::info_span!("Building key collection").entered();
205 let key_collection = binius_prover::protocols::shift::KeyCollection::build(
206 cs,
207 binius_core::constraint_system::InoutSegment::Public,
208 );
209 drop(key_collection_scope);
210 write_serialized(&key_collection, path)?;
211 tracing::info!("Key collection saved to '{}'", path);
212 }
213
214 Ok(())
215}
216
217fn run_load_prove(matches: &ArgMatches) -> Result<()> {
222 let cs_path = matches
224 .get_one::<String>("cs_path")
225 .expect("cs_path is required");
226 let pub_witness_path = matches
227 .get_one::<String>("pub_witness_path")
228 .expect("pub_witness_path is required");
229 let non_pub_data_path = matches
230 .get_one::<String>("non_pub_data_path")
231 .expect("non_pub_data_path is required");
232 let key_collection_path = matches.get_one::<String>("key_collection_path").cloned();
233 let log_inv_rate = *matches
234 .get_one::<u32>("log_inv_rate")
235 .expect("has default value");
236 let hash_suite = *matches
237 .get_one::<HashSuiteType>("hash_suite")
238 .expect("has default value");
239
240 let cs_load_scope = tracing::info_span!("Loading constraint system").entered();
242 let cs: ConstraintSystem = read_deserialized(cs_path)?;
243 tracing::info!("Constraint system loaded from '{}'", cs_path);
244 drop(cs_load_scope);
245
246 let maybe_key_collection = key_collection_path
248 .map(|kc_path| -> Result<_> {
249 let kc_load_scope = tracing::info_span!("Loading key collection").entered();
250 let key_collection: binius_prover::KeyCollection = read_deserialized(&kc_path)?;
251 tracing::info!("Key collection loaded from '{kc_path}'");
252 drop(kc_load_scope);
253 Ok(key_collection)
254 })
255 .transpose()?;
256
257 let witness_load_scope = tracing::info_span!("Loading witness data").entered();
259 let inout: ValuesData = read_deserialized(pub_witness_path)?;
260 tracing::info!("Public inout values loaded from '{}'", pub_witness_path);
261
262 let non_pub_data: ValuesData = read_deserialized(non_pub_data_path)?;
263 tracing::info!("Non-public data loaded from '{}'", non_pub_data_path);
264
265 let witness = cs.value_vec_from_data(&inout, &non_pub_data);
267 drop(witness_load_scope);
268
269 match hash_suite {
270 HashSuiteType::Sha256 => {
271 tracing::info!("Using SHA-256 hash suite for Merkle tree");
272 let (verifier, prover) =
273 setup::<StdHashSuite>(cs, log_inv_rate as usize, maybe_key_collection)?;
274 prove_verify(&verifier, &prover, &witness)?;
275 }
276 HashSuiteType::Blake3 => {
277 tracing::info!("Using Blake3 hash suite for Merkle tree");
278 let (verifier, prover) =
279 setup::<Blake3HashSuite>(cs, log_inv_rate as usize, maybe_key_collection)?;
280 prove_verify(&verifier, &prover, &witness)?;
281 }
282 };
283
284 Ok(())
285}
286
287pub struct Cli {
303 command: Command,
304 runners: Vec<(&'static str, Runner)>,
305}
306
307type Runner = fn(&ArgMatches, &str) -> Result<()>;
309
310impl Cli {
311 pub fn new(name: &'static str) -> Self {
313 Self {
314 command: Command::new(name)
315 .subcommand_required(true)
316 .arg_required_else_help(true),
317 runners: Vec::new(),
318 }
319 }
320
321 pub fn circuit<E: ExampleCircuit>(mut self, name: &'static str, about: &'static str) -> Self {
323 self.command = self
324 .command
325 .subcommand(build_command::<E>(name).about(about));
326 self.runners.push((name, run::<E>));
327 self
328 }
329
330 pub fn run(self) -> Result<()> {
332 let matches = self.command.get_matches();
333 let (name, sub_matches) = matches.subcommand().expect("subcommand is required");
334 let (_, run) = self
335 .runners
336 .iter()
337 .find(|(registered, _)| *registered == name)
338 .expect("clap only accepts a registered subcommand");
339 run(sub_matches, name)
340 }
341}
342
343fn build_command<E: ExampleCircuit>(name: &'static str) -> Command {
348 let command = Command::new(name)
349 .subcommand_required(false)
350 .arg_required_else_help(false);
351
352 let prove_cmd = build_prove_subcommand::<E>();
354 let stat_cmd = build_stat_subcommand::<E>();
355 let composition_cmd = build_composition_subcommand::<E>();
356 let check_snapshot_cmd = build_check_snapshot_subcommand::<E>();
357 let bless_snapshot_cmd = build_bless_snapshot_subcommand::<E>();
358 let save_cmd = build_save_subcommand::<E>();
359 let load_prove_cmd = build_load_prove_subcommand();
360 let verify_cmd = build_verify_subcommand::<E>();
361
362 let command = command
363 .subcommand(prove_cmd)
364 .subcommand(stat_cmd)
365 .subcommand(composition_cmd)
366 .subcommand(check_snapshot_cmd)
367 .subcommand(bless_snapshot_cmd)
368 .subcommand(save_cmd)
369 .subcommand(load_prove_cmd)
370 .subcommand(verify_cmd);
371
372 let command = command
374 .arg(
375 Arg::new("log_inv_rate")
376 .short('l')
377 .long("log-inv-rate")
378 .value_name("RATE")
379 .help("Log of the inverse rate for the proof system")
380 .default_value("1")
381 .value_parser(clap::value_parser!(u32).range(1..)),
382 )
383 .arg(
384 Arg::new("hash_suite")
385 .short('c')
386 .long("hash-suite")
387 .alias("compression")
388 .value_name("SUITE")
389 .help("Merkle hash suite to use (leaf hash + inner-node compression)")
390 .value_parser(clap::value_parser!(HashSuiteType))
391 .default_value("sha256"),
392 )
393 .arg(
394 Arg::new("zk")
395 .long("zk")
396 .help("Use the zero-knowledge proving config")
397 .action(clap::ArgAction::SetTrue),
398 )
399 .arg(
400 Arg::new("sign_message")
401 .long("sign-message")
402 .value_name("MESSAGE")
403 .requires("zk")
404 .help(
405 "Produce a zero-knowledge signature of knowledge over this message \
406 instead of a plain proof of knowledge (requires --zk)",
407 ),
408 )
409 .arg(
410 Arg::new("output")
411 .short('o')
412 .long("output")
413 .value_name("PATH")
414 .help("Write the serialized proof to this file"),
415 );
416
417 let command = E::Params::augment_args(command);
419 E::Instance::augment_args(command)
420}
421
422fn build_prove_subcommand<E: ExampleCircuit>() -> Command {
423 let mut cmd = Command::new("prove")
424 .about("Generate and verify a proof")
425 .arg(
426 Arg::new("log_inv_rate")
427 .short('l')
428 .long("log-inv-rate")
429 .value_name("RATE")
430 .help("Log of the inverse rate for the proof system")
431 .default_value("1")
432 .value_parser(clap::value_parser!(u32).range(1..)),
433 )
434 .arg(
435 Arg::new("hash_suite")
436 .short('c')
437 .long("hash-suite")
438 .alias("compression")
439 .value_name("SUITE")
440 .help("Merkle hash suite to use (leaf hash + inner-node compression)")
441 .value_parser(clap::value_parser!(HashSuiteType))
442 .default_value("sha256"),
443 )
444 .arg(
445 Arg::new("zk")
446 .long("zk")
447 .help("Use the zero-knowledge proving config")
448 .action(clap::ArgAction::SetTrue),
449 )
450 .arg(
451 Arg::new("sign_message")
452 .long("sign-message")
453 .value_name("MESSAGE")
454 .requires("zk")
455 .help(
456 "Produce a zero-knowledge signature of knowledge over this message \
457 instead of a plain proof of knowledge (requires --zk)",
458 ),
459 )
460 .arg(
461 Arg::new("output")
462 .short('o')
463 .long("output")
464 .value_name("PATH")
465 .help("Write the serialized proof to this file"),
466 );
467 cmd = E::Params::augment_args(cmd);
468 cmd = E::Instance::augment_args(cmd);
469 cmd
470}
471
472fn build_stat_subcommand<E: ExampleCircuit>() -> Command {
473 let cmd = Command::new("stat").about("Display circuit statistics");
474 E::Params::augment_args(cmd)
475}
476
477fn build_composition_subcommand<E: ExampleCircuit>() -> Command {
478 let cmd = Command::new("composition").about("Output circuit composition in JSON format");
479 E::Params::augment_args(cmd)
480}
481
482fn build_check_snapshot_subcommand<E: ExampleCircuit>() -> Command {
483 let cmd = Command::new("check-snapshot").about("Verify circuit statistics against a snapshot");
484 E::Params::augment_args(cmd)
485}
486
487fn build_bless_snapshot_subcommand<E: ExampleCircuit>() -> Command {
488 let cmd = Command::new("bless-snapshot").about("Update the snapshot with current statistics");
489 E::Params::augment_args(cmd)
490}
491
492fn build_save_subcommand<E: ExampleCircuit>() -> Command {
493 let mut cmd = Command::new("save").about(
494 "Save constraint system, public inout values, non-public data, and key collection to files if paths are provided",
495 );
496 cmd = cmd
497 .arg(
498 Arg::new("cs_path")
499 .long("cs-path")
500 .value_name("PATH")
501 .help("Output path for the constraint system binary"),
502 )
503 .arg(
504 Arg::new("pub_witness_path")
505 .long("pub-witness-path")
506 .value_name("PATH")
507 .help("Output path for the public inout values binary"),
508 )
509 .arg(
510 Arg::new("non_pub_data_path")
511 .long("non-pub-data-path")
512 .value_name("PATH")
513 .help("Output path for the non-public data (witness + internal) binary"),
514 )
515 .arg(
516 Arg::new("key_collection_path")
517 .long("key-collection-path")
518 .value_name("PATH")
519 .help("Output path for the key collection binary (for fast prover setup)"),
520 );
521 cmd = E::Params::augment_args(cmd);
522 cmd = E::Instance::augment_args(cmd);
523 cmd
524}
525
526fn build_load_prove_subcommand() -> Command {
527 Command::new("load-prove")
528 .about("Load constraint system, witness data, and optionally key collection from files and generate/verify proof")
529 .arg(
530 Arg::new("cs_path")
531 .long("cs-path")
532 .value_name("PATH")
533 .help("Input path for the constraint system binary")
534 .required(true),
535 )
536 .arg(
537 Arg::new("pub_witness_path")
538 .long("pub-witness-path")
539 .value_name("PATH")
540 .help("Input path for the public inout values binary")
541 .required(true),
542 )
543 .arg(
544 Arg::new("non_pub_data_path")
545 .long("non-pub-data-path")
546 .value_name("PATH")
547 .help("Input path for the non-public data (witness + internal) binary")
548 .required(true),
549 )
550 .arg(
551 Arg::new("key_collection_path")
552 .long("key-collection-path")
553 .value_name("PATH")
554 .help("Input path for the key collection binary (optional, for fast prover setup)"),
555 )
556 .arg(
557 Arg::new("log_inv_rate")
558 .short('l')
559 .long("log-inv-rate")
560 .value_name("RATE")
561 .help("Log of the inverse rate for the proof system")
562 .default_value("1")
563 .value_parser(clap::value_parser!(u32).range(1..)),
564 )
565 .arg(
566 Arg::new("hash_suite")
567 .short('c')
568 .long("hash-suite")
569 .alias("compression")
570 .value_name("SUITE")
571 .help("Merkle hash suite to use (leaf hash + inner-node compression)")
572 .value_parser(clap::value_parser!(HashSuiteType))
573 .default_value("sha256"),
574 )
575}
576
577fn build_verify_subcommand<E: ExampleCircuit>() -> Command {
578 let mut cmd = Command::new("verify")
579 .about("Verify a proof read from a file")
580 .arg(
581 Arg::new("proof_file")
582 .value_name("PROOF_FILE")
583 .help("Path to the proof file to verify")
584 .required(true),
585 )
586 .arg(
587 Arg::new("log_inv_rate")
588 .short('l')
589 .long("log-inv-rate")
590 .value_name("RATE")
591 .help("Log of the inverse rate for the proof system")
592 .default_value("1")
593 .value_parser(clap::value_parser!(u32).range(1..)),
594 )
595 .arg(
596 Arg::new("hash_suite")
597 .short('c')
598 .long("hash-suite")
599 .alias("compression")
600 .value_name("SUITE")
601 .help("Merkle hash suite to use (leaf hash + inner-node compression)")
602 .value_parser(clap::value_parser!(HashSuiteType))
603 .default_value("sha256"),
604 )
605 .arg(
606 Arg::new("zk")
607 .long("zk")
608 .help("Use the zero-knowledge verifier config")
609 .action(clap::ArgAction::SetTrue),
610 )
611 .arg(
612 Arg::new("sign_message")
613 .long("sign-message")
614 .value_name("MESSAGE")
615 .requires("zk")
616 .help(
617 "Verify a zero-knowledge signature of knowledge over this message \
618 (requires --zk)",
619 ),
620 );
621 cmd = E::Params::augment_args(cmd);
622 cmd = E::Instance::augment_args(cmd);
623 cmd
624}
625#[allow(unused_variables)]
627fn run<E: ExampleCircuit>(matches: &ArgMatches, circuit_name: &str) -> Result<()> {
628 let thread_pool_result = binius_utils::rayon::config::adjust_thread_pool();
633
634 #[cfg(feature = "perfetto")]
637 let _tracing_guard = {
638 let thread_count = binius_utils::rayon::current_num_threads();
640 let thread_mode = if thread_count == 1 { "st" } else { "mt" };
641
642 let mut builder = tracing_profile::TraceFilenameBuilder::for_benchmark(circuit_name)
643 .output_dir("perfetto_traces")
644 .timestamp() .git_info() .platform() .thread_mode(thread_mode);
648
649 let matches_for_params = matches.subcommand().map(|(_, sub)| sub).unwrap_or(matches);
653
654 if let Ok(params) = E::Params::from_arg_matches(matches_for_params)
655 && let Some(param_summary) = E::param_summary(¶ms)
656 {
657 builder = builder.add("params", param_summary);
658 }
659
660 tracing_profile::init_tracing_with_builder(builder)?
661 };
662 #[cfg(not(feature = "perfetto"))]
663 crate::init_tracing();
664
665 if let Err(err) = thread_pool_result {
667 tracing::warn!("could not pin the rayon thread pool to one thread: {err}");
668 }
669
670 match matches.subcommand() {
672 Some(("prove", sub_matches)) => run_prove::<E>(sub_matches),
673 Some(("stat", sub_matches)) => run_stat::<E>(sub_matches),
674 Some(("composition", sub_matches)) => run_composition::<E>(sub_matches),
675 Some(("check-snapshot", sub_matches)) => run_check_snapshot::<E>(sub_matches, circuit_name),
676 Some(("bless-snapshot", sub_matches)) => run_bless_snapshot::<E>(sub_matches, circuit_name),
677 Some(("save", sub_matches)) => run_save::<E>(sub_matches),
678 Some(("load-prove", sub_matches)) => run_load_prove(sub_matches),
679 Some(("verify", sub_matches)) => run_verify::<E>(sub_matches),
680 Some((cmd, _)) => anyhow::bail!("Unknown subcommand: {}", cmd),
681 None => {
682 run_prove::<E>(matches)
684 }
685 }
686}
687
688fn run_prove<E: ExampleCircuit>(matches: &ArgMatches) -> Result<()> {
689 let log_inv_rate = *matches
691 .get_one::<u32>("log_inv_rate")
692 .expect("has default value");
693 let hash_suite = *matches
694 .get_one::<HashSuiteType>("hash_suite")
695 .expect("has default value");
696 let zk = matches.get_flag("zk");
697 let sign_message = matches.get_one::<String>("sign_message").cloned();
698 let output = matches.get_one::<String>("output").cloned();
699 tracing::info!("Parsed hash suite: {hash_suite:?}");
700 if zk {
701 tracing::info!("Using zero-knowledge proving config");
702 }
703 if sign_message.is_some() {
704 tracing::info!("Producing a signature of knowledge over the provided message");
705 }
706 let message = sign_message.as_deref().map(str::as_bytes);
707
708 let params = E::Params::from_arg_matches(matches)?;
710 let instance = E::Instance::from_arg_matches(matches)?;
711
712 let build_scope = tracing::info_span!("Building circuit").entered();
714 let mut builder = CircuitBuilder::new();
715 let example = E::build(params, &mut builder)?;
716 let circuit = builder.build();
717 drop(build_scope);
718
719 let cs = circuit.constraint_system().clone();
721
722 let witness_population = tracing::info_span!(
724 "Generating witness",
725 operation = "witness_generation",
726 perfetto_category = "operation"
727 )
728 .entered();
729 let mut filler = circuit.new_witness_filler();
730 tracing::info_span!("Input population")
731 .in_scope(|| example.populate_witness(instance, &mut filler))?;
732 tracing::info_span!("Circuit evaluation")
733 .in_scope(|| circuit.populate_wire_witness(&mut filler))?;
734 let witness = filler.into_value_vec();
735 drop(witness_population);
736
737 prove_dispatch(cs, log_inv_rate as usize, hash_suite, zk, message, &witness, output.as_deref())
738}
739
740fn run_stat<E: ExampleCircuit>(matches: &ArgMatches) -> Result<()> {
741 let params = E::Params::from_arg_matches(matches)?;
743
744 let mut builder = CircuitBuilder::new();
746 let _example = E::build(params, &mut builder)?;
747 let circuit = builder.build();
748
749 let stat = CircuitStat::collect(&circuit);
751 print!("{}", stat);
752
753 Ok(())
754}
755
756fn run_composition<E: ExampleCircuit>(matches: &ArgMatches) -> Result<()> {
757 let params = E::Params::from_arg_matches(matches)?;
759
760 let mut builder = CircuitBuilder::new();
762 let _example = E::build(params, &mut builder)?;
763 let circuit = builder.build();
764
765 let dump = circuit.simple_json_dump();
767 println!("{}", dump);
768
769 Ok(())
770}
771
772fn run_check_snapshot<E: ExampleCircuit>(matches: &ArgMatches, circuit_name: &str) -> Result<()> {
773 let params = E::Params::from_arg_matches(matches)?;
775
776 let mut builder = CircuitBuilder::new();
778 let _example = E::build(params, &mut builder)?;
779 let circuit = builder.build();
780
781 crate::snapshot::check_snapshot(circuit_name, &circuit)?;
783
784 Ok(())
785}
786
787fn run_bless_snapshot<E: ExampleCircuit>(matches: &ArgMatches, circuit_name: &str) -> Result<()> {
788 let params = E::Params::from_arg_matches(matches)?;
790
791 let mut builder = CircuitBuilder::new();
793 let _example = E::build(params, &mut builder)?;
794 let circuit = builder.build();
795
796 crate::snapshot::bless_snapshot(circuit_name, &circuit)?;
798
799 Ok(())
800}
801
802fn run_save<E: ExampleCircuit>(matches: &ArgMatches) -> Result<()> {
803 let cs_path = matches.get_one::<String>("cs_path").cloned();
805 let pub_witness_path = matches.get_one::<String>("pub_witness_path").cloned();
806 let non_pub_data_path = matches.get_one::<String>("non_pub_data_path").cloned();
807 let key_collection_path = matches.get_one::<String>("key_collection_path").cloned();
808
809 if cs_path.is_none()
811 && pub_witness_path.is_none()
812 && non_pub_data_path.is_none()
813 && key_collection_path.is_none()
814 {
815 tracing::info!("No output paths provided; nothing to save");
816 return Ok(());
817 }
818
819 let params = E::Params::from_arg_matches(matches)?;
821 let instance = E::Instance::from_arg_matches(matches)?;
822
823 let mut builder = CircuitBuilder::new();
825 let example = E::build(params, &mut builder)?;
826 let circuit = builder.build();
827
828 let mut filler = circuit.new_witness_filler();
830 example.populate_witness(instance, &mut filler)?;
831 circuit.populate_wire_witness(&mut filler)?;
832 let witness: ValueVec = filler.into_value_vec();
833
834 save_artifacts(
835 circuit.constraint_system(),
836 &witness,
837 cs_path.as_deref(),
838 pub_witness_path.as_deref(),
839 non_pub_data_path.as_deref(),
840 key_collection_path.as_deref(),
841 )
842}
843
844fn run_verify<E: ExampleCircuit>(matches: &ArgMatches) -> Result<()> {
845 let proof_file = matches
846 .get_one::<String>("proof_file")
847 .expect("proof_file is required");
848 let log_inv_rate = *matches
849 .get_one::<u32>("log_inv_rate")
850 .expect("has default value");
851 let hash_suite = *matches
852 .get_one::<HashSuiteType>("hash_suite")
853 .expect("has default value");
854 let zk = matches.get_flag("zk");
855 let sign_message = matches.get_one::<String>("sign_message").cloned();
856 let message = sign_message.as_deref().map(str::as_bytes);
857
858 let proof: Proof<'static> = read_deserialized(proof_file)?;
860 let (proof_bytes, _) = proof.into_owned();
861
862 let params = E::Params::from_arg_matches(matches)?;
864 let instance = E::Instance::from_arg_matches(matches)?;
865
866 let build_scope = tracing::info_span!("Building circuit").entered();
868 let mut builder = CircuitBuilder::new();
869 let example = E::build(params, &mut builder)?;
870 let circuit = builder.build();
871 drop(build_scope);
872
873 let cs = circuit.constraint_system().clone();
875
876 let mut filler = circuit.new_witness_filler();
878 example.populate_witness(instance, &mut filler)?;
879 circuit.populate_wire_witness(&mut filler)?;
880 let witness = filler.into_value_vec();
881
882 verify_dispatch(cs, log_inv_rate as usize, hash_suite, zk, message, &witness, proof_bytes)?;
883
884 tracing::info!("Proof verified successfully.");
885 Ok(())
886}