use super::nonnative::bignat::{nat_to_limbs, BigNat};
use crate::{
constants::{BN_LIMB_WIDTH, BN_N_LIMBS},
frontend::{
num::AllocatedNum, AllocatedBit, Assignment, Boolean, ConstraintSystem, LinearCombination,
SynthesisError,
},
gadgets::nonnative::util::f_to_nat,
traits::Engine,
};
use ff::{Field, PrimeField, PrimeFieldBits};
use num_bigint::BigInt;
pub fn le_bits_to_num<Scalar, CS>(
mut cs: CS,
bits: &[AllocatedBit],
) -> Result<AllocatedNum<Scalar>, SynthesisError>
where
Scalar: PrimeField + PrimeFieldBits,
CS: ConstraintSystem<Scalar>,
{
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()
.and_then(|val| fe.map(|f| if val { f + coeff } else { f }));
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)
}
pub fn alloc_zero<F: PrimeField, CS: ConstraintSystem<F>>(mut cs: CS) -> AllocatedNum<F> {
let zero = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || F::ZERO);
cs.enforce(
|| "check zero is valid",
|lc| lc,
|lc| lc,
|lc| lc + zero.get_variable(),
);
zero
}
pub fn alloc_one<F: PrimeField, CS: ConstraintSystem<F>>(mut cs: CS) -> AllocatedNum<F> {
let one = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || F::ONE);
cs.enforce(
|| "check one is valid",
|lc| lc + CS::one(),
|lc| lc + CS::one(),
|lc| lc + one.get_variable(),
);
one
}
pub fn alloc_scalar_as_base<E, CS>(
mut cs: CS,
input: Option<E::Scalar>,
) -> Result<AllocatedNum<E::Base>, SynthesisError>
where
E: Engine,
<E as Engine>::Scalar: PrimeFieldBits,
CS: ConstraintSystem<<E as Engine>::Base>,
{
AllocatedNum::alloc(cs.namespace(|| "allocate scalar as base"), || {
let input_bits = input.unwrap_or(E::Scalar::ZERO).clone().to_le_bits();
let mut mult = E::Base::ONE;
let mut val = E::Base::ZERO;
for bit in input_bits {
if bit {
val += mult;
}
mult = mult + mult;
}
Ok(val)
})
}
pub fn scalar_as_base<E: Engine>(input: E::Scalar) -> E::Base {
field_switch::<E::Scalar, E::Base>(input)
}
pub fn base_as_scalar<E: Engine>(input: E::Base) -> E::Scalar {
field_switch::<E::Base, E::Scalar>(input)
}
pub fn field_switch<F1: PrimeField + PrimeFieldBits, F2: PrimeField>(x: F1) -> F2 {
let input_bits = x.to_le_bits();
let mut mult = F2::ONE;
let mut val = F2::ZERO;
for bit in input_bits {
if bit {
val += mult;
}
mult += mult;
}
val
}
pub fn to_bignat_repr<F1: PrimeField + PrimeFieldBits, F2: PrimeField>(x: &F1) -> Vec<F2> {
let limbs: Vec<F1> = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap();
limbs
.iter()
.map(|limb| field_switch::<F1, F2>(*limb))
.collect()
}
pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
val: &BigInt,
limb_width: usize,
n_limbs: usize,
) -> Result<BigNat<F>, 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,
)?;
(0..n_limbs).for_each(|i| {
cs.enforce(
|| format!("check limb {i}"),
|lc| lc + &bignat.limbs[i],
|lc| lc + CS::one(),
|lc| lc + (limbs[i], CS::one()),
);
});
Ok(bignat)
}
pub fn alloc_num_equals<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
b: &AllocatedNum<F>,
) -> Result<AllocatedBit, SynthesisError> {
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)?;
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)
}
pub fn conditionally_select<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
b: &AllocatedNum<F>,
condition: &Boolean,
) -> Result<AllocatedNum<F>, 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()?)
}
})?;
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)
}
pub fn conditionally_select_vec<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &[AllocatedNum<F>],
b: &[AllocatedNum<F>],
condition: &Boolean,
) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
a.iter()
.zip(b.iter())
.enumerate()
.map(|(i, (a, b))| {
conditionally_select(cs.namespace(|| format!("select_{i}")), a, b, condition)
})
.collect::<Result<Vec<AllocatedNum<F>>, SynthesisError>>()
}
pub fn conditionally_select_bignat<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &BigNat<F>,
b: &BigNat<F>,
condition: &Boolean,
) -> Result<BigNat<F>, 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,
)?;
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)
}
pub fn conditionally_select2<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
b: &AllocatedNum<F>,
condition: &AllocatedNum<F>,
) -> Result<AllocatedNum<F>, 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()?)
}
})?;
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)
}
pub fn select_zero_or_num2<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
condition: &AllocatedNum<F>,
) -> Result<AllocatedNum<F>, 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()?)
}
})?;
cs.enforce(
|| "conditional select constraint",
|lc| lc + a.get_variable(),
|lc| lc + CS::one() - condition.get_variable(),
|lc| lc + c.get_variable(),
);
Ok(c)
}
pub fn select_num_or_zero2<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
condition: &AllocatedNum<F>,
) -> Result<AllocatedNum<F>, 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)
}
pub fn select_num_or_zero<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
condition: &Boolean,
) -> Result<AllocatedNum<F>, 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)
}
pub fn select_one_or_num2<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
condition: &AllocatedNum<F>,
) -> Result<AllocatedNum<F>, 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)
}
pub fn select_one_or_diff2<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
b: &AllocatedNum<F>,
condition: &AllocatedNum<F>,
) -> Result<AllocatedNum<F>, 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)
}
pub fn select_num_or_one<F: PrimeField, CS: ConstraintSystem<F>>(
mut cs: CS,
a: &AllocatedNum<F>,
condition: &Boolean,
) -> Result<AllocatedNum<F>, 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)
}
pub fn alloc_constant<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
mut cs: CS,
c: &Scalar,
) -> Result<AllocatedNum<Scalar>, SynthesisError> {
let constant = AllocatedNum::alloc(cs.namespace(|| "constant"), || Ok(*c))?;
cs.enforce(
|| "check eq given constant",
|lc| lc + constant.get_variable(),
|lc| lc + CS::one(),
|lc| lc + (*c, CS::one()),
);
Ok(constant)
}
pub fn base_as_bigint<E: Engine>(input: E::Base) -> BigInt {
let input_bits = input.to_le_bits();
let mut mult = BigInt::from(1);
let mut val = BigInt::from(0);
for bit in input_bits {
if bit {
val += &mult;
}
mult *= BigInt::from(2);
}
val
}
pub fn le_num_to_num<Scalar, CS>(
mut cs: CS,
bits: &[AllocatedNum<Scalar>],
) -> Result<AllocatedNum<Scalar>, SynthesisError>
where
Scalar: PrimeField + PrimeFieldBits,
CS: ConstraintSystem<Scalar>,
{
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 == Scalar::ONE {
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)
}
pub fn fingerprint<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
mut cs: CS,
acc: &AllocatedNum<Scalar>,
c: &AllocatedNum<Scalar>,
c_i: &AllocatedNum<Scalar>,
v: &AllocatedNum<Scalar>,
) -> Result<(AllocatedNum<Scalar>, AllocatedNum<Scalar>), SynthesisError> {
let acc_out = AllocatedNum::alloc(cs.namespace(|| "acc_out"), || {
Ok(*acc.get_value().get()? + *c_i.get_value().get()? * *v.get_value().get()?)
})?;
cs.enforce(
|| "acc_out = acc + c_i * v",
|lc| lc + c_i.get_variable(),
|lc| lc + v.get_variable(),
|lc| lc + acc_out.get_variable() - acc.get_variable(),
);
let c_i_out = AllocatedNum::alloc(cs.namespace(|| "c_i_out"), || {
Ok(*c_i.get_value().get()? * *c.get_value().get()?)
})?;
cs.enforce(
|| "c_i_out = c * c^i",
|lc| lc + c.get_variable(),
|lc| lc + c_i.get_variable(),
|lc| lc + c_i_out.get_variable(),
);
Ok((acc_out, c_i_out))
}