arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/gadgets/utils.rs
src/gadgets/utils.rsRaw
  1use crate::traits::Group;
  2use bellperson::{
  3  gadgets::{
  4    boolean::{AllocatedBit, Boolean},
  5    num::AllocatedNum,
  6    Assignment,
  7  },
  8  ConstraintSystem, LinearCombination, SynthesisError,
  9};
 10use bellperson_nonnative::mp::bignat::{nat_to_limbs, BigNat};
 11use ff::{Field, PrimeField, PrimeFieldBits};
 12use rug::Integer;
 13
 14/// Gets as input the little indian representation of a number and spits out the number
 15#[allow(dead_code)]
 16pub fn le_bits_to_num<Scalar, CS>(
 17  mut cs: CS,
 18  bits: Vec<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.get_value().map(|val| {
 32      if val {
 33        fe.unwrap() + coeff
 34      } else {
 35        fe.unwrap()
 36      }
 37    });
 38    coeff = coeff.double();
 39  }
 40  let num = AllocatedNum::alloc(cs.namespace(|| "Field element"), || {
 41    fe.ok_or(SynthesisError::AssignmentMissing)
 42  })?;
 43  lc = lc - num.get_variable();
 44  cs.enforce(|| "compute number from bits", |lc| lc, |lc| lc, |_| lc);
 45  Ok(num)
 46}
 47
 48/// Allocate a variable that is set to zero
 49pub fn alloc_zero<F: PrimeField, CS: ConstraintSystem<F>>(
 50  mut cs: CS,
 51) -> Result<AllocatedNum<F>, SynthesisError> {
 52  let zero = AllocatedNum::alloc(cs.namespace(|| "alloc"), || Ok(F::zero()))?;
 53  cs.enforce(
 54    || "check zero is valid",
 55    |lc| lc,
 56    |lc| lc,
 57    |lc| lc + zero.get_variable(),
 58  );
 59  Ok(zero)
 60}
 61
 62/// Allocate a variable that is set to one
 63pub fn alloc_one<F: PrimeField, CS: ConstraintSystem<F>>(
 64  mut cs: CS,
 65) -> Result<AllocatedNum<F>, SynthesisError> {
 66  let one = AllocatedNum::alloc(cs.namespace(|| "alloc"), || Ok(F::one()))?;
 67  cs.enforce(
 68    || "check one is valid",
 69    |lc| lc + CS::one(),
 70    |lc| lc + CS::one(),
 71    |lc| lc + one.get_variable(),
 72  );
 73
 74  Ok(one)
 75}
 76
 77/// Allocate a scalar as a base. Only to be used is the scalar fits in base!
 78pub fn alloc_scalar_as_base<G, CS>(
 79  mut cs: CS,
 80  input: Option<G::Scalar>,
 81) -> Result<AllocatedNum<G::Base>, SynthesisError>
 82where
 83  G: Group,
 84  <G as Group>::Scalar: PrimeFieldBits,
 85  CS: ConstraintSystem<<G as Group>::Base>,
 86{
 87  AllocatedNum::alloc(cs.namespace(|| "allocate scalar as base"), || {
 88    let input_bits = input.get()?.clone().to_le_bits();
 89    let mut mult = G::Base::one();
 90    let mut val = G::Base::zero();
 91    for bit in input_bits {
 92      if bit {
 93        val += mult;
 94      }
 95      mult = mult + mult;
 96    }
 97    Ok(val)
 98  })
 99}
