Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Verifying ZKPs part one #983

Merged
merged 3 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ipa-core/src/protocol/ipa_prf/malicious_security/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod lagrange;
pub mod prover;
pub mod verifier;
42 changes: 27 additions & 15 deletions ipa-core/src/protocol/ipa_prf/malicious_security/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
ops::{Add, Sub},
};

use generic_array::{ArrayLength, GenericArray};
use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray};
use typenum::{Diff, Sum, U1};

use crate::{
Expand All @@ -14,7 +14,22 @@ use crate::{
};

pub struct ZeroKnowledgeProof<F: PrimeField, N: ArrayLength> {
g: GenericArray<F, N>,
pub g: GenericArray<F, N>,
}

impl<F, N> ZeroKnowledgeProof<F, N>
where
F: PrimeField,
N: ArrayLength,
{
pub fn new<I>(g: I) -> Self
where
I: IntoIterator<Item = F>,
{
ZeroKnowledgeProof {
g: g.into_iter().collect(),
}
}
}

#[derive(Debug)]
Expand All @@ -23,8 +38,8 @@ pub struct ProofGenerator<F: PrimeField> {
v: Vec<F>,
}

type TwoNMinusOne<N> = Diff<Sum<N, N>, U1>;
type TwoNPlusOne<N> = Sum<Sum<N, N>, U1>;
pub type TwoNMinusOne<N> = Diff<Sum<N, N>, U1>;
pub type TwoNPlusOne<N> = Sum<Sum<N, N>, U1>;

///
/// Distributed Zero Knowledge Proofs algorithm drawn from
Expand Down Expand Up @@ -84,13 +99,11 @@ where
zip(p, q)
.map(|(a, b)| *a * *b)
.chain(zip(p_extrapolated, q_extrapolated).map(|(a, b)| a * b))
.collect::<GenericArray<F, _>>()
});
let proof = ZeroKnowledgeProof {
g: extrapolated_points
.reduce(|acc, pts| zip(acc, pts).map(|(a, b)| a + b).collect())
.unwrap(),
};
let proof = ZeroKnowledgeProof::new(extrapolated_points.fold(
GenericArray::<F, TwoNMinusOne<λ>>::generate(|_| F::ZERO),
|acc, pts| zip(acc, pts).map(|(a, b)| a + b).collect(),
));
(proof, next_proof_generator)
}

Expand Down Expand Up @@ -118,14 +131,13 @@ where
let p_extrapolated = lagrange_table.eval(&p);
let q_extrapolated = lagrange_table.eval(&q);

ZeroKnowledgeProof {
g: zip(
ZeroKnowledgeProof::new(
zip(
p.into_iter().chain(p_extrapolated),
q.into_iter().chain(q_extrapolated),
)
.map(|(a, b)| a * b)
.collect(),
}
.map(|(a, b)| a * b),
)
}
}

Expand Down
258 changes: 258 additions & 0 deletions ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
use std::ops::{Add, Sub};

use generic_array::ArrayLength;
use typenum::{Sum, U1};

use super::prover::{TwoNMinusOne, TwoNPlusOne, ZeroKnowledgeProof};
use crate::{
ff::PrimeField,
protocol::ipa_prf::malicious_security::lagrange::{
CanonicalLagrangeDenominator, LagrangeTable,
},
};

pub struct ProofVerifier<F: PrimeField> {
u_or_v: Vec<F>,
out_share: F,
}

///
/// Distributed Zero Knowledge Proofs algorithm drawn from
/// `https://eprint.iacr.org/2023/909.pdf`
///
#[allow(non_camel_case_types)]
impl<F> ProofVerifier<F>
where
F: PrimeField,
{
pub fn new(u_or_v: Vec<F>, out_share: F) -> Self {
Self { u_or_v, out_share }
}

pub fn verify_proof<λ: ArrayLength>(
&self,
zkp: &ZeroKnowledgeProof<F, TwoNMinusOne<λ>>,
r: F,
) -> (F, ProofVerifier<F>)
where
λ: ArrayLength + Add + Sub<U1>,
<λ as Add>::Output: Sub<U1>,
<<λ as Add>::Output as Sub<U1>>::Output: ArrayLength,
<λ as Sub<U1>>::Output: ArrayLength,
{
debug_assert_eq!(self.u_or_v.len() % λ::USIZE, 0); // We should pad with zeroes eventually

let s = self.u_or_v.len() / λ::USIZE;

assert!(
s > 1,
"When the output is this small, you should call `verify_final_proof`"

Check warning on line 49 in ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs

View check run for this annotation

Codecov / codecov/patch

ipa-core/src/protocol/ipa_prf/malicious_security/verifier.rs#L49

Added line #L49 was not covered by tests
);

let denominator_g = CanonicalLagrangeDenominator::<F, TwoNMinusOne<λ>>::new();
let lagrange_table_g = LagrangeTable::<F, TwoNMinusOne<λ>, U1>::new(&denominator_g, &r);
let g_r_share = lagrange_table_g.eval(&zkp.g)[0];
let sum_share = (0..λ::USIZE).fold(F::ZERO, |acc, i| acc + zkp.g[i]);

// 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_or_q = CanonicalLagrangeDenominator::<F, λ>::new();
let lagrange_table_p_or_q_r = LagrangeTable::<F, λ, U1>::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_or_q_r.eval(p_or_q)[0]
})
.collect();
(
b_share,
ProofVerifier {
u_or_v: p_or_q_r,
out_share: g_r_share,
},
)
}

