//! This module implements various low-level gadgets use crate::traits::Group; use bellperson::{ gadgets::{ boolean::{AllocatedBit, Boolean}, num::AllocatedNum, Assignment, }, ConstraintSystem, LinearCombination, SynthesisError, }; use bellperson_nonnative::mp::bignat::{nat_to_limbs, BigNat}; use ff::{Field, PrimeField, PrimeFieldBits}; use num_bigint::BigInt; /// Gets as input the little indian representation of a number and spits out the number pub fn le_bits_to_num( mut cs: CS, bits: Vec, ) -> Result, SynthesisError> where Scalar: PrimeField + PrimeFieldBits, CS: ConstraintSystem, { // We loop over the input bits and construct the constraint // and the field element that corresponds to the result let mut lc = LinearCombination::zero(); let mut coeff = Scalar::one(); let mut fe = Some(Scalar::zero()); for bit in bits.iter() { lc = lc + (coeff, bit.get_variable()); fe = bit.get_value().map(|val| { if val { fe.unwrap() + coeff } else { fe.unwrap() } }); coeff = coeff.double(); } let num = AllocatedNum::alloc(cs.namespace(|| "Field element"), || { fe.ok_or(SynthesisError::AssignmentMissing) })?; lc = lc - num.get_variable(); cs.enforce(|| "compute number from bits", |lc| lc, |lc| lc, |_| lc); Ok(num) } /// Allocate a variable that is set to zero pub fn alloc_zero>( mut cs: CS, ) -> Result, SynthesisError> { let zero = AllocatedNum::alloc(cs.namespace(|| "alloc"), || Ok(F::zero()))?; cs.enforce( || "check zero is valid", |lc| lc, |lc| lc, |lc| lc + zero.get_variable(), ); Ok(zero) } /// Allocate a variable that is set to one pub fn alloc_one>( mut cs: CS, ) -> Result, SynthesisError> { let one = AllocatedNum::alloc(cs.namespace(|| "alloc"), || Ok(F::one()))?; cs.enforce( || "check one is valid", |lc| lc + CS::one(), |lc| lc + CS::one(), |lc| lc + one.get_variable(), ); Ok(one) } /// Allocate a scalar as a base. Only to be used is the scalar fits in base! pub fn alloc_scalar_as_base( mut cs: CS, input: Option, ) -> Result, SynthesisError> where G: Group, ::Scalar: PrimeFieldBits, CS: ConstraintSystem<::Base>, { AllocatedNum::alloc(cs.namespace(|| "allocate scalar as base"), || { let input_bits = input.unwrap_or_else(G::Scalar::zero).clone().to_le_bits(); let mut mult = G::Base::one(); let mut val = G::Base::zero(); for bit in input_bits { if bit { val += mult; } mult = mult + mult; } Ok(val) }) } /// interepret scalar as base pub fn scalar_as_base(input: G::Scalar) -> G::Base { let input_bits = input.to_le_bits(); let mut mult = G::Base::one(); let mut val = G::Base::zero(); for bit in input_bits { if bit { val += mult; } mult = mult + mult; } val } /// Allocate bignat a constant pub fn alloc_bignat_constant>( mut cs: CS, val: &BigInt, limb_width: usize, n_limbs: usize, ) -> Result, SynthesisError> { let limbs = nat_to_limbs(val, limb_width, n_limbs).unwrap(); let bignat = BigNat::alloc_from_limbs( cs.namespace(|| "alloc bignat"), || Ok(limbs.clone()), None, limb_width, n_limbs, )?; // Now enforce that the limbs are all equal to the constants #[allow(clippy::needless_range_loop)] for i in 0..n_limbs { cs.enforce( || format!("check limb {}", i), |lc| lc + &bignat.limbs[i], |lc| lc + CS::one(), |lc| lc + (limbs[i], CS::one()), ); } Ok(bignat) } /// Check that two numbers are equal and return a bit pub fn alloc_num_equals>( mut cs: CS, a: &AllocatedNum, b: &AllocatedNum, ) -> Result { // Allocate and constrain `r`: result boolean bit. // It equals `true` if `a` equals `b`, `false` otherwise let r_value = match (a.get_value(), b.get_value()) { (Some(a), Some(b)) => Some(a == b), _ => None, }; let r = AllocatedBit::alloc(cs.namespace(|| "r"), r_value)?; // Allocate t s.t. t=1 if z1 == z2 else 1/(z1 - z2) let t = AllocatedNum::alloc(cs.namespace(|| "t"), || { Ok(if *a.get_value().get()? == *b.get_value().get()? { F::one() } else { (*a.get_value().get()? - *b.get_value().get()?) .invert() .unwrap() }) })?; cs.enforce( || "t*(a - b) = 1 - r", |lc| lc + t.get_variable(), |lc| lc + a.get_variable() - b.get_variable(), |lc| lc + CS::one() - r.get_variable(), ); cs.enforce( || "r*(a - b) = 0", |lc| lc + r.get_variable(), |lc| lc + a.get_variable() - b.get_variable(), |lc| lc, ); Ok(r) } /// If condition return a otherwise b pub fn conditionally_select>( mut cs: CS, a: &AllocatedNum, b: &AllocatedNum, condition: &Boolean, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? { Ok(*a.get_value().get()?) } else { Ok(*b.get_value().get()?) } })?; // a * condition + b*(1-condition) = c -> // a * condition - b*condition = c - b cs.enforce( || "conditional select constraint", |lc| lc + a.get_variable() - b.get_variable(), |_| condition.lc(CS::one(), F::one()), |lc| lc + c.get_variable() - b.get_variable(), ); Ok(c) } /// If condition return a otherwise b where a and b are BigNats pub fn conditionally_select_bignat>( mut cs: CS, a: &BigNat, b: &BigNat, condition: &Boolean, ) -> Result, SynthesisError> { assert!(a.limbs.len() == b.limbs.len()); let c = BigNat::alloc_from_nat( cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? { Ok(a.value.get()?.clone()) } else { Ok(b.value.get()?.clone()) } }, a.params.limb_width, a.params.n_limbs, )?; // a * condition + b*(1-condition) = c -> // a * condition - b*condition = c - b for i in 0..c.limbs.len() { cs.enforce( || format!("conditional select constraint {}", i), |lc| lc + &a.limbs[i] - &b.limbs[i], |_| condition.lc(CS::one(), F::one()), |lc| lc + &c.limbs[i] - &b.limbs[i], ); } Ok(c) } /// Same as the above but Condition is an AllocatedNum that needs to be /// 0 or 1. 1 => True, 0 => False pub fn conditionally_select2>( mut cs: CS, a: &AllocatedNum, b: &AllocatedNum, condition: &AllocatedNum, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? == F::one() { Ok(*a.get_value().get()?) } else { Ok(*b.get_value().get()?) } })?; // a * condition + b*(1-condition) = c -> // a * condition - b*condition = c - b cs.enforce( || "conditional select constraint", |lc| lc + a.get_variable() - b.get_variable(), |lc| lc + condition.get_variable(), |lc| lc + c.get_variable() - b.get_variable(), ); Ok(c) } /// If condition set to 0 otherwise a. Condition is an allocated num pub fn select_zero_or_num2>( mut cs: CS, a: &AllocatedNum, condition: &AllocatedNum, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? == F::one() { Ok(F::zero()) } else { Ok(*a.get_value().get()?) } })?; // a * (1 - condition) = c cs.enforce( || "conditional select constraint", |lc| lc + a.get_variable(), |lc| lc + CS::one() - condition.get_variable(), |lc| lc + c.get_variable(), ); Ok(c) } /// If condition set to a otherwise 0. Condition is an allocated num pub fn select_num_or_zero2>( mut cs: CS, a: &AllocatedNum, condition: &AllocatedNum, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? == F::one() { Ok(*a.get_value().get()?) } else { Ok(F::zero()) } })?; cs.enforce( || "conditional select constraint", |lc| lc + a.get_variable(), |lc| lc + condition.get_variable(), |lc| lc + c.get_variable(), ); Ok(c) } /// If condition set to a otherwise 0 pub fn select_num_or_zero>( mut cs: CS, a: &AllocatedNum, condition: &Boolean, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? { Ok(*a.get_value().get()?) } else { Ok(F::zero()) } })?; cs.enforce( || "conditional select constraint", |lc| lc + a.get_variable(), |_| condition.lc(CS::one(), F::one()), |lc| lc + c.get_variable(), ); Ok(c) } /// If condition set to 1 otherwise a pub fn select_one_or_num2>( mut cs: CS, a: &AllocatedNum, condition: &AllocatedNum, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? == F::one() { Ok(F::one()) } else { Ok(*a.get_value().get()?) } })?; cs.enforce( || "conditional select constraint", |lc| lc + CS::one() - a.get_variable(), |lc| lc + condition.get_variable(), |lc| lc + c.get_variable() - a.get_variable(), ); Ok(c) } /// If condition set to 1 otherwise a - b pub fn select_one_or_diff2>( mut cs: CS, a: &AllocatedNum, b: &AllocatedNum, condition: &AllocatedNum, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? == F::one() { Ok(F::one()) } else { Ok(*a.get_value().get()? - *b.get_value().get()?) } })?; cs.enforce( || "conditional select constraint", |lc| lc + CS::one() - a.get_variable() + b.get_variable(), |lc| lc + condition.get_variable(), |lc| lc + c.get_variable() - a.get_variable() + b.get_variable(), ); Ok(c) } /// If condition set to a otherwise 1 for boolean conditions pub fn select_num_or_one>( mut cs: CS, a: &AllocatedNum, condition: &Boolean, ) -> Result, SynthesisError> { let c = AllocatedNum::alloc(cs.namespace(|| "conditional select result"), || { if *condition.get_value().get()? { Ok(*a.get_value().get()?) } else { Ok(F::one()) } })?; cs.enforce( || "conditional select constraint", |lc| lc + a.get_variable() - CS::one(), |_| condition.lc(CS::one(), F::one()), |lc| lc + c.get_variable() - CS::one(), ); Ok(c) }