use tinysnark::FieldT; use std::cell::Cell; use std::rc::Rc; use std::fmt; use std::collections::BTreeMap; pub type WitnessMap = BTreeMap, Vec, Rc)>>; struct VariableView<'a> { vars: &'a mut [FieldT], inputs: &'a [usize], outputs: &'a [usize] } impl<'a> VariableView<'a> { /// Sets an output variable at `index` to value `to`. pub fn set_output(&mut self, index: usize, to: FieldT) { self.vars[self.outputs[index]] = to; } /// Gets the value of an input variable at `index`. pub fn get_input(&self, index: usize) -> FieldT { self.vars[self.inputs[index]] } } use std::collections::Bound::Unbounded; pub fn witness_field_elements(vars: &mut [FieldT], witness_map: &WitnessMap) { for (n, group) in witness_map.range(Unbounded, Unbounded) { for &(ref i, ref o, ref f) in group.iter() { let mut vars = VariableView { vars: vars, inputs: &*i, outputs: &*o }; f(&mut vars); } } } #[derive(Clone)] pub enum Constraint { Bitness(Rc>), And(Rc>, Rc>, Rc>), Nand(Rc>, Rc>, Rc>), Xor(Rc>, Rc>, Rc>), Xnor(Rc>, Rc>, Rc>), MaterialNonimplication(Rc>, Rc>, Rc>), MaterialImplication(Rc>, Rc>, Rc>), Nor(Rc>, Rc>, Rc>), Or(Rc>, Rc>, Rc>) } impl fmt::Debug for Constraint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", match *self { Constraint::Bitness(ref b) => format!("bitness: {}", b.get()), Constraint::And(ref a, ref b, ref c) => format!("{} = {} AND {}", c.get(), a.get(), b.get()), Constraint::Nand(ref a, ref b, ref c) => format!("{} = {} NAND {}", c.get(), a.get(), b.get()), Constraint::Xor(ref a, ref b, ref c) => format!("{} = {} XOR {}", c.get(), a.get(), b.get()), Constraint::Xnor(ref a, ref b, ref c) => format!("{} = {} XNOR {}", c.get(), a.get(), b.get()), Constraint::MaterialNonimplication(ref a, ref b, ref c) => format!("{} = {} ↛ {}", c.get(), a.get(), b.get()), Constraint::MaterialImplication(ref a, ref b, ref c) => format!("{} = {} <-> {}", c.get(), a.get(), b.get()), Constraint::Nor(ref a, ref b, ref c) => format!("{} = {} NOR {}", c.get(), a.get(), b.get()), Constraint::Or(ref a, ref b, ref c) => format!("{} = {} OR {}", c.get(), a.get(), b.get()) }) } } struct Gadget { inputs: Vec, aux: Vec, witness: Rc, constraints: Vec, group: usize, visited: Cell } impl Gadget { pub fn walk(&self, counter: &mut usize, constraints: &mut Vec, witness_map: &mut WitnessMap) { if self.visited.get() { return; } self.visited.set(true); for a in &self.aux { assert!(a.index.get() == 0); a.index.set(*counter); *counter += 1; } constraints.extend_from_slice(&self.constraints); for i in &self.inputs { i.walk(counter, constraints, witness_map); } let input_indexes = self.inputs.iter().map(|i| i.index.get()).collect(); let output_indexes = self.aux.iter().map(|i| i.index.get()).collect(); witness_map.entry(self.group) .or_insert_with(|| Vec::new()) .push((input_indexes, output_indexes, self.witness.clone())); } } #[derive(Clone)] pub struct Var { index: Rc>, gadget: Option> } impl Var { // todo: make this not public pub fn new(i: usize) -> Var { Var { index: Rc::new(Cell::new(i)), gadget: None } } // make this not public or unsafe too pub fn index(&self) -> Rc> { self.index.clone() } pub fn val(&self, map: &[FieldT]) -> FieldT { let index = self.index.get(); assert!(index != 0); map[index] } fn group(&self) -> usize { match self.gadget { None => 0, Some(ref g) => g.group } } pub fn walk(&self, counter: &mut usize, constraints: &mut Vec, witness_map: &mut WitnessMap) { match self.gadget { None => {}, Some(ref g) => g.walk(counter, constraints, witness_map) } } } pub fn gadget( inputs: &[&Var], aux: usize, witness: W, constrain: C ) -> Vec where C: for<'a> Fn(&[&'a Var], &[&'a Var], &mut Vec) -> Vec<&'a Var>, W: Fn(&mut VariableView) + 'static { let this_group = inputs.iter().map(|i| i.group()).max().map(|a| a+1).unwrap_or(0); let aux: Vec<_> = (0..aux).map(|_| Var::new(0)).collect(); let aux: Vec<_> = aux.iter().collect(); let mut constraints = vec![]; let outputs = constrain(inputs, &*aux, &mut constraints); let gadget = Rc::new(Gadget { inputs: inputs.iter().map(|a| (*a).clone()).collect(), aux: aux.iter().map(|a| (*a).clone()).collect(), witness: Rc::new(witness), constraints: constraints, group: this_group, visited: Cell::new(false) }); outputs.into_iter().map(|a| { let mut a = (*a).clone(); // TODO: we should augment the gadget instead // of replacing it debug_assert!(a.gadget.is_none()); a.gadget = Some(gadget.clone()); a }).collect() }