arnaucube
arnaucube/circom-compatPublic Self-hosted repository
circom-compat/src/witness/witness_calculator.rs
src/witness/witness_calculator.rsRaw
  1use color_eyre::Result;
  2use num_bigint::BigInt;
  3use num_traits::Zero;
  4use std::cell::Cell;
  5use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, Store};
  6
  7use super::{fnv, SafeMemory, Wasm};
  8
  9#[derive(Clone, Debug)]
 10pub struct WitnessCalculator {
 11    pub instance: Wasm,
 12    pub memory: SafeMemory,
 13    pub n64: i32,
 14}
 15
 16impl WitnessCalculator {
 17    pub fn new(path: impl AsRef<std::path::Path>) -> Result<Self> {
 18        let store = Store::default();
 19        let module = Module::from_file(&store, path)?;
 20
 21        // Set up the memory
 22        let memory = Memory::new(&store, MemoryType::new(2000, None, false)).unwrap();
 23        let import_object = imports! {
 24            "env" => {
 25                "memory" => memory.clone(),
 26            },
 27            // Host function callbacks from the WASM
 28            "runtime" => {
 29                "error" => runtime::error(&store),
 30                "logSetSignal" => runtime::log_signal(&store),
 31                "logGetSignal" => runtime::log_signal(&store),
 32                "logFinishComponent" => runtime::log_component(&store),
 33                "logStartComponent" => runtime::log_component(&store),
 34                "log" => runtime::log_component(&store),
 35            }
 36        };
 37        let instance = Wasm::new(Instance::new(&module, &import_object)?);
 38
 39        let n32 = (instance.get_fr_len()? >> 2) - 2;
 40
 41        let mut memory = SafeMemory::new(memory, n32 as usize, BigInt::zero());
 42
 43        let ptr = instance.get_ptr_raw_prime()?;
 44        let prime = memory.read_big(ptr as usize, n32 as usize)?;
 45        let n64 = ((prime.bits() - 1) / 64 + 1) as i32;
 46        memory.prime = prime;
 47
 48        Ok(WitnessCalculator {
 49            instance,
 50            memory,
 51            n64,
 52        })
 53    }
 54
 55    pub fn calculate_witness<I: IntoIterator<Item = (String, Vec<BigInt>)>>(
 56        &mut self,
 57        inputs: I,
 58        sanity_check: bool,
 59    ) -> Result<Vec<BigInt>> {
 60        let old_mem_free_pos = self.memory.free_pos();
 61
 62        self.instance.init(sanity_check)?;
 63
 64        let p_sig_offset = self.memory.alloc_u32();
 65        let p_fr = self.memory.alloc_fr();
 66
 67        // allocate the inputs
 68        for (name, values) in inputs.into_iter() {
 69            let (msb, lsb) = fnv(&name);
 70            self.instance
 71                .get_signal_offset32(p_sig_offset, 0, msb, lsb)?;
 72
 73            let sig_offset = self.memory.read_u32(p_sig_offset as usize) as usize;
 74
 75            for (i, value) in values.into_iter().enumerate() {
 76                self.memory.write_fr(p_fr as usize, &value)?;
 77                self.instance
 78                    .set_signal(0, 0, (sig_offset + i) as i32, p_fr as i32)?;
 79            }
 80        }
 81
 82        let mut w = Vec::new();
 83        let n_vars = self.instance.get_n_vars()?;
 84        for i in 0..n_vars {
 85            let ptr = self.instance.get_ptr_witness(i)? as usize;
 86            let el = self.memory.read_fr(ptr)?;
 87            w.push(el);
 88        }
 89
 90        self.memory.set_free_pos(old_mem_free_pos);
 91
 92        Ok(w)
 93    }
 94
 95    pub fn calculate_witness_element<
 96        E: ark_ec::PairingEngine,
 97        I: IntoIterator<Item = (String, Vec<BigInt>)>,
 98    >(
 99        &mut self,
100        inputs: I,
101        sanity_check: bool,
102    ) -> Result<Vec<E::Fr>> {
103        use ark_ff::{FpParameters, PrimeField};
104        let witness = self.calculate_witness(inputs, sanity_check)?;
105        let modulus = <<E::Fr as PrimeField>::Params as FpParameters>::MODULUS;
106
107        // convert it to field elements
108        use num_traits::Signed;
109        let witness = witness
110            .into_iter()
111            .map(|w| {
112                let w = if w.sign() == num_bigint::Sign::Minus {
113                    // Need to negate the witness element if negative
114                    modulus.into() - w.abs().to_biguint().unwrap()
115                } else {
116                    w.to_biguint().unwrap()
117                };
118                E::Fr::from(w)
119            })
120            .collect::<Vec<_>>();
121
122        Ok(witness)
123    }
124
125    pub fn get_witness_buffer(&self) -> Result<Vec<u8>> {
126        let ptr = self.instance.get_ptr_witness_buffer()? as usize;
127
128        let view = self.memory.memory.view::<u8>();
129
130        let len = self.instance.get_n_vars()? * self.n64 * 8;
131        let arr = view[ptr..ptr + len as usize]
132            .iter()
133            .map(Cell::get)
134            .collect::<Vec<_>>();
135
136        Ok(arr)
137    }
138}
139
140// callback hooks for debugging
141mod runtime {
142    use super::*;
143
144    pub fn error(store: &Store) -> Function {
145        #[allow(unused)]
146        #[allow(clippy::many_single_char_names)]
147        fn func(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) {}
148        Function::new_native(store, func)
149    }
150
151    pub fn log_signal(store: &Store) -> Function {
152        #[allow(unused)]
153        fn func(a: i32, b: i32) {}
154        Function::new_native(store, func)
155    }
156
157    pub fn log_component(store: &Store) -> Function {
158        #[allow(unused)]
159        fn func(a: i32) {}
160        Function::new_native(store, func)
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use std::{collections::HashMap, path::PathBuf};
168
169    struct TestCase<'a> {
170        circuit_path: &'a str,
171        inputs_path: &'a str,
172        n_vars: u32,
173        n64: u32,
174        witness: &'a [&'a str],
175    }
176
177    pub fn root_path(p: &str) -> String {
178        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
179        path.push(p);
180        path.to_string_lossy().to_string()
181    }
182
183    #[test]
184    fn multiplier_1() {
185        run_test(TestCase {
186            circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(),
187            inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(),
188            n_vars: 4,
189            n64: 4,
190            witness: &["1", "33", "3", "11"],
191        });
192    }
193
194    #[test]
195    fn multiplier_2() {
196        run_test(TestCase {
197            circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(),
198            inputs_path: root_path("test-vectors/mycircuit-input2.json").as_str(),
199            n_vars: 4,
200            n64: 4,
201            witness: &[
202                "1",
203                "21888242871839275222246405745257275088548364400416034343698204186575672693159",
204                "21888242871839275222246405745257275088548364400416034343698204186575796149939",
205                "11",
206            ],
207        });
208    }
209
210    #[test]
211    fn multiplier_3() {
212        run_test(TestCase {
213            circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(),
214            inputs_path: root_path("test-vectors/mycircuit-input3.json").as_str(),
215            n_vars: 4,
216            n64: 4,
217            witness: &[
218                "1",
219                "21888242871839275222246405745257275088548364400416034343698204186575808493616",
220                "10944121435919637611123202872628637544274182200208017171849102093287904246808",
221                "2",
222            ],
223        });
224    }
225
226    #[test]
227    fn safe_multipler() {
228        let witness =
229            std::fs::read_to_string(&root_path("test-vectors/safe-circuit-witness.json")).unwrap();
230        let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
231        let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
232        run_test(TestCase {
233            circuit_path: root_path("test-vectors/circuit2.wasm").as_str(),
234            inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(),
235            n_vars: 132, // 128 + 4
236            n64: 4,
237            witness,
238        });
239    }
240
241    #[test]
242    fn smt_verifier() {
243        let witness =
244            std::fs::read_to_string(&root_path("test-vectors/smtverifier10-witness.json")).unwrap();
245        let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
246        let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
247
248        run_test(TestCase {
249            circuit_path: root_path("test-vectors/smtverifier10.wasm").as_str(),
250            inputs_path: root_path("test-vectors/smtverifier10-input.json").as_str(),
251            n_vars: 4794,
252            n64: 4,
253            witness,
254        });
255    }
256
257    use serde_json::Value;
258    use std::str::FromStr;
259
260    fn value_to_bigint(v: Value) -> BigInt {
261        match v {
262            Value::String(inner) => BigInt::from_str(&inner).unwrap(),
263            Value::Number(inner) => BigInt::from(inner.as_u64().expect("not a u32")),
264            _ => panic!("unsupported type"),
265        }
266    }
267
268    fn run_test(case: TestCase) {
269        let mut wtns = WitnessCalculator::new(case.circuit_path).unwrap();
270        assert_eq!(
271            wtns.memory.prime.to_str_radix(16),
272            "30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase()
273        );
274        assert_eq!(wtns.instance.get_n_vars().unwrap() as u32, case.n_vars);
275        assert_eq!(wtns.n64 as u32, case.n64);
276
277        let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap();
278        let inputs: std::collections::HashMap<String, serde_json::Value> =
279            serde_json::from_str(&inputs_str).unwrap();
280
281        let inputs = inputs
282            .iter()
283            .map(|(key, value)| {
284                let res = match value {
285                    Value::String(inner) => {
286                        vec![BigInt::from_str(inner).unwrap()]
287                    }
288                    Value::Number(inner) => {
289                        vec![BigInt::from(inner.as_u64().expect("not a u32"))]
290                    }
291                    Value::Array(inner) => inner.iter().cloned().map(value_to_bigint).collect(),
292                    _ => panic!(),
293                };
294
295                (key.clone(), res)
296            })
297            .collect::<HashMap<_, _>>();
298
299        let res = wtns.calculate_witness(inputs, false).unwrap();
300        for (r, w) in res.iter().zip(case.witness) {
301            assert_eq!(r, &BigInt::from_str(w).unwrap());
302        }
303    }
304}