diff --git a/Cargo.toml b/Cargo.toml index db396557..58d72e0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,6 @@ license-file = "LICENSE" keywords = ["zkSNARKs", "cryptography", "proofs"] [dependencies] -# TODO: replace with the next version of bellperson -# See : https://github.com/filecoin-project/bellperson/pull/314 -bellperson = { git="https://github.com/filecoin-project/bellperson", rev="8c5d202e", default-features = false } bellpepper-core = { version="0.2.0", default-features = false } bellpepper = { version="0.2.0", default-features = false } ff = { version = "0.13.0", features = ["derive"] } diff --git a/src/bellperson/mod.rs b/src/bellperson/mod.rs index 7b9fe0f7..64fdeabf 100644 --- a/src/bellperson/mod.rs +++ b/src/bellperson/mod.rs @@ -5,6 +5,7 @@ pub mod r1cs; pub mod shape_cs; pub mod solver; +pub mod test_shape_cs; #[cfg(test)] mod tests { diff --git a/src/bellperson/r1cs.rs b/src/bellperson/r1cs.rs index 35b915af..230db1bd 100644 --- a/src/bellperson/r1cs.rs +++ b/src/bellperson/r1cs.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] -use super::{shape_cs::ShapeCS, solver::SatisfyingAssignment}; +use super::{shape_cs::ShapeCS, solver::SatisfyingAssignment, test_shape_cs::TestShapeCS}; use crate::{ errors::NovaError, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, R1CS}, @@ -45,43 +45,53 @@ impl NovaWitness for SatisfyingAssignment { } } -impl NovaShape for ShapeCS { - fn r1cs_shape(&self) -> (R1CSShape, CommitmentKey) { - let mut A: Vec<(usize, usize, G::Scalar)> = Vec::new(); - let mut B: Vec<(usize, usize, G::Scalar)> = Vec::new(); - let mut C: Vec<(usize, usize, G::Scalar)> = Vec::new(); - - let mut num_cons_added = 0; - let mut X = (&mut A, &mut B, &mut C, &mut num_cons_added); - - let num_inputs = self.num_inputs(); - let num_constraints = self.num_constraints(); - let num_vars = self.num_aux(); - - for constraint in self.constraints.iter() { - add_constraint( - &mut X, - num_vars, - &constraint.0, - &constraint.1, - &constraint.2, - ); +macro_rules! impl_nova_shape { + ( $name:ident) => { + impl NovaShape for $name + where + G::Scalar: PrimeField, + { + fn r1cs_shape(&self) -> (R1CSShape, CommitmentKey) { + let mut A: Vec<(usize, usize, G::Scalar)> = Vec::new(); + let mut B: Vec<(usize, usize, G::Scalar)> = Vec::new(); + let mut C: Vec<(usize, usize, G::Scalar)> = Vec::new(); + + let mut num_cons_added = 0; + let mut X = (&mut A, &mut B, &mut C, &mut num_cons_added); + + let num_inputs = self.num_inputs(); + let num_constraints = self.num_constraints(); + let num_vars = self.num_aux(); + + for constraint in self.constraints.iter() { + add_constraint( + &mut X, + num_vars, + &constraint.0, + &constraint.1, + &constraint.2, + ); + } + + assert_eq!(num_cons_added, num_constraints); + + let S: R1CSShape = { + // Don't count One as an input for shape's purposes. + let res = R1CSShape::new(num_constraints, num_vars, num_inputs - 1, &A, &B, &C); + res.unwrap() + }; + + let ck = R1CS::::commitment_key(&S); + + (S, ck) + } } - - assert_eq!(num_cons_added, num_constraints); - - let S: R1CSShape = { - // Don't count One as an input for shape's purposes. - let res = R1CSShape::new(num_constraints, num_vars, num_inputs - 1, &A, &B, &C); - res.unwrap() - }; - - let ck = R1CS::::commitment_key(&S); - - (S, ck) - } + }; } +impl_nova_shape!(ShapeCS); +impl_nova_shape!(TestShapeCS); + fn add_constraint( X: &mut ( &mut Vec<(usize, usize, S)>, diff --git a/src/bellperson/shape_cs.rs b/src/bellperson/shape_cs.rs index 33b4791d..2fd13fd6 100644 --- a/src/bellperson/shape_cs.rs +++ b/src/bellperson/shape_cs.rs @@ -1,92 +1,24 @@ //! Support for generating R1CS shape using bellperson. -use std::{ - cmp::Ordering, - collections::{BTreeMap, HashMap}, -}; - use crate::traits::Group; use bellpepper_core::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; -use core::fmt::Write; -use ff::{Field, PrimeField}; - -#[derive(Clone, Copy)] -struct OrderedVariable(Variable); - -#[derive(Debug)] -enum NamedObject { - Constraint(usize), - Var(Variable), - Namespace, -} - -impl Eq for OrderedVariable {} -impl PartialEq for OrderedVariable { - fn eq(&self, other: &OrderedVariable) -> bool { - match (self.0.get_unchecked(), other.0.get_unchecked()) { - (Index::Input(ref a), Index::Input(ref b)) => a == b, - (Index::Aux(ref a), Index::Aux(ref b)) => a == b, - _ => false, - } - } -} -impl PartialOrd for OrderedVariable { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for OrderedVariable { - fn cmp(&self, other: &Self) -> Ordering { - match (self.0.get_unchecked(), other.0.get_unchecked()) { - (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), - (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), - (Index::Input(_), Index::Aux(_)) => Ordering::Less, - (Index::Aux(_), Index::Input(_)) => Ordering::Greater, - } - } -} +use ff::PrimeField; #[allow(clippy::upper_case_acronyms)] /// `ShapeCS` is a `ConstraintSystem` for creating `R1CSShape`s for a circuit. -pub struct ShapeCS { - named_objects: HashMap, - current_namespace: Vec, +pub struct ShapeCS +where + G::Scalar: PrimeField, +{ #[allow(clippy::type_complexity)] /// All constraints added to the `ShapeCS`. pub constraints: Vec<( LinearCombination, LinearCombination, LinearCombination, - String, )>, - inputs: Vec, - aux: Vec, -} - -fn proc_lc( - terms: &LinearCombination, -) -> BTreeMap { - let mut map = BTreeMap::new(); - for (var, &coeff) in terms.iter() { - map - .entry(OrderedVariable(var)) - .or_insert_with(|| Scalar::ZERO) - .add_assign(&coeff); - } - - // Remove terms that have a zero coefficient to normalize - let mut to_remove = vec![]; - for (var, coeff) in map.iter() { - if coeff.is_zero().into() { - to_remove.push(*var) - } - } - - for var in to_remove { - map.remove(&var); - } - - map + inputs: usize, + aux: usize, } impl ShapeCS { @@ -102,124 +34,21 @@ impl ShapeCS { /// Returns the number of inputs defined for this `ShapeCS`. pub fn num_inputs(&self) -> usize { - self.inputs.len() + self.inputs } /// Returns the number of aux inputs defined for this `ShapeCS`. pub fn num_aux(&self) -> usize { - self.aux.len() - } - - /// Print all public inputs, aux inputs, and constraint names. - #[allow(dead_code)] - pub fn pretty_print_list(&self) -> Vec { - let mut result = Vec::new(); - - for input in &self.inputs { - result.push(format!("INPUT {input}")); - } - for aux in &self.aux { - result.push(format!("AUX {aux}")); - } - - for (_a, _b, _c, name) in &self.constraints { - result.push(name.to_string()); - } - - result - } - - /// Print all iputs and a detailed representation of each constraint. - #[allow(dead_code)] - pub fn pretty_print(&self) -> String { - let mut s = String::new(); - - for input in &self.inputs { - writeln!(s, "INPUT {}", &input).unwrap() - } - - let negone = -::ONE; - - let powers_of_two = (0..G::Scalar::NUM_BITS) - .map(|i| G::Scalar::from(2u64).pow_vartime([u64::from(i)])) - .collect::>(); - - let pp = |s: &mut String, lc: &LinearCombination| { - s.push('('); - let mut is_first = true; - for (var, coeff) in proc_lc::(lc) { - if coeff == negone { - s.push_str(" - ") - } else if !is_first { - s.push_str(" + ") - } - is_first = false; - - if coeff != ::ONE && coeff != negone { - for (i, x) in powers_of_two.iter().enumerate() { - if x == &coeff { - write!(s, "2^{i} . ").unwrap(); - break; - } - } - - write!(s, "{coeff:?} . ").unwrap() - } - - match var.0.get_unchecked() { - Index::Input(i) => { - write!(s, "`I{}`", &self.inputs[i]).unwrap(); - } - Index::Aux(i) => { - write!(s, "`A{}`", &self.aux[i]).unwrap(); - } - } - } - if is_first { - // Nothing was visited, print 0. - s.push('0'); - } - s.push(')'); - }; - - for (a, b, c, name) in &self.constraints { - s.push('\n'); - - write!(s, "{name}: ").unwrap(); - pp(&mut s, a); - write!(s, " * ").unwrap(); - pp(&mut s, b); - s.push_str(" = "); - pp(&mut s, c); - } - - s.push('\n'); - - s - } - - /// Associate `NamedObject` with `path`. - /// `path` must not already have an associated object. - fn set_named_obj(&mut self, path: String, to: NamedObject) { - assert!( - !self.named_objects.contains_key(&path), - "tried to create object at existing path: {path}" - ); - - self.named_objects.insert(path, to); + self.aux } } impl Default for ShapeCS { fn default() -> Self { - let mut map = HashMap::new(); - map.insert("ONE".into(), NamedObject::Var(ShapeCS::::one())); ShapeCS { - named_objects: map, - current_namespace: vec![], constraints: vec![], - inputs: vec![String::from("ONE")], - aux: vec![], + inputs: 1, + aux: 0, } } } @@ -227,31 +56,29 @@ impl Default for ShapeCS { impl ConstraintSystem for ShapeCS { type Root = Self; - fn alloc(&mut self, annotation: A, _f: F) -> Result + fn alloc(&mut self, _annotation: A, _f: F) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { - let path = compute_path(&self.current_namespace, &annotation().into()); - self.aux.push(path); + self.aux += 1; - Ok(Variable::new_unchecked(Index::Aux(self.aux.len() - 1))) + Ok(Variable::new_unchecked(Index::Aux(self.aux - 1))) } - fn alloc_input(&mut self, annotation: A, _f: F) -> Result + fn alloc_input(&mut self, _annotation: A, _f: F) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { - let path = compute_path(&self.current_namespace, &annotation().into()); - self.inputs.push(path); + self.inputs += 1; - Ok(Variable::new_unchecked(Index::Input(self.inputs.len() - 1))) + Ok(Variable::new_unchecked(Index::Input(self.inputs - 1))) } - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) + fn enforce(&mut self, _annotation: A, a: LA, b: LB, c: LC) where A: FnOnce() -> AR, AR: Into, @@ -259,73 +86,23 @@ impl ConstraintSystem for ShapeCS { LB: FnOnce(LinearCombination) -> LinearCombination, LC: FnOnce(LinearCombination) -> LinearCombination, { - let path = compute_path(&self.current_namespace, &annotation().into()); - let index = self.constraints.len(); - self.set_named_obj(path.clone(), NamedObject::Constraint(index)); - let a = a(LinearCombination::zero()); let b = b(LinearCombination::zero()); let c = c(LinearCombination::zero()); - self.constraints.push((a, b, c, path)); + self.constraints.push((a, b, c)); } - fn push_namespace(&mut self, name_fn: N) + fn push_namespace(&mut self, _name_fn: N) where NR: Into, N: FnOnce() -> NR, { - let name = name_fn().into(); - let path = compute_path(&self.current_namespace, &name); - self.set_named_obj(path, NamedObject::Namespace); - self.current_namespace.push(name); } - fn pop_namespace(&mut self) { - assert!(self.current_namespace.pop().is_some()); - } + fn pop_namespace(&mut self) {} fn get_root(&mut self) -> &mut Self::Root { self } } - -fn compute_path(ns: &[String], this: &str) -> String { - assert!( - !this.chars().any(|a| a == '/'), - "'/' is not allowed in names" - ); - - let mut name = ns.join("/"); - if !name.is_empty() { - name.push('/'); - } - - name.push_str(this); - - name -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compute_path() { - let ns = vec!["path".to_string(), "to".to_string(), "dir".to_string()]; - let this = "file"; - assert_eq!(compute_path(&ns, this), "path/to/dir/file"); - - let ns = vec!["".to_string(), "".to_string(), "".to_string()]; - let this = "file"; - assert_eq!(compute_path(&ns, this), "///file"); - } - - #[test] - #[should_panic(expected = "'/' is not allowed in names")] - fn test_compute_path_invalid() { - let ns = vec!["path".to_string(), "to".to_string(), "dir".to_string()]; - let this = "fi/le"; - compute_path(&ns, this); - } -} diff --git a/src/bellperson/solver.rs b/src/bellperson/solver.rs index 793f451d..03acc70e 100644 --- a/src/bellperson/solver.rs +++ b/src/bellperson/solver.rs @@ -4,21 +4,9 @@ use crate::traits::Group; use ff::Field; use bellpepper_core::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; -use bellperson::multiexp::DensityTracker; /// A `ConstraintSystem` which calculates witness values for a concrete instance of an R1CS circuit. -#[derive(PartialEq)] pub struct SatisfyingAssignment { - // Density of queries - a_aux_density: DensityTracker, - b_input_density: DensityTracker, - b_aux_density: DensityTracker, - - // Evaluations of A, B, C polynomials - a: Vec, - b: Vec, - c: Vec, - // Assignments of variables pub(crate) input_assignment: Vec, pub(crate) aux_assignment: Vec, @@ -29,54 +17,25 @@ impl fmt::Debug for SatisfyingAssignment { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt .debug_struct("SatisfyingAssignment") - .field("a_aux_density", &self.a_aux_density) - .field("b_input_density", &self.b_input_density) - .field("b_aux_density", &self.b_aux_density) - .field( - "a", - &self - .a - .iter() - .map(|v| format!("Fr({v:?})")) - .collect::>(), - ) - .field( - "b", - &self - .b - .iter() - .map(|v| format!("Fr({v:?})")) - .collect::>(), - ) - .field( - "c", - &self - .c - .iter() - .map(|v| format!("Fr({v:?})")) - .collect::>(), - ) .field("input_assignment", &self.input_assignment) .field("aux_assignment", &self.aux_assignment) .finish() } } +impl PartialEq for SatisfyingAssignment { + fn eq(&self, other: &SatisfyingAssignment) -> bool { + self.input_assignment == other.input_assignment && self.aux_assignment == other.aux_assignment + } +} + impl ConstraintSystem for SatisfyingAssignment { type Root = Self; fn new() -> Self { let input_assignment = vec![G::Scalar::ONE]; - let mut d = DensityTracker::new(); - d.add_element(); Self { - a_aux_density: DensityTracker::new(), - b_input_density: d, - b_aux_density: DensityTracker::new(), - a: vec![], - b: vec![], - c: vec![], input_assignment, aux_assignment: vec![], } @@ -89,8 +48,6 @@ impl ConstraintSystem for SatisfyingAssignment { AR: Into, { self.aux_assignment.push(f()?); - self.a_aux_density.add_element(); - self.b_aux_density.add_element(); Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) } @@ -102,7 +59,6 @@ impl ConstraintSystem for SatisfyingAssignment { AR: Into, { self.input_assignment.push(f()?); - self.b_input_density.add_element(); Ok(Variable(Index::Input(self.input_assignment.len() - 1))) } @@ -139,17 +95,60 @@ impl ConstraintSystem for SatisfyingAssignment { } fn extend(&mut self, other: &Self) { - self.a_aux_density.extend(&other.a_aux_density, false); - self.b_input_density.extend(&other.b_input_density, true); - self.b_aux_density.extend(&other.b_aux_density, false); - - self.a.extend(&other.a); - self.b.extend(&other.b); - self.c.extend(&other.c); - self.input_assignment // Skip first input, which must have been a temporarily allocated one variable. .extend(&other.input_assignment[1..]); self.aux_assignment.extend(&other.aux_assignment); } + + fn is_witness_generator(&self) -> bool { + true + } + + fn extend_inputs(&mut self, new_inputs: &[G::Scalar]) { + self.input_assignment.extend(new_inputs); + } + + fn extend_aux(&mut self, new_aux: &[G::Scalar]) { + self.aux_assignment.extend(new_aux); + } + + fn allocate_empty( + &mut self, + aux_n: usize, + inputs_n: usize, + ) -> (&mut [G::Scalar], &mut [G::Scalar]) { + let allocated_aux = { + let i = self.aux_assignment.len(); + self.aux_assignment.resize(aux_n + i, G::Scalar::ZERO); + &mut self.aux_assignment[i..] + }; + + let allocated_inputs = { + let i = self.input_assignment.len(); + self.input_assignment.resize(inputs_n + i, G::Scalar::ZERO); + &mut self.input_assignment[i..] + }; + + (allocated_aux, allocated_inputs) + } + + fn inputs_slice(&self) -> &[G::Scalar] { + &self.input_assignment + } + + fn aux_slice(&self) -> &[G::Scalar] { + &self.aux_assignment + } +} + +#[allow(dead_code)] +impl SatisfyingAssignment { + pub fn scalar_inputs(&self) -> Vec { + self.input_assignment.clone() + } + + pub fn scalar_aux(&self) -> Vec { + self.aux_assignment.clone() + } } diff --git a/src/bellperson/test_shape_cs.rs b/src/bellperson/test_shape_cs.rs new file mode 100644 index 00000000..e9cb9002 --- /dev/null +++ b/src/bellperson/test_shape_cs.rs @@ -0,0 +1,326 @@ +//! Support for generating R1CS shape with using bellperson. +//! `TestShapeCS` implements a superset of `ShapeCS`, adding non-trivial namespace support for use in testing. + +use std::{ + cmp::Ordering, + collections::{BTreeMap, HashMap}, +}; + +use crate::traits::Group; +use bellpepper_core::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; +use core::fmt::Write; +use ff::{Field, PrimeField}; + +#[derive(Clone, Copy)] +struct OrderedVariable(Variable); + +#[derive(Debug)] +enum NamedObject { + Constraint(usize), + Var(Variable), + Namespace, +} + +impl Eq for OrderedVariable {} +impl PartialEq for OrderedVariable { + fn eq(&self, other: &OrderedVariable) -> bool { + match (self.0.get_unchecked(), other.0.get_unchecked()) { + (Index::Input(ref a), Index::Input(ref b)) => a == b, + (Index::Aux(ref a), Index::Aux(ref b)) => a == b, + _ => false, + } + } +} +impl PartialOrd for OrderedVariable { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for OrderedVariable { + fn cmp(&self, other: &Self) -> Ordering { + match (self.0.get_unchecked(), other.0.get_unchecked()) { + (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), + (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), + (Index::Input(_), Index::Aux(_)) => Ordering::Less, + (Index::Aux(_), Index::Input(_)) => Ordering::Greater, + } + } +} + +#[allow(clippy::upper_case_acronyms)] +/// `TestShapeCS` is a `ConstraintSystem` for creating `R1CSShape`s for a circuit. +pub struct TestShapeCS +where + G::Scalar: PrimeField + Field, +{ + named_objects: HashMap, + current_namespace: Vec, + #[allow(clippy::type_complexity)] + /// All constraints added to the `TestShapeCS`. + pub constraints: Vec<( + LinearCombination, + LinearCombination, + LinearCombination, + String, + )>, + inputs: Vec, + aux: Vec, +} + +fn proc_lc( + terms: &LinearCombination, +) -> BTreeMap { + let mut map = BTreeMap::new(); + for (var, &coeff) in terms.iter() { + map + .entry(OrderedVariable(var)) + .or_insert_with(|| Scalar::ZERO) + .add_assign(&coeff); + } + + // Remove terms that have a zero coefficient to normalize + let mut to_remove = vec![]; + for (var, coeff) in map.iter() { + if coeff.is_zero().into() { + to_remove.push(*var) + } + } + + for var in to_remove { + map.remove(&var); + } + + map +} + +impl TestShapeCS +where + G::Scalar: PrimeField, +{ + #[allow(unused)] + /// Create a new, default `TestShapeCS`, + pub fn new() -> Self { + TestShapeCS::default() + } + + /// Returns the number of constraints defined for this `TestShapeCS`. + pub fn num_constraints(&self) -> usize { + self.constraints.len() + } + + /// Returns the number of inputs defined for this `TestShapeCS`. + pub fn num_inputs(&self) -> usize { + self.inputs.len() + } + + /// Returns the number of aux inputs defined for this `TestShapeCS`. + pub fn num_aux(&self) -> usize { + self.aux.len() + } + + /// Print all public inputs, aux inputs, and constraint names. + #[allow(dead_code)] + pub fn pretty_print_list(&self) -> Vec { + let mut result = Vec::new(); + + for input in &self.inputs { + result.push(format!("INPUT {input}")); + } + for aux in &self.aux { + result.push(format!("AUX {aux}")); + } + + for (_a, _b, _c, name) in &self.constraints { + result.push(name.to_string()); + } + + result + } + + /// Print all iputs and a detailed representation of each constraint. + #[allow(dead_code)] + pub fn pretty_print(&self) -> String { + let mut s = String::new(); + + for input in &self.inputs { + writeln!(s, "INPUT {}", &input).unwrap() + } + + let negone = -::ONE; + + let powers_of_two = (0..G::Scalar::NUM_BITS) + .map(|i| G::Scalar::from(2u64).pow_vartime([u64::from(i)])) + .collect::>(); + + let pp = |s: &mut String, lc: &LinearCombination| { + s.push('('); + let mut is_first = true; + for (var, coeff) in proc_lc::(lc) { + if coeff == negone { + s.push_str(" - ") + } else if !is_first { + s.push_str(" + ") + } + is_first = false; + + if coeff != ::ONE && coeff != negone { + for (i, x) in powers_of_two.iter().enumerate() { + if x == &coeff { + write!(s, "2^{i} . ").unwrap(); + break; + } + } + + write!(s, "{coeff:?} . ").unwrap() + } + + match var.0.get_unchecked() { + Index::Input(i) => { + write!(s, "`I{}`", &self.inputs[i]).unwrap(); + } + Index::Aux(i) => { + write!(s, "`A{}`", &self.aux[i]).unwrap(); + } + } + } + if is_first { + // Nothing was visited, print 0. + s.push('0'); + } + s.push(')'); + }; + + for (a, b, c, name) in &self.constraints { + s.push('\n'); + + write!(s, "{name}: ").unwrap(); + pp(&mut s, a); + write!(s, " * ").unwrap(); + pp(&mut s, b); + s.push_str(" = "); + pp(&mut s, c); + } + + s.push('\n'); + + s + } + + /// Associate `NamedObject` with `path`. + /// `path` must not already have an associated object. + fn set_named_obj(&mut self, path: String, to: NamedObject) { + assert!( + !self.named_objects.contains_key(&path), + "tried to create object at existing path: {path}" + ); + + self.named_objects.insert(path, to); + } +} + +impl Default for TestShapeCS +where + G::Scalar: PrimeField, +{ + fn default() -> Self { + let mut map = HashMap::new(); + map.insert("ONE".into(), NamedObject::Var(TestShapeCS::::one())); + TestShapeCS { + named_objects: map, + current_namespace: vec![], + constraints: vec![], + inputs: vec![String::from("ONE")], + aux: vec![], + } + } +} + +impl ConstraintSystem for TestShapeCS +where + G::Scalar: PrimeField, +{ + type Root = Self; + + fn alloc(&mut self, annotation: A, _f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let path = compute_path(&self.current_namespace, &annotation().into()); + self.aux.push(path); + + Ok(Variable::new_unchecked(Index::Aux(self.aux.len() - 1))) + } + + fn alloc_input(&mut self, annotation: A, _f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let path = compute_path(&self.current_namespace, &annotation().into()); + self.inputs.push(path); + + Ok(Variable::new_unchecked(Index::Input(self.inputs.len() - 1))) + } + + fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, + { + let path = compute_path(&self.current_namespace, &annotation().into()); + let index = self.constraints.len(); + self.set_named_obj(path.clone(), NamedObject::Constraint(index)); + + let a = a(LinearCombination::zero()); + let b = b(LinearCombination::zero()); + let c = c(LinearCombination::zero()); + + self.constraints.push((a, b, c, path)); + } + + fn push_namespace(&mut self, name_fn: N) + where + NR: Into, + N: FnOnce() -> NR, + { + let name = name_fn().into(); + let path = compute_path(&self.current_namespace, &name); + self.set_named_obj(path, NamedObject::Namespace); + self.current_namespace.push(name); + } + + fn pop_namespace(&mut self) { + assert!(self.current_namespace.pop().is_some()); + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } +} + +fn compute_path(ns: &[String], this: &str) -> String { + assert!( + !this.chars().any(|a| a == '/'), + "'/' is not allowed in names" + ); + + let mut name = String::new(); + + let mut needs_separation = false; + for ns in ns.iter().chain(Some(this.to_string()).iter()) { + if needs_separation { + name += "/"; + } + + name += ns; + needs_separation = true; + } + + name +} diff --git a/src/circuit.rs b/src/circuit.rs index 0b340446..c3511350 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -368,7 +368,7 @@ impl<'a, G: Group, SC: StepCircuit> NovaAugmentedCircuit<'a, G, SC> { #[cfg(test)] mod tests { use super::*; - use crate::bellperson::{shape_cs::ShapeCS, solver::SatisfyingAssignment}; + use crate::bellperson::{solver::SatisfyingAssignment, test_shape_cs::TestShapeCS}; type PastaG1 = pasta_curves::pallas::Point; type PastaG2 = pasta_curves::vesta::Point; @@ -397,7 +397,7 @@ mod tests { // Initialize the shape and ck for the primary let circuit1: NovaAugmentedCircuit<'_, G2, TrivialTestCircuit<::Base>> = NovaAugmentedCircuit::new(&primary_params, None, &ttc1, ro_consts1.clone()); - let mut cs: ShapeCS = ShapeCS::new(); + let mut cs: TestShapeCS = TestShapeCS::new(); let _ = circuit1.synthesize(&mut cs); let (shape1, ck1) = cs.r1cs_shape(); assert_eq!(cs.num_constraints(), num_constraints_primary); @@ -406,7 +406,7 @@ mod tests { // Initialize the shape and ck for the secondary let circuit2: NovaAugmentedCircuit<'_, G1, TrivialTestCircuit<::Base>> = NovaAugmentedCircuit::new(&secondary_params, None, &ttc2, ro_consts2.clone()); - let mut cs: ShapeCS = ShapeCS::new(); + let mut cs: TestShapeCS = TestShapeCS::new(); let _ = circuit2.synthesize(&mut cs); let (shape2, ck2) = cs.r1cs_shape(); assert_eq!(cs.num_constraints(), num_constraints_secondary); diff --git a/src/gadgets/ecc.rs b/src/gadgets/ecc.rs index 29990e76..2e5d4ac8 100644 --- a/src/gadgets/ecc.rs +++ b/src/gadgets/ecc.rs @@ -750,7 +750,7 @@ mod tests { use super::*; use crate::bellperson::{ r1cs::{NovaShape, NovaWitness}, - {shape_cs::ShapeCS, solver::SatisfyingAssignment}, + {solver::SatisfyingAssignment, test_shape_cs::TestShapeCS}, }; use crate::provider::bn256_grumpkin::{bn256, grumpkin}; use ff::{Field, PrimeFieldBits}; @@ -989,7 +989,7 @@ mod tests { G2: Group::Scalar>, { // First create the shape - let mut cs: ShapeCS = ShapeCS::new(); + let mut cs: TestShapeCS = TestShapeCS::new(); let _ = synthesize_smul::(cs.namespace(|| "synthesize")); println!("Number of constraints: {}", cs.num_constraints()); let (shape, ck) = cs.r1cs_shape(); @@ -1042,7 +1042,7 @@ mod tests { G2: Group::Scalar>, { // First create the shape - let mut cs: ShapeCS = ShapeCS::new(); + let mut cs: TestShapeCS = TestShapeCS::new(); let _ = synthesize_add_equal::(cs.namespace(|| "synthesize add equal")); println!("Number of constraints: {}", cs.num_constraints()); let (shape, ck) = cs.r1cs_shape(); @@ -1099,7 +1099,7 @@ mod tests { G2: Group::Scalar>, { // First create the shape - let mut cs: ShapeCS = ShapeCS::new(); + let mut cs: TestShapeCS = TestShapeCS::new(); let _ = synthesize_add_negation::(cs.namespace(|| "synthesize add equal")); println!("Number of constraints: {}", cs.num_constraints()); let (shape, ck) = cs.r1cs_shape(); diff --git a/src/nifs.rs b/src/nifs.rs index 094d767a..7ad33092 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -167,12 +167,12 @@ mod tests { { use crate::bellperson::{ r1cs::{NovaShape, NovaWitness}, - shape_cs::ShapeCS, solver::SatisfyingAssignment, + test_shape_cs::TestShapeCS, }; // First create the shape - let mut cs: ShapeCS = ShapeCS::new(); + let mut cs: TestShapeCS = TestShapeCS::new(); let _ = synthesize_tiny_r1cs_bellperson(&mut cs, None); let (shape, ck) = cs.r1cs_shape(); let ro_consts =