/ algorithms / src / r1cs / test_constraint_checker.rs
test_constraint_checker.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use crate::r1cs::{errors::SynthesisError, ConstraintSystem, Index, LinearCombination, Variable};
 20  use alphavm_fields::Field;
 21  
 22  /// Constraint system for testing purposes.
 23  pub struct TestConstraintChecker<F: Field> {
 24      // the list of currently applicable input variables
 25      public_variables: Vec<F>,
 26      // the list of currently applicable auxiliary variables
 27      private_variables: Vec<F>,
 28      // whether or not unsatisfactory constraint has been found
 29      found_unsatisfactory_constraint: bool,
 30      // number of constraints
 31      num_constraints: usize,
 32      // constraint path segments in the stack
 33      segments: Vec<String>,
 34      // the first unsatisfied constraint
 35      first_unsatisfied_constraint: Option<String>,
 36  }
 37  
 38  impl<F: Field> Default for TestConstraintChecker<F> {
 39      fn default() -> Self {
 40          Self {
 41              public_variables: vec![F::one()],
 42              private_variables: vec![],
 43              found_unsatisfactory_constraint: false,
 44              num_constraints: 0,
 45              segments: vec![],
 46              first_unsatisfied_constraint: None,
 47          }
 48      }
 49  }
 50  
 51  impl<F: Field> TestConstraintChecker<F> {
 52      pub fn new() -> Self {
 53          Self::default()
 54      }
 55  
 56      pub fn which_is_unsatisfied(&self) -> Option<String> {
 57          self.first_unsatisfied_constraint.clone()
 58      }
 59  
 60      #[inline]
 61      pub fn is_satisfied(&self) -> bool {
 62          !self.found_unsatisfactory_constraint
 63      }
 64  
 65      #[inline]
 66      pub fn num_constraints(&self) -> usize {
 67          self.num_constraints
 68      }
 69  
 70      #[inline]
 71      pub fn public_inputs(&self) -> Vec<F> {
 72          self.public_variables[1..].to_vec()
 73      }
 74  }
 75  
 76  impl<F: Field> ConstraintSystem<F> for TestConstraintChecker<F> {
 77      type Root = Self;
 78  
 79      fn alloc<Fn, A, AR>(&mut self, _annotation: A, f: Fn) -> Result<Variable, SynthesisError>
 80      where
 81          Fn: FnOnce() -> Result<F, SynthesisError>,
 82          A: FnOnce() -> AR,
 83          AR: AsRef<str>,
 84      {
 85          let index = self.private_variables.len();
 86          self.private_variables.push(f()?);
 87          let var = Variable::new_unchecked(Index::Private(index));
 88  
 89          Ok(var)
 90      }
 91  
 92      fn alloc_input<Fn, A, AR>(&mut self, _annotation: A, f: Fn) -> Result<Variable, SynthesisError>
 93      where
 94          Fn: FnOnce() -> Result<F, SynthesisError>,
 95          A: FnOnce() -> AR,
 96          AR: AsRef<str>,
 97      {
 98          let index = self.public_variables.len();
 99          self.public_variables.push(f()?);
100          let var = Variable::new_unchecked(Index::Public(index));
101  
102          Ok(var)
103      }
104  
105      fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
106      where
107          A: FnOnce() -> AR,
108          AR: AsRef<str>,
109          LA: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
110          LB: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
111          LC: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
112      {
113          self.num_constraints += 1;
114  
115          let eval_lc = |lc: Vec<(Variable, F)>| -> F {
116              lc.into_iter()
117                  .map(|(var, coeff)| {
118                      let value = match var.get_unchecked() {
119                          Index::Public(index) => self.public_variables[index],
120                          Index::Private(index) => self.private_variables[index],
121                      };
122                      value * coeff
123                  })
124                  .sum::<F>()
125          };
126  
127          let a = eval_lc(a(LinearCombination::zero()).0);
128          let b = eval_lc(b(LinearCombination::zero()).0);
129          let c = eval_lc(c(LinearCombination::zero()).0);
130  
131          if a * b != c && self.first_unsatisfied_constraint.is_none() {
132              self.found_unsatisfactory_constraint = true;
133  
134              let new = annotation().as_ref().to_string();
135              assert!(!new.contains('/'), "'/' is not allowed in names");
136  
137              let mut path = self.segments.clone();
138              path.push(new);
139              self.first_unsatisfied_constraint = Some(path.join("/"));
140          }
141      }
142  
143      fn push_namespace<NR: AsRef<str>, N: FnOnce() -> NR>(&mut self, name_fn: N) {
144          let new = name_fn().as_ref().to_string();
145          assert!(!new.contains('/'), "'/' is not allowed in names");
146  
147          self.segments.push(new)
148      }
149  
150      fn pop_namespace(&mut self) {
151          self.segments.pop();
152      }
153  
154      #[inline]
155      fn get_root(&mut self) -> &mut Self::Root {
156          self
157      }
158  
159      #[inline]
160      fn num_constraints(&self) -> usize {
161          self.num_constraints()
162      }
163  
164      #[inline]
165      fn num_public_variables(&self) -> usize {
166          self.public_variables.len()
167      }
168  
169      #[inline]
170      fn num_private_variables(&self) -> usize {
171          self.private_variables.len()
172      }
173  
174      #[inline]
175      fn is_in_setup_mode(&self) -> bool {
176          false
177      }
178  }