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