#![allow(missing_docs)]
use ff::PrimeField;
#[cfg(any(test, feature = "testing"))]
use midnight_proofs::plonk::Error;
use num_bigint::{BigInt as BI, BigUint, Sign};
use num_integer::Integer;
use num_traits::{Num, One, Signed, Zero};
#[cfg(any(test, feature = "testing"))]
use {
midnight_proofs::circuit::Layouter,
midnight_proofs::plonk::{Column, ConstraintSystem, Instance},
};
pub fn modulus<F: PrimeField>() -> BigUint {
BigUint::from_str_radix(&F::MODULUS[2..], 16).unwrap()
}
pub fn qnr<F: PrimeField>() -> F {
debug_assert!(bool::from(F::MULTIPLICATIVE_GENERATOR.sqrt().is_none()));
F::MULTIPLICATIVE_GENERATOR
}
pub fn big_to_fe<F: PrimeField>(e: BigUint) -> F {
let modulus = modulus::<F>();
let e = e % modulus;
F::from_str_vartime(&e.to_str_radix(10)[..]).unwrap()
}
pub fn fe_to_big<F: PrimeField>(fe: F) -> BigUint {
BigUint::from_bytes_le(fe.to_repr().as_ref())
}
pub fn fe_to_u32<F: PrimeField>(fe: F) -> u32 {
let u32_digits = fe_to_big(fe).to_u32_digits();
assert!(u32_digits.len() <= 1);
u32_digits.first().cloned().unwrap_or(0)
}
pub fn fe_to_u64<F: PrimeField>(fe: F) -> u64 {
let u64_digits = fe_to_big(fe).to_u64_digits();
assert!(u64_digits.len() <= 1);
u64_digits.first().cloned().unwrap_or(0)
}
pub fn fe_to_u128<F: PrimeField>(fe: F) -> u128 {
let u64_digits = fe_to_big(fe).to_u64_digits();
assert!(u64_digits.len() <= 2);
((u64_digits.get(1).cloned().unwrap_or(0) as u128) << 64)
| (u64_digits.first().cloned().unwrap_or(0) as u128)
}
pub fn u32_to_fe<F: PrimeField>(x: u32) -> F {
F::from(x as u64)
}
pub fn u64_to_fe<F: PrimeField>(x: u64) -> F {
F::from(x)
}
pub fn u128_to_fe<F: PrimeField>(x: u128) -> F {
F::from_u128(x)
}
fn from_u64_le_digits<F: PrimeField>(digits: &[u64]) -> F {
if digits.is_empty() {
return F::ZERO;
}
let mut acc = F::from(*digits.last().unwrap());
for digit in digits.iter().rev().skip(1) {
for _ in 0..64 {
acc = acc.double();
}
acc += F::from(*digit)
}
acc
}
pub fn bigint_to_fe<F: PrimeField>(value: &BI) -> F where
{
let (sign, u64_chunks) = value.to_u64_digits();
let res = from_u64_le_digits::<F>(&u64_chunks);
if sign == Sign::Minus {
-res
} else {
res
}
}
pub fn fe_to_bigint<F: PrimeField>(value: &F) -> BI {
BI::from_bytes_le(Sign::Plus, F::to_repr(value).as_ref())
}
pub fn fe_to_le_bits<F: PrimeField>(value: &F, nb_bits: Option<usize>) -> Vec<bool> {
let big = fe_to_big(*value);
let mut bits: Vec<bool> = (0..big.bits()).map(|i| big.bit(i)).collect();
if let Some(n) = nb_bits {
assert!(n >= bits.len());
bits.resize(n, false);
}
bits
}
pub fn le_bits_to_field_elem<F: PrimeField>(bits: &[bool]) -> F {
assert!(bits.len() as u32 <= F::NUM_BITS);
let mut repr = F::from(0).to_repr();
let view = repr.as_mut();
let bytes = bits.chunks(8).map(|bits| {
bits.iter()
.enumerate()
.fold(0u8, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
});
for (byte, repr) in bytes.zip(view.iter_mut()) {
*repr = byte
}
F::from_repr(repr).unwrap()
}
pub fn glv_scalar_decomposition<F: PrimeField>(x: &F, zeta: &F) -> ((bool, F), (bool, F)) {
let n: BI = modulus::<F>().into();
let lambda = fe_to_bigint(zeta);
let k = fe_to_bigint(x);
let xgcd = |a: &BI, b: &BI| -> (Vec<BI>, Vec<BI>, Vec<BI>) {
let mut rs = vec![a.clone(), b.clone()];
let mut ss = vec![BI::one(), BI::zero()];
let mut ts = vec![BI::zero(), BI::one()];
loop {
if rs[rs.len() - 1].is_zero() {
break;
}
let q = rs[rs.len() - 2].clone() / rs[rs.len() - 1].clone();
let r = rs[rs.len() - 2].clone() % rs[rs.len() - 1].clone();
let s = ss[rs.len() - 2].clone() - q.clone() * ss[rs.len() - 1].clone();
let t = ts[rs.len() - 2].clone() - q.clone() * ts[rs.len() - 1].clone();
rs.push(r);
ss.push(s);
ts.push(t);
}
(rs, ss, ts)
};
let (rs, _ss, ts) = xgcd(&n, &lambda);
let l = rs.iter().position(|r: &BI| r.pow(2) < n).unwrap() - 1;
let cond = rs[l].pow(2) + ts[l].pow(2) <= rs[l + 2].pow(2) + ts[l + 2].pow(2);
let ll = if cond { l } else { l + 2 };
let a1 = rs[l + 1].clone();
let b1 = -ts[l + 1].clone();
let a2 = rs[ll].clone();
let b2 = -ts[ll].clone();
let div_with_rounding = |a: &BI, b: &BI| -> BI {
let (q, r) = a.div_rem(b);
q + BI::from(if r.clone() + r > b.clone() { 1 } else { 0 })
};
let c1 = div_with_rounding(&(b2.clone() * k.clone()), &n);
let c2 = div_with_rounding(&(-b1.clone() * k.clone()), &n);
let k1 = k - c1.clone() * a1 - c2.clone() * a2;
let k2 = -c1 * b1 - c2 * b2;
let s1 = k1.is_positive();
let s2 = k2.is_positive();
let x1 = if s1 { k1 } else { -k1 };
let x2 = if s2 { k2 } else { -k2 };
let max_length = F::NUM_BITS.div_ceil(2) as u64;
if x1.bits() > max_length || x2.bits() > max_length {
panic!(
"Oops, an error occurred in GLV decomposition. \
Please, open an issue to report this problem: \
https://github.com/midnightntwrk/midnight-circuits/issues"
)
};
((s1, bigint_to_fe(&x1)), (s2, bigint_to_fe(&x2)))
}
#[cfg(any(test, feature = "testing"))]
pub trait FromScratch<F: PrimeField> {
type Config: Clone + std::fmt::Debug;
fn new_from_scratch(config: &Self::Config) -> Self;
fn configure_from_scratch(
meta: &mut ConstraintSystem<F>,
instance_columns: &[Column<Instance>; 2],
) -> Self::Config;
fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error>;
}