1use std::{
12 env::current_dir,
13 fs,
14 path::{Path, PathBuf},
15 process::Command,
16};
17
18use bellpepper_core::{num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError};
19use color_eyre::Result;
20use ff::PrimeField;
21use r1cs::{CircomConfig, R1CS};
22
23use crate::reader::load_witness_from_file;
24
25pub mod r1cs;
26pub mod reader;
27pub mod witness;
28
29pub fn generate_witness_from_wasm<F: PrimeField>(
30 witness_dir: PathBuf,
31 witness_input_json: String,
32 witness_output: impl AsRef<Path>,
33) -> Vec<F> {
34 let root = current_dir().unwrap();
35 let witness_generator_input = root.join("circom_input.json");
36 fs::write(&witness_generator_input, witness_input_json).unwrap();
37
38 let mut witness_js = witness_dir.clone();
39 witness_js.push("generate_witness.js");
40 let mut witness_wasm = witness_dir.clone();
41 witness_wasm.push("main.wasm");
42
43 let output = Command::new("node")
44 .arg(&witness_js)
45 .arg(&witness_wasm)
46 .arg(&witness_generator_input)
47 .arg(witness_output.as_ref())
48 .output()
49 .expect("failed to execute process");
50 if !output.stdout.is_empty() || !output.stderr.is_empty() {
51 print!("stdout: {}", std::str::from_utf8(&output.stdout).unwrap());
52 print!("stderr: {}", std::str::from_utf8(&output.stderr).unwrap());
53 }
54 let _ = fs::remove_file(witness_generator_input);
55 load_witness_from_file(witness_output)
56}
57
58pub fn calculate_witness<F: PrimeField>(
60 cfg: &CircomConfig<F>,
61 input: Vec<(String, Vec<F>)>,
62 sanity_check: bool,
63) -> Result<Vec<F>> {
64 let mut lock = cfg.wtns.lock().unwrap();
65 let witness_calculator = &mut *lock;
66 witness_calculator.calculate_witness(input, sanity_check)
67}
68
69pub fn synthesize<F: PrimeField, CS: ConstraintSystem<F>>(
71 cs: &mut CS,
72 r1cs: R1CS<F>,
73 witness: Option<Vec<F>>,
74) -> Result<AllocatedNum<F>, SynthesisError> {
75 let witness = &witness;
82
83 let mut vars: Vec<AllocatedNum<F>> = vec![];
84
85 for i in 1..r1cs.num_inputs {
86 let f: F = {
87 match witness {
88 None => F::ONE,
89 Some(w) => w[i],
90 }
91 };
92 let v = AllocatedNum::alloc(cs.namespace(|| format!("public_{}", i)), || Ok(f))?;
93
94 vars.push(v);
95 }
96
97 for i in 0..r1cs.num_aux {
98 let f: F = {
100 match witness {
101 None => F::ONE,
102 Some(w) => w[i + r1cs.num_inputs],
103 }
104 };
105
106 let v = AllocatedNum::alloc(cs.namespace(|| format!("aux_{}", i)), || Ok(f))?;
107 vars.push(v);
108 }
109
110 let output = vars[0].clone();
111
112 let make_lc = |lc_data: Vec<(usize, F)>| {
113 let res = lc_data.iter().fold(
114 LinearCombination::<F>::zero(),
115 |lc: LinearCombination<F>, (index, coeff)| {
116 lc + if *index > 0 {
117 (*coeff, vars[*index - 1].get_variable())
118 } else {
119 (*coeff, CS::one())
120 }
121 },
122 );
123 res
124 };
125
126 for (i, constraint) in r1cs.constraints.into_iter().enumerate() {
127 cs.enforce(
128 || format!("constraint {}", i),
129 |_| make_lc(constraint.0),
130 |_| make_lc(constraint.1),
131 |_| make_lc(constraint.2),
132 );
133 }
134
135 Ok(output)
136}