Skip to content

Commit 72ad8cf

Browse files
committed
Delete dead pub, return Result instead of panicking, fix comment and add tests
1 parent e7761ed commit 72ad8cf

3 files changed

Lines changed: 143 additions & 140 deletions

File tree

crypto/stark/src/gkr.rs

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,15 @@ fn evaluate_mle<E: IsField>(
389389
pub fn gkr_prove<E: IsField>(
390390
tree: &[SummationLayer<E>],
391391
transcript: &mut impl IsTranscript<E>,
392-
) -> (
393-
GkrProof<E>,
394-
Vec<FieldElement<E>>,
395-
FieldElement<E>,
396-
FieldElement<E>,
397-
) {
392+
) -> Result<
393+
(
394+
GkrProof<E>,
395+
Vec<FieldElement<E>>,
396+
FieldElement<E>,
397+
FieldElement<E>,
398+
),
399+
GkrError,
400+
> {
398401
assert!(!tree.is_empty(), "tree must have at least one layer");
399402

400403
let num_layers = tree.len(); // layers 0..num_layers-1, root is at num_layers-1
@@ -408,24 +411,26 @@ pub fn gkr_prove<E: IsField>(
408411
let root_n = &root.numerators[0];
409412
let root_d = &root.denominators[0];
410413

411-
// Compute claimed_sum = root_n / root_d
412-
let root_d_inv = root_d.inv().expect("root denominator must be nonzero");
414+
// Compute claimed_sum = root_n / root_d. root_d = 0 means the LogUp challenge z
415+
// collided with a fingerprint denominator (probability ~1/p ≈ 2^{-64}); return
416+
// an error rather than panicking so the caller can retry with a fresh transcript.
417+
let root_d_inv = root_d.inv().map_err(|_| GkrError::ZeroDenominator)?;
413418
let claimed_sum = root_n * &root_d_inv;
414419

415420
// Append the claimed sum to the transcript
416421
transcript.append_field_element(&claimed_sum);
417422

418423
// If the tree has only 1 layer (just leaves = root), no reductions needed
419424
if num_layers == 1 {
420-
return (
425+
return Ok((
421426
GkrProof {
422427
claimed_sum,
423428
layer_proofs: vec![],
424429
},
425430
vec![],
426431
root_n.clone(),
427432
root_d.clone(),
428-
);
433+
));
429434
}
430435

431436
let mut layer_proofs = Vec::with_capacity(num_layers - 1);
@@ -992,18 +997,18 @@ pub fn gkr_prove<E: IsField>(
992997
}
993998
}
994999

995-
(
1000+
Ok((
9961001
GkrProof {
9971002
claimed_sum,
9981003
layer_proofs,
9991004
},
10001005
current_point,
10011006
n_claim,
10021007
d_claim,
1003-
)
1008+
))
10041009
}
10051010

1006-
/// Errors that can occur during GKR verification.
1011+
/// Errors that can occur during GKR proving or verification.
10071012
#[derive(Debug, Clone)]
10081013
pub enum GkrError {
10091014
/// The summation tree structure is invalid.
@@ -1015,6 +1020,9 @@ pub enum GkrError {
10151020
/// The claimed sum does not match (unused in the verifier itself,
10161021
/// but available for callers that compare the claimed sum to an external value).
10171022
ClaimedSumMismatch,
1023+
/// The root denominator is zero (LogUp challenge z collided with a fingerprint
1024+
/// denominator). Probability ~1/p ≈ 2^{-64}; prover should sample a new transcript.
1025+
ZeroDenominator,
10181026
}
10191027

10201028
impl fmt::Display for GkrError {
@@ -1032,6 +1040,12 @@ impl fmt::Display for GkrError {
10321040
GkrError::ClaimedSumMismatch => {
10331041
write!(f, "claimed sum mismatch")
10341042
}
1043+
GkrError::ZeroDenominator => {
1044+
write!(
1045+
f,
1046+
"GKR root denominator is zero (LogUp challenge z collided with a fingerprint denominator; probability ~1/p ≈ 2^{{-64}})"
1047+
)
1048+
}
10351049
}
10361050
}
10371051
}
@@ -1069,7 +1083,7 @@ pub fn gkr_verify<E: IsField>(
10691083
// Step 2: Initialize claims.
10701084
// The verifier sets n_claim = claimed_sum, d_claim = 1.
10711085
// This represents the same rational value as root_n/root_d.
1072-
// Soundness of the first (trivial) layer is enforced by later sumcheck layers.
1086+
// Trivial layers (0 sumcheck rounds) are gate-checked directly in the loop below.
10731087
let mut n_claim = proof.claimed_sum.clone();
10741088
let mut d_claim = FieldElement::<E>::one();
10751089
let mut current_point: Vec<FieldElement<E>> = vec![];
@@ -2400,7 +2414,8 @@ mod tests {
24002414
let tree = build_summation_tree(nums, dens);
24012415

24022416
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[]);
2403-
let (proof, final_point, final_n_claim, final_d_claim) = gkr_prove(&tree, &mut transcript);
2417+
let (proof, final_point, final_n_claim, final_d_claim) =
2418+
gkr_prove(&tree, &mut transcript).unwrap();
24042419

24052420
// claimed_sum = 68/55
24062421
let expected_sum = &FE::from(68u64) * &FE::from(55u64).inv().unwrap();
@@ -2451,7 +2466,8 @@ mod tests {
24512466
let tree = build_summation_tree(nums.clone(), dens.clone());
24522467

24532468
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[]);
2454-
let (proof, final_point, final_n_claim, final_d_claim) = gkr_prove(&tree, &mut transcript);
2469+
let (proof, final_point, final_n_claim, final_d_claim) =
2470+
gkr_prove(&tree, &mut transcript).unwrap();
24552471

24562472
// claimed_sum = root_n / root_d = 1136 / 384
24572473
let expected_sum = &FE::from(1136u64) * &FE::from(384u64).inv().unwrap();
@@ -2505,7 +2521,7 @@ mod tests {
25052521
let expected_sum = root_n * &root_d.inv().unwrap();
25062522

25072523
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[]);
2508-
let (proof, _, _, _) = gkr_prove(&tree, &mut transcript);
2524+
let (proof, _, _, _) = gkr_prove(&tree, &mut transcript).unwrap();
25092525

25102526
assert_eq!(proof.claimed_sum, expected_sum);
25112527
}
@@ -2520,7 +2536,8 @@ mod tests {
25202536
let tree = build_summation_tree(nums.clone(), dens.clone());
25212537

25222538
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[]);
2523-
let (proof, final_point, final_n_claim, final_d_claim) = gkr_prove(&tree, &mut transcript);
2539+
let (proof, final_point, final_n_claim, final_d_claim) =
2540+
gkr_prove(&tree, &mut transcript).unwrap();
25242541

25252542
// Should have 3 layer proofs
25262543
assert_eq!(proof.layer_proofs.len(), 3);
@@ -2553,7 +2570,8 @@ mod tests {
25532570
let tree = build_summation_tree(nums.clone(), dens.clone());
25542571

25552572
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAB]);
2556-
let (proof, final_point, final_n_claim, final_d_claim) = gkr_prove(&tree, &mut transcript);
2573+
let (proof, final_point, final_n_claim, final_d_claim) =
2574+
gkr_prove(&tree, &mut transcript).unwrap();
25572575

