Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nova/big nat #185

Merged
merged 11 commits into from
Dec 21, 2023
6 changes: 3 additions & 3 deletions nova/src/circuit/augmented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::circuit::nifs::NifsCircuit;
use crate::circuit::MimcROCircuit;
use crate::function::FunctionCircuit;
use crate::gadget::{R1csInstanceAssignment, RelaxedR1csInstanceAssignment};
use crate::hash::MIMC_ROUNDS;
use crate::hash::{CHALLENGE_BITS, MIMC_ROUNDS};
use crate::relaxed_r1cs::{R1csInstance, RelaxedR1csInstance};
use std::marker::PhantomData;
use zkstd::circuit::prelude::{FieldAssignment, PointAssignment};
Expand Down Expand Up @@ -131,8 +131,8 @@ impl<C: CircuitDriver, FC: FunctionCircuit<C::Base>> AugmentedFCircuit<C, FC> {
) -> FieldAssignment<C::Base> {
let mut transcript = MimcROCircuit::<MIMC_ROUNDS, C>::default();
transcript.append_point(commit_t);
u_range.absorb_by_transcript(&mut transcript);
transcript.squeeze(cs)
u_range.absorb_by_transcript(cs, &mut transcript);
transcript.squeeze(cs, CHALLENGE_BITS)
}
}

Expand Down
42 changes: 25 additions & 17 deletions nova/src/circuit/nifs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use core::marker::PhantomData;
use num_bigint::BigInt;
use num_traits::Num;
use std::ops::{Add, Mul};

use crate::driver::{f_to_nat, nat_to_f};
use crate::gadget::{f_to_nat, BigNatAssignment, BN_LIMB_WIDTH, BN_N_LIMBS};
use crate::gadget::{R1csInstanceAssignment, RelaxedR1csInstanceAssignment};
use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs};
use zkstd::common::{Group, IntGroup};
Expand Down Expand Up @@ -35,31 +34,40 @@ impl<C: CircuitDriver> NifsCircuit<C> {

let r_bn = f_to_nat(&r.value(cs));
let m_bn = BigInt::from_str_radix(C::ORDER_STR, 16).unwrap();
let r_bn_ass =
BigNatAssignment::witness_from_field_assignment(cs, &r, BN_LIMB_WIDTH, BN_N_LIMBS);
let m_bn_ass = BigNatAssignment::witness_from_big_int(cs, m_bn, BN_LIMB_WIDTH, BN_N_LIMBS);

// TODO: Should be done without using BigInt
// u_fold = U.u + r
let u = f_to_nat(&u_range.u.value(cs));
let u_fold = FieldAssignment::witness(cs, nat_to_f(&(u.add(r_bn.clone()) % m_bn.clone())));
// FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero());
let u_fold = FieldAssignment::witness(cs, u_range.u.value(cs) + r.value(cs));
FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero());

// TODO: BigNatAssignment should be use for module arithmetics
// Fold U.x0 + r * x0
let x0_range_bn = f_to_nat(&u_range.x0.value(cs));
let x0_single_bn = f_to_nat(&u_single.x0.value(cs));
let r_x0 = x0_single_bn.mul(r_bn.clone()) % m_bn.clone();
let x0_fold = (x0_range_bn + r_x0) % m_bn.clone();
let x0_single_bn = BigNatAssignment::witness_from_big_int(
cs,
f_to_nat(&u_single.x0.value(cs)),
BN_LIMB_WIDTH,
BN_N_LIMBS,
);
let r_x0 = x0_single_bn.mult_mod(cs, &r_bn_ass, &m_bn_ass);
let x0_fold = u_range.x0.add(&r_x0).red_mod(cs, &m_bn_ass);

// Fold U.x1 + r * x1
let x1_range_bn = f_to_nat(&u_range.x1.value(cs));
let x1_single_bn = f_to_nat(&u_single.x1.value(cs));
let r_x1 = x1_single_bn.mul(r_bn) % m_bn.clone();
let x1_fold = (x1_range_bn + r_x1) % m_bn;
let x1_single_bn = BigNatAssignment::witness_from_big_int(
cs,
f_to_nat(&u_single.x1.value(cs)),
BN_LIMB_WIDTH,
BN_N_LIMBS,
);
let r_x1 = x1_single_bn.mult_mod(cs, &r_bn_ass, &m_bn_ass);
let x1_fold = u_range.x1.add(&r_x1).red_mod(cs, &m_bn_ass);

