1use ff::{PrimeField, PrimeFieldBits};
4
5use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
6
7#[derive(Debug, Clone)]
10pub struct AllocatedBit {
11 variable: Variable,
12 value: Option<bool>,
13}
14
15impl AllocatedBit {
16 pub fn get_value(&self) -> Option<bool> {
18 self.value
19 }
20
21 pub fn get_variable(&self) -> Variable {
23 self.variable
24 }
25
26 pub fn from_parts_unchecked(variable: Variable, value: Option<bool>) -> Self {
36 AllocatedBit { variable, value }
37 }
38
39 pub fn alloc_conditionally<Scalar, CS>(
43 mut cs: CS,
44 value: Option<bool>,
45 must_be_false: &AllocatedBit,
46 ) -> Result<Self, SynthesisError>
47 where
48 Scalar: PrimeField,
49 CS: ConstraintSystem<Scalar>,
50 {
51 let var = cs.alloc(
52 || "boolean",
53 || {
54 if value.ok_or(SynthesisError::AssignmentMissing)? {
55 Ok(Scalar::ONE)
56 } else {
57 Ok(Scalar::ZERO)
58 }
59 },
60 )?;
61
62 cs.enforce(
69 || "boolean constraint",
70 |lc| lc + CS::one() - must_be_false.variable - var,
71 |lc| lc + var,
72 |lc| lc,
73 );
74
75 Ok(AllocatedBit {
76 variable: var,
77 value,
78 })
79 }
80
81 pub fn alloc<Scalar, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
84 where
85 Scalar: PrimeField,
86 CS: ConstraintSystem<Scalar>,
87 {
88 let var = cs.alloc(
89 || "boolean",
90 || {
91 if value.ok_or(SynthesisError::AssignmentMissing)? {
92 Ok(Scalar::ONE)
93 } else {
94 Ok(Scalar::ZERO)
95 }
96 },
97 )?;
98
99 cs.enforce(
102 || "boolean constraint",
103 |lc| lc + CS::one() - var,
104 |lc| lc + var,
105 |lc| lc,
106 );
107
108 Ok(AllocatedBit {
109 variable: var,
110 value,
111 })
112 }
113
114 pub fn xor<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
117 where
118 Scalar: PrimeField,
119 CS: ConstraintSystem<Scalar>,
120 {
121 let mut result_value = None;
122
123 let result_var = cs.alloc(
124 || "xor result",
125 || {
126 if a.value.ok_or(SynthesisError::AssignmentMissing)?
127 ^ b.value.ok_or(SynthesisError::AssignmentMissing)?
128 {
129 result_value = Some(true);
130
131 Ok(Scalar::ONE)
132 } else {
133 result_value = Some(false);
134
135 Ok(Scalar::ZERO)
136 }
137 },
138 )?;
139
140 cs.enforce(
156 || "xor constraint",
157 |lc| lc + a.variable + a.variable,
158 |lc| lc + b.variable,
159 |lc| lc + a.variable + b.variable - result_var,
160 );
161
162 Ok(AllocatedBit {
163 variable: result_var,
164 value: result_value,
165 })
166 }
167
168 pub fn and<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
171 where
172 Scalar: PrimeField,
173 CS: ConstraintSystem<Scalar>,
174 {
175 let mut result_value = None;
176
177 let result_var = cs.alloc(
178 || "and result",
179 || {
180 if a.value.ok_or(SynthesisError::AssignmentMissing)?
181 & b.value.ok_or(SynthesisError::AssignmentMissing)?
182 {
183 result_value = Some(true);
184
185 Ok(Scalar::ONE)
186 } else {
187 result_value = Some(false);
188
189 Ok(Scalar::ZERO)
190 }
191 },
192 )?;
193
194 cs.enforce(
197 || "and constraint",
198 |lc| lc + a.variable,
199 |lc| lc + b.variable,
200 |lc| lc + result_var,
201 );
202
203 Ok(AllocatedBit {
204 variable: result_var,
205 value: result_value,
206 })
207 }
208
209 pub fn and_not<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
211 where
212 Scalar: PrimeField,
213 CS: ConstraintSystem<Scalar>,
214 {
215 let mut result_value = None;
216
217 let result_var = cs.alloc(
218 || "and not result",
219 || {
220 if a.value.ok_or(SynthesisError::AssignmentMissing)?
221 & !b.value.ok_or(SynthesisError::AssignmentMissing)?
222 {
223 result_value = Some(true);
224
225 Ok(Scalar::ONE)
226 } else {
227 result_value = Some(false);
228
229 Ok(Scalar::ZERO)
230 }
231 },
232 )?;
233
234 cs.enforce(
237 || "and not constraint",
238 |lc| lc + a.variable,
239 |lc| lc + CS::one() - b.variable,
240 |lc| lc + result_var,
241 );
242
243 Ok(AllocatedBit {
244 variable: result_var,
245 value: result_value,
246 })
247 }
248
249 pub fn nor<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
251 where
252 Scalar: PrimeField,
253 CS: ConstraintSystem<Scalar>,
254 {
255 let mut result_value = None;
256
257 let result_var = cs.alloc(
258 || "nor result",
259 || {
260 if !a.value.ok_or(SynthesisError::AssignmentMissing)?
261 & !b.value.ok_or(SynthesisError::AssignmentMissing)?
262 {
263 result_value = Some(true);
264
265 Ok(Scalar::ONE)
266 } else {
267 result_value = Some(false);
268
269 Ok(Scalar::ZERO)
270 }
271 },
272 )?;
273
274 cs.enforce(
277 || "nor constraint",
278 |lc| lc + CS::one() - a.variable,
279 |lc| lc + CS::one() - b.variable,
280 |lc| lc + result_var,
281 );
282
283 Ok(AllocatedBit {
284 variable: result_var,
285 value: result_value,
286 })
287 }
288}
289
290pub fn field_into_allocated_bits_le<Scalar, CS>(
292 mut cs: CS,
293 value: Option<Scalar>,
294) -> Result<Vec<AllocatedBit>, SynthesisError>
295where
296 Scalar: PrimeField,
297 Scalar: PrimeFieldBits,
298 CS: ConstraintSystem<Scalar>,
299{
300 let values = match value {
302 Some(ref value) => {
303 let field_char = Scalar::char_le_bits();
304 let mut field_char = field_char.into_iter().rev();
305
306 let mut tmp = Vec::with_capacity(Scalar::NUM_BITS as usize);
307
308 let mut found_one = false;
309 for b in value.to_le_bits().into_iter().rev() {
310 found_one |= field_char.next().unwrap();
312 if !found_one {
313 continue;
314 }
315
316 tmp.push(Some(b));
317 }
318
319 assert_eq!(tmp.len(), Scalar::NUM_BITS as usize);
320
321 tmp
322 }
323 None => vec![None; Scalar::NUM_BITS as usize],
324 };
325
326 let bits = values
328 .into_iter()
329 .rev()
330 .enumerate()
331 .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("bit {i}")), b))
332 .collect::<Result<Vec<_>, SynthesisError>>()?;
333
334 Ok(bits)
335}
336
337#[derive(Clone, Debug)]
340pub enum Boolean {
341 Is(AllocatedBit),
343 Not(AllocatedBit),
345 Constant(bool),
347}
348
349impl Boolean {
350 pub fn is_constant(&self) -> bool {
352 matches!(*self, Boolean::Constant(_))
353 }
354
355 pub fn enforce_equal<Scalar, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError>
357 where
358 Scalar: PrimeField,
359 CS: ConstraintSystem<Scalar>,
360 {
361 match (a, b) {
362 (&Boolean::Constant(a), &Boolean::Constant(b)) => {
363 if a == b {
364 Ok(())
365 } else {
366 Err(SynthesisError::Unsatisfiable(
367 "Booleans are not equal".to_string(),
368 ))
369 }
370 }
371 (&Boolean::Constant(true), a) | (a, &Boolean::Constant(true)) => {
372 cs.enforce(
373 || "enforce equal to one",
374 |lc| lc,
375 |lc| lc,
376 |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::ONE),
377 );
378
379 Ok(())
380 }
381 (&Boolean::Constant(false), a) | (a, &Boolean::Constant(false)) => {
382 cs.enforce(
383 || "enforce equal to zero",
384 |lc| lc,
385 |lc| lc,
386 |_| a.lc(CS::one(), Scalar::ONE),
387 );
388
389 Ok(())
390 }
391 (a, b) => {
392 cs.enforce(
393 || "enforce equal",
394 |lc| lc,
395 |lc| lc,
396 |_| a.lc(CS::one(), Scalar::ONE) - &b.lc(CS::one(), Scalar::ONE),
397 );
398
399 Ok(())
400 }
401 }
402 }
403
404 pub fn get_value(&self) -> Option<bool> {
406 match *self {
407 Boolean::Constant(c) => Some(c),
408 Boolean::Is(ref v) => v.get_value(),
409 Boolean::Not(ref v) => v.get_value().map(|b| !b),
410 }
411 }
412
413 pub fn lc<Scalar: PrimeField>(&self, one: Variable, coeff: Scalar) -> LinearCombination<Scalar> {
415 match *self {
416 Boolean::Constant(c) => {
417 if c {
418 LinearCombination::<Scalar>::zero() + (coeff, one)
419 } else {
420 LinearCombination::<Scalar>::zero()
421 }
422 }
423 Boolean::Is(ref v) => LinearCombination::<Scalar>::zero() + (coeff, v.get_variable()),
424 Boolean::Not(ref v) => {
425 LinearCombination::<Scalar>::zero() + (coeff, one) - (coeff, v.get_variable())
426 }
427 }
428 }
429
430 pub fn constant(b: bool) -> Self {
432 Boolean::Constant(b)
433 }
434
435 pub fn not(&self) -> Self {
437 match *self {
438 Boolean::Constant(c) => Boolean::Constant(!c),
439 Boolean::Is(ref v) => Boolean::Not(v.clone()),
440 Boolean::Not(ref v) => Boolean::Is(v.clone()),
441 }
442 }
443
444 pub fn xor<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
446 where
447 Scalar: PrimeField,
448 CS: ConstraintSystem<Scalar>,
449 {
450 match (a, b) {
451 (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()),
452 (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()),
453 (is @ &Boolean::Is(_), not @ &Boolean::Not(_))
455 | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => {
456 Ok(Boolean::xor(cs, is, ¬.not())?.not())
457 }
458 (&Boolean::Is(ref a), &Boolean::Is(ref b)) | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => {
460 Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?))
461 }
462 }
463 }
464
465 pub fn and<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
467 where
468 Scalar: PrimeField,
469 CS: ConstraintSystem<Scalar>,
470 {
471 match (a, b) {
472 (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => {
474 Ok(Boolean::Constant(false))
475 }
476 (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.clone()),
478 (&Boolean::Is(ref is), &Boolean::Not(ref not))
480 | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => {
481 Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?))
482 }
483 (Boolean::Not(a), Boolean::Not(b)) => Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)),
485 (Boolean::Is(a), Boolean::Is(b)) => Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)),
487 }
488 }
489
490 pub fn or<'a, Scalar, CS>(
492 mut cs: CS,
493 a: &'a Boolean,
494 b: &'a Boolean,
495 ) -> Result<Boolean, SynthesisError>
496 where
497 Scalar: PrimeField,
498 CS: ConstraintSystem<Scalar>,
499 {
500 Ok(Boolean::not(&Boolean::and(
501 cs.namespace(|| "not and (not a) (not b)"),
502 &Boolean::not(a),
503 &Boolean::not(b),
504 )?))
505 }
506
507 pub fn sha256_ch<'a, Scalar, CS>(
509 mut cs: CS,
510 a: &'a Self,
511 b: &'a Self,
512 c: &'a Self,
513 ) -> Result<Self, SynthesisError>
514 where
515 Scalar: PrimeField,
516 CS: ConstraintSystem<Scalar>,
517 {
518 let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
519 (Some(a), Some(b), Some(c)) => {
520 Some((a & b) ^ ((!a) & c))
522 }
523 _ => None,
524 };
525
526 match (a, b, c) {
527 (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => {
528 return Ok(Boolean::Constant(ch_value.expect("they're all constants")));
531 }
532 (&Boolean::Constant(false), _, c) => {
533 return Ok(c.clone());
540 }
541 (a, &Boolean::Constant(false), c) => {
542 return Boolean::and(cs, &a.not(), c);
547 }
548 (a, b, &Boolean::Constant(false)) => {
549 return Boolean::and(cs, a, b);
554 }
555 (a, b, &Boolean::Constant(true)) => {
556 return Ok(Boolean::and(cs, a, &b.not())?.not());
563 }
564 (a, &Boolean::Constant(true), c) => {
565 return Ok(Boolean::and(cs, &a.not(), &c.not())?.not());
572 }
573 (&Boolean::Constant(true), _, _) => {
574 }
580 (
581 &Boolean::Is(_) | &Boolean::Not(_),
582 &Boolean::Is(_) | &Boolean::Not(_),
583 &Boolean::Is(_) | &Boolean::Not(_),
584 ) => {}
585 }
586
587 let ch = cs.alloc(
588 || "ch",
589 || {
590 ch_value.ok_or(SynthesisError::AssignmentMissing).map(|v| {
591 if v {
592 Scalar::ONE
593 } else {
594 Scalar::ZERO
595 }
596 })
597 },
598 )?;
599
600 cs.enforce(
602 || "ch computation",
603 |_| b.lc(CS::one(), Scalar::ONE) - &c.lc(CS::one(), Scalar::ONE),
604 |_| a.lc(CS::one(), Scalar::ONE),
605 |lc| lc + ch - &c.lc(CS::one(), Scalar::ONE),
606 );
607
608 Ok(
609 AllocatedBit {
610 value: ch_value,
611 variable: ch,
612 }
613 .into(),
614 )
615 }
616
617 pub fn sha256_maj<'a, Scalar, CS>(
619 mut cs: CS,
620 a: &'a Self,
621 b: &'a Self,
622 c: &'a Self,
623 ) -> Result<Self, SynthesisError>
624 where
625 Scalar: PrimeField,
626 CS: ConstraintSystem<Scalar>,
627 {
628 let maj_value = match (a.get_value(), b.get_value(), c.get_value()) {
629 (Some(a), Some(b), Some(c)) => {
630 Some((a & b) ^ (a & c) ^ (b & c))
632 }
633 _ => None,
634 };
635
636 match (a, b, c) {
637 (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => {
638 return Ok(Boolean::Constant(maj_value.expect("they're all constants")));
641 }
642 (&Boolean::Constant(false), b, c) => {
643 return Boolean::and(cs, b, c);
648 }
649 (a, &Boolean::Constant(false), c) => {
650 return Boolean::and(cs, a, c);
655 }
656 (a, b, &Boolean::Constant(false)) => {
657 return Boolean::and(cs, a, b);
662 }
663 (a, b, &Boolean::Constant(true)) => {
664 return Ok(Boolean::and(cs, &a.not(), &b.not())?.not());
671 }
672 (a, &Boolean::Constant(true), c) => {
673 return Ok(Boolean::and(cs, &a.not(), &c.not())?.not());
678 }
679 (&Boolean::Constant(true), b, c) => {
680 return Ok(Boolean::and(cs, &b.not(), &c.not())?.not());
685 }
686 (
687 &Boolean::Is(_) | &Boolean::Not(_),
688 &Boolean::Is(_) | &Boolean::Not(_),
689 &Boolean::Is(_) | &Boolean::Not(_),
690 ) => {}
691 }
692
693 let maj = cs.alloc(
694 || "maj",
695 || {
696 maj_value.ok_or(SynthesisError::AssignmentMissing).map(|v| {
697 if v {
698 Scalar::ONE
699 } else {
700 Scalar::ZERO
701 }
702 })
703 },
704 )?;
705
706 let bc = Self::and(cs.namespace(|| "b and c"), b, c)?;
716
717 cs.enforce(
718 || "maj computation",
719 |_| {
720 bc.lc(CS::one(), Scalar::ONE) + &bc.lc(CS::one(), Scalar::ONE)
721 - &b.lc(CS::one(), Scalar::ONE)
722 - &c.lc(CS::one(), Scalar::ONE)
723 },
724 |_| a.lc(CS::one(), Scalar::ONE),
725 |_| bc.lc(CS::one(), Scalar::ONE) - maj,
726 );
727
728 Ok(
729 AllocatedBit {
730 value: maj_value,
731 variable: maj,
732 }
733 .into(),
734 )
735 }
736}
737
738impl From<AllocatedBit> for Boolean {
739 fn from(b: AllocatedBit) -> Boolean {
740 Boolean::Is(b)
741 }
742}