1use ff::{PrimeField, PrimeFieldBits};
4use serde::{Deserialize, Serialize};
5
6use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
7
8use crate::gadgets::boolean::{self, AllocatedBit, Boolean};
9
10#[derive(Debug, Serialize, Deserialize)]
11pub struct AllocatedNum<Scalar: PrimeField> {
12 value: Option<Scalar>,
13 variable: Variable,
14}
15
16impl<Scalar: PrimeField> Clone for AllocatedNum<Scalar> {
17 fn clone(&self) -> Self {
18 AllocatedNum {
19 value: self.value,
20 variable: self.variable,
21 }
22 }
23}
24
25impl<Scalar: PrimeField> AllocatedNum<Scalar> {
26 pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
28 where
29 CS: ConstraintSystem<Scalar>,
30 F: FnOnce() -> Result<Scalar, SynthesisError>,
31 {
32 let mut new_value = None;
33 let var = cs.alloc(
34 || "num",
35 || {
36 let tmp = value()?;
37
38 new_value = Some(tmp);
39
40 Ok(tmp)
41 },
42 )?;
43
44 Ok(AllocatedNum {
45 value: new_value,
46 variable: var,
47 })
48 }
49
50 pub fn alloc_infallible<CS, F>(cs: CS, value: F) -> Self
53 where
54 CS: ConstraintSystem<Scalar>,
55 F: FnOnce() -> Scalar,
56 {
57 Self::alloc(cs, || Ok(value())).unwrap()
58 }
59
60 pub fn alloc_input<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
62 where
63 CS: ConstraintSystem<Scalar>,
64 F: FnOnce() -> Result<Scalar, SynthesisError>,
65 {
66 let mut new_value = None;
67 let var = cs.alloc_input(
68 || "input num",
69 || {
70 let tmp = value()?;
71
72 new_value = Some(tmp);
73
74 Ok(tmp)
75 },
76 )?;
77
78 Ok(AllocatedNum {
79 value: new_value,
80 variable: var,
81 })
82 }
83
84 pub fn alloc_maybe_input<CS, F>(
89 cs: CS,
90 is_input: bool,
91 value: F,
92 ) -> Result<Self, SynthesisError>
93 where
94 CS: ConstraintSystem<Scalar>,
95 F: FnOnce() -> Result<Scalar, SynthesisError>,
96 {
97 if is_input {
98 Self::alloc_input(cs, value)
99 } else {
100 Self::alloc(cs, value)
101 }
102 }
103
104 pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
105 where
106 CS: ConstraintSystem<Scalar>,
107 {
108 let input = cs.alloc_input(
109 || "input variable",
110 || self.value.ok_or(SynthesisError::AssignmentMissing),
111 )?;
112
113 cs.enforce(
114 || "enforce input is correct",
115 |lc| lc + input,
116 |lc| lc + CS::one(),
117 |lc| lc + self.variable,
118 );
119
120 Ok(())
121 }
122
123 pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
129 where
130 CS: ConstraintSystem<Scalar>,
131 Scalar: PrimeFieldBits,
132 {
133 pub fn kary_and<Scalar, CS>(
134 mut cs: CS,
135 v: &[AllocatedBit],
136 ) -> Result<AllocatedBit, SynthesisError>
137 where
138 Scalar: PrimeField,
139 CS: ConstraintSystem<Scalar>,
140 {
141 assert!(!v.is_empty());
142
143 let mut cur = None;
146
147 for (i, v) in v.iter().enumerate() {
148 if cur.is_none() {
149 cur = Some(v.clone());
150 } else {
151 cur = Some(AllocatedBit::and(
152 cs.namespace(|| format!("and {}", i)),
153 cur.as_ref().unwrap(),
154 v,
155 )?);
156 }
157 }
158
159 Ok(cur.expect("v.len() > 0"))
160 }
161
162 let a = self.value.map(|e| e.to_le_bits());
165 let b = (-Scalar::ONE).to_le_bits();
166
167 let mut a = a.as_ref().map(|e| e.into_iter().rev());
169
170 let mut result = vec![];
171
172 let mut last_run = None;
174 let mut current_run = vec![];
175
176 let mut found_one = false;
177 let mut i = 0;
178 for b in b.into_iter().rev() {
179 let a_bit: Option<bool> = a.as_mut().map(|e| *e.next().unwrap());
180
181 found_one |= b;
183 if !found_one {
184 if let Some(a_bit) = a_bit {
186 assert!(!a_bit);
187 }
188 continue;
189 }
190
191 if b {
192 let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?;
195 current_run.push(a_bit.clone());
197 result.push(a_bit);
198 } else {
199 if !current_run.is_empty() {
200 if last_run.is_some() {
204 current_run.push(last_run.clone().unwrap());
205 }
206 last_run = Some(kary_and(
207 cs.namespace(|| format!("run ending at {}", i)),
208 ¤t_run,
209 )?);
210 current_run.truncate(0);
211 }
212
213 let a_bit = AllocatedBit::alloc_conditionally(
219 cs.namespace(|| format!("bit {}", i)),
220 a_bit,
221 last_run.as_ref().expect("char always starts with a one"),
222 )?;
223 result.push(a_bit);
224 }
225
226 i += 1;
227 }
228
229 assert_eq!(current_run.len(), 0);
232
233 let mut lc = LinearCombination::zero();
237 let mut coeff = Scalar::ONE;
238
239 for bit in result.iter().rev() {
240 lc = lc + (coeff, bit.get_variable());
241
242 coeff = coeff.double();
243 }
244
245 lc = lc - self.variable;
246
247 cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
248
249 Ok(result.into_iter().map(Boolean::from).rev().collect())
251 }
252
253 pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
257 where
258 CS: ConstraintSystem<Scalar>,
259 Scalar: PrimeFieldBits,
260 {
261 let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?;
262
263 let mut lc = LinearCombination::zero();
264 let mut coeff = Scalar::ONE;
265
266 for bit in bits.iter() {
267 lc = lc + (coeff, bit.get_variable());
268
269 coeff = coeff.double();
270 }
271
272 lc = lc - self.variable;
273
274 cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
275
276 Ok(bits.into_iter().map(Boolean::from).collect())
277 }
278
279 pub fn add<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
280 where
281 CS: ConstraintSystem<Scalar>,
282 {
283 let mut value = None;
284
285 let var = cs.alloc(
286 || "sum num",
287 || {
288 let mut tmp = self.value.ok_or(SynthesisError::AssignmentMissing)?;
289 tmp.add_assign(other.value.ok_or(SynthesisError::AssignmentMissing)?);
290
291 value = Some(tmp);
292
293 Ok(tmp)
294 },
295 )?;
296
297 cs.enforce(
299 || "addition constraint",
300 |lc| lc + self.variable + other.variable,
301 |lc| lc + CS::one(),
302 |lc| lc + var,
303 );
304
305 Ok(AllocatedNum {
306 value,
307 variable: var,
308 })
309 }
310
311 pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
312 where
313 CS: ConstraintSystem<Scalar>,
314 {
315 let mut value = None;
316
317 let var = cs.alloc(
318 || "product num",
319 || {
320 let mut tmp = self.value.ok_or(SynthesisError::AssignmentMissing)?;
321 tmp.mul_assign(other.value.ok_or(SynthesisError::AssignmentMissing)?);
322
323 value = Some(tmp);
324
325 Ok(tmp)
326 },
327 )?;
328
329 cs.enforce(
331 || "multiplication constraint",
332 |lc| lc + self.variable,
333 |lc| lc + other.variable,
334 |lc| lc + var,
335 );
336
337 Ok(AllocatedNum {
338 value,
339 variable: var,
340 })
341 }
342
343 pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
344 where
345 CS: ConstraintSystem<Scalar>,
346 {
347 let mut value = None;
348
349 let var = cs.alloc(
350 || "squared num",
351 || {
352 let mut tmp = self.value.ok_or(SynthesisError::AssignmentMissing)?;
353 tmp = tmp.square();
354
355 value = Some(tmp);
356
357 Ok(tmp)
358 },
359 )?;
360
361 cs.enforce(
363 || "squaring constraint",
364 |lc| lc + self.variable,
365 |lc| lc + self.variable,
366 |lc| lc + var,
367 );
368
369 Ok(AllocatedNum {
370 value,
371 variable: var,
372 })
373 }
374
375 pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
376 where
377 CS: ConstraintSystem<Scalar>,
378 {
379 let inv = cs.alloc(
380 || "ephemeral inverse",
381 || {
382 let tmp = self.value.ok_or(SynthesisError::AssignmentMissing)?;
383
384 if tmp.is_zero().into() {
385 Err(SynthesisError::DivisionByZero)
386 } else {
387 Ok(tmp.invert().unwrap())
388 }
389 },
390 )?;
391
392 cs.enforce(
396 || "nonzero assertion constraint",
397 |lc| lc + self.variable,
398 |lc| lc + inv,
399 |lc| lc + CS::one(),
400 );
401
402 Ok(())
403 }
404
405 pub fn conditionally_reverse<CS>(
409 mut cs: CS,
410 a: &Self,
411 b: &Self,
412 condition: &Boolean,
413 ) -> Result<(Self, Self), SynthesisError>
414 where
415 CS: ConstraintSystem<Scalar>,
416 {
417 let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
418 if condition
419 .get_value()
420 .ok_or(SynthesisError::AssignmentMissing)?
421 {
422 Ok(b.value.ok_or(SynthesisError::AssignmentMissing)?)
423 } else {
424 Ok(a.value.ok_or(SynthesisError::AssignmentMissing)?)
425 }
426 })?;
427
428 cs.enforce(
429 || "first conditional reversal",
430 |lc| lc + a.variable - b.variable,
431 |_| condition.lc(CS::one(), Scalar::ONE),
432 |lc| lc + a.variable - c.variable,
433 );
434
435 let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
436 if condition
437 .get_value()
438 .ok_or(SynthesisError::AssignmentMissing)?
439 {
440 Ok(a.value.ok_or(SynthesisError::AssignmentMissing)?)
441 } else {
442 Ok(b.value.ok_or(SynthesisError::AssignmentMissing)?)
443 }
444 })?;
445
446 cs.enforce(
447 || "second conditional reversal",
448 |lc| lc + b.variable - a.variable,
449 |_| condition.lc(CS::one(), Scalar::ONE),
450 |lc| lc + b.variable - d.variable,
451 );
452
453 Ok((c, d))
454 }
455
456 pub fn get_value(&self) -> Option<Scalar> {
457 self.value
458 }
459
460 pub fn get_variable(&self) -> Variable {
461 self.variable
462 }
463}
464
465#[derive(Debug, Clone)]
466pub struct Num<Scalar: PrimeField> {
467 value: Option<Scalar>,
468 lc: LinearCombination<Scalar>,
469}
470
471impl<Scalar: PrimeField> From<AllocatedNum<Scalar>> for Num<Scalar> {
472 fn from(num: AllocatedNum<Scalar>) -> Num<Scalar> {
473 Num {
474 value: num.value,
475 lc: LinearCombination::<Scalar>::from_variable(num.variable),
476 }
477 }
478}
479
480impl<Scalar: PrimeField> Num<Scalar> {
481 pub fn zero() -> Self {
482 Num {
483 value: Some(Scalar::ZERO),
484 lc: LinearCombination::zero(),
485 }
486 }
487
488 pub fn get_value(&self) -> Option<Scalar> {
489 self.value
490 }
491
492 pub fn lc(&self, coeff: Scalar) -> LinearCombination<Scalar> {
493 LinearCombination::zero() + (coeff, &self.lc)
494 }
495
496 pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: Scalar) -> Self {
497 let newval = match (self.value, bit.get_value()) {
498 (Some(mut curval), Some(bval)) => {
499 if bval {
500 curval.add_assign(&coeff);
501 }
502
503 Some(curval)
504 }
505 _ => None,
506 };
507
508 Num {
509 value: newval,
510 lc: self.lc + &bit.lc(one, coeff),
511 }
512 }
513
514 #[allow(clippy::should_implement_trait)]
515 pub fn add(self, other: &Self) -> Self {
516 let lc = self.lc + &other.lc;
517 let value = match (self.value, other.value) {
518 (Some(v1), Some(v2)) => {
519 let mut tmp = v1;
520 tmp.add_assign(&v2);
521 Some(tmp)
522 }
523 (Some(v), None) | (None, Some(v)) => Some(v),
524 (None, None) => None,
525 };
526
527 Num { value, lc }
528 }
529
530 pub fn scale(mut self, scalar: Scalar) -> Self {
531 for (_variable, fr) in self.lc.iter_mut() {
532 fr.mul_assign(&scalar);
533 }
534
535 if let Some(ref mut v) = self.value {
536 v.mul_assign(&scalar);
537 }
538
539 self
540 }
541}
542
543#[cfg(test)]
544mod test {
545 use std::ops::{AddAssign, MulAssign, SubAssign};
546
547 use crate::ConstraintSystem;
548 use blstrs::Scalar as Fr;
549 use ff::{Field, PrimeField, PrimeFieldBits};
550 use rand_core::SeedableRng;
551 use rand_xorshift::XorShiftRng;
552
553 use super::{AllocatedNum, Boolean, Num};
554 use crate::util_cs::test_cs::*;
555
556 #[test]
557 fn test_allocated_num() {
558 let mut cs = TestConstraintSystem::<Fr>::new();
559
560 AllocatedNum::alloc(&mut cs, || Ok(Fr::ONE)).unwrap();
561
562 assert!(cs.get("num") == Fr::ONE);
563 }
564
565 #[test]
566 fn test_allocated_infallible_num() {
567 let mut cs = TestConstraintSystem::<Fr>::new();
568
569 AllocatedNum::alloc_infallible(&mut cs, || Fr::ONE);
570
571 assert!(cs.get("num") == Fr::ONE);
572 }
573
574 #[test]
575 fn test_num_addition() {
576 let mut cs = TestConstraintSystem::<Fr>::new();
577
578 let mut char = Fr::char();
579 char[0] -= 1u8;
580 let mod_minus_one = Fr::from_repr(char);
581 assert!(bool::from(mod_minus_one.is_some()));
582 let mod_minus_one = mod_minus_one.unwrap();
583
584 let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(mod_minus_one)).unwrap();
585 let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::ONE)).unwrap();
586 let c = a.add(&mut cs, &b).unwrap();
587
588 assert!(cs.is_satisfied());
589 assert!(cs.get("sum num") == Fr::ZERO);
590 assert!(c.value.unwrap() == Fr::ZERO);
591 cs.set("sum num", Fr::ONE);
592 assert!(!cs.is_satisfied());
593 }
594
595 #[test]
596 fn test_num_squaring() {
597 let mut cs = TestConstraintSystem::<Fr>::new();
598
599 let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from(3u64))).unwrap();
600 let n2 = n.square(&mut cs).unwrap();
601
602 assert!(cs.is_satisfied());
603 assert!(cs.get("squared num") == Fr::from(9u64));
604 assert!(n2.value.unwrap() == Fr::from(9u64));
605 cs.set("squared num", Fr::from(10u64));
606 assert!(!cs.is_satisfied());
607 }
608
609 #[test]
610 fn test_num_multiplication() {
611 let mut cs = TestConstraintSystem::<Fr>::new();
612
613 let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from(12u64))).unwrap();
614 let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from(10u64))).unwrap();
615 let n3 = n.mul(&mut cs, &n2).unwrap();
616
617 assert!(cs.is_satisfied());
618 assert!(cs.get("product num") == Fr::from(120u64));
619 assert!(n3.value.unwrap() == Fr::from(120u64));
620 cs.set("product num", Fr::from(121u64));
621 assert!(!cs.is_satisfied());
622 }
623
624 #[test]
625 fn test_num_conditional_reversal() {
626 let mut rng = XorShiftRng::from_seed([
627 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
628 0xbc, 0xe5,
629 ]);
630 {
631 let mut cs = TestConstraintSystem::<Fr>::new();
632
633 let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
634 let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
635 let condition = Boolean::constant(false);
636 let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
637
638 assert!(cs.is_satisfied());
639
640 assert_eq!(a.value.unwrap(), c.value.unwrap());
641 assert_eq!(b.value.unwrap(), d.value.unwrap());
642 }
643
644 {
645 let mut cs = TestConstraintSystem::<Fr>::new();
646
647 let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
648 let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
649 let condition = Boolean::constant(true);
650 let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
651
652 assert!(cs.is_satisfied());
653
654 assert_eq!(a.value.unwrap(), d.value.unwrap());
655 assert_eq!(b.value.unwrap(), c.value.unwrap());
656 }
657 }
658
659 #[test]
660 fn test_num_nonzero() {
661 {
662 let mut cs = TestConstraintSystem::<Fr>::new();
663
664 let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from(3u64))).unwrap();
665 n.assert_nonzero(&mut cs).unwrap();
666
667 assert!(cs.is_satisfied());
668 cs.set("ephemeral inverse", Fr::from(3u64));
669 assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint"));
670 }
671 {
672 let mut cs = TestConstraintSystem::<Fr>::new();
673
674 let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::ZERO)).unwrap();
675 assert!(n.assert_nonzero(&mut cs).is_err());
676 }
677 }
678
679 #[test]
680 fn test_into_bits_strict() {
681 let negone = -Fr::ONE;
682
683 let mut cs = TestConstraintSystem::<Fr>::new();
684
685 let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
686 n.to_bits_le_strict(&mut cs).unwrap();
687
688 assert!(cs.is_satisfied());
689
690 cs.set("bit 254/boolean", Fr::ONE);
692
693 assert_eq!(
695 cs.which_is_unsatisfied().unwrap(),
696 "bit 254/boolean constraint"
697 );
698 }
699
700 #[test]
701 fn test_into_bits() {
702 let mut rng = XorShiftRng::from_seed([
703 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
704 0xbc, 0xe5,
705 ]);
706
707 for i in 0..200 {
708 let r = Fr::random(&mut rng);
709 let mut cs = TestConstraintSystem::<Fr>::new();
710
711 let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
712
713 let bits = if i % 2 == 0 {
714 n.to_bits_le(&mut cs).unwrap()
715 } else {
716 n.to_bits_le_strict(&mut cs).unwrap()
717 };
718
719 assert!(cs.is_satisfied());
720
721 for (i, b) in r.to_le_bits().iter().enumerate() {
722 match bits.get(i) {
726 Some(Boolean::Is(a)) => assert_eq!(b, a.get_value().unwrap()),
727 Some(_) => unreachable!(),
728 None => assert!(!b),
729 };
730 }
731
732 cs.set("num", Fr::random(&mut rng));
733 assert!(!cs.is_satisfied());
734 cs.set("num", r);
735 assert!(cs.is_satisfied());
736
737 for i in 0..Fr::NUM_BITS {
738 let name = format!("bit {}/boolean", i);
739 let cur = cs.get(&name);
740 let mut tmp = Fr::ONE;
741 tmp.sub_assign(&cur);
742 cs.set(&name, tmp);
743 assert!(!cs.is_satisfied());
744 cs.set(&name, cur);
745 assert!(cs.is_satisfied());
746 }
747 }
748 }
749
750 #[test]
751 fn test_num_scale() {
752 use crate::{Index, LinearCombination, Variable};
753
754 let mut rng = XorShiftRng::from_seed([
755 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
756 0xbc, 0xe5,
757 ]);
758
759 let n = 5;
760
761 let mut lc = LinearCombination::<Fr>::zero();
762
763 let mut expected_sums = vec![Fr::ZERO; n];
764 let mut value = Fr::ZERO;
765 for (i, expected_sum) in expected_sums.iter_mut().enumerate() {
766 let coeff = Fr::random(&mut rng);
767 lc = lc + (coeff, Variable::new_unchecked(Index::Aux(i)));
768 expected_sum.add_assign(&coeff);
769
770 value.add_assign(&coeff);
771 }
772
773 let scalar = Fr::random(&mut rng);
774 let num = Num {
775 value: Some(value),
776 lc,
777 };
778
779 let scaled_num = num.clone().scale(scalar);
780
781 let mut scaled_value = num.value.unwrap();
782 scaled_value.mul_assign(&scalar);
783
784 assert_eq!(scaled_value, scaled_num.value.unwrap());
785
786 scaled_num.lc.iter().for_each(|(var, coeff)| match var.0 {
788 Index::Aux(i) => {
789 let mut tmp = expected_sums[i];
790 tmp.mul_assign(&scalar);
791 assert_eq!(tmp, *coeff)
792 }
793 _ => panic!("unexpected variable type"),
794 });
795 }
796}