Skip to content

Commit af58b0f

Browse files
Merge pull request #980 from private-attribution/random_challenge
Next part of the ZKPs
2 parents 3777d9f + 1ba4fda commit af58b0f

File tree

3 files changed

+187
-50
lines changed

3 files changed

+187
-50
lines changed

ipa-core/src/protocol/ipa_prf/malicious_security/lagrange.rs

+43-50
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1-
use std::fmt::Debug;
1+
use std::{borrow::Borrow, fmt::Debug};
22

33
use generic_array::{ArrayLength, GenericArray};
44
use typenum::{Unsigned, U1};
55

66
use crate::ff::{Field, PrimeField, Serializable};
77

8-
/// A degree `N-1` polynomial is stored as `N` points `(x,y)`
9-
/// where the "x coordinates" of the input points are `x_0` to `x_N` are `F::ZERO` to `(N-1)*F::ONE`
10-
/// Therefore, we only need to store the `y` coordinates.
11-
#[derive(Debug, PartialEq, Clone)]
12-
pub struct Polynomial<F: Field, N: ArrayLength> {
13-
y_coordinates: GenericArray<F, N>,
14-
}
15-
168
/// The Canonical Lagrange denominator is defined as the denominator of the Lagrange base polynomials
179
/// `https://en.wikipedia.org/wiki/Lagrange_polynomial`
1810
/// where the "x coordinates" of the input points are `x_0` to `x_N` are `F::ZERO` to `(N-1)*F::ONE`
@@ -99,16 +91,25 @@ where
9991
N: ArrayLength,
10092
M: ArrayLength,
10193
{
102-
/// This function uses the `LagrangeTable` to evaluate `polynomial` on the specified output "x coordinates"
103-
/// outputs the "y coordinates" such that `(x,y)` lies on `polynomial`
104-
pub fn eval(&self, polynomial: &Polynomial<F, N>) -> GenericArray<F, M> {
94+
/// This function uses the `LagrangeTable` to evaluate `polynomial` on the _output_ "x coordinates"
95+
/// that were used to generate this table.
96+
/// It is assumed that the `y_coordinates` provided to this function correspond the values of the _input_ "x coordinates"
97+
/// that were used to generate this table.
98+
pub fn eval<I, J>(&self, y_coordinates: I) -> GenericArray<F, M>
99+
where
100+
I: IntoIterator<Item = J> + Copy,
101+
I::IntoIter: ExactSizeIterator,
102+
J: Borrow<F>,
103+
{
104+
debug_assert_eq!(y_coordinates.into_iter().len(), N::USIZE);
105+
105106
self.table
106107
.iter()
107108
.map(|table_row| {
108109
table_row
109110
.iter()
110-
.zip(polynomial.y_coordinates.iter())
111-
.fold(F::ZERO, |acc, (&base, &y)| acc + base * y)
111+
.zip(y_coordinates)
112+
.fold(F::ZERO, |acc, (&base, y)| acc + base * (*y.borrow()))
112113
})
113114
.collect()
114115
}
@@ -168,84 +169,77 @@ where
168169

169170
#[cfg(all(test, unit_test))]
170171
mod test {
171-
use std::fmt::Debug;
172+
use std::{borrow::Borrow, fmt::Debug};
172173

173-
use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray};
174+
use generic_array::{ArrayLength, GenericArray};
174175
use proptest::{prelude::*, proptest};
175176
use typenum::{U1, U32, U7, U8};
176177

177178
use crate::{
178-
ff::Field,
179+
ff::PrimeField,
179180
protocol::ipa_prf::malicious_security::lagrange::{
180-
CanonicalLagrangeDenominator, LagrangeTable, Polynomial,
181+
CanonicalLagrangeDenominator, LagrangeTable,
181182
},
182183
};
183184

184185
type TestField = crate::ff::Fp32BitPrime;
185186

186187
#[derive(Debug, PartialEq, Clone)]
187-
struct MonomialFormPolynomial<F: Field, N: ArrayLength> {
188+
struct MonomialFormPolynomial<F: PrimeField, N: ArrayLength> {
188189
coefficients: GenericArray<F, N>,
189190
}
190191

191192
impl<F, N> MonomialFormPolynomial<F, N>
192193
where
193-
F: Field,
194+
F: PrimeField,
194195
N: ArrayLength,
195196
{
197+
fn gen_y_values_of_canonical_points(self) -> GenericArray<F, N> {
198+
// Sadly, we cannot just use the range (0..N::U128) because it does not implement ExactSizeIterator
199+
let canonical_points =
200+
(0..N::USIZE).map(|i| F::try_from(u128::try_from(i).unwrap()).unwrap());
201+
self.eval(canonical_points)
202+
}
203+
196204
/// test helper function that evaluates a polynomial in monomial form, i.e. `sum_i c_i x^i` on points `x_output`
197205
/// where `c_0` to `c_N` are stored in `polynomial`
198-
fn eval<M>(&self, x_output: &GenericArray<F, M>) -> GenericArray<F, M>
206+
fn eval<M, I, J>(&self, x_output: I) -> GenericArray<F, M>
199207
where
208+
I: IntoIterator<Item = J>,
209+
I::IntoIter: ExactSizeIterator,
210+
J: Borrow<F>,
200211
M: ArrayLength,
201212
{
202213
x_output
203-
.iter()
204-
.map(|&x| {
214+
.into_iter()
215+
.map(|x| {
205216
// monomial base, i.e. `x^k`
206217
// evaluate p via `sum_k coefficient_k * x^k`
207218
let (_, y) = self
208219
.coefficients
209220
.iter()
210221
.fold((F::ONE, F::ZERO), |(base, y), &coef| {
211-
(base * x, y + coef * base)
222+
(base * (*x.borrow()), y + coef * base)
212223
});
213224
y
214225
})
215226
.collect()
216227
}
217228
}
218229

219-
impl<F, N> From<MonomialFormPolynomial<F, N>> for Polynomial<F, N>
220-
where
221-
F: Field + TryFrom<u128>,
222-
<F as TryFrom<u128>>::Error: Debug,
223-
N: ArrayLength,
224-
{
225-
fn from(value: MonomialFormPolynomial<F, N>) -> Self {
226-
let canonical_points: GenericArray<F, N> =
227-
GenericArray::generate(|i| F::try_from(u128::try_from(i).unwrap()).unwrap());
228-
Polynomial {
229-
y_coordinates: value.eval(&canonical_points),
230-
}
231-
}
232-
}
233-
234230
fn lagrange_single_output_point_using_new(
235231
output_point: TestField,
236232
input_points: [TestField; 32],
237233
) {
238234
let polynomial_monomial_form = MonomialFormPolynomial {
239235
coefficients: GenericArray::<TestField, U32>::from_array(input_points),
240236
};
241-
let output_expected = polynomial_monomial_form.eval(
242-
&GenericArray::<TestField, U1>::from_array([output_point; 1]),
243-
);
244-
let polynomial = Polynomial::from(polynomial_monomial_form.clone());
237+
let output_expected = polynomial_monomial_form.eval(&[output_point]);
245238
let denominator = CanonicalLagrangeDenominator::<TestField, U32>::new();
246239
// generate table using new
247240
let lagrange_table = LagrangeTable::<TestField, U32, U1>::new(&denominator, &output_point);
248-
let output = lagrange_table.eval(&polynomial);
241+
let output =
242+
lagrange_table.eval(&polynomial_monomial_form.gen_y_values_of_canonical_points());
249243
assert_eq!(output, output_expected);
250244
}
251245

@@ -261,15 +255,14 @@ mod test {
261255
coefficients: GenericArray::<TestField, U8>::from_array(input_points),
262256
};
263257
// the canonical x coordinates are 0..7, the outputs use coordinates 8..15:
264-
let x_coordinates_output = GenericArray::<_, U7>::generate(|i| {
265-
TestField::try_from(u128::try_from(i).unwrap() + 8).unwrap()
266-
});
267-
let output_expected = polynomial_monomial_form.eval(&x_coordinates_output);
268-
let polynomial = Polynomial::from(polynomial_monomial_form.clone());
258+
let x_coordinates_output =
259+
(0..7).map(|i| TestField::try_from(u128::try_from(i).unwrap() + 8).unwrap());
260+
let output_expected = polynomial_monomial_form.eval(x_coordinates_output);
269261
let denominator = CanonicalLagrangeDenominator::<TestField, U8>::new();
270262
// generate table using from
271263
let lagrange_table = LagrangeTable::<TestField, U8, U7>::from(denominator);
272-
let output = lagrange_table.eval(&polynomial);
264+
let output =
265+
lagrange_table.eval(&polynomial_monomial_form.gen_y_values_of_canonical_points());
273266
assert_eq!(output, output_expected);
274267
}
275268

Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod lagrange;
2+
pub mod prover;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use std::{
2+
iter::zip,
3+
ops::{Add, Sub},
4+
};
5+
6+
use generic_array::{ArrayLength, GenericArray};
7+
use typenum::{Diff, Sum, U1};
8+
9+
use crate::{
10+
ff::PrimeField,
11+
protocol::ipa_prf::malicious_security::lagrange::{
12+
CanonicalLagrangeDenominator, LagrangeTable,
13+
},
14+
};
15+
16+
pub struct ZeroKnowledgeProof<F: PrimeField, N: ArrayLength> {
17+
g: GenericArray<F, N>,
18+
}
19+
20+
pub struct ProofGenerator<F: PrimeField> {
21+
u: Vec<F>,
22+
v: Vec<F>,
23+
}
24+
25+
type TwoNMinusOne<N> = Diff<Sum<N, N>, U1>;
26+
27+
///
28+
/// Distributed Zero Knowledge Proofs algorithm drawn from
29+
/// `https://eprint.iacr.org/2023/909.pdf`
30+
///
31+
#[allow(non_camel_case_types)]
32+
impl<F> ProofGenerator<F>
33+
where
34+
F: PrimeField,
35+
{
36+
pub fn new(u: Vec<F>, v: Vec<F>) -> Self {
37+
debug_assert_eq!(u.len(), v.len(), "u and v must be of equal length");
38+
Self { u, v }
39+
}
40+
41+
pub fn compute_proof<λ: ArrayLength>(
42+
&self,
43+
r: F,
44+
) -> (ZeroKnowledgeProof<F, TwoNMinusOne<λ>>, ProofGenerator<F>)
45+
where
46+
λ: ArrayLength + Add + Sub<U1>,
47+
<λ as Add>::Output: Sub<U1>,
48+
<<λ as Add>::Output as Sub<U1>>::Output: ArrayLength,
49+
<λ as Sub<U1>>::Output: ArrayLength,
50+
{
51+
debug_assert_eq!(self.u.len() % λ::USIZE, 0); // We should pad with zeroes eventually
52+
53+
let s = self.u.len() / λ::USIZE;
54+
55+
assert!(
56+
s > 1,
57+
"When the output is this small, you should call `compute_final_proof`"
58+
);
59+
60+
let mut next_proof_generator = ProofGenerator {
61+
u: Vec::<F>::with_capacity(s),
62+
v: Vec::<F>::with_capacity(s),
63+
};
64+
65+
let denominator = CanonicalLagrangeDenominator::<F, λ>::new();
66+
let lagrange_table_r = LagrangeTable::<F, λ, U1>::new(&denominator, &r);
67+
let lagrange_table = LagrangeTable::<F, λ, <λ as Sub<U1>>::Output>::from(denominator);
68+
let extrapolated_points = (0..s).map(|i| {
69+
let start = i * λ::USIZE;
70+
let end = start + λ::USIZE;
71+
let p = &self.u[start..end];
72+
let q = &self.v[start..end];
73+
let p_extrapolated = lagrange_table.eval(p);
74+
let q_extrapolated = lagrange_table.eval(q);
75+
let p_r = lagrange_table_r.eval(p)[0];
76+
let q_r = lagrange_table_r.eval(q)[0];
77+
next_proof_generator.u.push(p_r);
78+
next_proof_generator.v.push(q_r);
79+
// p.into_iter() has elements that are &F
80+
// p_extrapolated.into_iter() has elements that are F
81+
// So these iterators cannot be chained.
82+
zip(p, q)
83+
.map(|(a, b)| *a * *b)
84+
.chain(zip(p_extrapolated, q_extrapolated).map(|(a, b)| a * b))
85+
.collect::<GenericArray<F, _>>()
86+
});
87+
let proof = ZeroKnowledgeProof {
88+
g: extrapolated_points
89+
.reduce(|acc, pts| zip(acc, pts).map(|(a, b)| a + b).collect())
90+
.unwrap(),
91+
};
92+
(proof, next_proof_generator)
93+
}
94+
}
95+
96+
#[cfg(all(test, unit_test))]
97+
mod test {
98+
use typenum::U4;
99+
100+
use super::ProofGenerator;
101+
use crate::ff::{Fp31, U128Conversions};
102+
103+
#[test]
104+
fn sample_proof() {
105+
const U: [u128; 32] = [
106+
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,
107+
30, 16, 0, 0, 30, 16,
108+
];
109+
const V: [u128; 32] = [
110+
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,
111+
1, 1, 0, 0, 1, 1,
112+
];
113+
const EXPECTED: [u128; 7] = [0, 30, 29, 30, 5, 28, 13];
114+
const R1: u128 = 22;
115+
const EXPECTED_NEXT_U: [u128; 8] = [0, 0, 26, 0, 7, 18, 24, 13];
116+
const EXPECTED_NEXT_V: [u128; 8] = [10, 21, 30, 28, 15, 21, 3, 3];
117+
let pg: ProofGenerator<Fp31> = ProofGenerator::new(
118+
U.into_iter().map(|x| Fp31::try_from(x).unwrap()).collect(),
119+
V.into_iter().map(|x| Fp31::try_from(x).unwrap()).collect(),
120+
);
121+
let (proof, next_proof_generator) = pg.compute_proof::<U4>(Fp31::try_from(R1).unwrap());
122+
assert_eq!(
123+
proof.g.into_iter().map(|x| x.as_u128()).collect::<Vec<_>>(),
124+
EXPECTED,
125+
);
126+
assert_eq!(
127+
next_proof_generator
128+
.u
129+
.into_iter()
130+
.map(|x| x.as_u128())
131+
.collect::<Vec<_>>(),
132+
EXPECTED_NEXT_U,
133+
);
134+
assert_eq!(
135+
next_proof_generator
136+
.v
137+
.into_iter()
138+
.map(|x| x.as_u128())
139+
.collect::<Vec<_>>(),
140+
EXPECTED_NEXT_V,
141+
);
142+
}
143+
}

0 commit comments

Comments
 (0)