diff --git a/kzg_prover/src/circuits/tests.rs b/kzg_prover/src/circuits/tests.rs index e0690a78..3c68e155 100644 --- a/kzg_prover/src/circuits/tests.rs +++ b/kzg_prover/src/circuits/tests.rs @@ -80,7 +80,8 @@ mod test { valid_prover.assert_satisfied(); // 1. Proving phase - // The Custodian generates the ZK proof + // The Custodian generates the ZK-SNARK Halo2 proof that commits to the user entry values in advice polynomials + // and also range-checks the user balance values let (zk_snark_proof, advice_polys, omega) = full_prover(¶ms, &pk, circuit.clone(), vec![vec![]]); @@ -88,19 +89,19 @@ mod test { // (The first column is the user IDs) let balance_column_range = 1..N_CURRENCIES + 1; - // The Custodian makes multi-openings at x = 0 for the Verifier - let grand_sums_multi_proof = open_grand_sums::( + // The Custodian makes a batch opening proof of all user balance polynomials at x = 0 for the Verifier + let grand_sums_batch_proof = open_grand_sums::( &advice_polys.advice_polys, &advice_polys.advice_blinds, ¶ms, balance_column_range, ); - // The Custodian creates a KZG multi-proof of the 4th user ID & balances inclusion + // The Custodian creates a KZG batch proof of the 4th user ID & balances inclusion let user_index = 3_u16; let column_range = 0..N_CURRENCIES + 1; - let openings_multi_proof = open_user_points::( + let openings_batch_proof = open_user_points::( &advice_polys.advice_polys, &advice_polys.advice_blinds, ¶ms, @@ -122,12 +123,12 @@ mod test { // Both the Custodian and the Verifier know what column range are the balance columns let balance_column_range = 1..N_CURRENCIES + 1; - // The Custodian communicates the KZG multi-opening transcript to the Verifier - // The Verifier verifies the KZG multi-opening and calculates the grand sums + // The Custodian communicates the KZG batch opening transcript to the Verifier + // The Verifier verifies the KZG batch opening and calculates the grand sums let (verified, grand_sum) = verify_grand_sum_openings::( ¶ms, &zk_snark_proof, - grand_sums_multi_proof, + grand_sums_batch_proof, poly_degree, balance_column_range, ); @@ -143,7 +144,7 @@ mod test { let (inclusion_verified, id_and_balance_values) = verify_user_inclusion::( ¶ms, &zk_snark_proof, - &openings_multi_proof, + &openings_batch_proof, column_range, omega, user_index, @@ -164,7 +165,74 @@ mod test { ); } } + } + + #[test] + fn test_invalid_univariate_grand_sum_proof() { + const N_USERS: usize = 16; + + // Initialize an empty circuit + let circuit = UnivariateGrandSum::::init_empty(); + + // Generate a universal trusted setup for testing purposes. + // + // The verification key (vk) and the proving key (pk) are then generated. + // An empty circuit is used here to emphasize that the circuit inputs are not relevant when generating the keys. + // Important: The dimensions of the circuit used to generate the keys must match those of the circuit used to generate the proof. + // In this case, the dimensions are represented by the number fo users. + let (params, pk, vk) = generate_setup_artifacts(K, None, circuit).unwrap(); + + // Only now we can instantiate the circuit with the actual inputs + let path = "src/csv/entry_16.csv"; + + let mut entries: Vec> = vec![Entry::init_empty(); N_USERS]; + let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES]; + + let _ = + parse_csv_to_entries::<&str, N_CURRENCIES, N_BYTES>(path, &mut entries, &mut cryptos) + .unwrap(); + + // Calculate total for all entry columns + let mut csv_total: Vec = vec![BigUint::from(0u32); N_CURRENCIES]; + + for entry in &entries { + for (i, balance) in entry.balances().iter().enumerate() { + csv_total[i] += balance; + } + } + let circuit = UnivariateGrandSum::::init(entries.to_vec()); + + let valid_prover = MockProver::run(K, &circuit, vec![vec![]]).unwrap(); + + valid_prover.assert_satisfied(); + + // 1. Proving phase + // The Custodian generates the ZK proof + let (zk_snark_proof, advice_polys, omega) = + full_prover(¶ms, &pk, circuit.clone(), vec![vec![]]); + + // The Custodian creates a KZG batch proof of the 4th user ID & balances inclusion + let user_index = 3_u16; + + let column_range = 0..N_CURRENCIES + 1; + let openings_batch_proof = open_user_points::( + &advice_polys.advice_polys, + &advice_polys.advice_blinds, + ¶ms, + column_range, + omega, + user_index, + ); + + // 2. Verification phase + // The Verifier verifies the ZK proof + assert!(full_verifier(¶ms, &vk, &zk_snark_proof, vec![vec![]])); + + // The Verifier is able to independently extract the omega from the verification key + let omega = pk.get_vk().get_domain().get_omega(); + + // Both the Custodian and the Verifier know what column range are the balance columns let balance_column_range = 1..N_CURRENCIES + 1; // Test failure case with the wrong group generator @@ -173,18 +241,20 @@ mod test { let (balances_verified, _) = verify_user_inclusion::( ¶ms, &zk_snark_proof, - &openings_multi_proof, + &openings_batch_proof, balance_column_range, bad_omega, user_index, ); //The verification should fail assert!(!balances_verified); + + // TODO add more negative tests } #[cfg(feature = "dev-graph")] #[test] - fn print_solvency_v2_circuit() { + fn print_univariate_grand_sum_circuit() { use plotters::prelude::*; let path = "src/csv/entry_16.csv"; diff --git a/kzg_prover/src/circuits/utils.rs b/kzg_prover/src/circuits/utils.rs index 220e3aa3..a07c6f43 100644 --- a/kzg_prover/src/circuits/utils.rs +++ b/kzg_prover/src/circuits/utils.rs @@ -118,8 +118,20 @@ pub fn full_prover>( (proof, advice_polys, omega) } -/// Creates the univariate polynomial grand sum openings -/// The challenge is set to zero to obtain the constant term of the polynomials +/// Creates the univariate polynomial grand sum openings. +/// The polynomials are evaluated at X = 0 to obtain their constant term. +/// +/// # Arguments +/// +/// * `advice_polys` - the advice polynomials +/// * `advice_blinds` - the advice polynomials blinds +/// * `params` - the KZG parameters +/// * `balance_column_range` - the range of the balance columns used to calculate the grand sums +/// +/// # Returns +/// +/// * `Vec` - the KZG batch proof containing the quotient polynomial commitments +/// and the evaluations of the polynomials at X = 0 pub fn open_grand_sums( advice_polys: &[Polynomial], advice_blinds: &[Blind], @@ -141,6 +153,22 @@ pub fn open_grand_sums( .to_vec() } +/// Creates a KZG batch proof for the `advice_polys` polynomial openings +/// at a point corresponding to the `user_index` +/// +/// # Arguments +/// +/// * `advice_polys` - the advice polynomials +/// * `advice_blinds` - the advice polynomials blinds +/// * `params` - the KZG parameters +/// * `column_range` - the advice column range to be used for the proof +/// * `omega` - $\omega$, the generator of the $2^k$ order multiplicative subgroup used to interpolate the polynomials. +/// * `user_index` - the index of the user whose entry is being proven +/// +/// # Returns +/// +/// * `Vec` - the KZG batch proof containing the quotient polynomial commitments +/// and the evaluations of the polynomials at the point corresponding to the `user_index` pub fn open_user_points( advice_polys: &[Polynomial], advice_blinds: &[Blind], @@ -165,10 +193,24 @@ pub fn open_user_points( } /// Verifies the univariate polynomial grand sum openings +/// and calculates the grand sums +/// +/// # Arguments +/// +/// * `params` - the KZG parameters +/// * `zk_snark_proof` - the ZK-SNARK proof of the circuit whose advice columns contain the user balance polynomials +/// * `grand_sum_opening_batch_proof` - the KZG batch proof of the grand sum polynomials +/// * `polynomial_degree` - the degree of the polynomials +/// * `balance_column_range` - the range of the advice columns that represent user balances +/// +/// # Returns +/// +/// * `bool` - whether the grand sum openings are verified correctly +/// * `Vec` - the grand sums pub fn verify_grand_sum_openings( params: &ParamsKZG, zk_snark_proof: &[u8], - challenge_opening_multi_proof: Vec, + grand_sum_opening_batch_proof: Vec, polynomial_degree: u64, balance_column_range: Range, ) -> (bool, Vec) { @@ -194,7 +236,7 @@ pub fn verify_grand_sum_openings( N_CURRENCIES, >( params, - &challenge_opening_multi_proof, + &grand_sum_opening_batch_proof, Fp::zero(), &advice_commitments, ); @@ -208,11 +250,26 @@ pub fn verify_grand_sum_openings( ) } -pub fn verify_user_inclusion( +/// Verifies the KZG batch proof of the polynomial openings being the evaluations +/// of the advice polynomials at the point corresponding to the user index +/// +/// # Arguments +/// * `params` - the KZG parameters +/// * `zk_snark_proof` - the ZK-SNARK proof of the circuit whose advice columns contain the user entry polynomials +/// * `balance_opening_batch_proof` - the KZG batch proof of the user entry polynomials +/// * `column_range` - the range of the advice columns that represent user entry +/// * `omega` - $\omega$, the generator of the $2^k$ order multiplicative subgroup used to interpolate the polynomials. +/// * `user_index` - the index of the user whose entry is being proven +/// * `N_POINTS` - the size of the user entry being verified (e.g., 1 ID value + 4 balance values = 5) +/// +/// # Returns +/// * `bool` - whether the user entry openings are verified correctly +/// * `Vec` - the evaluations of the advice polynomials at the point corresponding to the user index +pub fn verify_user_inclusion( params: &ParamsKZG, zk_snark_proof: &[u8], - balance_opening_multi_proof: &[u8], - balance_column_range: Range, + balance_opening_batch_proof: &[u8], + column_range: Range, omega: Fp, user_index: u16, ) -> (bool, Vec) { @@ -221,10 +278,13 @@ pub fn verify_user_inclusion( //Read the commitment points for all the advice polynomials from the proof transcript and put them into a vector let mut advice_commitments = Vec::new(); - (0..N_CURRENCIES + balance_column_range.start).for_each(|_| { + for i in 0..column_range.end { let point = transcript.read_point().unwrap(); - advice_commitments.push(point); - }); + //Skip advice polynomial commitments before the desired range + if i >= column_range.start { + advice_commitments.push(point); + } + } let mut verification_results = Vec::::new(); @@ -234,10 +294,10 @@ pub fn verify_user_inclusion( Challenge255, Blake2bRead<_, _, Challenge255<_>>, AccumulatorStrategy<_>, - N_CURRENCIES, + N_POINTS, >( params, - balance_opening_multi_proof, + balance_opening_batch_proof, omega.pow_vartime([user_index as u64]), &advice_commitments, ); @@ -252,7 +312,19 @@ pub fn verify_user_inclusion( ) } -/// Creates a KZG multi-opening proof for the polynomial evaluations at a challenge +/// Creates a KZG batch proof for the polynomial evaluations at a challenge +/// +/// # Arguments +/// +/// * `params` - the KZG parameters +/// * `polynomials` - the polynomials to be opened +/// * `blinds` - the polynomials blinds +/// * `challenge` - the challenge at which the polynomials are evaluated +/// +/// # Returns +/// +/// * `Vec` containing the quotient polynomial commitments +/// and the evaluations of the polynomials at the challenge fn create_opening_proof_at_challenge< 'params, Scheme: CommitmentScheme, @@ -298,8 +370,22 @@ where transcript.finalize() } -/// Verifies a KZG proof for a polynomial evaluation at a challenge -pub fn verify_opening< +/// Verifies a KZG batch proof for a polynomial evaluation at a challenge +/// and returns the evaluations of the polynomials at the challenge +/// as well as the verification result +/// +/// # Arguments +/// +/// * `params` - the KZG parameters +/// * `proof` - the KZG batch proof +/// * `challenge` - the challenge at which the polynomials are evaluated +/// * `commitment_points` - the commitment points of the polynomials +/// +/// # Returns +/// +/// * `bool` - whether the proof is verified correctly +/// * `Vec` - the evaluations of the polynomials at the challenge +fn verify_opening< 'a, 'params, Scheme: CommitmentScheme,