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 }