/ algorithms / src / r1cs / test_constraint_checker.rs
test_constraint_checker.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaVM library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use crate::r1cs::{ConstraintSystem, Index, LinearCombination, Variable, errors::SynthesisError};
 17  use alphavm_fields::Field;
 18  
 19  /// Constraint system for testing purposes.
 20  pub struct TestConstraintChecker<F: Field> {
 21      // the list of currently applicable input variables
 22      public_variables: Vec<F>,
 23      // the list of currently applicable auxiliary variables
 24      private_variables: Vec<F>,
 25      // whether or not unsatisfactory constraint has been found
 26      found_unsatisfactory_constraint: bool,
 27      // number of constraints
 28      num_constraints: usize,
 29      // constraint path segments in the stack
 30      segments: Vec<String>,
 31      // the first unsatisfied constraint
 32      first_unsatisfied_constraint: Option<String>,
 33  }
 34  
 35  impl<F: Field> Default for TestConstraintChecker<F> {
 36      fn default() -> Self {
 37          Self {
 38              public_variables: vec![F::one()],
 39              private_variables: vec![],
 40              found_unsatisfactory_constraint: false,
 41              num_constraints: 0,
 42              segments: vec![],
 43              first_unsatisfied_constraint: None,
 44          }
 45      }
 46  }
 47  
 48  impl<F: Field> TestConstraintChecker<F> {
 49      pub fn new() -> Self {
 50          Self::default()
 51      }
 52  
 53      pub fn which_is_unsatisfied(&self) -> Option<String> {
 54          self.first_unsatisfied_constraint.clone()
 55      }
 56  
 57      #[inline]
 58      pub fn is_satisfied(&self) -> bool {
 59          !self.found_unsatisfactory_constraint
 60      }
 61  
 62      #[inline]
 63      pub fn num_constraints(&self) -> usize {
 64          self.num_constraints
 65      }
 66  
 67      #[inline]
 68      pub fn public_inputs(&self) -> Vec<F> {
 69          self.public_variables[1..].to_vec()
 70      }
 71  }
 72  
 73  impl<F: Field> ConstraintSystem<F> for TestConstraintChecker<F> {
 74      type Root = Self;
 75  
 76      fn alloc<Fn, A, AR>(&mut self, _annotation: A, f: Fn) -> Result<Variable, SynthesisError>
 77      where
 78          Fn: FnOnce() -> Result<F, SynthesisError>,
 79          A: FnOnce() -> AR,
 80          AR: AsRef<str>,
 81      {
 82          let index = self.private_variables.len();
 83          self.private_variables.push(f()?);
 84          let var = Variable::new_unchecked(Index::Private(index));
 85  
 86          Ok(var)
 87      }
 88  
 89      fn alloc_input<Fn, A, AR>(&mut self, _annotation: A, f: Fn) -> Result<Variable, SynthesisError>
 90      where
 91          Fn: FnOnce() -> Result<F, SynthesisError>,
 92          A: FnOnce() -> AR,
 93          AR: AsRef<str>,
 94      {
 95          let index = self.public_variables.len();
 96          self.public_variables.push(f()?);
 97          let var = Variable::new_unchecked(Index::Public(index));
 98  
 99          Ok(var)
100      }
101  
102      fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
103      where
104          A: FnOnce() -> AR,
105          AR: AsRef<str>,
106          LA: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
107          LB: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
108          LC: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
109      {
110          self.num_constraints += 1;
111  
112          let eval_lc = |lc: Vec<(Variable, F)>| -> F {
113              lc.into_iter()
114                  .map(|(var, coeff)| {
115                      let value = match var.get_unchecked() {
116                          Index::Public(index) => self.public_variables[index],
117                          Index::Private(index) => self.private_variables[index],
118                      };
119                      value * coeff
120                  })
121                  .sum::<F>()
122          };
123  
124          let a = eval_lc(a(LinearCombination::zero()).0);
125          let b = eval_lc(b(LinearCombination::zero()).0);
126          let c = eval_lc(c(LinearCombination::zero()).0);
127  
128          if a * b != c && self.first_unsatisfied_constraint.is_none() {
129              self.found_unsatisfactory_constraint = true;
130  
131              let new = annotation().as_ref().to_string();
132              assert!(!new.contains('/'), "'/' is not allowed in names");
133  
134              let mut path = self.segments.clone();
135              path.push(new);
136              self.first_unsatisfied_constraint = Some(path.join("/"));
137          }
138      }
139  
140      fn push_namespace<NR: AsRef<str>, N: FnOnce() -> NR>(&mut self, name_fn: N) {
141          let new = name_fn().as_ref().to_string();
142          assert!(!new.contains('/'), "'/' is not allowed in names");
143  
144          self.segments.push(new)
145      }
146  
147      fn pop_namespace(&mut self) {
148          self.segments.pop();
149      }
150  
151      #[inline]
152      fn get_root(&mut self) -> &mut Self::Root {
153          self
154      }
155  
156      #[inline]
157      fn num_constraints(&self) -> usize {
158          self.num_constraints()
159      }
160  
161      #[inline]
162      fn num_public_variables(&self) -> usize {
163          self.public_variables.len()
164      }
165  
166      #[inline]
167      fn num_private_variables(&self) -> usize {
168          self.private_variables.len()
169      }
170  
171      #[inline]
172      fn is_in_setup_mode(&self) -> bool {
173          false
174      }
175  }