From dfc5b8afeb4d5e119ea6a1ac288671b305ae33f7 Mon Sep 17 00:00:00 2001 From: kunxian xia Date: Thu, 2 Jul 2026 23:25:44 +0800 Subject: [PATCH] Move transcript replay to proof shape --- ceno_recursion_v2/src/circuit/inner/mod.rs | 8 - ceno_recursion_v2/src/circuit/inner/trace.rs | 6 - .../src/circuit/inner/vm_pvs/air.rs | 191 +----------------- .../src/circuit/inner/vm_pvs/mod.rs | 59 +----- .../src/circuit/inner/vm_pvs/trace.rs | 48 +---- .../src/continuation/prover/inner/mod.rs | 4 - ceno_recursion_v2/src/proof_shape/mod.rs | 60 +++++- .../src/proof_shape/proof_shape/air.rs | 155 +++++++++++++- .../src/proof_shape/proof_shape/trace.rs | 90 ++++++++- ceno_recursion_v2/src/proof_shape/pvs/air.rs | 19 +- ceno_recursion_v2/src/system/mod.rs | 51 +---- ceno_recursion_v2/src/system/preflight/mod.rs | 9 - ceno_recursion_v2/src/tower/mod.rs | 47 ++--- ceno_recursion_v2/src/transcript/mod.rs | 7 +- 14 files changed, 364 insertions(+), 390 deletions(-) diff --git a/ceno_recursion_v2/src/circuit/inner/mod.rs b/ceno_recursion_v2/src/circuit/inner/mod.rs index a754f9b17..cedc5134d 100644 --- a/ceno_recursion_v2/src/circuit/inner/mod.rs +++ b/ceno_recursion_v2/src/circuit/inner/mod.rs @@ -29,18 +29,14 @@ pub use trace::*; pub struct InnerCircuit { pub verifier_circuit: Arc, pub def_hook_commit: Option, - pub has_fixed_commit: bool, - pub has_fixed_no_omc_init_commit: bool, pub instance_public_value_indices: Arc>>, } impl, S: AggregationSubCircuit> Circuit for InnerCircuit { fn airs(&self) -> Vec> { let bus_inventory = self.verifier_circuit.bus_inventory(); - let transcript_bus = bus_inventory.transcript_bus; let public_values_bus = bus_inventory.public_values_bus; let cached_commit_bus = bus_inventory.cached_commit_bus; - let lookup_challenge_bus = bus_inventory.lookup_challenge_bus; let poseidon2_compress_bus = bus_inventory.poseidon2_compress_bus; let pvs_air_consistency_bus = PvsAirConsistencyBus::new(self.verifier_circuit.next_bus_idx()); @@ -63,14 +59,10 @@ impl, S: AggregationSubCircuit> Circuit for I }) as AirRef; let vm_pvs_air = Arc::new(vm_pvs::VmPvsAir { - transcript_bus, public_values_bus, cached_commit_bus, - lookup_challenge_bus, pvs_air_consistency_bus, deferral_enabled, - has_fixed_commit: self.has_fixed_commit, - has_fixed_no_omc_init_commit: self.has_fixed_no_omc_init_commit, instance_public_value_indices: self.instance_public_value_indices.clone(), }) as AirRef; diff --git a/ceno_recursion_v2/src/circuit/inner/trace.rs b/ceno_recursion_v2/src/circuit/inner/trace.rs index 980e82288..d577ecb1b 100644 --- a/ceno_recursion_v2/src/circuit/inner/trace.rs +++ b/ceno_recursion_v2/src/circuit/inner/trace.rs @@ -89,12 +89,6 @@ impl InnerTraceGen> for InnerTraceGenImpl { let mut sponge = initial_transcript.clone(); let mut preflight = Preflight::default(); super::verifier::run_preflight(child_vk, proof, &mut preflight, &mut sponge); - super::vm_pvs::run_preflight(child_vk, proof, &mut preflight, &mut sponge); - let num_lookup_challenge_consumers = proof.chip_proofs.len(); - preflight.vm_pvs.lookup_challenge_alpha_lookup_count = - num_lookup_challenge_consumers; - preflight.vm_pvs.lookup_challenge_beta_lookup_count = - num_lookup_challenge_consumers; (preflight, sponge) }) .unzip(); diff --git a/ceno_recursion_v2/src/circuit/inner/vm_pvs/air.rs b/ceno_recursion_v2/src/circuit/inner/vm_pvs/air.rs index 14de5826a..12c807b38 100644 --- a/ceno_recursion_v2/src/circuit/inner/vm_pvs/air.rs +++ b/ceno_recursion_v2/src/circuit/inner/vm_pvs/air.rs @@ -1,34 +1,24 @@ use std::{borrow::Borrow, sync::Arc}; use ceno_emul::{FullTracer as Tracer, WORD_SIZE}; -use ceno_zkvm::{ - instructions::riscv::constants::{ - END_CYCLE_IDX, END_PC_IDX, EXIT_CODE_IDX, HEAP_LENGTH_IDX, HEAP_START_ADDR_IDX, - HINT_LENGTH_IDX, HINT_START_ADDR_IDX, INIT_CYCLE_IDX, INIT_PC_IDX, PUBIO_DIGEST_IDX, - PUBIO_DIGEST_U16_LIMBS, SHARD_ID_IDX, SHARD_RW_SUM_IDX, - }, - structs::VK_DIGEST_LEN, +use ceno_zkvm::instructions::riscv::constants::{ + END_CYCLE_IDX, END_PC_IDX, EXIT_CODE_IDX, HEAP_LENGTH_IDX, HEAP_START_ADDR_IDX, + HINT_LENGTH_IDX, HINT_START_ADDR_IDX, INIT_CYCLE_IDX, INIT_PC_IDX, PUBIO_DIGEST_IDX, + PUBIO_DIGEST_U16_LIMBS, SHARD_ID_IDX, SHARD_RW_SUM_IDX, }; use openvm_circuit_primitives::utils::{and, assert_array_eq, not}; use openvm_stark_backend::{ BaseAirWithPublicValues, PartitionedBaseAir, interaction::InteractionBuilder, }; -use openvm_stark_sdk::config::baby_bear_poseidon2::{D_EF, DIGEST_SIZE}; use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir}; use p3_field::PrimeCharacteristicRing; use p3_matrix::Matrix; -use recursion_circuit::bus::{ - CachedCommitBus, PublicValuesBus, PublicValuesBusMessage, TranscriptBus, TranscriptBusMessage, -}; +use recursion_circuit::bus::{CachedCommitBus, PublicValuesBus, PublicValuesBusMessage}; use stark_recursion_circuit_derive::AlignedBorrow; -use crate::{ - bus::{LookupChallengeBus, LookupChallengeKind, LookupChallengeMessage}, - circuit::inner::{ - bus::{PvsAirConsistencyBus, PvsAirConsistencyMessage}, - vm_pvs::VmPvs, - }, - utils::TranscriptLabel, +use crate::circuit::inner::{ + bus::{PvsAirConsistencyBus, PvsAirConsistencyMessage}, + vm_pvs::VmPvs, }; #[repr(C)] @@ -38,26 +28,14 @@ pub struct VmPvsCols { pub is_valid: F, pub is_last: F, pub has_verifier_pvs: F, - pub vk_digest: [[F; D_EF]; VK_DIGEST_LEN], - pub lookup_challenge_alpha: [F; D_EF], - pub lookup_challenge_beta: [F; D_EF], - pub lookup_challenge_alpha_lookup_count: F, - pub lookup_challenge_beta_lookup_count: F, - pub fixed_commit_log2_max_codeword_size: F, - pub fixed_no_omc_init_commit_log2_max_codeword_size: F, - pub witness_commit_log2_max_codeword_size: F, pub child_pvs: VmPvs, } pub struct VmPvsAir { - pub transcript_bus: TranscriptBus, pub public_values_bus: PublicValuesBus, pub cached_commit_bus: CachedCommitBus, - pub lookup_challenge_bus: LookupChallengeBus, pub pvs_air_consistency_bus: PvsAirConsistencyBus, pub deferral_enabled: bool, - pub has_fixed_commit: bool, - pub has_fixed_no_omc_init_commit: bool, pub instance_public_value_indices: Arc>>, } @@ -198,159 +176,6 @@ impl Air f // local.is_valid * is_leaf, // ); - // Mirror the native verifier transcript prefix exactly: - // vk digest, transcript-visible public values, commitments with size - // fields, then lookup challenge samples. - for (tidx, value) in [(0usize, 1668508018u32), (1usize, 118u32)] { - self.transcript_bus.receive( - builder, - local.proof_idx, - TranscriptBusMessage { - tidx: AB::Expr::from_usize(tidx), - value: AB::Expr::from_u32(value), - is_sample: AB::Expr::ZERO, - }, - local.is_valid, - ); - } - - let mut transcript_tidx = TranscriptLabel::Riscv.field_len(); - for digest in local.vk_digest { - self.transcript_bus.observe_ext( - builder, - local.proof_idx, - AB::Expr::from_usize(transcript_tidx), - digest, - local.is_valid, - ); - transcript_tidx += D_EF; - } - - for instance_indices in self.instance_public_value_indices.iter() { - for global_pv_idx in instance_indices { - self.transcript_bus.receive( - builder, - local.proof_idx, - TranscriptBusMessage { - tidx: AB::Expr::from_usize(transcript_tidx), - value: vm_public_value_by_index::(local, *global_pv_idx), - is_sample: AB::Expr::ZERO, - }, - local.is_valid, - ); - transcript_tidx += 1; - } - } - - if self.has_fixed_commit { - for (didx, value) in local.child_pvs.fixed_commit.iter().enumerate() { - self.transcript_bus.receive( - builder, - local.proof_idx, - TranscriptBusMessage { - tidx: AB::Expr::from_usize(transcript_tidx + didx), - value: (*value).into(), - is_sample: AB::Expr::ZERO, - }, - local.is_valid, - ); - } - transcript_tidx += DIGEST_SIZE; - self.transcript_bus.observe( - builder, - local.proof_idx, - AB::Expr::from_usize(transcript_tidx), - local.fixed_commit_log2_max_codeword_size, - local.is_valid, - ); - transcript_tidx += 1; - } - - if self.has_fixed_no_omc_init_commit { - for (didx, value) in local.child_pvs.fixed_no_omc_init_commit.iter().enumerate() { - self.transcript_bus.receive( - builder, - local.proof_idx, - TranscriptBusMessage { - tidx: AB::Expr::from_usize(transcript_tidx + didx), - value: (*value).into(), - is_sample: AB::Expr::ZERO, - }, - local.is_valid, - ); - } - transcript_tidx += DIGEST_SIZE; - self.transcript_bus.observe( - builder, - local.proof_idx, - AB::Expr::from_usize(transcript_tidx), - local.fixed_no_omc_init_commit_log2_max_codeword_size, - local.is_valid, - ); - transcript_tidx += 1; - } - - for (didx, value) in local.child_pvs.witness_commit.iter().enumerate() { - self.transcript_bus.receive( - builder, - local.proof_idx, - TranscriptBusMessage { - tidx: AB::Expr::from_usize(transcript_tidx + didx), - value: (*value).into(), - is_sample: AB::Expr::ZERO, - }, - local.is_valid, - ); - } - transcript_tidx += DIGEST_SIZE; - self.transcript_bus.observe( - builder, - local.proof_idx, - AB::Expr::from_usize(transcript_tidx), - local.witness_commit_log2_max_codeword_size, - local.is_valid, - ); - transcript_tidx += 1; - - self.transcript_bus.sample_ext( - builder, - local.proof_idx, - AB::Expr::from_usize(transcript_tidx), - local.lookup_challenge_alpha, - local.is_valid, - ); - transcript_tidx += D_EF; - self.transcript_bus.sample_ext( - builder, - local.proof_idx, - AB::Expr::from_usize(transcript_tidx), - local.lookup_challenge_beta, - local.is_valid, - ); - - for i in 0..D_EF { - self.lookup_challenge_bus.add_key_with_lookups( - builder, - local.proof_idx, - LookupChallengeMessage { - kind: AB::Expr::from_usize(LookupChallengeKind::Alpha.as_usize()), - word_idx: AB::Expr::from_usize(i), - value: local.lookup_challenge_alpha[i].into(), - }, - local.is_valid * local.lookup_challenge_alpha_lookup_count, - ); - self.lookup_challenge_bus.add_key_with_lookups( - builder, - local.proof_idx, - LookupChallengeMessage { - kind: AB::Expr::from_usize(LookupChallengeKind::Beta.as_usize()), - word_idx: AB::Expr::from_usize(i), - value: local.lookup_challenge_beta[i].into(), - }, - local.is_valid * local.lookup_challenge_beta_lookup_count, - ); - } - // We look up proof metadata from VerifierPvsAir here to ensure consistency on each row. self.pvs_air_consistency_bus.lookup_key( builder, diff --git a/ceno_recursion_v2/src/circuit/inner/vm_pvs/mod.rs b/ceno_recursion_v2/src/circuit/inner/vm_pvs/mod.rs index da9c73da9..388e4f408 100644 --- a/ceno_recursion_v2/src/circuit/inner/vm_pvs/mod.rs +++ b/ceno_recursion_v2/src/circuit/inner/vm_pvs/mod.rs @@ -1,11 +1,7 @@ use ceno_zkvm::instructions::riscv::constants::PUBIO_DIGEST_U16_LIMBS; -use openvm_stark_backend::{FiatShamirTranscript, TranscriptHistory}; -use openvm_stark_sdk::config::baby_bear_poseidon2::{BabyBearPoseidon2Config, DIGEST_SIZE, F}; -use p3_field::PrimeCharacteristicRing; +use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE; use stark_recursion_circuit_derive::AlignedBorrow; -use crate::system::{Preflight, RecursionField, RecursionProof, RecursionVk}; - mod air; mod trace; @@ -35,56 +31,3 @@ pub struct VmPvs { pub use air::*; pub use trace::*; - -#[tracing::instrument(level = "trace", skip_all)] -pub fn run_preflight( - child_vk: &RecursionVk, - proof: &RecursionProof, - preflight: &mut Preflight, - ts: &mut TS, -) where - TS: FiatShamirTranscript + TranscriptHistory, -{ - let vk_digest = child_vk.compute_digest(); - for elem in vk_digest { - ts.observe_ext(elem); - } - - // Observe public values in canonical circuit-instance order first. - for (_, circuit_vk) in child_vk.circuit_vks.iter() { - for instance_value in circuit_vk.get_cs().zkvm_v1_css.instance.iter() { - ts.observe( - proof - .public_values - .query_by_index::(instance_value.0), - ); - } - } - - if let Some(fixed_commit) = child_vk.fixed_commit.as_ref() { - for elem in fixed_commit.commit.clone().into_iter() { - ts.observe(elem); - } - ts.observe(F::from_u64(fixed_commit.log2_max_codeword_size as u64)); - } - - if let Some(fixed_no_omc) = child_vk.fixed_no_omc_init_commit.as_ref() { - for elem in fixed_no_omc.commit.clone().into_iter() { - ts.observe(elem); - } - ts.observe(F::from_u64(fixed_no_omc.log2_max_codeword_size as u64)); - } - - let witin = &proof.witin_commit; - for elem in witin.commit.clone().into_iter() { - ts.observe(elem); - } - ts.observe(F::from_u64(witin.log2_max_codeword_size as u64)); - - let alpha_ext = ts.sample_ext(); - let beta_ext = ts.sample_ext(); - preflight.vm_pvs.lookup_challenge_alpha = alpha_ext; - preflight.vm_pvs.lookup_challenge_beta = beta_ext; - preflight.vm_pvs.lookup_challenge_alpha_lookup_count = 0; - preflight.vm_pvs.lookup_challenge_beta_lookup_count = 0; -} diff --git a/ceno_recursion_v2/src/circuit/inner/vm_pvs/trace.rs b/ceno_recursion_v2/src/circuit/inner/vm_pvs/trace.rs index 10523e93a..fc328101d 100644 --- a/ceno_recursion_v2/src/circuit/inner/vm_pvs/trace.rs +++ b/ceno_recursion_v2/src/circuit/inner/vm_pvs/trace.rs @@ -1,10 +1,8 @@ use ceno_zkvm::instructions::riscv::constants::{LIMB_BITS, LIMB_MASK, PUBIO_DIGEST_U16_LIMBS}; use openvm_cpu_backend::CpuBackend; use openvm_stark_backend::prover::AirProvingContext; -use openvm_stark_sdk::config::baby_bear_poseidon2::{ - BabyBearPoseidon2Config, D_EF, DIGEST_SIZE, EF, F, -}; -use p3_field::{BasedVectorSpace, PrimeCharacteristicRing}; +use openvm_stark_sdk::config::baby_bear_poseidon2::{BabyBearPoseidon2Config, DIGEST_SIZE, F}; +use p3_field::PrimeCharacteristicRing; use p3_matrix::dense::RowMajorMatrix; use std::borrow::BorrowMut; @@ -19,12 +17,12 @@ use crate::{ pub fn generate_proving_ctx( child_vk: &RecursionVk, proofs: &[RecursionProof], - preflights: &[Preflight], + _preflights: &[Preflight], proofs_type: ProofsType, child_is_app: bool, deferral_enabled: bool, ) -> AirProvingContext> { - debug_assert_eq!(proofs.len(), preflights.len()); + debug_assert_eq!(proofs.len(), _preflights.len()); let _ = (proofs_type, child_is_app); assert!( !deferral_enabled, @@ -39,26 +37,14 @@ pub fn generate_proving_ctx( child_vk .fixed_commit .as_ref() - .map(|commitment| commitment.commit.clone()), + .map(|commitment| commitment.commit), ); let fixed_no_omc_init_commit = extract_commit( child_vk .fixed_no_omc_init_commit .as_ref() - .map(|commitment| commitment.commit.clone()), + .map(|commitment| commitment.commit), ); - let fixed_commit_log2_max_codeword_size = child_vk - .fixed_commit - .as_ref() - .map(|commitment| commitment.log2_max_codeword_size) - .unwrap_or_default(); - let fixed_no_omc_init_commit_log2_max_codeword_size = child_vk - .fixed_no_omc_init_commit - .as_ref() - .map(|commitment| commitment.log2_max_codeword_size) - .unwrap_or_default(); - let vk_digest = child_vk.compute_digest().map(ef_to_limbs); - for (row_idx, row) in trace.chunks_exact_mut(width).enumerate() { let (base_row, def_row) = row.split_at_mut(VmPvsCols::::width()); let cols: &mut VmPvsCols = base_row.borrow_mut(); @@ -66,23 +52,9 @@ pub fn generate_proving_ctx( if row_idx < proofs.len() { let proof = &proofs[row_idx]; - let preflight = &preflights[row_idx]; cols.is_valid = F::ONE; cols.is_last = F::from_bool(row_idx + 1 == proofs.len()); cols.has_verifier_pvs = F::ZERO; - cols.vk_digest = vk_digest; - cols.lookup_challenge_alpha = ef_to_limbs(preflight.vm_pvs.lookup_challenge_alpha); - cols.lookup_challenge_beta = ef_to_limbs(preflight.vm_pvs.lookup_challenge_beta); - cols.lookup_challenge_alpha_lookup_count = - F::from_usize(preflight.vm_pvs.lookup_challenge_alpha_lookup_count); - cols.lookup_challenge_beta_lookup_count = - F::from_usize(preflight.vm_pvs.lookup_challenge_beta_lookup_count); - cols.fixed_commit_log2_max_codeword_size = - F::from_usize(fixed_commit_log2_max_codeword_size); - cols.fixed_no_omc_init_commit_log2_max_codeword_size = - F::from_usize(fixed_no_omc_init_commit_log2_max_codeword_size); - cols.witness_commit_log2_max_codeword_size = - F::from_usize(proof.witin_commit.log2_max_codeword_size); cols.child_pvs = build_vm_pvs(fixed_commit, fixed_no_omc_init_commit, proof); } @@ -117,7 +89,7 @@ fn build_vm_pvs( VmPvs { fixed_commit, fixed_no_omc_init_commit, - witness_commit: extract_commit(Some(proof.witin_commit.commit.clone())), + witness_commit: extract_commit(Some(proof.witin_commit.commit)), exit_code: split_u32_lo_hi(pv.exit_code), init_pc: F::from_u32(pv.init_pc), init_cycle: F::from_u64(pv.init_cycle), @@ -157,9 +129,3 @@ fn split_public_io_digest(words: [u32; 8]) -> [F; PUBIO_DIGEST_U16_LIMBS] { F::from_u32((words[word_idx] >> (limb_idx * LIMB_BITS)) & LIMB_MASK) }) } - -fn ef_to_limbs(value: EF) -> [F; D_EF] { - let mut out = [F::ZERO; D_EF]; - out.copy_from_slice(value.as_basis_coefficients_slice()); - out -} diff --git a/ceno_recursion_v2/src/continuation/prover/inner/mod.rs b/ceno_recursion_v2/src/continuation/prover/inner/mod.rs index d3af0ff1c..051af5d82 100644 --- a/ceno_recursion_v2/src/continuation/prover/inner/mod.rs +++ b/ceno_recursion_v2/src/continuation/prover/inner/mod.rs @@ -82,8 +82,6 @@ impl< let circuit = Arc::new(InnerCircuit::new( Arc::new(verifier_circuit), def_hook_commit.map(|d| d.into()), - child_vk.fixed_commit.is_some(), - child_vk.fixed_no_omc_init_commit.is_some(), instance_public_value_indices, )); let (pk, vk) = engine.keygen(&circuit.airs()); @@ -129,8 +127,6 @@ impl< let circuit = Arc::new(InnerCircuit::new( Arc::new(verifier_circuit), def_hook_commit.map(|d| d.into()), - child_vk.fixed_commit.is_some(), - child_vk.fixed_no_omc_init_commit.is_some(), instance_public_value_indices, )); let vk = Arc::new(pk.get_vk()); diff --git a/ceno_recursion_v2/src/proof_shape/mod.rs b/ceno_recursion_v2/src/proof_shape/mod.rs index 6df925b2c..ad5cd591d 100644 --- a/ceno_recursion_v2/src/proof_shape/mod.rs +++ b/ceno_recursion_v2/src/proof_shape/mod.rs @@ -7,8 +7,8 @@ use openvm_stark_backend::{ AirRef, FiatShamirTranscript, StarkProtocolConfig, TranscriptHistory, p3_maybe_rayon::prelude::*, prover::AirProvingContext, }; -use openvm_stark_sdk::config::baby_bear_poseidon2::{BabyBearPoseidon2Config, F}; -use p3_field::PrimeCharacteristicRing; +use openvm_stark_sdk::config::baby_bear_poseidon2::{BabyBearPoseidon2Config, D_EF, EF, F}; +use p3_field::{BasedVectorSpace, PrimeCharacteristicRing}; use p3_matrix::dense::RowMajorMatrix; use crate::{ @@ -19,7 +19,7 @@ use crate::{ }, system::{ AirModule, BusIndexManager, BusInventory, GlobalCtxCpu, POW_CHECKER_HEIGHT, Preflight, - RecursionProof, RecursionVk, TraceGenModule, TraceVData, + RecursionField, RecursionProof, RecursionVk, TraceGenModule, TraceVData, }, tracegen::{ModuleChip, RowMajorChip}, }; @@ -52,6 +52,8 @@ pub struct AirMetadata { pub struct ProofShapeModule { // Verifying key fields per_air: Vec, + has_fixed_commit: bool, + has_fixed_no_omc_init_commit: bool, // Buses (inventory for external, others are internal) bus_inventory: BusInventory, @@ -82,6 +84,8 @@ impl ProofShapeModule { let range_bus = bus_inventory.range_checker_bus; Self { per_air, + has_fixed_commit: child_vk.fixed_commit.is_some(), + has_fixed_no_omc_init_commit: child_vk.fixed_no_omc_init_commit.is_some(), bus_inventory, range_bus, permutation_bus: ProofShapePermutationBus::new(b.new_bus_idx()), @@ -102,7 +106,47 @@ impl ProofShapeModule { ) where TS: FiatShamirTranscript + TranscriptHistory, { - let _ = self; + let vk_digest = child_vk.compute_digest(); + for elem in vk_digest { + ts.observe_ext(elem); + } + + // Observe public values in canonical circuit-instance order. PublicValuesAir constrains + // the corresponding TranscriptBus receives; proof-shape owns the surrounding prefix. + for (_, circuit_vk) in child_vk.circuit_vks.iter() { + for instance_value in circuit_vk.get_cs().zkvm_v1_css.instance.iter() { + ts.observe( + proof + .public_values + .query_by_index::(instance_value.0), + ); + } + } + + if let Some(fixed_commit) = child_vk.fixed_commit.as_ref() { + for elem in fixed_commit.commit.into_iter() { + ts.observe(elem); + } + ts.observe(F::from_u64(fixed_commit.log2_max_codeword_size as u64)); + } + + if let Some(fixed_no_omc) = child_vk.fixed_no_omc_init_commit.as_ref() { + for elem in fixed_no_omc.commit.into_iter() { + ts.observe(elem); + } + ts.observe(F::from_u64(fixed_no_omc.log2_max_codeword_size as u64)); + } + + let witin = &proof.witin_commit; + for elem in witin.commit.into_iter() { + ts.observe(elem); + } + ts.observe(F::from_u64(witin.log2_max_codeword_size as u64)); + + let alpha_ext = ts.sample_ext(); + let beta_ext = ts.sample_ext(); + preflight.proof_shape.lookup_challenge_alpha = ef_to_limbs(alpha_ext); + preflight.proof_shape.lookup_challenge_beta = ef_to_limbs(beta_ext); let transcript_start_tidx = ts.len(); preflight.proof_shape.fork_start_tidx = ts.len(); @@ -248,6 +292,12 @@ fn extract_air_metadata_from_vk(child_vk: &RecursionVk) -> Vec { .collect_vec() } +fn ef_to_limbs(value: EF) -> [F; D_EF] { + let mut out = [F::ZERO; D_EF]; + out.copy_from_slice(value.as_basis_coefficients_slice()); + out +} + impl AirModule for ProofShapeModule { fn num_airs(&self) -> usize { 3 @@ -256,6 +306,8 @@ impl AirModule for ProofShapeModule { fn airs>(&self) -> Vec> { let proof_shape_air = ProofShapeAir::<4, 8> { per_air: self.per_air.clone(), + has_fixed_commit: self.has_fixed_commit, + has_fixed_no_omc_init_commit: self.has_fixed_no_omc_init_commit, idx_encoder: self.idx_encoder.clone(), range_bus: self.range_bus, permutation_bus: self.permutation_bus, diff --git a/ceno_recursion_v2/src/proof_shape/proof_shape/air.rs b/ceno_recursion_v2/src/proof_shape/proof_shape/air.rs index 483db9519..1c2957c53 100644 --- a/ceno_recursion_v2/src/proof_shape/proof_shape/air.rs +++ b/ceno_recursion_v2/src/proof_shape/proof_shape/air.rs @@ -1,5 +1,6 @@ use std::{borrow::Borrow, sync::Arc}; +use ceno_zkvm::structs::VK_DIGEST_LEN; use itertools::fold; use openvm_circuit_primitives::{ SubAir, @@ -9,7 +10,7 @@ use openvm_circuit_primitives::{ use openvm_stark_backend::{ BaseAirWithPublicValues, PartitionedBaseAir, interaction::InteractionBuilder, }; -use openvm_stark_sdk::config::baby_bear_poseidon2::D_EF; +use openvm_stark_sdk::config::baby_bear_poseidon2::{D_EF, DIGEST_SIZE}; use p3_air::{Air, AirBuilder, BaseAir}; use p3_field::{PrimeCharacteristicRing, PrimeField32}; use p3_matrix::Matrix; @@ -87,6 +88,13 @@ pub struct ProofShapeCols { /// forking). Constrained to be identical across all rows within a proof. pub lookup_challenge_alpha: [F; D_EF], pub lookup_challenge_beta: [F; D_EF], + pub vk_digest: [[F; D_EF]; VK_DIGEST_LEN], + pub fixed_commit: [F; DIGEST_SIZE], + pub fixed_commit_log2_max_codeword_size: F, + pub fixed_no_omc_init_commit: [F; DIGEST_SIZE], + pub fixed_no_omc_init_commit_log2_max_codeword_size: F, + pub witness_commit: [F; DIGEST_SIZE], + pub witness_commit_log2_max_codeword_size: F, /// Fork-local tidx of the final fork sample merged back into the trunk transcript. pub fork_sample_tidx: F, /// Trunk tidx where this fork's final sample is observed during merge. @@ -111,6 +119,8 @@ pub struct ProofShapeVarCols<'a, F> { pub struct ProofShapeAir { // Parameters derived from vk pub per_air: Vec, + pub has_fixed_commit: bool, + pub has_fixed_no_omc_init_commit: bool, // Primitives pub idx_encoder: Arc, @@ -333,6 +343,149 @@ where // TRANSCRIPT OBSERVATIONS /////////////////////////////////////////////////////////////////////////////////////////// + // The RISC-V label is observed before proof-shape preflight starts, but proof-shape owns + // the verifier transcript prefix that follows it. + for (tidx, value) in [(0usize, 1668508018u32), (1usize, 118u32)] { + self.transcript_bus.receive( + builder, + local.proof_idx, + TranscriptBusMessage { + tidx: AB::Expr::from_usize(tidx), + value: AB::Expr::from_u32(value), + is_sample: AB::Expr::ZERO, + }, + local.is_last, + ); + } + + let mut prefix_tidx = TranscriptLabel::Riscv.field_len(); + for digest in local.vk_digest { + self.transcript_bus.observe_ext( + builder, + local.proof_idx, + AB::Expr::from_usize(prefix_tidx), + digest, + local.is_last, + ); + prefix_tidx += D_EF; + } + + let num_public_values = self + .per_air + .iter() + .map(|air_data| air_data.num_public_values) + .sum::(); + prefix_tidx += num_public_values; + + if self.has_fixed_commit { + for (didx, value) in local.fixed_commit.iter().enumerate() { + self.transcript_bus.receive( + builder, + local.proof_idx, + TranscriptBusMessage { + tidx: AB::Expr::from_usize(prefix_tidx + didx), + value: (*value).into(), + is_sample: AB::Expr::ZERO, + }, + local.is_last, + ); + } + prefix_tidx += DIGEST_SIZE; + self.transcript_bus.observe( + builder, + local.proof_idx, + AB::Expr::from_usize(prefix_tidx), + local.fixed_commit_log2_max_codeword_size, + local.is_last, + ); + prefix_tidx += 1; + } + + if self.has_fixed_no_omc_init_commit { + for (didx, value) in local.fixed_no_omc_init_commit.iter().enumerate() { + self.transcript_bus.receive( + builder, + local.proof_idx, + TranscriptBusMessage { + tidx: AB::Expr::from_usize(prefix_tidx + didx), + value: (*value).into(), + is_sample: AB::Expr::ZERO, + }, + local.is_last, + ); + } + prefix_tidx += DIGEST_SIZE; + self.transcript_bus.observe( + builder, + local.proof_idx, + AB::Expr::from_usize(prefix_tidx), + local.fixed_no_omc_init_commit_log2_max_codeword_size, + local.is_last, + ); + prefix_tidx += 1; + } + + for (didx, value) in local.witness_commit.iter().enumerate() { + self.transcript_bus.receive( + builder, + local.proof_idx, + TranscriptBusMessage { + tidx: AB::Expr::from_usize(prefix_tidx + didx), + value: (*value).into(), + is_sample: AB::Expr::ZERO, + }, + local.is_last, + ); + } + prefix_tidx += DIGEST_SIZE; + self.transcript_bus.observe( + builder, + local.proof_idx, + AB::Expr::from_usize(prefix_tidx), + local.witness_commit_log2_max_codeword_size, + local.is_last, + ); + prefix_tidx += 1; + + self.transcript_bus.sample_ext( + builder, + local.proof_idx, + AB::Expr::from_usize(prefix_tidx), + local.lookup_challenge_alpha, + local.is_last, + ); + prefix_tidx += D_EF; + self.transcript_bus.sample_ext( + builder, + local.proof_idx, + AB::Expr::from_usize(prefix_tidx), + local.lookup_challenge_beta, + local.is_last, + ); + + for i in 0..D_EF { + self.lookup_challenge_bus.add_key_with_lookups( + builder, + local.proof_idx, + LookupChallengeMessage { + kind: AB::Expr::from_usize(LookupChallengeKind::Alpha.as_usize()), + word_idx: AB::Expr::from_usize(i), + value: local.lookup_challenge_alpha[i].into(), + }, + local.is_last * local.num_present, + ); + self.lookup_challenge_bus.add_key_with_lookups( + builder, + local.proof_idx, + LookupChallengeMessage { + kind: AB::Expr::from_usize(LookupChallengeKind::Beta.as_usize()), + word_idx: AB::Expr::from_usize(i), + value: local.lookup_challenge_beta[i].into(), + }, + local.is_last * local.num_present, + ); + } + self.starting_tidx_bus.receive( builder, local.proof_idx, diff --git a/ceno_recursion_v2/src/proof_shape/proof_shape/trace.rs b/ceno_recursion_v2/src/proof_shape/proof_shape/trace.rs index 6cbdc88b3..129326128 100644 --- a/ceno_recursion_v2/src/proof_shape/proof_shape/trace.rs +++ b/ceno_recursion_v2/src/proof_shape/proof_shape/trace.rs @@ -1,7 +1,8 @@ use std::{borrow::BorrowMut, sync::Arc}; +use ceno_zkvm::structs::VK_DIGEST_LEN; use openvm_circuit_primitives::encoder::Encoder; -use openvm_stark_sdk::config::baby_bear_poseidon2::{D_EF, EF, F}; +use openvm_stark_sdk::config::baby_bear_poseidon2::{D_EF, DIGEST_SIZE, EF, F}; use p3_field::{BasedVectorSpace, PrimeCharacteristicRing}; use p3_matrix::dense::RowMajorMatrix; @@ -56,6 +57,43 @@ fn assign_ext(dst: &mut [F; D_EF], value: EF) { dst.copy_from_slice(value.as_basis_coefficients_slice()); } +fn ef_to_limbs(value: EF) -> [F; D_EF] { + let mut out = [F::ZERO; D_EF]; + assign_ext(&mut out, value); + out +} + +fn extract_commit(commit: Option>) -> [F; DIGEST_SIZE] { + let mut out = [F::ZERO; DIGEST_SIZE]; + if let Some(commit) = commit { + for (dst, src) in out.iter_mut().zip(commit) { + *dst = src; + } + } + out +} + +#[allow(clippy::too_many_arguments)] +fn assign_transcript_prefix_cols( + cols: &mut ProofShapeCols, + vk_digest: [[F; D_EF]; VK_DIGEST_LEN], + fixed_commit: [F; DIGEST_SIZE], + fixed_commit_log2_max_codeword_size: usize, + fixed_no_omc_init_commit: [F; DIGEST_SIZE], + fixed_no_omc_init_commit_log2_max_codeword_size: usize, + proof: &RecursionProof, +) { + cols.vk_digest = vk_digest; + cols.fixed_commit = fixed_commit; + cols.fixed_commit_log2_max_codeword_size = F::from_usize(fixed_commit_log2_max_codeword_size); + cols.fixed_no_omc_init_commit = fixed_no_omc_init_commit; + cols.fixed_no_omc_init_commit_log2_max_codeword_size = + F::from_usize(fixed_no_omc_init_commit_log2_max_codeword_size); + cols.witness_commit = extract_commit(Some(proof.witin_commit.commit)); + cols.witness_commit_log2_max_codeword_size = + F::from_usize(proof.witin_commit.log2_max_codeword_size); +} + fn tower_layer_count(chip_proof: &ceno_zkvm::scheme::ZKVMChipProof) -> usize { let proof_layers = chip_proof.tower_proof.proofs.len(); let has_root_specs = !chip_proof.r_out_evals.is_empty() @@ -155,6 +193,29 @@ impl RowMajorChip let width = self.placeholder_width(); let mut trace = vec![F::ZERO; height * width]; let mut chunks = trace.chunks_exact_mut(width); + let vk_digest = child_vk.compute_digest().map(ef_to_limbs); + let fixed_commit = extract_commit( + child_vk + .fixed_commit + .as_ref() + .map(|commitment| commitment.commit), + ); + let fixed_no_omc_init_commit = extract_commit( + child_vk + .fixed_no_omc_init_commit + .as_ref() + .map(|commitment| commitment.commit), + ); + let fixed_commit_log2_max_codeword_size = child_vk + .fixed_commit + .as_ref() + .map(|commitment| commitment.log2_max_codeword_size) + .unwrap_or_default(); + let fixed_no_omc_init_commit_log2_max_codeword_size = child_vk + .fixed_no_omc_init_commit + .as_ref() + .map(|commitment| commitment.log2_max_codeword_size) + .unwrap_or_default(); for (proof_idx, (proof, preflight)) in proofs.iter().zip(preflights.iter()).enumerate() { let fork_id_by_chip: std::collections::BTreeMap = proof @@ -220,6 +281,15 @@ impl RowMajorChip cols.num_columns = F::ZERO; cols.lookup_challenge_alpha = preflight.proof_shape.lookup_challenge_alpha; cols.lookup_challenge_beta = preflight.proof_shape.lookup_challenge_beta; + assign_transcript_prefix_cols( + cols, + vk_digest, + fixed_commit, + fixed_commit_log2_max_codeword_size, + fixed_no_omc_init_commit, + fixed_no_omc_init_commit_log2_max_codeword_size, + proof, + ); if let Some((sample_tidx, sample)) = fork_merge_sample(preflight, fork_id) { cols.fork_sample_tidx = F::from_usize(sample_tidx); cols.merge_tidx = @@ -274,6 +344,15 @@ impl RowMajorChip cols.num_columns = F::ZERO; cols.lookup_challenge_alpha = preflight.proof_shape.lookup_challenge_alpha; cols.lookup_challenge_beta = preflight.proof_shape.lookup_challenge_beta; + assign_transcript_prefix_cols( + cols, + vk_digest, + fixed_commit, + fixed_commit_log2_max_codeword_size, + fixed_no_omc_init_commit, + fixed_no_omc_init_commit_log2_max_codeword_size, + proof, + ); cols.fork_sample_tidx = F::ZERO; cols.merge_tidx = F::ZERO; cols.fork_merge_sample = [F::ZERO; D_EF]; @@ -316,6 +395,15 @@ impl RowMajorChip cols.num_columns = F::ZERO; cols.lookup_challenge_alpha = preflight.proof_shape.lookup_challenge_alpha; cols.lookup_challenge_beta = preflight.proof_shape.lookup_challenge_beta; + assign_transcript_prefix_cols( + cols, + vk_digest, + fixed_commit, + fixed_commit_log2_max_codeword_size, + fixed_no_omc_init_commit, + fixed_no_omc_init_commit_log2_max_codeword_size, + proof, + ); cols.fork_sample_tidx = F::ZERO; cols.merge_tidx = F::ZERO; cols.fork_merge_sample = [F::ZERO; D_EF]; diff --git a/ceno_recursion_v2/src/proof_shape/pvs/air.rs b/ceno_recursion_v2/src/proof_shape/pvs/air.rs index 7aa106903..c2e562cf2 100644 --- a/ceno_recursion_v2/src/proof_shape/pvs/air.rs +++ b/ceno_recursion_v2/src/proof_shape/pvs/air.rs @@ -12,7 +12,7 @@ use ceno_zkvm::structs::VK_DIGEST_LEN; use openvm_stark_sdk::config::baby_bear_poseidon2::D_EF; use crate::{ - bus::{PublicValuesBus, PublicValuesBusMessage, TranscriptBus}, + bus::{PublicValuesBus, PublicValuesBusMessage, TranscriptBus, TranscriptBusMessage}, proof_shape::bus::NumPublicValuesBus, subairs::nested_for_loop::{NestedForLoopIoCols, NestedForLoopSubAir}, utils::TranscriptLabel, @@ -116,8 +116,8 @@ where when_same_air.assert_eq(next.pv_idx, local.pv_idx + AB::Expr::ONE); when_same_air.assert_eq(next.tidx, local.tidx + AB::Expr::ONE); - // VmPvsAir remains active outside the system module and consumes - // verifier public values through this bus. + // VmPvsAir remains active outside the system module and consumes verifier public values + // through this bus. self.public_values_bus.send( builder, local.proof_idx, @@ -130,7 +130,16 @@ where ); let _ = self.continuations_enabled; - // VmPvsAir owns the verifier transcript prefix, including these public values. - let _ = &self.transcript_bus; + // Public values are the per-AIR portion of the verifier transcript prefix. + self.transcript_bus.receive( + builder, + local.proof_idx, + TranscriptBusMessage { + tidx: local.tidx.into(), + value: local.value.into(), + is_sample: AB::Expr::ZERO, + }, + local.is_valid, + ); } } diff --git a/ceno_recursion_v2/src/system/mod.rs b/ceno_recursion_v2/src/system/mod.rs index 0cd0c2b1d..bb8ee167a 100644 --- a/ceno_recursion_v2/src/system/mod.rs +++ b/ceno_recursion_v2/src/system/mod.rs @@ -204,28 +204,6 @@ impl VerifierSubCircuit { Self::new_with_options(child_vk, VerifierConfig::default()) } - fn vm_pvs_lookup_challenges_from_transcript(sponge: &TS) -> (EF, EF) - where - TS: Clone + TranscriptHistory, - { - let log = sponge.clone().into_log(); - let values = log.values(); - let samples = log.samples(); - let start = values - .len() - .checked_sub(2 * D_EF) - .expect("VM PVS transcript must end with alpha/beta extension samples"); - debug_assert!( - samples[start..start + 2 * D_EF] - .iter() - .all(|is_sample| *is_sample), - "VM PVS transcript suffix must contain alpha/beta samples" - ); - let alpha = EF::from_basis_coefficients_fn(|i| values[start + i]); - let beta = EF::from_basis_coefficients_fn(|i| values[start + D_EF + i]); - (alpha, beta) - } - pub fn new_with_options(child_vk: Arc, config: VerifierConfig) -> Self { // let child_mvk = convert_vk_from_zkvm(child_vk.as_ref()); // let proof_shape_constraint = LinearConstraint { @@ -303,7 +281,7 @@ impl VerifierSubCircuit { /// Runs preflight for a single proof, with proper transcript forking. /// /// This mirrors the native verifier's `verify_proof_validity` fork protocol: - /// 1. Trunk: proof-shape metadata only (VmPvs preflight already observed/sampled α/β) + /// 1. Trunk: proof-shape observes the verifier prefix and samples α/β /// 2. Fork: fresh transcript per chip, observe fork prelude, run Tower + Main /// 3. Merge: each fork samples 1 ext element → observe into trunk #[tracing::instrument(name = "execute_preflight", skip_all)] @@ -320,24 +298,17 @@ impl VerifierSubCircuit { + Clone, { let mut preflight = Preflight::default(); - let (alpha_ext, beta_ext) = Self::vm_pvs_lookup_challenges_from_transcript(&sponge); - preflight.vm_pvs.lookup_challenge_alpha = alpha_ext; - preflight.vm_pvs.lookup_challenge_beta = beta_ext; // Phase 1: Trunk operations. - // Proof-shape metadata and alpha/beta sampling after pre-verifier transcript observes. + // Proof-shape owns the verifier transcript prefix and alpha/beta sampling. self.proof_shape .run_preflight(child_vk, proof, &mut preflight, &mut sponge); - let num_lookup_challenge_consumers = preflight.proof_shape.sorted_trace_vdata.len(); - preflight.vm_pvs.lookup_challenge_alpha_lookup_count = num_lookup_challenge_consumers; - preflight.vm_pvs.lookup_challenge_beta_lookup_count = num_lookup_challenge_consumers; - - // VmPvs is owned by the pre-system preflight. The incoming transcript - // has already sampled these two challenges; recover and forward them - // so forked chip transcripts bind the same alpha/beta as the native - // verifier. - preflight.proof_shape.lookup_challenge_alpha = ef_to_limbs(alpha_ext); - preflight.proof_shape.lookup_challenge_beta = ef_to_limbs(beta_ext); + let alpha_ext = + EF::from_basis_coefficients_slice(&preflight.proof_shape.lookup_challenge_alpha) + .expect("lookup challenge alpha must have extension-field width"); + let beta_ext = + EF::from_basis_coefficients_slice(&preflight.proof_shape.lookup_challenge_beta) + .expect("lookup challenge beta must have extension-field width"); // Mark where merge observations begin in the trunk transcript. let fork_offset = sponge.len(); @@ -479,12 +450,6 @@ impl VerifierSubCircuit { } } -fn ef_to_limbs(value: EF) -> [F; D_EF] { - let mut out = [F::ZERO; D_EF]; - out.copy_from_slice(value.as_basis_coefficients_slice()); - out -} - impl, const MAX_NUM_PROOFS: usize> VerifierTraceGen, SC> for VerifierSubCircuit { diff --git a/ceno_recursion_v2/src/system/preflight/mod.rs b/ceno_recursion_v2/src/system/preflight/mod.rs index 21fbafe8a..5acc3cf3a 100644 --- a/ceno_recursion_v2/src/system/preflight/mod.rs +++ b/ceno_recursion_v2/src/system/preflight/mod.rs @@ -18,7 +18,6 @@ pub struct Preflight { pub main: MainPreflight, pub gkr: TowerPreflight, pub batch_constraint: BatchConstraintPreflight, - pub vm_pvs: VmPvsPreflight, } impl Preflight { @@ -66,14 +65,6 @@ pub struct ProofShapePreflight { pub after_forked_challenge_2: EF, } -#[derive(Clone, Debug, Default)] -pub struct VmPvsPreflight { - pub lookup_challenge_alpha: EF, - pub lookup_challenge_beta: EF, - pub lookup_challenge_alpha_lookup_count: usize, - pub lookup_challenge_beta_lookup_count: usize, -} - #[derive(Clone, Debug, Default)] pub struct TraceVData { pub log_height: usize, diff --git a/ceno_recursion_v2/src/tower/mod.rs b/ceno_recursion_v2/src/tower/mod.rs index 3323b213e..81c1431b0 100644 --- a/ceno_recursion_v2/src/tower/mod.rs +++ b/ceno_recursion_v2/src/tower/mod.rs @@ -1294,7 +1294,7 @@ impl> TraceGenModule mod debug_tests { use super::*; use crate::{ - system::RecursionPcs, + system::{BusIndexManager, BusInventory, RecursionPcs}, utils::{TranscriptLabel, transcript_observe_label}, }; use ceno_zkvm::scheme::{constants::NUM_FANIN, verifier::TowerVerify}; @@ -1421,6 +1421,27 @@ mod debug_tests { } } + fn proof_shape_lookup_challenges(vk: &RecursionVk, proof: &RecursionProof) -> (EF, EF) { + let mut bus_idx_manager = BusIndexManager::new(); + let bus_inventory = BusInventory::new(&mut bus_idx_manager); + let module = crate::proof_shape::ProofShapeModule::new( + vk, + &mut bus_idx_manager, + bus_inventory, + false, + ); + let mut sponge = default_duplex_sponge_recorder(); + transcript_observe_label(&mut sponge, TranscriptLabel::Riscv.as_bytes()); + let mut preflight = Preflight::default(); + module.run_preflight(vk, proof, &mut preflight, &mut sponge); + let alpha = + EF::from_basis_coefficients_slice(&preflight.proof_shape.lookup_challenge_alpha) + .unwrap(); + let beta = EF::from_basis_coefficients_slice(&preflight.proof_shape.lookup_challenge_beta) + .unwrap(); + (alpha, beta) + } + fn manual_first_expected(chip_proof: &ZKVMChipProof, lambda: EF, eq: EF) -> EF { let prod_count = chip_proof.r_out_evals.len() + chip_proof.w_out_evals.len(); let mut total = EF::ZERO; @@ -1566,17 +1587,7 @@ mod debug_tests { let basic_alpha = basic.read_challenge().elements; let basic_beta = basic.read_challenge().elements; - let mut sponge = default_duplex_sponge_recorder(); - transcript_observe_label(&mut sponge, TranscriptLabel::Riscv.as_bytes()); - let mut openvm_preflight = Preflight::default(); - super::super::circuit::inner::vm_pvs::run_preflight( - &vk, - &proof, - &mut openvm_preflight, - &mut sponge, - ); - let openvm_alpha = openvm_preflight.vm_pvs.lookup_challenge_alpha; - let openvm_beta = openvm_preflight.vm_pvs.lookup_challenge_beta; + let (openvm_alpha, openvm_beta) = proof_shape_lookup_challenges(&vk, &proof); eprintln!( "global basic alpha={:?} beta={:?}; openvm alpha={:?} beta={:?}", @@ -1681,17 +1692,7 @@ mod debug_tests { } let basic_schedule = observe_basic_tower(&mut basic_fork, chip_proof); - let mut sponge = default_duplex_sponge_recorder(); - transcript_observe_label(&mut sponge, TranscriptLabel::Riscv.as_bytes()); - let mut openvm_preflight = Preflight::default(); - super::super::circuit::inner::vm_pvs::run_preflight( - &vk, - &proof, - &mut openvm_preflight, - &mut sponge, - ); - let openvm_alpha = openvm_preflight.vm_pvs.lookup_challenge_alpha; - let openvm_beta = openvm_preflight.vm_pvs.lookup_challenge_beta; + let (openvm_alpha, openvm_beta) = proof_shape_lookup_challenges(&vk, &proof); let mut openvm_fork = default_duplex_sponge_recorder(); transcript_observe_label(&mut openvm_fork, TranscriptLabel::Fork.as_bytes()); FiatShamirTranscript::::observe_ext( diff --git a/ceno_recursion_v2/src/transcript/mod.rs b/ceno_recursion_v2/src/transcript/mod.rs index 122be500d..4223ede91 100644 --- a/ceno_recursion_v2/src/transcript/mod.rs +++ b/ceno_recursion_v2/src/transcript/mod.rs @@ -368,10 +368,9 @@ fn trunk_global_export_ranges(preflight: &Preflight) -> Vec<(usize, usize)> { if preflight.batch_constraint.lambda_tidx == 0 && preflight.batch_constraint.tidx_before_univariate == 0 { - // VmPvsAir remains active outside the system module and consumes the - // verifier transcript prefix. With BatchConstraint disabled, the trunk - // contains only that prefix plus fork-merge observations, so exporting - // the full trunk remains balanced. + // ProofShape/PublicValues AIRs consume the verifier transcript prefix. With + // BatchConstraint disabled, the trunk contains only that prefix plus fork-merge + // observations, so exporting the full trunk remains balanced. return if log_len == 0 { Vec::new() } else {