Skip to main content

nova_snark/gadgets/
utils.rs

1//! This module implements various low-level gadgets
2use super::nonnative::bignat::{nat_to_limbs, BigNat};
3use crate::{
4  constants::{BN_LIMB_WIDTH, BN_N_LIMBS},
5  frontend::{
6    num::AllocatedNum, AllocatedBit, Assignment, Boolean, ConstraintSystem, LinearCombination,
7    SynthesisError,
8  },
9  gadgets::nonnative::util::f_to_nat,
10  traits::Engine,
11};
12use ff::{Field, PrimeField, PrimeFieldBits};
13use num_bigint::BigInt;
14
15/// Gets as input the little indian representation of a number and spits out the number
16pub fn le_bits_to_num<Scalar, CS>(
17  mut cs: CS,
18  bits: &[AllocatedBit],
19) -> Result<AllocatedNum<Scalar>, SynthesisError>
20where
21  Scalar: PrimeField + PrimeFieldBits,
22  CS: ConstraintSystem<Scalar>,
23{
24  // We loop over the input bits and construct the constraint
25  // and the field element that corresponds to the result
26  let mut lc = LinearCombination::zero();
27  let mut coeff = Scalar::ONE;
28  let mut fe = Some(Scalar::ZERO);
29  for bit in bits.iter() {
30    lc = lc + (coeff, bit.get_variable());
31    fe = bit
32      .get_value()
33      .and_then(|val| fe.map(|f| if val { f + coeff } else { f }));
34    coeff = coeff.double();
35  }
36  let num = AllocatedNum::alloc(cs.namespace(|| "Field element"), || {
37    fe.ok_or(SynthesisError::AssignmentMissing)
38  })?;
39  lc = lc - num.get_variable();
40  cs.enforce(|| "compute number from bits", |lc| lc, |lc| lc, |_| lc);
41  Ok(num)
42}
43
44/// Allocate a variable that is set to zero
45pub fn alloc_zero<F: PrimeField, CS: ConstraintSystem<F>>(mut cs: CS) -> AllocatedNum<F> {
46  let zero = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || F::ZERO);
47  cs.enforce(
48    || "check zero is valid",
49    |lc| lc,
50    |lc| lc,
51    |lc| lc + zero.get_variable(),
52  );
53  zero
54}
55
56/// Allocate a variable that is set to one
57pub fn alloc_one<F: PrimeField, CS: ConstraintSystem<F>>(mut cs: CS) -> AllocatedNum<F> {
58  let one = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || F::ONE);
59  cs.enforce(
60    || "check one is valid",
61    |lc| lc + CS::one(),
62    |lc| lc + CS::one(),
63    |lc| lc + one.get_variable(),
64  );
65
66  one
67}
68
69/// Allocate a scalar as a base. Only to be used is the scalar fits in base!
70pub fn alloc_scalar_as_base<E, CS>(
71  mut cs: CS,
72  input: Option<E::Scalar>,
73) -> Result<AllocatedNum<E::Base>, SynthesisError>
74where
75  E: Engine,
76  <E as Engine>::Scalar: PrimeFieldBits,
77  CS: ConstraintSystem<<E as Engine>::Base>,
78{
79  AllocatedNum::alloc(cs.namespace(|| "allocate scalar as base"), || {
80    let input_bits = input.unwrap_or(E::Scalar::ZERO).clone().to_le_bits();
81    let mut mult = E::Base::ONE;
82    let mut val = E::Base::ZERO;
83    for bit in input_bits {
84      if bit {
85        val += mult;
86      }
87      mult = mult + mult;
88    }
89    Ok(val)
90  })
91}
92
93/// interpret scalar as base
94/// Only to be used is the scalar fits in base!
95pub fn scalar_as_base<E: Engine>(input: E::Scalar) -> E::Base {
96  field_switch::<E::Scalar, E::Base>(input)
97}
98
99/// interpret base as scalar
100/// Only to be used is the scalar fits in base!
101pub fn base_as_scalar<E: Engine>(input: E::Base) -> E::Scalar {
102  field_switch::<E::Base, E::Scalar>(input)
103}
104
105/// Switch between two fields
106pub fn field_switch<F1: PrimeField + PrimeFieldBits, F2: PrimeField>(x: F1) -> F2 {
107  let input_bits = x.to_le_bits();
108  let mut mult = F2::ONE;
109  let mut val = F2::ZERO;
110  for bit in input_bits {
111    if bit {
112      val += mult;
113    }
114    mult += mult;
115  }
116  val
117}
118
119/// Provide a bignat representation of one field in another field
120pub fn to_bignat_repr<F1: PrimeField + PrimeFieldBits, F2: PrimeField>(x: &F1) -> Vec<F2> {
121  let limbs: Vec<F1> = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap();
122  limbs
123    .iter()
124    .map(|limb| field_switch::<F1, F2>(*limb))
125    .collect()
126}
127
128/// Allocate bignat a constant
129pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
130  mut cs: CS,
131  val: &BigInt,
132  limb_width: usize,
133  n_limbs: usize,
134) -> Result<BigNat<F>, SynthesisError> {
135  let limbs = nat_to_limbs(val, limb_width, n_limbs).unwrap();
136  let bignat = BigNat::alloc_from_limbs(
137    cs.namespace(|| "alloc bignat"),
138    || Ok(limbs.clone()),
139    None,
140    limb_width,
141    n_limbs,
142  )?;
143  // Now enforce that the limbs are all equal to the constants
144  (0..n_limbs).for_each(|i| {
145    cs.enforce(
146      || format!("check limb {i}"),
147      |lc| lc + &bignat.limbs[i],
148      |lc| lc + CS::one(),
149      |lc| lc + (limbs[i], CS::one()),
150    );
151  });
152  Ok(bignat)
153}
154
155/// Check that two numbers are equal and return a bit
156pub fn alloc_num_equals<F: PrimeField, CS: ConstraintSystem<F>>(
157  mut cs: CS,
158  a: &AllocatedNum<F>,
159  b: &AllocatedNum<F>,
160) -> Result<AllocatedBit, SynthesisError> {
161  // Allocate and constrain `r`: result boolean bit.
162  // It equals `true` if `a` equals `b`, `false` otherwise
163  let r_value = match (a.get_value(), b.get_value()) {
164    (Some(a), Some(b)) => Some(a == b),
165    _ => None,
166  };
167
168  let r = AllocatedBit::alloc(cs.namespace(|| "r"), r_value)?;
169
170  // Allocate t s.t. t=1 if z1 == z2 else 1/(z1 - z2)
171
172  let t = AllocatedNum::alloc(cs.namespace(|| "t"), || {
173    Ok(if *a.get_value().get()? == *b.get_value().get()? {
174      F::ONE
175    } else {
176      (*a.get_value().get()? - *b.get_value().get()?)
177        .invert()
178        .unwrap()
179    })
180  })?;
181
182  cs.enforce(
183    || "t*(a - b) = 1 - r",
184    |lc| lc + t.get_variable(),
185    |lc| lc + a.get_variable() - b.get_variable(),
186    |lc| lc + CS::one() - r.get_variable(),
187  );
188
189  cs.enforce(
190    || "r*(a - b) = 0",
191    |lc| lc + r.get_variable(),
192    |lc| lc + a.get_variable() - b.get_variable(),
193    |lc| lc,
194  );
195
196  Ok(r)
197}
198
199/// If condition return a otherwise b
200pub fn conditionally_select<F: PrimeField, CS: ConstraintSystem<F>>(
201  mut cs: CS,
202  a: &AllocatedNum<F>,
203  b: &AllocatedNum<F>,
204  condition: &Boolean,
205) -> Result<AllocatedNum<F>, SynthesisError> {
206  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
207    if *condition.get_value().get()? {
208      Ok(*a.get_value().get()?)
209    } else {
210      Ok(*b.get_value().get()?)
211    }
212  })?;
213
214  // a * condition + b*(1-condition) = c ->
215  // a * condition - b*condition = c - b
216  cs.enforce(
217    || "conditional select constraint",
218    |lc| lc + a.get_variable() - b.get_variable(),
219    |_| condition.lc(CS::one(), F::ONE),
220    |lc| lc + c.get_variable() - b.get_variable(),
221  );
222
223  Ok(c)
224}
225
226/// If condition return a otherwise b
227pub fn conditionally_select_vec<F: PrimeField, CS: ConstraintSystem<F>>(
228  mut cs: CS,
229  a: &[AllocatedNum<F>],
230  b: &[AllocatedNum<F>],
231  condition: &Boolean,
232) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
233  a.iter()
234    .zip(b.iter())
235    .enumerate()
236    .map(|(i, (a, b))| {
237      conditionally_select(cs.namespace(|| format!("select_{i}")), a, b, condition)
238    })
239    .collect::<Result<Vec<AllocatedNum<F>>, SynthesisError>>()
240}
241
242/// If condition return a otherwise b where a and b are `BigNats`
243pub fn conditionally_select_bignat<F: PrimeField, CS: ConstraintSystem<F>>(
244  mut cs: CS,
245  a: &BigNat<F>,
246  b: &BigNat<F>,
247  condition: &Boolean,
248) -> Result<BigNat<F>, SynthesisError> {
249  assert!(a.limbs.len() == b.limbs.len());
250  let c = BigNat::alloc_from_nat(
251    cs.namespace(|| "conditional select result"),
252    || {
253      if *condition.get_value().get()? {
254        Ok(a.value.get()?.clone())
255      } else {
256        Ok(b.value.get()?.clone())
257      }
258    },
259    a.params.limb_width,
260    a.params.n_limbs,
261  )?;
262
263  // a * condition + b*(1-condition) = c ->
264  // a * condition - b*condition = c - b
265  for i in 0..c.limbs.len() {
266    cs.enforce(
267      || format!("conditional select constraint {i}"),
268      |lc| lc + &a.limbs[i] - &b.limbs[i],
269      |_| condition.lc(CS::one(), F::ONE),
270      |lc| lc + &c.limbs[i] - &b.limbs[i],
271    );
272  }
273  Ok(c)
274}
275
276/// Same as the above but Condition is an `AllocatedNum` that needs to be
277/// 0 or 1. 1 => True, 0 => False
278pub fn conditionally_select2<F: PrimeField, CS: ConstraintSystem<F>>(
279  mut cs: CS,
280  a: &AllocatedNum<F>,
281  b: &AllocatedNum<F>,
282  condition: &AllocatedNum<F>,
283) -> Result<AllocatedNum<F>, SynthesisError> {
284  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
285    if *condition.get_value().get()? == F::ONE {
286      Ok(*a.get_value().get()?)
287    } else {
288      Ok(*b.get_value().get()?)
289    }
290  })?;
291
292  // a * condition + b*(1-condition) = c ->
293  // a * condition - b*condition = c - b
294  cs.enforce(
295    || "conditional select constraint",
296    |lc| lc + a.get_variable() - b.get_variable(),
297    |lc| lc + condition.get_variable(),
298    |lc| lc + c.get_variable() - b.get_variable(),
299  );
300
301  Ok(c)
302}
303
304/// If condition set to 0 otherwise a. Condition is an allocated num
305pub fn select_zero_or_num2<F: PrimeField, CS: ConstraintSystem<F>>(
306  mut cs: CS,
307  a: &AllocatedNum<F>,
308  condition: &AllocatedNum<F>,
309) -> Result<AllocatedNum<F>, SynthesisError> {
310  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
311    if *condition.get_value().get()? == F::ONE {
312      Ok(F::ZERO)
313    } else {
314      Ok(*a.get_value().get()?)
315    }
316  })?;
317
318  // a * (1 - condition) = c
319  cs.enforce(
320    || "conditional select constraint",
321    |lc| lc + a.get_variable(),
322    |lc| lc + CS::one() - condition.get_variable(),
323    |lc| lc + c.get_variable(),
324  );
325
326  Ok(c)
327}
328
329/// If condition set to a otherwise 0. Condition is an allocated num
330pub fn select_num_or_zero2<F: PrimeField, CS: ConstraintSystem<F>>(
331  mut cs: CS,
332  a: &AllocatedNum<F>,
333  condition: &AllocatedNum<F>,
334) -> Result<AllocatedNum<F>, SynthesisError> {
335  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
336    if *condition.get_value().get()? == F::ONE {
337      Ok(*a.get_value().get()?)
338    } else {
339      Ok(F::ZERO)
340    }
341  })?;
342
343  cs.enforce(
344    || "conditional select constraint",
345    |lc| lc + a.get_variable(),
346    |lc| lc + condition.get_variable(),
347    |lc| lc + c.get_variable(),
348  );
349
350  Ok(c)
351}
352
353/// If condition set to a otherwise 0
354pub fn select_num_or_zero<F: PrimeField, CS: ConstraintSystem<F>>(
355  mut cs: CS,
356  a: &AllocatedNum<F>,
357  condition: &Boolean,
358) -> Result<AllocatedNum<F>, SynthesisError> {
359  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
360    if *condition.get_value().get()? {
361      Ok(*a.get_value().get()?)
362    } else {
363      Ok(F::ZERO)
364    }
365  })?;
366
367  cs.enforce(
368    || "conditional select constraint",
369    |lc| lc + a.get_variable(),
370    |_| condition.lc(CS::one(), F::ONE),
371    |lc| lc + c.get_variable(),
372  );
373
374  Ok(c)
375}
376
377/// If condition set to 1 otherwise a
378pub fn select_one_or_num2<F: PrimeField, CS: ConstraintSystem<F>>(
379  mut cs: CS,
380  a: &AllocatedNum<F>,
381  condition: &AllocatedNum<F>,
382) -> Result<AllocatedNum<F>, SynthesisError> {
383  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
384    if *condition.get_value().get()? == F::ONE {
385      Ok(F::ONE)
386    } else {
387      Ok(*a.get_value().get()?)
388    }
389  })?;
390
391  cs.enforce(
392    || "conditional select constraint",
393    |lc| lc + CS::one() - a.get_variable(),
394    |lc| lc + condition.get_variable(),
395    |lc| lc + c.get_variable() - a.get_variable(),
396  );
397  Ok(c)
398}
399
400/// If condition set to 1 otherwise a - b
401pub fn select_one_or_diff2<F: PrimeField, CS: ConstraintSystem<F>>(
402  mut cs: CS,
403  a: &AllocatedNum<F>,
404  b: &AllocatedNum<F>,
405  condition: &AllocatedNum<F>,
406) -> Result<AllocatedNum<F>, SynthesisError> {
407  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
408    if *condition.get_value().get()? == F::ONE {
409      Ok(F::ONE)
410    } else {
411      Ok(*a.get_value().get()? - *b.get_value().get()?)
412    }
413  })?;
414
415  cs.enforce(
416    || "conditional select constraint",
417    |lc| lc + CS::one() - a.get_variable() + b.get_variable(),
418    |lc| lc + condition.get_variable(),
419    |lc| lc + c.get_variable() - a.get_variable() + b.get_variable(),
420  );
421  Ok(c)
422}
423
424/// If condition set to a otherwise 1 for boolean conditions
425pub fn select_num_or_one<F: PrimeField, CS: ConstraintSystem<F>>(
426  mut cs: CS,
427  a: &AllocatedNum<F>,
428  condition: &Boolean,
429) -> Result<AllocatedNum<F>, SynthesisError> {
430  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
431    if *condition.get_value().get()? {
432      Ok(*a.get_value().get()?)
433    } else {
434      Ok(F::ONE)
435    }
436  })?;
437
438  cs.enforce(
439    || "conditional select constraint",
440    |lc| lc + a.get_variable() - CS::one(),
441    |_| condition.lc(CS::one(), F::ONE),
442    |lc| lc + c.get_variable() - CS::one(),
443  );
444
445  Ok(c)
446}
447
448/// Allocate a constant value in the circuit
449pub fn alloc_constant<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
450  mut cs: CS,
451  c: &Scalar,
452) -> Result<AllocatedNum<Scalar>, SynthesisError> {
453  let constant = AllocatedNum::alloc(cs.namespace(|| "constant"), || Ok(*c))?;
454
455  cs.enforce(
456    || "check eq given constant",
457    |lc| lc + constant.get_variable(),
458    |lc| lc + CS::one(),
459    |lc| lc + (*c, CS::one()),
460  );
461
462  Ok(constant)
463}
464
465/// Convert a base field element to a BigInt representation.
466pub fn base_as_bigint<E: Engine>(input: E::Base) -> BigInt {
467  let input_bits = input.to_le_bits();
468  let mut mult = BigInt::from(1);
469  let mut val = BigInt::from(0);
470  for bit in input_bits {
471    if bit {
472      val += &mult;
473    }
474    mult *= BigInt::from(2);
475  }
476  val
477}
478
479/// Gets as input the little endian representation of a number (as AllocatedNum bits)
480/// and outputs the number.
481///
482/// Unlike le_bits_to_num which takes AllocatedBit, this takes AllocatedNum where
483/// each num is constrained to be 0 or 1.
484pub fn le_num_to_num<Scalar, CS>(
485  mut cs: CS,
486  bits: &[AllocatedNum<Scalar>],
487) -> Result<AllocatedNum<Scalar>, SynthesisError>
488where
489  Scalar: PrimeField + PrimeFieldBits,
490  CS: ConstraintSystem<Scalar>,
491{
492  // We loop over the input bits and construct the constraint
493  // and the field element that corresponds to the result
494  let mut lc = LinearCombination::zero();
495  let mut coeff = Scalar::ONE;
496  let mut fe = Some(Scalar::ZERO);
497  for bit in bits.iter() {
498    lc = lc + (coeff, bit.get_variable());
499    fe = bit.get_value().map(|val| {
500      if val == Scalar::ONE {
501        fe.unwrap() + coeff
502      } else {
503        fe.unwrap()
504      }
505    });
506    coeff = coeff.double();
507  }
508  let num = AllocatedNum::alloc(cs.namespace(|| "Field element"), || {
509    fe.ok_or(SynthesisError::AssignmentMissing)
510  })?;
511  lc = lc - num.get_variable();
512  cs.enforce(|| "compute number from bits", |lc| lc, |lc| lc, |_| lc);
513  Ok(num)
514}
515
516/// Compute acc_out = acc + c_i * v and c_i_out = c * c_i
517///
518/// This is used to fingerprint values in a circuit by computing a running sum.
519pub fn fingerprint<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
520  mut cs: CS,
521  acc: &AllocatedNum<Scalar>,
522  c: &AllocatedNum<Scalar>,
523  c_i: &AllocatedNum<Scalar>,
524  v: &AllocatedNum<Scalar>,
525) -> Result<(AllocatedNum<Scalar>, AllocatedNum<Scalar>), SynthesisError> {
526  // we need to compute acc_out <- acc + c_i * v (the initial value of acc is zero and c_i = 1)
527  // we then return acc_out and c_i_out = c_i * c
528
529  // (1) we first compute acc_out <- acc + c_i * v
530  let acc_out = AllocatedNum::alloc(cs.namespace(|| "acc_out"), || {
531    Ok(*acc.get_value().get()? + *c_i.get_value().get()? * *v.get_value().get()?)
532  })?;
533  cs.enforce(
534    || "acc_out = acc + c_i * v",
535    |lc| lc + c_i.get_variable(),
536    |lc| lc + v.get_variable(),
537    |lc| lc + acc_out.get_variable() - acc.get_variable(),
538  );
539
540  // (2) we then compute c_i_out <- c * c^i
541  let c_i_out = AllocatedNum::alloc(cs.namespace(|| "c_i_out"), || {
542    Ok(*c_i.get_value().get()? * *c.get_value().get()?)
543  })?;
544  cs.enforce(
545    || "c_i_out = c * c^i",
546    |lc| lc + c.get_variable(),
547    |lc| lc + c_i.get_variable(),
548    |lc| lc + c_i_out.get_variable(),
549  );
550
551  Ok((acc_out, c_i_out))
552}