diff --git a/Cargo.toml b/Cargo.toml index 9ca5024..1e2f63f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["plonk", "kzg", "fri"] +members = ["plonk", "kzg", "fri", "nova"] resolver = "2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html \ No newline at end of file diff --git a/kzg/src/scheme.rs b/kzg/src/scheme.rs index 252cfa0..31b3503 100644 --- a/kzg/src/scheme.rs +++ b/kzg/src/scheme.rs @@ -6,13 +6,14 @@ use ark_ec::pairing::Pairing; use ark_ec::short_weierstrass::Affine; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{One, Zero}; +use ark_poly::univariate::DensePolynomial; use ark_poly::{DenseUVPolynomial, Polynomial}; use rand::{Rng, RngCore}; use crate::commitment::KzgCommitment; use crate::opening::KzgOpening; use crate::srs::Srs; -use crate::types::{G1Point, Poly}; +use crate::types::{G1Point, Poly, ScalarField}; /// Implements the KZG polynomial commitment scheme. /// @@ -50,6 +51,21 @@ impl KzgScheme { KzgCommitment(commitment) } + /// Commits to a coefficient vector using the KZG scheme. + /// + /// # Parameters + /// + /// - `coeffs`: The coefficient vector to be committed to. + /// + /// # Returns + /// + /// The commitment to the polynomial. + pub fn commit_vector(&self, coeffs: &[ScalarField]) -> KzgCommitment { + let new_poly = DensePolynomial::from_coefficients_vec(coeffs.clone().into()); + let commitment = self.evaluate_in_s(&new_poly); + KzgCommitment(commitment) + } + /// Commits to a parameter using the KZG scheme. /// /// # Parameters @@ -103,6 +119,28 @@ impl KzgScheme { KzgOpening(opening, evaluation_at_z) } + /// Opens a commitment at a specified point. + /// + /// # Parameters + /// + /// - `coeffs`: The coefficient vector to be opened. + /// - `z`: The point at which the polynomial is opened. + /// + /// # Returns + /// + /// The opening at the specified point. + pub fn open_vector(&self, coeffs: &[ScalarField], z: impl Into) -> KzgOpening { + let z = z.into(); + let mut polynomial = DensePolynomial::from_coefficients_vec(coeffs.clone().into()); + let evaluation_at_z = polynomial.evaluate(&z); + let first = polynomial.coeffs.first_mut().expect("at least 1"); + *first -= evaluation_at_z; + let root = Poly::from_coefficients_slice(&[-z, 1.into()]); + let quotient_poly = &polynomial / &root; + let opening = self.evaluate_in_s("ient_poly); + KzgOpening(opening, evaluation_at_z) + } + /// Verifies the correctness of an opening. /// /// # Parameters diff --git a/nova/examples/examples.rs b/nova/examples/examples.rs index 9df304b..6f3f2b1 100644 --- a/nova/examples/examples.rs +++ b/nova/examples/examples.rs @@ -1,23 +1,23 @@ use ark_ff::{BigInteger, One, PrimeField, Zero}; -use sha2::Sha256; use kzg::scheme::KzgScheme; use kzg::srs::Srs; use kzg::types::{BaseField, ScalarField}; use nova::circuit::{AugmentedCircuit, FCircuit, State}; -use nova::ivc::{IVC, IVCProof, ZkIVCProof}; +use nova::ivc::{IVCProof, ZkIVCProof, IVC}; use nova::r1cs::{create_trivial_pair, FInstance, FWitness, R1CS}; use nova::transcript::Transcript; use nova::utils::{to_f_matrix, to_f_vec}; +use sha2::Sha256; struct TestCircuit {} impl FCircuit for TestCircuit { fn run(&self, z_i: &State, w_i: &FWitness) -> State { - let x = w_i.w[0].clone(); + let x = w_i.w[0]; let res = x * x * x + x + ScalarField::from(5); let base_res = BaseField::from_le_bytes_mod_order(&res.into_bigint().to_bytes_le()); State { - state: z_i.state + base_res + state: z_i.state + base_res, } } } @@ -27,7 +27,11 @@ fn main() { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3, 4, 1, 2]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -36,38 +40,48 @@ fn main() { let x_len = x[0].len(); // Generate witnesses and instances - let w: Vec = witnesses.iter().map(|witness| FWitness::new(witness, matrix_a.len())).collect(); - let mut u: Vec = w.iter().zip(x).map(|(w, x)| w.commit(&scheme, &x)).collect(); + let w: Vec = witnesses + .iter() + .map(|witness| FWitness::new(witness, matrix_a.len())) + .collect(); + let mut u: Vec = w + .iter() + .zip(x) + .map(|(w, x)| w.commit(&scheme, &x)) + .collect(); // step i let mut i = BaseField::zero(); // generate trivial instance-witness pair - let (trivial_witness, trivial_instance) = create_trivial_pair(x_len, witnesses[0].len(), &scheme); + let (trivial_witness, trivial_instance) = + create_trivial_pair(x_len, witnesses[0].len(), &scheme); // generate f_circuit instance - let f_circuit = TestCircuit{}; + let f_circuit = TestCircuit {}; // generate states - let mut z = vec![State{state: BaseField::from(0)}; 5]; + let mut z = vec![ + State { + state: BaseField::from(0) + }; + 5 + ]; for index in 1..5 { - z[index] = f_circuit.run(&z[index - 1], &w[index-1]); + z[index] = f_circuit.run(&z[index - 1], &w[index - 1]); } let mut prover_transcript; let mut verifier_transcript = Transcript::::default(); // create F' - let augmented_circuit = AugmentedCircuit::::new( - f_circuit, - &trivial_instance, - &z[0] - ); + let augmented_circuit = + AugmentedCircuit::::new(f_circuit, &trivial_instance, &z[0]); // generate IVC let mut ivc = IVC:: { scheme, - augmented_circuit + augmented_circuit, }; // initialize IVC proof, zkIVCProof, folded witness and folded instance @@ -80,18 +94,13 @@ fn main() { for step in 0..4 { println!("Step: {:?}", step); if step == 0 { - res = ivc.augmented_circuit.run( - &u[step], - None, - &w[step], - None, - ); + res = ivc.augmented_circuit.run(&u[step], None, &w[step], None); } else { res = ivc.augmented_circuit.run( &ivc_proof.u_i, Some(&ivc_proof.big_u_i.clone()), &ivc_proof.w_i, - Some(&zk_ivc_proof.com_t.clone().unwrap()) + Some(&zk_ivc_proof.com_t.clone().unwrap()), ); } @@ -109,20 +118,34 @@ fn main() { // update for next step - if step != 3 { // do not update if we have done with IVC + if step != 3 { + // do not update if we have done with IVC ivc.augmented_circuit.next_step(); - i = i + BaseField::one(); + i += BaseField::one(); assert_eq!(ivc.augmented_circuit.z_i.state, z[step + 1].state); prover_transcript = Transcript::::default(); verifier_transcript = Transcript::::default(); - let hash_x = AugmentedCircuit::::hash_io(i, &z[0], &z[step + 1], &folded_instance); + let hash_x = AugmentedCircuit::::hash_io( + i, + &z[0], + &z[step + 1], + &folded_instance, + ); // convert u_1_x from BaseField into ScalarField - u[step + 1].x = vec![ScalarField::from_le_bytes_mod_order(&hash_x.into_bigint().to_bytes_le())]; + u[step + 1].x = vec![ScalarField::from_le_bytes_mod_order( + &hash_x.into_bigint().to_bytes_le(), + )]; // generate ivc_proof and zkSNARK proof. - ivc_proof = IVCProof::new(&u[step + 1], &w[step + 1], &folded_instance, &folded_witness); - (folded_witness, folded_instance, zk_ivc_proof) = ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript); + ivc_proof = IVCProof::new( + &u[step + 1], + &w[step + 1], + &folded_instance, + &folded_witness, + ); + (folded_witness, folded_instance, zk_ivc_proof) = + ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript); } } } @@ -131,19 +154,19 @@ fn gen_test_values(inputs: Vec) -> (R1CS, Vec>, // R1CS for: x^3 + x + 5 = y (example from article // https://vitalik.eth.limo/general/2016/12/10/qap.html ) - let a = to_f_matrix::(vec![ + let a = to_f_matrix::(&vec![ vec![1, 0, 0, 0, 0, 0], vec![0, 1, 0, 0, 0, 0], vec![1, 0, 1, 0, 0, 0], vec![0, 0, 0, 1, 0, 5], ]); - let b = to_f_matrix::(vec![ + let b = to_f_matrix::(&vec![ vec![1, 0, 0, 0, 0, 0], vec![1, 0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0, 1], vec![0, 0, 0, 0, 0, 1], ]); - let c = to_f_matrix::(vec![ + let c = to_f_matrix::(&vec![ vec![0, 1, 0, 0, 0, 0], vec![0, 0, 1, 0, 0, 0], vec![0, 0, 0, 1, 0, 0], @@ -156,15 +179,21 @@ fn gen_test_values(inputs: Vec) -> (R1CS, Vec>, for input in inputs { let w_i = to_f_vec::(vec![ input, - input * input, // x^2 - input * input * input, // x^2 * x - input * input * input + input, // x^3 + x + input * input, // x^2 + input * input * input, // x^2 * x + input * input * input + input, // x^3 + x ]); w.push(w_i.clone()); - let x_i = to_f_vec::(vec![input * input * input + input + 5]); // output: x^3 + x + 5 + let x_i = to_f_vec::(vec![input * input * input + input + 5]); // output: x^3 + x + 5 x.push(x_i.clone()); } - let r1cs = R1CS:: { matrix_a: a, matrix_b: b, matrix_c: c, num_io: 1, num_vars: 4 }; + let r1cs = R1CS:: { + matrix_a: a, + matrix_b: b, + matrix_c: c, + num_io: 1, + num_vars: 4, + }; (r1cs, w, x) -} \ No newline at end of file +} diff --git a/nova/src/circuit.rs b/nova/src/circuit.rs index 7028b2f..a557352 100644 --- a/nova/src/circuit.rs +++ b/nova/src/circuit.rs @@ -1,14 +1,13 @@ -use std::marker::PhantomData; -use std::ops::Add; +use crate::nifs::NIFS; +use crate::r1cs::{FInstance, FWitness}; +use crate::transcript::Transcript; use ark_ff::{BigInteger, One, PrimeField, Zero}; use ark_serialize::CanonicalSerialize; -use sha2::Digest; use kzg::commitment::KzgCommitment; use kzg::types::{BaseField, ScalarField}; -use crate::nifs::{NIFS}; -use crate::r1cs::{FInstance, FWitness}; -use crate::transcript::Transcript; - +use sha2::Digest; +use std::marker::PhantomData; +use std::ops::Add; /// State structure of IVC, which is presented in BaseField of Bsn12_381 curve /// Todo: Implement a general version. @@ -23,7 +22,6 @@ pub trait FCircuit { fn run(&self, z_i: &State, w_i: &FWitness) -> State; } - /// F' circuit pub struct AugmentedCircuit { // F function @@ -46,13 +44,8 @@ pub struct AugmentedCircuit AugmentedCircuit { - - pub fn new( - f_circuit: FC, - trivial_instance: &FInstance, - z_0: &State, - ) -> Self { +impl AugmentedCircuit { + pub fn new(f_circuit: FC, trivial_instance: &FInstance, z_0: &State) -> Self { Self { f_circuit, i: BaseField::zero(), @@ -62,7 +55,7 @@ impl AugmentedCircui z_i1: None, h_i: None, h_i1: None, - phantom_data_t: PhantomData + phantom_data_t: PhantomData, } } pub fn run( @@ -71,10 +64,8 @@ impl AugmentedCircui big_u_i: Option<&FInstance>, w_i: &FWitness, com_t: Option<&KzgCommitment>, - ) -> Result{ - + ) -> Result { if self.i != BaseField::from(0) { - // check that if i > 0 then U_i and com_t must exist if big_u_i.is_none() || com_t.is_none() { return Err(String::from("Wrong parameters.")); @@ -82,18 +73,18 @@ impl AugmentedCircui // check that if i > 0 then the hash_x must exist if self.h_i.is_none() { - return Err(String::from("The hash public IO must exist")) + return Err(String::from("The hash public IO must exist")); } // get hash_x - let hash_x = self.h_i.clone().unwrap(); + let hash_x = self.h_i.unwrap(); // 1. check that u.x =? hash_x // Because u_i.x is in ScalarField while hash_x is in BaseField, they need to // be converted into a comparable type // Todo: Non-native field transform - let u_dot_x = u_i.x[0].clone(); + let u_dot_x = u_i.x[0]; let hash_fr = ScalarField::from_le_bytes_mod_order(&hash_x.into_bigint().to_bytes_le()); if u_dot_x != hash_fr { return Err(String::from("Public IO is wrong ")); @@ -122,20 +113,21 @@ impl AugmentedCircui let z_i1 = self.f_circuit.run(&self.z_i, w_i); // compute hash - let new_hash= Self::hash_io(self.i.add(BaseField::one()), &self.z_0, &z_i1, &big_u_i1); + let new_hash = Self::hash_io(self.i.add(BaseField::one()), &self.z_0, &z_i1, &big_u_i1); // store the next hash self.h_i1 = Some(new_hash); // store the next state self.z_i1 = Some(z_i1); - - } else { // i == 0 + } else { + // i == 0 // compute z_1 = F(z_0, w_i) let z_i1 = self.f_circuit.run(&self.z_i, w_i); // compute hash - let new_hash = Self::hash_io(BaseField::one(), &self.z_0, &z_i1, &self.trivial_instance); + let new_hash = + Self::hash_io(BaseField::one(), &self.z_0, &z_i1, &self.trivial_instance); // store the next hash self.h_i1 = Some(new_hash); @@ -144,25 +136,20 @@ impl AugmentedCircui } // 4. output the hash - return Ok(self.h_i1.unwrap()); + Ok(self.h_i1.unwrap()) } - /// updating F' function for the next step of IVC. + /// Updating F' function for the next step of IVC. pub fn next_step(&mut self) { self.z_i = self.z_i1.clone().unwrap(); self.z_i1 = None; - self.i = self.i + BaseField::one(); + self.i += BaseField::one(); self.h_i = self.h_i1; self.h_i1 = None; } /// A function computes public IO of an instance: u.x = hash(i, z0, zi, Ui). - pub fn hash_io( - i: BaseField, - z_0: &State, - z_i: &State, - big_u_i: &FInstance - ) -> BaseField { + pub fn hash_io(i: BaseField, z_0: &State, z_i: &State, big_u_i: &FInstance) -> BaseField { let mut hasher = T::default(); i.serialize_uncompressed(&mut hasher).unwrap(); z_0.state.serialize_uncompressed(&mut hasher).unwrap(); @@ -184,23 +171,23 @@ impl AugmentedCircui #[cfg(test)] #[allow(dead_code)] mod test { - use sha2::Sha256; - use kzg::scheme::KzgScheme; - use kzg::srs::Srs; + use super::*; use crate::nifs::nifs_verifier::gen_test_values; use crate::r1cs::create_trivial_pair; - use super::*; + use kzg::scheme::KzgScheme; + use kzg::srs::Srs; + use sha2::Sha256; struct TestCircuit {} impl FCircuit for TestCircuit { fn run(&self, z_i: &State, w_i: &FWitness) -> State { - let x = w_i.w[0].clone(); + let x = w_i.w[0]; let res = x * x * x + x + ScalarField::from(5); // because res is in scalar field, we need to convert it into base_field let base_res = BaseField::from_le_bytes_mod_order(&res.into_bigint().to_bytes_le()); State { - state: z_i.state + base_res + state: z_i.state + base_res, } } } @@ -209,7 +196,11 @@ mod test { fn test_augmented_circuit_01() { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -223,11 +214,15 @@ mod test { let (_, trivial_instance) = create_trivial_pair(x[0].len(), witnesses[0].len(), &scheme); // generate f_circuit instance - let f_circuit = TestCircuit{}; + let f_circuit = TestCircuit {}; // generate state - let z_0 = State{state: BaseField::from(0)}; - let z_1 = State{state: BaseField::from(35)}; + let z_0 = State { + state: BaseField::from(0), + }; + let z_1 = State { + state: BaseField::from(35), + }; // let prover_transcript = Transcript::::default(); // create F' @@ -240,25 +235,28 @@ mod test { z_i1: None, h_i: None, h_i1: None, - phantom_data_t: PhantomData + phantom_data_t: PhantomData, }; - let res1 = augmented_circuit.run( - &u_0, - None, - &w_0, - None, - ); + let res1 = augmented_circuit.run(&u_0, None, &w_0, None); // check if F' is running if res1.is_err() { - println!("{:?}",res1); + println!("{:?}", res1); } assert!(res1.is_ok()); let hash = res1.unwrap(); // check if the hash output is correct - assert_eq!(hash, AugmentedCircuit::::hash_io(BaseField::one(), &z_0, &z_1, &trivial_instance)); + assert_eq!( + hash, + AugmentedCircuit::::hash_io( + BaseField::one(), + &z_0, + &z_1, + &trivial_instance + ) + ); augmented_circuit.next_step(); // check if the state produced is correct assert_eq!(augmented_circuit.z_i.state, z_1.state); @@ -268,7 +266,11 @@ mod test { fn test_augmented_circuit_02() { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3, 4]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -279,22 +281,44 @@ mod test { let mut u_1 = w_1.commit(&scheme, &x[1]); // generate trivial_instance - let (trivial_witness, trivial_instance) = create_trivial_pair(x[0].len(), witnesses[0].len(), &scheme); + let (trivial_witness, trivial_instance) = + create_trivial_pair(x[0].len(), witnesses[0].len(), &scheme); // generate f_circuit instance - let f_circuit = TestCircuit{}; + let f_circuit = TestCircuit {}; // generate state - let z_0 = State{state: BaseField::from(0)}; - let z_1 = State{state: BaseField::from(35)}; - let z_2 = State{state: BaseField::from(108)}; + let z_0 = State { + state: BaseField::from(0), + }; + let z_1 = State { + state: BaseField::from(35), + }; + let z_2 = State { + state: BaseField::from(108), + }; let mut prover_transcript = Transcript::::default(); - let u_1_x = AugmentedCircuit::::hash_io(BaseField::from(1), &z_0, &z_1, &trivial_instance); + let u_1_x = AugmentedCircuit::::hash_io( + BaseField::from(1), + &z_0, + &z_1, + &trivial_instance, + ); // convert u_1_x from BaseField into ScalarField - u_1.x = vec![ScalarField::from_le_bytes_mod_order(&u_1_x.into_bigint().to_bytes_le())]; + u_1.x = vec![ScalarField::from_le_bytes_mod_order( + &u_1_x.into_bigint().to_bytes_le(), + )]; - let (_, folded_instance, com_t, _) = NIFS::::prover(&r1cs, &w_1, &trivial_witness, &u_1, &trivial_instance, &scheme, &mut prover_transcript); + let (_, folded_instance, com_t, _) = NIFS::::prover( + &r1cs, + &w_1, + &trivial_witness, + &u_1, + &trivial_instance, + &scheme, + &mut prover_transcript, + ); // create F' let mut augmented_circuit = AugmentedCircuit:: { @@ -306,39 +330,43 @@ mod test { z_i1: None, h_i: Some(u_1_x), h_i1: None, - phantom_data_t: PhantomData + phantom_data_t: PhantomData, }; - - - let res1 = augmented_circuit.run( - &u_1, - Some(&trivial_instance), - &w_1, - Some(&com_t) - ); + let res1 = augmented_circuit.run(&u_1, Some(&trivial_instance), &w_1, Some(&com_t)); // check if F' is running if res1.is_err() { - println!("{:?}",res1); + println!("{:?}", res1); } assert!(res1.is_ok()); let hash = res1.unwrap(); // check if the hash output is correct - assert_eq!(hash, AugmentedCircuit::::hash_io(BaseField::from(2), &z_0, &z_2, &folded_instance)); + assert_eq!( + hash, + AugmentedCircuit::::hash_io( + BaseField::from(2), + &z_0, + &z_2, + &folded_instance + ) + ); augmented_circuit.next_step(); // check if the state produced is correct assert_eq!(augmented_circuit.z_i.state, z_2.state); } - #[test] #[should_panic] fn test_augmented_circuit_03() { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3, 4]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -349,22 +377,44 @@ mod test { let mut u_1 = w_1.commit(&scheme, &x[1]); // generate trivial_instance - let (trivial_witness, trivial_instance) = create_trivial_pair(x[0].len(), witnesses[0].len(), &scheme); + let (trivial_witness, trivial_instance) = + create_trivial_pair(x[0].len(), witnesses[0].len(), &scheme); // generate f_circuit instance - let f_circuit = TestCircuit{}; + let f_circuit = TestCircuit {}; // generate state - let z_0 = State{state: BaseField::from(0)}; - let z_1 = State{state: BaseField::from(35)}; - let z_2 = State{state: BaseField::from(130)}; + let z_0 = State { + state: BaseField::from(0), + }; + let z_1 = State { + state: BaseField::from(35), + }; + let z_2 = State { + state: BaseField::from(130), + }; let mut prover_transcript = Transcript::::default(); - let u_1_x = AugmentedCircuit::::hash_io(BaseField::from(1), &z_0, &z_1, &trivial_instance); + let u_1_x = AugmentedCircuit::::hash_io( + BaseField::from(1), + &z_0, + &z_1, + &trivial_instance, + ); // convert u_1_x from BaseField into ScalarField - u_1.x = vec![ScalarField::from_le_bytes_mod_order(&u_1_x.into_bigint().to_bytes_le())]; + u_1.x = vec![ScalarField::from_le_bytes_mod_order( + &u_1_x.into_bigint().to_bytes_le(), + )]; - let (_, folded_instance, com_t, _) = NIFS::::prover(&r1cs, &w_1, &trivial_witness, &u_1, &trivial_instance, &scheme, &mut prover_transcript); + let (_, folded_instance, com_t, _) = NIFS::::prover( + &r1cs, + &w_1, + &trivial_witness, + &u_1, + &trivial_instance, + &scheme, + &mut prover_transcript, + ); // create F' let mut augmented_circuit = AugmentedCircuit:: { @@ -376,27 +426,28 @@ mod test { z_i1: None, h_i: Some(u_1_x), h_i1: None, - phantom_data_t: PhantomData + phantom_data_t: PhantomData, }; - - - let res1 = augmented_circuit.run( - &u_1, - Some(&trivial_instance), - &w_1, - Some(&com_t) - ); + let res1 = augmented_circuit.run(&u_1, Some(&trivial_instance), &w_1, Some(&com_t)); // check if F' is running if res1.is_err() { - println!("{:?}",res1); + println!("{:?}", res1); } assert!(res1.is_ok()); let hash = res1.unwrap(); // check if the hash output is correct - assert_eq!(hash, AugmentedCircuit::::hash_io(BaseField::from(2), &z_0, &z_2, &folded_instance)); + assert_eq!( + hash, + AugmentedCircuit::::hash_io( + BaseField::from(2), + &z_0, + &z_2, + &folded_instance + ) + ); augmented_circuit.next_step(); // check if the state produced is correct assert_eq!(augmented_circuit.z_i.state, z_2.state); diff --git a/nova/src/ivc/ivc_prover.rs b/nova/src/ivc/ivc_prover.rs index 08dea2d..2f96124 100644 --- a/nova/src/ivc/ivc_prover.rs +++ b/nova/src/ivc/ivc_prover.rs @@ -1,15 +1,14 @@ -use ark_ff::{Zero}; -use sha2::Digest; -use kzg::types::{ScalarField}; -use crate::circuit::{FCircuit}; -use crate::ivc::{IVC, IVCProof, ZkIVCProof}; +use crate::circuit::FCircuit; +use crate::ivc::{IVCProof, ZkIVCProof, IVC}; use crate::nifs::NIFS; use crate::r1cs::{FInstance, FWitness, R1CS}; use crate::transcript::Transcript; +use ark_ff::Zero; +use kzg::types::ScalarField; +use sha2::Digest; #[allow(dead_code)] -impl IVC { - +impl IVC { /// IVC prover will fold 2 instance-witness pairs into one via NIFS /// and generate zkSNARK proof for it. pub fn prove( @@ -18,13 +17,11 @@ impl IVC { ivc_proof: &IVCProof, prover_transcript: &mut Transcript, ) -> (FWitness, FInstance, ZkIVCProof) { - let i = self.augmented_circuit.i; - if ! i.is_zero() { - + if !i.is_zero() { // 1 + 2. Parse Π and compute U', W' and com_T let (big_w_out, big_u_out, com_t, r) = NIFS::::prover( - &r1cs, + r1cs, &ivc_proof.w_i, &ivc_proof.big_w_i, &ivc_proof.u_i, @@ -34,7 +31,8 @@ impl IVC { ); // 3. Generate zkSNARK proof - let nifs_proof = NIFS::::prove(r, &big_w_out, &big_u_out, &self.scheme, prover_transcript); + let nifs_proof = + NIFS::::prove(r, &big_w_out, &big_u_out, &self.scheme, prover_transcript); ( big_w_out, @@ -43,9 +41,9 @@ impl IVC { u_i: ivc_proof.u_i.clone(), big_u_i: ivc_proof.big_u_i.clone(), com_t: Some(com_t), - folded_u_proof: Some(nifs_proof) - }) - + folded_u_proof: Some(nifs_proof), + }, + ) } else { ( ivc_proof.big_w_i.clone(), @@ -54,8 +52,9 @@ impl IVC { u_i: ivc_proof.u_i.clone(), big_u_i: ivc_proof.big_u_i.clone(), com_t: None, - folded_u_proof: None - }) + folded_u_proof: None, + }, + ) } } -} \ No newline at end of file +} diff --git a/nova/src/ivc/ivc_verifier.rs b/nova/src/ivc/ivc_verifier.rs index f24cea0..43e379f 100644 --- a/nova/src/ivc/ivc_verifier.rs +++ b/nova/src/ivc/ivc_verifier.rs @@ -1,41 +1,39 @@ -use ark_ff::{BigInteger, One, PrimeField, Zero}; -use sha2::Digest; -use kzg::types::{BaseField, ScalarField}; use crate::circuit::{AugmentedCircuit, FCircuit}; -use crate::ivc::{IVC, ZkIVCProof}; +use crate::ivc::{ZkIVCProof, IVC}; use crate::nifs::NIFS; use crate::transcript::Transcript; +use ark_ff::{BigInteger, One, PrimeField, Zero}; +use kzg::types::{BaseField, ScalarField}; +use sha2::Digest; #[allow(dead_code)] -impl IVC { - +impl IVC { /// IVC verifier will do 5 steps as mentioned in constructor 4 /// of Nova paper. pub fn verify( &mut self, zk_ivc_proof: &ZkIVCProof, - verifier_transcript: &mut Transcript + verifier_transcript: &mut Transcript, ) -> Result<(), String> { - let i = self.augmented_circuit.i; let z_0 = &self.augmented_circuit.z_0; let z_i = &self.augmented_circuit.z_i; - if i == BaseField::zero() { - return if z_0.state == z_i.state { + if z_0.state == z_i.state { Ok(()) } else { Err(String::from("Verify failed: wrong state")) } } else { - // 1. parse zkIVCProof = (U, u, comT, nifs_proof) let u_i = zk_ivc_proof.u_i.clone(); let big_u_i = zk_ivc_proof.big_u_i.clone(); if zk_ivc_proof.com_t.is_none() { - return Err(String::from("Verify failed: commitment of cross term T must exist")); + return Err(String::from( + "Verify failed: commitment of cross term T must exist", + )); } if zk_ivc_proof.folded_u_proof.is_none() { @@ -46,7 +44,8 @@ impl IVC { // 2. check that u.x = hash(i, z_0, z_i, U) let hash_io = AugmentedCircuit::::hash_io(i, z_0, z_i, &big_u_i); - let hash_fr = ScalarField::from_le_bytes_mod_order(&hash_io.into_bigint().to_bytes_le()); + let hash_fr = + ScalarField::from_le_bytes_mod_order(&hash_io.into_bigint().to_bytes_le()); if u_i.x[0] != hash_fr { return Err(String::from("Verify failed: Public IO is wrong")); } @@ -55,7 +54,7 @@ impl IVC { if u_i.com_e != self.augmented_circuit.trivial_instance.com_e { return Err(String::from("Verify failed: Commitment of E is wrong")); } - if ! u_i.u.is_one() { + if !u_i.u.is_one() { return Err(String::from("Verify failed: Scalar u is wrong")); } @@ -63,10 +62,16 @@ impl IVC { let big_u_out = NIFS::::verifier(folded_u_proof.r, &u_i, &big_u_i, &com_t); // 5. verify that zkSNARK.V(U', pi_U') = 1 - let res = NIFS::::verify(&folded_u_proof, &u_i, &big_u_i, &big_u_out, &com_t, &self.scheme, verifier_transcript); - - res + NIFS::::verify( + &folded_u_proof, + &u_i, + &big_u_i, + &big_u_out, + &com_t, + &self.scheme, + verifier_transcript, + ) } } } @@ -74,26 +79,26 @@ impl IVC { #[cfg(test)] #[allow(dead_code)] mod tests { - use std::marker::PhantomData; - use sha2::Sha256; - use kzg::scheme::KzgScheme; - use kzg::srs::Srs; + use super::*; + use crate::circuit::State; use crate::ivc::IVCProof; use crate::nifs::nifs_verifier::gen_test_values; - use super::*; use crate::r1cs::{create_trivial_pair, FInstance, FWitness}; use crate::transcript::Transcript; - use crate::circuit::State; + use kzg::scheme::KzgScheme; + use kzg::srs::Srs; + use sha2::Sha256; + use std::marker::PhantomData; struct TestCircuit {} impl FCircuit for TestCircuit { fn run(&self, z_i: &State, w_i: &FWitness) -> State { - let x = w_i.w[0].clone(); + let x = w_i.w[0]; let res = x * x * x + x + ScalarField::from(5); // because res is in scalar field, we need to convert it into base_field let base_res = BaseField::from_le_bytes_mod_order(&res.into_bigint().to_bytes_le()); State { - state: z_i.state + base_res + state: z_i.state + base_res, } } } @@ -105,7 +110,11 @@ mod tests { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3, 4, 1, 2]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -114,39 +123,48 @@ mod tests { let x_len = x[0].len(); // Generate witnesses and instances - let w: Vec = witnesses.iter().map(|witness| FWitness::new(witness, matrix_a.len())).collect(); - let mut u: Vec = w.iter().zip(x).map(|(w, x)| w.commit(&scheme, &x)).collect(); + let w: Vec = witnesses + .iter() + .map(|witness| FWitness::new(witness, matrix_a.len())) + .collect(); + let mut u: Vec = w + .iter() + .zip(x) + .map(|(w, x)| w.commit(&scheme, &x)) + .collect(); // step i let mut i = BaseField::zero(); // generate trivial instance-witness pair - let (trivial_witness, trivial_instance) = create_trivial_pair(x_len, witnesses[0].len(), &scheme); + let (trivial_witness, trivial_instance) = + create_trivial_pair(x_len, witnesses[0].len(), &scheme); // generate f_circuit instance - let f_circuit = TestCircuit{}; + let f_circuit = TestCircuit {}; // generate states - let mut z = vec![State{state: BaseField::from(0)}; 5]; + let mut z = vec![ + State { + state: BaseField::from(0) + }; + 5 + ]; for index in 1..5 { - z[index] = f_circuit.run(&z[index - 1], &w[index-1]); + z[index] = f_circuit.run(&z[index - 1], &w[index - 1]); } let mut prover_transcript; let mut verifier_transcript = Transcript::::default(); - // create F' - let augmented_circuit = AugmentedCircuit::::new( - f_circuit, - &trivial_instance, - &z[0] - ); + let augmented_circuit = + AugmentedCircuit::::new(f_circuit, &trivial_instance, &z[0]); // generate IVC let mut ivc = IVC:: { scheme, - augmented_circuit + augmented_circuit, }; // initialize IVC proof, zkIVCProof, folded witness and folded instance @@ -157,22 +175,16 @@ mod tests { let mut res; for step in 0..4 { - println!("Step: {:?}", step); if step == 0 { - res = ivc.augmented_circuit.run( - &u[step], - None, - &w[step], - None, - ); + res = ivc.augmented_circuit.run(&u[step], None, &w[step], None); } else { res = ivc.augmented_circuit.run( &ivc_proof.u_i, Some(&ivc_proof.big_u_i.clone()), &ivc_proof.w_i, - Some(&zk_ivc_proof.com_t.clone().unwrap()) + Some(&zk_ivc_proof.com_t.clone().unwrap()), ); } @@ -190,20 +202,34 @@ mod tests { // update for next step - if step != 3 { // do not update if we have done with IVC + if step != 3 { + // do not update if we have done with IVC ivc.augmented_circuit.next_step(); - i = i + BaseField::one(); + i += BaseField::one(); assert_eq!(ivc.augmented_circuit.z_i.state, z[step + 1].state); prover_transcript = Transcript::::default(); verifier_transcript = Transcript::::default(); - let hash_x = AugmentedCircuit::::hash_io(i, &z[0], &z[step + 1], &folded_instance); + let hash_x = AugmentedCircuit::::hash_io( + i, + &z[0], + &z[step + 1], + &folded_instance, + ); // convert u_1_x from BaseField into ScalarField - u[step + 1].x = vec![ScalarField::from_le_bytes_mod_order(&hash_x.into_bigint().to_bytes_le())]; + u[step + 1].x = vec![ScalarField::from_le_bytes_mod_order( + &hash_x.into_bigint().to_bytes_le(), + )]; // generate ivc_proof and zkSNARK proof. - ivc_proof = IVCProof::new(&u[step + 1], &w[step + 1], &folded_instance, &folded_witness); - (folded_witness, folded_instance, zk_ivc_proof) = ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript); + ivc_proof = IVCProof::new( + &u[step + 1], + &w[step + 1], + &folded_instance, + &folded_witness, + ); + (folded_witness, folded_instance, zk_ivc_proof) = + ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript); } } } @@ -216,7 +242,11 @@ mod tests { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3, 4, 1]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -240,10 +270,12 @@ mod tests { let trivial_instance = trivial_witness.commit(&scheme, &trivial_x); // generate f_circuit instance - let f_circuit = TestCircuit{}; + let f_circuit = TestCircuit {}; // generate state - let z_0 = State{state: BaseField::from(0)}; + let z_0 = State { + state: BaseField::from(0), + }; let z_1 = f_circuit.run(&z_0, &w_0); let z_2 = f_circuit.run(&z_1, &w_1); @@ -260,13 +292,13 @@ mod tests { z_i1: None, h_i: None, h_i1: None, - phantom_data_t: PhantomData + phantom_data_t: PhantomData, }; // generate IVC let mut ivc = IVC:: { scheme, - augmented_circuit + augmented_circuit, }; // initialize IVC proof, zkIVCProof, folded witness (W) and folded instance (U) @@ -284,19 +316,13 @@ mod tests { folded_u_proof: None, }; - let folded_witness; let folded_instance; println!("Step 1"); // run F' for the first time // With i = 0, the instance U_i and commitment com_t do not exist. - let res1 = ivc.augmented_circuit.run( - &u_0, - None, - &w_0, - None, - ); + let res1 = ivc.augmented_circuit.run(&u_0, None, &w_0, None); if res1.is_err() { println!("Step: {:?}, {:?}", i, res1); @@ -312,21 +338,24 @@ mod tests { // update for next step ivc.augmented_circuit.next_step(); - i = i + BaseField::one(); + i += BaseField::one(); assert_eq!(ivc.augmented_circuit.z_i.state, BaseField::from(35)); prover_transcript = Transcript::::default(); verifier_transcript = Transcript::::default(); // because all instances above are from F, not F', so we need to do this trick. - let u_1_x = AugmentedCircuit::::hash_io(i, &z_0, &z_1, &trivial_instance); + let u_1_x = + AugmentedCircuit::::hash_io(i, &z_0, &z_1, &trivial_instance); // convert u_1_x from BaseField into ScalarField - u_1.x = vec![ScalarField::from_le_bytes_mod_order(&u_1_x.into_bigint().to_bytes_le())]; + u_1.x = vec![ScalarField::from_le_bytes_mod_order( + &u_1_x.into_bigint().to_bytes_le(), + )]; // U_1 is a trivial instance (via the paper). // Prover fold u_1 and U_1 into U_2. // generate IVC proof. - ivc_proof = IVCProof{ + ivc_proof = IVCProof { u_i: u_1.clone(), w_i: w_1, big_u_i: trivial_instance.clone(), @@ -334,11 +363,8 @@ mod tests { }; // generate W_2, U_2 and zkIVCProof via IVC proof - (folded_witness, folded_instance, zk_ivc_proof) = ivc.prove( - &r1cs, - &ivc_proof, - &mut prover_transcript - ); + (folded_witness, folded_instance, zk_ivc_proof) = + ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript); println!("Step 2"); // run F' for the second time @@ -346,7 +372,7 @@ mod tests { &ivc_proof.u_i, Some(&ivc_proof.big_u_i.clone()), &ivc_proof.w_i, - Some(&zk_ivc_proof.com_t.clone().unwrap()) + Some(&zk_ivc_proof.com_t.clone().unwrap()), ); if res2.is_err() { @@ -363,28 +389,30 @@ mod tests { // update next step ivc.augmented_circuit.next_step(); - i = i + BaseField::one(); + i += BaseField::one(); prover_transcript = Transcript::::default(); verifier_transcript = Transcript::::default(); // check if this state is 108. - assert_eq!(ivc.augmented_circuit.z_i.state, BaseField::from(108), "Wrong state"); - + assert_eq!( + ivc.augmented_circuit.z_i.state, + BaseField::from(108), + "Wrong state" + ); - let u_2_x = AugmentedCircuit::::hash_io(i, &z_0, &z_2, &folded_instance); - u_2.x = vec![ScalarField::from_le_bytes_mod_order(&u_2_x.into_bigint().to_bytes_le())]; + let u_2_x = + AugmentedCircuit::::hash_io(i, &z_0, &z_2, &folded_instance); + u_2.x = vec![ScalarField::from_le_bytes_mod_order( + &u_2_x.into_bigint().to_bytes_le(), + )]; ivc_proof = IVCProof { u_i: u_2, w_i: w_2, big_u_i: folded_instance, // U_2 - big_w_i: folded_witness // W_2 + big_w_i: folded_witness, // W_2 }; // Compute W_3, U_3, and zkSNARK proof - (_, _, zk_ivc_proof) = ivc.prove( - &r1cs, - &ivc_proof, - &mut prover_transcript - ); + (_, _, zk_ivc_proof) = ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript); println!("Step 3"); // run F' for the last time @@ -392,7 +420,7 @@ mod tests { &ivc_proof.u_i, Some(&ivc_proof.big_u_i.clone()), &ivc_proof.w_i, - Some(&zk_ivc_proof.com_t.clone().unwrap()) + Some(&zk_ivc_proof.com_t.clone().unwrap()), ); if res3.is_err() { @@ -410,6 +438,10 @@ mod tests { // update next step ivc.augmented_circuit.next_step(); // check if this state is 115. - assert_eq!(ivc.augmented_circuit.z_i.state, BaseField::from(115), "Wrong state"); + assert_eq!( + ivc.augmented_circuit.z_i.state, + BaseField::from(115), + "Wrong state" + ); } -} \ No newline at end of file +} diff --git a/nova/src/ivc/mod.rs b/nova/src/ivc/mod.rs index bcc79b1..eb6747c 100644 --- a/nova/src/ivc/mod.rs +++ b/nova/src/ivc/mod.rs @@ -1,12 +1,12 @@ mod ivc_prover; mod ivc_verifier; -use sha2::Digest; -use kzg::commitment::KzgCommitment; -use kzg::scheme::KzgScheme; use crate::circuit::{AugmentedCircuit, FCircuit}; -use crate::nifs::{NIFSProof}; +use crate::nifs::NIFSProof; use crate::r1cs::{FInstance, FWitness}; +use kzg::commitment::KzgCommitment; +use kzg::scheme::KzgScheme; +use sha2::Digest; /// This struct is the zero knowledge proof for IVC /// π = (U, u, com_T , π_U') where U' is the folded instance @@ -28,53 +28,40 @@ pub struct IVCProof { #[allow(dead_code)] impl IVCProof { - - pub fn new( - u_i: &FInstance, - w_i: &FWitness, - big_u_i: &FInstance, - big_w_i: &FWitness, - ) -> Self { + pub fn new(u_i: &FInstance, w_i: &FWitness, big_u_i: &FInstance, big_w_i: &FWitness) -> Self { Self { u_i: u_i.clone(), w_i: w_i.clone(), big_u_i: big_u_i.clone(), - big_w_i: big_w_i.clone() + big_w_i: big_w_i.clone(), } } // Generate a trivial IVC proof. - pub fn trivial_ivc_proof( - trivial_instance: &FInstance, - trivial_witness: &FWitness, - ) -> Self { + pub fn trivial_ivc_proof(trivial_instance: &FInstance, trivial_witness: &FWitness) -> Self { Self { u_i: trivial_instance.clone(), w_i: trivial_witness.clone(), big_u_i: trivial_instance.clone(), - big_w_i: trivial_witness.clone() + big_w_i: trivial_witness.clone(), } } } #[allow(dead_code)] impl ZkIVCProof { - pub fn trivial_zk_ivc_proof( - trivial_instance: &FInstance, - ) -> Self { + pub fn trivial_zk_ivc_proof(trivial_instance: &FInstance) -> Self { Self { u_i: trivial_instance.clone(), big_u_i: trivial_instance.clone(), com_t: None, - folded_u_proof: None + folded_u_proof: None, } } - } /// IVC structure includes a scheme for commitment and an augmented F' function -pub struct IVC > { +pub struct IVC { pub scheme: KzgScheme, pub augmented_circuit: AugmentedCircuit, } - diff --git a/nova/src/lib.rs b/nova/src/lib.rs index 5011401..8b65ecd 100644 --- a/nova/src/lib.rs +++ b/nova/src/lib.rs @@ -1,6 +1,6 @@ +pub mod circuit; +pub mod ivc; pub mod nifs; -pub mod utils; +pub mod r1cs; pub mod transcript; -pub mod ivc; -pub mod circuit; -pub mod r1cs; \ No newline at end of file +pub mod utils; diff --git a/nova/src/nifs/mod.rs b/nova/src/nifs/mod.rs index 8ad05f7..4bf05c8 100644 --- a/nova/src/nifs/mod.rs +++ b/nova/src/nifs/mod.rs @@ -1,17 +1,18 @@ +use ark_ec::CurveGroup; use std::marker::PhantomData; use std::ops::Mul; -use ark_ec::CurveGroup; +use crate::r1cs::{FInstance, FWitness, R1CS}; +use crate::utils::{ + hadamard_product, matrix_vector_product, vec_add, vec_sub, vector_elem_product, +}; use kzg::commitment::KzgCommitment; -use kzg::types::ScalarField; -use sha2::{Digest}; use kzg::opening::KzgOpening; -use crate::r1cs::{FInstance, FWitness, R1CS}; -use crate::utils::{hadamard_product, matrix_vector_product, vec_add, vec_sub, vector_elem_product}; +use kzg::types::ScalarField; +use sha2::Digest; -pub(crate) mod nifs_verifier; mod nifs_prover; - +pub(crate) mod nifs_verifier; /// NIFS Proof is a zk proof. To convince the verifier, prover creates an opening /// for each E and W. @@ -20,15 +21,14 @@ pub struct NIFSProof { pub r: ScalarField, pub opening_point: ScalarField, pub opening_e: KzgOpening, - pub opening_w: KzgOpening + pub opening_w: KzgOpening, } pub struct NIFS { _phantom_data_t: PhantomData, } -impl NIFS { - +impl NIFS { /// Compute the cross-term T /// T = AZ1 ◦ BZ2 + AZ2 ◦ BZ1 − u1 · CZ2 − u2 · CZ1. pub fn compute_t( @@ -36,9 +36,8 @@ impl NIFS { u1: ScalarField, u2: ScalarField, z1: &Vec, - z2: &Vec + z2: &Vec, ) -> Vec { - let az1 = matrix_vector_product(&r1cs.matrix_a, z1); let bz1 = matrix_vector_product(&r1cs.matrix_b, z1); let cz1 = matrix_vector_product(&r1cs.matrix_c, z1); @@ -65,22 +64,20 @@ impl NIFS { r: ScalarField, fw1: &FWitness, fw2: &FWitness, - t: &Vec, + t: &[ScalarField], // rT: ScalarField, ) -> FWitness { + let new_e = fw1 + .e + .iter() + .zip(t.iter()) + .zip(&fw2.e) + .map(|((e1, t), e2)| *e1 + r * *t + r * r * *e2) + .collect(); - let new_e = fw1.e.iter().zip(t.iter()).zip(&fw2.e).map(|((e1, t), e2)| { - *e1 + r * *t + r * r * *e2 - }).collect(); - - let new_w = fw1.w.iter().zip(&fw2.w).map(|(a, b)| { - *a + *b * r - }).collect(); + let new_w = fw1.w.iter().zip(&fw2.w).map(|(a, b)| *a + *b * r).collect(); - FWitness{ - e: new_e, - w: new_w - } + FWitness { e: new_e, w: new_w } } /// Fold two instances into one. @@ -94,21 +91,18 @@ impl NIFS { fi2: &FInstance, com_t: &KzgCommitment, ) -> FInstance { - let new_com_e = KzgCommitment((fi1.com_e.0 + com_t.0.mul(r) + fi2.com_e.0.mul(r * r)).into_affine()); + let new_com_e = + KzgCommitment((fi1.com_e.0 + com_t.0.mul(r) + fi2.com_e.0.mul(r * r)).into_affine()); let new_com_w = KzgCommitment((fi1.com_w.0 + fi2.com_w.0.mul(r)).into_affine()); let new_u = fi1.u + fi2.u * r; - let new_x = fi1.x.iter().zip(&fi2.x).map(|(a, b)| { - *a + *b * r - }).collect(); + let new_x = fi1.x.iter().zip(&fi2.x).map(|(a, b)| *a + *b * r).collect(); - FInstance{ + FInstance { com_e: new_com_e, u: new_u, com_w: new_com_w, - x: new_x + x: new_x, } } - } - diff --git a/nova/src/nifs/nifs_prover.rs b/nova/src/nifs/nifs_prover.rs index e70c597..764dbce 100644 --- a/nova/src/nifs/nifs_prover.rs +++ b/nova/src/nifs/nifs_prover.rs @@ -1,14 +1,12 @@ -use sha2::Digest; +use crate::nifs::{FInstance, FWitness, NIFSProof, NIFS}; +use crate::r1cs::R1CS; +use crate::transcript::Transcript; use kzg::commitment::KzgCommitment; use kzg::scheme::KzgScheme; use kzg::types::ScalarField; -use crate::nifs::{FInstance, FWitness, NIFS, NIFSProof}; -use crate::r1cs::R1CS; -use crate::transcript::Transcript; - - -impl NIFS { +use sha2::Digest; +impl NIFS { /// Prover output a folded instance-witness pair, com_T and challenge r via Fiat-Shamir pub fn prover( r1cs: &R1CS, @@ -17,9 +15,8 @@ impl NIFS { fi1: &FInstance, fi2: &FInstance, scheme: &KzgScheme, - transcript: &mut Transcript + transcript: &mut Transcript, ) -> (FWitness, FInstance, KzgCommitment, ScalarField) { - // generate Z = (W, x, u) let mut z1 = fw1.w.clone(); z1.append(&mut fi1.x.clone()); @@ -29,7 +26,7 @@ impl NIFS { z2.append(&mut fi2.x.clone()); z2.push(fi2.u); - let t = NIFS::::compute_t(&r1cs, fi1.u, fi2.u, &z1, &z2); + let t = NIFS::::compute_t(r1cs, fi1.u, fi2.u, &z1, &z2); let com_t = scheme.commit_vector(&t); transcript.feed_scalar_num(fi1.u); @@ -51,7 +48,6 @@ impl NIFS { scheme: &KzgScheme, transcript: &mut Transcript, ) -> NIFSProof { - // opening = Transcript(fi_cmE, fi_cmW); transcript.feed(&fi.com_e); transcript.feed(&fi.com_w); @@ -64,7 +60,7 @@ impl NIFS { r, opening_point, opening_e, - opening_w + opening_w, } } } @@ -72,17 +68,21 @@ impl NIFS { #[cfg(test)] mod test { - use sha2::Sha256; - use kzg::srs::Srs; + use super::*; use crate::nifs::nifs_verifier::gen_test_values; use crate::r1cs::is_r1cs_satisfied; - use super::*; + use kzg::srs::Srs; + use sha2::Sha256; #[test] pub fn test_prover_folding() { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3, 4]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -90,20 +90,22 @@ mod test { let scheme = KzgScheme::new(srs); // Generate witnesses and instances - let w: Vec = witnesses.iter().map(|witness| FWitness::new(witness, matrix_a.len())).collect(); - let u: Vec = w.iter().zip(x).map(|(w, x)| w.commit(&scheme, &x)).collect(); + let w: Vec = witnesses + .iter() + .map(|witness| FWitness::new(witness, matrix_a.len())) + .collect(); + let u: Vec = w + .iter() + .zip(x) + .map(|(w, x)| w.commit(&scheme, &x)) + .collect(); let mut transcript = Transcript::::default(); - let (folded_witness, folded_instance, _, _) = NIFS::::prover( - &r1cs, - &w[0], &w[1], - &u[0], &u[1], - &scheme, - &mut transcript - ); + let (folded_witness, folded_instance, _, _) = + NIFS::::prover(&r1cs, &w[0], &w[1], &u[0], &u[1], &scheme, &mut transcript); - let ok = is_r1cs_satisfied(&r1cs,&folded_instance, &folded_witness, &scheme); + let ok = is_r1cs_satisfied(&r1cs, &folded_instance, &folded_witness, &scheme); if ok.is_err() { println!("{:?}", ok); diff --git a/nova/src/nifs/nifs_verifier.rs b/nova/src/nifs/nifs_verifier.rs index ccf9087..e39f8ad 100644 --- a/nova/src/nifs/nifs_verifier.rs +++ b/nova/src/nifs/nifs_verifier.rs @@ -1,15 +1,13 @@ +use crate::nifs::{FInstance, NIFSProof, NIFS, R1CS}; +use crate::transcript::Transcript; +use crate::utils::{to_f_matrix, to_f_vec}; use ark_ff::PrimeField; -use sha2::Digest; use kzg::commitment::KzgCommitment; use kzg::scheme::KzgScheme; use kzg::types::ScalarField; -use crate::nifs::{FInstance, NIFS, R1CS, NIFSProof}; -use crate::transcript::Transcript; -use crate::utils::{to_f_matrix, to_f_vec}; - - -impl NIFS { +use sha2::Digest; +impl NIFS { /// NIFS.V generate the folded instance. pub fn verifier( r: ScalarField, @@ -29,20 +27,15 @@ impl NIFS { fi3: &FInstance, // folded instance. com_t: &KzgCommitment, scheme: &KzgScheme, - transcript: &mut Transcript + transcript: &mut Transcript, ) -> Result<(), String> { - // verify challenge. let mut res = Self::verify_challenge(proof.r, fi1.u, fi2.u, com_t, transcript); - if res.is_err() { - return res; - } + res.as_ref()?; // verify opening. res = Self::verify_opening(proof, fi3, scheme, transcript); - if res.is_err() { - return res; - } + res.as_ref()?; Ok(()) } @@ -53,18 +46,18 @@ impl NIFS { fi1_u: ScalarField, fi2_u: ScalarField, com_t: &KzgCommitment, - transcript: &mut Transcript + transcript: &mut Transcript, ) -> Result<(), String> { // Recreate challenge r transcript.feed_scalar_num(fi1_u); transcript.feed_scalar_num(fi2_u); - transcript.feed(&com_t); + transcript.feed(com_t); let [new_r] = transcript.generate_challenges(); // Verify that proof.r = Transcript(fi1.u, fi2.u, cmT) if new_r != r { - return Err(String::from("Verify: Error in computing random r")) + return Err(String::from("Verify: Error in computing random r")); } Ok(()) @@ -75,14 +68,16 @@ impl NIFS { proof: &NIFSProof, fi3: &FInstance, // folded instance. scheme: &KzgScheme, - transcript: &mut Transcript + transcript: &mut Transcript, ) -> Result<(), String> { transcript.feed(&fi3.com_e); transcript.feed(&fi3.com_w); // Verify Opening_point = Transcript(fi1.cmE, fi1.cmW) let [opening_point] = transcript.generate_challenges(); if opening_point != proof.opening_point { - return Err(String::from("Verify: Error in computing random opening point")); + return Err(String::from( + "Verify: Error in computing random opening point", + )); } // Verify opening @@ -104,19 +99,19 @@ pub fn gen_test_values(inputs: Vec) -> (R1CS, Vec(vec![ + let a = to_f_matrix::(&vec![ vec![1, 0, 0, 0, 0, 0], vec![0, 1, 0, 0, 0, 0], vec![1, 0, 1, 0, 0, 0], vec![0, 0, 0, 1, 0, 5], ]); - let b = to_f_matrix::(vec![ + let b = to_f_matrix::(&vec![ vec![1, 0, 0, 0, 0, 0], vec![1, 0, 0, 0, 0, 0], vec![0, 0, 0, 0, 0, 1], vec![0, 0, 0, 0, 0, 1], ]); - let c = to_f_matrix::(vec![ + let c = to_f_matrix::(&vec![ vec![0, 1, 0, 0, 0, 0], vec![0, 0, 1, 0, 0, 0], vec![0, 0, 0, 1, 0, 0], @@ -129,16 +124,22 @@ pub fn gen_test_values(inputs: Vec) -> (R1CS, Vec(vec![ input, - input * input, // x^2 - input * input * input, // x^2 * x - input * input * input + input, // x^3 + x + input * input, // x^2 + input * input * input, // x^2 * x + input * input * input + input, // x^3 + x ]); w.push(w_i.clone()); - let x_i = to_f_vec::(vec![input * input * input + input + 5]); // output: x^3 + x + 5 + let x_i = to_f_vec::(vec![input * input * input + input + 5]); // output: x^3 + x + 5 x.push(x_i.clone()); } - let r1cs = R1CS:: { matrix_a: a, matrix_b: b, matrix_c: c, num_io: 1, num_vars: 4 }; + let r1cs = R1CS:: { + matrix_a: a, + matrix_b: b, + matrix_c: c, + num_io: 1, + num_vars: 4, + }; (r1cs, w, x) } @@ -146,15 +147,19 @@ pub fn gen_test_values(inputs: Vec) -> (R1CS, Vec::prove(r, &p_folded_witness, &p_folded_instance, &scheme, &mut prover_transcript); + let (p_folded_witness, p_folded_instance, com_t, r) = NIFS::prover( + &r1cs, + &fw1, + &fw2, + &fi1, + &fi2, + &scheme, + &mut prover_transcript, + ); + + let proof = NIFS::::prove( + r, + &p_folded_witness, + &p_folded_instance, + &scheme, + &mut prover_transcript, + ); let v_folded_instance = NIFS::::verifier(r, &fi1, &fi2, &com_t); - let result = NIFS::::verify(&proof, &fi1, &fi2, &v_folded_instance, &com_t, &scheme, &mut verifier_transcript); + let result = NIFS::::verify( + &proof, + &fi1, + &fi2, + &v_folded_instance, + &com_t, + &scheme, + &mut verifier_transcript, + ); println!("{:?}", result); assert!(result.is_ok()); } -} \ No newline at end of file +} diff --git a/nova/src/r1cs/mod.rs b/nova/src/r1cs/mod.rs index cb3b8e5..37be303 100644 --- a/nova/src/r1cs/mod.rs +++ b/nova/src/r1cs/mod.rs @@ -1,8 +1,10 @@ -use ark_ff::{PrimeField, Zero, One}; +use crate::utils::{ + hadamard_product, matrix_vector_product, vec_add, vec_equal, vector_elem_product, +}; +use ark_ff::{One, PrimeField, Zero}; use kzg::commitment::KzgCommitment; use kzg::scheme::KzgScheme; use kzg::types::ScalarField; -use crate::utils::{hadamard_product, matrix_vector_product, vec_add, vec_equal, vector_elem_product}; /// Create R1CS structure #[derive(Clone)] @@ -24,7 +26,6 @@ pub struct FInstance { pub x: Vec, } - /// Create Committed Relaxed FWitness with KZG commitment /// Todo: Need to implement a general-curve commitment #[derive(Debug, Clone)] @@ -36,11 +37,11 @@ pub struct FWitness { } #[allow(dead_code)] impl FWitness { - pub fn new(w: &Vec, len: usize) -> Self { + pub fn new(w: &[ScalarField], len: usize) -> Self { FWitness { e: vec![ScalarField::zero(); len], // rE: ScalarField::rand(&mut rand::thread_rng()), - w: w.clone(), + w: w.clone().into(), // rW: ScalarField::rand(&mut rand::thread_rng()), } } @@ -54,7 +55,7 @@ impl FWitness { } /// Commit a witness into its corresponding instance. - pub fn commit(&self, scheme: &KzgScheme, x: &Vec) -> FInstance { + pub fn commit(&self, scheme: &KzgScheme, x: &[ScalarField]) -> FInstance { let com_e = scheme.commit_vector(&self.e); // cE.0 = cE.0.mul(self.rE).into_affine(); let com_w = scheme.commit_vector(&self.w); @@ -64,7 +65,7 @@ impl FWitness { com_e, u: ScalarField::one(), com_w, - x: x.clone(), + x: x.clone().into(), } } } @@ -74,11 +75,11 @@ impl FWitness { pub fn create_trivial_pair( x_len: usize, w_len: usize, - scheme: &KzgScheme) --> (FWitness, FInstance){ + scheme: &KzgScheme, +) -> (FWitness, FInstance) { let trivial_x = vec![ScalarField::from(0); x_len]; let trivial_witness = FWitness::new_trivial_witness(w_len); - let trivial_instance = trivial_witness.commit(&scheme, &trivial_x); + let trivial_instance = trivial_witness.commit(scheme, &trivial_x); (trivial_witness, trivial_instance) } @@ -89,7 +90,7 @@ pub fn is_r1cs_satisfied( r1cs: &R1CS, f_instance: &FInstance, f_witness: &FWitness, - scheme: &KzgScheme + scheme: &KzgScheme, ) -> Result<(), String> { if r1cs.num_vars != f_witness.w.len() { return Err(String::from("Witness does not match with matrices")); @@ -114,30 +115,33 @@ pub fn is_r1cs_satisfied( let res_eq = vec_equal(&left_side, &right_side); // check whether Instance satisfies Witness - let res_com = (f_instance.com_w == scheme.commit_vector(&f_witness.w)) && (f_instance.com_e == scheme.commit_vector(&f_witness.e)); + let res_com = (f_instance.com_w == scheme.commit_vector(&f_witness.w)) + && (f_instance.com_e == scheme.commit_vector(&f_witness.e)); if res_com && res_eq { Ok(()) } else { - return Err(String::from("Instance does not satisfy the Witness.")) + Err(String::from("Instance does not satisfy the Witness.")) } - } - #[cfg(test)] mod tests { + use crate::nifs::nifs_verifier::gen_test_values; + use crate::r1cs::{is_r1cs_satisfied, FInstance, FWitness}; use kzg::scheme::KzgScheme; use kzg::srs::Srs; use kzg::types::ScalarField; - use crate::nifs::nifs_verifier::gen_test_values; - use crate::r1cs::{FInstance, FWitness, is_r1cs_satisfied}; #[test] pub fn test_r1cs_satisfaction_condition() { // generate R1CS, witnesses and public input, output. let (r1cs, witnesses, x) = gen_test_values::(vec![3]); - let (matrix_a, _, _) = (r1cs.matrix_a.clone(), r1cs.matrix_b.clone(), r1cs.matrix_c.clone()); + let (matrix_a, _, _) = ( + r1cs.matrix_a.clone(), + r1cs.matrix_b.clone(), + r1cs.matrix_c.clone(), + ); // Trusted setup let domain_size = witnesses[0].len() + x[0].len() + 1; @@ -145,14 +149,21 @@ mod tests { let scheme = KzgScheme::new(srs); // Generate witnesses and instances - let w: Vec = witnesses.iter().map(|witness| FWitness::new(witness, matrix_a.len())).collect(); - let u: Vec = w.iter().zip(x).map(|(w, x)| w.commit(&scheme, &x)).collect(); - - let ok = is_r1cs_satisfied(&r1cs,&u[0], &w[0], &scheme); + let w: Vec = witnesses + .iter() + .map(|witness| FWitness::new(witness, matrix_a.len())) + .collect(); + let u: Vec = w + .iter() + .zip(x) + .map(|(w, x)| w.commit(&scheme, &x)) + .collect(); + + let ok = is_r1cs_satisfied(&r1cs, &u[0], &w[0], &scheme); if ok.is_err() { println!("{:?}", ok); } assert!(ok.is_ok()); } -} \ No newline at end of file +} diff --git a/nova/src/transcript.rs b/nova/src/transcript.rs index feaa7fa..6014387 100644 --- a/nova/src/transcript.rs +++ b/nova/src/transcript.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use ark_bls12_381::Fr; -use ark_ff::{UniformRand}; +use ark_ff::UniformRand; use ark_serialize::{CanonicalSerialize, Write}; use rand::rngs::StdRng; use rand::SeedableRng; @@ -81,8 +81,7 @@ impl Transcript { pub fn feed_scalar_num(&mut self, num: ScalarField) { let mut hasher = T::default(); hasher.update(self.data.take().unwrap_or_default()); - num - .serialize_uncompressed(HashMarshaller(&mut hasher)) + num.serialize_uncompressed(HashMarshaller(&mut hasher)) .expect("HashMarshaller::flush should be infallible!"); self.data = Some(hasher.finalize().to_vec()); self.generated = false; @@ -136,11 +135,10 @@ impl<'a, H: Digest> Write for HashMarshaller<'a, H> { mod tests { use std::ops::Mul; + use ark_bls12_381::Fr; use ark_ec::{AffineRepr, CurveGroup}; - use sha2::Sha256; use kzg::types::G1Point; - use ark_bls12_381::Fr; - + use sha2::Sha256; use super::*; @@ -153,8 +151,7 @@ mod tests { Transcript::::from_commitment(&commitments1).generate_challenges(); let commitments2: [KzgCommitment; 1] = [commitment2.clone()]; - let [b] = - Transcript::::from_commitment(&commitments2).generate_challenges(); + let [b] = Transcript::::from_commitment(&commitments2).generate_challenges(); assert_ne!(a, b, "should be different"); let commitments3: [KzgCommitment; 2] = [commitment1.clone(), commitment2.clone()]; @@ -170,8 +167,8 @@ mod tests { let a = ScalarField::from(15); let b = ScalarField::from(20); - let [x, y, z] = Transcript::::from_scalar_number(&vec![a, b]).generate_challenges(); - let [x1, y1, z1] = Transcript::::from_scalar_number(&vec![a, b]).generate_challenges(); + let [x, y, z] = Transcript::::from_scalar_number(&[a, b]).generate_challenges(); + let [x1, y1, z1] = Transcript::::from_scalar_number(&[a, b]).generate_challenges(); assert_eq!(x, x1, "should be equal"); assert_eq!(y, y1, "should be equal"); @@ -182,8 +179,10 @@ mod tests { fn transcript_test_02() { let a = ScalarField::from(15); let b = ScalarField::from(20); - let commitment1 = KzgCommitment(G1Point::generator().mul(ScalarField::from(1)).into_affine()); - let commitment2 = KzgCommitment(G1Point::generator().mul(ScalarField::from(2)).into_affine()); + let commitment1 = + KzgCommitment(G1Point::generator().mul(ScalarField::from(1)).into_affine()); + let commitment2 = + KzgCommitment(G1Point::generator().mul(ScalarField::from(2)).into_affine()); let mut ts1 = Transcript::::default(); let mut ts2 = Transcript::::default(); @@ -201,7 +200,6 @@ mod tests { assert_eq!(x, x1, "should be equal"); assert_eq!(y, y1, "should be equal"); assert_eq!(z, z1, "should be equal"); - } #[test] diff --git a/nova/src/utils.rs b/nova/src/utils.rs index 43b74f8..296760d 100644 --- a/nova/src/utils.rs +++ b/nova/src/utils.rs @@ -11,7 +11,7 @@ use ark_ff::PrimeField; /// /// A vector resulting from the product of the matrix and the vector. #[allow(dead_code)] -pub fn matrix_vector_product(matrix: &Vec>, z: &Vec) -> Vec { +pub fn matrix_vector_product(matrix: &[Vec], z: &[F]) -> Vec { let mut r: Vec = vec![F::zero(); matrix.len()]; for i in 0..matrix.len() { for j in 0..matrix[i].len() { @@ -32,8 +32,7 @@ pub fn matrix_vector_product(matrix: &Vec>, z: &Vec) -> /// /// A vector resulting from the Hadamard product of the two input vectors. #[allow(dead_code)] -pub fn hadamard_product(a: &Vec, b: &Vec) -> Vec { - +pub fn hadamard_product(a: &[F], b: &[F]) -> Vec { let mut r: Vec = vec![F::zero(); a.len()]; for i in 0..a.len() { r[i] = a[i] * b[i]; @@ -52,7 +51,7 @@ pub fn hadamard_product(a: &Vec, b: &Vec) -> Vec { /// /// A vector resulting from multiplying each element of `a` by `u`. #[allow(dead_code)] -pub fn vector_elem_product(a: &Vec, u: F) -> Vec { +pub fn vector_elem_product(a: &[F], u: F) -> Vec { let mut r: Vec = vec![F::zero(); a.len()]; for i in 0..a.len() { r[i] = a[i] * u; @@ -71,7 +70,7 @@ pub fn vector_elem_product(a: &Vec, u: F) -> Vec { /// /// A vector resulting from subtracting `b` from `a`. #[allow(dead_code)] -pub fn vec_sub(a: &Vec, b: &Vec) -> Vec { +pub fn vec_sub(a: &[F], b: &[F]) -> Vec { assert_eq!(a.len(), b.len()); let mut r: Vec = vec![F::zero(); a.len()]; for i in 0..a.len() { @@ -91,7 +90,7 @@ pub fn vec_sub(a: &Vec, b: &Vec) -> Vec { /// /// A vector resulting from adding `a` and `b`. #[allow(dead_code)] -pub fn vec_add(a: &Vec, b: &Vec) -> Vec { +pub fn vec_add(a: &[F], b: &[F]) -> Vec { assert_eq!(a.len(), b.len()); let mut r: Vec = vec![F::zero(); a.len()]; for i in 0..a.len() { @@ -111,7 +110,7 @@ pub fn vec_add(a: &Vec, b: &Vec) -> Vec { /// /// `true` if `a` and `b` are equal, `false` otherwise. #[allow(dead_code)] -pub fn vec_equal(a: &Vec, b: &Vec) -> bool { +pub fn vec_equal(a: &[F], b: &[F]) -> bool { if a.len() != b.len() { return false; } @@ -134,7 +133,7 @@ pub fn vec_equal(a: &Vec, b: &Vec) -> bool { /// /// A matrix represented as a vector of vectors of `F` values. #[allow(dead_code)] -pub fn to_f_matrix (matrix: Vec>) -> Vec> { +pub fn to_f_matrix(matrix: &[Vec]) -> Vec> { let mut r: Vec> = vec![Vec::new(); matrix.len()]; for i in 0..matrix.len() { r[i] = vec![F::zero(); matrix[i].len()];