100
101/// Allocate bignat a constant
102pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
103  mut cs: CS,
104  val: &Integer,
105  limb_width: usize,
106  n_limbs: usize,
107) -> Result<BigNat<F>, SynthesisError> {
108  let limbs = nat_to_limbs(val, limb_width, n_limbs).unwrap();
109  let bignat = BigNat::alloc_from_limbs(
110    cs.namespace(|| "alloc bignat"),
111    || Ok(limbs.clone()),
112    None,
113    limb_width,
114    n_limbs,
115  )?;
116  // Now enforce that the limbs are all equal to the constants
117  #[allow(clippy::needless_range_loop)]
118  for i in 0..n_limbs {
119    cs.enforce(
120      || format!("check limb {}", i),
121      |lc| lc + &bignat.limbs[i],
122      |lc| lc + CS::one(),
123      |lc| lc + (limbs[i], CS::one()),
124    );
125  }
126  Ok(bignat)
127}
128
129/// Check that two numbers are equal and return a bit
130pub fn alloc_num_equals<F: PrimeField, CS: ConstraintSystem<F>>(
131  mut cs: CS,
132  a: AllocatedNum<F>,
133  b: AllocatedNum<F>,
134) -> Result<AllocatedBit, SynthesisError> {
135  // Allocate and constrain `r`: result boolean bit.
136  // It equals `true` if `a` equals `b`, `false` otherwise
137  let r_value = match (a.get_value(), b.get_value()) {
138    (Some(a), Some(b)) => Some(a == b),
139    _ => None,
140  };
141
142  let r = AllocatedBit::alloc(cs.namespace(|| "r"), r_value)?;
143
144  let delta = AllocatedNum::alloc(cs.namespace(|| "delta"), || {
145    let a_value = *a.get_value().get()?;
146    let b_value = *b.get_value().get()?;
147
148    let mut delta = a_value;
149    delta.sub_assign(&b_value);
150
151    Ok(delta)
152  })?;
153
154  cs.enforce(
155    || "delta = (a - b)",
156    |lc| lc + a.get_variable() - b.get_variable(),
157    |lc| lc + CS::one(),
158    |lc| lc + delta.get_variable(),
159  );
160
161  let delta_inv = AllocatedNum::alloc(cs.namespace(|| "delta_inv"), || {
162    let delta = *delta.get_value().get()?;
163
164    if delta.is_zero().unwrap_u8() == 1 {
165      Ok(F::one()) // we can return any number here, it doesn't matter
166    } else {
167      Ok(delta.invert().unwrap())
168    }
169  })?;
170
171  // Allocate `t = delta * delta_inv`
172  // If `delta` is non-zero (a != b), `t` will equal 1
173  // If `delta` is zero (a == b), `t` cannot equal 1
174  let t = AllocatedNum::alloc(cs.namespace(|| "t"), || {
175    let mut tmp = *delta.get_value().get()?;
176    tmp.mul_assign(&(*delta_inv.get_value().get()?));
177
178    Ok(tmp)
179  })?;
180
181  // Constrain allocation:
182  // t = (a - b) * delta_inv
183  cs.enforce(
184    || "t = (a - b) * delta_inv",
185    |lc| lc + a.get_variable() - b.get_variable(),
186    |lc| lc + delta_inv.get_variable(),
187    |lc| lc + t.get_variable(),
188  );
189
190  // Constrain:
191  // (a - b) * (t - 1) == 0
192  // This enforces that correct `delta_inv` was provided,
193  // and thus `t` is 1 if `(a - b)` is non zero (a != b )
194  cs.enforce(
195    || "(a - b) * (t - 1) == 0",
196    |lc| lc + a.get_variable() - b.get_variable(),
197    |lc| lc + t.get_variable() - CS::one(),
198    |lc| lc,
199  );
200
201  // Constrain:
202  // (a - b) * r == 0
203  // This enforces that `r` is zero if `(a - b)` is non-zero (a != b)
204  cs.enforce(
205    || "(a - b) * r == 0",
206    |lc| lc + a.get_variable() - b.get_variable(),
207    |lc| lc + r.get_variable(),
208    |lc| lc,
209  );
210
211  // Constrain:
212  // (t - 1) * (r - 1) == 0
213  // This enforces that `r` is one if `t` is not one (a == b)
214  cs.enforce(
215    || "(t - 1) * (r - 1) == 0",
216    |lc| lc + t.get_variable() - CS::one(),
217    |lc| lc + r.get_variable() - CS::one(),
218    |lc| lc,
219  );
220
221  Ok(r)
222}
223
224/// If condition return a otherwise b
225pub fn conditionally_select<F: PrimeField, CS: ConstraintSystem<F>>(
226  mut cs: CS,
227  a: &AllocatedNum<F>,
228  b: &AllocatedNum<F>,
229  condition: &Boolean,
230) -> Result<AllocatedNum<F>, SynthesisError> {
231  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
232    if *condition.get_value().get()? {
233      Ok(*a.get_value().get()?)
234    } else {
235      Ok(*b.get_value().get()?)
236    }
237  })?;
238
239  // a * condition + b*(1-condition) = c ->
240  // a * condition - b*condition = c - b
241  cs.enforce(
242    || "conditional select constraint",
243    |lc| lc + a.get_variable() - b.get_variable(),
244    |_| condition.lc(CS::one(), F::one()),
245    |lc| lc + c.get_variable() - b.get_variable(),
246  );
247
248  Ok(c)
249}
250
251/// If condition return a otherwise b where a and b are BigNats
252pub fn conditionally_select_bignat<F: PrimeField, CS: ConstraintSystem<F>>(
253  mut cs: CS,
254  a: &BigNat<F>,
255  b: &BigNat<F>,
256  condition: &Boolean,
257) -> Result<BigNat<F>, SynthesisError> {
258  assert!(a.limbs.len() == b.limbs.len());
259  let c = BigNat::alloc_from_nat(
260    cs.namespace(|| "conditional select result"),
261    || {
262      if *condition.get_value().get()? {
263        Ok(a.value.get()?.clone())
264      } else {
265        Ok(b.value.get()?.clone())
266      }
267    },
268    a.params.limb_width,
269    a.params.n_limbs,
270  )?;
271
272  // a * condition + b*(1-condition) = c ->
273  // a * condition - b*condition = c - b
274  for i in 0..c.limbs.len() {
275    cs.enforce(
276      || format!("conditional select constraint {}", i),
277      |lc| lc + &a.limbs[i] - &b.limbs[i],
278      |_| condition.lc(CS::one(), F::one()),
279      |lc| lc + &c.limbs[i] - &b.limbs[i],
280    );
281  }
282  Ok(c)
283}
284
285/// Same as the above but Condition is an AllocatedNum that needs to be
286/// 0 or 1. 1 => True, 0 => False
287pub fn conditionally_select2<F: PrimeField, CS: ConstraintSystem<F>>(
288  mut cs: CS,
289  a: &AllocatedNum<F>,
290  b: &AllocatedNum<F>,
291  condition: &AllocatedNum<F>,
292) -> Result<AllocatedNum<F>, SynthesisError> {
293  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
294    if *condition.get_value().get()? == F::one() {
295      Ok(*a.get_value().get()?)
296    } else {
297      Ok(*b.get_value().get()?)
298    }
299  })?;
300
301  // a * condition + b*(1-condition) = c ->
302  // a * condition - b*condition = c - b
303  cs.enforce(
304    || "conditional select constraint",
305    |lc| lc + a.get_variable() - b.get_variable(),
306    |lc| lc + condition.get_variable(),
307    |lc| lc + c.get_variable() - b.get_variable(),
308  );
309
310  Ok(c)
311}
312
313/// If condition set to 0 otherwise a
314pub fn select_zero_or<F: PrimeField, CS: ConstraintSystem<F>>(
315  mut cs: CS,
316  a: &AllocatedNum<F>,
317  condition: &AllocatedNum<F>,
318) -> Result<AllocatedNum<F>, SynthesisError> {
319  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
320    if *condition.get_value().get()? == F::one() {
321      Ok(F::zero())
322    } else {
323      Ok(*a.get_value().get()?)
324    }
325  })?;
326
327  // a * (1 - condition) = c
328  cs.enforce(
329    || "conditional select constraint",
330    |lc| lc + a.get_variable(),
331    |lc| lc + CS::one() - condition.get_variable(),
332    |lc| lc + c.get_variable(),
333  );
334
335  Ok(c)
336}
337
338/// If condition set to 1 otherwise a
339pub fn select_one_or<F: PrimeField, CS: ConstraintSystem<F>>(
340  mut cs: CS,
341  a: &AllocatedNum<F>,
342  condition: &AllocatedNum<F>,
343) -> Result<AllocatedNum<F>, SynthesisError> {
344  let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
345    if *condition.get_value().get()? == F::one() {
346      Ok(F::one())
347    } else {
348      Ok(*a.get_value().get()?)
349    }
350  })?;
351
352  cs.enforce(
353    || "conditional select constraint",
354    |lc| lc + CS::one() - a.get_variable(),
355    |lc| lc + condition.get_variable(),
356    |lc| lc + c.get_variable() - a.get_variable(),
357  );
358  Ok(c)
359}