From f8f4e50a44b64e26fda3395f856e9442442131b2 Mon Sep 17 00:00:00 2001 From: Oles Holembovskyy Date: Mon, 31 Jul 2023 12:19:39 +0200 Subject: [PATCH 01/15] goldilocks field implementation --- Cargo.toml | 7 +- src/lib.rs | 2 + src/plonk/circuit/goldilocks/mod.rs | 811 ++++++++++++++++++ .../circuit/goldilocks/prime_field_like.rs | 213 +++++ src/plonk/circuit/mod.rs | 1 + 5 files changed, 1033 insertions(+), 1 deletion(-) create mode 100644 src/plonk/circuit/goldilocks/mod.rs create mode 100644 src/plonk/circuit/goldilocks/prime_field_like.rs diff --git a/Cargo.toml b/Cargo.toml index acd36d26..22e7146b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,11 @@ plonk = ["bellman/plonk"] allocator = ["bellman/allocator"] [dependencies] +derivative = "2" + +# boojum = {package = "boojum", path = "../boojum" } +boojum = {git = "https://github.com/matter-labs/boojum.git", branch = "main"} + rand = "0.4" digest = "0.9" byteorder = "1" @@ -35,7 +40,7 @@ num-derive = "0.2" indexmap = "1.9" smallvec = "1.10" -# bellman = { package = "bellman_ce", path = "../../bellman/plonk" } +# bellman = { package = "bellman_ce", path = "../bellman" } bellman = { package = "bellman_ce", git = "https://github.com/matter-labs/bellman", branch = "dev" } blake2-rfc_bellman_edition = "0.0.1" #poseidon_hash = { path = "../poseidon_hash" } diff --git a/src/lib.rs b/src/lib.rs index ac2ff9a3..9eb42c2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ extern crate blake2; extern crate serde; extern crate num_derive; extern crate indexmap; +extern crate boojum; +extern crate derivative; use bellman::pairing; use bellman::pairing::ff; diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs new file mode 100644 index 00000000..ff849a1f --- /dev/null +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -0,0 +1,811 @@ +use num_traits::ops::overflowing; + +use crate::bellman::pairing::{ + Engine, +}; + +use crate::bellman::pairing::ff::{ + Field, + PrimeField, + PrimeFieldRepr, + BitIterator +}; + +use crate::bellman::{ + SynthesisError, +}; + +use plonk::circuit::boolean::Boolean; + +use crate::bellman::plonk::better_better_cs::cs::{ + Variable, + ConstraintSystem, + ArithmeticTerm, + MainGateTerm, + Width4MainGateWithDNext, + MainGate, + GateInternal, + Gate, + LinearCombinationOfTerms, + PolynomialMultiplicativeTerm, + PolynomialInConstraint, + TimeDilation, + Coefficient, + PlonkConstraintSystemParams, + PlonkCsWidth4WithNextStepParams, + TrivialAssembly +}; + +use crate::plonk::circuit::Assignment; +use super::*; +use super::bigint::*; + +use crate::plonk::circuit::allocated_num::{AllocatedNum, Num}; +use crate::plonk::circuit::simple_term::{Term}; +use crate::plonk::circuit::linear_combination::LinearCombination; + +use plonk::circuit::bigint_new::{ + enforce_range_check_using_naive_approach, + enforce_range_check_using_bitop_table, + BITWISE_LOGICAL_OPS_TABLE_NAME +}; + +use boojum::field::{ + goldilocks::GoldilocksField as GL, + U64Representable, + PrimeField as PF, + goldilocks::GoldilocksExt2, + ExtensionField +}; + +use std::result; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use derivative::*; +use std::hash::{Hash, Hasher}; + +mod prime_field_like; + +#[derive(Derivative)] +#[derivative(Clone, Copy, Copy, Default, Debug(bound = ""))] +pub struct GoldilocksField { + inner: Num, +} + +impl Hash for GoldilocksField { + fn hash(&self, state: &mut H) { + let value: Option = (*self).into_u64(); + value.hash(state); + } +} + +fn range_check_for_num_bits>( + cs: &mut CS, + num: &Num, + num_bits: usize +) -> Result<(), SynthesisError> { + assert!(num_bits % 16 == 0); + + if let Num::Constant(value) = num { + for el in value.into_repr().as_ref().iter().skip(1) { + assert_eq!(0, *el) + } + } else { + // Name of the table should be checked + if let Ok(table) = cs.get_table(BITWISE_LOGICAL_OPS_TABLE_NAME) { + enforce_range_check_using_bitop_table(cs, &num.get_variable(), num_bits, table, true)?; + } else { + enforce_range_check_using_naive_approach(cs, &num.get_variable(), num_bits)?; + } + } + + Ok(()) +} + +impl GoldilocksField { + const ORDER: u64 = 0xFFFFFFFF00000001; + const ORDER_BITS: usize = 64; + const REMAINDER: u64 = 0xFFFFFFFF; // ORDER + REMAINDER = 2^ORDER_BITS + + pub fn zero() -> Self { + Self::constant(0) + } + + pub fn one() -> Self { + Self::constant(1) + } + + pub fn minus_one() -> Self { + Self::constant(Self::ORDER - 1) + } + + pub fn constant_from_field(value: GL) -> Self { + Self::constant(value.as_u64_reduced()) + } + + pub fn constant(value: u64) -> Self { + assert!(value < Self::ORDER); + Self { + inner: Num::Constant(E::Fr::from_repr(value.into()).unwrap()), + } + } + + fn into_u64(self) -> Option { + if let Some(value) = self.inner.get_value() { + let value_buffer = value.into_repr(); + for el in value_buffer.as_ref().iter().skip(1) { + assert_eq!(0, *el) + } + Some(value_buffer.as_ref()[0]) + } else { + None + } + } + + fn into_field(self) -> Option { + self.into_u64().map(|value| GL::from_u64_unchecked(value)) + } + + pub fn is_constant(&self) -> bool { + self.inner.is_constant() + } + + pub fn alloc>( + cs: &mut CS, + witness: Option + ) -> Result { + let num = Num::alloc(cs, witness)?; + Self::from_num(cs, num) + } + + pub fn alloc_from_u64>( + cs: &mut CS, + witness: Option + ) -> Result { + let witness = witness.map(|value| E::Fr::from_repr(value.into()).unwrap()); + Self::alloc(cs, witness) + } + + pub fn alloc_from_field>( + cs: &mut CS, + witness: Option + ) -> Result { + let witness = witness.map(|value| value.as_u64_reduced()); + Self::alloc_from_u64(cs, witness) + } + + pub unsafe fn from_num_unchecked(num: Num) -> Result { + Ok(Self { + inner: num, + }) + } + + pub fn from_num>(cs: &mut CS, num: Num) -> Result { + let remainder = Num::Constant(E::Fr::from_repr(Self::REMAINDER.into()).unwrap()); + range_check_for_num_bits(cs, &num, 64)?; + + let check = num.add(cs, &remainder)?; + range_check_for_num_bits(cs, &check, 64)?; + + Ok(Self { + inner: num, + }) + } + + pub fn from_num_to_multiple, const N: usize>(cs: &mut CS, num: Num) -> Result<[Self; N], SynthesisError> { + assert!(N * Self::ORDER_BITS <= E::Fr::CAPACITY as usize, "scalar field capacity is too small"); + let mut result = [Self::constant(0); N]; + + if N == 1 { + result[0] = Self::from_num(cs, num)?; + return Ok(result); + } + + if let Num::Constant(value) = num { + let repr = value.into_repr(); + + for (i, el) in repr.as_ref()[..N].iter().enumerate() { + assert!(*el < Self::ORDER); + result[i] = Self::constant(*el); + } + for el in repr.as_ref().iter().skip(N) { + assert_eq!(0, *el); + } + } else { + let mut chunks = [None; N]; + if let Some(value) = num.get_value() { + let repr = value.into_repr(); + + for (i, el) in repr.as_ref()[..N].iter().enumerate() { + assert!(*el < Self::ORDER); + chunks[i] = Some(*el); + } + for el in repr.as_ref().iter().skip(N) { + assert_eq!(0, *el); + } + } + + let mut coeff = E::Fr::one(); + let mut shift_repr = E::Fr::one().into_repr(); + shift_repr.shl(Self::ORDER_BITS as u32); + let shift = E::Fr::from_repr(shift_repr).unwrap(); + + let mut minus_one = E::Fr::one(); + minus_one.negate(); + + let mut lc = LinearCombination::::zero(); + for (i, chunk) in chunks.into_iter().enumerate() { + let chunk = Self::alloc_from_u64(cs, *chunk)?; + lc.add_assign_number_with_coeff(&chunk.inner, coeff); + coeff.mul_assign(&shift); + + result[i] = chunk; + } + lc.add_assign_number_with_coeff(&num, minus_one); + lc.enforce_zero(cs)?; + } + + Ok(result) + } + + pub fn negate>( + &self, + cs: &mut CS, + ) -> Result { + let order = Num::Constant(E::Fr::from_repr(Self::ORDER.into()).unwrap()); + let negate = order.sub(cs, &self.inner)?; + Ok(Self { inner: negate }) + } + + pub fn add>( + &self, + cs: &mut CS, + other: &Self, + ) -> Result { + if let (Num::Constant(a), Num::Constant(b)) = (&self.inner, &other.inner) { + let a = a.into_repr().as_ref()[0] as u128; + let b = b.into_repr().as_ref()[0] as u128; + + let sum = (a + b) % Self::ORDER as u128; + + return Ok(Self::constant(sum as u64)); + } + + let mut order = E::Fr::from_repr(Self::ORDER.into()).unwrap(); + order.negate(); + let minus_order = Num::Constant(order); + + let overflow = if let (Some(a), Some(b)) = (self.inner.get_value(), other.inner.get_value()) { + let a = a.into_repr().as_ref()[0] as u128; + let b = b.into_repr().as_ref()[0] as u128; + + if a + b > Self::ORDER as u128 { + Some(true) + } else { + Some(false) + } + } else { + None + }; + + let overflow = Boolean::alloc(cs, overflow)?; + + let tmp = minus_order.mul(cs, &overflow.into())?; + let result = self.inner.add_two(cs, &other.inner, &tmp)?; + + Self::from_num(cs, result) + } + + pub fn inverse>( + &self, + cs: &mut CS, + ) -> Result { + let mut inverse_witness = self.into_field(); + inverse_witness = inverse_witness.map(|el| el.inverse().expect("should be invertible")); + let inverse = Self::alloc_from_field(cs, inverse_witness)?; + + let check = self.mul(cs, &inverse)?; + check.enforce_equal(cs, &Self::one())?; + + Ok(inverse) + } + + pub fn mul>( + &self, + cs: &mut CS, + other: &Self, + ) -> Result { + let zero = Self::constant(0); + + self.mul_add(cs, other, &zero) + } + + /// self * other + third + pub fn mul_add>( + &self, + cs: &mut CS, + other: &Self, + third: &Self, + ) -> Result { + if let (Num::Constant(a), Num::Constant(b), Num::Constant(c)) = (&self.inner, &other.inner, &third.inner) { + let a = a.into_repr().as_ref()[0] as u128; + let b = b.into_repr().as_ref()[0] as u128; + let c = c.into_repr().as_ref()[0] as u128; + + let result = (a * b + c) % Self::ORDER as u128; + + return Ok(Self::constant(result as u64)); + } + + let mut order = E::Fr::from_repr(Self::ORDER.into()).unwrap(); + order.negate(); + let minus_order = Num::Constant(order); + + let overflow = if let (Some(a), Some(b), Some(c)) = (self.inner.get_value(), other.inner.get_value(), third.inner.get_value()) { + let a = a.into_repr().as_ref()[0] as u128; + let b = b.into_repr().as_ref()[0] as u128; + let c = c.into_repr().as_ref()[0] as u128; + + let res = (a * b + c) / Self::ORDER as u128; + + Some(E::Fr::from_repr((res as u64).into()).unwrap()) + } else { + None + }; + + let overflow = Num::alloc(cs, overflow)?; + range_check_for_num_bits(cs, &overflow, 64)?; + + let tmp = minus_order.mul(cs, &overflow)?; + let mut result = self.inner.mul(cs, &other.inner)?; + result = result.add_two(cs, &third.inner, &tmp)?; + + Self::from_num(cs, result) + } + + fn enforce_equal>( + &self, + cs: &mut CS, + other: &Self, + ) -> Result<(), SynthesisError> { + self.inner.enforce_equal(cs, &other.inner) + } +} + +pub type GLExt = ExtensionField::; + +/// Extension with poly x^2 - 7 +#[derive(Derivative)] +#[derivative(Clone, Copy, Copy, Default, Debug(bound = ""), Hash(bound = ""))] +pub struct GoldilocksFieldExt { + inner: [GoldilocksField; 2], +} + +impl GoldilocksFieldExt { + const NON_RESIDUE: u64 = 7; + const EXTENSION_DEGREE: usize = 2; + + pub fn zero() -> Self { + Self::from_coords([GoldilocksField::zero(); 2]) + } + + pub fn one() -> Self { + Self::from_coords([GoldilocksField::one(), GoldilocksField::zero()]) + } + + pub fn minus_one() -> Self { + Self::from_coords([GoldilocksField::minus_one(), GoldilocksField::zero()]) + } + + pub fn from_coords(inner: [GoldilocksField; 2]) -> Self { + Self { inner } + } + + pub fn into_field_ext(self) -> Option { + if let (Some(x), Some(y)) = (self.inner[0].into_field(), self.inner[1].into_field()) { + Some(GLExt { + coeffs: [x, y], + _marker: std::marker::PhantomData, + }) + } else { + None + } + } + + pub fn alloc_from_field_ext>( + cs: &mut CS, + witness: Option + ) -> Result { + let (x_witness, y_witness); + if let Some(witness) = witness { + x_witness = Some(witness.coeffs[0]); + y_witness = Some(witness.coeffs[1]); + } else { + x_witness = None; + y_witness = None; + }; + + Ok(Self { + inner: [ + GoldilocksField::alloc_from_field(cs, x_witness)?, + GoldilocksField::alloc_from_field(cs, y_witness)?, + ] + }) + } + + pub fn constant_from_field(value: GL) -> Self { + Self::from_coords([GoldilocksField::constant_from_field(value), GoldilocksField::zero()]) + } + + pub fn from_num_coords>( + cs: &mut CS, + inner: [Num; 2] + ) -> Result { + Ok(Self { + inner: [ + GoldilocksField::from_num(cs, inner[0])?, + GoldilocksField::from_num(cs, inner[1])?, + ] + }) + } + + pub fn constant(value: [u64; 2]) -> Self { + let order = GoldilocksField::::ORDER; + assert!(value[0] < order && value[1] < order); + Self { + inner: [ + GoldilocksField::constant(value[0]), + GoldilocksField::constant(value[1]), + ], + } + } + + pub fn is_constant(&self) -> bool { + self.inner[0].is_constant() && self.inner[1].is_constant() + } + + pub fn negate>( + &self, + cs: &mut CS, + ) -> Result { + let x = self.inner[0].negate(cs)?; + let y = self.inner[1].negate(cs)?; + + Ok(GoldilocksFieldExt::from_coords([x, y]).into()) + } + + pub fn add>( + &self, + cs: &mut CS, + other: &Self, + ) -> Result { + let mut result = [GoldilocksField::zero(); 2]; + for i in 0..Self::EXTENSION_DEGREE { + result[i] = self.inner[i].add(cs, &other.inner[i])?; + } + + Ok(Self::from_coords(result)) + } + + pub fn inverse>( + &self, + cs: &mut CS, + ) -> Result { + let mut field_ext = self.into_field_ext(); + field_ext = field_ext.map(|x| x.inverse().expect("should be non-zero")); + let inversed = Self::alloc_from_field_ext(cs, field_ext)?; + + // check inverse + let one = Self::one(); + let check = self.mul(cs, &inversed)?; + check.enforce_equal(cs, &one)?; + + Ok(inversed) + } + + /// self * other + third + pub fn mul_add>( + &self, + cs: &mut CS, + other: &Self, + third: &Self, + ) -> Result { + let mut res_witness = [None; 2]; + let mut divs = [None; 2]; + if let ( + Some(a_x), Some(a_y), + Some(b_x), Some(b_y), + Some(c_x), Some(c_y), + ) = ( + &self.inner[0].inner.get_value(), &self.inner[1].inner.get_value(), + &other.inner[0].inner.get_value(), &other.inner[1].inner.get_value(), + &third.inner[0].inner.get_value(), &third.inner[1].inner.get_value(), + ) { + let a_x = a_x.into_repr().as_ref()[0] as u128; + let a_y = a_y.into_repr().as_ref()[0] as u128; + let b_x = b_x.into_repr().as_ref()[0] as u128; + let b_y = b_y.into_repr().as_ref()[0] as u128; + let c_x = c_x.into_repr().as_ref()[0] as u128; + let c_y = c_y.into_repr().as_ref()[0] as u128; + + // first coordinate + let mut res_x_part = a_y * b_y; + let mut div_x = (res_x_part / GoldilocksField::::ORDER as u128) * Self::NON_RESIDUE as u128; + res_x_part %= GoldilocksField::::ORDER as u128; + + let mut res_x = a_x * b_x; + div_x += res_x / GoldilocksField::::ORDER as u128; + res_x %= GoldilocksField::::ORDER as u128; + + res_x += c_x + res_x_part * Self::NON_RESIDUE as u128; + div_x += res_x / GoldilocksField::::ORDER as u128; + res_x %= GoldilocksField::::ORDER as u128; + + res_witness[0] = Some(res_x as u64); + + // second coordinate + let mut res_y_part = a_y * b_x; + let mut div_y = res_y_part / GoldilocksField::::ORDER as u128; + res_y_part %= GoldilocksField::::ORDER as u128; + + let mut res_y = a_x * b_y + res_y_part + c_y; + div_y += res_y / GoldilocksField::::ORDER as u128; + res_y %= GoldilocksField::::ORDER as u128; + + res_witness[1] = Some(res_y as u64); + + // divs + divs[0] = Some(E::Fr::from_str(&div_x.to_string()).unwrap()); + divs[1] = Some(E::Fr::from_str(&div_y.to_string()).unwrap()); + } + + if self.is_constant() && other.is_constant() { + return Ok( + Self::constant( + [res_witness[0].unwrap(), res_witness[1].unwrap()] + ) + ); + } + + let result = [ + GoldilocksField::alloc_from_u64(cs, res_witness[0])?, + GoldilocksField::alloc_from_u64(cs, res_witness[1])?, + ]; + + let divs = [ + Num::alloc(cs, divs[0])?, + Num::alloc(cs, divs[1])? + ]; + range_check_for_num_bits(cs, &divs[0], 80)?; + range_check_for_num_bits(cs, &divs[1], 80)?; + + // check multiplication + let mut minus_one = E::Fr::one(); + minus_one.negate(); + let non_residue = E::Fr::from_repr(Self::NON_RESIDUE.into()).unwrap(); + + let order = GoldilocksField::::ORDER; + let mut minus_order = E::Fr::from_repr(order.into()).unwrap(); + minus_order.negate(); + + + // check first coordinate + let v_0 = self.inner[0].inner.mul(cs, &other.inner[0].inner)?; + let v_1 = self.inner[1].inner.mul(cs, &other.inner[1].inner)?; + + let mut lc = LinearCombination::::zero(); + lc.add_assign_number_with_coeff(&v_0, E::Fr::one()); + lc.add_assign_number_with_coeff(&v_1, non_residue); + lc.add_assign_number_with_coeff(&third.inner[0].inner, E::Fr::one()); + lc.add_assign_number_with_coeff(&result[0].inner, minus_one); + lc.add_assign_number_with_coeff(&divs[0], minus_order); + lc.enforce_zero(cs)?; + + + // check second coordinate + let v_0 = self.inner[0].inner.mul(cs, &other.inner[1].inner)?; + let v_1 = self.inner[1].inner.mul(cs, &other.inner[0].inner)?; + + let mut lc = LinearCombination::::zero(); + lc.add_assign_number_with_coeff(&v_0, E::Fr::one()); + lc.add_assign_number_with_coeff(&v_1, E::Fr::one()); + lc.add_assign_number_with_coeff(&third.inner[1].inner, E::Fr::one()); + lc.add_assign_number_with_coeff(&result[1].inner, minus_one); + lc.add_assign_number_with_coeff(&divs[1], minus_order); + lc.enforce_zero(cs)?; + + + Ok(Self::from_coords(result)) + } + + pub fn mul>( + &self, + cs: &mut CS, + other: &Self, + ) -> Result { + self.mul_add(cs, other, &Self::zero()) + } + + pub fn mul_by_base_field>( + &self, + cs: &mut CS, + other: &GoldilocksField, + ) -> Result { + let mut result = [GoldilocksField::zero(); 2]; + for i in 0..Self::EXTENSION_DEGREE { + result[i] = self.inner[i].mul(cs, &other)?; + } + + Ok(Self::from_coords(result)) + } + + fn enforce_equal>( + &self, + cs: &mut CS, + other: &Self, + ) -> Result<(), SynthesisError> { + self.inner[0].enforce_equal(cs, &other.inner[0])?; + self.inner[1].enforce_equal(cs, &other.inner[1])?; + Ok(()) + } + + pub fn evaluate_poly>( + cs: &mut CS, + point: &Self, + poly: &[Self], + ) -> Result { + if poly.len() == 0 { + return Ok(Self::zero()); + } + let mut result = poly.last().unwrap().clone(); + + for coeff in poly.iter().rev().skip(1) { + result = result.mul_add(cs, point, coeff)?; + } + + Ok(result) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::bellman::plonk::better_better_cs::cs::*; + extern crate boojum; + + use crate::bellman::pairing::bn256::{Bn256, Fr}; + use rand::Rng; + use boojum::field::U64Representable; + use boojum::field::SmallField; + use boojum::field::Field; + + #[test] + fn test_goldilocks_field() { + let mut assembly = TrivialAssembly::::new(); + let _before = assembly.n(); + + let mut rng = rand::thread_rng(); + let buffer_u64 = [0; 10].map(|_| rng.gen_range(0, GL::CHAR)); + let buffer_gl = buffer_u64.map(|x| GL::from_u64_unchecked(x)); + + let buffer_circuit = buffer_u64.map(|x| + GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap() + ); + // let buffer_circuit = buffer_u64.map(|x| + // GoldilocksField::constant(x) + // ); + + let circuit_sum = buffer_circuit[0].add(&mut assembly, &buffer_circuit[1]).unwrap(); + let mut gl_sum = buffer_gl[0]; + gl_sum.add_assign(&buffer_gl[1]); + + assert_eq!(Some(gl_sum.as_u64_reduced()), circuit_sum.into_u64()); + + // add table for range check + let columns3 = vec![ + PolyIdentifier::VariablesPolynomial(0), + PolyIdentifier::VariablesPolynomial(1), + PolyIdentifier::VariablesPolynomial(2), + ]; + + + let name = BITWISE_LOGICAL_OPS_TABLE_NAME; + let bitwise_logic_table = LookupTableApplication::new( + name, + TwoKeysOneValueBinopTable::::new(8, name), + columns3.clone(), + None, + true, + ); + assembly.add_table(bitwise_logic_table).unwrap(); + + + let circuit_fma = buffer_circuit[2].mul_add( + &mut assembly, + &buffer_circuit[3], + &buffer_circuit[4] + ).unwrap(); + + let mut gl_fma = buffer_gl[2]; + gl_fma.mul_assign(&buffer_gl[3]); + gl_fma.add_assign(&buffer_gl[4]); + + assert_eq!(Some(gl_fma.as_u64_reduced()), circuit_fma.into_u64()); + + let mut repr = Fr::default().into_repr(); + repr.as_mut()[..3].copy_from_slice(&buffer_u64[5..8]); + let combined = Num::alloc( + &mut assembly, + Some(Fr::from_repr(repr).unwrap()) + ).unwrap(); + + let parts = GoldilocksField::from_num_to_multiple::<_, 3>(&mut assembly, combined).unwrap(); + + for (i, part) in parts.into_iter().enumerate() { + assert_eq!(Some(buffer_u64[5 + i]), (*part).into_u64()); + } + + assert!(assembly.is_satisfied()); + } + + #[test] + fn test_goldilocks_field_extension() { + let mut assembly = TrivialAssembly::::new(); + let _before = assembly.n(); + + let mut rng = rand::thread_rng(); + let buffer_u64 = [0; 10].map(|_| rng.gen_range(0, GL::CHAR)); + + let buffer_circuit = buffer_u64.map(|x| + GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap() + ); + // let buffer_circuit = buffer_u64.map(|x| + // GoldilocksField::constant(x) + // ); + + // add table for range check + let columns3 = vec![ + PolyIdentifier::VariablesPolynomial(0), + PolyIdentifier::VariablesPolynomial(1), + PolyIdentifier::VariablesPolynomial(2), + ]; + + let name = BITWISE_LOGICAL_OPS_TABLE_NAME; + let bitwise_logic_table = LookupTableApplication::new( + name, + TwoKeysOneValueBinopTable::::new(8, name), + columns3.clone(), + None, + true, + ); + assembly.add_table(bitwise_logic_table).unwrap(); + + let a = GoldilocksFieldExt::::from_coords([buffer_circuit[0], buffer_circuit[1]]); + let b = GoldilocksFieldExt::::from_coords([buffer_circuit[2], buffer_circuit[3]]); + let c = GoldilocksFieldExt::::from_coords([buffer_circuit[4], buffer_circuit[5]]); + + let fma_actual = a.mul_add(&mut assembly, &b, &c).unwrap(); + + // calculating expected value + let mut x_coord = a.inner[0].mul(&mut assembly, &b.inner[0]).unwrap(); + let mut part = a.inner[1].mul(&mut assembly, &b.inner[1]).unwrap(); + let non_residue = GoldilocksField::constant(GoldilocksFieldExt::::NON_RESIDUE); + part = part.mul(&mut assembly, &non_residue).unwrap(); + x_coord = x_coord.add(&mut assembly, &part).unwrap(); + x_coord = x_coord.add(&mut assembly, &c.inner[0]).unwrap(); + + let mut y_coord = a.inner[0].mul(&mut assembly, &b.inner[1]).unwrap(); + part = a.inner[1].mul(&mut assembly, &b.inner[0]).unwrap(); + y_coord = y_coord.add(&mut assembly, &part).unwrap(); + y_coord = y_coord.add(&mut assembly, &c.inner[1]).unwrap(); + + let fma_expected = GoldilocksFieldExt::::from_coords([x_coord, y_coord]); + + fma_actual.enforce_equal(&mut assembly, &fma_expected).unwrap(); + + let inversed = fma_actual.inverse(&mut assembly).unwrap(); + + assert!(assembly.is_satisfied()); + } + +} diff --git a/src/plonk/circuit/goldilocks/prime_field_like.rs b/src/plonk/circuit/goldilocks/prime_field_like.rs new file mode 100644 index 00000000..bae1443c --- /dev/null +++ b/src/plonk/circuit/goldilocks/prime_field_like.rs @@ -0,0 +1,213 @@ +use super::*; + +use boojum::field::traits::field_like::PrimeFieldLike; +use boojum::field::goldilocks::GoldilocksField as GL; + +#[derive(Derivative)] +#[derivative(Clone, Copy, Debug(bound = ""), Hash(bound = ""))] +pub struct GoldilocksAsFieldWrapper> { + inner: GoldilocksField, + #[derivative(Debug = "ignore", Hash = "ignore")] + _marker: std::marker::PhantomData CS>, +} + +impl> From> for GoldilocksAsFieldWrapper { + fn from(value: GoldilocksField) -> Self { + Self { + inner: value, + _marker: std::marker::PhantomData, + } + } +} + +impl> std::fmt::Display for GoldilocksAsFieldWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Num as PrimeFieldLike{{")?; + writeln!(f, "inner num: {:?},", self.inner.inner)?; + writeln!(f, "}}") + } +} + +impl> PrimeFieldLike for GoldilocksAsFieldWrapper +where + CS: 'static, +{ + type Base = GL; + type Context = CS; + + // identities + fn zero(_ctx: &mut Self::Context) -> Self { + let inner = GoldilocksField::zero(); + inner.into() + } + + fn one(_ctx: &mut Self::Context) -> Self { + let inner = GoldilocksField::one(); + inner.into() + } + + fn minus_one(_ctx: &mut Self::Context) -> Self { + let inner = GoldilocksField::minus_one(); + inner.into() + } + + // Arithmetics. Expressed in mutable way. It would not matter in after inlining + fn add_assign(&'_ mut self, other: &Self, ctx: &mut Self::Context) -> &'_ mut Self { + let new = self.inner.add(ctx, &other.inner).unwrap(); + *self = new.into(); + + self + } + + fn sub_assign(&'_ mut self, other: &Self, ctx: &mut Self::Context) -> &'_ mut Self { + let mut other_negate = *other; + other_negate.negate(ctx); + self.add_assign(&other_negate, ctx); + + self + } + + fn mul_assign(&'_ mut self, other: &Self, ctx: &mut Self::Context) -> &'_ mut Self { + let new = self.inner.mul(ctx, &other.inner).unwrap(); + *self = new.into(); + + self + } + + fn square(&'_ mut self, ctx: &mut Self::Context) -> &'_ mut Self { + let this = self.inner; + let new = self.inner.mul(ctx, &this).unwrap(); + *self = new.into(); + + self + } + + fn negate(&'_ mut self, ctx: &mut Self::Context) -> &'_ mut Self { + self.inner = self.inner.negate(ctx).unwrap(); + + self + } + + fn double(&'_ mut self, ctx: &mut Self::Context) -> &'_ mut Self { + let this = self.inner; + let new = self.inner.add(ctx, &this).unwrap(); + *self = new.into(); + + self + } + + // infallible inverse + fn inverse(&self, ctx: &mut Self::Context) -> Self { + self.inner.inverse(ctx).unwrap().into() + } + + // constant creation + fn constant(value: Self::Base, _ctx: &mut Self::Context) -> Self { + GoldilocksField::constant_from_field(value).into() + } +} + + +#[derive(Derivative)] +#[derivative(Clone, Copy, Debug(bound = ""), Hash(bound = ""))] +pub struct GoldilocksExtAsFieldWrapper> { + inner: GoldilocksFieldExt, + #[derivative(Debug = "ignore", Hash = "ignore")] + _marker: std::marker::PhantomData CS>, +} + +impl> From> for GoldilocksExtAsFieldWrapper { + fn from(value: GoldilocksFieldExt) -> Self { + Self { + inner: value, + _marker: std::marker::PhantomData, + } + } +} + +impl> std::fmt::Display for GoldilocksExtAsFieldWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Num as PrimeFieldLike{{")?; + writeln!(f, "inner field coeffs: {:?},", self.inner)?; + writeln!(f, "}}") + } +} + +impl> PrimeFieldLike for GoldilocksExtAsFieldWrapper +where + CS: 'static, +{ + type Base = GL; + type Context = CS; + + // identities + fn zero(_ctx: &mut Self::Context) -> Self { + let inner = GoldilocksFieldExt::zero(); + inner.into() + } + + fn one(_ctx: &mut Self::Context) -> Self { + let inner = GoldilocksFieldExt::one(); + inner.into() + } + + fn minus_one(_ctx: &mut Self::Context) -> Self { + let inner = GoldilocksFieldExt::minus_one(); + inner.into() + } + + // Arithmetics. Expressed in mutable way. It would not matter in after inlining + fn add_assign(&'_ mut self, other: &Self, ctx: &mut Self::Context) -> &'_ mut Self { + let new = self.inner.add(ctx, &other.inner).unwrap(); + *self = new.into(); + + self + } + + fn sub_assign(&'_ mut self, other: &Self, ctx: &mut Self::Context) -> &'_ mut Self { + let mut other_negate = *other; + other_negate.negate(ctx); + self.add_assign(&other_negate, ctx); + + self + } + + fn mul_assign(&'_ mut self, other: &Self, ctx: &mut Self::Context) -> &'_ mut Self { + let new = self.inner.mul(ctx, &other.inner).unwrap(); + *self = new.into(); + + self + } + + fn square(&'_ mut self, ctx: &mut Self::Context) -> &'_ mut Self { + let this = self.inner; + let new = self.inner.mul(ctx, &this).unwrap(); + *self = new.into(); + + self + } + + fn negate(&'_ mut self, ctx: &mut Self::Context) -> &'_ mut Self { + self.inner = self.inner.negate(ctx).unwrap(); + + self + } + + fn double(&'_ mut self, ctx: &mut Self::Context) -> &'_ mut Self { + let this = self.inner; + let new = self.inner.add(ctx, &this).unwrap(); + *self = new.into(); + + self + } + + // infallible inverse + fn inverse(&self, ctx: &mut Self::Context) -> Self { + self.inner.inverse(ctx).unwrap().into() + } + + // constant creation + fn constant(value: Self::Base, _ctx: &mut Self::Context) -> Self { + GoldilocksFieldExt::constant_from_field(value).into() + } +} diff --git a/src/plonk/circuit/mod.rs b/src/plonk/circuit/mod.rs index 735d0993..524615f4 100644 --- a/src/plonk/circuit/mod.rs +++ b/src/plonk/circuit/mod.rs @@ -13,6 +13,7 @@ pub mod bigint_new; pub mod simple_term; pub mod curve; pub mod curve_new; +pub mod goldilocks; pub mod verifier_circuit; pub mod tables; pub mod counter; From 2b8cd09dee2911072c820821c76ad92d546fb49e Mon Sep 17 00:00:00 2001 From: Oles Holembovskyy Date: Wed, 16 Aug 2023 13:48:38 +0200 Subject: [PATCH 02/15] implemented functions for snark wrapper --- Cargo.toml | 2 +- src/plonk/circuit/goldilocks/mod.rs | 144 +++++++++++++++--- .../circuit/goldilocks/prime_field_like.rs | 57 +++++++ 3 files changed, 177 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22e7146b..f277ba3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ allocator = ["bellman/allocator"] derivative = "2" # boojum = {package = "boojum", path = "../boojum" } -boojum = {git = "https://github.com/matter-labs/boojum.git", branch = "main"} +boojum = {git = "https://github.com/matter-labs/boojum.git", branch = "snark-wrapper"} rand = "0.4" digest = "0.9" diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs index ff849a1f..b5f1b6ee 100644 --- a/src/plonk/circuit/goldilocks/mod.rs +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -64,7 +64,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use derivative::*; use std::hash::{Hash, Hasher}; -mod prime_field_like; +pub mod prime_field_like; #[derive(Derivative)] #[derivative(Clone, Copy, Copy, Default, Debug(bound = ""))] @@ -103,9 +103,9 @@ fn range_check_for_num_bits>( } impl GoldilocksField { - const ORDER: u64 = 0xFFFFFFFF00000001; - const ORDER_BITS: usize = 64; - const REMAINDER: u64 = 0xFFFFFFFF; // ORDER + REMAINDER = 2^ORDER_BITS + pub const ORDER: u64 = 0xFFFFFFFF00000001; + pub const ORDER_BITS: usize = 64; + pub const REMAINDER: u64 = 0xFFFFFFFF; // ORDER + REMAINDER = 2^ORDER_BITS pub fn zero() -> Self { Self::constant(0) @@ -192,37 +192,38 @@ impl GoldilocksField { }) } - pub fn from_num_to_multiple, const N: usize>(cs: &mut CS, num: Num) -> Result<[Self; N], SynthesisError> { + pub fn into_num(&self) -> Num { + self.inner + } + + pub fn from_num_to_multiple_with_reduction, const N: usize>( + cs: &mut CS, + num: Num + ) -> Result<[Self; N], SynthesisError> { assert!(N * Self::ORDER_BITS <= E::Fr::CAPACITY as usize, "scalar field capacity is too small"); let mut result = [Self::constant(0); N]; - if N == 1 { - result[0] = Self::from_num(cs, num)?; - return Ok(result); - } - if let Num::Constant(value) = num { let repr = value.into_repr(); for (i, el) in repr.as_ref()[..N].iter().enumerate() { - assert!(*el < Self::ORDER); - result[i] = Self::constant(*el); - } - for el in repr.as_ref().iter().skip(N) { - assert_eq!(0, *el); - } + result[i] = Self::constant(el % Self::ORDER); + } } else { let mut chunks = [None; N]; + let mut overflowing = [None; N]; + let mut rest = vec![]; if let Some(value) = num.get_value() { let repr = value.into_repr(); for (i, el) in repr.as_ref()[..N].iter().enumerate() { - assert!(*el < Self::ORDER); - chunks[i] = Some(*el); - } - for el in repr.as_ref().iter().skip(N) { - assert_eq!(0, *el); + overflowing[i] = Some(*el > Self::ORDER); + chunks[i] = Some(el % Self::ORDER); } + rest.extend(repr.as_ref().iter().skip(N).map(|el| Some(*el))); + } else { + let repr = ::Repr::default(); + rest.extend(vec![None; repr.as_ref().len() - N]); } let mut coeff = E::Fr::one(); @@ -230,17 +231,29 @@ impl GoldilocksField { shift_repr.shl(Self::ORDER_BITS as u32); let shift = E::Fr::from_repr(shift_repr).unwrap(); + let mut shifted_modulus = E::Fr::from_repr(Self::ORDER.into()).unwrap(); + let mut minus_one = E::Fr::one(); minus_one.negate(); let mut lc = LinearCombination::::zero(); - for (i, chunk) in chunks.into_iter().enumerate() { - let chunk = Self::alloc_from_u64(cs, *chunk)?; + for i in 0..N { + let chunk = Self::alloc_from_u64(cs, chunks[i])?; lc.add_assign_number_with_coeff(&chunk.inner, coeff); coeff.mul_assign(&shift); + let overflow = Boolean::alloc(cs, overflowing[i])?; + lc.add_assign_boolean_with_coeff(&overflow, coeff); + shifted_modulus.mul_assign(&shift); + result[i] = chunk; } + for el in rest { + let rest = Num::alloc(cs, el.map(|value| E::Fr::from_repr(value.into()).unwrap()))?; + range_check_for_num_bits(cs, &rest, 64)?; + lc.add_assign_number_with_coeff(&rest, coeff); + coeff.mul_assign(&shift); + } lc.add_assign_number_with_coeff(&num, minus_one); lc.enforce_zero(cs)?; } @@ -363,13 +376,75 @@ impl GoldilocksField { Self::from_num(cs, result) } - fn enforce_equal>( + pub fn equals>( + cs: &mut CS, + this: &Self, + other: &Self, + ) -> Result { + Num::equals(cs, &this.inner,&other.inner) + } + + pub fn enforce_equal>( &self, cs: &mut CS, other: &Self, ) -> Result<(), SynthesisError> { self.inner.enforce_equal(cs, &other.inner) } + + pub fn conditionally_select>( + cs: &mut CS, + bit: Boolean, + first: &Self, + second: &Self + ) -> Result { + let result = Num::conditionally_select(cs, &bit, &first.inner, &second.inner)?; + + Ok(Self { inner: result} ) + } + + pub fn spread_into_bits, const LIMIT: usize>( + &self, + cs: &mut CS, + ) -> Result<[Boolean; LIMIT], SynthesisError> { + let witness = match self.inner.get_value() { + Some(value) => { + let repr = value.into_repr(); + // this is MSB iterator + let bit_iterator = BitIterator::new(&repr); + + let mut result = vec![]; + for el in bit_iterator { + result.push(el); + } + // now it's LSB based + result.reverse(); + + Some(result) + } + None => None, + }; + + let mut result = [Boolean::constant(false); LIMIT]; + for (i, dst) in result.iter_mut().enumerate() { + let wit = witness.as_ref().map(|el| el[i]); + let boolean = Boolean::alloc(cs, wit)?; + *dst = boolean + } + + let mut offset = E::Fr::one(); + let mut lc = LinearCombination::zero(); + for bit in result.iter() { + lc.add_assign_boolean_with_coeff(&bit, offset); + offset.double(); + } + let mut minus_one = E::Fr::one(); + minus_one.negate(); + lc.add_assign_number_with_coeff(&self.inner, minus_one); + lc.enforce_zero(cs)?; + + Ok(result) + } } pub type GLExt = ExtensionField::; @@ -649,6 +724,25 @@ impl GoldilocksFieldExt { Ok(()) } + pub fn conditionally_select>( + cs: &mut CS, + bit: Boolean, + first: &Self, + second: &Self + ) -> Result { + let mut result = [GoldilocksField::zero(); 2]; + for i in 0..Self::EXTENSION_DEGREE { + result[i] = GoldilocksField::conditionally_select( + cs, + bit.clone(), + &first.inner[i], + &second.inner[i] + )?; + } + + Ok(Self::from_coords(result)) + } + pub fn evaluate_poly>( cs: &mut CS, point: &Self, @@ -739,7 +833,7 @@ mod test { Some(Fr::from_repr(repr).unwrap()) ).unwrap(); - let parts = GoldilocksField::from_num_to_multiple::<_, 3>(&mut assembly, combined).unwrap(); + let parts = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>(&mut assembly, combined).unwrap(); for (i, part) in parts.into_iter().enumerate() { assert_eq!(Some(buffer_u64[5 + i]), (*part).into_u64()); diff --git a/src/plonk/circuit/goldilocks/prime_field_like.rs b/src/plonk/circuit/goldilocks/prime_field_like.rs index bae1443c..b4bc7309 100644 --- a/src/plonk/circuit/goldilocks/prime_field_like.rs +++ b/src/plonk/circuit/goldilocks/prime_field_like.rs @@ -28,6 +28,13 @@ impl> std::fmt::Display for GoldilocksAsField } } +impl> GoldilocksAsFieldWrapper { + pub fn conditionally_select(cs: &mut CS, bit: Boolean, first: &Self, second: &Self) -> Self { + let inner = GoldilocksField::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); + inner.into() + } +} + impl> PrimeFieldLike for GoldilocksAsFieldWrapper where CS: 'static, @@ -133,6 +140,56 @@ impl> std::fmt::Display for GoldilocksExtAsFi } } +impl> GoldilocksExtAsFieldWrapper { + pub fn conditionally_select(cs: &mut CS, bit: Boolean, first: &Self, second: &Self) -> Self { + let inner = GoldilocksFieldExt::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); + inner.into() + } + + pub fn from_coeffs_in_base(coeffs: [GoldilocksField; 2]) -> Self { + let inner = GoldilocksFieldExt::from_coords(coeffs); + inner.into() + } + + pub const fn into_coeffs_in_base(self) -> [GoldilocksField; 2] { + [self.inner.inner[0], self.inner.inner[1]] + } + + pub fn from_wrapper_coeffs_in_base(coeffs: [GoldilocksAsFieldWrapper; 2]) -> Self { + let coeffs = [ + coeffs[0].inner, + coeffs[1].inner, + ]; + Self::from_coeffs_in_base(coeffs) + } + + pub fn mul_by_base_and_accumulate_into( + dst: &mut Self, + base: &GoldilocksAsFieldWrapper, + other: &Self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { + for (dst, src) in dst.inner.inner.iter_mut() + .zip(other.inner.inner.iter()) { + *dst = dst.mul_add(cs, src, &base.inner)?; + } + + Ok(()) + } + + pub fn mul_assign_by_base( + &mut self, + cs: &mut CS, + base: &GoldilocksAsFieldWrapper + ) -> Result<(), SynthesisError> { + for dst in self.inner.inner.iter_mut() { + *dst = dst.mul(cs, &base.inner)?; + } + + Ok(()) + } +} + impl> PrimeFieldLike for GoldilocksExtAsFieldWrapper where CS: 'static, From 60ef813129bb7bbfd8724d496f7162f1cae458d4 Mon Sep 17 00:00:00 2001 From: Oles Holembovskyy Date: Wed, 23 Aug 2023 15:50:44 +0200 Subject: [PATCH 03/15] update bellman --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f277ba3f..8c57383c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ indexmap = "1.9" smallvec = "1.10" # bellman = { package = "bellman_ce", path = "../bellman" } -bellman = { package = "bellman_ce", git = "https://github.com/matter-labs/bellman", branch = "dev" } +bellman = { package = "bellman_ce", git = "https://github.com/matter-labs/bellman", branch = "snark-wrapper" } blake2-rfc_bellman_edition = "0.0.1" #poseidon_hash = { path = "../poseidon_hash" } #poseidon_hash = {git = "https://github.com/shamatar/poseidon_hash.git"} From 6eaf7f71468725569e23ca9dea5d894d84b18998 Mon Sep 17 00:00:00 2001 From: Oles Holembovskyy Date: Wed, 30 Aug 2023 12:27:56 +0200 Subject: [PATCH 04/15] boojum updated --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8c57383c..52c3ec02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ allocator = ["bellman/allocator"] derivative = "2" # boojum = {package = "boojum", path = "../boojum" } -boojum = {git = "https://github.com/matter-labs/boojum.git", branch = "snark-wrapper"} +boojum = {git = "https://github.com/matter-labs/boojum.git", branch = "main"} rand = "0.4" digest = "0.9" From 6108de015e01e7f732a08a12e4c5cd9c3c20e16c Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 22 Sep 2023 09:07:03 +0300 Subject: [PATCH 05/15] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 52c3ec02..fbed5980 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ allocator = ["bellman/allocator"] derivative = "2" # boojum = {package = "boojum", path = "../boojum" } -boojum = {git = "https://github.com/matter-labs/boojum.git", branch = "main"} +boojum = {git = "https://github.com/matter-labs/era-boojum.git", branch = "main"} rand = "0.4" digest = "0.9" From 9ea1ddc51582a24f37d36f542afa9cee5d791254 Mon Sep 17 00:00:00 2001 From: Yury Akudovich Date: Tue, 26 Sep 2023 12:10:22 +0200 Subject: [PATCH 06/15] Remove Secrets & Add CI to Branch snark_wrapper (#55) --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 098b9efc..38ad02f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,13 @@ name: Build and compile on: push: - branches: ["dev"] + branches: + - dev + - snark_wrapper pull_request: - branches: ["dev"] + branches: + - dev + - snark_wrapper concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -28,12 +32,10 @@ jobs: - name: Setup rust run: | rustup set profile minimal - rustup toolchain install nightly-2022-08-23 - rustup default nightly-2022-08-23 + rustup toolchain install nightly-2023-08-23 + rustup default nightly-2023-08-23 - name: Install build dependencies run: | - git config --global --add url."https://${{ secrets.ZKSYNC_ADMIN_BOT_ORG_REPO_WRITE }}:x-oauth-basic@github.com/".insteadOf ssh://git@github.com/ - git config --global --add url."https://${{ secrets.ZKSYNC_ADMIN_BOT_ORG_REPO_WRITE }}:x-oauth-basic@github.com/".insteadOf https://github.com/ sudo apt update && sudo apt install -y pkg-config libssl-dev libclang-dev gcc g++ - name: Compile run: | From 13b493d7619945b035cbbc79d74212298f0b7a26 Mon Sep 17 00:00:00 2001 From: Yury Akudovich Date: Tue, 26 Sep 2023 14:18:53 +0200 Subject: [PATCH 07/15] Moves to ubuntu-latest in snark_wrapper (#57) * Moves to ubuntu-latest. Removes installs. * Adds license check CI. --- .github/workflows/cargo-license.yaml | 8 +++ .github/workflows/ci.yml | 5 +- deny.toml | 79 ++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/cargo-license.yaml create mode 100644 deny.toml diff --git a/.github/workflows/cargo-license.yaml b/.github/workflows/cargo-license.yaml new file mode 100644 index 00000000..189b4716 --- /dev/null +++ b/.github/workflows/cargo-license.yaml @@ -0,0 +1,8 @@ +name: Cargo license check +on: pull_request +jobs: + cargo-deny: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: EmbarkStudios/cargo-deny-action@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38ad02f3..8b9c1aa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: jobs: build: - runs-on: [self-hosted, ci-runner] + runs-on: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -34,9 +34,6 @@ jobs: rustup set profile minimal rustup toolchain install nightly-2023-08-23 rustup default nightly-2023-08-23 - - name: Install build dependencies - run: | - sudo apt update && sudo apt install -y pkg-config libssl-dev libclang-dev gcc g++ - name: Compile run: | cargo build --verbose diff --git a/deny.toml b/deny.toml new file mode 100644 index 00000000..6977d43f --- /dev/null +++ b/deny.toml @@ -0,0 +1,79 @@ +all-features = false +no-default-features = false + +[advisories] +vulnerability = "deny" +unmaintained = "warn" +yanked = "warn" +notice = "warn" +ignore = [ + #"RUSTSEC-0000-0000", +] + +[licenses] +unlicensed = "deny" +allow = [ + #"Apache-2.0 WITH LLVM-exception", + "MIT", + "Apache-2.0", + "ISC", + "Unlicense", + "MPL-2.0", + "Unicode-DFS-2016", + "CC0-1.0", + "BSD-2-Clause", + "BSD-3-Clause", + "Zlib", +] +deny = [ + #"Nokia", +] +copyleft = "warn" +allow-osi-fsf-free = "neither" +default = "deny" +confidence-threshold = 0.8 +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +unused-allowed-license = "allow" + +[licenses.private] +ignore = false +registries = [ + #"https://sekretz.com/registry +] + +[bans] +multiple-versions = "warn" +wildcards = "allow" +highlight = "all" +workspace-default-features = "allow" +external-default-features = "allow" +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, +] + +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +[sources] +unknown-registry = "deny" +unknown-git = "allow" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] + +[sources.allow-org] +#github = ["matter-labs"] From b41aa2b0ecf6efd19d9d0e0c7d20cce1bfaab3fb Mon Sep 17 00:00:00 2001 From: olesHolem Date: Wed, 27 Sep 2023 17:56:54 +0300 Subject: [PATCH 08/15] fixed mul_add issue --- src/plonk/circuit/goldilocks/mod.rs | 4 ++-- src/plonk/circuit/goldilocks/prime_field_like.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs index b5f1b6ee..381b82b0 100644 --- a/src/plonk/circuit/goldilocks/mod.rs +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -130,7 +130,7 @@ impl GoldilocksField { } } - fn into_u64(self) -> Option { + pub fn into_u64(self) -> Option { if let Some(value) = self.inner.get_value() { let value_buffer = value.into_repr(); for el in value_buffer.as_ref().iter().skip(1) { @@ -142,7 +142,7 @@ impl GoldilocksField { } } - fn into_field(self) -> Option { + pub fn into_field(self) -> Option { self.into_u64().map(|value| GL::from_u64_unchecked(value)) } diff --git a/src/plonk/circuit/goldilocks/prime_field_like.rs b/src/plonk/circuit/goldilocks/prime_field_like.rs index b4bc7309..48719ee6 100644 --- a/src/plonk/circuit/goldilocks/prime_field_like.rs +++ b/src/plonk/circuit/goldilocks/prime_field_like.rs @@ -171,7 +171,7 @@ impl> GoldilocksExtAsFieldWrapper { ) -> Result<(), SynthesisError> { for (dst, src) in dst.inner.inner.iter_mut() .zip(other.inner.inner.iter()) { - *dst = dst.mul_add(cs, src, &base.inner)?; + *dst = src.mul_add(cs, &base.inner, dst)?; } Ok(()) From ef6372c97d3fc59e4eae29b61616ae30be3339a0 Mon Sep 17 00:00:00 2001 From: olesHolem Date: Thu, 12 Oct 2023 03:15:56 +0300 Subject: [PATCH 09/15] done --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9eb42c2b..28545ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ pub extern crate bellman; +pub extern crate boojum; extern crate blake2_rfc_bellman_edition as blake2_rfc; extern crate digest; extern crate rand; @@ -20,7 +21,6 @@ extern crate blake2; extern crate serde; extern crate num_derive; extern crate indexmap; -extern crate boojum; extern crate derivative; use bellman::pairing; From 50b198cd81760d7c49ae7cdefb620e8d0d21b53c Mon Sep 17 00:00:00 2001 From: olesHolem Date: Fri, 3 Nov 2023 15:04:04 +0200 Subject: [PATCH 10/15] Fr to Goldilocks fixed --- src/plonk/circuit/goldilocks/mod.rs | 376 ++++++++++-------- .../circuit/goldilocks/prime_field_like.rs | 33 +- 2 files changed, 225 insertions(+), 184 deletions(-) diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs index 381b82b0..88251476 100644 --- a/src/plonk/circuit/goldilocks/mod.rs +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -1,68 +1,42 @@ +use itertools::all; use num_traits::ops::overflowing; -use crate::bellman::pairing::{ - Engine, -}; - -use crate::bellman::pairing::ff::{ - Field, - PrimeField, - PrimeFieldRepr, - BitIterator -}; - -use crate::bellman::{ - SynthesisError, -}; +use crate::bellman::pairing::ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; +use crate::bellman::pairing::Engine; +use crate::bellman::SynthesisError; use plonk::circuit::boolean::Boolean; use crate::bellman::plonk::better_better_cs::cs::{ - Variable, - ConstraintSystem, - ArithmeticTerm, - MainGateTerm, + ArithmeticTerm, Coefficient, ConstraintSystem, Gate, GateInternal, LinearCombinationOfTerms, + MainGate, MainGateTerm, PlonkConstraintSystemParams, PlonkCsWidth4WithNextStepParams, + PolynomialInConstraint, PolynomialMultiplicativeTerm, TimeDilation, TrivialAssembly, Variable, Width4MainGateWithDNext, - MainGate, - GateInternal, - Gate, - LinearCombinationOfTerms, - PolynomialMultiplicativeTerm, - PolynomialInConstraint, - TimeDilation, - Coefficient, - PlonkConstraintSystemParams, - PlonkCsWidth4WithNextStepParams, - TrivialAssembly }; -use crate::plonk::circuit::Assignment; -use super::*; use super::bigint::*; +use super::*; +use crate::plonk::circuit::Assignment; use crate::plonk::circuit::allocated_num::{AllocatedNum, Num}; -use crate::plonk::circuit::simple_term::{Term}; use crate::plonk::circuit::linear_combination::LinearCombination; +use crate::plonk::circuit::simple_term::Term; use plonk::circuit::bigint_new::{ - enforce_range_check_using_naive_approach, - enforce_range_check_using_bitop_table, - BITWISE_LOGICAL_OPS_TABLE_NAME + enforce_range_check_using_bitop_table, enforce_range_check_using_naive_approach, + BITWISE_LOGICAL_OPS_TABLE_NAME, }; use boojum::field::{ - goldilocks::GoldilocksField as GL, - U64Representable, - PrimeField as PF, - goldilocks::GoldilocksExt2, - ExtensionField + goldilocks::GoldilocksExt2, goldilocks::GoldilocksField as GL, ExtensionField, + PrimeField as PF, U64Representable, }; -use std::result; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; use derivative::*; use std::hash::{Hash, Hasher}; +use std::result; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; pub mod prime_field_like; @@ -82,7 +56,7 @@ impl Hash for GoldilocksField { fn range_check_for_num_bits>( cs: &mut CS, num: &Num, - num_bits: usize + num_bits: usize, ) -> Result<(), SynthesisError> { assert!(num_bits % 16 == 0); @@ -145,14 +119,14 @@ impl GoldilocksField { pub fn into_field(self) -> Option { self.into_u64().map(|value| GL::from_u64_unchecked(value)) } - + pub fn is_constant(&self) -> bool { self.inner.is_constant() } pub fn alloc>( cs: &mut CS, - witness: Option + witness: Option, ) -> Result { let num = Num::alloc(cs, witness)?; Self::from_num(cs, num) @@ -160,7 +134,7 @@ impl GoldilocksField { pub fn alloc_from_u64>( cs: &mut CS, - witness: Option + witness: Option, ) -> Result { let witness = witness.map(|value| E::Fr::from_repr(value.into()).unwrap()); Self::alloc(cs, witness) @@ -168,28 +142,27 @@ impl GoldilocksField { pub fn alloc_from_field>( cs: &mut CS, - witness: Option + witness: Option, ) -> Result { let witness = witness.map(|value| value.as_u64_reduced()); Self::alloc_from_u64(cs, witness) } pub unsafe fn from_num_unchecked(num: Num) -> Result { - Ok(Self { - inner: num, - }) + Ok(Self { inner: num }) } - pub fn from_num>(cs: &mut CS, num: Num) -> Result { + pub fn from_num>( + cs: &mut CS, + num: Num, + ) -> Result { let remainder = Num::Constant(E::Fr::from_repr(Self::REMAINDER.into()).unwrap()); range_check_for_num_bits(cs, &num, 64)?; let check = num.add(cs, &remainder)?; range_check_for_num_bits(cs, &check, 64)?; - Ok(Self { - inner: num, - }) + Ok(Self { inner: num }) } pub fn into_num(&self) -> Num { @@ -197,10 +170,14 @@ impl GoldilocksField { } pub fn from_num_to_multiple_with_reduction, const N: usize>( - cs: &mut CS, - num: Num + cs: &mut CS, + num: Num, ) -> Result<[Self; N], SynthesisError> { - assert!(N * Self::ORDER_BITS <= E::Fr::CAPACITY as usize, "scalar field capacity is too small"); + assert_eq!(Self::ORDER_BITS, 64, "Only this case is supported for now"); + assert!( + N * Self::ORDER_BITS <= E::Fr::CAPACITY as usize, + "Scalar field capacity is too small" + ); let mut result = [Self::constant(0); N]; if let Num::Constant(value) = num { @@ -208,63 +185,111 @@ impl GoldilocksField { for (i, el) in repr.as_ref()[..N].iter().enumerate() { result[i] = Self::constant(el % Self::ORDER); - } + } } else { - let mut chunks = [None; N]; + let mut u64_chunks = vec![]; let mut overflowing = [None; N]; - let mut rest = vec![]; if let Some(value) = num.get_value() { let repr = value.into_repr(); for (i, el) in repr.as_ref()[..N].iter().enumerate() { - overflowing[i] = Some(*el > Self::ORDER); - chunks[i] = Some(el % Self::ORDER); + overflowing[i] = Some(*el >= Self::ORDER); } - rest.extend(repr.as_ref().iter().skip(N).map(|el| Some(*el))); + u64_chunks = repr.as_ref().iter().map(|el| Some(*el)).collect(); } else { let repr = ::Repr::default(); - rest.extend(vec![None; repr.as_ref().len() - N]); + u64_chunks = vec![None; repr.as_ref().len()]; } + // Firstly we check that chunks sum is correct + let mut allocated_chunks = vec![]; + let mut coeff = E::Fr::one(); let mut shift_repr = E::Fr::one().into_repr(); shift_repr.shl(Self::ORDER_BITS as u32); let shift = E::Fr::from_repr(shift_repr).unwrap(); - let mut shifted_modulus = E::Fr::from_repr(Self::ORDER.into()).unwrap(); - let mut minus_one = E::Fr::one(); minus_one.negate(); let mut lc = LinearCombination::::zero(); - for i in 0..N { - let chunk = Self::alloc_from_u64(cs, chunks[i])?; - lc.add_assign_number_with_coeff(&chunk.inner, coeff); - coeff.mul_assign(&shift); - - let overflow = Boolean::alloc(cs, overflowing[i])?; - lc.add_assign_boolean_with_coeff(&overflow, coeff); - shifted_modulus.mul_assign(&shift); - - result[i] = chunk; - } - for el in rest { - let rest = Num::alloc(cs, el.map(|value| E::Fr::from_repr(value.into()).unwrap()))?; - range_check_for_num_bits(cs, &rest, 64)?; - lc.add_assign_number_with_coeff(&rest, coeff); + for chunk in u64_chunks { + let witness = chunk.map(|value| E::Fr::from_repr(value.into()).unwrap()); + let allocated_chunk = Num::alloc(cs, witness)?; + range_check_for_num_bits(cs, &allocated_chunk, 64)?; + lc.add_assign_number_with_coeff(&allocated_chunk, coeff); coeff.mul_assign(&shift); + allocated_chunks.push(allocated_chunk) } lc.add_assign_number_with_coeff(&num, minus_one); lc.enforce_zero(cs)?; + + // Now we check that there is no overflow + assert_eq!( + allocated_chunks.len(), + 4, + "Only this case is supported for now" + ); + + let mut first_u128_chunk: LinearCombination = allocated_chunks[0].into(); + first_u128_chunk.add_assign_number_with_coeff(&allocated_chunks[1], shift); + let first_u128_chunk = first_u128_chunk.into_num(cs)?; + + let mut second_u128_chunk: LinearCombination = allocated_chunks[2].into(); + second_u128_chunk.add_assign_number_with_coeff(&allocated_chunks[3], shift); + let second_u128_chunk = second_u128_chunk.into_num(cs)?; + + let max_field_element = minus_one; + let (first_max_element_chunk, second_max_element_chunk) = { + let fe_repr: Vec<_> = max_field_element + .into_repr() + .as_ref() + .iter() + .map(|value| E::Fr::from_repr((*value).into()).unwrap()) + .collect(); + + let mut first_chunk = fe_repr[1]; + first_chunk.mul_assign(&shift); + first_chunk.add_assign(&fe_repr[0]); + + let mut second_chunk = fe_repr[3]; + second_chunk.mul_assign(&shift); + second_chunk.add_assign(&fe_repr[2]); + + (Num::Constant(first_chunk), Num::Constant(second_chunk)) + }; + + let check = second_max_element_chunk.sub(cs, &second_u128_chunk)?; + range_check_for_num_bits(cs, &check, 128)?; + let flag = check.is_zero(cs)?.not(); + + let mut double_shift_repr = E::Fr::one().into_repr(); + double_shift_repr.shl(2 * Self::ORDER_BITS as u32); + let double_shift = E::Fr::from_repr(double_shift_repr).unwrap(); + + let mut check_2: LinearCombination = first_max_element_chunk.into(); + check_2.add_assign_number_with_coeff(&first_u128_chunk, minus_one); + check_2.add_assign_boolean_with_coeff(&flag, double_shift); + let check_2 = check_2.into_num(cs)?; + range_check_for_num_bits(cs, &check_2, 144)?; + + // Now we can get Goldilocks elements from N u64 chunks + let mut neg_modulus = E::Fr::from_repr(Self::ORDER.into()).unwrap(); + neg_modulus.negate(); + for i in 0..N { + let mut result_element: LinearCombination = allocated_chunks[i].into(); + let overflow_flag = Boolean::alloc(cs, overflowing[i])?; + result_element.add_assign_boolean_with_coeff(&overflow_flag, neg_modulus); + let result_num = result_element.into_num(cs)?; + + result[i] = Self::from_num(cs, result_num)?; + } } Ok(result) } - pub fn negate>( - &self, - cs: &mut CS, - ) -> Result { + pub fn negate>(&self, cs: &mut CS) -> Result { let order = Num::Constant(E::Fr::from_repr(Self::ORDER.into()).unwrap()); let negate = order.sub(cs, &self.inner)?; Ok(Self { inner: negate }) @@ -288,7 +313,8 @@ impl GoldilocksField { order.negate(); let minus_order = Num::Constant(order); - let overflow = if let (Some(a), Some(b)) = (self.inner.get_value(), other.inner.get_value()) { + let overflow = if let (Some(a), Some(b)) = (self.inner.get_value(), other.inner.get_value()) + { let a = a.into_repr().as_ref()[0] as u128; let b = b.into_repr().as_ref()[0] as u128; @@ -308,11 +334,8 @@ impl GoldilocksField { Self::from_num(cs, result) } - - pub fn inverse>( - &self, - cs: &mut CS, - ) -> Result { + + pub fn inverse>(&self, cs: &mut CS) -> Result { let mut inverse_witness = self.into_field(); inverse_witness = inverse_witness.map(|el| el.inverse().expect("should be invertible")); let inverse = Self::alloc_from_field(cs, inverse_witness)?; @@ -340,7 +363,9 @@ impl GoldilocksField { other: &Self, third: &Self, ) -> Result { - if let (Num::Constant(a), Num::Constant(b), Num::Constant(c)) = (&self.inner, &other.inner, &third.inner) { + if let (Num::Constant(a), Num::Constant(b), Num::Constant(c)) = + (&self.inner, &other.inner, &third.inner) + { let a = a.into_repr().as_ref()[0] as u128; let b = b.into_repr().as_ref()[0] as u128; let c = c.into_repr().as_ref()[0] as u128; @@ -354,7 +379,11 @@ impl GoldilocksField { order.negate(); let minus_order = Num::Constant(order); - let overflow = if let (Some(a), Some(b), Some(c)) = (self.inner.get_value(), other.inner.get_value(), third.inner.get_value()) { + let overflow = if let (Some(a), Some(b), Some(c)) = ( + self.inner.get_value(), + other.inner.get_value(), + third.inner.get_value(), + ) { let a = a.into_repr().as_ref()[0] as u128; let b = b.into_repr().as_ref()[0] as u128; let c = c.into_repr().as_ref()[0] as u128; @@ -381,7 +410,7 @@ impl GoldilocksField { this: &Self, other: &Self, ) -> Result { - Num::equals(cs, &this.inner,&other.inner) + Num::equals(cs, &this.inner, &other.inner) } pub fn enforce_equal>( @@ -396,11 +425,11 @@ impl GoldilocksField { cs: &mut CS, bit: Boolean, first: &Self, - second: &Self + second: &Self, ) -> Result { let result = Num::conditionally_select(cs, &bit, &first.inner, &second.inner)?; - Ok(Self { inner: result} ) + Ok(Self { inner: result }) } pub fn spread_into_bits, const LIMIT: usize>( @@ -447,7 +476,7 @@ impl GoldilocksField { } } -pub type GLExt = ExtensionField::; +pub type GLExt = ExtensionField; /// Extension with poly x^2 - 7 #[derive(Derivative)] @@ -489,7 +518,7 @@ impl GoldilocksFieldExt { pub fn alloc_from_field_ext>( cs: &mut CS, - witness: Option + witness: Option, ) -> Result { let (x_witness, y_witness); if let Some(witness) = witness { @@ -504,23 +533,26 @@ impl GoldilocksFieldExt { inner: [ GoldilocksField::alloc_from_field(cs, x_witness)?, GoldilocksField::alloc_from_field(cs, y_witness)?, - ] + ], }) } pub fn constant_from_field(value: GL) -> Self { - Self::from_coords([GoldilocksField::constant_from_field(value), GoldilocksField::zero()]) + Self::from_coords([ + GoldilocksField::constant_from_field(value), + GoldilocksField::zero(), + ]) } pub fn from_num_coords>( - cs: &mut CS, - inner: [Num; 2] + cs: &mut CS, + inner: [Num; 2], ) -> Result { - Ok(Self { + Ok(Self { inner: [ - GoldilocksField::from_num(cs, inner[0])?, - GoldilocksField::from_num(cs, inner[1])?, - ] + GoldilocksField::from_num(cs, inner[0])?, + GoldilocksField::from_num(cs, inner[1])?, + ], }) } @@ -539,13 +571,10 @@ impl GoldilocksFieldExt { self.inner[0].is_constant() && self.inner[1].is_constant() } - pub fn negate>( - &self, - cs: &mut CS, - ) -> Result { + pub fn negate>(&self, cs: &mut CS) -> Result { let x = self.inner[0].negate(cs)?; let y = self.inner[1].negate(cs)?; - + Ok(GoldilocksFieldExt::from_coords([x, y]).into()) } @@ -562,10 +591,7 @@ impl GoldilocksFieldExt { Ok(Self::from_coords(result)) } - pub fn inverse>( - &self, - cs: &mut CS, - ) -> Result { + pub fn inverse>(&self, cs: &mut CS) -> Result { let mut field_ext = self.into_field_ext(); field_ext = field_ext.map(|x| x.inverse().expect("should be non-zero")); let inversed = Self::alloc_from_field_ext(cs, field_ext)?; @@ -587,14 +613,13 @@ impl GoldilocksFieldExt { ) -> Result { let mut res_witness = [None; 2]; let mut divs = [None; 2]; - if let ( - Some(a_x), Some(a_y), - Some(b_x), Some(b_y), - Some(c_x), Some(c_y), - ) = ( - &self.inner[0].inner.get_value(), &self.inner[1].inner.get_value(), - &other.inner[0].inner.get_value(), &other.inner[1].inner.get_value(), - &third.inner[0].inner.get_value(), &third.inner[1].inner.get_value(), + if let (Some(a_x), Some(a_y), Some(b_x), Some(b_y), Some(c_x), Some(c_y)) = ( + &self.inner[0].inner.get_value(), + &self.inner[1].inner.get_value(), + &other.inner[0].inner.get_value(), + &other.inner[1].inner.get_value(), + &third.inner[0].inner.get_value(), + &third.inner[1].inner.get_value(), ) { let a_x = a_x.into_repr().as_ref()[0] as u128; let a_y = a_y.into_repr().as_ref()[0] as u128; @@ -605,7 +630,8 @@ impl GoldilocksFieldExt { // first coordinate let mut res_x_part = a_y * b_y; - let mut div_x = (res_x_part / GoldilocksField::::ORDER as u128) * Self::NON_RESIDUE as u128; + let mut div_x = + (res_x_part / GoldilocksField::::ORDER as u128) * Self::NON_RESIDUE as u128; res_x_part %= GoldilocksField::::ORDER as u128; let mut res_x = a_x * b_x; @@ -635,11 +661,10 @@ impl GoldilocksFieldExt { } if self.is_constant() && other.is_constant() { - return Ok( - Self::constant( - [res_witness[0].unwrap(), res_witness[1].unwrap()] - ) - ); + return Ok(Self::constant([ + res_witness[0].unwrap(), + res_witness[1].unwrap(), + ])); } let result = [ @@ -647,10 +672,7 @@ impl GoldilocksFieldExt { GoldilocksField::alloc_from_u64(cs, res_witness[1])?, ]; - let divs = [ - Num::alloc(cs, divs[0])?, - Num::alloc(cs, divs[1])? - ]; + let divs = [Num::alloc(cs, divs[0])?, Num::alloc(cs, divs[1])?]; range_check_for_num_bits(cs, &divs[0], 80)?; range_check_for_num_bits(cs, &divs[1], 80)?; @@ -663,7 +685,6 @@ impl GoldilocksFieldExt { let mut minus_order = E::Fr::from_repr(order.into()).unwrap(); minus_order.negate(); - // check first coordinate let v_0 = self.inner[0].inner.mul(cs, &other.inner[0].inner)?; let v_1 = self.inner[1].inner.mul(cs, &other.inner[1].inner)?; @@ -676,7 +697,6 @@ impl GoldilocksFieldExt { lc.add_assign_number_with_coeff(&divs[0], minus_order); lc.enforce_zero(cs)?; - // check second coordinate let v_0 = self.inner[0].inner.mul(cs, &other.inner[1].inner)?; let v_1 = self.inner[1].inner.mul(cs, &other.inner[0].inner)?; @@ -689,7 +709,6 @@ impl GoldilocksFieldExt { lc.add_assign_number_with_coeff(&divs[1], minus_order); lc.enforce_zero(cs)?; - Ok(Self::from_coords(result)) } @@ -728,7 +747,7 @@ impl GoldilocksFieldExt { cs: &mut CS, bit: Boolean, first: &Self, - second: &Self + second: &Self, ) -> Result { let mut result = [GoldilocksField::zero(); 2]; for i in 0..Self::EXTENSION_DEGREE { @@ -736,7 +755,7 @@ impl GoldilocksFieldExt { cs, bit.clone(), &first.inner[i], - &second.inner[i] + &second.inner[i], )?; } @@ -766,30 +785,35 @@ mod test { use super::*; use crate::bellman::plonk::better_better_cs::cs::*; extern crate boojum; - + use crate::bellman::pairing::bn256::{Bn256, Fr}; - use rand::Rng; - use boojum::field::U64Representable; - use boojum::field::SmallField; use boojum::field::Field; + use boojum::field::SmallField; + use boojum::field::U64Representable; + use rand::{Rand, Rng}; #[test] fn test_goldilocks_field() { - let mut assembly = TrivialAssembly::::new(); + let mut assembly = TrivialAssembly::< + Bn256, + PlonkCsWidth4WithNextStepParams, + Width4MainGateWithDNext, + >::new(); let _before = assembly.n(); let mut rng = rand::thread_rng(); let buffer_u64 = [0; 10].map(|_| rng.gen_range(0, GL::CHAR)); let buffer_gl = buffer_u64.map(|x| GL::from_u64_unchecked(x)); - let buffer_circuit = buffer_u64.map(|x| - GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap() - ); - // let buffer_circuit = buffer_u64.map(|x| + let buffer_circuit = + buffer_u64.map(|x| GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap()); + // let buffer_circuit = buffer_u64.map(|x| // GoldilocksField::constant(x) // ); - let circuit_sum = buffer_circuit[0].add(&mut assembly, &buffer_circuit[1]).unwrap(); + let circuit_sum = buffer_circuit[0] + .add(&mut assembly, &buffer_circuit[1]) + .unwrap(); let mut gl_sum = buffer_gl[0]; gl_sum.add_assign(&buffer_gl[1]); @@ -802,7 +826,6 @@ mod test { PolyIdentifier::VariablesPolynomial(2), ]; - let name = BITWISE_LOGICAL_OPS_TABLE_NAME; let bitwise_logic_table = LookupTableApplication::new( name, @@ -813,12 +836,9 @@ mod test { ); assembly.add_table(bitwise_logic_table).unwrap(); - - let circuit_fma = buffer_circuit[2].mul_add( - &mut assembly, - &buffer_circuit[3], - &buffer_circuit[4] - ).unwrap(); + let circuit_fma = buffer_circuit[2] + .mul_add(&mut assembly, &buffer_circuit[3], &buffer_circuit[4]) + .unwrap(); let mut gl_fma = buffer_gl[2]; gl_fma.mul_assign(&buffer_gl[3]); @@ -828,32 +848,51 @@ mod test { let mut repr = Fr::default().into_repr(); repr.as_mut()[..3].copy_from_slice(&buffer_u64[5..8]); - let combined = Num::alloc( - &mut assembly, - Some(Fr::from_repr(repr).unwrap()) - ).unwrap(); + let combined = Num::alloc(&mut assembly, Some(Fr::from_repr(repr).unwrap())).unwrap(); - let parts = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>(&mut assembly, combined).unwrap(); + let parts = + GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>(&mut assembly, combined) + .unwrap(); for (i, part) in parts.into_iter().enumerate() { assert_eq!(Some(buffer_u64[5 + i]), (*part).into_u64()); } + let random_fr = Fr::rand(&mut rng); + let constant_random_fr = Num::Constant(random_fr); + let allocated_random_fr = Num::alloc(&mut assembly, Some(random_fr)).unwrap(); + let expected = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>( + &mut assembly, + constant_random_fr, + ) + .unwrap(); + let actual = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>( + &mut assembly, + allocated_random_fr, + ) + .unwrap(); + for (exp, act) in expected.iter().zip(actual.iter()) { + exp.enforce_equal(&mut assembly, act).unwrap(); + } + assert!(assembly.is_satisfied()); } #[test] fn test_goldilocks_field_extension() { - let mut assembly = TrivialAssembly::::new(); + let mut assembly = TrivialAssembly::< + Bn256, + PlonkCsWidth4WithNextStepParams, + Width4MainGateWithDNext, + >::new(); let _before = assembly.n(); let mut rng = rand::thread_rng(); let buffer_u64 = [0; 10].map(|_| rng.gen_range(0, GL::CHAR)); - let buffer_circuit = buffer_u64.map(|x| - GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap() - ); - // let buffer_circuit = buffer_u64.map(|x| + let buffer_circuit = + buffer_u64.map(|x| GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap()); + // let buffer_circuit = buffer_u64.map(|x| // GoldilocksField::constant(x) // ); @@ -895,11 +934,12 @@ mod test { let fma_expected = GoldilocksFieldExt::::from_coords([x_coord, y_coord]); - fma_actual.enforce_equal(&mut assembly, &fma_expected).unwrap(); + fma_actual + .enforce_equal(&mut assembly, &fma_expected) + .unwrap(); let inversed = fma_actual.inverse(&mut assembly).unwrap(); assert!(assembly.is_satisfied()); } - } diff --git a/src/plonk/circuit/goldilocks/prime_field_like.rs b/src/plonk/circuit/goldilocks/prime_field_like.rs index 48719ee6..d3142a1e 100644 --- a/src/plonk/circuit/goldilocks/prime_field_like.rs +++ b/src/plonk/circuit/goldilocks/prime_field_like.rs @@ -1,7 +1,7 @@ use super::*; -use boojum::field::traits::field_like::PrimeFieldLike; use boojum::field::goldilocks::GoldilocksField as GL; +use boojum::field::traits::field_like::PrimeFieldLike; #[derive(Derivative)] #[derivative(Clone, Copy, Debug(bound = ""), Hash(bound = ""))] @@ -11,7 +11,9 @@ pub struct GoldilocksAsFieldWrapper> { _marker: std::marker::PhantomData CS>, } -impl> From> for GoldilocksAsFieldWrapper { +impl> From> + for GoldilocksAsFieldWrapper +{ fn from(value: GoldilocksField) -> Self { Self { inner: value, @@ -30,7 +32,8 @@ impl> std::fmt::Display for GoldilocksAsField impl> GoldilocksAsFieldWrapper { pub fn conditionally_select(cs: &mut CS, bit: Boolean, first: &Self, second: &Self) -> Self { - let inner = GoldilocksField::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); + let inner = + GoldilocksField::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); inner.into() } } @@ -114,7 +117,6 @@ where } } - #[derive(Derivative)] #[derivative(Clone, Copy, Debug(bound = ""), Hash(bound = ""))] pub struct GoldilocksExtAsFieldWrapper> { @@ -123,7 +125,9 @@ pub struct GoldilocksExtAsFieldWrapper> { _marker: std::marker::PhantomData CS>, } -impl> From> for GoldilocksExtAsFieldWrapper { +impl> From> + for GoldilocksExtAsFieldWrapper +{ fn from(value: GoldilocksFieldExt) -> Self { Self { inner: value, @@ -142,7 +146,8 @@ impl> std::fmt::Display for GoldilocksExtAsFi impl> GoldilocksExtAsFieldWrapper { pub fn conditionally_select(cs: &mut CS, bit: Boolean, first: &Self, second: &Self) -> Self { - let inner = GoldilocksFieldExt::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); + let inner = + GoldilocksFieldExt::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); inner.into() } @@ -156,10 +161,7 @@ impl> GoldilocksExtAsFieldWrapper { } pub fn from_wrapper_coeffs_in_base(coeffs: [GoldilocksAsFieldWrapper; 2]) -> Self { - let coeffs = [ - coeffs[0].inner, - coeffs[1].inner, - ]; + let coeffs = [coeffs[0].inner, coeffs[1].inner]; Self::from_coeffs_in_base(coeffs) } @@ -169,8 +171,7 @@ impl> GoldilocksExtAsFieldWrapper { other: &Self, cs: &mut CS, ) -> Result<(), SynthesisError> { - for (dst, src) in dst.inner.inner.iter_mut() - .zip(other.inner.inner.iter()) { + for (dst, src) in dst.inner.inner.iter_mut().zip(other.inner.inner.iter()) { *dst = src.mul_add(cs, &base.inner, dst)?; } @@ -178,9 +179,9 @@ impl> GoldilocksExtAsFieldWrapper { } pub fn mul_assign_by_base( - &mut self, - cs: &mut CS, - base: &GoldilocksAsFieldWrapper + &mut self, + cs: &mut CS, + base: &GoldilocksAsFieldWrapper, ) -> Result<(), SynthesisError> { for dst in self.inner.inner.iter_mut() { *dst = dst.mul(cs, &base.inner)?; @@ -262,7 +263,7 @@ where fn inverse(&self, ctx: &mut Self::Context) -> Self { self.inner.inverse(ctx).unwrap().into() } - + // constant creation fn constant(value: Self::Base, _ctx: &mut Self::Context) -> Self { GoldilocksFieldExt::constant_from_field(value).into() From ac78e3ec6d662a88ecf6ea97225ab72dcf3bc359 Mon Sep 17 00:00:00 2001 From: olesHolem Date: Mon, 6 Nov 2023 00:44:16 +0200 Subject: [PATCH 11/15] cargo fmt rollbacked --- src/plonk/circuit/goldilocks/mod.rs | 269 +++++++++--------- .../circuit/goldilocks/prime_field_like.rs | 35 ++- 2 files changed, 159 insertions(+), 145 deletions(-) diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs index 88251476..52b6729d 100644 --- a/src/plonk/circuit/goldilocks/mod.rs +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -1,42 +1,68 @@ -use itertools::all; use num_traits::ops::overflowing; -use crate::bellman::pairing::ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; -use crate::bellman::pairing::Engine; -use crate::bellman::SynthesisError; +use crate::bellman::pairing::{ + Engine, +}; + +use crate::bellman::pairing::ff::{ + Field, + PrimeField, + PrimeFieldRepr, + BitIterator +}; + +use crate::bellman::{ + SynthesisError, +}; use plonk::circuit::boolean::Boolean; use crate::bellman::plonk::better_better_cs::cs::{ - ArithmeticTerm, Coefficient, ConstraintSystem, Gate, GateInternal, LinearCombinationOfTerms, - MainGate, MainGateTerm, PlonkConstraintSystemParams, PlonkCsWidth4WithNextStepParams, - PolynomialInConstraint, PolynomialMultiplicativeTerm, TimeDilation, TrivialAssembly, Variable, + Variable, + ConstraintSystem, + ArithmeticTerm, + MainGateTerm, Width4MainGateWithDNext, + MainGate, + GateInternal, + Gate, + LinearCombinationOfTerms, + PolynomialMultiplicativeTerm, + PolynomialInConstraint, + TimeDilation, + Coefficient, + PlonkConstraintSystemParams, + PlonkCsWidth4WithNextStepParams, + TrivialAssembly }; -use super::bigint::*; -use super::*; use crate::plonk::circuit::Assignment; +use super::*; +use super::bigint::*; use crate::plonk::circuit::allocated_num::{AllocatedNum, Num}; +use crate::plonk::circuit::simple_term::{Term}; use crate::plonk::circuit::linear_combination::LinearCombination; -use crate::plonk::circuit::simple_term::Term; use plonk::circuit::bigint_new::{ - enforce_range_check_using_bitop_table, enforce_range_check_using_naive_approach, - BITWISE_LOGICAL_OPS_TABLE_NAME, + enforce_range_check_using_naive_approach, + enforce_range_check_using_bitop_table, + BITWISE_LOGICAL_OPS_TABLE_NAME }; use boojum::field::{ - goldilocks::GoldilocksExt2, goldilocks::GoldilocksField as GL, ExtensionField, - PrimeField as PF, U64Representable, + goldilocks::GoldilocksField as GL, + U64Representable, + PrimeField as PF, + goldilocks::GoldilocksExt2, + ExtensionField }; -use derivative::*; -use std::hash::{Hash, Hasher}; use std::result; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use derivative::*; +use std::hash::{Hash, Hasher}; pub mod prime_field_like; @@ -56,7 +82,7 @@ impl Hash for GoldilocksField { fn range_check_for_num_bits>( cs: &mut CS, num: &Num, - num_bits: usize, + num_bits: usize ) -> Result<(), SynthesisError> { assert!(num_bits % 16 == 0); @@ -119,14 +145,14 @@ impl GoldilocksField { pub fn into_field(self) -> Option { self.into_u64().map(|value| GL::from_u64_unchecked(value)) } - + pub fn is_constant(&self) -> bool { self.inner.is_constant() } pub fn alloc>( cs: &mut CS, - witness: Option, + witness: Option ) -> Result { let num = Num::alloc(cs, witness)?; Self::from_num(cs, num) @@ -134,7 +160,7 @@ impl GoldilocksField { pub fn alloc_from_u64>( cs: &mut CS, - witness: Option, + witness: Option ) -> Result { let witness = witness.map(|value| E::Fr::from_repr(value.into()).unwrap()); Self::alloc(cs, witness) @@ -142,27 +168,28 @@ impl GoldilocksField { pub fn alloc_from_field>( cs: &mut CS, - witness: Option, + witness: Option ) -> Result { let witness = witness.map(|value| value.as_u64_reduced()); Self::alloc_from_u64(cs, witness) } pub unsafe fn from_num_unchecked(num: Num) -> Result { - Ok(Self { inner: num }) + Ok(Self { + inner: num, + }) } - pub fn from_num>( - cs: &mut CS, - num: Num, - ) -> Result { + pub fn from_num>(cs: &mut CS, num: Num) -> Result { let remainder = Num::Constant(E::Fr::from_repr(Self::REMAINDER.into()).unwrap()); range_check_for_num_bits(cs, &num, 64)?; let check = num.add(cs, &remainder)?; range_check_for_num_bits(cs, &check, 64)?; - Ok(Self { inner: num }) + Ok(Self { + inner: num, + }) } pub fn into_num(&self) -> Num { @@ -289,7 +316,10 @@ impl GoldilocksField { Ok(result) } - pub fn negate>(&self, cs: &mut CS) -> Result { + pub fn negate>( + &self, + cs: &mut CS, + ) -> Result { let order = Num::Constant(E::Fr::from_repr(Self::ORDER.into()).unwrap()); let negate = order.sub(cs, &self.inner)?; Ok(Self { inner: negate }) @@ -313,8 +343,7 @@ impl GoldilocksField { order.negate(); let minus_order = Num::Constant(order); - let overflow = if let (Some(a), Some(b)) = (self.inner.get_value(), other.inner.get_value()) - { + let overflow = if let (Some(a), Some(b)) = (self.inner.get_value(), other.inner.get_value()) { let a = a.into_repr().as_ref()[0] as u128; let b = b.into_repr().as_ref()[0] as u128; @@ -334,8 +363,11 @@ impl GoldilocksField { Self::from_num(cs, result) } - - pub fn inverse>(&self, cs: &mut CS) -> Result { + + pub fn inverse>( + &self, + cs: &mut CS, + ) -> Result { let mut inverse_witness = self.into_field(); inverse_witness = inverse_witness.map(|el| el.inverse().expect("should be invertible")); let inverse = Self::alloc_from_field(cs, inverse_witness)?; @@ -363,9 +395,7 @@ impl GoldilocksField { other: &Self, third: &Self, ) -> Result { - if let (Num::Constant(a), Num::Constant(b), Num::Constant(c)) = - (&self.inner, &other.inner, &third.inner) - { + if let (Num::Constant(a), Num::Constant(b), Num::Constant(c)) = (&self.inner, &other.inner, &third.inner) { let a = a.into_repr().as_ref()[0] as u128; let b = b.into_repr().as_ref()[0] as u128; let c = c.into_repr().as_ref()[0] as u128; @@ -379,11 +409,7 @@ impl GoldilocksField { order.negate(); let minus_order = Num::Constant(order); - let overflow = if let (Some(a), Some(b), Some(c)) = ( - self.inner.get_value(), - other.inner.get_value(), - third.inner.get_value(), - ) { + let overflow = if let (Some(a), Some(b), Some(c)) = (self.inner.get_value(), other.inner.get_value(), third.inner.get_value()) { let a = a.into_repr().as_ref()[0] as u128; let b = b.into_repr().as_ref()[0] as u128; let c = c.into_repr().as_ref()[0] as u128; @@ -410,7 +436,7 @@ impl GoldilocksField { this: &Self, other: &Self, ) -> Result { - Num::equals(cs, &this.inner, &other.inner) + Num::equals(cs, &this.inner,&other.inner) } pub fn enforce_equal>( @@ -425,11 +451,11 @@ impl GoldilocksField { cs: &mut CS, bit: Boolean, first: &Self, - second: &Self, + second: &Self ) -> Result { let result = Num::conditionally_select(cs, &bit, &first.inner, &second.inner)?; - Ok(Self { inner: result }) + Ok(Self { inner: result} ) } pub fn spread_into_bits, const LIMIT: usize>( @@ -476,7 +502,7 @@ impl GoldilocksField { } } -pub type GLExt = ExtensionField; +pub type GLExt = ExtensionField::; /// Extension with poly x^2 - 7 #[derive(Derivative)] @@ -518,7 +544,7 @@ impl GoldilocksFieldExt { pub fn alloc_from_field_ext>( cs: &mut CS, - witness: Option, + witness: Option ) -> Result { let (x_witness, y_witness); if let Some(witness) = witness { @@ -533,26 +559,23 @@ impl GoldilocksFieldExt { inner: [ GoldilocksField::alloc_from_field(cs, x_witness)?, GoldilocksField::alloc_from_field(cs, y_witness)?, - ], + ] }) } pub fn constant_from_field(value: GL) -> Self { - Self::from_coords([ - GoldilocksField::constant_from_field(value), - GoldilocksField::zero(), - ]) + Self::from_coords([GoldilocksField::constant_from_field(value), GoldilocksField::zero()]) } pub fn from_num_coords>( - cs: &mut CS, - inner: [Num; 2], + cs: &mut CS, + inner: [Num; 2] ) -> Result { - Ok(Self { + Ok(Self { inner: [ - GoldilocksField::from_num(cs, inner[0])?, - GoldilocksField::from_num(cs, inner[1])?, - ], + GoldilocksField::from_num(cs, inner[0])?, + GoldilocksField::from_num(cs, inner[1])?, + ] }) } @@ -571,10 +594,13 @@ impl GoldilocksFieldExt { self.inner[0].is_constant() && self.inner[1].is_constant() } - pub fn negate>(&self, cs: &mut CS) -> Result { + pub fn negate>( + &self, + cs: &mut CS, + ) -> Result { let x = self.inner[0].negate(cs)?; let y = self.inner[1].negate(cs)?; - + Ok(GoldilocksFieldExt::from_coords([x, y]).into()) } @@ -591,7 +617,10 @@ impl GoldilocksFieldExt { Ok(Self::from_coords(result)) } - pub fn inverse>(&self, cs: &mut CS) -> Result { + pub fn inverse>( + &self, + cs: &mut CS, + ) -> Result { let mut field_ext = self.into_field_ext(); field_ext = field_ext.map(|x| x.inverse().expect("should be non-zero")); let inversed = Self::alloc_from_field_ext(cs, field_ext)?; @@ -613,13 +642,14 @@ impl GoldilocksFieldExt { ) -> Result { let mut res_witness = [None; 2]; let mut divs = [None; 2]; - if let (Some(a_x), Some(a_y), Some(b_x), Some(b_y), Some(c_x), Some(c_y)) = ( - &self.inner[0].inner.get_value(), - &self.inner[1].inner.get_value(), - &other.inner[0].inner.get_value(), - &other.inner[1].inner.get_value(), - &third.inner[0].inner.get_value(), - &third.inner[1].inner.get_value(), + if let ( + Some(a_x), Some(a_y), + Some(b_x), Some(b_y), + Some(c_x), Some(c_y), + ) = ( + &self.inner[0].inner.get_value(), &self.inner[1].inner.get_value(), + &other.inner[0].inner.get_value(), &other.inner[1].inner.get_value(), + &third.inner[0].inner.get_value(), &third.inner[1].inner.get_value(), ) { let a_x = a_x.into_repr().as_ref()[0] as u128; let a_y = a_y.into_repr().as_ref()[0] as u128; @@ -630,8 +660,7 @@ impl GoldilocksFieldExt { // first coordinate let mut res_x_part = a_y * b_y; - let mut div_x = - (res_x_part / GoldilocksField::::ORDER as u128) * Self::NON_RESIDUE as u128; + let mut div_x = (res_x_part / GoldilocksField::::ORDER as u128) * Self::NON_RESIDUE as u128; res_x_part %= GoldilocksField::::ORDER as u128; let mut res_x = a_x * b_x; @@ -661,10 +690,11 @@ impl GoldilocksFieldExt { } if self.is_constant() && other.is_constant() { - return Ok(Self::constant([ - res_witness[0].unwrap(), - res_witness[1].unwrap(), - ])); + return Ok( + Self::constant( + [res_witness[0].unwrap(), res_witness[1].unwrap()] + ) + ); } let result = [ @@ -672,7 +702,10 @@ impl GoldilocksFieldExt { GoldilocksField::alloc_from_u64(cs, res_witness[1])?, ]; - let divs = [Num::alloc(cs, divs[0])?, Num::alloc(cs, divs[1])?]; + let divs = [ + Num::alloc(cs, divs[0])?, + Num::alloc(cs, divs[1])? + ]; range_check_for_num_bits(cs, &divs[0], 80)?; range_check_for_num_bits(cs, &divs[1], 80)?; @@ -685,6 +718,7 @@ impl GoldilocksFieldExt { let mut minus_order = E::Fr::from_repr(order.into()).unwrap(); minus_order.negate(); + // check first coordinate let v_0 = self.inner[0].inner.mul(cs, &other.inner[0].inner)?; let v_1 = self.inner[1].inner.mul(cs, &other.inner[1].inner)?; @@ -697,6 +731,7 @@ impl GoldilocksFieldExt { lc.add_assign_number_with_coeff(&divs[0], minus_order); lc.enforce_zero(cs)?; + // check second coordinate let v_0 = self.inner[0].inner.mul(cs, &other.inner[1].inner)?; let v_1 = self.inner[1].inner.mul(cs, &other.inner[0].inner)?; @@ -709,6 +744,7 @@ impl GoldilocksFieldExt { lc.add_assign_number_with_coeff(&divs[1], minus_order); lc.enforce_zero(cs)?; + Ok(Self::from_coords(result)) } @@ -747,7 +783,7 @@ impl GoldilocksFieldExt { cs: &mut CS, bit: Boolean, first: &Self, - second: &Self, + second: &Self ) -> Result { let mut result = [GoldilocksField::zero(); 2]; for i in 0..Self::EXTENSION_DEGREE { @@ -755,7 +791,7 @@ impl GoldilocksFieldExt { cs, bit.clone(), &first.inner[i], - &second.inner[i], + &second.inner[i] )?; } @@ -785,35 +821,30 @@ mod test { use super::*; use crate::bellman::plonk::better_better_cs::cs::*; extern crate boojum; - + use crate::bellman::pairing::bn256::{Bn256, Fr}; - use boojum::field::Field; - use boojum::field::SmallField; + use rand::Rng; use boojum::field::U64Representable; - use rand::{Rand, Rng}; + use boojum::field::SmallField; + use boojum::field::Field; #[test] fn test_goldilocks_field() { - let mut assembly = TrivialAssembly::< - Bn256, - PlonkCsWidth4WithNextStepParams, - Width4MainGateWithDNext, - >::new(); + let mut assembly = TrivialAssembly::::new(); let _before = assembly.n(); let mut rng = rand::thread_rng(); let buffer_u64 = [0; 10].map(|_| rng.gen_range(0, GL::CHAR)); let buffer_gl = buffer_u64.map(|x| GL::from_u64_unchecked(x)); - let buffer_circuit = - buffer_u64.map(|x| GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap()); - // let buffer_circuit = buffer_u64.map(|x| + let buffer_circuit = buffer_u64.map(|x| + GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap() + ); + // let buffer_circuit = buffer_u64.map(|x| // GoldilocksField::constant(x) // ); - let circuit_sum = buffer_circuit[0] - .add(&mut assembly, &buffer_circuit[1]) - .unwrap(); + let circuit_sum = buffer_circuit[0].add(&mut assembly, &buffer_circuit[1]).unwrap(); let mut gl_sum = buffer_gl[0]; gl_sum.add_assign(&buffer_gl[1]); @@ -826,6 +857,7 @@ mod test { PolyIdentifier::VariablesPolynomial(2), ]; + let name = BITWISE_LOGICAL_OPS_TABLE_NAME; let bitwise_logic_table = LookupTableApplication::new( name, @@ -836,9 +868,12 @@ mod test { ); assembly.add_table(bitwise_logic_table).unwrap(); - let circuit_fma = buffer_circuit[2] - .mul_add(&mut assembly, &buffer_circuit[3], &buffer_circuit[4]) - .unwrap(); + + let circuit_fma = buffer_circuit[2].mul_add( + &mut assembly, + &buffer_circuit[3], + &buffer_circuit[4] + ).unwrap(); let mut gl_fma = buffer_gl[2]; gl_fma.mul_assign(&buffer_gl[3]); @@ -848,51 +883,32 @@ mod test { let mut repr = Fr::default().into_repr(); repr.as_mut()[..3].copy_from_slice(&buffer_u64[5..8]); - let combined = Num::alloc(&mut assembly, Some(Fr::from_repr(repr).unwrap())).unwrap(); + let combined = Num::alloc( + &mut assembly, + Some(Fr::from_repr(repr).unwrap()) + ).unwrap(); - let parts = - GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>(&mut assembly, combined) - .unwrap(); + let parts = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>(&mut assembly, combined).unwrap(); for (i, part) in parts.into_iter().enumerate() { assert_eq!(Some(buffer_u64[5 + i]), (*part).into_u64()); } - let random_fr = Fr::rand(&mut rng); - let constant_random_fr = Num::Constant(random_fr); - let allocated_random_fr = Num::alloc(&mut assembly, Some(random_fr)).unwrap(); - let expected = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>( - &mut assembly, - constant_random_fr, - ) - .unwrap(); - let actual = GoldilocksField::from_num_to_multiple_with_reduction::<_, 3>( - &mut assembly, - allocated_random_fr, - ) - .unwrap(); - for (exp, act) in expected.iter().zip(actual.iter()) { - exp.enforce_equal(&mut assembly, act).unwrap(); - } - assert!(assembly.is_satisfied()); } #[test] fn test_goldilocks_field_extension() { - let mut assembly = TrivialAssembly::< - Bn256, - PlonkCsWidth4WithNextStepParams, - Width4MainGateWithDNext, - >::new(); + let mut assembly = TrivialAssembly::::new(); let _before = assembly.n(); let mut rng = rand::thread_rng(); let buffer_u64 = [0; 10].map(|_| rng.gen_range(0, GL::CHAR)); - let buffer_circuit = - buffer_u64.map(|x| GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap()); - // let buffer_circuit = buffer_u64.map(|x| + let buffer_circuit = buffer_u64.map(|x| + GoldilocksField::alloc_from_u64(&mut assembly, Some(x)).unwrap() + ); + // let buffer_circuit = buffer_u64.map(|x| // GoldilocksField::constant(x) // ); @@ -934,12 +950,11 @@ mod test { let fma_expected = GoldilocksFieldExt::::from_coords([x_coord, y_coord]); - fma_actual - .enforce_equal(&mut assembly, &fma_expected) - .unwrap(); + fma_actual.enforce_equal(&mut assembly, &fma_expected).unwrap(); let inversed = fma_actual.inverse(&mut assembly).unwrap(); assert!(assembly.is_satisfied()); } -} + +} \ No newline at end of file diff --git a/src/plonk/circuit/goldilocks/prime_field_like.rs b/src/plonk/circuit/goldilocks/prime_field_like.rs index d3142a1e..a7d26f22 100644 --- a/src/plonk/circuit/goldilocks/prime_field_like.rs +++ b/src/plonk/circuit/goldilocks/prime_field_like.rs @@ -1,7 +1,7 @@ use super::*; -use boojum::field::goldilocks::GoldilocksField as GL; use boojum::field::traits::field_like::PrimeFieldLike; +use boojum::field::goldilocks::GoldilocksField as GL; #[derive(Derivative)] #[derivative(Clone, Copy, Debug(bound = ""), Hash(bound = ""))] @@ -11,9 +11,7 @@ pub struct GoldilocksAsFieldWrapper> { _marker: std::marker::PhantomData CS>, } -impl> From> - for GoldilocksAsFieldWrapper -{ +impl> From> for GoldilocksAsFieldWrapper { fn from(value: GoldilocksField) -> Self { Self { inner: value, @@ -32,8 +30,7 @@ impl> std::fmt::Display for GoldilocksAsField impl> GoldilocksAsFieldWrapper { pub fn conditionally_select(cs: &mut CS, bit: Boolean, first: &Self, second: &Self) -> Self { - let inner = - GoldilocksField::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); + let inner = GoldilocksField::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); inner.into() } } @@ -117,6 +114,7 @@ where } } + #[derive(Derivative)] #[derivative(Clone, Copy, Debug(bound = ""), Hash(bound = ""))] pub struct GoldilocksExtAsFieldWrapper> { @@ -125,9 +123,7 @@ pub struct GoldilocksExtAsFieldWrapper> { _marker: std::marker::PhantomData CS>, } -impl> From> - for GoldilocksExtAsFieldWrapper -{ +impl> From> for GoldilocksExtAsFieldWrapper { fn from(value: GoldilocksFieldExt) -> Self { Self { inner: value, @@ -146,8 +142,7 @@ impl> std::fmt::Display for GoldilocksExtAsFi impl> GoldilocksExtAsFieldWrapper { pub fn conditionally_select(cs: &mut CS, bit: Boolean, first: &Self, second: &Self) -> Self { - let inner = - GoldilocksFieldExt::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); + let inner = GoldilocksFieldExt::conditionally_select(cs, bit, &first.inner, &second.inner).unwrap(); inner.into() } @@ -161,7 +156,10 @@ impl> GoldilocksExtAsFieldWrapper { } pub fn from_wrapper_coeffs_in_base(coeffs: [GoldilocksAsFieldWrapper; 2]) -> Self { - let coeffs = [coeffs[0].inner, coeffs[1].inner]; + let coeffs = [ + coeffs[0].inner, + coeffs[1].inner, + ]; Self::from_coeffs_in_base(coeffs) } @@ -171,7 +169,8 @@ impl> GoldilocksExtAsFieldWrapper { other: &Self, cs: &mut CS, ) -> Result<(), SynthesisError> { - for (dst, src) in dst.inner.inner.iter_mut().zip(other.inner.inner.iter()) { + for (dst, src) in dst.inner.inner.iter_mut() + .zip(other.inner.inner.iter()) { *dst = src.mul_add(cs, &base.inner, dst)?; } @@ -179,9 +178,9 @@ impl> GoldilocksExtAsFieldWrapper { } pub fn mul_assign_by_base( - &mut self, - cs: &mut CS, - base: &GoldilocksAsFieldWrapper, + &mut self, + cs: &mut CS, + base: &GoldilocksAsFieldWrapper ) -> Result<(), SynthesisError> { for dst in self.inner.inner.iter_mut() { *dst = dst.mul(cs, &base.inner)?; @@ -263,9 +262,9 @@ where fn inverse(&self, ctx: &mut Self::Context) -> Self { self.inner.inverse(ctx).unwrap().into() } - + // constant creation fn constant(value: Self::Base, _ctx: &mut Self::Context) -> Self { GoldilocksFieldExt::constant_from_field(value).into() } -} +} \ No newline at end of file From 59ea44a11e48af7086c372266f30da83c8cf74c4 Mon Sep 17 00:00:00 2001 From: olesHolem Date: Mon, 6 Nov 2023 01:20:44 +0200 Subject: [PATCH 12/15] function docs added --- src/plonk/circuit/goldilocks/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs index 52b6729d..346b0826 100644 --- a/src/plonk/circuit/goldilocks/mod.rs +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -196,6 +196,10 @@ impl GoldilocksField { self.inner } + /// This function is used in SNARK-wrapper to get Goldilocks challenges + /// from random Bn256 scalar field element. Usually, we use N = 3. + /// Note: we lose some information during this conversion, but it's OK. + /// We only care about good output distribution. pub fn from_num_to_multiple_with_reduction, const N: usize>( cs: &mut CS, num: Num, From 93865835bd81a607f76416c7fdd1d7e47c78c2d5 Mon Sep 17 00:00:00 2001 From: olesHolem Date: Sat, 18 Nov 2023 15:16:22 +0300 Subject: [PATCH 13/15] small fix --- src/plonk/circuit/goldilocks/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plonk/circuit/goldilocks/mod.rs b/src/plonk/circuit/goldilocks/mod.rs index 381b82b0..ab25b70a 100644 --- a/src/plonk/circuit/goldilocks/mod.rs +++ b/src/plonk/circuit/goldilocks/mod.rs @@ -634,7 +634,7 @@ impl GoldilocksFieldExt { divs[1] = Some(E::Fr::from_str(&div_y.to_string()).unwrap()); } - if self.is_constant() && other.is_constant() { + if self.is_constant() && other.is_constant() && third.is_constant() { return Ok( Self::constant( [res_witness[0].unwrap(), res_witness[1].unwrap()] From 9ce92dbe8505ce945e17a3a150280f47ff45eeeb Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Fri, 5 Jul 2024 16:02:27 +0400 Subject: [PATCH 14/15] Prepare 0.2.0 release --- Cargo.toml | 6 +++--- rust-toolchain | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 rust-toolchain diff --git a/Cargo.toml b/Cargo.toml index fbed5980..9b1d3450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ description = "Cryptographic library for SNARK gadgets" homepage = "https://github.com/matter-labs/franklin-crypto" license = "MIT/Apache-2.0" name = "franklin-crypto" -version = "0.0.5" +version = "0.2.0" [lib] crate-type = ["lib", "staticlib"] @@ -19,7 +19,7 @@ allocator = ["bellman/allocator"] derivative = "2" # boojum = {package = "boojum", path = "../boojum" } -boojum = {git = "https://github.com/matter-labs/era-boojum.git", branch = "main"} +boojum = "=0.2.0" rand = "0.4" digest = "0.9" @@ -41,7 +41,7 @@ indexmap = "1.9" smallvec = "1.10" # bellman = { package = "bellman_ce", path = "../bellman" } -bellman = { package = "bellman_ce", git = "https://github.com/matter-labs/bellman", branch = "snark-wrapper" } +bellman = { package = "bellman_ce", version = "=0.8.0" } blake2-rfc_bellman_edition = "0.0.1" #poseidon_hash = { path = "../poseidon_hash" } #poseidon_hash = {git = "https://github.com/shamatar/poseidon_hash.git"} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 00000000..5aaef38c --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly-2024-05-07 From 2e8ba2c6ebb36c333e1933a2fce3c828bf656578 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 1 Aug 2024 18:02:45 +0400 Subject: [PATCH 15/15] Revert undesired CI changes --- .github/workflows/ci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b9c1aa9..bb0ffde5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,9 @@ name: Build and compile on: push: - branches: - - dev - - snark_wrapper + branches: ["dev"] pull_request: - branches: - - dev - - snark_wrapper + branches: ["dev"] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}