diff --git a/ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs b/ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs index 6d084c3f2..08aa7b5a0 100644 --- a/ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs +++ b/ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs @@ -1,12 +1,9 @@ -use std::{ - iter::zip, - ops::{Add, Sub}, -}; +use std::ops::{Add, Sub}; -use generic_array::{ArrayLength, GenericArray}; -use typenum::{Diff, Sum, U1}; +use generic_array::ArrayLength; +use typenum::{Sum, U1}; -use super::prover::{TwoNMinusOne, ZeroKnowledgeProof}; +use super::prover::{TwoNMinusOne, TwoNPlusOne, ZeroKnowledgeProof}; use crate::{ ff::PrimeField, protocol::ipa_prf::malicious_security::lagrange::{ @@ -60,14 +57,14 @@ where // Reveal `b_share` to one another to reconstruct `b` and check if `b = 0`. If the check doesn't pass, abort. let b_share = sum_share - self.out_share; - let denominator_p = CanonicalLagrangeDenominator::::new(); - let lagrange_table_p_r = LagrangeTable::::new(&denominator_p, &r); + let denominator_p_or_q = CanonicalLagrangeDenominator::::new(); + let lagrange_table_p_or_q_r = LagrangeTable::::new(&denominator_p_or_q, &r); let p_or_q_r = (0..s) .map(|i| { let start = i * λ::USIZE; let end = start + λ::USIZE; let p_or_q = &self.u_or_v[start..end]; - lagrange_table_p_r.eval(p_or_q)[0] + lagrange_table_p_or_q_r.eval(p_or_q)[0] }) .collect(); ( @@ -78,11 +75,40 @@ where }, ) } + + pub fn verify_final_proof<λ>( + &self, + zkp: ZeroKnowledgeProof>, + r: F, + p_or_q_0: F, + ) -> (F, F) + where + λ: ArrayLength + Add + Add, + <λ as Add>::Output: Add, + <<λ as Add>::Output as Add>::Output: ArrayLength, + <λ as Add>::Output: ArrayLength, + { + assert_eq!(self.u_or_v.len(), λ::USIZE); // We should pad with zeroes eventually + + // We need a table of size `λ + 1` since we add a random point at x=0 + let denominator = CanonicalLagrangeDenominator::>::new(); + let lagrange_table = LagrangeTable::, U1>::new(&denominator, &r); + + let mut p_or_q = vec![p_or_q_0]; + p_or_q.extend_from_slice(&self.u_or_v); + let p_or_q_extrapolated = lagrange_table.eval(&p_or_q)[0]; + + let denominator_g = CanonicalLagrangeDenominator::>::new(); + let lagrange_table_g = LagrangeTable::, U1>::new(&denominator_g, &r); + let out_share = lagrange_table_g.eval(&zkp.g)[0]; + + (p_or_q_extrapolated, out_share) + } } #[cfg(all(test, unit_test))] mod test { - use typenum::{U4, U7}; + use typenum::{U2, U4, U5, U7}; use super::ProofVerifier; use crate::{ @@ -91,7 +117,7 @@ mod test { }; #[test] - fn sample_proof() { + fn sample_proof_u() { const U_1: [u128; 32] = [ 0, 30, 0, 16, 0, 1, 0, 15, 0, 0, 0, 16, 0, 30, 0, 16, 29, 1, 1, 15, 0, 0, 1, 15, 2, 30, 30, 16, 0, 0, 30, 16, @@ -110,7 +136,13 @@ mod test { const EXPECTED_G_R_2: u128 = 13; const EXPECTED_B_2: u128 = 0; + const ZKP_3: [u128; 5] = [21, 1, 6, 25, 1]; const U_3: [u128; 2] = [3, 3]; + const R_3: u128 = 30; + const P_RANDOM_WEIGHT: u128 = 12; + + const EXPECTED_P_FINAL: u128 = 30; + const EXPECTED_G_R_FINAL: u128 = 0; let pv_1: ProofVerifier = ProofVerifier::new( U_1.into_iter() @@ -140,5 +172,87 @@ mod test { U_3, ); assert_eq!(pv_3.out_share.as_u128(), EXPECTED_G_R_2); + + // final iteration + let zkp_3 = ZeroKnowledgeProof::::new(ZKP_3.map(|x| Fp31::try_from(x).unwrap())); + + let (p_final, out_share) = pv_3.verify_final_proof::( + zkp_3, + Fp31::try_from(R_3).unwrap(), + Fp31::try_from(P_RANDOM_WEIGHT).unwrap(), + ); + + assert_eq!(p_final.as_u128(), EXPECTED_P_FINAL); + assert_eq!(out_share.as_u128(), EXPECTED_G_R_FINAL); + } + + #[test] + fn sample_proof_v() { + const V_1: [u128; 32] = [ + 0, 0, 0, 30, 0, 0, 0, 1, 30, 30, 30, 30, 0, 0, 30, 30, 0, 30, 0, 30, 0, 0, 0, 1, 0, 0, + 1, 1, 0, 0, 1, 1, + ]; + const OUT_1: u128 = 0; + const ZKP_1: [u128; 7] = [0, 30, 16, 13, 25, 3, 6]; + const R_1: u128 = 22; + + const EXPECTED_G_R_1: u128 = 10; + const EXPECTED_B_1: u128 = 28; + + const V_2: [u128; 8] = [10, 21, 30, 28, 15, 21, 3, 3]; + const ZKP_2: [u128; 7] = [1, 12, 29, 30, 7, 7, 3]; + const R_2: u128 = 17; + + const EXPECTED_G_R_2: u128 = 12; + const EXPECTED_B_2: u128 = 0; + + const ZKP_3: [u128; 5] = [22, 14, 4, 20, 16]; + const V_3: [u128; 2] = [5, 24]; + const R_3: u128 = 30; + const Q_RANDOM_WEIGHT: u128 = 1; + + const EXPECTED_Q_FINAL: u128 = 12; + const EXPECTED_G_R_FINAL: u128 = 19; + + let pv_1: ProofVerifier = ProofVerifier::new( + V_1.into_iter() + .map(|x| Fp31::try_from(x).unwrap()) + .collect(), + Fp31::try_from(OUT_1).unwrap(), + ); + + // first iteration + let zkp_1 = ZeroKnowledgeProof::::new(ZKP_1.map(|x| Fp31::try_from(x).unwrap())); + + let (b_share_1, pv_2) = pv_1.verify_proof::(zkp_1, Fp31::try_from(R_1).unwrap()); + assert_eq!(b_share_1.as_u128(), EXPECTED_B_1); + assert_eq!( + pv_2.u_or_v.iter().map(Fp31::as_u128).collect::>(), + V_2, + ); + assert_eq!(pv_2.out_share.as_u128(), EXPECTED_G_R_1); + + // second iteration + let zkp_2 = ZeroKnowledgeProof::::new(ZKP_2.map(|x| Fp31::try_from(x).unwrap())); + + let (b_share_2, pv_3) = pv_2.verify_proof::(zkp_2, Fp31::try_from(R_2).unwrap()); + assert_eq!(b_share_2.as_u128(), EXPECTED_B_2); + assert_eq!( + pv_3.u_or_v.iter().map(Fp31::as_u128).collect::>(), + V_3, + ); + assert_eq!(pv_3.out_share.as_u128(), EXPECTED_G_R_2); + + // final iteration + let zkp_3 = ZeroKnowledgeProof::::new(ZKP_3.map(|x| Fp31::try_from(x).unwrap())); + + let (q_final, out_share) = pv_3.verify_final_proof::( + zkp_3, + Fp31::try_from(R_3).unwrap(), + Fp31::try_from(Q_RANDOM_WEIGHT).unwrap(), + ); + + assert_eq!(q_final.as_u128(), EXPECTED_Q_FINAL); + assert_eq!(out_share.as_u128(), EXPECTED_G_R_FINAL); } }