25582576
// Should have 4 layer proofs
25592577
assert_eq!(proof.layer_proofs.len(), 4);
@@ -2586,10 +2604,10 @@ mod tests {
25862604
let tree = build_summation_tree(nums, dens);
25872605

25882606
let mut t1 = DefaultTranscript::<GoldilocksField>::new(&[0x42]);
2589-
let (proof1, point1, n1, d1) = gkr_prove(&tree, &mut t1);
2607+
let (proof1, point1, n1, d1) = gkr_prove(&tree, &mut t1).unwrap();
25902608

25912609
let mut t2 = DefaultTranscript::<GoldilocksField>::new(&[0x42]);
2592-
let (proof2, point2, n2, d2) = gkr_prove(&tree, &mut t2);
2610+
let (proof2, point2, n2, d2) = gkr_prove(&tree, &mut t2).unwrap();
25932611

25942612
assert_eq!(proof1.claimed_sum, proof2.claimed_sum);
25952613
assert_eq!(point1, point2);
@@ -2635,7 +2653,7 @@ mod tests {
26352653
// Re-run the protocol manually to extract the combined_claim at each layer
26362654
// and verify the sumcheck round poly sum
26372655
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[]);
2638-
let (proof, _, _, _) = gkr_prove(&tree, &mut transcript);
2656+
let (proof, _, _, _) = gkr_prove(&tree, &mut transcript).unwrap();
26392657

26402658
// Replay transcript to get the same challenges
26412659
let mut replay = DefaultTranscript::<GoldilocksField>::new(&[]);
@@ -2700,7 +2718,8 @@ mod tests {
27002718
let tree = build_summation_tree(nums, dens);
27012719

27022720
let mut transcript = DefaultTranscript::<GoldilocksField>::new(&[]);
2703-
let (proof, final_point, final_n_claim, final_d_claim) = gkr_prove(&tree, &mut transcript);
2721+
let (proof, final_point, final_n_claim, final_d_claim) =
2722+
gkr_prove(&tree, &mut transcript).unwrap();
27042723

27052724
assert_eq!(proof.claimed_sum, FE::from(6u64)); // 42/7 = 6
27062725
assert_eq!(proof.layer_proofs.len(), 0);
@@ -2732,7 +2751,8 @@ mod tests {
27322751

27332752
// Prove
27342753
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
2735-
let (proof, prover_point, prover_n, prover_d) = gkr_prove(&tree, &mut prover_transcript);
2754+
let (proof, prover_point, prover_n, prover_d) =
2755+
gkr_prove(&tree, &mut prover_transcript).unwrap();
27362756

27372757
// Verify with a fresh transcript (same seed)
27382758
let mut verifier_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
@@ -2772,7 +2792,8 @@ mod tests {
27722792

27732793
// Prove
27742794
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
2775-
let (proof, prover_point, prover_n, prover_d) = gkr_prove(&tree, &mut prover_transcript);
2795+
let (proof, prover_point, prover_n, prover_d) =
2796+
gkr_prove(&tree, &mut prover_transcript).unwrap();
27762797

27772798
// Verify with a fresh transcript (same seed)
27782799
let mut verifier_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
@@ -2818,7 +2839,7 @@ mod tests {
28182839

28192840
// Prove with correct claimed_sum
28202841
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
2821-
let (mut proof, _, _, _) = gkr_prove(&tree, &mut prover_transcript);
2842+
let (mut proof, _, _, _) = gkr_prove(&tree, &mut prover_transcript).unwrap();
28222843

28232844
// Tamper with the claimed_sum
28242845
proof.claimed_sum = &proof.claimed_sum + &FE::one();
@@ -2836,6 +2857,52 @@ mod tests {
28362857
);
28372858
}
28382859

2860+
#[test]
2861+
fn test_gkr_verify_trivial_layer_gate_check_rejected() {
2862+
// A 4-leaf tree produces:
2863+
// layer_proofs[0]: trivial (root→layer1, parent_num_vars=0, round_polys=[])
2864+
// layer_proofs[1]: non-trivial (layer1→leaves, parent_num_vars=1)
2865+
//
2866+
// Tamper with child_claims of the trivial layer_proofs[0] and assert
2867+
// that gkr_verify returns GkrError::GateCheckFailed.
2868+
let nums = vec![
2869+
FE::from(1u64),
2870+
FE::from(3u64),
2871+
FE::from(5u64),
2872+
FE::from(7u64),
2873+
];
2874+
let dens = vec![
2875+
FE::from(2u64),
2876+
FE::from(4u64),
2877+
FE::from(6u64),
2878+
FE::from(8u64),
2879+
];
2880+
let tree = build_summation_tree(nums, dens);
2881+
2882+
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xC0]);
2883+
let (mut proof, _, _, _) = gkr_prove(&tree, &mut prover_transcript).unwrap();
2884+
2885+
// layer_proofs[0] is the trivial layer: round_polys must be empty
2886+
assert!(
2887+
proof.layer_proofs[0].sumcheck_proof.round_polys.is_empty(),
2888+
"layer_proofs[0] must be the trivial layer for a 4-leaf tree"
2889+
);
2890+
2891+
// Corrupt nl (child_claims[0]) by adding 1 — this breaks the gate equation
2892+
// n_claim*(dl*dr) = nl*dr + nr*dl without altering the transcript ordering
2893+
proof.layer_proofs[0].child_claims[0] =
2894+
proof.layer_proofs[0].child_claims[0].clone() + FE::one();
2895+
2896+
let mut verifier_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xC0]);
2897+
let result = gkr_verify(&proof, &mut verifier_transcript);
2898+
2899+
assert!(
2900+
matches!(result, Err(GkrError::GateCheckFailed { layer: 0 })),
2901+
"Tampered trivial-layer child_claims must be rejected with GateCheckFailed {{ layer: 0 }}, got: {:?}",
2902+
result
2903+
);
2904+
}
2905+
28392906
#[test]
28402907
fn test_gkr_prove_verify_roundtrip_16() {
28412908
// 16 leaves with various fractions
@@ -2846,7 +2913,8 @@ mod tests {
28462913

28472914
// Prove
28482915
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
2849-
let (proof, prover_point, prover_n, prover_d) = gkr_prove(&tree, &mut prover_transcript);
2916+
let (proof, prover_point, prover_n, prover_d) =
2917+
gkr_prove(&tree, &mut prover_transcript).unwrap();
28502918

28512919
// Verify with a fresh transcript (same seed)
28522920
let mut verifier_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xAA]);
@@ -2885,7 +2953,8 @@ mod tests {
28852953

28862954
// Prove
28872955
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0x5F, 0x00]);
2888-
let (proof, prover_point, prover_n, prover_d) = gkr_prove(&tree, &mut prover_transcript);
2956+
let (proof, prover_point, prover_n, prover_d) =
2957+
gkr_prove(&tree, &mut prover_transcript).unwrap();
28892958

28902959
// Verify with a fresh transcript (same seed)
28912960
let mut verifier_transcript = DefaultTranscript::<GoldilocksField>::new(&[0x5F, 0x00]);
@@ -2922,7 +2991,8 @@ mod tests {
29222991
let tree = build_summation_tree(nums.clone(), dens.clone());
29232992

29242993
let mut prover_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xBB]);
2925-
let (proof, prover_point, prover_n, prover_d) = gkr_prove(&tree, &mut prover_transcript);
2994+
let (proof, prover_point, prover_n, prover_d) =
2995+
gkr_prove(&tree, &mut prover_transcript).unwrap();
29262996

29272997
let mut verifier_transcript = DefaultTranscript::<GoldilocksField>::new(&[0xBB]);
29282998
let result = gkr_verify(&proof, &mut verifier_transcript);

0 commit comments

Comments
 (0)