From 1aa221ae5a382cf8f1958e9d256befe830fd69bb Mon Sep 17 00:00:00 2001 From: kunxian xia Date: Wed, 21 Jan 2026 18:17:12 +0800 Subject: [PATCH 1/4] 1st round fold without computing eval from scratch --- Cargo.lock | 22 +++--- Cargo.toml | 20 ++--- .../src/basefold_verifier/query_phase.rs | 74 +++++-------------- .../src/basefold_verifier/verifier.rs | 9 +-- 4 files changed, 43 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be858ab57..c9b608288 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2210,7 +2210,7 @@ dependencies = [ [[package]] name = "ff_ext" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "once_cell", "p3", @@ -3214,7 +3214,7 @@ dependencies = [ [[package]] name = "mpcs" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "bincode 1.3.3", "clap", @@ -3238,7 +3238,7 @@ dependencies = [ [[package]] name = "multilinear_extensions" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "either", "ff_ext", @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "p3" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "p3-air", "p3-baby-bear", @@ -5094,7 +5094,7 @@ dependencies = [ [[package]] name = "poseidon" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "ff_ext", "p3", @@ -6050,7 +6050,7 @@ dependencies = [ [[package]] name = "sp1-curves" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "cfg-if", "dashu", @@ -6175,7 +6175,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sumcheck" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "either", "ff_ext", @@ -6193,7 +6193,7 @@ dependencies = [ [[package]] name = "sumcheck_macro" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "itertools 0.13.0", "p3", @@ -6600,7 +6600,7 @@ dependencies = [ [[package]] name = "transcript" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "ff_ext", "itertools 0.13.0", @@ -6894,7 +6894,7 @@ dependencies = [ [[package]] name = "whir" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "bincode 1.3.3", "clap", @@ -7181,7 +7181,7 @@ dependencies = [ [[package]] name = "witness" version = "0.1.0" -source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.19#eb292fef68831ba85e6553a56cacbf770ea9d122" +source = "git+https://github.com/scroll-tech/gkr-backend.git?tag=v1.0.0-alpha.20#4f5a2b99af8edc9e8d808a6ab7c73e4088d61e39" dependencies = [ "ff_ext", "multilinear_extensions", diff --git a/Cargo.toml b/Cargo.toml index ec839acac..7e1c39ecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,16 +27,16 @@ version = "0.1.0" ceno_crypto_primitives = { git = "https://github.com/scroll-tech/ceno-patch.git", package = "ceno_crypto_primitives", branch = "main" } ceno_syscall = { git = "https://github.com/scroll-tech/ceno-patch.git", package = "ceno_syscall", branch = "main" } -ff_ext = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "ff_ext", tag = "v1.0.0-alpha.19" } -mpcs = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "mpcs", tag = "v1.0.0-alpha.19" } -multilinear_extensions = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "multilinear_extensions", tag = "v1.0.0-alpha.19" } -p3 = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "p3", tag = "v1.0.0-alpha.19" } -poseidon = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "poseidon", tag = "v1.0.0-alpha.19" } -sp1-curves = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "sp1-curves", tag = "v1.0.0-alpha.19" } -sumcheck = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "sumcheck", tag = "v1.0.0-alpha.19" } -transcript = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "transcript", tag = "v1.0.0-alpha.19" } -whir = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "whir", tag = "v1.0.0-alpha.19" } -witness = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "witness", tag = "v1.0.0-alpha.19" } +ff_ext = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "ff_ext", tag = "v1.0.0-alpha.20" } +mpcs = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "mpcs", tag = "v1.0.0-alpha.20" } +multilinear_extensions = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "multilinear_extensions", tag = "v1.0.0-alpha.20" } +p3 = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "p3", tag = "v1.0.0-alpha.20" } +poseidon = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "poseidon", tag = "v1.0.0-alpha.20" } +sp1-curves = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "sp1-curves", tag = "v1.0.0-alpha.20" } +sumcheck = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "sumcheck", tag = "v1.0.0-alpha.20" } +transcript = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "transcript", tag = "v1.0.0-alpha.20" } +whir = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "whir", tag = "v1.0.0-alpha.20" } +witness = { git = "https://github.com/scroll-tech/gkr-backend.git", package = "witness", tag = "v1.0.0-alpha.20" } anyhow = { version = "1.0", default-features = false } bincode = "1" diff --git a/ceno_recursion/src/basefold_verifier/query_phase.rs b/ceno_recursion/src/basefold_verifier/query_phase.rs index 6064731c7..b32be2b67 100644 --- a/ceno_recursion/src/basefold_verifier/query_phase.rs +++ b/ceno_recursion/src/basefold_verifier/query_phase.rs @@ -307,12 +307,6 @@ pub struct QueryPhaseVerifierInputVariable { pub rounds: Array>, } -#[derive(DslVariable, Clone)] -pub struct PackedCodeword { - pub low: Ext, - pub high: Ext, -} - #[derive(DslVariable, Clone)] pub struct RoundContextVariable { pub(crate) opened_values_buffer: Array>>, @@ -388,9 +382,9 @@ pub(crate) fn batch_verifier_query_phase( let initial_cur_num_var: Var = builder.eval(input.max_num_var.clone()); let initial_log2_height: Var = - builder.eval(initial_cur_num_var + Usize::from(get_rate_log() - 1)); + builder.eval(initial_cur_num_var + Usize::from(get_rate_log())); builder.assert_eq::>( - input.proof.commits.len() + Usize::from(1), + input.proof.commits.len(), input.fold_challenges.len(), ); @@ -482,15 +476,11 @@ pub(crate) fn batch_verifier_query_phase( }); let idx_bits = idx_bits.slice(builder, 1, log2_max_codeword_size); - let reduced_codeword_by_height: Array> = + let reduced_codeword_by_height: Array> = builder.dyn_array(log2_max_codeword_size); // initialize reduced_codeword_by_height with zeroes iter_zip!(builder, reduced_codeword_by_height).for_each(|ptr_vec, builder| { - let zero_codeword = PackedCodeword { - low: zero, - high: zero, - }; - builder.set_value(&reduced_codeword_by_height, ptr_vec[0], zero_codeword); + builder.set_value(&reduced_codeword_by_height, ptr_vec[0], zero); }); let query = builder.iter_ptr_get(&input.proof.query_opening_proof, ptr_vec[1]); @@ -523,39 +513,23 @@ pub(crate) fn batch_verifier_query_phase( builder.iter_ptr_get(&round_context.minus_alpha_offsets, ptr_vec[1]); let opening = builder.iter_ptr_get(&round.openings, ptr_vec[4]); let width = opening.point_and_evals.evals.len(); - let low_values = opened_values_buffer.slice(builder, 0, width.clone()); - let high_values = opened_values_buffer.slice( - builder, - width.clone(), - opened_values_buffer.len(), - ); let all_zeros_slice = all_zeros.slice(builder, 0, width.clone()); - let low = builder.fri_single_reduced_opening_eval( - alpha, - opened_values.id.get_var(), - zero_flag, - &low_values, - &all_zeros_slice, - ); - let high = builder.fri_single_reduced_opening_eval( + let eval = builder.fri_single_reduced_opening_eval( alpha, opened_values.id.get_var(), zero_flag, - &high_values, + &opened_values_buffer, &all_zeros_slice, ); - builder.assign(&low, low * minus_alpha_offset); - builder.assign(&high, high * minus_alpha_offset); + builder.assign(&eval, eval * minus_alpha_offset); - let codeword: PackedCodeword = PackedCodeword { low, high }; - let codeword_acc = builder.get(&reduced_codeword_by_height, log2_height); + let eval_acc = builder.get(&reduced_codeword_by_height, log2_height); - // reduced_openings[log2_height] += codeword - builder.assign(&codeword_acc.low, codeword_acc.low + codeword.low); - builder.assign(&codeword_acc.high, codeword_acc.high + codeword.high); + // reduced_openings[log2_height] += eval + builder.assign(&eval_acc, eval_acc + eval); - builder.set_value(&reduced_codeword_by_height, log2_height, codeword_acc); + builder.set_value(&reduced_codeword_by_height, log2_height, eval_acc); // reorder opened values according to the permutation let mat_j = @@ -587,8 +561,6 @@ pub(crate) fn batch_verifier_query_phase( let cur_num_var: Var = builder.eval(initial_cur_num_var); let log2_height: Var = builder.eval(initial_log2_height); - let r = builder.get(&input.fold_challenges, 0); - let codeword = builder.get(&reduced_codeword_by_height, log2_height); let coeff = verifier_folding_coeffs_level( builder, &two_adic_generators_inverses, @@ -596,18 +568,12 @@ pub(crate) fn batch_verifier_query_phase( &idx_bits, inv_2, ); - let folded = codeword_fold_with_challenge::( - builder, - codeword.low, - codeword.high, - r, - coeff, - inv_2, - ); + let folded = builder.constant(C::EF::ZERO); // check commit phases let commits = &input.proof.commits; builder.assert_eq::>(commits.len(), opening_ext.len()); + builder.cycle_tracker_start("FRI rounds"); let i: Var = builder.constant(C::N::ZERO); iter_zip!(builder, commits, opening_ext).for_each(|ptr_vec, builder| { @@ -618,8 +584,6 @@ pub(crate) fn batch_verifier_query_phase( let sibling_value = commit_phase_step.sibling_value; let proof = commit_phase_step.opening_proof; - builder.assign(&cur_num_var, cur_num_var - Usize::from(1)); - builder.assign(&log2_height, log2_height - Usize::from(1)); let folded_idx = builder.get(&idx_bits, i); let new_involved_packed_codeword = @@ -637,7 +601,7 @@ pub(crate) fn batch_verifier_query_phase( // So prev ^ 2 = 1/4 * omega^{-2^(MAX - (level + 2)) * 2 * (index+2^level*prev_bit)} // = 1/4 * omega^{-2^(MAX - (level + 1)) * (index+2^level*prev_bit)} // = 1/4 * omega^{-2^(MAX - (level + 1)) * index - 2^(MAX - (level + 1)) * 2^level*prev_bit} - // = 1/2 * curr * omeag^{- 2^(MAX - 1) * prev_bit} + // = 1/2 * curr * omega^{- 2^(MAX - 1) * prev_bit} // = 1/2 * curr * generator_of_order(2)^{-prev_bit} // Note that generator_of_order(2) is exactly -1, so // prev ^ 2 = 1/2 * curr * (-1)^{-prev_bit} @@ -650,10 +614,10 @@ pub(crate) fn batch_verifier_query_phase( builder.if_eq(folded_idx, Usize::from(0)).then_or_else( |builder| { - builder.assign(&folded, folded + new_involved_packed_codeword.low); + builder.assign(&folded, folded + new_involved_packed_codeword); }, |builder| { - builder.assign(&folded, folded + new_involved_packed_codeword.high); + builder.assign(&folded, folded + new_involved_packed_codeword); builder.assign(&coeff, -coeff); }, ); @@ -688,6 +652,8 @@ pub(crate) fn batch_verifier_query_phase( codeword_fold_with_challenge(builder, left, right, r, coeff, inv_2); builder.assign(&folded, new_folded); builder.assign(&i, i_plus_one); + builder.assign(&cur_num_var, cur_num_var - Usize::from(1)); + builder.assign(&log2_height, log2_height - Usize::from(1)); }); builder.cycle_tracker_end("FRI rounds"); // assert that final_value[i] = folded @@ -929,9 +895,7 @@ pub mod tests { .sample_and_append_challenge(b"commit round") .elements, ); - if i < num_rounds - 1 { - write_digest_to_transcript(&commits[i], &mut transcript); - } + write_digest_to_transcript(&commits[i], &mut transcript); } transcript.append_field_element_exts_iter(opening_proof.final_message.iter().flatten()); diff --git a/ceno_recursion/src/basefold_verifier/verifier.rs b/ceno_recursion/src/basefold_verifier/verifier.rs index cb380b068..c343660d0 100644 --- a/ceno_recursion/src/basefold_verifier/verifier.rs +++ b/ceno_recursion/src/basefold_verifier/verifier.rs @@ -113,12 +113,9 @@ pub fn batch_verify( transcript_observe_label(builder, challenger, b"commit round"); let challenge = challenger.sample_ext(builder); builder.set(&fold_challenges, index_vec[0], challenge); - builder - .if_ne(index_vec[0], num_rounds - Usize::from(1)) - .then(|builder| { - let commit = builder.get(&proof.commits, index_vec[0]); - challenger.observe_digest(builder, commit.value.into()); - }); + + let commit = builder.get(&proof.commits, index_vec[0]); + challenger.observe_digest(builder, commit.value.into()); }); iter_zip!(builder, proof.final_message).for_each(|ptr_vec_sumcheck_message, builder| { From 22bb7ae55ce3590a4c4cc833a556087ab592abbd Mon Sep 17 00:00:00 2001 From: kunxian xia Date: Wed, 21 Jan 2026 21:10:27 +0800 Subject: [PATCH 2/4] wip --- .../src/basefold_verifier/query_phase.rs | 83 ++++++++----------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/ceno_recursion/src/basefold_verifier/query_phase.rs b/ceno_recursion/src/basefold_verifier/query_phase.rs index b32be2b67..30f057952 100644 --- a/ceno_recursion/src/basefold_verifier/query_phase.rs +++ b/ceno_recursion/src/basefold_verifier/query_phase.rs @@ -383,10 +383,7 @@ pub(crate) fn batch_verifier_query_phase( let initial_cur_num_var: Var = builder.eval(input.max_num_var.clone()); let initial_log2_height: Var = builder.eval(initial_cur_num_var + Usize::from(get_rate_log())); - builder.assert_eq::>( - input.proof.commits.len(), - input.fold_challenges.len(), - ); + builder.assert_eq::>(input.proof.commits.len(), input.fold_challenges.len()); let rounds_context: Array> = builder.dyn_array(input.rounds.len()); let batch_coeffs_offset: Var = builder.constant(C::N::ZERO); @@ -474,7 +471,7 @@ pub(crate) fn batch_verifier_query_phase( let bit = builder.get(&idx_bits, i_vec[0]); builder.assert_eq::>(bit, Usize::from(0)); }); - let idx_bits = idx_bits.slice(builder, 1, log2_max_codeword_size); + let idx_bits = idx_bits.slice(builder, 0, log2_max_codeword_size); let reduced_codeword_by_height: Array> = builder.dyn_array(log2_max_codeword_size); @@ -557,8 +554,6 @@ pub(crate) fn batch_verifier_query_phase( builder.cycle_tracker_end("Batching and first FRI round"); let opening_ext = query.commit_phase_openings; - // fold 1st codeword - let cur_num_var: Var = builder.eval(initial_cur_num_var); let log2_height: Var = builder.eval(initial_log2_height); let coeff = verifier_folding_coeffs_level( @@ -584,45 +579,12 @@ pub(crate) fn batch_verifier_query_phase( let sibling_value = commit_phase_step.sibling_value; let proof = commit_phase_step.opening_proof; - let folded_idx = builder.get(&idx_bits, i); let new_involved_packed_codeword = builder.get(&reduced_codeword_by_height, log2_height); + builder.assign(&folded, folded + new_involved_packed_codeword); - // Note that previous coeff is - // 1/2 * generator_of_order(2^{level + 2})^{-prev_index} - // = 1/2 * generator_of_order(2^{level + 2})^{-(index+2^level*prev_bit)} - // = 1/2 * omega^{-2^(MAX - (level + 2)) * (index+2^level*prev_bit)} - // where generator_of_order(2^k) returns the generator of order 2^k, which is omega^{2^(MAX - k)} - // since omega is the generator of order 2^MAX, where MAX is the two-adicity of this field. - // Here prev_bit is the most significant bit of prev_index, and index is removing this bit - // from prev_index. - // - // So prev ^ 2 = 1/4 * omega^{-2^(MAX - (level + 2)) * 2 * (index+2^level*prev_bit)} - // = 1/4 * omega^{-2^(MAX - (level + 1)) * (index+2^level*prev_bit)} - // = 1/4 * omega^{-2^(MAX - (level + 1)) * index - 2^(MAX - (level + 1)) * 2^level*prev_bit} - // = 1/2 * curr * omega^{- 2^(MAX - 1) * prev_bit} - // = 1/2 * curr * generator_of_order(2)^{-prev_bit} - // Note that generator_of_order(2) is exactly -1, so - // prev ^ 2 = 1/2 * curr * (-1)^{-prev_bit} - // which gives us - // curr = 2 * prev^2 * (-1)^{prev_bit} - // Note that we haven't multplied the (-1)^{prev_bit} in the following line. - // Because `folded_idx` is just the `prev_bit`, so we reuse the following `if_eq` and multiply -1 - // in the branch where `folded_idx` is != 0. - builder.assign(&coeff, coeff * coeff * two_felt); - - builder.if_eq(folded_idx, Usize::from(0)).then_or_else( - |builder| { - builder.assign(&folded, folded + new_involved_packed_codeword); - }, - |builder| { - builder.assign(&folded, folded + new_involved_packed_codeword); - builder.assign(&coeff, -coeff); - }, - ); - - // leafs + // leaf let leafs = builder.dyn_array(2); let sibling_idx = builder.eval_expr(RVar::from(1) - folded_idx); builder.set_value(&leafs, folded_idx, folded); @@ -634,8 +596,9 @@ pub(crate) fn batch_verifier_query_phase( // mmcs_ext.verify_batch let dimensions = builder.dyn_array(1); let opened_values = builder.dyn_array(1); + let log2_height_minus_one = builder.eval(log2_height - Usize::from(1)); builder.set_value(&opened_values, 0, leafs.clone()); - builder.set_value(&dimensions, 0, log2_height); + builder.set_value(&dimensions, 0, log2_height_minus_one); let ext_mmcs_verifier_input = ExtMmcsVerifierInputVariable { commit: commit.clone(), dimensions, @@ -645,15 +608,41 @@ pub(crate) fn batch_verifier_query_phase( }; ext_mmcs_verify_batch::(builder, ext_mmcs_verifier_input); - let r = builder.get(&input.fold_challenges, i_plus_one); + let r = builder.get(&input.fold_challenges, i); let left = builder.get(&leafs, 0); let right = builder.get(&leafs, 1); let new_folded = codeword_fold_with_challenge(builder, left, right, r, coeff, inv_2); builder.assign(&folded, new_folded); builder.assign(&i, i_plus_one); - builder.assign(&cur_num_var, cur_num_var - Usize::from(1)); - builder.assign(&log2_height, log2_height - Usize::from(1)); + builder.assign(&log2_height, log2_height_minus_one); + + // Note that previous coeff is + // 1/2 * generator_of_order(2^{level + 2})^{-prev_index} + // = 1/2 * generator_of_order(2^{level + 2})^{-(index+2^level*prev_bit)} + // = 1/2 * omega^{-2^(MAX - (level + 2)) * (index+2^level*prev_bit)} + // where generator_of_order(2^k) returns the generator of order 2^k, which is omega^{2^(MAX - k)} + // since omega is the generator of order 2^MAX, where MAX is the two-adicity of this field. + // Here prev_bit is the most significant bit of prev_index, and index is removing this bit + // from prev_index. + // + // So prev ^ 2 = 1/4 * omega^{-2^(MAX - (level + 2)) * 2 * (index+2^level*prev_bit)} + // = 1/4 * omega^{-2^(MAX - (level + 1)) * (index+2^level*prev_bit)} + // = 1/4 * omega^{-2^(MAX - (level + 1)) * index - 2^(MAX - (level + 1)) * 2^level*prev_bit} + // = 1/2 * curr * omega^{- 2^(MAX - 1) * prev_bit} + // = 1/2 * curr * generator_of_order(2)^{-prev_bit} + // Note that generator_of_order(2) is exactly -1, so + // prev ^ 2 = 1/2 * curr * (-1)^{-prev_bit} + // which gives us + // curr = 2 * prev^2 * (-1)^{prev_bit} + // Note that we haven't multplied the (-1)^{prev_bit} in the following line. + // Because `folded_idx` is just the `prev_bit`, so we reuse the following `if_eq` and multiply -1 + // in the branch where `folded_idx` is != 0. + builder.assign(&coeff, coeff * coeff * two_felt); + + builder.if_ne(folded_idx, Usize::from(0)).then(|builder| { + builder.assign(&coeff, -coeff); + }); }); builder.cycle_tracker_end("FRI rounds"); // assert that final_value[i] = folded @@ -669,7 +658,7 @@ pub(crate) fn batch_verifier_query_phase( ); }); let final_value = builder.get(&final_codeword.values, final_idx); - builder.assert_eq::>(final_value, folded); + // builder.assert_eq::>(final_value, folded); }, ); // 1. check initial claim match with first round sumcheck value From 8c5b7113c369cde4a2840e290ebccce85ca95e68 Mon Sep 17 00:00:00 2001 From: kunxian xia Date: Thu, 22 Jan 2026 15:37:23 +0800 Subject: [PATCH 3/4] fix base_mmcs & ext_mmcs verify errors --- .../src/basefold_verifier/query_phase.rs | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/ceno_recursion/src/basefold_verifier/query_phase.rs b/ceno_recursion/src/basefold_verifier/query_phase.rs index 30f057952..8921254d1 100644 --- a/ceno_recursion/src/basefold_verifier/query_phase.rs +++ b/ceno_recursion/src/basefold_verifier/query_phase.rs @@ -410,12 +410,11 @@ pub(crate) fn batch_verifier_query_phase( .for_each(|ptr_vec, builder| { let opening = builder.iter_ptr_get(&round.openings, ptr_vec[2]); let log2_height: Var = - builder.eval(opening.num_var + Usize::from(get_rate_log() - 1)); + builder.eval(opening.num_var + Usize::from(get_rate_log())); builder.iter_ptr_set(&log2_heights, ptr_vec[1], log2_height); let width = opening.point_and_evals.evals.len(); - let opened_value_len: Var = builder.eval(width.clone() * two); - let opened_value_buffer = builder.dyn_array(opened_value_len); + let opened_value_buffer = builder.dyn_array(width.clone()); builder.iter_ptr_set( &opened_values_buffer, ptr_vec[0], @@ -473,8 +472,8 @@ pub(crate) fn batch_verifier_query_phase( }); let idx_bits = idx_bits.slice(builder, 0, log2_max_codeword_size); - let reduced_codeword_by_height: Array> = - builder.dyn_array(log2_max_codeword_size); + let ro_len: Var = builder.eval(log2_max_codeword_size + Usize::from(1)); + let reduced_codeword_by_height: Array> = builder.dyn_array(ro_len); // initialize reduced_codeword_by_height with zeroes iter_zip!(builder, reduced_codeword_by_height).for_each(|ptr_vec, builder| { builder.set_value(&reduced_codeword_by_height, ptr_vec[0], zero); @@ -556,13 +555,16 @@ pub(crate) fn batch_verifier_query_phase( let log2_height: Var = builder.eval(initial_log2_height); - let coeff = verifier_folding_coeffs_level( - builder, - &two_adic_generators_inverses, - log2_height, - &idx_bits, - inv_2, - ); + let coeff = { + let leaf_idx_bits = idx_bits.slice(builder, 1, idx_bits.len()); + verifier_folding_coeffs_level( + builder, + &two_adic_generators_inverses, + log2_height, + &leaf_idx_bits, + inv_2, + ) + }; let folded = builder.constant(C::EF::ZERO); // check commit phases @@ -639,10 +641,12 @@ pub(crate) fn batch_verifier_query_phase( // Because `folded_idx` is just the `prev_bit`, so we reuse the following `if_eq` and multiply -1 // in the branch where `folded_idx` is != 0. builder.assign(&coeff, coeff * coeff * two_felt); - - builder.if_ne(folded_idx, Usize::from(0)).then(|builder| { - builder.assign(&coeff, -coeff); - }); + let next_folded_idx = builder.get(&idx_bits, i_plus_one.clone()); + builder + .if_ne(next_folded_idx, Usize::from(0)) + .then(|builder| { + builder.assign(&coeff, -coeff); + }); }); builder.cycle_tracker_end("FRI rounds"); // assert that final_value[i] = folded @@ -658,7 +662,7 @@ pub(crate) fn batch_verifier_query_phase( ); }); let final_value = builder.get(&final_codeword.values, final_idx); - // builder.assert_eq::>(final_value, folded); + builder.assert_eq::>(final_value, folded); }, ); // 1. check initial claim match with first round sumcheck value @@ -765,13 +769,22 @@ pub mod tests { BasefoldDefault, BasefoldRSParams, BasefoldSpec, PCSFriParam, PolynomialCommitmentScheme, pcs_batch_commit, pcs_trim, util::hash::write_digest_to_transcript, }; - use openvm_circuit::arch::{SystemConfig, VmExecutor, instructions::program::Program}; + use openvm_circuit::{ + arch::{SystemConfig, VmExecutor, instructions::program::Program}, + utils::{TestStarkEngine, air_test_impl}, + }; use openvm_instructions::exe::VmExe; - use openvm_native_circuit::{Native, NativeConfig}; + use openvm_native_circuit::{Native, NativeBuilder, NativeConfig}; use openvm_native_compiler::asm::AsmBuilder; use openvm_native_recursion::hints::Hintable; use openvm_stark_backend::p3_challenger::GrindingChallenger; - use openvm_stark_sdk::p3_baby_bear::BabyBear; + use openvm_stark_sdk::{ + config::{ + baby_bear_poseidon2::BabyBearPoseidon2Engine, + fri_params::standard_fri_params_with_100_bits_conjectured_security, + }, + p3_baby_bear::BabyBear, + }; use rand::thread_rng; use transcript::{BasicTranscript, Transcript}; @@ -782,6 +795,7 @@ pub mod tests { use crate::{ basefold_verifier::{ basefold::{Round, RoundOpening}, + mmcs::MmcsCommitment, query_phase::PointAndEvals, }, tower_verifier::binding::Point, @@ -932,12 +946,17 @@ pub mod tests { .with_max_segment_len((1 << 25) - 100); let config = NativeConfig::new(system_config, Native); - let executor = VmExecutor::::new(config).unwrap(); let exe = VmExe::new(program); + let executor = VmExecutor::::new(config.clone()).unwrap(); let interpreter = executor.instance(&exe).unwrap(); interpreter - .execute(witness, None) + .execute(witness.clone(), None) .expect("constructed test should pass"); + + let fri_params = standard_fri_params_with_100_bits_conjectured_security(1); + let vb = NativeBuilder::default(); + air_test_impl::(fri_params, vb, config, exe, witness, 1, true) + .unwrap(); } #[test] From d8bb3667f18fbff4de97c5d6a7cf2f4be6086e26 Mon Sep 17 00:00:00 2001 From: kunxian xia Date: Thu, 22 Jan 2026 15:45:28 +0800 Subject: [PATCH 4/4] clippy --- ceno_recursion/src/basefold_verifier/query_phase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ceno_recursion/src/basefold_verifier/query_phase.rs b/ceno_recursion/src/basefold_verifier/query_phase.rs index 8921254d1..6bf40113f 100644 --- a/ceno_recursion/src/basefold_verifier/query_phase.rs +++ b/ceno_recursion/src/basefold_verifier/query_phase.rs @@ -641,7 +641,7 @@ pub(crate) fn batch_verifier_query_phase( // Because `folded_idx` is just the `prev_bit`, so we reuse the following `if_eq` and multiply -1 // in the branch where `folded_idx` is != 0. builder.assign(&coeff, coeff * coeff * two_felt); - let next_folded_idx = builder.get(&idx_bits, i_plus_one.clone()); + let next_folded_idx = builder.get(&idx_bits, i_plus_one); builder .if_ne(next_folded_idx, Usize::from(0)) .then(|builder| {