Nova/src/gadgets/utils.rs
src/gadgets/utils.rsRaw
1//! This module implements various low-level gadgets
2use crate::traits::Group;
3use bellperson::{
4 gadgets::{
5 boolean::{AllocatedBit, Boolean},
6 num::AllocatedNum,
7 Assignment,
8 },
9 ConstraintSystem, LinearCombination, SynthesisError,
10};
11use bellperson_nonnative::mp::bignat::{nat_to_limbs, BigNat};
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: 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.unwrap_or_else(G::Scalar::zero).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/// interepret scalar as base
102pub fn scalar_as_base<G: Group>(input: G::Scalar) -> G::Base {
103 let input_bits = input.to_le_bits();
104 let mut mult = G::Base::one();
105 let mut val = G::Base::zero();
106 for bit in input_bits {
107 if bit {
108 val += mult;
109 }
110 mult = mult + mult;
111 }
112 val
113}
114
115/// Allocate bignat a constant
116pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
117 mut cs: CS,
118 val: &BigInt,
119 limb_width: usize,
120 n_limbs: usize,
121) -> Result<BigNat<F>, SynthesisError> {
122 let limbs = nat_to_limbs(val, limb_width, n_limbs).unwrap();
123 let bignat = BigNat::alloc_from_limbs(
124 cs.namespace(|| "alloc bignat"),
125 || Ok(limbs.clone()),
126 None,
127 limb_width,
128 n_limbs,
129 )?;
130 // Now enforce that the limbs are all equal to the constants
131 #[allow(clippy::needless_range_loop)]
132 for i in 0..n_limbs {
133 cs.enforce(
134 || format!("check limb {}", i),
135 |lc| lc + &bignat.limbs[i],
136 |lc| lc + CS::one(),
137 |lc| lc + (limbs[i], CS::one()),
138 );
139 }
140 Ok(bignat)
141}
142
143/// Check that two numbers are equal and return a bit
144pub fn alloc_num_equals<F: PrimeField, CS: ConstraintSystem<F>>(
145 mut cs: CS,
146 a: &AllocatedNum<F>,
147 b: &AllocatedNum<F>,
148) -> Result<AllocatedBit, SynthesisError> {
149 // Allocate and constrain `r`: result boolean bit.
150 // It equals `true` if `a` equals `b`, `false` otherwise
151 let r_value = match (a.get_value(), b.get_value()) {
152 (Some(a), Some(b)) => Some(a == b),
153 _ => None,
154 };
155
156 let r = AllocatedBit::alloc(cs.namespace(|| "r"), r_value)?;
157
158 let delta = AllocatedNum::alloc(cs.namespace(|| "delta"), || {
159 let a_value = *a.get_value().get()?;
160 let b_value = *b.get_value().get()?;
161
162 let mut delta = a_value;
163 delta.sub_assign(&b_value);
164
165 Ok(delta)
166 })?;
167
168 cs.enforce(
169 || "delta = (a - b)",
170 |lc| lc + a.get_variable() - b.get_variable(),
171 |lc| lc + CS::one(),
172 |lc| lc + delta.get_variable(),
173 );
174
175 let delta_inv = AllocatedNum::alloc(cs.namespace(|| "delta_inv"), || {
176 let delta = *delta.get_value().get()?;
177
178 if delta.is_zero().unwrap_u8() == 1 {
179 Ok(F::one()) // we can return any number here, it doesn't matter
180 } else {
181 Ok(delta.invert().unwrap())
182 }
183 })?;
184
185 // Allocate `t = delta * delta_inv`
186 // If `delta` is non-zero (a != b), `t` will equal 1
187 // If `delta` is zero (a == b), `t` cannot equal 1
188 let t = AllocatedNum::alloc(cs.namespace(|| "t"), || {
189 let mut tmp = *delta.get_value().get()?;
190 tmp.mul_assign(&(*delta_inv.get_value().get()?));
191
192 Ok(tmp)
193 })?;
194
195 // Constrain allocation:
196 // t = (a - b) * delta_inv
197 cs.enforce(
198 || "t = (a - b) * delta_inv",
199 |lc| lc + a.get_variable() - b.get_variable(),
200 |lc| lc + delta_inv.get_variable(),
201 |lc| lc + t.get_variable(),
202 );
203
204 // Constrain:
205 // (a - b) * (t - 1) == 0
206 // This enforces that correct `delta_inv` was provided,
207 // and thus `t` is 1 if `(a - b)` is non zero (a != b )
208 cs.enforce(
209 || "(a - b) * (t - 1) == 0",
210 |lc| lc + a.get_variable() - b.get_variable(),
211 |lc| lc + t.get_variable() - CS::one(),
212 |lc| lc,
213 );
214
215 // Constrain:
216 // (a - b) * r == 0
217 // This enforces that `r` is zero if `(a - b)` is non-zero (a != b)
218 cs.enforce(
219 || "(a - b) * r == 0",
220 |lc| lc + a.get_variable() - b.get_variable(),
221 |lc| lc + r.get_variable(),
222 |lc| lc,
223 );
224
225 // Constrain:
226 // (t - 1) * (r - 1) == 0
227 // This enforces that `r` is one if `t` is not one (a == b)
228 cs.enforce(
229 || "(t - 1) * (r - 1) == 0",
230 |lc| lc + t.get_variable() - CS::one(),
231 |lc| lc + r.get_variable() - CS::one(),
232 |lc| lc,
233 );
234
235 Ok(r)
236}
237
238/// If condition return a otherwise b
239pub fn conditionally_select<F: PrimeField, CS: ConstraintSystem<F>>(
240 mut cs: CS,
241 a: &AllocatedNum<F>,
242 b: &AllocatedNum<F>,
243 condition: &Boolean,
244) -> Result<AllocatedNum<F>, SynthesisError> {
245 let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
246 if *condition.get_value().get()? {
247 Ok(*a.get_value().get()?)
248 } else {
249 Ok(*b.get_value().get()?)
250 }
251 })?;
252
253 // a * condition + b*(1-condition) = c ->
254 // a * condition - b*condition = c - b
255 cs.enforce(
256 || "conditional select constraint",
257 |lc| lc + a.get_variable() - b.get_variable(),
258 |_| condition.lc(CS::one(), F::one()),
259 |lc| lc + c.get_variable() - b.get_variable(),
260 );
261
262 Ok(c)
263}
264
265/// If condition return a otherwise b where a and b are BigNats
266pub fn conditionally_select_bignat<F: PrimeField, CS: ConstraintSystem<F>>(
267 mut cs: CS,
268 a: &BigNat<F>,
269 b: &BigNat<F>,
270 condition: &Boolean,
271) -> Result<BigNat<F>, SynthesisError> {
272 assert!(a.limbs.len() == b.limbs.len());
273 let c = BigNat::alloc_from_nat(
274 cs.namespace(|| "conditional select result"),
275 || {
276 if *condition.get_value().get()? {
277 Ok(a.value.get()?.clone())
278 } else {
279 Ok(b.value.get()?.clone())
280 }
281 },
282 a.params.limb_width,
283 a.params.n_limbs,
284 )?;
285
286 // a * condition + b*(1-condition) = c ->
287 // a * condition - b*condition = c - b
288 for i in 0..c.limbs.len() {
289 cs.enforce(
290 || format!("conditional select constraint {}", i),
291 |lc| lc + &a.limbs[i] - &b.limbs[i],
292 |_| condition.lc(CS::one(), F::one()),
293 |lc| lc + &c.limbs[i] - &b.limbs[i],
294 );
295 }
296 Ok(c)
297}
298
299/// Same as the above but Condition is an AllocatedNum that needs to be
300/// 0 or 1. 1 => True, 0 => False
301pub fn conditionally_select2<F: PrimeField, CS: ConstraintSystem<F>>(
302 mut cs: CS,
303 a: &AllocatedNum<F>,
304 b: &AllocatedNum<F>,
305 condition: &AllocatedNum<F>,
306) -> Result<AllocatedNum<F>, SynthesisError> {
307 let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
308 if *condition.get_value().get()? == F::one() {
309 Ok(*a.get_value().get()?)
310 } else {
311 Ok(*b.get_value().get()?)
312 }
313 })?;
314
315 // a * condition + b*(1-condition) = c ->
316 // a * condition - b*condition = c - b
317 cs.enforce(
318 || "conditional select constraint",
319 |lc| lc + a.get_variable() - b.get_variable(),
320 |lc| lc + condition.get_variable(),
321 |lc| lc + c.get_variable() - b.get_variable(),
322 );
323
324 Ok(c)
325}
326
327/// If condition set to 0 otherwise a
328pub fn select_zero_or<F: PrimeField, CS: ConstraintSystem<F>>(
329 mut cs: CS,
330 a: &AllocatedNum<F>,
331 condition: &AllocatedNum<F>,
332) -> Result<AllocatedNum<F>, SynthesisError> {
333 let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
334 if *condition.get_value().get()? == F::one() {
335 Ok(F::zero())
336 } else {
337 Ok(*a.get_value().get()?)
338 }
339 })?;
340
341 // a * (1 - condition) = c
342 cs.enforce(
343 || "conditional select constraint",
344 |lc| lc + a.get_variable(),
345 |lc| lc + CS::one() - condition.get_variable(),
346 |lc| lc + c.get_variable(),
347 );
348
349 Ok(c)
350}
351
352/// If condition set to 1 otherwise a
353pub fn select_one_or<F: PrimeField, CS: ConstraintSystem<F>>(
354 mut cs: CS,
355 a: &AllocatedNum<F>,
356 condition: &AllocatedNum<F>,
357) -> Result<AllocatedNum<F>, SynthesisError> {
358 let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || {
359 if *condition.get_value().get()? == F::one() {
360 Ok(F::one())
361 } else {
362 Ok(*a.get_value().get()?)
363 }
364 })?;
365
366 cs.enforce(
367 || "conditional select constraint",
368 |lc| lc + CS::one() - a.get_variable(),
369 |lc| lc + condition.get_variable(),
370 |lc| lc + c.get_variable() - a.get_variable(),
371 );
372 Ok(c)
373}