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