Skip to content

Commit 585fed9

Browse files
Sumcheck integration (#977)
* refactor code * refactor * add fix error handling * fix unreachable path error handling * add README * fix clippy * fix comments * fix typo * fix clippy * Update README.md --------- Co-authored-by: Diego K <[email protected]>
1 parent 98d7d8c commit 585fed9

File tree

4 files changed

+484
-294
lines changed

4 files changed

+484
-294
lines changed

provers/sumcheck/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Sumcheck Protocol
2+
3+
A naive implementation of the Sumcheck Protocol.
4+
5+
## Overview
6+
7+
The Sumcheck Protocol allows a prover to convince a verifier that the sum of a multivariate polynomial over the Boolean hypercube equals a claimed value without the verifier having to compute the entire sum.
8+
9+
It is an essential building block for many SNARK protocols, given that it reduces the complexity of computing the sum to performing $O(\nu)$ additions, plus an evaluation at a random point.
10+
11+
The protocol proceeds in rounds, with one round per variable of the multivariate polynomial. In each round, the prover sends a univariate polynomial, and the verifier responds with a random challenge. This process reduces a claim about a multivariate polynomial to a claim about a single evaluation point.
12+
13+
14+
## Example
15+
16+
Here's a simple example of how to use the Sumcheck Protocol:
17+
18+
```rust
19+
use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField;
20+
use lambdaworks_math::field::element::FieldElement;
21+
use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial;
22+
use lambdaworks_sumcheck::{prove, verify};
23+
24+
// Define the field
25+
type F = U64PrimeField<17>;
26+
27+
// Create a multilinear polynomial
28+
let evaluations = vec![
29+
FieldElement::<F>::from(3),
30+
FieldElement::<F>::from(4),
31+
FieldElement::<F>::from(5),
32+
FieldElement::<F>::from(7),
33+
];
34+
let poly = DenseMultilinearPolynomial::new(evaluations);
35+
36+
// Generate a proof
37+
let (claimed_sum, proof) = prove(poly);
38+
39+
// Verify the proof
40+
let result = verify(poly.num_vars(), claimed_sum, proof, Some(poly));
41+
assert!(result.is_ok() && result.unwrap());
42+
```
43+
44+
45+
## References
46+
47+
- [Proofs, Arguments, and Zero-Knowledge. Chapter 4](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf)
48+
- [Lambdaclass Blog Post: Have you checked your sums?](https://blog.lambdaclass.com/have-you-checked-your-sums/)

provers/sumcheck/src/lib.rs

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use lambdaworks_math::field::element::FieldElement;
88
use lambdaworks_math::field::traits::{HasDefaultTranscript, IsField};
99
use lambdaworks_math::traits::ByteConversion;
1010

11+
pub use prover::prove;
12+
pub use verifier::verify;
13+
1114
pub trait Channel<F: IsField> {
1215
fn append_felt(&mut self, element: &FieldElement<F>);
1316
fn draw_felt(&mut self) -> FieldElement<F>;
@@ -26,3 +29,318 @@ where
2629
self.sample_field_element()
2730
}
2831
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
use super::*;
36+
use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField;
37+
use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial;
38+
use verifier::VerifierRoundResult;
39+
40+
// Using a small prime field with modulus 101.
41+
const MODULUS: u64 = 101;
42+
type F = U64PrimeField<MODULUS>;
43+
type FE = FieldElement<F>;
44+
45+
#[test]
46+
fn test_protocol() {
47+
let poly = DenseMultilinearPolynomial::new(vec![
48+
FE::from(3),
49+
FE::from(5),
50+
FE::from(7),
51+
FE::from(11),
52+
]);
53+
54+
let (claimed_sum, proof_polys) = prove(poly.clone());
55+
56+
let result = verify(poly.num_vars(), claimed_sum, proof_polys, Some(poly));
57+
58+
assert!(result.unwrap_or(false), "Valid proof should be accepted");
59+
}
60+
61+
#[test]
62+
fn test_interactive_sumcheck() {
63+
let poly = DenseMultilinearPolynomial::new(vec![
64+
FE::from(1),
65+
FE::from(2),
66+
FE::from(1),
67+
FE::from(4),
68+
]);
69+
70+
let mut prover = prover::Prover::new(poly.clone());
71+
let c_1 = prover.c_1();
72+
println!("\nInitial claimed sum c₁: {:?}", c_1);
73+
let mut transcript = DefaultTranscript::<F>::default();
74+
let mut verifier = verifier::Verifier::new(poly.num_vars(), Some(poly), c_1);
75+
76+
// Round 0
77+
println!("\n-- Round 0 --");
78+
let univar0 = prover.poly.to_univariate();
79+
println!(
80+
"Univariate polynomial g₀(x) coefficients: {:?}",
81+
univar0.coefficients
82+
);
83+
let eval_0 = univar0.evaluate(&FieldElement::<F>::zero());
84+
let eval_1 = univar0.evaluate(&FieldElement::<F>::one());
85+
println!(
86+
"g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}",
87+
eval_0,
88+
eval_1,
89+
eval_0 + eval_1
90+
);
91+
let res0 = verifier.do_round(univar0, &mut transcript).unwrap();
92+
let r0 = if let VerifierRoundResult::NextRound(chal) = res0 {
93+
println!("Challenge r₀: {:?}", chal);
94+
chal
95+
} else {
96+
panic!("Expected NextRound result");
97+
};
98+
99+
// Round 1
100+
println!("\n-- Round 1 (Final) --");
101+
let univar1 = prover.round(r0);
102+
println!(
103+
"Univariate polynomial g₁(x) coefficients: {:?}",
104+
univar1.coefficients
105+
);
106+
let eval_0 = univar1.evaluate(&FieldElement::<F>::zero());
107+
let eval_1 = univar1.evaluate(&FieldElement::<F>::one());
108+
println!(
109+
"g₁(0) = {:?}, g₁(1) = {:?}, sum = {:?}",
110+
eval_0,
111+
eval_1,
112+
eval_0 + eval_1
113+
);
114+
let res1 = verifier.do_round(univar1, &mut transcript).unwrap();
115+
if let VerifierRoundResult::Final(ok) = res1 {
116+
println!(
117+
"\nFinal verification result: {}",
118+
if ok { "ACCEPTED" } else { "REJECTED" }
119+
);
120+
assert!(ok, "Final round verification failed");
121+
} else {
122+
panic!("Expected Final result");
123+
}
124+
}
125+
126+
/// Test based on a textbook example for a 3-variable polynomial
127+
#[test]
128+
fn test_from_book() {
129+
// 3-variable polynomial with evaluations:
130+
// (0,0,0)=1, (1,0,0)=2, (0,1,0)=3, (1,1,0)=4,
131+
// (0,0,1)=5, (1,0,1)=6, (0,1,1)=7, (1,1,1)=8.
132+
let poly = DenseMultilinearPolynomial::new(vec![
133+
FE::from(1),
134+
FE::from(2),
135+
FE::from(3),
136+
FE::from(4),
137+
FE::from(5),
138+
FE::from(6),
139+
FE::from(7),
140+
FE::from(8),
141+
]);
142+
// Total sum (claimed sum) is 36.
143+
let mut prover = prover::Prover::new(poly.clone());
144+
let c_1 = prover.c_1();
145+
println!("\nInitial claimed sum c₁: {:?}", c_1);
146+
let mut transcript = DefaultTranscript::<F>::default();
147+
let mut verifier = verifier::Verifier::new(poly.num_vars(), Some(poly), c_1);
148+
149+
// Round 0
150+
let mut g = prover.poly.to_univariate();
151+
println!("\n-- Round 0 --");
152+
println!(
153+
"Univariate polynomial g₀(x) coefficients: {:?}",
154+
g.coefficients
155+
);
156+
let eval_0 = g.evaluate(&FieldElement::<F>::zero());
157+
let eval_1 = g.evaluate(&FieldElement::<F>::one());
158+
println!(
159+
"g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}",
160+
eval_0,
161+
eval_1,
162+
eval_0 + eval_1
163+
);
164+
let res0 = verifier.do_round(g, &mut transcript).unwrap();
165+
let mut current_challenge = if let VerifierRoundResult::NextRound(chal) = res0 {
166+
println!("Challenge r₀: {:?}", chal);
167+
chal
168+
} else {
169+
panic!("Expected NextRound result");
170+
};
171+
172+
// Continue rounds until final.
173+
let mut round = 1;
174+
while verifier.round < verifier.n {
175+
println!(
176+
"\n-- Round {} {}",
177+
round,
178+
if round == verifier.n - 1 {
179+
"(Final)"
180+
} else {
181+
""
182+
}
183+
);
184+
g = prover.round(current_challenge);
185+
println!(
186+
"Univariate polynomial g{}(x) coefficients: {:?}",
187+
round, g.coefficients
188+
);
189+
let eval_0 = g.evaluate(&FieldElement::<F>::zero());
190+
let eval_1 = g.evaluate(&FieldElement::<F>::one());
191+
println!(
192+
"g{}(0) = {:?}, g{}(1) = {:?}, sum = {:?}",
193+
round,
194+
eval_0,
195+
round,
196+
eval_1,
197+
eval_0 + eval_1
198+
);
199+
let res = verifier.do_round(g, &mut transcript).unwrap();
200+
match res {
201+
VerifierRoundResult::NextRound(chal) => {
202+
println!("Challenge r{}: {:?}", round, chal);
203+
current_challenge = chal;
204+
}
205+
VerifierRoundResult::Final(ok) => {
206+
println!(
207+
"\nFinal verification result: {}",
208+
if ok { "ACCEPTED" } else { "REJECTED" }
209+
);
210+
assert!(ok, "Final round verification failed");
211+
break;
212+
}
213+
}
214+
round += 1;
215+
}
216+
}
217+
218+
#[test]
219+
fn test_from_book_ported() {
220+
// 3-variable polynomial: f(x₀,x₁,x₂)=2*x₀ + x₀*x₂ + x₁*x₂.
221+
// Evaluations (little-endian): [0, 2, 0, 2, 0, 3, 1, 4]. Total sum = 12.
222+
let poly = DenseMultilinearPolynomial::new(vec![
223+
FE::from(0),
224+
FE::from(2),
225+
FE::from(0),
226+
FE::from(2),
227+
FE::from(0),
228+
FE::from(3),
229+
FE::from(1),
230+
FE::from(4),
231+
]);
232+
let mut prover = prover::Prover::new(poly.clone());
233+
let c_1 = prover.c_1();
234+
println!("\nInitial claimed sum c₁: {:?}", c_1);
235+
let mut transcript = DefaultTranscript::<F>::default();
236+
let mut verifier = verifier::Verifier::new(poly.num_vars(), Some(poly), c_1);
237+
238+
// Round 0:
239+
println!("\n-- Round 0 --");
240+
let univar0 = prover.poly.to_univariate();
241+
println!(
242+
"Univariate polynomial g₀(x) coefficients: {:?}",
243+
univar0.coefficients
244+
);
245+
let eval_0 = univar0.evaluate(&FieldElement::<F>::zero());
246+
let eval_1 = univar0.evaluate(&FieldElement::<F>::one());
247+
println!(
248+
"g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}",
249+
eval_0,
250+
eval_1,
251+
eval_0 + eval_1
252+
);
253+
let res0 = verifier.do_round(univar0, &mut transcript).unwrap();
254+
let r0 = if let VerifierRoundResult::NextRound(chal) = res0 {
255+
println!("Challenge r₀: {:?}", chal);
256+
chal
257+
} else {
258+
panic!("Expected NextRound result");
259+
};
260+
261+
// Round 1:
262+
println!("\n-- Round 1 --");
263+
let univar1 = prover.round(r0);
264+
println!(
265+
"Univariate polynomial g₁(x) coefficients: {:?}",
266+
univar1.coefficients
267+
);
268+
let eval_0 = univar1.evaluate(&FieldElement::<F>::zero());
269+
let eval_1 = univar1.evaluate(&FieldElement::<F>::one());
270+
println!(
271+
"g₁(0) = {:?}, g₁(1) = {:?}, sum = {:?}",
272+
eval_0,
273+
eval_1,
274+
eval_0 + eval_1
275+
);
276+
let res1 = verifier.do_round(univar1, &mut transcript).unwrap();
277+
let r1 = if let VerifierRoundResult::NextRound(chal) = res1 {
278+
println!("Challenge r₁: {:?}", chal);
279+
chal
280+
} else {
281+
panic!("Expected NextRound result");
282+
};
283+
284+
// Round 2 (final round):
285+
println!("\n-- Round 2 (Final) --");
286+
let univar2 = prover.round(r1);
287+
println!(
288+
"Univariate polynomial g₂(x) coefficients: {:?}",
289+
univar2.coefficients
290+
);
291+
let eval_0 = univar2.evaluate(&FieldElement::<F>::zero());
292+
let eval_1 = univar2.evaluate(&FieldElement::<F>::one());
293+
println!(
294+
"g₂(0) = {:?}, g₂(1) = {:?}, sum = {:?}",
295+
eval_0,
296+
eval_1,
297+
eval_0 + eval_1
298+
);
299+
let res2 = verifier.do_round(univar2, &mut transcript).unwrap();
300+
if let VerifierRoundResult::Final(ok) = res2 {
301+
println!(
302+
"\nFinal verification result: {}",
303+
if ok { "ACCEPTED" } else { "REJECTED" }
304+
);
305+
assert!(ok, "Final round verification failed");
306+
} else {
307+
panic!("Expected Final result");
308+
}
309+
}
310+
311+
#[test]
312+
fn failing_verification_test() {
313+
let poly = DenseMultilinearPolynomial::new(vec![
314+
FE::from(1),
315+
FE::from(2),
316+
FE::from(1),
317+
FE::from(4),
318+
]);
319+
let prover = prover::Prover::new(poly.clone());
320+
// Deliberately use an incorrect claimed sum.
321+
let incorrect_c1 = FE::from(999);
322+
println!("\nInitial (incorrect) claimed sum c₁: {:?}", incorrect_c1);
323+
let mut transcript = DefaultTranscript::<F>::default();
324+
let mut verifier = verifier::Verifier::new(poly.num_vars(), Some(poly), incorrect_c1);
325+
326+
println!("\n-- Round 0 --");
327+
let univar0 = prover.poly.to_univariate();
328+
println!(
329+
"Univariate polynomial g₀(x) coefficients: {:?}",
330+
univar0.coefficients
331+
);
332+
let eval_0 = univar0.evaluate(&FieldElement::<F>::zero());
333+
let eval_1 = univar0.evaluate(&FieldElement::<F>::one());
334+
println!(
335+
"g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}",
336+
eval_0,
337+
eval_1,
338+
eval_0 + eval_1
339+
);
340+
let res0 = verifier.do_round(univar0, &mut transcript);
341+
if let Err(e) = &res0 {
342+
println!("\nExpected verification error: {:?}", e);
343+
}
344+
assert!(res0.is_err(), "Expected verification error");
345+
}
346+
}

0 commit comments

Comments
 (0)