RelaxedR1csInstanceAssignment {
commit_w: w_fold,
commit_e: e_fold,
u: u_fold,
x0: FieldAssignment::witness(cs, nat_to_f(&x0_fold)),
x1: FieldAssignment::witness(cs, nat_to_f(&x1_fold)),
x0: x0_fold,
x1: x1_fold,
}
}
}
33 changes: 20 additions & 13 deletions nova/src/circuit/transcript.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::gadget::MimcAssignment;

use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs};
use zkstd::common::IntGroup;
use zkstd::common::{IntGroup, Ring};

pub(crate) struct MimcROCircuit<const ROUND: usize, C: CircuitDriver> {
hasher: MimcAssignment<ROUND, C::Base>,
Expand All @@ -23,15 +23,10 @@ impl<const ROUND: usize, C: CircuitDriver> MimcROCircuit<ROUND, C> {
pub(crate) fn append(&mut self, absorb: FieldAssignment<C::Base>) {
self.state.push(absorb)
}
pub(crate) fn hash_vec<CS: CircuitDriver<Scalar = C::Base>>(
&mut self,
cs: &mut R1cs<CS>,
values: Vec<FieldAssignment<C::Base>>,
) -> FieldAssignment<C::Base> {
pub(crate) fn append_vec(&mut self, values: Vec<FieldAssignment<C::Base>>) {
for x in values {
self.state.push(x);
self.append(x);
}
self.squeeze(cs)
}

pub(crate) fn append_point(&mut self, point: PointAssignment<C::Base>) {
Expand All @@ -43,18 +38,30 @@ impl<const ROUND: usize, C: CircuitDriver> MimcROCircuit<ROUND, C> {
pub(crate) fn squeeze<CS: CircuitDriver<Scalar = C::Base>>(
&self,
cs: &mut R1cs<CS>,
num_bits: usize,
) -> FieldAssignment<C::Base> {
self.state.iter().fold(self.key.clone(), |acc, scalar| {
let hash = self.state.iter().fold(self.key.clone(), |acc, scalar| {
let h = self.hasher.hash(cs, scalar.clone(), acc.clone());
&(&acc + scalar) + &h
})
});

let bits = FieldAssignment::to_bits(cs, &hash, num_bits);

// TODO: Do faster
let mut mult = FieldAssignment::constant(&C::Base::one());
let mut val = FieldAssignment::constant(&C::Base::zero());
for bit in bits.iter().rev().take(num_bits) {
val = FieldAssignment::conditional_select(cs, &(&val + &mult), &val, bit);
mult = &mult + &mult;
}
val
}
}

#[cfg(test)]
mod tests {
use super::MimcROCircuit;
use crate::hash::{MimcRO, MIMC_ROUNDS};
use crate::hash::{MimcRO, HASH_BITS, MIMC_ROUNDS};

use crate::driver::{Bn254Driver, GrumpkinDriver};
use bn_254::Fr;
Expand All @@ -78,8 +85,8 @@ mod tests {
mimc_circuit.append(scalar_assignment);
mimc_circuit.append_point(point_assignment);

let expected = mimc.squeeze().into();
let circuit_result = mimc_circuit.squeeze(&mut cs);
let expected = mimc.squeeze(HASH_BITS).into();
let circuit_result = mimc_circuit.squeeze(&mut cs, HASH_BITS);
FieldAssignment::enforce_eq_constant(&mut cs, &circuit_result, &expected);
assert!(cs.is_sat());
}
Expand Down
66 changes: 18 additions & 48 deletions nova/src/driver.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use bn_254::{params::PARAM_B3 as BN254_PARAM_B3, Fq, Fr, G1Affine};
use grumpkin::{params::PARAM_B3 as GRUMPKIN_PARAM_B3, Affine};
use num_bigint::{BigInt, Sign};
use zkstd::circuit::CircuitDriver;
use zkstd::common::{IntGroup, PrimeField, Ring};

Expand Down Expand Up @@ -42,50 +41,6 @@ impl CircuitDriver for Bn254Driver {
}
}

/// Convert a field element to a natural number
pub fn f_to_nat<F: PrimeField>(f: &F) -> BigInt {
BigInt::from_bytes_le(Sign::Plus, &f.to_raw_bytes())
}

/// Convert a natural number to a field element.
pub fn nat_to_f<F: PrimeField>(n: &BigInt) -> F {
let mut bytes = n.to_signed_bytes_le();
if bytes.len() > 64 {
panic!("Length exceed the field size");
};
bytes.resize(64, 0);

let mut res = [0; 64];
res[0..64].copy_from_slice(&bytes);

F::from_bytes_wide(&res)
}

/// Compute the limbs encoding a natural number.
/// The limbs are assumed to be based the `limb_width` power of 2.
pub fn nat_to_limbs<F: PrimeField>(nat: &BigInt, limb_width: usize, n_limbs: usize) -> Vec<F> {
let mask = int_with_n_ones(limb_width);
let mut nat = nat.clone();
if nat.bits() as usize <= n_limbs * limb_width {
(0..n_limbs)
.map(|_| {
let r = &nat & &mask;
nat >>= limb_width as u32;
nat_to_f(&r)
})
.collect()
} else {
panic!("Wrong amount of bits");
}
}

fn int_with_n_ones(n: usize) -> BigInt {
let mut m = BigInt::from(1);
m <<= n as u32;
m -= 1;
m
}

/// interpret scalar as base
pub fn scalar_as_base<C: CircuitDriver>(input: C::Scalar) -> C::Base {
let input_bits = input.to_bits();
Expand Down Expand Up @@ -128,20 +83,35 @@ mod grumpkin_gadget_tests {
for _ in 0..100 {
let mut cs: R1cs<GrumpkinDriver> = R1cs::default();
let mut ncs = cs.clone();
let bound = Scalar::from(10);
let bound = Scalar::random(OsRng);

let x_ass = FieldAssignment::instance(&mut cs, bound);
let x_bits = FieldAssignment::to_bits(&mut cs, &x_ass);
let x_bits = FieldAssignment::to_bits(&mut cs, &x_ass, 256);
FieldAssignment::range_check(&mut cs, &x_bits, bound);
assert!(cs.is_sat());

let x_ass = FieldAssignment::instance(&mut ncs, bound + Scalar::one());
let x_bits = FieldAssignment::to_bits(&mut ncs, &x_ass);
let x_bits = FieldAssignment::to_bits(&mut ncs, &x_ass, 256);
FieldAssignment::range_check(&mut ncs, &x_bits, bound);
assert!(!ncs.is_sat());
}
}

#[test]
fn range_proof_bits_test() {
let mut cs: R1cs<GrumpkinDriver> = R1cs::default();
let x = Scalar::from(13);

let x_ass = FieldAssignment::instance(&mut cs, x);
let x_bits_256 = FieldAssignment::to_bits(&mut cs, &x_ass, 256);
let x_bits_4 = FieldAssignment::to_bits(&mut cs, &x_ass, 4);
FieldAssignment::range_check_bits(&mut cs, &x_bits_256, 4);
FieldAssignment::range_check_bits(&mut cs, &x_bits_4, 4);
assert!(cs.is_sat());
FieldAssignment::range_check_bits(&mut cs, &x_bits_256, 3);
assert!(!cs.is_sat());
}

#[test]
fn field_add_test() {
let mut cs: R1cs<GrumpkinDriver> = R1cs::default();
Expand Down
4 changes: 2 additions & 2 deletions nova/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use zkstd::r1cs::R1cs;
pub trait FunctionCircuit<F: PrimeField>: Clone + Debug + Default {
fn invoke(z_i: &DenseVectors<F>) -> DenseVectors<F>;

fn invoke_cs<CS: CircuitDriver<Scalar = F>>(
cs: &mut R1cs<CS>,
fn invoke_cs<C: CircuitDriver<Scalar = F>>(
cs: &mut R1cs<C>,
z_i: Vec<FieldAssignment<F>>,
) -> Vec<FieldAssignment<F>>;
}
1 change: 1 addition & 0 deletions nova/src/gadget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod instance;
mod mimc;
mod relaxed_instance;

pub(crate) use big_nat::{f_to_nat, nat_to_limbs, BigNatAssignment, BN_LIMB_WIDTH, BN_N_LIMBS};
pub(crate) use instance::R1csInstanceAssignment;
pub(crate) use mimc::MimcAssignment;
pub(crate) use relaxed_instance::RelaxedR1csInstanceAssignment;
Loading
Loading