1use crate::Index;
17use snarkvm_fields::PrimeField;
18
19use indexmap::IndexMap;
20use std::sync::Arc;
21
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
23pub enum AssignmentVariable<F: PrimeField> {
24 Constant(F),
25 Public(Index),
26 Private(Index),
27}
28
29impl<F: PrimeField> From<&crate::Variable<F>> for AssignmentVariable<F> {
30 fn from(variable: &crate::Variable<F>) -> Self {
32 match variable {
33 crate::Variable::Constant(value) => Self::Constant(**value),
34 crate::Variable::Public(index_value) => {
35 let (index, _value) = index_value.as_ref();
36 Self::Public(*index)
37 }
38 crate::Variable::Private(index_value) => {
39 let (index, _value) = index_value.as_ref();
40 Self::Private(*index)
41 }
42 }
43 }
44}
45
46#[derive(Clone, Debug)]
47pub struct AssignmentLC<F: PrimeField> {
48 constant: F,
49 terms: Vec<(AssignmentVariable<F>, F)>,
50}
51
52impl<F: PrimeField> From<&crate::LinearCombination<F>> for AssignmentLC<F> {
53 fn from(lc: &crate::LinearCombination<F>) -> Self {
55 Self {
56 constant: lc.to_constant(),
57 terms: FromIterator::from_iter(
58 lc.to_terms().iter().map(|(variable, coefficient)| (variable.into(), *coefficient)),
59 ),
60 }
61 }
62}
63
64impl<F: PrimeField> AssignmentLC<F> {
65 pub const fn constant(&self) -> F {
67 self.constant
68 }
69
70 pub const fn terms(&self) -> &Vec<(AssignmentVariable<F>, F)> {
72 &self.terms
73 }
74
75 pub(super) fn num_nonzeros(&self) -> u64 {
77 match self.constant.is_zero() {
79 true => self.terms.len() as u64,
80 false => (self.terms.len() as u64).saturating_add(1),
81 }
82 }
83}
84
85#[derive(Clone, Debug)]
88pub struct Assignment<F: PrimeField> {
89 public: Arc<[(Index, F)]>,
91 private: Arc<[(Index, F)]>,
93 constraints: Arc<[(AssignmentLC<F>, AssignmentLC<F>, AssignmentLC<F>)]>,
95 num_variables: u64,
97}
98
99impl<F: PrimeField> From<crate::R1CS<F>> for Assignment<F> {
100 fn from(r1cs: crate::R1CS<F>) -> Self {
102 Self {
103 public: FromIterator::from_iter(
104 r1cs.to_public_variables().iter().map(|variable| (variable.index(), variable.value())),
105 ),
106 private: FromIterator::from_iter(
107 r1cs.to_private_variables().iter().map(|variable| (variable.index(), variable.value())),
108 ),
109 constraints: FromIterator::from_iter(r1cs.to_constraints().iter().map(|constraint| {
110 let (a, b, c) = constraint.to_terms();
111 (a.into(), b.into(), c.into())
112 })),
113 num_variables: r1cs.num_variables(),
114 }
115 }
116}
117
118impl<F: PrimeField> Assignment<F> {
119 pub const fn public_inputs(&self) -> &Arc<[(Index, F)]> {
121 &self.public
122 }
123
124 pub const fn private_inputs(&self) -> &Arc<[(Index, F)]> {
126 &self.private
127 }
128
129 pub const fn constraints(&self) -> &Arc<[(AssignmentLC<F>, AssignmentLC<F>, AssignmentLC<F>)]> {
131 &self.constraints
132 }
133
134 pub fn num_public(&self) -> u64 {
136 self.public.len() as u64
137 }
138
139 pub fn num_private(&self) -> u64 {
141 self.private.len() as u64
142 }
143
144 pub fn num_variables(&self) -> u64 {
146 self.num_variables
147 }
148
149 pub fn num_constraints(&self) -> u64 {
151 self.constraints.len() as u64
152 }
153
154 pub fn num_nonzeros(&self) -> (u64, u64, u64) {
156 self.constraints
157 .iter()
158 .map(|(a, b, c)| (a.num_nonzeros(), b.num_nonzeros(), c.num_nonzeros()))
159 .fold((0, 0, 0), |(a, b, c), (x, y, z)| (a.saturating_add(x), b.saturating_add(y), c.saturating_add(z)))
160 }
161}
162
163impl<F: PrimeField> snarkvm_algorithms::r1cs::ConstraintSynthesizer<F> for Assignment<F> {
164 fn generate_constraints<CS: snarkvm_algorithms::r1cs::ConstraintSystem<F>>(
166 &self,
167 cs: &mut CS,
168 ) -> Result<(), snarkvm_algorithms::r1cs::SynthesisError> {
169 struct Converter {
171 public: IndexMap<u64, snarkvm_algorithms::r1cs::Variable>,
172 private: IndexMap<u64, snarkvm_algorithms::r1cs::Variable>,
173 }
174
175 let mut converter = Converter { public: Default::default(), private: Default::default() };
176
177 assert_eq!(1, cs.num_public_variables());
179 assert_eq!(0, cs.num_private_variables());
180 assert_eq!(0, cs.num_constraints());
181
182 let result = converter.public.insert(0, CS::one());
183 assert!(result.is_none(), "Overwrote an existing public variable in the converter");
184
185 for (i, (index, value)) in self.public.iter().skip(1).enumerate() {
188 assert_eq!((i + 1) as u64, *index, "Public vars in first system must be processed in lexicographic order");
189
190 let gadget = cs.alloc_input(|| format!("Public {i}"), || Ok(*value))?;
191
192 assert_eq!(
193 snarkvm_algorithms::r1cs::Index::Public(*index as usize),
194 gadget.get_unchecked(),
195 "Public variables in the second system must match the first system (with an off-by-1 for the public case)"
196 );
197
198 let result = converter.public.insert(*index, gadget);
199
200 assert!(result.is_none(), "Overwrote an existing public variable in the converter");
201 }
202
203 for (i, (index, value)) in self.private.iter().enumerate() {
205 assert_eq!(i as u64, *index, "Private variables in first system must be processed in lexicographic order");
206
207 let gadget = cs.alloc(|| format!("Private {i}"), || Ok(*value))?;
208
209 assert_eq!(
210 snarkvm_algorithms::r1cs::Index::Private(i),
211 gadget.get_unchecked(),
212 "Private variables in the second system must match the first system"
213 );
214
215 let result = converter.private.insert(*index, gadget);
216
217 assert!(result.is_none(), "Overwrote an existing private variable in the converter");
218 }
219
220 for (i, (a, b, c)) in self.constraints.iter().enumerate() {
222 let convert_linear_combination = |lc: &AssignmentLC<F>| -> snarkvm_algorithms::r1cs::LinearCombination<F> {
224 let mut linear_combination = snarkvm_algorithms::r1cs::LinearCombination::<F>::zero();
226
227 for (variable, coefficient) in lc.terms.iter() {
229 match variable {
230 AssignmentVariable::Constant(_) => {
231 unreachable!(
232 "Failed during constraint translation. The first system by definition cannot have constant variables in the terms"
233 )
234 }
235 AssignmentVariable::Public(index) => {
236 let gadget = converter.public.get(index).unwrap();
237 assert_eq!(
238 snarkvm_algorithms::r1cs::Index::Public(*index as usize),
239 gadget.get_unchecked(),
240 "Failed during constraint translation. The public variable in the second system must match the first system (with an off-by-1 for the public case)"
241 );
242 linear_combination += (*coefficient, *gadget);
243 }
244 AssignmentVariable::Private(index) => {
245 let gadget = converter.private.get(index).unwrap();
246 assert_eq!(
247 snarkvm_algorithms::r1cs::Index::Private(*index as usize),
248 gadget.get_unchecked(),
249 "Failed during constraint translation. The private variable in the second system must match the first system"
250 );
251 linear_combination += (*coefficient, *gadget);
252 }
253 }
254 }
255
256 if !lc.constant.is_zero() {
258 linear_combination += (
259 lc.constant,
260 snarkvm_algorithms::r1cs::Variable::new_unchecked(snarkvm_algorithms::r1cs::Index::Public(0)),
261 );
262 }
263
264 linear_combination
266 };
267
268 cs.enforce(
269 || format!("Constraint {i}"),
270 |lc| lc + convert_linear_combination(a),
271 |lc| lc + convert_linear_combination(b),
272 |lc| lc + convert_linear_combination(c),
273 );
274 }
275
276 assert_eq!(self.num_public(), cs.num_public_variables() as u64);
278 assert_eq!(self.num_private(), cs.num_private_variables() as u64);
279 assert_eq!(self.num_constraints(), cs.num_constraints() as u64);
280
281 Ok(())
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use snarkvm_algorithms::{AlgebraicSponge, SNARK, r1cs::ConstraintSynthesizer};
288 use snarkvm_circuit::prelude::*;
289 use snarkvm_curves::bls12_377::Fr;
290
291 fn create_example_circuit<E: Environment>() -> Field<E> {
293 let one = snarkvm_console_types::Field::<E::Network>::one();
294 let two = one + one;
295
296 const EXPONENT: u64 = 64;
297
298 let mut candidate = Field::<E>::new(Mode::Public, one);
300 let mut accumulator = Field::new(Mode::Private, two);
301 for _ in 0..EXPONENT {
302 candidate += &accumulator;
303 accumulator *= Field::new(Mode::Private, two);
304 }
305
306 assert_eq!((accumulator - Field::one()).eject_value(), candidate.eject_value());
307 assert_eq!(2, E::num_public());
308 assert_eq!(2 * EXPONENT + 1, E::num_private());
309 assert_eq!(EXPONENT, E::num_constraints());
310 assert!(E::is_satisfied());
311
312 candidate
313 }
314
315 #[test]
316 fn test_constraint_converter() {
317 let _candidate_output = create_example_circuit::<Circuit>();
318 let assignment = Circuit::eject_assignment_and_reset();
319 assert_eq!(0, Circuit::num_constants());
320 assert_eq!(1, Circuit::num_public());
321 assert_eq!(0, Circuit::num_private());
322 assert_eq!(0, Circuit::num_constraints());
323
324 let mut cs = snarkvm_algorithms::r1cs::TestConstraintSystem::new();
325 assignment.generate_constraints(&mut cs).unwrap();
326 {
327 use snarkvm_algorithms::r1cs::ConstraintSystem;
328 assert_eq!(assignment.num_public(), cs.num_public_variables() as u64);
329 assert_eq!(assignment.num_private(), cs.num_private_variables() as u64);
330 assert_eq!(assignment.num_constraints(), cs.num_constraints() as u64);
331 assert!(cs.is_satisfied());
332 }
333 }
334
335 #[test]
336 fn test_varuna() {
337 let _candidate_output = create_example_circuit::<Circuit>();
338 let assignment = Circuit::eject_assignment_and_reset();
339 assert_eq!(0, Circuit::num_constants());
340 assert_eq!(1, Circuit::num_public());
341 assert_eq!(0, Circuit::num_private());
342 assert_eq!(0, Circuit::num_constraints());
343
344 use snarkvm_algorithms::{
347 crypto_hash::PoseidonSponge,
348 snark::varuna::{VarunaHidingMode, VarunaSNARK, ahp::AHPForR1CS},
349 };
350 use snarkvm_curves::bls12_377::{Bls12_377, Fq};
351 use snarkvm_utilities::rand::TestRng;
352
353 type FS = PoseidonSponge<Fq, 2, 1>;
354 type VarunaInst = VarunaSNARK<Bls12_377, FS, VarunaHidingMode>;
355
356 let rng = &mut TestRng::default();
357
358 let max_degree = AHPForR1CS::<Fr, VarunaHidingMode>::max_degree(200, 200, 300).unwrap();
359 let universal_srs = VarunaInst::universal_setup(max_degree).unwrap();
360 let universal_prover = &universal_srs.to_universal_prover().unwrap();
361 let universal_verifier = &universal_srs.to_universal_verifier().unwrap();
362 let fs_pp = FS::sample_parameters();
363
364 let (index_pk, index_vk) = VarunaInst::circuit_setup(&universal_srs, &assignment).unwrap();
365 println!("Called circuit setup");
366
367 let proof = VarunaInst::prove(universal_prover, &fs_pp, &index_pk, &assignment, rng).unwrap();
368 println!("Called prover");
369
370 let one = <Circuit as Environment>::BaseField::one();
371 assert!(VarunaInst::verify(universal_verifier, &fs_pp, &index_vk, [one, one], &proof).unwrap());
372 println!("Called verifier");
373 println!("\nShould not verify (i.e. verifier messages should print below):");
374 assert!(!VarunaInst::verify(universal_verifier, &fs_pp, &index_vk, [one, one + one], &proof).unwrap());
375 }
376}