pub fn verify_final_proof<λ>(
&self,
zkp: &ZeroKnowledgeProof<F, TwoNPlusOne<λ>>,
r: F,
p_or_q_0: F,
) -> (F, F)
where
λ: ArrayLength + Add + Add<U1>,
<λ as Add>::Output: Add<U1>,
<<λ as Add>::Output as Add<U1>>::Output: ArrayLength,
<λ as Add<U1>>::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::<F, Sum<λ, U1>>::new();
let lagrange_table = LagrangeTable::<F, Sum<λ, U1>, 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::<F, TwoNPlusOne<λ>>::new();
let lagrange_table_g = LagrangeTable::<F, TwoNPlusOne<λ>, 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::{U2, U4, U5, U7};

use super::ProofVerifier;
use crate::{
ff::{Fp31, U128Conversions},
protocol::ipa_prf::malicious_security::prover::ZeroKnowledgeProof,
};

#[test]
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,
];
const OUT_1: u128 = 27;
const ZKP_1: [u128; 7] = [0, 0, 13, 17, 11, 25, 7];
const R_1: u128 = 22;

const EXPECTED_G_R_1: u128 = 0;
const EXPECTED_B_1: u128 = 3;

const U_2: [u128; 8] = [0, 0, 26, 0, 7, 18, 24, 13];
const ZKP_2: [u128; 7] = [11, 25, 17, 9, 22, 23, 3];
const R_2: u128 = 17;

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<Fp31> = ProofVerifier::new(
U_1.into_iter()
.map(|x| Fp31::try_from(x).unwrap())
.collect(),
Fp31::try_from(OUT_1).unwrap(),
);

// first iteration
let zkp_1 = ZeroKnowledgeProof::<Fp31, U7>::new(ZKP_1.map(|x| Fp31::try_from(x).unwrap()));

let (b_share_1, pv_2) = pv_1.verify_proof::<U4>(&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::<Vec<_>>(),
U_2,
);
assert_eq!(pv_2.out_share.as_u128(), EXPECTED_G_R_1);

// second iteration
let zkp_2 = ZeroKnowledgeProof::<Fp31, U7>::new(ZKP_2.map(|x| Fp31::try_from(x).unwrap()));

let (b_share_2, pv_3) = pv_2.verify_proof::<U4>(&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::<Vec<_>>(),
U_3,
);
assert_eq!(pv_3.out_share.as_u128(), EXPECTED_G_R_2);

// final iteration
let zkp_3 = ZeroKnowledgeProof::<Fp31, U5>::new(ZKP_3.map(|x| Fp31::try_from(x).unwrap()));

let (p_final, out_share) = pv_3.verify_final_proof::<U2>(
&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<Fp31> = 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::<Fp31, U7>::new(ZKP_1.map(|x| Fp31::try_from(x).unwrap()));

let (b_share_1, pv_2) = pv_1.verify_proof::<U4>(&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::<Vec<_>>(),
V_2,
);
assert_eq!(pv_2.out_share.as_u128(), EXPECTED_G_R_1);

// second iteration
let zkp_2 = ZeroKnowledgeProof::<Fp31, U7>::new(ZKP_2.map(|x| Fp31::try_from(x).unwrap()));

let (b_share_2, pv_3) = pv_2.verify_proof::<U4>(&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::<Vec<_>>(),
V_3,
);
assert_eq!(pv_3.out_share.as_u128(), EXPECTED_G_R_2);

// final iteration
let zkp_3 = ZeroKnowledgeProof::<Fp31, U5>::new(ZKP_3.map(|x| Fp31::try_from(x).unwrap()));

let (q_final, out_share) = pv_3.verify_final_proof::<U2>(
&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);
}
}
Loading