snarkvm_r1cs/
test_constraint_checker.rs

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