From ed36cafe54ca6b036d41c142c70680e040fe9ce8 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Mon, 6 Oct 2025 18:11:15 +0800 Subject: [PATCH 01/10] wip non-pow2 add --- ceno_zkvm/src/scheme/cpu/mod.rs | 32 +++---- ceno_zkvm/src/scheme/verifier.rs | 1 + gkr_iop/src/gkr/layer/zerocheck_layer.rs | 2 +- gkr_iop/src/selector.rs | 105 ++++++++++++++++++++++- 4 files changed, 120 insertions(+), 20 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index 1ab14e3f1..ff1686e35 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -83,12 +83,12 @@ impl CpuEccProver { let mut expr_builder = VirtualPolynomialsBuilder::new(num_threads, out_rt.len()); - let sel = SelectorType::Prefix(E::BaseField::ZERO, 0.into()); + let sel_add = SelectorType::QuarkBinaryTreeLessThan(0.into()); let num_instances = (1 << n) - 1; - let mut sel_mle: MultilinearExtension<'_, E> = sel.compute(&out_rt, num_instances).unwrap(); - let sel_expr = expr_builder.lift(sel_mle.to_either()); - - let mut exprs = vec![]; + let mut sel_add_mle: MultilinearExtension<'_, E> = + sel_add.compute(&out_rt, num_instances).unwrap(); + let sel_add_expr = expr_builder.lift(sel_add_mle.to_either()); + let mut exprs_add = vec![]; let filter_bj = |v: &[MultilinearExtension<'_, E>], j: usize| { v.iter() @@ -157,7 +157,7 @@ impl CpuEccProver { ); // affine addition // zerocheck: 0 = s[0,b] * (x[b,0] - x[b,1]) - (y[b,0] - y[b,1]) with b != (1,...,1) - exprs.extend( + exprs_add.extend( (s.clone() * (&x0 - &x1) - (&y0 - &y1)) .to_exprs() .into_iter() @@ -166,7 +166,7 @@ impl CpuEccProver { ); // zerocheck: 0 = s[0,b]^2 - x[b,0] - x[b,1] - x[1,b] with b != (1,...,1) - exprs.extend( + exprs_add.extend( ((&s * &s) - &x0 - &x1 - &x3) .to_exprs() .into_iter() @@ -179,7 +179,7 @@ impl CpuEccProver { ); // zerocheck: 0 = s[0,b] * (x[b,0] - x[1,b]) - (y[b,0] + y[1,b]) with b != (1,...,1) - exprs.extend( + exprs_add.extend( (s.clone() * (&x0 - &x3) - (&y0 + &y3)) .to_exprs() .into_iter() @@ -191,11 +191,10 @@ impl CpuEccProver { .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), ); - let (zerocheck_proof, state) = IOPProverState::prove( - expr_builder - .to_virtual_polys(&[exprs.into_iter().sum::>() * sel_expr], &[]), - transcript, - ); + let exprs_add = exprs_add.into_iter().sum::>() * sel_add_expr; + + let (zerocheck_proof, state) = + IOPProverState::prove(expr_builder.to_virtual_polys(&[exprs_add], &[]), transcript); let rt = state.collect_raw_challenges(); let evals = state.get_mle_flatten_final_evaluations(); @@ -1054,7 +1053,7 @@ mod tests { use std::iter::repeat; use ff_ext::BabyBearExt4; - use itertools::Itertools; + use itertools::{Itertools, assert_equal}; use multilinear_extensions::{ mle::{IntoMLE, MultilinearExtension}, util::transpose, @@ -1099,13 +1098,14 @@ mod tests { })); points.extend( - points[points.len() - num_inputs..] + inputs .chunks_exact(2) .map(|chunk| { let p = chunk[0].clone(); let q = chunk[1].clone(); - p + q + assert!(!p.is_infinity); + if q.is_infinity { p.double() } else { p + q } }) .collect_vec(), ); diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index abe0f6ec2..a66a64116 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -1054,6 +1054,7 @@ impl EccVerifier { .sum(); let sel = eq_eval_less_or_equal_than(num_instances - 1, &out_rt, &rt); + // let sel = eq_quark_form(num_instances - 1, &out_rt, &rt); if sumcheck_claim.expected_evaluation != v * sel { return Err(ZKVMError::VerifyError( (format!( diff --git a/gkr_iop/src/gkr/layer/zerocheck_layer.rs b/gkr_iop/src/gkr/layer/zerocheck_layer.rs index 1d4e6c56a..949ac5963 100644 --- a/gkr_iop/src/gkr/layer/zerocheck_layer.rs +++ b/gkr_iop/src/gkr/layer/zerocheck_layer.rs @@ -453,7 +453,7 @@ pub fn extend_exprs_with_rotation( | SelectorType::Prefix(_, sel) | SelectorType::OrderedSparse32 { expression: sel, .. - } => match_expr(sel) * zero_check_expr, + } | SelectorType::QuarkBinaryTreeLessThan(sel)=> match_expr(sel) * zero_check_expr, }; zero_check_exprs.push(expr); } diff --git a/gkr_iop/src/selector.rs b/gkr_iop/src/selector.rs index bc57295f1..d0ba45512 100644 --- a/gkr_iop/src/selector.rs +++ b/gkr_iop/src/selector.rs @@ -1,6 +1,7 @@ -use rayon::iter::IndexedParallelIterator; +use rayon::iter::{IndexedParallelIterator, IntoParallelIterator}; use ff_ext::ExtensionField; +use itertools::{Itertools, assert_equal}; use multilinear_extensions::{ Expression, mle::{IntoMLE, MultilinearExtension, Point}, @@ -28,10 +29,11 @@ pub enum SelectorType { indices: Vec, expression: Expression, }, + /// binary tree [`quark`] from paper + QuarkBinaryTreeLessThan(Expression), } impl SelectorType { - /// Compute true and false mle eq(1; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (eq() - sel(y; b[5..])) pub fn compute( &self, out_point: &Point, @@ -50,6 +52,7 @@ impl SelectorType { } Some(sel.into_mle()) } + /// Compute true and false mle eq(1; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (eq() - sel(y; b[5..])) SelectorType::OrderedSparse32 { indices, .. } => { let mut sel = build_eq_x_r_vec(out_point); sel.par_chunks_exact_mut(CYCLIC_POW2_5.len()) @@ -75,10 +78,49 @@ impl SelectorType { }); Some(sel.into_mle()) } + SelectorType::QuarkBinaryTreeLessThan(_) => { + let mut sel: Vec = build_eq_x_r_vec(out_point); + let n = sel.len(); + + let num_instances_sequence = (0..out_point.len()) + // clean up sig bits + .scan(num_instances & !1, |acc, _| { + if *acc > 0 { + let cur = *acc; + *acc /= 2; + Some(cur) + } else { + Some(0) + } + }) + .collect::>(); + + (0..out_point.len()) + .into_par_iter() + .zip_eq(num_instances_sequence) + .for_each(|(level, num_instance_in_level)| { + let chunk_size = n / (1 << (level + 1)); // divide by 2^level + + // Compute sub-slice + let start = chunk_size / 2; // Example offset logic + let end = start + chunk_size.min(n - start); + + if start < n { + let slice = &mut sel[start..end]; + // SAFETY: Each `level` writes to disjoint regions + for x in slice + .iter_mut() + .take(num_instance_in_level.min(slice.len())) + { + *x = E::ZERO; + } + } + }); + Some(sel.into_mle()) + } } } - /// Evaluate true and false mle eq(CYCLIC_POW2_5[round]; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (1 - sel(y; b[5..])) pub fn evaluate( &self, evals: &mut Vec, @@ -100,6 +142,7 @@ impl SelectorType { eq_eval_less_or_equal_than(num_instances - 1, out_point, in_point), ) } + /// Evaluate true and false mle eq(CYCLIC_POW2_5[round]; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (1 - sel(y; b[5..])) SelectorType::OrderedSparse32 { indices, expression, @@ -114,6 +157,62 @@ impl SelectorType { eq_eval_less_or_equal_than(num_instances - 1, &out_point[5..], &in_point[5..]); (expression, eval * sel) } + SelectorType::QuarkBinaryTreeLessThan(expr) => { + // num_instances count on leaf layer + // where nodes size is 2^(N) / 2 + debug_assert!(num_instances <= (1 << (out_point.len() - 1))); + if out_point.is_empty() { + panic!("empty out_point size") + } + assert_eq!(out_point.len(), in_point.len()); + if out_point.len() == 1 { + ( + expr, + eq_eval_less_or_equal_than(num_instances - 1, &out_point, &in_point), + ) + } else { + let mut num_instances_sequence = (0..out_point.len()) + // clean up sig bits + .scan(num_instances & !1, |acc, _| { + if *acc > 0 { + let cur = *acc; + *acc /= 2; + Some(cur) + } else { + Some(0) + } + }) + .collect::>(); + num_instances_sequence.reverse(); + // traverse tuple 2 per iteration + let mut num_instances_sequence_iter = + num_instances_sequence.iter().tuple_windows(); + + let mut res = E::ZERO; + for i in 1..out_point.len() { + let (num_instances_rhs_half, num_instances_lhs) = + num_instances_sequence_iter.by_ref().next().unwrap(); + let lhs_res = if *num_instances_lhs > 0 { + (E::ONE - out_point[i]) + * (E::ONE - in_point[i]) + * eq_eval_less_or_equal_than( + *num_instances_lhs - 1, + &out_point[..i], + &in_point[..i], + ) + } else { + E::ZERO + }; + let rhs_res = if *num_instances_rhs_half > 0 { + (out_point[i] * in_point[i]) * res + } else { + E::ZERO + }; + res = lhs_res + rhs_res; + } + (expr, res) + } + } }; let Expression::StructuralWitIn(wit_id, _) = expr else { panic!("Wrong selector expression format"); From 45448fc96ae285e5d0f36b4834201501cffa6128 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Mon, 6 Oct 2025 23:45:09 +0800 Subject: [PATCH 02/10] new selector type compulte & evaluation --- gkr_iop/src/selector.rs | 152 ++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 70 deletions(-) diff --git a/gkr_iop/src/selector.rs b/gkr_iop/src/selector.rs index d0ba45512..b3926d74f 100644 --- a/gkr_iop/src/selector.rs +++ b/gkr_iop/src/selector.rs @@ -1,4 +1,4 @@ -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator}; +use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator}; use ff_ext::ExtensionField; use itertools::{Itertools, assert_equal}; @@ -52,7 +52,7 @@ impl SelectorType { } Some(sel.into_mle()) } - /// Compute true and false mle eq(1; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (eq() - sel(y; b[5..])) + // compute true and false mle eq(1; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (eq() - sel(y; b[5..])) SelectorType::OrderedSparse32 { indices, .. } => { let mut sel = build_eq_x_r_vec(out_point); sel.par_chunks_exact_mut(CYCLIC_POW2_5.len()) @@ -79,12 +79,13 @@ impl SelectorType { Some(sel.into_mle()) } SelectorType::QuarkBinaryTreeLessThan(_) => { + // num_instances: number of prefix one in leaf layer let mut sel: Vec = build_eq_x_r_vec(out_point); let n = sel.len(); let num_instances_sequence = (0..out_point.len()) // clean up sig bits - .scan(num_instances & !1, |acc, _| { + .scan(num_instances / 2, |acc, _| { if *acc > 0 { let cur = *acc; *acc /= 2; @@ -95,27 +96,40 @@ impl SelectorType { }) .collect::>(); - (0..out_point.len()) - .into_par_iter() - .zip_eq(num_instances_sequence) - .for_each(|(level, num_instance_in_level)| { - let chunk_size = n / (1 << (level + 1)); // divide by 2^level - - // Compute sub-slice - let start = chunk_size / 2; // Example offset logic - let end = start + chunk_size.min(n - start); - - if start < n { - let slice = &mut sel[start..end]; - // SAFETY: Each `level` writes to disjoint regions - for x in slice - .iter_mut() - .take(num_instance_in_level.min(slice.len())) - { - *x = E::ZERO; - } - } - }); + // split sel into different size of region, set tailing 0 of respective chunk size + // 1st round: take v = sel[0..sel.len()/2], zero out v[num_instances_sequence[0]..] + // 2nd round: take v = sel[sel.len()/2 .. sel.len()/4], zero out v[num_instances_sequence[1]..] + // ... + // each round: progressively smaller chunk + // example: round 0 uses first half, round 1 uses next quarter, etc. + // compute cumulative start indices: + // e.g. chunk = n/2, then start = 0, chunk, chunk + chunk/2, chunk + chunk/2 + chunk/4, ... + // compute disjoint start indices and lengths + let chunks: Vec<(usize, usize)> = { + let mut result = Vec::new(); + let mut start = 0; + let mut chunk_len = n / 2; + while chunk_len > 0 { + result.push((start, chunk_len)); + start += chunk_len; + chunk_len /= 2; + } + result + }; + + for (i, (start, len)) in chunks.into_iter().enumerate() { + let slice = &mut sel[start..start + len]; + + // determine from which index to zero + let zero_start = num_instances_sequence.get(i).copied().unwrap_or(0).min(len); + + for x in &mut slice[zero_start..] { + *x = E::ZERO; + } + } + + // zero out last bh evaluations + *sel.last_mut().unwrap() = E::ZERO; Some(sel.into_mle()) } } @@ -142,7 +156,7 @@ impl SelectorType { eq_eval_less_or_equal_than(num_instances - 1, out_point, in_point), ) } - /// Evaluate true and false mle eq(CYCLIC_POW2_5[round]; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (1 - sel(y; b[5..])) + // evaluate true and false mle eq(CYCLIC_POW2_5[round]; b[..5]) * sel(y; b[5..]), and eq(1; b[..5]) * (1 - sel(y; b[5..])) SelectorType::OrderedSparse32 { indices, expression, @@ -160,58 +174,56 @@ impl SelectorType { SelectorType::QuarkBinaryTreeLessThan(expr) => { // num_instances count on leaf layer // where nodes size is 2^(N) / 2 - debug_assert!(num_instances <= (1 << (out_point.len() - 1))); + // out_point.len() is also log(2^(N)) - 1 + // so num_instances and 1 << out_point.len() are on same scaling + assert!(num_instances > 0); + assert!(num_instances <= (1 << out_point.len())); if out_point.is_empty() { panic!("empty out_point size") } assert_eq!(out_point.len(), in_point.len()); - if out_point.len() == 1 { - ( - expr, - eq_eval_less_or_equal_than(num_instances - 1, &out_point, &in_point), - ) - } else { - let mut num_instances_sequence = (0..out_point.len()) - // clean up sig bits - .scan(num_instances & !1, |acc, _| { - if *acc > 0 { - let cur = *acc; - *acc /= 2; - Some(cur) - } else { - Some(0) - } - }) - .collect::>(); - num_instances_sequence.reverse(); - // traverse tuple 2 per iteration - let mut num_instances_sequence_iter = - num_instances_sequence.iter().tuple_windows(); - - let mut res = E::ZERO; - for i in 1..out_point.len() { - let (num_instances_rhs_half, num_instances_lhs) = - num_instances_sequence_iter.by_ref().next().unwrap(); - let lhs_res = if *num_instances_lhs > 0 { - (E::ONE - out_point[i]) - * (E::ONE - in_point[i]) - * eq_eval_less_or_equal_than( - *num_instances_lhs - 1, - &out_point[..i], - &in_point[..i], - ) - } else { - E::ZERO - }; - let rhs_res = if *num_instances_rhs_half > 0 { - (out_point[i] * in_point[i]) * res + + let mut prefix_one_seq = (0..out_point.len()) + .scan(num_instances / 2, |acc, _| { + if *acc > 0 { + let cur = *acc; + *acc /= 2; + Some(cur) } else { - E::ZERO - }; - res = lhs_res + rhs_res; + Some(0) + } + }) + .collect::>(); + prefix_one_seq.reverse(); + let mut prefix_one_seq_iter = prefix_one_seq.iter(); + + let mut res = if let Some(first) = prefix_one_seq_iter.by_ref().next() { + if *first > 0 { + assert_eq!(*first, 1); + (E::ONE - out_point[0]) * (E::ONE - in_point[0]) + } else { + E::ZERO } - (expr, res) + } else { + unreachable!() + }; + for i in 1..out_point.len() { + let num_prefix_one_lhs = prefix_one_seq_iter.by_ref().next().unwrap(); + let lhs_res = if *num_prefix_one_lhs > 0 { + (E::ONE - out_point[i]) + * (E::ONE - in_point[i]) + * eq_eval_less_or_equal_than( + *num_prefix_one_lhs - 1, + &out_point[..i], + &in_point[..i], + ) + } else { + E::ZERO + }; + let rhs_res = (out_point[i] * in_point[i]) * res; + res = lhs_res + rhs_res; } + (expr, res) } }; let Expression::StructuralWitIn(wit_id, _) = expr else { From fb1a3a2fac0908800bf7e22eea123ea226a216b4 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Tue, 7 Oct 2025 00:07:39 +0800 Subject: [PATCH 03/10] pass num_instance as argument --- ceno_zkvm/src/scheme/cpu/mod.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index ff1686e35..0771ec2ac 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -43,6 +43,7 @@ use sumcheck::{ use transcript::Transcript; use witness::next_pow2_instance_padding; +use gkr_iop::hal::MultilinearPolynomial; #[cfg(feature = "sanity-check")] use {crate::scheme::septic_curve::SepticExtension, gkr_iop::utils::eq_eval_less_or_equal_than}; @@ -65,6 +66,7 @@ impl CpuEccProver { pub fn create_ecc_proof<'a, E: ExtensionField>( &self, + num_instances: usize, mut xs: Vec>, mut ys: Vec>, invs: Vec>, @@ -84,7 +86,6 @@ impl CpuEccProver { let mut expr_builder = VirtualPolynomialsBuilder::new(num_threads, out_rt.len()); let sel_add = SelectorType::QuarkBinaryTreeLessThan(0.into()); - let num_instances = (1 << n) - 1; let mut sel_add_mle: MultilinearExtension<'_, E> = sel_add.compute(&out_rt, num_instances).unwrap(); let sel_add_expr = expr_builder.lift(sel_add_mle.to_either()); @@ -205,6 +206,7 @@ impl CpuEccProver { #[cfg(feature = "sanity-check")] { + let last_evaluation_index = (1 << n) - 1; let s = invs.iter().map(|x| x.as_view_slice(2, 0)).collect_vec(); let x0 = filter_bj(&xs, 0); let y0 = filter_bj(&ys, 0); @@ -213,11 +215,11 @@ impl CpuEccProver { let x3 = xs.iter().map(|x| x.as_view_slice(2, 1)).collect_vec(); let y3 = ys.iter().map(|y| y.as_view_slice(2, 1)).collect_vec(); let final_sum_x: SepticExtension = (x3.iter()) - .map(|x| x.get_base_field_vec()[num_instances - 1]) // x[1,...,1,0] + .map(|x| x.get_base_field_vec()[last_evaluation_index - 1]) // x[1,...,1,0] .collect_vec() .into(); let final_sum_y: SepticExtension = (y3.iter()) - .map(|y| y.get_base_field_vec()[num_instances - 1]) // x[1,...,1,0] + .map(|y| y.get_base_field_vec()[last_evaluation_index - 1]) // x[1,...,1,0] .collect_vec() .into(); let final_sum = SepticPoint::from_affine(final_sum_x, final_sum_y); @@ -225,7 +227,7 @@ impl CpuEccProver { assert_eq!(final_sum, sum); // check evaluations assert_eq!( - eq_eval_less_or_equal_than(num_instances - 1, &out_rt, &rt), + eq_eval_less_or_equal_than(last_evaluation_index - 1, &out_rt, &rt), evals[0] ); for i in 0..SEPTIC_EXTENSION_DEGREE { @@ -1103,9 +1105,7 @@ mod tests { .map(|chunk| { let p = chunk[0].clone(); let q = chunk[1].clone(); - - assert!(!p.is_infinity); - if q.is_infinity { p.double() } else { p + q } + p + q }) .collect_vec(), ); @@ -1144,6 +1144,7 @@ mod tests { let mut transcript = BasicTranscript::new(b"test"); let prover = CpuEccProver::new(); let quark_proof = prover.create_ecc_proof( + n_points, xs.to_vec(), ys.to_vec(), s.to_vec(), From 0371dd60098e2bcb58a62839b94090710c1edd5e Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Tue, 7 Oct 2025 15:31:57 +0800 Subject: [PATCH 04/10] test pass --- ceno_zkvm/src/scheme/cpu/mod.rs | 90 ++++++++++++++++++++++------ ceno_zkvm/src/scheme/septic_curve.rs | 22 +++++++ ceno_zkvm/src/scheme/verifier.rs | 60 +++++++++---------- gkr_iop/src/selector.rs | 4 +- 4 files changed, 125 insertions(+), 51 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index 0771ec2ac..0a46730c7 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -32,8 +32,11 @@ use multilinear_extensions::{ virtual_poly::build_eq_x_r_vec, virtual_polys::VirtualPolynomialsBuilder, }; -use p3::field::FieldAlgebra; -use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use p3::field::{Field, FieldAlgebra}; +use rayon::iter::{ + IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, + IntoParallelRefMutIterator, ParallelIterator, +}; use std::{collections::BTreeMap, sync::Arc}; use sumcheck::{ macros::{entered_span, exit_span}, @@ -80,16 +83,38 @@ impl CpuEccProver { let out_rt = transcript.sample_and_append_vec(b"ecc", n); let num_threads = optimal_sumcheck_threads(out_rt.len()); - let alpha_pows = - transcript.sample_and_append_challenge_pows(SEPTIC_EXTENSION_DEGREE * 3, b"ecc_alpha"); + // 2: expression got add and double + // 3: each contribute 3 zero constrains + let alpha_pows = transcript + .sample_and_append_challenge_pows(SEPTIC_EXTENSION_DEGREE * 3 * 2, b"ecc_alpha"); + let mut alpha_pows_iter = alpha_pows.iter(); let mut expr_builder = VirtualPolynomialsBuilder::new(num_threads, out_rt.len()); let sel_add = SelectorType::QuarkBinaryTreeLessThan(0.into()); let mut sel_add_mle: MultilinearExtension<'_, E> = sel_add.compute(&out_rt, num_instances).unwrap(); + // we construct sel_double witness here + // verifier can derive it via `sel_double = 1 - sel_add - last_onehot` + let mut sel_double_mle: Vec = build_eq_x_r_vec(&out_rt); + match sel_add_mle.evaluations() { + FieldType::Ext(sel_add_mle) => sel_add_mle + .par_iter() + .zip_eq(sel_double_mle.par_iter_mut()) + .for_each(|(sel_add, sel_double)| { + if *sel_add != E::ZERO { + *sel_double = E::ZERO; + } + }), + _ => unreachable!(), + } + *sel_double_mle.last_mut().unwrap() = E::ZERO; + let mut sel_double_mle = sel_double_mle.into_mle(); let sel_add_expr = expr_builder.lift(sel_add_mle.to_either()); + let sel_double_expr = expr_builder.lift(sel_double_mle.to_either()); + let mut exprs_add = vec![]; + let mut exprs_double = vec![]; let filter_bj = |v: &[MultilinearExtension<'_, E>], j: usize| { v.iter() @@ -162,7 +187,7 @@ impl CpuEccProver { (s.clone() * (&x0 - &x1) - (&y0 - &y1)) .to_exprs() .into_iter() - .zip(alpha_pows.iter().take(SEPTIC_EXTENSION_DEGREE)) + .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), ); @@ -171,11 +196,7 @@ impl CpuEccProver { ((&s * &s) - &x0 - &x1 - &x3) .to_exprs() .into_iter() - .zip( - alpha_pows[SEPTIC_EXTENSION_DEGREE..] - .iter() - .take(SEPTIC_EXTENSION_DEGREE), - ) + .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), ); @@ -184,25 +205,56 @@ impl CpuEccProver { (s.clone() * (&x0 - &x3) - (&y0 + &y3)) .to_exprs() .into_iter() - .zip( - alpha_pows[SEPTIC_EXTENSION_DEGREE * 2..] - .iter() - .take(SEPTIC_EXTENSION_DEGREE), - ) + .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), ); let exprs_add = exprs_add.into_iter().sum::>() * sel_add_expr; - let (zerocheck_proof, state) = - IOPProverState::prove(expr_builder.to_virtual_polys(&[exprs_add], &[]), transcript); + // deal with double + // 0 = s[0,b] * (2*y[b,0]) - (3*x[b,0]^2 + a) + exprs_double.extend( + (s.clone() * (&y0.mul_scalar(Either::Left(E::BaseField::TWO))) + - ((&x0 * &x0.mul_scalar(Either::Left(E::BaseField::from_canonical_u32(3)))) + .add_scalar(Either::Left(E::BaseField::TWO)))) + .to_exprs() + .into_iter() + .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) + .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), + ); + + // 0 = s[0,b]^2 - 2*x[b,0] - x[1,b] + exprs_double.extend( + ((&s * &s) - (&x0.mul_scalar(Either::Left(E::BaseField::TWO))) - &x3) + .to_exprs() + .into_iter() + .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) + .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), + ); + + // 0 = s * (x[b,0] - x[1,b]) - (y[b,0] + y[1, b]) + exprs_double.extend( + (s.clone() * (&x0 - &x3) - (&y0 + &y3)) + .to_exprs() + .into_iter() + .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) + .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), + ); + assert!(alpha_pows_iter.next().is_none()); + + let exprs_double = exprs_double.into_iter().sum::>() * sel_double_expr; + + let (zerocheck_proof, state) = IOPProverState::prove( + expr_builder.to_virtual_polys(&[exprs_add + exprs_double], &[]), + transcript, + ); let rt = state.collect_raw_challenges(); let evals = state.get_mle_flatten_final_evaluations(); assert_eq!(zerocheck_proof.extract_sum(), E::ZERO); // 7 for x[rt,0], x[rt,1], y[rt,0], y[rt,1], x[1,rt], y[1,rt], s[0,rt] - assert_eq!(evals.len(), 1 + SEPTIC_EXTENSION_DEGREE * 7); + assert_eq!(evals.len(), 2 + SEPTIC_EXTENSION_DEGREE * 7); #[cfg(feature = "sanity-check")] { @@ -1055,7 +1107,7 @@ mod tests { use std::iter::repeat; use ff_ext::BabyBearExt4; - use itertools::{Itertools, assert_equal}; + use itertools::Itertools; use multilinear_extensions::{ mle::{IntoMLE, MultilinearExtension}, util::transpose, diff --git a/ceno_zkvm/src/scheme/septic_curve.rs b/ceno_zkvm/src/scheme/septic_curve.rs index ed3030d23..99a5878a0 100644 --- a/ceno_zkvm/src/scheme/septic_curve.rs +++ b/ceno_zkvm/src/scheme/septic_curve.rs @@ -594,6 +594,28 @@ impl MulAssign for SepticExtension { #[derive(Clone, Debug)] pub struct SymbolicSepticExtension(pub Vec>); +impl SymbolicSepticExtension { + pub fn mul_scalar(&self, scalar: Either) -> Self { + let res = self + .0 + .iter() + .map(|a| a.clone() * Expression::Constant(scalar)) + .collect(); + + SymbolicSepticExtension(res) + } + + pub fn add_scalar(&self, scalar: Either) -> Self { + let res = self + .0 + .iter() + .map(|a| a.clone() + Expression::Constant(scalar)) + .collect(); + + SymbolicSepticExtension(res) + } +} + impl Add for &SymbolicSepticExtension { type Output = SymbolicSepticExtension; diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index a66a64116..0b92e162e 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -5,7 +5,22 @@ use ff_ext::ExtensionField; #[cfg(debug_assertions)] use ff_ext::{Instrumented, PoseidonField}; -use gkr_iop::{gkr::GKRClaims, utils::eq_eval_less_or_equal_than}; +use crate::{ + error::ZKVMError, + scheme::{ + constants::{NUM_FANIN, NUM_FANIN_LOGUP, SEL_DEGREE, SEPTIC_EXTENSION_DEGREE}, + septic_curve::SepticExtension, + }, + structs::{ + ComposedConstrainSystem, EccQuarkProof, PointAndEval, TowerProofs, VerifyingKey, + ZKVMVerifyingKey, + }, + utils::{ + eval_inner_repeated_incremental_vec, eval_outer_repeated_incremental_vec, + eval_stacked_constant_vec, eval_stacked_wellform_address_vec, eval_wellform_address_vec, + }, +}; +use gkr_iop::{gkr::GKRClaims, selector::SelectorType, utils::eq_eval_less_or_equal_than}; use itertools::{Itertools, chain, interleave, izip}; use mpcs::{Point, PolynomialCommitmentScheme}; use multilinear_extensions::{ @@ -23,22 +38,6 @@ use sumcheck::{ use transcript::{ForkableTranscript, Transcript}; use witness::next_pow2_instance_padding; -use crate::{ - error::ZKVMError, - scheme::{ - constants::{NUM_FANIN, NUM_FANIN_LOGUP, SEL_DEGREE, SEPTIC_EXTENSION_DEGREE}, - septic_curve::SepticExtension, - }, - structs::{ - ComposedConstrainSystem, EccQuarkProof, PointAndEval, TowerProofs, VerifyingKey, - ZKVMVerifyingKey, - }, - utils::{ - eval_inner_repeated_incremental_vec, eval_outer_repeated_incremental_vec, - eval_stacked_constant_vec, eval_stacked_wellform_address_vec, eval_wellform_address_vec, - }, -}; - use super::{ZKVMChipProof, ZKVMProof}; pub struct ZKVMVerifier> { @@ -996,31 +995,31 @@ impl EccVerifier { transcript, ); - let s0: SepticExtension = proof.evals[1..][0..SEPTIC_EXTENSION_DEGREE] + let s0: SepticExtension = proof.evals[2..][0..][..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); - let x0: SepticExtension = proof.evals[1..] - [SEPTIC_EXTENSION_DEGREE..2 * SEPTIC_EXTENSION_DEGREE] + let x0: SepticExtension = proof.evals[2..][SEPTIC_EXTENSION_DEGREE..] + [..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); - let y0: SepticExtension = proof.evals[1..] - [2 * SEPTIC_EXTENSION_DEGREE..3 * SEPTIC_EXTENSION_DEGREE] + let y0: SepticExtension = proof.evals[2..][2 * SEPTIC_EXTENSION_DEGREE..] + [..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); - let x1: SepticExtension = proof.evals[1..] - [3 * SEPTIC_EXTENSION_DEGREE..4 * SEPTIC_EXTENSION_DEGREE] + let x1: SepticExtension = proof.evals[2..][3 * SEPTIC_EXTENSION_DEGREE..] + [..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); - let y1: SepticExtension = proof.evals[1..] - [4 * SEPTIC_EXTENSION_DEGREE..5 * SEPTIC_EXTENSION_DEGREE] + let y1: SepticExtension = proof.evals[2..][4 * SEPTIC_EXTENSION_DEGREE..] + [..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); - let x3: SepticExtension = proof.evals[1..] - [5 * SEPTIC_EXTENSION_DEGREE..6 * SEPTIC_EXTENSION_DEGREE] + let x3: SepticExtension = proof.evals[2..][5 * SEPTIC_EXTENSION_DEGREE..] + [..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); - let y3: SepticExtension = proof.evals[1..] - [6 * SEPTIC_EXTENSION_DEGREE..7 * SEPTIC_EXTENSION_DEGREE] + let y3: SepticExtension = proof.evals[2..][6 * SEPTIC_EXTENSION_DEGREE..] + [..SEPTIC_EXTENSION_DEGREE] .try_into() .unwrap(); @@ -1054,6 +1053,7 @@ impl EccVerifier { .sum(); let sel = eq_eval_less_or_equal_than(num_instances - 1, &out_rt, &rt); + // let SelectorType::QuarkBinaryTreeLessThan(1.into()); // let sel = eq_quark_form(num_instances - 1, &out_rt, &rt); if sumcheck_claim.expected_evaluation != v * sel { return Err(ZKVMError::VerifyError( diff --git a/gkr_iop/src/selector.rs b/gkr_iop/src/selector.rs index b3926d74f..b5e34630d 100644 --- a/gkr_iop/src/selector.rs +++ b/gkr_iop/src/selector.rs @@ -1,7 +1,7 @@ -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator}; +use rayon::iter::IndexedParallelIterator; use ff_ext::ExtensionField; -use itertools::{Itertools, assert_equal}; +use itertools::Itertools; use multilinear_extensions::{ Expression, mle::{IntoMLE, MultilinearExtension, Point}, From 7e56337952b7a82cc70d6f98c77aa73e51b289e0 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Tue, 7 Oct 2025 16:00:05 +0800 Subject: [PATCH 05/10] refactor and use new selector type --- ceno_zkvm/src/scheme/cpu/mod.rs | 2 +- ceno_zkvm/src/scheme/verifier.rs | 92 +++++++++++++++++--------------- ceno_zkvm/src/structs.rs | 2 +- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index 0a46730c7..219b28843 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -311,7 +311,7 @@ impl CpuEccProver { // TODO: prove the validity of s[0,rt], x[rt,0], x[rt,1], y[rt,0], y[rt,1], x[1,rt], y[1,rt] EccQuarkProof { zerocheck_proof, - num_vars: n, + num_instances, evals, sum, } diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index 0b92e162e..cc20d1831 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -24,7 +24,8 @@ use gkr_iop::{gkr::GKRClaims, selector::SelectorType, utils::eq_eval_less_or_equ use itertools::{Itertools, chain, interleave, izip}; use mpcs::{Point, PolynomialCommitmentScheme}; use multilinear_extensions::{ - Instance, StructuralWitIn, StructuralWitInType, + Expression, Instance, StructuralWitIn, StructuralWitInType, + StructuralWitInType::StackedConstantSequence, mle::IntoMLE, util::ceil_log2, utils::eval_by_expr_with_instance, @@ -811,7 +812,7 @@ impl TowerVerify { let max_num_variables = num_variables.iter().max().unwrap(); - let (next_rt, _) = (0..(max_num_variables-1)).try_fold( + let (next_rt, _) = (0..(max_num_variables - 1)).try_fold( ( PointAndEval { point: initial_rt, @@ -844,31 +845,31 @@ impl TowerVerify { // prod'[b] = prod[0,b] * prod[1,b] // prod'[out_rt] = \sum_b eq(out_rt,b) * prod'[b] = \sum_b eq(out_rt,b) * prod[0,b] * prod[1,b] eq * *alpha - * if round < *max_round-1 {tower_proofs.prod_specs_eval[spec_index][round].iter().copied().product()} else { - E::ZERO - } + * if round < *max_round - 1 { tower_proofs.prod_specs_eval[spec_index][round].iter().copied().product() } else { + E::ZERO + } }) .sum::() + (0..num_logup_spec) - .zip_eq(alpha_pows[num_prod_spec..].chunks(2)) - .zip_eq(num_variables[num_prod_spec..].iter()) - .map(|((spec_index, alpha), max_round)| { - // logup_q'[b] = logup_q[0,b] * logup_q[1,b] - // logup_p'[b] = logup_p[0,b] * logup_q[1,b] + logup_p[1,b] * logup_q[0,b] - // logup_p'[out_rt] = \sum_b eq(out_rt,b) * (logup_p[0,b] * logup_q[1,b] + logup_p[1,b] * logup_q[0,b]) - // logup_q'[out_rt] = \sum_b eq(out_rt,b) * logup_q[0,b] * logup_q[1,b] - let (alpha_numerator, alpha_denominator) = (&alpha[0], &alpha[1]); - eq * if round < *max_round-1 { - let evals = &tower_proofs.logup_specs_eval[spec_index][round]; - let (p1, p2, q1, q2) = - (evals[0], evals[1], evals[2], evals[3]); - *alpha_numerator * (p1 * q2 + p2 * q1) - + *alpha_denominator * (q1 * q2) - } else { - E::ZERO - } - }) - .sum::(); + .zip_eq(alpha_pows[num_prod_spec..].chunks(2)) + .zip_eq(num_variables[num_prod_spec..].iter()) + .map(|((spec_index, alpha), max_round)| { + // logup_q'[b] = logup_q[0,b] * logup_q[1,b] + // logup_p'[b] = logup_p[0,b] * logup_q[1,b] + logup_p[1,b] * logup_q[0,b] + // logup_p'[out_rt] = \sum_b eq(out_rt,b) * (logup_p[0,b] * logup_q[1,b] + logup_p[1,b] * logup_q[0,b]) + // logup_q'[out_rt] = \sum_b eq(out_rt,b) * logup_q[0,b] * logup_q[1,b] + let (alpha_numerator, alpha_denominator) = (&alpha[0], &alpha[1]); + eq * if round < *max_round - 1 { + let evals = &tower_proofs.logup_specs_eval[spec_index][round]; + let (p1, p2, q1, q2) = + (evals[0], evals[1], evals[2], evals[3]); + *alpha_numerator * (p1 * q2 + p2 * q1) + + *alpha_denominator * (q1 * q2) + } else { + E::ZERO + } + }) + .sum::(); if expected_evaluation != sumcheck_claim.expected_evaluation { return Err(ZKVMError::VerifyError("mismatch tower evaluation".into())); @@ -877,7 +878,7 @@ impl TowerVerify { // derive single eval // rt' = r_merge || rt // r_merge.len() == ceil_log2(num_product_fanin) - let r_merge =transcript.sample_and_append_vec(b"merge", log2_num_fanin); + let r_merge = transcript.sample_and_append_vec(b"merge", log2_num_fanin); let coeffs = build_eq_x_r_vec_sequential(&r_merge); assert_eq!(coeffs.len(), num_fanin); let rt_prime = [rt, r_merge].concat(); @@ -893,17 +894,17 @@ impl TowerVerify { .zip(num_variables.iter()) .map(|((spec_index, alpha), max_round)| { // prod'[rt,r_merge] = \sum_b eq(r_merge, b) * prod'[b,rt] - if round < max_round -1 { + if round < max_round - 1 { // merged evaluation let evals = izip!( tower_proofs.prod_specs_eval[spec_index][round].iter(), coeffs.iter() ) - .map(|(a, b)| *a * *b) - .sum::(); + .map(|(a, b)| *a * *b) + .sum::(); // this will keep update until round > evaluation prod_spec_point_n_eval[spec_index] = PointAndEval::new(rt_prime.clone(), evals); - if next_round < max_round -1 { + if next_round < max_round - 1 { *alpha * evals } else { E::ZERO @@ -917,28 +918,28 @@ impl TowerVerify { .zip_eq(next_alpha_pows[num_prod_spec..].chunks(2)) .zip_eq(num_variables[num_prod_spec..].iter()) .map(|((spec_index, alpha), max_round)| { - if round < max_round -1 { + if round < max_round - 1 { let (alpha_numerator, alpha_denominator) = (&alpha[0], &alpha[1]); // merged evaluation let p_evals = izip!( tower_proofs.logup_specs_eval[spec_index][round][0..2].iter(), coeffs.iter() ) - .map(|(a, b)| *a * *b) - .sum::(); + .map(|(a, b)| *a * *b) + .sum::(); let q_evals = izip!( tower_proofs.logup_specs_eval[spec_index][round][2..4].iter(), coeffs.iter() ) - .map(|(a, b)| *a * *b) - .sum::(); + .map(|(a, b)| *a * *b) + .sum::(); // this will keep update until round > evaluation logup_spec_p_point_n_eval[spec_index] = PointAndEval::new(rt_prime.clone(), p_evals); logup_spec_q_point_n_eval[spec_index] = PointAndEval::new(rt_prime.clone(), q_evals); - if next_round < max_round -1 { + if next_round < max_round - 1 { *alpha_numerator * p_evals + *alpha_denominator * q_evals } else { E::ZERO @@ -980,7 +981,8 @@ impl EccVerifier { proof: &EccQuarkProof, transcript: &mut impl Transcript, ) -> Result<(), ZKVMError> { - let out_rt = transcript.sample_and_append_vec(b"ecc", proof.num_vars); + let num_vars = next_pow2_instance_padding(proof.num_instances).ilog2() as usize; + let out_rt = transcript.sample_and_append_vec(b"ecc", num_vars); let alpha_pows = transcript.sample_and_append_challenge_pows(SEPTIC_EXTENSION_DEGREE * 3, b"ecc_alpha"); @@ -989,7 +991,7 @@ impl EccVerifier { &proof.zerocheck_proof, &VPAuxInfo { max_degree: 3, - max_num_variables: proof.num_vars, + max_num_variables: num_vars, phantom: PhantomData, }, transcript, @@ -1023,7 +1025,6 @@ impl EccVerifier { .try_into() .unwrap(); - let num_instances = (1 << proof.num_vars) - 1; let rt = sumcheck_claim .point .iter() @@ -1052,15 +1053,20 @@ impl EccVerifier { }) .sum(); - let sel = eq_eval_less_or_equal_than(num_instances - 1, &out_rt, &rt); - // let SelectorType::QuarkBinaryTreeLessThan(1.into()); - // let sel = eq_quark_form(num_instances - 1, &out_rt, &rt); - if sumcheck_claim.expected_evaluation != v * sel { + let sel_add_expr = SelectorType::::QuarkBinaryTreeLessThan(Expression::StructuralWitIn( + 0, + // this value doesn't matter, as we only need structural id + StackedConstantSequence { max_value: 0 }, + )); + let mut sel_evals = vec![E::ZERO]; + sel_add_expr.evaluate(&mut sel_evals, &out_rt, &rt, proof.num_instances, 0); + let sel_add = sel_evals[0]; + if sumcheck_claim.expected_evaluation != v * sel_add { return Err(ZKVMError::VerifyError( (format!( "ecc zerocheck failed: mismatched evaluation, expected {}, got {}", sumcheck_claim.expected_evaluation, - v * sel + v * sel_add )) .into(), )); diff --git a/ceno_zkvm/src/structs.rs b/ceno_zkvm/src/structs.rs index c8d37346f..afa5cf256 100644 --- a/ceno_zkvm/src/structs.rs +++ b/ceno_zkvm/src/structs.rs @@ -31,7 +31,7 @@ use witness::RowMajorMatrix; ))] pub struct EccQuarkProof { pub zerocheck_proof: IOPProof, - pub num_vars: usize, + pub num_instances: usize, pub evals: Vec, // x[rt,0], x[rt,1], y[rt,0], y[rt,1], x[0,rt], y[0,rt], s[0,rt] pub sum: SepticPoint, } From b7b459bdb354ed30a17f46bc5da33e5c0127dd33 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Tue, 7 Oct 2025 16:43:25 +0800 Subject: [PATCH 06/10] rename double to bypass --- ceno_zkvm/src/scheme/cpu/mod.rs | 76 ++++++++++++++++----------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index 219b28843..7a628a48d 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -83,10 +83,11 @@ impl CpuEccProver { let out_rt = transcript.sample_and_append_vec(b"ecc", n); let num_threads = optimal_sumcheck_threads(out_rt.len()); - // 2: expression got add and double - // 3: each contribute 3 zero constrains - let alpha_pows = transcript - .sample_and_append_challenge_pows(SEPTIC_EXTENSION_DEGREE * 3 * 2, b"ecc_alpha"); + // expression with add (3 zero constrains) and bypass (2 zero constrains) + let alpha_pows = transcript.sample_and_append_challenge_pows( + SEPTIC_EXTENSION_DEGREE * 3 + SEPTIC_EXTENSION_DEGREE * 2, + b"ecc_alpha", + ); let mut alpha_pows_iter = alpha_pows.iter(); let mut expr_builder = VirtualPolynomialsBuilder::new(num_threads, out_rt.len()); @@ -94,13 +95,13 @@ impl CpuEccProver { let sel_add = SelectorType::QuarkBinaryTreeLessThan(0.into()); let mut sel_add_mle: MultilinearExtension<'_, E> = sel_add.compute(&out_rt, num_instances).unwrap(); - // we construct sel_double witness here + // we construct sel_bypass witness here // verifier can derive it via `sel_double = 1 - sel_add - last_onehot` - let mut sel_double_mle: Vec = build_eq_x_r_vec(&out_rt); + let mut sel_bypass_mle: Vec = build_eq_x_r_vec(&out_rt); match sel_add_mle.evaluations() { FieldType::Ext(sel_add_mle) => sel_add_mle .par_iter() - .zip_eq(sel_double_mle.par_iter_mut()) + .zip_eq(sel_bypass_mle.par_iter_mut()) .for_each(|(sel_add, sel_double)| { if *sel_add != E::ZERO { *sel_double = E::ZERO; @@ -108,13 +109,13 @@ impl CpuEccProver { }), _ => unreachable!(), } - *sel_double_mle.last_mut().unwrap() = E::ZERO; - let mut sel_double_mle = sel_double_mle.into_mle(); + *sel_bypass_mle.last_mut().unwrap() = E::ZERO; + let mut sel_bypass_mle = sel_bypass_mle.into_mle(); let sel_add_expr = expr_builder.lift(sel_add_mle.to_either()); - let sel_double_expr = expr_builder.lift(sel_double_mle.to_either()); + let sel_bypass_expr = expr_builder.lift(sel_bypass_mle.to_either()); let mut exprs_add = vec![]; - let mut exprs_double = vec![]; + let mut exprs_bypass = vec![]; let filter_bj = |v: &[MultilinearExtension<'_, E>], j: usize| { v.iter() @@ -211,30 +212,19 @@ impl CpuEccProver { let exprs_add = exprs_add.into_iter().sum::>() * sel_add_expr; - // deal with double - // 0 = s[0,b] * (2*y[b,0]) - (3*x[b,0]^2 + a) - exprs_double.extend( - (s.clone() * (&y0.mul_scalar(Either::Left(E::BaseField::TWO))) - - ((&x0 * &x0.mul_scalar(Either::Left(E::BaseField::from_canonical_u32(3)))) - .add_scalar(Either::Left(E::BaseField::TWO)))) - .to_exprs() - .into_iter() - .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) - .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), - ); - - // 0 = s[0,b]^2 - 2*x[b,0] - x[1,b] - exprs_double.extend( - ((&s * &s) - (&x0.mul_scalar(Either::Left(E::BaseField::TWO))) - &x3) + // deal with bypass + // 0 = (x[1,b] - x[b,0]) + exprs_bypass.extend( + (&x3 - &x0) .to_exprs() .into_iter() .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) .map(|(e, alpha)| e * Expression::Constant(Either::Right(*alpha))), ); - // 0 = s * (x[b,0] - x[1,b]) - (y[b,0] + y[1, b]) - exprs_double.extend( - (s.clone() * (&x0 - &x3) - (&y0 + &y3)) + // 0 = (y[1,b] - y[b,0]) + exprs_bypass.extend( + (&y3 - &y0) .to_exprs() .into_iter() .zip_eq(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) @@ -242,10 +232,10 @@ impl CpuEccProver { ); assert!(alpha_pows_iter.next().is_none()); - let exprs_double = exprs_double.into_iter().sum::>() * sel_double_expr; + let exprs_bypass = exprs_bypass.into_iter().sum::>() * sel_bypass_expr; let (zerocheck_proof, state) = IOPProverState::prove( - expr_builder.to_virtual_polys(&[exprs_add + exprs_double], &[]), + expr_builder.to_virtual_polys(&[exprs_add + exprs_bypass], &[]), transcript, ); @@ -1104,7 +1094,7 @@ where #[cfg(test)] mod tests { - use std::iter::repeat; + use std::{iter, iter::repeat}; use ff_ext::BabyBearExt4; use itertools::Itertools; @@ -1112,7 +1102,7 @@ mod tests { mle::{IntoMLE, MultilinearExtension}, util::transpose, }; - use p3::babybear::BabyBear; + use p3::{babybear::BabyBear, field::FieldAlgebra}; use transcript::BasicTranscript; use crate::scheme::{ @@ -1128,7 +1118,7 @@ mod tests { type F = BabyBear; let log2_n = 6; - let n_points = 1 << log2_n; + let n_points = 64; let mut rng = rand::thread_rng(); let final_sum; @@ -1138,7 +1128,10 @@ mod tests { let mut points = (0..n_points) .map(|_| SepticPoint::::random(&mut rng)) .collect_vec(); - let mut s = Vec::with_capacity(n_points); + points.extend( + iter::repeat(SepticPoint::point_at_infinity()).take((1 << log2_n) - points.len()), + ); + let mut s = Vec::with_capacity(1 << (log2_n + 1)); for layer in (1..=log2_n).rev() { let num_inputs = 1 << layer; @@ -1147,8 +1140,11 @@ mod tests { s.extend(inputs.chunks_exact(2).map(|chunk| { let p = &chunk[0]; let q = &chunk[1]; - - (&p.y - &q.y) * (&p.x - &q.x).inverse().unwrap() + if q.is_infinity { + SepticExtension::zero() + } else { + (&p.y - &q.y) * (&p.x - &q.x).inverse().unwrap() + } })); points.extend( @@ -1165,11 +1161,11 @@ mod tests { final_sum = points.last().cloned().unwrap(); // padding to 2*N - s.extend(repeat(SepticExtension::zero()).take(n_points + 1)); + s.extend(repeat(SepticExtension::zero()).take((1 << (log2_n + 1)) - s.len())); points.push(SepticPoint::point_at_infinity()); - assert_eq!(s.len(), 2 * n_points); - assert_eq!(points.len(), 2 * n_points); + assert_eq!(s.len(), 1 << (log2_n + 1)); + assert_eq!(points.len(), 1 << (log2_n + 1)); // transform points to row major matrix let trace = points From 99c46ae69b6f26db1f5d607c581cb08fe9ccd529 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Wed, 8 Oct 2025 09:26:29 +0800 Subject: [PATCH 07/10] one unittest pass --- ceno_zkvm/src/scheme/cpu/mod.rs | 16 ++++---- ceno_zkvm/src/scheme/verifier.rs | 65 +++++++++++++++++++++++--------- gkr_iop/src/selector.rs | 31 ++++++++------- 3 files changed, 75 insertions(+), 37 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index 7a628a48d..6299fedda 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -96,15 +96,16 @@ impl CpuEccProver { let mut sel_add_mle: MultilinearExtension<'_, E> = sel_add.compute(&out_rt, num_instances).unwrap(); // we construct sel_bypass witness here - // verifier can derive it via `sel_double = 1 - sel_add - last_onehot` + // verifier can derive it via `sel_bypass = 1 - sel_add - last_onehot` let mut sel_bypass_mle: Vec = build_eq_x_r_vec(&out_rt); match sel_add_mle.evaluations() { FieldType::Ext(sel_add_mle) => sel_add_mle - .par_iter() - .zip_eq(sel_bypass_mle.par_iter_mut()) - .for_each(|(sel_add, sel_double)| { + .iter() + .zip_eq(sel_bypass_mle.iter_mut()) + .enumerate() + .for_each(|(i, (sel_add, sel_bypass))| { if *sel_add != E::ZERO { - *sel_double = E::ZERO; + *sel_bypass = E::ZERO; } }), _ => unreachable!(), @@ -1117,8 +1118,8 @@ mod tests { type E = BabyBearExt4; type F = BabyBear; - let log2_n = 6; - let n_points = 64; + let log2_n = 3; + let n_points = 5; let mut rng = rand::thread_rng(); let final_sum; @@ -1205,6 +1206,7 @@ mod tests { assert!( verifier .verify_ecc_proof(&quark_proof, &mut transcript) + .inspect_err(|err| println!("err {:?}", err)) .is_ok() ); } diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index cc20d1831..7e701d34b 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -21,7 +21,7 @@ use crate::{ }, }; use gkr_iop::{gkr::GKRClaims, selector::SelectorType, utils::eq_eval_less_or_equal_than}; -use itertools::{Itertools, chain, interleave, izip}; +use itertools::{Itertools, assert_equal, chain, interleave, izip}; use mpcs::{Point, PolynomialCommitmentScheme}; use multilinear_extensions::{ Expression, Instance, StructuralWitIn, StructuralWitInType, @@ -983,8 +983,11 @@ impl EccVerifier { ) -> Result<(), ZKVMError> { let num_vars = next_pow2_instance_padding(proof.num_instances).ilog2() as usize; let out_rt = transcript.sample_and_append_vec(b"ecc", num_vars); - let alpha_pows = - transcript.sample_and_append_challenge_pows(SEPTIC_EXTENSION_DEGREE * 3, b"ecc_alpha"); + let alpha_pows = transcript.sample_and_append_challenge_pows( + SEPTIC_EXTENSION_DEGREE * 3 + SEPTIC_EXTENSION_DEGREE * 2, + b"ecc_alpha", + ); + let mut alpha_pows_iter = alpha_pows.iter(); let sumcheck_claim = IOPVerifierState::verify( E::ZERO, @@ -1034,6 +1037,8 @@ impl EccVerifier { // zerocheck: 0 = s[0,b] * (x[b,0] - x[b,1]) - (y[b,0] - y[b,1]) // zerocheck: 0 = s[0,b]^2 - x[b,0] - x[b,1] - x[1,b] // zerocheck: 0 = s[0,b] * (x[b,0] - x[1,b]) - (y[b,0] + y[1,b]) + // zerocheck: 0 = (x[1,b] - x[b,0]) + // zerocheck: 0 = (y[1,b] - y[b,0]) // // note that they are not septic extension field elements, // we just want to reuse the multiply/add/sub formulas @@ -1041,17 +1046,15 @@ impl EccVerifier { let v2: SepticExtension = s0.square() - &x0 - &x1 - &x3; let v3: SepticExtension = s0 * (&x0 - &x3) - (&y0 + &y3); - let v: E = vec![v1, v2, v3] - .into_iter() - .enumerate() - .flat_map(|(i, v)| { - let start = i * SEPTIC_EXTENSION_DEGREE; - let end = (i + 1) * SEPTIC_EXTENSION_DEGREE; - v.0.into_iter() - .zip(alpha_pows[start..end].iter()) - .map(|(c, alpha)| c * *alpha) - }) - .sum(); + let v4: SepticExtension = (&x3 - &x0); + let v5: SepticExtension = (&y3 - &y0); + + let [v1, v2, v3, v4, v5] = [v1, v2, v3, v4, v5].map(|v| { + v.0.into_iter() + .zip(alpha_pows_iter.by_ref().take(SEPTIC_EXTENSION_DEGREE)) + .map(|(c, alpha)| c * *alpha) + .collect_vec() + }); let sel_add_expr = SelectorType::::QuarkBinaryTreeLessThan(Expression::StructuralWitIn( 0, @@ -1060,13 +1063,41 @@ impl EccVerifier { )); let mut sel_evals = vec![E::ZERO]; sel_add_expr.evaluate(&mut sel_evals, &out_rt, &rt, proof.num_instances, 0); - let sel_add = sel_evals[0]; - if sumcheck_claim.expected_evaluation != v * sel_add { + let expected_sel_add = sel_evals[0]; + + if proof.evals[0] != expected_sel_add { + return Err(ZKVMError::VerifyError( + (format!( + "sel_add evaluation mismatch, expected {}, got {}", + expected_sel_add, proof.evals[0] + )) + .into(), + )); + } + let expected_sel_bypass = eq_eval(&out_rt, &rt) + - expected_sel_add + - (out_rt.iter().copied().product::() * rt.iter().copied().product::()); + + if proof.evals[1] != expected_sel_bypass { + return Err(ZKVMError::VerifyError( + (format!( + "sel_bypass evaluation mismatch, expected {}, got {}", + expected_sel_bypass, proof.evals[1] + )) + .into(), + )); + } + + let add_evaluations = vec![v1, v2, v3].into_iter().flatten().sum::(); + let bypass_evaluations = vec![v4, v5].into_iter().flatten().sum::(); + if sumcheck_claim.expected_evaluation + != add_evaluations * expected_sel_add + bypass_evaluations * expected_sel_bypass + { return Err(ZKVMError::VerifyError( (format!( "ecc zerocheck failed: mismatched evaluation, expected {}, got {}", sumcheck_claim.expected_evaluation, - v * sel_add + add_evaluations * expected_sel_add + bypass_evaluations * expected_sel_bypass )) .into(), )); diff --git a/gkr_iop/src/selector.rs b/gkr_iop/src/selector.rs index b5e34630d..f02b7d2dc 100644 --- a/gkr_iop/src/selector.rs +++ b/gkr_iop/src/selector.rs @@ -85,15 +85,19 @@ impl SelectorType { let num_instances_sequence = (0..out_point.len()) // clean up sig bits - .scan(num_instances / 2, |acc, _| { - if *acc > 0 { - let cur = *acc; - *acc /= 2; - Some(cur) - } else { - Some(0) - } - }) + .scan( + (num_instances / 2, num_instances.div_ceil(2)), + |(n_instance, raw_instance_ceiling), _| { + if *n_instance > 0 { + let cur = *n_instance; + *n_instance = *raw_instance_ceiling / 2; + *raw_instance_ceiling = raw_instance_ceiling.div_ceil(2); + Some(cur) + } else { + Some(0) + } + }, + ) .collect::>(); // split sel into different size of region, set tailing 0 of respective chunk size @@ -184,10 +188,11 @@ impl SelectorType { assert_eq!(out_point.len(), in_point.len()); let mut prefix_one_seq = (0..out_point.len()) - .scan(num_instances / 2, |acc, _| { - if *acc > 0 { - let cur = *acc; - *acc /= 2; + .scan((num_instances / 2, num_instances.div_ceil(2)), |(n_instance, raw_instance_ceiling), _| { + if *n_instance > 0 { + let cur = *n_instance; + *n_instance = *raw_instance_ceiling / 2; + *raw_instance_ceiling = raw_instance_ceiling.div_ceil(2); Some(cur) } else { Some(0) From a691dcbe731011af8af5e9e7c64edb1714da5518 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Wed, 8 Oct 2025 10:04:04 +0800 Subject: [PATCH 08/10] refactor unittest --- ceno_zkvm/src/scheme/cpu/mod.rs | 51 ++++++++++++++++++-------------- ceno_zkvm/src/scheme/utils.rs | 2 +- ceno_zkvm/src/scheme/verifier.rs | 8 ++--- gkr_iop/src/selector.rs | 24 ++++++++------- 4 files changed, 46 insertions(+), 39 deletions(-) diff --git a/ceno_zkvm/src/scheme/cpu/mod.rs b/ceno_zkvm/src/scheme/cpu/mod.rs index 6299fedda..ec634df94 100644 --- a/ceno_zkvm/src/scheme/cpu/mod.rs +++ b/ceno_zkvm/src/scheme/cpu/mod.rs @@ -32,7 +32,6 @@ use multilinear_extensions::{ virtual_poly::build_eq_x_r_vec, virtual_polys::VirtualPolynomialsBuilder, }; -use p3::field::{Field, FieldAlgebra}; use rayon::iter::{ IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, @@ -46,7 +45,6 @@ use sumcheck::{ use transcript::Transcript; use witness::next_pow2_instance_padding; -use gkr_iop::hal::MultilinearPolynomial; #[cfg(feature = "sanity-check")] use {crate::scheme::septic_curve::SepticExtension, gkr_iop::utils::eq_eval_less_or_equal_than}; @@ -96,14 +94,13 @@ impl CpuEccProver { let mut sel_add_mle: MultilinearExtension<'_, E> = sel_add.compute(&out_rt, num_instances).unwrap(); // we construct sel_bypass witness here - // verifier can derive it via `sel_bypass = 1 - sel_add - last_onehot` + // verifier can derive it via `sel_bypass = eq - sel_add - sel_last_onehot` let mut sel_bypass_mle: Vec = build_eq_x_r_vec(&out_rt); match sel_add_mle.evaluations() { FieldType::Ext(sel_add_mle) => sel_add_mle - .iter() - .zip_eq(sel_bypass_mle.iter_mut()) - .enumerate() - .for_each(|(i, (sel_add, sel_bypass))| { + .par_iter() + .zip_eq(sel_bypass_mle.par_iter_mut()) + .for_each(|(sel_add, sel_bypass)| { if *sel_add != E::ZERO { *sel_bypass = E::ZERO; } @@ -1095,31 +1092,35 @@ where #[cfg(test)] mod tests { - use std::{iter, iter::repeat}; - + use crate::scheme::{ + constants::SEPTIC_EXTENSION_DEGREE, + cpu::CpuEccProver, + septic_curve::{SepticExtension, SepticPoint}, + verifier::EccVerifier, + }; use ff_ext::BabyBearExt4; use itertools::Itertools; use multilinear_extensions::{ mle::{IntoMLE, MultilinearExtension}, util::transpose, }; - use p3::{babybear::BabyBear, field::FieldAlgebra}; + use p3::babybear::BabyBear; + use std::iter::repeat_n; use transcript::BasicTranscript; - - use crate::scheme::{ - constants::SEPTIC_EXTENSION_DEGREE, - cpu::CpuEccProver, - septic_curve::{SepticExtension, SepticPoint}, - verifier::EccVerifier, - }; + use witness::next_pow2_instance_padding; #[test] fn test_ecc_quark_prover() { + for n_points in 1..2 ^ 10 { + test_ecc_quark_prover_inner(n_points) + } + } + + fn test_ecc_quark_prover_inner(n_points: usize) { type E = BabyBearExt4; type F = BabyBear; - let log2_n = 3; - let n_points = 5; + let log2_n = next_pow2_instance_padding(n_points).ilog2(); let mut rng = rand::thread_rng(); let final_sum; @@ -1129,9 +1130,10 @@ mod tests { let mut points = (0..n_points) .map(|_| SepticPoint::::random(&mut rng)) .collect_vec(); - points.extend( - iter::repeat(SepticPoint::point_at_infinity()).take((1 << log2_n) - points.len()), - ); + points.extend(repeat_n( + SepticPoint::point_at_infinity(), + (1 << log2_n) - points.len(), + )); let mut s = Vec::with_capacity(1 << (log2_n + 1)); for layer in (1..=log2_n).rev() { @@ -1162,7 +1164,10 @@ mod tests { final_sum = points.last().cloned().unwrap(); // padding to 2*N - s.extend(repeat(SepticExtension::zero()).take((1 << (log2_n + 1)) - s.len())); + s.extend(repeat_n( + SepticExtension::zero(), + (1 << (log2_n + 1)) - s.len(), + )); points.push(SepticPoint::point_at_infinity()); assert_eq!(s.len(), 1 << (log2_n + 1)); diff --git a/ceno_zkvm/src/scheme/utils.rs b/ceno_zkvm/src/scheme/utils.rs index e48978c51..f7ff6cd4b 100644 --- a/ceno_zkvm/src/scheme/utils.rs +++ b/ceno_zkvm/src/scheme/utils.rs @@ -2,7 +2,7 @@ use crate::{ scheme::{ constants::{MIN_PAR_SIZE, SEPTIC_JACOBIAN_NUM_MLES}, hal::{MainSumcheckProver, ProofInput, ProverDevice}, - septic_curve::{SepticExtension, SepticJacobianPoint, SepticPoint}, + septic_curve::{SepticExtension, SepticJacobianPoint}, }, structs::ComposedConstrainSystem, }; diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index 7e701d34b..88a18079f 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -20,8 +20,8 @@ use crate::{ eval_stacked_constant_vec, eval_stacked_wellform_address_vec, eval_wellform_address_vec, }, }; -use gkr_iop::{gkr::GKRClaims, selector::SelectorType, utils::eq_eval_less_or_equal_than}; -use itertools::{Itertools, assert_equal, chain, interleave, izip}; +use gkr_iop::{gkr::GKRClaims, selector::SelectorType}; +use itertools::{Itertools, chain, interleave, izip}; use mpcs::{Point, PolynomialCommitmentScheme}; use multilinear_extensions::{ Expression, Instance, StructuralWitIn, StructuralWitInType, @@ -1046,8 +1046,8 @@ impl EccVerifier { let v2: SepticExtension = s0.square() - &x0 - &x1 - &x3; let v3: SepticExtension = s0 * (&x0 - &x3) - (&y0 + &y3); - let v4: SepticExtension = (&x3 - &x0); - let v5: SepticExtension = (&y3 - &y0); + let v4: SepticExtension = &x3 - &x0; + let v5: SepticExtension = &y3 - &y0; let [v1, v2, v3, v4, v5] = [v1, v2, v3, v4, v5].map(|v| { v.0.into_iter() diff --git a/gkr_iop/src/selector.rs b/gkr_iop/src/selector.rs index f02b7d2dc..37259e7a5 100644 --- a/gkr_iop/src/selector.rs +++ b/gkr_iop/src/selector.rs @@ -1,7 +1,6 @@ use rayon::iter::IndexedParallelIterator; use ff_ext::ExtensionField; -use itertools::Itertools; use multilinear_extensions::{ Expression, mle::{IntoMLE, MultilinearExtension, Point}, @@ -188,16 +187,19 @@ impl SelectorType { assert_eq!(out_point.len(), in_point.len()); let mut prefix_one_seq = (0..out_point.len()) - .scan((num_instances / 2, num_instances.div_ceil(2)), |(n_instance, raw_instance_ceiling), _| { - if *n_instance > 0 { - let cur = *n_instance; - *n_instance = *raw_instance_ceiling / 2; - *raw_instance_ceiling = raw_instance_ceiling.div_ceil(2); - Some(cur) - } else { - Some(0) - } - }) + .scan( + (num_instances / 2, num_instances.div_ceil(2)), + |(n_instance, raw_instance_ceiling), _| { + if *n_instance > 0 { + let cur = *n_instance; + *n_instance = *raw_instance_ceiling / 2; + *raw_instance_ceiling = raw_instance_ceiling.div_ceil(2); + Some(cur) + } else { + Some(0) + } + }, + ) .collect::>(); prefix_one_seq.reverse(); let mut prefix_one_seq_iter = prefix_one_seq.iter(); From 0f18b66994291b35d5b6cf8aba8c70f64cc3db0b Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Wed, 8 Oct 2025 10:15:04 +0800 Subject: [PATCH 09/10] more docs --- ceno_zkvm/src/scheme/verifier.rs | 2 ++ gkr_iop/src/selector.rs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index 88a18079f..b26b8a3ef 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -1074,6 +1074,8 @@ impl EccVerifier { .into(), )); } + + // derive `sel_bypass = eq - sel_add - sel_last_onehot` let expected_sel_bypass = eq_eval(&out_rt, &rt) - expected_sel_add - (out_rt.iter().copied().product::() * rt.iter().copied().product::()); diff --git a/gkr_iop/src/selector.rs b/gkr_iop/src/selector.rs index 37259e7a5..02f5547b2 100644 --- a/gkr_iop/src/selector.rs +++ b/gkr_iop/src/selector.rs @@ -77,6 +77,7 @@ impl SelectorType { }); Some(sel.into_mle()) } + // also see evaluate() function for more explanation SelectorType::QuarkBinaryTreeLessThan(_) => { // num_instances: number of prefix one in leaf layer let mut sel: Vec = build_eq_x_r_vec(out_point); @@ -186,6 +187,12 @@ impl SelectorType { } assert_eq!(out_point.len(), in_point.len()); + // we break down this special selector evaluation into recursive structure + // iterating through out_point and in_point, for each i + // next_eval = lhs * (1-out_point[i]) * (1 - in_point[i]) + prev_eval * out_point[i] * in_point[i] + // where the lhs is in consecutive prefix 1 follow by 0 + + // calculate prefix 1 length of each layer let mut prefix_one_seq = (0..out_point.len()) .scan( (num_instances / 2, num_instances.div_ceil(2)), From 2a84e0ae7b61064277d4fdd3f33cef430e10a714 Mon Sep 17 00:00:00 2001 From: "sm.wu" Date: Wed, 8 Oct 2025 10:26:22 +0800 Subject: [PATCH 10/10] cargo fmt --- ceno_zkvm/src/scheme/verifier.rs | 31 ++++++++++++------------ gkr_iop/src/gkr/layer/zerocheck_layer.rs | 3 ++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs index b26b8a3ef..65eba768f 100644 --- a/ceno_zkvm/src/scheme/verifier.rs +++ b/ceno_zkvm/src/scheme/verifier.rs @@ -5,21 +5,6 @@ use ff_ext::ExtensionField; #[cfg(debug_assertions)] use ff_ext::{Instrumented, PoseidonField}; -use crate::{ - error::ZKVMError, - scheme::{ - constants::{NUM_FANIN, NUM_FANIN_LOGUP, SEL_DEGREE, SEPTIC_EXTENSION_DEGREE}, - septic_curve::SepticExtension, - }, - structs::{ - ComposedConstrainSystem, EccQuarkProof, PointAndEval, TowerProofs, VerifyingKey, - ZKVMVerifyingKey, - }, - utils::{ - eval_inner_repeated_incremental_vec, eval_outer_repeated_incremental_vec, - eval_stacked_constant_vec, eval_stacked_wellform_address_vec, eval_wellform_address_vec, - }, -}; use gkr_iop::{gkr::GKRClaims, selector::SelectorType}; use itertools::{Itertools, chain, interleave, izip}; use mpcs::{Point, PolynomialCommitmentScheme}; @@ -39,6 +24,22 @@ use sumcheck::{ use transcript::{ForkableTranscript, Transcript}; use witness::next_pow2_instance_padding; +use crate::{ + error::ZKVMError, + scheme::{ + constants::{NUM_FANIN, NUM_FANIN_LOGUP, SEL_DEGREE, SEPTIC_EXTENSION_DEGREE}, + septic_curve::SepticExtension, + }, + structs::{ + ComposedConstrainSystem, EccQuarkProof, PointAndEval, TowerProofs, VerifyingKey, + ZKVMVerifyingKey, + }, + utils::{ + eval_inner_repeated_incremental_vec, eval_outer_repeated_incremental_vec, + eval_stacked_constant_vec, eval_stacked_wellform_address_vec, eval_wellform_address_vec, + }, +}; + use super::{ZKVMChipProof, ZKVMProof}; pub struct ZKVMVerifier> { diff --git a/gkr_iop/src/gkr/layer/zerocheck_layer.rs b/gkr_iop/src/gkr/layer/zerocheck_layer.rs index 949ac5963..97cbf3c73 100644 --- a/gkr_iop/src/gkr/layer/zerocheck_layer.rs +++ b/gkr_iop/src/gkr/layer/zerocheck_layer.rs @@ -453,7 +453,8 @@ pub fn extend_exprs_with_rotation( | SelectorType::Prefix(_, sel) | SelectorType::OrderedSparse32 { expression: sel, .. - } | SelectorType::QuarkBinaryTreeLessThan(sel)=> match_expr(sel) * zero_check_expr, + } + | SelectorType::QuarkBinaryTreeLessThan(sel) => match_expr(sel) * zero_check_expr, }; zero_check_exprs.push(expr); }