diff --git a/jolt-core/benches/commit.rs b/jolt-core/benches/commit.rs index 046935f9f5..cf5ca05910 100644 --- a/jolt-core/benches/commit.rs +++ b/jolt-core/benches/commit.rs @@ -6,7 +6,6 @@ use jolt_core::utils::math::Math; use rand::Rng; use rand_chacha::ChaCha20Rng; use rand_core::{RngCore, SeedableRng}; -// use rayon::prelude::*; fn benchmark_dory_dense(c: &mut Criterion, name: &str, k: usize, t: usize) { let globals = DoryGlobals::initialize_context(k, t, DoryContext::Main, None); @@ -20,7 +19,7 @@ fn benchmark_dory_dense(c: &mut Criterion, name: &str, k: usize, t: usize) { c.bench_function(&format!("{name} Dory commit_rows"), |b| { b.iter(|| { let _ = globals; - DoryCommitmentScheme::commit(&poly, &setup); + DoryCommitmentScheme::default().commit(&poly, &setup); }); }); } @@ -43,10 +42,7 @@ fn benchmark_dory_one_hot_batch(c: &mut Criterion, name: &str, k: usize, t: usiz c.bench_function(&format!("{name} Dory one-hot commit"), |b| { b.iter(|| { let _ = globals; - DoryCommitmentScheme::batch_commit(&polys, &setup); - // polys.par_iter().for_each(|poly| { - // DoryCommitmentScheme::commit(&poly, &setup); - // }); + DoryCommitmentScheme::default().batch_commit(&polys, &setup); }); }); } @@ -75,7 +71,7 @@ fn benchmark_dory_mixed_batch(c: &mut Criterion, name: &str, k: usize, t: usize) c.bench_function(&format!("{name} Dory mixed batch commit"), |b| { b.iter(|| { let _ = globals; - DoryCommitmentScheme::batch_commit(&polys, &setup); + DoryCommitmentScheme::default().batch_commit(&polys, &setup); }); }); } diff --git a/jolt-core/src/poly/commitment/commitment_scheme.rs b/jolt-core/src/poly/commitment/commitment_scheme.rs index 728b93e063..83c62f6847 100644 --- a/jolt-core/src/poly/commitment/commitment_scheme.rs +++ b/jolt-core/src/poly/commitment/commitment_scheme.rs @@ -2,6 +2,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use std::borrow::Borrow; use std::fmt::Debug; +use crate::poly::opening_proof::BatchPolynomialSource; use crate::transcripts::Transcript; use crate::{ curve::JoltCurve, @@ -10,8 +11,10 @@ use crate::{ utils::{errors::ProofVerifyError, small_scalar::SmallScalar}, }; -pub trait CommitmentScheme: Clone + Sync + Send + 'static { +pub trait CommitmentScheme: Clone + Sync + Send + Default + 'static { type Field: JoltField + Sized; + /// PCS-specific configuration carried by the instance. Opaque to generic code. + type Config: Clone + Sync + Send + CanonicalSerialize + CanonicalDeserialize; type ProverSetup: Clone + Sync + Send + Debug + CanonicalSerialize + CanonicalDeserialize; type VerifierSetup: Clone + Sync + Send + Debug + CanonicalSerialize + CanonicalDeserialize; type Commitment: Default @@ -23,101 +26,47 @@ pub trait CommitmentScheme: Clone + Sync + Send + 'static { + CanonicalDeserialize + Clone; type Proof: Sync + Send + CanonicalSerialize + CanonicalDeserialize + Clone + Debug; - type BatchedProof: Sync + Send + CanonicalSerialize + CanonicalDeserialize; /// A hint that helps the prover compute an opening proof. Typically some byproduct of /// the commitment computation, e.g. for Dory the Pedersen commitments to the rows can be /// used as a hint for the opening proof. type OpeningProofHint: Sync + Send + Clone + Debug + PartialEq; - /// Generates the prover setup for this PCS. `max_num_vars` is the maximum number of - /// variables of any polynomial that will be committed using this setup. fn setup_prover(max_num_vars: usize) -> Self::ProverSetup; - /// Generates the verifier setup from the prover setup. fn setup_verifier(setup: &Self::ProverSetup) -> Self::VerifierSetup; - /// Commits to a multilinear polynomial using the provided setup. - /// - /// # Arguments - /// * `poly` - The multilinear polynomial to commit to - /// * `setup` - The prover setup for the commitment scheme - /// - /// # Returns - /// A tuple containing the commitment to the polynomial and a hint that can be used - /// to optimize opening proof generation + /// Reconstruct a PCS instance from a batched proof (e.g. for the verifier to + /// recover PCS-specific configuration serialized during proving). + fn from_proof(proof: &Self::Proof) -> Self; + + fn config(&self) -> &Self::Config; + fn commit( + &self, poly: &MultilinearPolynomial, setup: &Self::ProverSetup, ) -> (Self::Commitment, Self::OpeningProofHint); - /// Commits to multiple multilinear polynomials in batch. - /// - /// # Arguments - /// * `polys` - A slice of multilinear polynomials to commit to - /// * `gens` - The prover setup for the commitment scheme - /// - /// # Returns - /// A vector of commitments, one for each input polynomial fn batch_commit( + &self, polys: &[U], gens: &Self::ProverSetup, ) -> Vec<(Self::Commitment, Self::OpeningProofHint)> where U: Borrow> + Sync; - /// Homomorphically combines multiple commitments into a single commitment, computed as a - /// linear combination with the given coefficients. - fn combine_commitments>( - _commitments: &[C], - _coeffs: &[Self::Field], - ) -> Self::Commitment { - todo!("`combine_commitments` should be on a separate `AdditivelyHomomorphic` trait") - } - - /// Homomorphically combines multiple opening proof hints into a single hint, computed as a - /// linear combination with the given coefficients. - fn combine_hints( - _hints: Vec, - _coeffs: &[Self::Field], - ) -> Self::OpeningProofHint { - unimplemented!() - } - - /// Generates a proof of evaluation for a polynomial at a specific point. - /// - /// # Arguments - /// * `setup` - The prover setup for the commitment scheme - /// * `poly` - The multilinear polynomial being proved - /// * `opening_point` - The point at which the polynomial is evaluated - /// * `hint` - An optional hint that helps optimize the proof generation. - /// When `None`, implementations should compute the hint internally if needed. - /// * `transcript` - The transcript for Fiat-Shamir transformation - /// - /// # Returns - /// A tuple containing: - /// - The proof of the polynomial evaluation at the specified point - /// - An optional ZK blinding factor (y_blinding) for use in BlindFold; None for non-ZK schemes fn prove( + &self, setup: &Self::ProverSetup, poly: &MultilinearPolynomial, opening_point: &[::Challenge], hint: Option, transcript: &mut ProofTranscript, + commitment: &Self::Commitment, ) -> (Self::Proof, Option); - /// Verifies a proof of polynomial evaluation at a specific point. - /// - /// # Arguments - /// * `proof` - The proof to be verified - /// * `setup` - The verifier setup for the commitment scheme - /// * `transcript` - The transcript for Fiat-Shamir transformation - /// * `opening_point` - The point at which the polynomial is evaluated - /// * `opening` - The claimed evaluation value of the polynomial at the opening point - /// * `commitment` - The commitment to the polynomial - /// - /// # Returns - /// Ok(()) if the proof is valid, otherwise a ProofVerifyError fn verify( + &self, proof: &Self::Proof, setup: &Self::VerifierSetup, transcript: &mut ProofTranscript, @@ -126,6 +75,31 @@ pub trait CommitmentScheme: Clone + Sync + Send + 'static { commitment: &Self::Commitment, ) -> Result<(), ProofVerifyError>; + #[allow(clippy::too_many_arguments)] + fn batch_prove>( + &self, + setup: &Self::ProverSetup, + poly_source: &S, + hints: Vec, + commitments: &[&Self::Commitment], + opening_point: &[::Challenge], + claims: &[Self::Field], + coeffs: &[Self::Field], + transcript: &mut ProofTranscript, + ) -> (Self::Proof, Option); + + #[allow(clippy::too_many_arguments)] + fn batch_verify( + &self, + proof: &Self::Proof, + setup: &Self::VerifierSetup, + transcript: &mut ProofTranscript, + opening_point: &[::Challenge], + commitments: &[&Self::Commitment], + claims: &[Self::Field], + coeffs: &[Self::Field], + ) -> Result<(), ProofVerifyError>; + fn protocol_name() -> &'static [u8]; /// Extracts raw BN254 G1 generators and blinding generator from the prover setup. @@ -155,18 +129,21 @@ pub trait StreamingCommitmentScheme: CommitmentScheme { /// The type representing chunk state (tier 1 commitments) type ChunkState: Send + Sync + Clone + PartialEq + Debug; - /// Compute tier 1 commitment for a chunk of small scalar values - fn process_chunk(setup: &Self::ProverSetup, chunk: &[T]) -> Self::ChunkState; + fn process_chunk( + &self, + setup: &Self::ProverSetup, + chunk: &[T], + ) -> Self::ChunkState; - /// Compute tier 1 commitment for a chunk of one-hot values fn process_chunk_onehot( + &self, setup: &Self::ProverSetup, onehot_k: usize, chunk: &[Option], ) -> Self::ChunkState; - /// Compute tier 2 commitment from accumulated tier 1 commitments fn aggregate_chunks( + &self, setup: &Self::ProverSetup, onehot_k: Option, tier1_commitments: &[Self::ChunkState], diff --git a/jolt-core/src/poly/commitment/dory/commitment_scheme.rs b/jolt-core/src/poly/commitment/dory/commitment_scheme.rs index 1a3c76d2af..ea2d3d29a8 100644 --- a/jolt-core/src/poly/commitment/dory/commitment_scheme.rs +++ b/jolt-core/src/poly/commitment/dory/commitment_scheme.rs @@ -13,6 +13,7 @@ use crate::{ CommitmentScheme, StreamingCommitmentScheme, ZkEvalCommitment, }, poly::multilinear_polynomial::MultilinearPolynomial, + poly::opening_proof::BatchPolynomialSource, transcripts::Transcript, utils::{errors::ProofVerifyError, math::Math, small_scalar::SmallScalar}, }; @@ -24,11 +25,34 @@ use dory::primitives::{ poly::Polynomial, }; use rayon::prelude::*; -use std::borrow::Borrow; use tracing::trace_span; #[derive(Clone)] -pub struct DoryCommitmentScheme; +pub struct DoryCommitmentScheme { + pub layout: DoryLayout, + pub sigma: usize, + pub nu: usize, +} + +impl Default for DoryCommitmentScheme { + fn default() -> Self { + Self { + layout: DoryGlobals::get_layout(), + sigma: DoryGlobals::get_num_columns().log_2(), + nu: DoryGlobals::get_max_num_rows().log_2(), + } + } +} + +/// Split `total_vars` into balanced `(sigma, nu)` where sigma = ceil(total_vars / 2) +/// and nu = total_vars - sigma. sigma is the number of column variables, +/// nu is the number of row variables. +#[inline] +pub fn balanced_sigma_nu(total_vars: usize) -> (usize, usize) { + let sigma = total_vars.div_ceil(2); + let nu = total_vars - sigma; + (sigma, nu) +} #[derive(Clone, Debug, PartialEq)] pub struct DoryOpeningProofHint(Vec); @@ -76,11 +100,11 @@ pub fn bind_opening_inputs_zk; type OpeningProofHint = DoryOpeningProofHint; fn setup_prover(max_num_vars: usize) -> Self::ProverSetup { @@ -105,16 +129,23 @@ impl CommitmentScheme for DoryCommitmentScheme { setup.to_verifier_setup() } + fn from_proof(_proof: &Self::Proof) -> Self { + Self::default() + } + + fn config(&self) -> &DoryLayout { + &self.layout + } + fn commit( + &self, poly: &MultilinearPolynomial, setup: &Self::ProverSetup, ) -> (Self::Commitment, Self::OpeningProofHint) { let _span = trace_span!("DoryCommitmentScheme::commit").entered(); - let num_cols = DoryGlobals::get_num_columns(); - let num_rows = DoryGlobals::get_max_num_rows(); - let sigma = num_cols.log_2(); - let nu = num_rows.log_2(); + let sigma = self.sigma; + let nu = self.nu; let (tier_2, row_commitments, _commit_blind) = as Polynomial>::commit::< @@ -128,6 +159,7 @@ impl CommitmentScheme for DoryCommitmentScheme { } fn batch_commit( + &self, polys: &[U], gens: &Self::ProverSetup, ) -> Vec<(Self::Commitment, Self::OpeningProofHint)> @@ -138,32 +170,33 @@ impl CommitmentScheme for DoryCommitmentScheme { polys .par_iter() - .map(|poly| Self::commit(poly.borrow(), gens)) + .map(|poly| self.commit(poly.borrow(), gens)) .collect() } fn prove( + &self, setup: &Self::ProverSetup, poly: &MultilinearPolynomial, opening_point: &[::Challenge], hint: Option, transcript: &mut ProofTranscript, + _commitment: &Self::Commitment, ) -> (Self::Proof, Option) { let _span = trace_span!("DoryCommitmentScheme::prove").entered(); let (row_commitments, commit_blind) = hint .map(|h| (h.into_rows(), DoryField::zero())) .unwrap_or_else(|| { - let (_commitment, row_commitments) = Self::commit(poly, setup); + let (_commitment, row_commitments) = self.commit(poly, setup); (row_commitments.into_rows(), DoryField::zero()) }); - let num_cols = DoryGlobals::get_num_columns(); - let num_rows = DoryGlobals::get_max_num_rows(); - let sigma = num_cols.log_2(); - let nu = num_rows.log_2(); + let sigma = self.sigma; + let nu = self.nu; - let reordered_point = reorder_opening_point_for_layout::(opening_point); + let reordered_point = + reorder_opening_point_for_layout::(self.layout, opening_point); let ark_point: Vec = reordered_point .iter() .rev() @@ -197,6 +230,7 @@ impl CommitmentScheme for DoryCommitmentScheme { } fn verify( + &self, proof: &Self::Proof, setup: &Self::VerifierSetup, transcript: &mut ProofTranscript, @@ -206,7 +240,8 @@ impl CommitmentScheme for DoryCommitmentScheme { ) -> Result<(), ProofVerifyError> { let _span = trace_span!("DoryCommitmentScheme::verify").entered(); - let reordered_point = reorder_opening_point_for_layout::(opening_point); + let reordered_point = + reorder_opening_point_for_layout::(self.layout, opening_point); // Dory uses the opposite endian-ness as Jolt let ark_point: Vec = reordered_point @@ -234,6 +269,52 @@ impl CommitmentScheme for DoryCommitmentScheme { Ok(()) } + fn batch_prove>( + &self, + setup: &Self::ProverSetup, + poly_source: &S, + hints: Vec, + commitments: &[&Self::Commitment], + opening_point: &[::Challenge], + _claims: &[Self::Field], + coeffs: &[Self::Field], + transcript: &mut ProofTranscript, + ) -> (Self::Proof, Option) { + let joint_poly = poly_source.build_joint_polynomial(coeffs); + let combined_hint = self.combine_hints_internal(hints, coeffs); + let joint_commitment = Self::combine_commitments_internal(commitments, coeffs); + self.prove( + setup, + &joint_poly, + opening_point, + Some(combined_hint), + transcript, + &joint_commitment, + ) + } + + fn batch_verify( + &self, + proof: &Self::Proof, + setup: &Self::VerifierSetup, + transcript: &mut ProofTranscript, + opening_point: &[::Challenge], + commitments: &[&Self::Commitment], + claims: &[Self::Field], + coeffs: &[Self::Field], + ) -> Result<(), ProofVerifyError> { + let joint_commitment = Self::combine_commitments_internal(commitments, coeffs); + let joint_claim: ark_bn254::Fr = coeffs.iter().zip(claims).map(|(c, v)| *c * *v).sum(); + self.verify( + proof, + setup, + transcript, + opening_point, + &joint_claim, + &joint_commitment, + ) + } + fn protocol_name() -> &'static [u8] { b"Dory" } @@ -251,24 +332,21 @@ impl CommitmentScheme for DoryCommitmentScheme { let h1 = crate::curve::Bn254G1(setup.0.h1.0); Some((g1s, h1)) } +} - /// In Dory, the opening proof hint consists of the Pedersen commitments to the rows - /// of the polynomial coefficient matrix. In the context of a batch opening proof, we - /// can homomorphically combine the row commitments for multiple polynomials into the - /// row commitments for the RLC of those polynomials. This is more efficient than computing - /// the row commitments for the RLC from scratch. - /// - #[tracing::instrument(skip_all, name = "DoryCommitmentScheme::combine_hints")] - fn combine_hints( - hints: Vec, - coeffs: &[Self::Field], - ) -> Self::OpeningProofHint { - let num_rows = DoryGlobals::get_max_num_rows(); - - let mut rlc_hint = vec![ArkG1(G1Projective::zero()); num_rows]; +impl DoryCommitmentScheme { + #[tracing::instrument(skip_all, name = "DoryCommitmentScheme::combine_hints_internal")] + pub(crate) fn combine_hints_internal( + &self, + hints: Vec, + coeffs: &[ark_bn254::Fr], + ) -> DoryOpeningProofHint { + let max_num_rows = 1 << self.nu; + let mut rlc_hint = vec![ArkG1(G1Projective::zero()); max_num_rows]; for (coeff, mut hint) in coeffs.iter().zip(hints.into_iter()) { - hint.0.resize(num_rows, ArkG1(G1Projective::zero())); + hint.0.resize(max_num_rows, ArkG1(G1Projective::zero())); + // SAFETY: ArkG1 is repr(transparent) over G1Projective let row_commitments: &mut [G1Projective] = unsafe { std::slice::from_raw_parts_mut( hint.0.as_mut_ptr() as *mut G1Projective, @@ -295,20 +373,15 @@ impl CommitmentScheme for DoryCommitmentScheme { DoryOpeningProofHint::new(rlc_hint) } - /// Homomorphically combines multiple commitments using a random linear combination. - /// Computes: sum_i(coeff_i * commitment_i) for the GT elements. - #[tracing::instrument(skip_all, name = "DoryCommitmentScheme::combine_commitments")] - fn combine_commitments>( - commitments: &[C], - coeffs: &[Self::Field], - ) -> Self::Commitment { - let _span = trace_span!("DoryCommitmentScheme::combine_commitments").entered(); - - // Combine GT elements using parallel RLC - let commitments_vec: Vec<&ArkGT> = commitments.iter().map(|c| c.borrow()).collect(); + /// Homomorphically combines multiple GT commitments using a random linear combination. + #[tracing::instrument(skip_all, name = "DoryCommitmentScheme::combine_commitments_internal")] + pub(crate) fn combine_commitments_internal( + commitments: &[&ArkGT], + coeffs: &[ark_bn254::Fr], + ) -> ArkGT { coeffs .par_iter() - .zip(commitments_vec.par_iter()) + .zip(commitments.par_iter()) .map(|(coeff, commitment)| { let ark_coeff = jolt_to_ark(coeff); ark_coeff * **commitment @@ -318,13 +391,15 @@ impl CommitmentScheme for DoryCommitmentScheme { } impl StreamingCommitmentScheme for DoryCommitmentScheme { - type ChunkState = Vec; // Tier 1 commitment chunks + type ChunkState = Vec; #[tracing::instrument(skip_all, name = "DoryCommitmentScheme::compute_tier1_commitment")] - fn process_chunk(setup: &Self::ProverSetup, chunk: &[T]) -> Self::ChunkState { - debug_assert_eq!(chunk.len(), DoryGlobals::get_num_columns()); - - let row_len = DoryGlobals::get_num_columns(); + fn process_chunk( + &self, + setup: &Self::ProverSetup, + chunk: &[T], + ) -> Self::ChunkState { + let row_len = chunk.len(); let g1_slice = unsafe { std::slice::from_raw_parts(setup.g1_vec.as_ptr(), setup.g1_vec.len()) }; @@ -343,13 +418,14 @@ impl StreamingCommitmentScheme for DoryCommitmentScheme { name = "DoryCommitmentScheme::compute_tier1_commitment_onehot" )] fn process_chunk_onehot( + &self, setup: &Self::ProverSetup, onehot_k: usize, chunk: &[Option], ) -> Self::ChunkState { let K = onehot_k; + let row_len = chunk.len(); - let row_len = DoryGlobals::get_num_columns(); let g1_slice = unsafe { std::slice::from_raw_parts(setup.g1_vec.as_ptr(), setup.g1_vec.len()) }; @@ -378,16 +454,14 @@ impl StreamingCommitmentScheme for DoryCommitmentScheme { #[tracing::instrument(skip_all, name = "DoryCommitmentScheme::compute_tier2_commitment")] fn aggregate_chunks( + &self, setup: &Self::ProverSetup, onehot_k: Option, chunks: &[Self::ChunkState], ) -> (Self::Commitment, Self::OpeningProofHint) { - let num_rows = DoryGlobals::get_max_num_rows(); - - if let Some(_K) = onehot_k { - let row_len = DoryGlobals::get_num_columns(); - let T = DoryGlobals::get_T(); - let rows_per_k = T / row_len; + if let Some(K) = onehot_k { + let rows_per_k = chunks.len(); + let num_rows = K * rows_per_k; let mut row_commitments = vec![ArkG1(G1Projective::zero()); num_rows]; for (chunk_index, commitments) in chunks.iter().enumerate() { @@ -453,9 +527,12 @@ where /// /// For CycleMajor layout, returns the point unchanged. fn reorder_opening_point_for_layout( + layout: DoryLayout, opening_point: &[F::Challenge], ) -> Vec { - if DoryGlobals::get_layout() == DoryLayout::AddressMajor { + if layout == DoryLayout::AddressMajor { + // For AddressMajor, T is needed to split the point. + // Fall back to DoryGlobals for now; will be eliminated in Phase 2d. let log_T = DoryGlobals::get_T().log_2(); let log_K = opening_point.len().saturating_sub(log_T); let (r_address, r_cycle) = opening_point.split_at(log_K); diff --git a/jolt-core/src/poly/commitment/dory/mod.rs b/jolt-core/src/poly/commitment/dory/mod.rs index 4204949e88..5f68eff2fc 100644 --- a/jolt-core/src/poly/commitment/dory/mod.rs +++ b/jolt-core/src/poly/commitment/dory/mod.rs @@ -13,7 +13,7 @@ mod tests; #[cfg(feature = "zk")] pub use commitment_scheme::bind_opening_inputs_zk; -pub use commitment_scheme::{bind_opening_inputs, DoryCommitmentScheme}; +pub use commitment_scheme::{balanced_sigma_nu, bind_opening_inputs, DoryCommitmentScheme}; pub use dory_globals::{DoryContext, DoryGlobals, DoryLayout}; pub use jolt_dory_routines::{JoltG1Routines, JoltG2Routines}; pub use wrappers::{ diff --git a/jolt-core/src/poly/commitment/dory/tests.rs b/jolt-core/src/poly/commitment/dory/tests.rs index 0237a55490..6049044dc3 100644 --- a/jolt-core/src/poly/commitment/dory/tests.rs +++ b/jolt-core/src/poly/commitment/dory/tests.rs @@ -27,7 +27,8 @@ mod tests { .map(|_| ::Challenge::random(&mut rng)) .collect(); - let (commitment, row_commitments) = DoryCommitmentScheme::commit(&poly, prover_setup); + let (commitment, row_commitments) = + DoryCommitmentScheme::default().commit(&poly, prover_setup); let evaluation = as PolynomialEvaluation>::evaluate( &poly, @@ -36,17 +37,18 @@ mod tests { let mut prove_transcript = Blake2bTranscript::new(b"dory_test"); bind_opening_inputs::(&mut prove_transcript, &opening_point, &evaluation); - let (proof, _y_blinding) = DoryCommitmentScheme::prove( + let (proof, _y_blinding) = DoryCommitmentScheme::default().prove( prover_setup, &poly, &opening_point, Some(row_commitments), &mut prove_transcript, + &commitment, ); let mut verify_transcript = Blake2bTranscript::new(b"dory_test"); bind_opening_inputs::(&mut verify_transcript, &opening_point, &evaluation); - let verification_result = DoryCommitmentScheme::verify( + let verification_result = DoryCommitmentScheme::default().verify( &proof, verifier_setup, &mut verify_transcript, @@ -257,19 +259,21 @@ mod tests { let prover_setup = DoryCommitmentScheme::setup_prover(num_vars); let verifier_setup = DoryCommitmentScheme::setup_verifier(&prover_setup); - let (commitment, row_commitments) = DoryCommitmentScheme::commit(&poly, &prover_setup); + let (commitment, row_commitments) = + DoryCommitmentScheme::default().commit(&poly, &prover_setup); let mut prove_transcript = Blake2bTranscript::new(DoryCommitmentScheme::protocol_name()); let correct_evaluation = poly.evaluate(&opening_point); bind_opening_inputs::(&mut prove_transcript, &opening_point, &correct_evaluation); - let (proof, _y_blinding) = DoryCommitmentScheme::prove( + let (proof, _y_blinding) = DoryCommitmentScheme::default().prove( &prover_setup, &poly, &opening_point, Some(row_commitments), &mut prove_transcript, + &commitment, ); // Test 1: Tamper with the evaluation @@ -283,7 +287,7 @@ mod tests { &opening_point, &tampered_evaluation, ); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -317,7 +321,7 @@ mod tests { &opening_point, &correct_evaluation, ); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &tampered_proof, &verifier_setup, &mut verify_transcript, @@ -345,7 +349,7 @@ mod tests { &tampered_opening_point, &correct_evaluation, ); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -366,7 +370,8 @@ mod tests { let wrong_coeffs: Vec = (0..num_coeffs).map(|_| Fr::rand(&mut rng)).collect(); let wrong_poly = MultilinearPolynomial::LargeScalars(DensePolynomial::new(wrong_coeffs)); - let (wrong_commitment, _) = DoryCommitmentScheme::commit(&wrong_poly, &prover_setup); + let (wrong_commitment, _) = + DoryCommitmentScheme::default().commit(&wrong_poly, &prover_setup); let mut verify_transcript = Blake2bTranscript::new(DoryCommitmentScheme::protocol_name()); @@ -375,7 +380,7 @@ mod tests { &opening_point, &correct_evaluation, ); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -398,7 +403,7 @@ mod tests { &opening_point, &correct_evaluation, ); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -422,7 +427,7 @@ mod tests { &opening_point, &correct_evaluation, ); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -474,7 +479,8 @@ mod tests { let prover_setup = DoryCommitmentScheme::setup_prover(num_vars); let verifier_setup = DoryCommitmentScheme::setup_verifier(&prover_setup); - let (commitment, row_commitments) = DoryCommitmentScheme::commit(&poly, &prover_setup); + let (commitment, row_commitments) = + DoryCommitmentScheme::default().commit(&poly, &prover_setup); let evaluation = as PolynomialEvaluation>::evaluate( &poly, @@ -483,17 +489,18 @@ mod tests { let mut prove_transcript = Blake2bTranscript::new(b"dory_test"); bind_opening_inputs::(&mut prove_transcript, &opening_point, &evaluation); - let (proof, _y_blinding) = DoryCommitmentScheme::prove( + let (proof, _y_blinding) = DoryCommitmentScheme::default().prove( &prover_setup, &poly, &opening_point, Some(row_commitments), &mut prove_transcript, + &commitment, ); let mut verify_transcript = Blake2bTranscript::new(b"dory_test"); bind_opening_inputs::(&mut verify_transcript, &opening_point, &evaluation); - let verification_result = DoryCommitmentScheme::verify( + let verification_result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -535,7 +542,7 @@ mod tests { // Step 2: Commit to each polynomial let commitments_and_hints: Vec<_> = polys .iter() - .map(|poly| DoryCommitmentScheme::commit(poly, &prover_setup)) + .map(|poly| DoryCommitmentScheme::default().commit(poly, &prover_setup)) .collect(); let commitments: Vec<_> = commitments_and_hints.iter().map(|(c, _)| *c).collect(); @@ -544,42 +551,39 @@ mod tests { // Step 3: Generate 5 random coefficients let coeffs: Vec = (0..num_polys).map(|_| Fr::rand(&mut rng)).collect(); - // Step 4: Homomorphically combine commitments and hints - let combined_commitment = DoryCommitmentScheme::combine_commitments(&commitments, &coeffs); - let combined_hint = DoryCommitmentScheme::combine_hints(hints, &coeffs); + let commitment_refs: Vec<&ArkGT> = commitments.iter().collect(); + let combined_commitment = + DoryCommitmentScheme::combine_commitments_internal(&commitment_refs, &coeffs); + let combined_hint = DoryCommitmentScheme::default().combine_hints_internal(hints, &coeffs); - // Step 5: Generate evaluation point first let opening_point: Vec<::Challenge> = (0..num_vars) .map(|_| ::Challenge::random(&mut rng)) .collect(); - // Step 6: Compute expected evaluation as linear combination: eval = coeff[0]*P0(r) + ... + coeff[4]*P4(r) let mut evaluation = Fr::zero(); for (poly, coeff) in polys.iter().zip(coeffs.iter()) { let poly_eval = poly.evaluate(&opening_point); evaluation += *coeff * poly_eval; } - // Step 7: Compute combined polynomial: P = coeff[0]*P0 + coeff[1]*P1 + ... + coeff[4]*P4 let poly_refs: Vec<&MultilinearPolynomial> = polys.iter().collect(); let combined_poly = DensePolynomial::linear_combination(&poly_refs, &coeffs); let combined_poly = MultilinearPolynomial::from(combined_poly.Z); - // Step 8: Create evaluation proof using combined commitment and hint let mut prove_transcript = Blake2bTranscript::new(b"dory_homomorphic_test"); bind_opening_inputs::(&mut prove_transcript, &opening_point, &evaluation); - let (proof, _y_blinding) = DoryCommitmentScheme::prove( + let (proof, _y_blinding) = DoryCommitmentScheme::default().prove( &prover_setup, &combined_poly, &opening_point, Some(combined_hint), &mut prove_transcript, + &combined_commitment, ); - // Step 9: Verify the proof let mut verify_transcript = Blake2bTranscript::new(b"dory_homomorphic_test"); bind_opening_inputs::(&mut verify_transcript, &opening_point, &evaluation); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -607,7 +611,6 @@ mod tests { let mut rng = thread_rng(); - // Step 1: Generate 5 random polynomials let polys: Vec> = (0..num_polys) .map(|_| { let coeffs: Vec = (0..num_coeffs).map(|_| Fr::rand(&mut rng)).collect(); @@ -618,62 +621,55 @@ mod tests { let prover_setup = DoryCommitmentScheme::setup_prover(num_vars); let verifier_setup = DoryCommitmentScheme::setup_verifier(&prover_setup); - // Step 2: Use batch_commit - let commitments_and_hints = DoryCommitmentScheme::batch_commit(&polys, &prover_setup); + let commitments_and_hints = + DoryCommitmentScheme::default().batch_commit(&polys, &prover_setup); let commitments: Vec<_> = commitments_and_hints.iter().map(|(c, _)| *c).collect(); let hints: Vec<_> = commitments_and_hints.into_iter().map(|(_, h)| h).collect(); - // Step 3: Generate random coefficients (like gamma powers in opening_proof.rs) let coeffs: Vec = (0..num_polys).map(|_| Fr::rand(&mut rng)).collect(); - // Step 4: Homomorphically combine commitments and hints - let combined_commitment = DoryCommitmentScheme::combine_commitments(&commitments, &coeffs); - let combined_hint = DoryCommitmentScheme::combine_hints(hints, &coeffs); + let commitment_refs: Vec<&ArkGT> = commitments.iter().collect(); + let combined_commitment = + DoryCommitmentScheme::combine_commitments_internal(&commitment_refs, &coeffs); + let combined_hint = DoryCommitmentScheme::default().combine_hints_internal(hints, &coeffs); - // Step 5: Generate evaluation point let opening_point: Vec<::Challenge> = (0..num_vars) .map(|_| ::Challenge::random(&mut rng)) .collect(); - // Step 6: Compute expected evaluation as linear combination let mut evaluation = Fr::zero(); for (poly, coeff) in polys.iter().zip(coeffs.iter()) { let poly_eval = poly.evaluate(&opening_point); evaluation += *coeff * poly_eval; } - // Step 7: Create combined polynomial let poly_refs: Vec<&MultilinearPolynomial> = polys.iter().collect(); let combined_poly = DensePolynomial::linear_combination(&poly_refs, &coeffs); let combined_poly = MultilinearPolynomial::from(combined_poly.Z); - // Step 8: Verify that directly committing to the combined polynomial gives the same result - // as homomorphically combining the individual commitments let (direct_commitment, direct_hint) = - DoryCommitmentScheme::commit(&combined_poly, &prover_setup); + DoryCommitmentScheme::default().commit(&combined_poly, &prover_setup); - // The commitments should match assert_eq!( combined_commitment, direct_commitment, "Homomorphically combined commitment should match direct commitment to RLC" ); - // Step 9: Create evaluation proof using combined hint let mut prove_transcript = Blake2bTranscript::new(b"dory_batch_commit_e2e_test"); bind_opening_inputs::(&mut prove_transcript, &opening_point, &evaluation); - let (proof, _y_blinding) = DoryCommitmentScheme::prove( + let (proof, _y_blinding) = DoryCommitmentScheme::default().prove( &prover_setup, &combined_poly, &opening_point, Some(combined_hint), &mut prove_transcript, + &combined_commitment, ); - // Step 10: Verify the proof let mut verify_transcript = Blake2bTranscript::new(b"dory_batch_commit_e2e_test"); bind_opening_inputs::(&mut verify_transcript, &opening_point, &evaluation); - let result = DoryCommitmentScheme::verify( + let result = DoryCommitmentScheme::default().verify( &proof, &verifier_setup, &mut verify_transcript, @@ -687,20 +683,20 @@ mod tests { "Verification should succeed with batch_commit flow: {result:?}" ); - // Step 11: Also verify that proving with the direct hint works let mut prove_transcript2 = Blake2bTranscript::new(b"dory_batch_commit_e2e_test"); bind_opening_inputs::(&mut prove_transcript2, &opening_point, &evaluation); - let (proof2, _y_blinding2) = DoryCommitmentScheme::prove( + let (proof2, _y_blinding2) = DoryCommitmentScheme::default().prove( &prover_setup, &combined_poly, &opening_point, Some(direct_hint), &mut prove_transcript2, + &direct_commitment, ); let mut verify_transcript2 = Blake2bTranscript::new(b"dory_batch_commit_e2e_test"); bind_opening_inputs::(&mut verify_transcript2, &opening_point, &evaluation); - let result2 = DoryCommitmentScheme::verify( + let result2 = DoryCommitmentScheme::default().verify( &proof2, &verifier_setup, &mut verify_transcript2, @@ -803,11 +799,13 @@ mod tests { DoryGlobals::set_layout(DoryLayout::CycleMajor); let poly1 = MultilinearPolynomial::LargeScalars(DensePolynomial::new(coeffs.clone())); - let (commitment_cycle_major, _) = DoryCommitmentScheme::commit(&poly1, &prover_setup); + let (commitment_cycle_major, _) = + DoryCommitmentScheme::default().commit(&poly1, &prover_setup); DoryGlobals::set_layout(DoryLayout::AddressMajor); let poly2 = MultilinearPolynomial::LargeScalars(DensePolynomial::new(coeffs)); - let (commitment_addr_major, _) = DoryCommitmentScheme::commit(&poly2, &prover_setup); + let (commitment_addr_major, _) = + DoryCommitmentScheme::default().commit(&poly2, &prover_setup); assert_eq!( commitment_cycle_major, commitment_addr_major, @@ -883,10 +881,12 @@ mod tests { .map(|_| ::Challenge::random(&mut rng)) .collect(); + let pcs = DoryCommitmentScheme::default(); + let prover_setup = DoryCommitmentScheme::setup_prover(num_vars); let verifier_setup = DoryCommitmentScheme::setup_verifier(&prover_setup); - let (commitment, row_commitments) = DoryCommitmentScheme::commit(&poly, &prover_setup); + let (commitment, row_commitments) = pcs.commit(&poly, &prover_setup); let evaluation = as PolynomialEvaluation>::evaluate( &poly, @@ -895,17 +895,18 @@ mod tests { let mut prove_transcript = Blake2bTranscript::new(b"dory_test"); bind_opening_inputs::(&mut prove_transcript, &opening_point, &evaluation); - let (proof, _y_binding) = DoryCommitmentScheme::prove( + let (proof, _y_binding) = pcs.prove( &prover_setup, &poly, &opening_point, Some(row_commitments), &mut prove_transcript, + &commitment, ); let mut verify_transcript = Blake2bTranscript::new(b"dory_test"); bind_opening_inputs::(&mut verify_transcript, &opening_point, &evaluation); - let verification_result = DoryCommitmentScheme::verify( + let verification_result = pcs.verify( &proof, &verifier_setup, &mut verify_transcript, diff --git a/jolt-core/src/poly/commitment/hyperkzg.rs b/jolt-core/src/poly/commitment/hyperkzg.rs index 43220d9afd..f9058b5bd9 100644 --- a/jolt-core/src/poly/commitment/hyperkzg.rs +++ b/jolt-core/src/poly/commitment/hyperkzg.rs @@ -14,6 +14,7 @@ use super::{ }; use crate::field::JoltField; use crate::poly::multilinear_polynomial::{MultilinearPolynomial, PolynomialEvaluation}; +use crate::poly::opening_proof::BatchPolynomialSource; use crate::poly::rlc_polynomial::RLCPolynomial; use crate::zkvm::witness::CommittedPolynomial; use crate::{ @@ -273,6 +274,14 @@ pub struct HyperKZG { _phantom: PhantomData

, } +impl Default for HyperKZG

{ + fn default() -> Self { + Self { + _phantom: PhantomData, + } + } +} + impl HyperKZG

where

::ScalarField: JoltField, @@ -411,12 +420,12 @@ where

::ScalarField: JoltField, { type Field = P::ScalarField; + type Config = (); type ProverSetup = HyperKZGProverKey

; type VerifierSetup = HyperKZGVerifierKey

; type Commitment = HyperKZGCommitment

; type Proof = HyperKZGProof

; - type BatchedProof = HyperKZGProof

; type OpeningProofHint = (); fn setup_prover(max_num_vars: usize) -> Self::ProverSetup { @@ -435,8 +444,17 @@ where } } + fn from_proof(_proof: &Self::Proof) -> Self { + Self::default() + } + + fn config(&self) -> &() { + &() + } + #[tracing::instrument(skip_all, name = "HyperKZG::commit")] fn commit( + &self, poly: &MultilinearPolynomial, setup: &Self::ProverSetup, ) -> (Self::Commitment, Self::OpeningProofHint) { @@ -453,6 +471,7 @@ where #[tracing::instrument(skip_all, name = "HyperKZG::batch_commit")] fn batch_commit( + &self, polys: &[U], gens: &Self::ProverSetup, ) -> Vec<(Self::Commitment, Self::OpeningProofHint)> @@ -466,56 +485,114 @@ where .collect() } - fn combine_commitments>( - commitments: &[C], - coeffs: &[Self::Field], - ) -> Self::Commitment { - let combined_commitment: P::G1 = commitments - .iter() - .zip(coeffs.iter()) - .map(|(commitment, coeff)| commitment.borrow().0 * coeff) - .sum(); - HyperKZGCommitment(combined_commitment.into_affine()) - } - fn prove( + &self, setup: &Self::ProverSetup, poly: &MultilinearPolynomial, - opening_point: &[::Challenge], // point at which the polynomial is evaluated + opening_point: &[::Challenge], _hint: Option, transcript: &mut ProofTranscript, + _commitment: &Self::Commitment, ) -> (Self::Proof, Option) { let eval = poly.evaluate(opening_point); let proof = HyperKZG::

::open(setup, poly, opening_point, &eval, transcript).unwrap(); - (proof, None) // HyperKZG doesn't have ZK blinding + (proof, None) } fn verify( + &self, proof: &Self::Proof, setup: &Self::VerifierSetup, transcript: &mut ProofTranscript, - opening_point: &[::Challenge], // point at which the polynomial is evaluated - opening: &Self::Field, // evaluation \widetilde{Z}(r) + opening_point: &[::Challenge], + opening: &Self::Field, commitment: &Self::Commitment, ) -> Result<(), ProofVerifyError> { HyperKZG::

::verify(setup, commitment, opening_point, opening, proof, transcript) } + fn batch_prove>( + &self, + setup: &Self::ProverSetup, + poly_source: &S, + _hints: Vec, + commitments: &[&Self::Commitment], + opening_point: &[::Challenge], + _claims: &[Self::Field], + coeffs: &[Self::Field], + transcript: &mut ProofTranscript, + ) -> (Self::Proof, Option) { + let joint_poly = poly_source.build_joint_polynomial(coeffs); + let joint_commitment = Self::combine_commitments_internal(commitments, coeffs); + self.prove( + setup, + &joint_poly, + opening_point, + None, + transcript, + &joint_commitment, + ) + } + + fn batch_verify( + &self, + proof: &Self::Proof, + setup: &Self::VerifierSetup, + transcript: &mut ProofTranscript, + opening_point: &[::Challenge], + commitments: &[&Self::Commitment], + claims: &[Self::Field], + coeffs: &[Self::Field], + ) -> Result<(), ProofVerifyError> { + let joint_commitment = Self::combine_commitments_internal(commitments, coeffs); + let joint_claim: Self::Field = coeffs.iter().zip(claims).map(|(c, v)| *c * *v).sum(); + HyperKZG::

::verify( + setup, + &joint_commitment, + opening_point, + &joint_claim, + proof, + transcript, + ) + } + fn protocol_name() -> &'static [u8] { b"hyperkzg" } } +impl HyperKZG

+where +

::ScalarField: JoltField, +{ + fn combine_commitments_internal( + commitments: &[&HyperKZGCommitment

], + coeffs: &[P::ScalarField], + ) -> HyperKZGCommitment

{ + let combined: P::G1 = commitments + .iter() + .zip(coeffs.iter()) + .map(|(commitment, coeff)| commitment.0 * coeff) + .sum(); + HyperKZGCommitment(combined.into_affine()) + } +} + impl super::commitment_scheme::StreamingCommitmentScheme for HyperKZG

where

::ScalarField: JoltField, { type ChunkState = (); - fn process_chunk(_setup: &Self::ProverSetup, _chunk: &[T]) -> Self::ChunkState { + fn process_chunk( + &self, + _setup: &Self::ProverSetup, + _chunk: &[T], + ) -> Self::ChunkState { } fn process_chunk_onehot( + &self, _setup: &Self::ProverSetup, _onehot_k: usize, _chunk: &[Option], @@ -523,6 +600,7 @@ where } fn aggregate_chunks( + &self, _setup: &Self::ProverSetup, _onehot_k: Option, _tier1_commitments: &[Self::ChunkState], @@ -540,114 +618,6 @@ mod tests { use rand::Rng; use rand_core::SeedableRng; - //#[test] - //fn test_hyperkzg_eval() { - // // Test with poly(X1, X2) = 1 + X1 + X2 + X1*X2 - // let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); - // let srs = HyperKZGSRS::setup(&mut rng, 3); - // let (pk, vk): (HyperKZGProverKey, HyperKZGVerifierKey) = srs.trim(3); - // - // // poly is in eval. representation; evaluated at [(0,0), (0,1), (1,0), (1,1)] - // let poly = - // MultilinearPolynomial::from(vec![Fr::from(1), Fr::from(2), Fr::from(2), Fr::from(4)]); - // - // let C = HyperKZG::commit(&pk, &poly).unwrap(); - // - // let test_inner = - // |point: Vec>, eval: Fr| -> Result<(), ProofVerifyError> { - // let mut tr = Blake2bTranscript::new(b"TestEval"); - // let proof = HyperKZG::open(&pk, &poly, &point, &eval, &mut tr).unwrap(); - // let mut tr = Blake2bTranscript::new(b"TestEval"); - // HyperKZG::verify(&vk, &C, &point, &eval, &proof, &mut tr) - // }; - // - // // Call the prover with a (point, eval) pair. - // // The prover does not recompute so it may produce a proof, but it should not verify - // let point = vec![Fr::from(0), Fr::from(0)]; - // let eval = Fr::from(1); - // assert!(test_inner(point, eval).is_ok()); - // - // let point = vec![Fr::from(0), Fr::from(1)]; - // let eval = Fr::from(2); - // assert!(test_inner(point, eval).is_ok()); - // - // let point = vec![Fr::from(1), Fr::from(1)]; - // let eval = Fr::from(4); - // assert!(test_inner(point, eval).is_ok()); - // - // let point = vec![Fr::from(0), Fr::from(2)]; - // let eval = Fr::from(3); - // assert!(test_inner(point, eval).is_ok()); - // - // let point = vec![Fr::from(2), Fr::from(2)]; - // let eval = Fr::from(9); - // assert!(test_inner(point, eval).is_ok()); - // - // // Try a couple incorrect evaluations and expect failure - // let point = vec![Fr::from(2), Fr::from(2)]; - // let eval = Fr::from(50); - // assert!(test_inner(point, eval).is_err()); - // - // let point = vec![Fr::from(0), Fr::from(2)]; - // let eval = Fr::from(4); - // assert!(test_inner(point, eval).is_err()); - //} - - // THIS test does not make sense for MontU128Challenge - //#[test] - //fn test_hyperkzg_small() { - // let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); - // - // // poly = [1, 2, 1, 4] - // let poly = - // MultilinearPolynomial::from(vec![Fr::from(1), Fr::from(2), Fr::from(1), Fr::from(4)]); - // - // // point = [4,3] - // let point = vec![Fr::from(4), Fr::from(3)]; - // - // // eval = 28 - // let eval = Fr::from(28); - // - // let srs = HyperKZGSRS::setup(&mut rng, 3); - // let (pk, vk): (HyperKZGProverKey, HyperKZGVerifierKey) = srs.trim(3); - // - // // make a commitment - // let C = HyperKZG::commit(&pk, &poly).unwrap(); - // - // // prove an evaluation - // let mut tr = Blake2bTranscript::new(b"TestEval"); - // let proof = HyperKZG::open(&pk, &poly, &point, &eval, &mut tr).unwrap(); - // let post_c_p = tr.challenge_scalar::(); - // - // // verify the evaluation - // let mut verifier_transcript = Blake2bTranscript::new(b"TestEval"); - // assert!( - // HyperKZG::verify(&vk, &C, &point, &eval, &proof, &mut verifier_transcript,).is_ok() - // ); - // let post_c_v = verifier_transcript.challenge_scalar::(); - // - // // check if the prover transcript and verifier transcript are kept in the same state - // assert_eq!(post_c_p, post_c_v); - // - // let mut proof_bytes = Vec::new(); - // proof.serialize_compressed(&mut proof_bytes).unwrap(); - // assert_eq!(proof_bytes.len(), 368); - // - // // Change the proof and expect verification to fail - // let mut bad_proof = proof.clone(); - // let v1 = bad_proof.v[1].clone(); - // bad_proof.v[0].clone_from(&v1); - // let mut verifier_transcript2 = Blake2bTranscript::new(b"TestEval"); - // assert!(HyperKZG::verify( - // &vk, - // &C, - // &point, - // &eval, - // &bad_proof, - // &mut verifier_transcript2 - // ) - // .is_err()); - //} #[test] fn test_hyperkzg_large() { // test the hyperkzg prover and verifier with random instances (derived from a seed) diff --git a/jolt-core/src/poly/commitment/mock.rs b/jolt-core/src/poly/commitment/mock.rs index f0453a6def..7598099297 100644 --- a/jolt-core/src/poly/commitment/mock.rs +++ b/jolt-core/src/poly/commitment/mock.rs @@ -6,6 +6,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use crate::{ field::JoltField, poly::multilinear_polynomial::MultilinearPolynomial, + poly::opening_proof::BatchPolynomialSource, transcripts::Transcript, utils::{errors::ProofVerifyError, small_scalar::SmallScalar}, }; @@ -17,6 +18,14 @@ pub struct MockCommitScheme { _marker: PhantomData, } +impl Default for MockCommitScheme { + fn default() -> Self { + Self { + _marker: PhantomData, + } + } +} + #[derive(Default, Debug, PartialEq, Clone, CanonicalDeserialize, CanonicalSerialize)] pub struct MockCommitment { _field: PhantomData, @@ -32,18 +41,27 @@ where F: JoltField, { type Field = F; + type Config = (); type ProverSetup = (); type VerifierSetup = (); type Commitment = MockCommitment; type Proof = MockProof; - type BatchedProof = MockProof; type OpeningProofHint = (); fn setup_prover(_num_vars: usize) -> Self::ProverSetup {} fn setup_verifier(_setup: &Self::ProverSetup) -> Self::VerifierSetup {} + fn from_proof(_proof: &Self::Proof) -> Self { + Self::default() + } + + fn config(&self) -> &() { + &() + } + fn commit( + &self, _poly: &MultilinearPolynomial, _setup: &Self::ProverSetup, ) -> (Self::Commitment, Self::OpeningProofHint) { @@ -51,37 +69,27 @@ where } fn batch_commit

( + &self, polys: &[P], gens: &Self::ProverSetup, ) -> Vec<(Self::Commitment, Self::OpeningProofHint)> where - P: Borrow>, + P: Borrow> + Sync, { polys .iter() - .map(|poly| (Self::commit(poly.borrow(), gens).0, ())) + .map(|poly| (self.commit(poly.borrow(), gens).0, ())) .collect() } - fn combine_commitments>( - _commitments: &[C], - _coeffs: &[Self::Field], - ) -> Self::Commitment { - MockCommitment::default() - } - - fn combine_hints( - _hints: Vec, - _coeffs: &[Self::Field], - ) -> Self::OpeningProofHint { - } - fn prove( + &self, _setup: &Self::ProverSetup, _poly: &MultilinearPolynomial, opening_point: &[::Challenge], _hint: Option, _transcript: &mut ProofTranscript, + _commitment: &Self::Commitment, ) -> (Self::Proof, Option) { ( MockProof { @@ -92,6 +100,7 @@ where } fn verify( + &self, proof: &Self::Proof, _setup: &Self::VerifierSetup, _transcript: &mut ProofTranscript, @@ -103,6 +112,39 @@ where Ok(()) } + fn batch_prove>( + &self, + _setup: &Self::ProverSetup, + _poly_source: &S, + _hints: Vec, + _commitments: &[&Self::Commitment], + opening_point: &[::Challenge], + _claims: &[Self::Field], + _coeffs: &[Self::Field], + _transcript: &mut ProofTranscript, + ) -> (Self::Proof, Option) { + ( + MockProof { + opening_point: opening_point.to_owned(), + }, + None, + ) + } + + fn batch_verify( + &self, + proof: &Self::Proof, + _setup: &Self::VerifierSetup, + _transcript: &mut ProofTranscript, + opening_point: &[::Challenge], + _commitments: &[&Self::Commitment], + _claims: &[Self::Field], + _coeffs: &[Self::Field], + ) -> Result<(), ProofVerifyError> { + assert_eq!(proof.opening_point, opening_point); + Ok(()) + } + fn protocol_name() -> &'static [u8] { b"mock_commit" } @@ -114,10 +156,15 @@ where { type ChunkState = (); - fn process_chunk(_setup: &Self::ProverSetup, _chunk: &[T]) -> Self::ChunkState { + fn process_chunk( + &self, + _setup: &Self::ProverSetup, + _chunk: &[T], + ) -> Self::ChunkState { } fn process_chunk_onehot( + &self, _setup: &Self::ProverSetup, _onehot_k: usize, _chunk: &[Option], @@ -125,6 +172,7 @@ where } fn aggregate_chunks( + &self, _setup: &Self::ProverSetup, _onehot_k: Option, _tier1_commitments: &[Self::ChunkState], diff --git a/jolt-core/src/poly/multilinear_polynomial.rs b/jolt-core/src/poly/multilinear_polynomial.rs index c0706c0022..c439cd2df8 100644 --- a/jolt-core/src/poly/multilinear_polynomial.rs +++ b/jolt-core/src/poly/multilinear_polynomial.rs @@ -92,7 +92,8 @@ impl MultilinearPolynomial { MultilinearPolynomial::I128Scalars(poly) => poly.coeffs.len(), MultilinearPolynomial::U128Scalars(poly) => poly.coeffs.len(), MultilinearPolynomial::S128Scalars(poly) => poly.coeffs.len(), - _ => unimplemented!("Unexpected MultilinearPolynomial variant"), + MultilinearPolynomial::OneHot(poly) => poly.K * poly.nonzero_indices.len(), + MultilinearPolynomial::RLC(poly) => poly.len(), } } @@ -125,7 +126,8 @@ impl MultilinearPolynomial { MultilinearPolynomial::I128Scalars(poly) => poly.len(), MultilinearPolynomial::U128Scalars(poly) => poly.len(), MultilinearPolynomial::S128Scalars(poly) => poly.len(), - _ => unimplemented!("Unexpected MultilinearPolynomial variant"), + MultilinearPolynomial::OneHot(poly) => poly.K * poly.nonzero_indices.len(), + MultilinearPolynomial::RLC(poly) => poly.len(), } } diff --git a/jolt-core/src/poly/opening_proof.rs b/jolt-core/src/poly/opening_proof.rs index 13af2cb5f7..6bddceeebe 100644 --- a/jolt-core/src/poly/opening_proof.rs +++ b/jolt-core/src/poly/opening_proof.rs @@ -16,15 +16,48 @@ use std::cell::RefCell; use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; -use super::{ - commitment::commitment_scheme::CommitmentScheme, multilinear_polynomial::MultilinearPolynomial, -}; +use super::multilinear_polynomial::MultilinearPolynomial; use crate::{ field::JoltField, transcripts::Transcript, zkvm::witness::{CommittedPolynomial, VirtualPolynomial}, }; +/// Provides lazy access to polynomial data for batch opening proofs. +/// Constructed by the Jolt prover from trace + preprocessing data. +/// Each PCS calls methods on the source as needed: +/// - Dory calls `build_joint_polynomial` to get a streaming RLC polynomial +/// - Hachi ignores the source and uses ring_coeffs from its OpeningProofHint +pub trait BatchPolynomialSource: Send + Sync { + /// Construct the joint (RLC) polynomial: `sum_i coeffs[i] * poly_i`. + /// The returned polynomial may evaluate lazily (e.g., streaming from trace data). + fn build_joint_polynomial(&self, coeffs: &[F]) -> MultilinearPolynomial; +} + +/// Streams polynomial data from the execution trace for Dory batch opening. +/// Wraps the existing `RLCPolynomial::new_streaming` path so that Dory's +/// `batch_prove` avoids regenerating witness polynomials. +pub struct StreamingBatchSource { + pub one_hot_params: OneHotParams, + pub trace_source: TraceSource, + pub streaming_data: Arc, + pub advice_polys: HashMap>, + pub poly_ids: Vec, +} + +impl BatchPolynomialSource for StreamingBatchSource { + fn build_joint_polynomial(&self, coeffs: &[F]) -> MultilinearPolynomial { + MultilinearPolynomial::RLC(RLCPolynomial::new_streaming( + self.one_hot_params.clone(), + Arc::clone(&self.streaming_data), + self.trace_source.clone(), + self.poly_ids.clone(), + coeffs, + self.advice_polys.clone(), + )) + } +} + pub type Endianness = bool; pub const BIG_ENDIAN: Endianness = false; pub const LITTLE_ENDIAN: Endianness = true; @@ -239,61 +272,6 @@ pub trait OpeningAccumulator { ) -> Option<(OpeningPoint, F)>; } -/// State for Dory batch opening (Stage 8). -/// This is a generic interface for batch opening proofs. -#[derive(Clone, Allocative)] -pub struct DoryOpeningState { - /// Unified opening point for all polynomials (length = log_k_chunk + log_T) - pub opening_point: Vec, - /// γ^i coefficients for the RLC polynomial - pub gamma_powers: Vec, - /// (polynomial, claim) pairs at the opening point - /// (with Lagrange factors already applied for shorter polys) - pub polynomial_claims: Vec<(CommittedPolynomial, F)>, -} - -impl DoryOpeningState { - /// Build streaming RLC polynomial from this state. - /// Streams directly from trace - no witness regeneration needed. - /// Advice polynomials are passed separately (not streamed from trace). - #[tracing::instrument(skip_all)] - pub fn build_streaming_rlc>( - &self, - one_hot_params: OneHotParams, - trace_source: TraceSource, - rlc_streaming_data: Arc, - mut opening_hints: HashMap, - advice_polys: HashMap>, - ) -> (MultilinearPolynomial, PCS::OpeningProofHint) { - // Accumulate gamma coefficients per polynomial - let mut rlc_map = BTreeMap::new(); - for (gamma, (poly, _claim)) in self.gamma_powers.iter().zip(self.polynomial_claims.iter()) { - *rlc_map.entry(*poly).or_insert(F::zero()) += *gamma; - } - - let (poly_ids, coeffs): (Vec, Vec) = - rlc_map.iter().map(|(k, v)| (*k, *v)).unzip(); - - let joint_poly = MultilinearPolynomial::RLC(RLCPolynomial::new_streaming( - one_hot_params, - rlc_streaming_data, - trace_source, - poly_ids.clone(), - &coeffs, - advice_polys, - )); - - let hints: Vec = rlc_map - .into_keys() - .map(|k| opening_hints.remove(&k).unwrap()) - .collect(); - - let hint = PCS::combine_hints(hints, &coeffs); - - (joint_poly, hint) - } -} - impl Default for ProverOpeningAccumulator where F: JoltField, diff --git a/jolt-core/src/poly/rlc_polynomial.rs b/jolt-core/src/poly/rlc_polynomial.rs index b60aa40c3a..9e2a86f4f0 100644 --- a/jolt-core/src/poly/rlc_polynomial.rs +++ b/jolt-core/src/poly/rlc_polynomial.rs @@ -91,6 +91,11 @@ impl PartialEq for RLCPolynomial { } impl RLCPolynomial { + /// Total number of coefficients in the Dory matrix for this RLC polynomial. + pub fn len(&self) -> usize { + DoryGlobals::get_num_columns() * DoryGlobals::get_max_num_rows() + } + pub fn new() -> Self { Self { dense_rlc: unsafe_allocate_zero_vec(DoryGlobals::get_T()), diff --git a/jolt-core/src/zkvm/proof_serialization.rs b/jolt-core/src/zkvm/proof_serialization.rs index a8058963e4..9291b532fb 100644 --- a/jolt-core/src/zkvm/proof_serialization.rs +++ b/jolt-core/src/zkvm/proof_serialization.rs @@ -1,5 +1,6 @@ #[cfg(not(feature = "zk"))] use std::collections::BTreeMap; +use std::fs::File; use std::io::{Read, Write}; use ark_serialize::{ @@ -507,7 +508,6 @@ pub fn serialize_and_print_size( file_name: &str, item: &impl CanonicalSerialize, ) -> Result<(), SerializationError> { - use std::fs::File; let mut file = File::create(file_name)?; item.serialize_compressed(&mut file)?; let file_size_bytes = file.metadata()?.len(); diff --git a/jolt-core/src/zkvm/prover.rs b/jolt-core/src/zkvm/prover.rs index b30ec0c105..5a3a72c14f 100644 --- a/jolt-core/src/zkvm/prover.rs +++ b/jolt-core/src/zkvm/prover.rs @@ -4,7 +4,7 @@ use crate::zkvm::{claim_reductions::advice::ReductionPhase, config::OneHotConfig #[cfg(not(target_arch = "wasm32"))] use std::time::Instant; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, fs::File, io::{Read, Write}, path::Path, @@ -37,8 +37,8 @@ use crate::{ eq_poly::EqPolynomial, multilinear_polynomial::MultilinearPolynomial, opening_proof::{ - compute_advice_lagrange_factor, DoryOpeningState, OpeningAccumulator, - ProverOpeningAccumulator, SumcheckId, + compute_advice_lagrange_factor, OpeningAccumulator, ProverOpeningAccumulator, + StreamingBatchSource, SumcheckId, }, rlc_polynomial::{RLCStreamingData, TraceSource}, }, @@ -503,7 +503,19 @@ where let untrusted_advice_commitment = self.generate_and_commit_untrusted_advice(); self.generate_and_commit_trusted_advice(); - // Add advice hints for batched Stage 8 opening + // Build per-polynomial commitment map for Stage 8 + let main_polys = all_committed_polynomials(&self.one_hot_params); + let mut commitment_map: HashMap = main_polys + .into_iter() + .zip(commitments.iter().cloned()) + .collect(); + if let Some(ref c) = self.advice.trusted_advice_commitment { + commitment_map.insert(CommittedPolynomial::TrustedAdvice, c.clone()); + } + if let Some(ref c) = untrusted_advice_commitment { + commitment_map.insert(CommittedPolynomial::UntrustedAdvice, c.clone()); + } + if let Some(hint) = self.advice.trusted_advice_hint.take() { opening_proof_hints.insert(CommittedPolynomial::TrustedAdvice, hint); } @@ -525,7 +537,7 @@ where r_stage1, r_stage2, r_stage3, r_stage4, r_stage5, r_stage6, r_stage7, ]; - let joint_opening_proof = self.prove_stage8(opening_proof_hints); + let joint_opening_proof = self.prove_stage8(opening_proof_hints, commitment_map); #[cfg(feature = "zk")] let blindfold_proof = self.prove_blindfold(&joint_opening_proof); @@ -690,7 +702,7 @@ where &trace, Some(&self.one_hot_params), ); - PCS::commit(&witness, &self.preprocessing.generators) + PCS::default().commit(&witness, &self.preprocessing.generators) }) .unzip(); @@ -750,7 +762,7 @@ where .zip(&polys) .map(|(tier1_commitments, poly)| { let onehot_k = poly.get_onehot_k(&self.one_hot_params); - PCS::aggregate_chunks( + PCS::default().aggregate_chunks( &self.preprocessing.generators, onehot_k, &tier1_commitments, @@ -795,7 +807,7 @@ where let _guard = DoryGlobals::initialize_context(1, advice_len, DoryContext::UntrustedAdvice, None); let _ctx = DoryGlobals::with_context(DoryContext::UntrustedAdvice); - let (commitment, hint) = PCS::commit(&poly, &self.preprocessing.generators); + let (commitment, hint) = PCS::default().commit(&poly, &self.preprocessing.generators); self.transcript .append_serializable(b"untrusted_advice", &commitment); @@ -1820,14 +1832,15 @@ where (sumcheck_proof, r_stage7) } - /// Stage 8: Dory batch opening proof. - /// Builds streaming RLC polynomial directly from trace (no witness regeneration needed). + /// Stage 8: PCS batch opening proof. + /// Streams polynomial data lazily from trace via `StreamingBatchSource` -- no witness regeneration. #[tracing::instrument(skip_all)] fn prove_stage8( &mut self, opening_proof_hints: HashMap, + commitment_map: HashMap, ) -> PCS::Proof { - tracing::info!("Stage 8 proving (Dory batch opening)"); + tracing::info!("Stage 8 proving (batch opening)"); let _guard = DoryGlobals::initialize_context( self.one_hot_params.k_chunk, @@ -1836,8 +1849,6 @@ where Some(DoryGlobals::get_layout()), ); - // Get the unified opening point from HammingWeightClaimReduction - // This contains (r_address_stage7 || r_cycle_stage6) in big-endian let (opening_point, _) = self.opening_accumulator.get_committed_polynomial_opening( CommittedPolynomial::InstructionRa(0), SumcheckId::HammingWeightClaimReduction, @@ -1849,16 +1860,31 @@ where let mut polynomial_claims = Vec::new(); let mut scaling_factors = Vec::new(); - // Dense polynomials: RamInc and RdInc (from IncClaimReduction in Stage 6) - // at r_cycle_stage6 only (length log_T) - let (_, ram_inc_claim) = self.opening_accumulator.get_committed_polynomial_opening( - CommittedPolynomial::RamInc, - SumcheckId::IncClaimReduction, - ); - let (_, rd_inc_claim) = self.opening_accumulator.get_committed_polynomial_opening( - CommittedPolynomial::RdInc, - SumcheckId::IncClaimReduction, - ); + let (_ram_inc_point, ram_inc_claim) = + self.opening_accumulator.get_committed_polynomial_opening( + CommittedPolynomial::RamInc, + SumcheckId::IncClaimReduction, + ); + let (_rd_inc_point, rd_inc_claim) = + self.opening_accumulator.get_committed_polynomial_opening( + CommittedPolynomial::RdInc, + SumcheckId::IncClaimReduction, + ); + + #[cfg(test)] + { + let r_cycle_stage6 = &opening_point.r[log_k_chunk..]; + debug_assert_eq!( + _ram_inc_point.r.as_slice(), + r_cycle_stage6, + "RamInc opening point should match r_cycle from HammingWeightClaimReduction" + ); + debug_assert_eq!( + _rd_inc_point.r.as_slice(), + r_cycle_stage6, + "RdInc opening point should match r_cycle from HammingWeightClaimReduction" + ); + } // Dense polynomials are zero-padded in the Dory matrix, so their evaluation // includes a factor eq(r_addr, 0) = ∏(1 − r_addr_i). @@ -1868,8 +1894,6 @@ where polynomial_claims.push((CommittedPolynomial::RdInc, rd_inc_claim * lagrange_factor)); scaling_factors.push(lagrange_factor); - // Sparse polynomials: all RA polys (from HammingWeightClaimReduction) - // These are at (r_address_stage7, r_cycle_stage6) for i in 0..self.one_hot_params.instruction_d { let (_, claim) = self.opening_accumulator.get_committed_polynomial_opening( CommittedPolynomial::InstructionRa(i), @@ -1895,9 +1919,6 @@ where scaling_factors.push(F::one()); } - // Advice polynomials: TrustedAdvice and UntrustedAdvice (from AdviceClaimReduction in Stage 6) - // These are committed with smaller dimensions, so we apply Lagrange factors to embed - // them in the top-left block of the main Dory matrix. #[cfg(feature = "zk")] let mut include_trusted_advice = false; #[cfg(feature = "zk")] @@ -1937,7 +1958,7 @@ where } } - // 2. Sample gamma and compute powers for RLC + // Sample gamma and compute powers for RLC let claims: Vec = polynomial_claims.iter().map(|(_, c)| *c).collect(); // In non-ZK mode, absorb claims before sampling gamma for Fiat-Shamir binding. // In ZK mode, claims are secret; binding comes from BlindFold constraints instead. @@ -1963,19 +1984,36 @@ where include_untrusted_advice, ); - // Build DoryOpeningState - let state = DoryOpeningState { - opening_point: opening_point.r.clone(), - gamma_powers, - polynomial_claims, - }; + // Accumulate gamma coefficients per unique polynomial (BTreeMap orders by CommittedPolynomial) + let mut rlc_map = BTreeMap::new(); + for (gamma, (poly, claim)) in gamma_powers.iter().zip(polynomial_claims.iter()) { + let entry = rlc_map.entry(*poly).or_insert((F::zero(), F::zero())); + entry.0 += *gamma; + entry.1 = *claim; + } + + let (poly_ids, coeffs_and_claims): (Vec, Vec<(F, F)>) = + rlc_map.into_iter().collect(); + let (coeffs, sorted_claims): (Vec, Vec) = coeffs_and_claims.into_iter().unzip(); + + // Collect per-polynomial hints and commitments in the same order + let mut hint_map = opening_proof_hints; + let hints: Vec = poly_ids + .iter() + .map(|id| hint_map.remove(id).unwrap()) + .collect(); + let mut commit_map = commitment_map; + let commitment_refs: Vec = poly_ids + .iter() + .map(|id| commit_map.remove(id).unwrap()) + .collect(); + let commitment_ref_slice: Vec<&PCS::Commitment> = commitment_refs.iter().collect(); let streaming_data = Arc::new(RLCStreamingData { bytecode: Arc::clone(&self.preprocessing.shared.bytecode), memory_layout: self.preprocessing.shared.memory_layout.clone(), }); - // Build advice polynomials map for RLC let mut advice_polys = HashMap::new(); if let Some(poly) = self.advice.trusted_advice_polynomial.take() { advice_polys.insert(CommittedPolynomial::TrustedAdvice, poly); @@ -1984,21 +2022,22 @@ where advice_polys.insert(CommittedPolynomial::UntrustedAdvice, poly); } - // Build streaming RLC polynomial directly (no witness poly regeneration!) - // Use materialized trace (default, single pass) instead of lazy trace - let (joint_poly, hint) = state.build_streaming_rlc::( - self.one_hot_params.clone(), - TraceSource::Materialized(Arc::clone(&self.trace)), + let poly_source = StreamingBatchSource { + one_hot_params: self.one_hot_params.clone(), + trace_source: TraceSource::Materialized(Arc::clone(&self.trace)), streaming_data, - opening_proof_hints, advice_polys, - ); + poly_ids, + }; - let (proof, _y_blinding) = PCS::prove( + let (proof, _y_blinding) = PCS::default().batch_prove( &self.preprocessing.generators, - &joint_poly, + &poly_source, + hints, + &commitment_ref_slice, &opening_point.r, - Some(hint), + &sorted_claims, + &coeffs, &mut self.transcript, ); @@ -2196,7 +2235,7 @@ mod tests { DoryGlobals::initialize_context(1, advice_len, DoryContext::TrustedAdvice, None); let (commitment, hint) = { let _ctx = DoryGlobals::with_context(DoryContext::TrustedAdvice); - DoryCommitmentScheme::commit(&poly, &preprocessing.generators) + DoryCommitmentScheme::default().commit(&poly, &preprocessing.generators) }; (commitment, hint) } diff --git a/jolt-core/src/zkvm/verifier.rs b/jolt-core/src/zkvm/verifier.rs index 9e63a1a254..8ec9d4af0d 100644 --- a/jolt-core/src/zkvm/verifier.rs +++ b/jolt-core/src/zkvm/verifier.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::fs::File; use std::io::{Read, Write}; use std::path::Path; @@ -75,8 +75,8 @@ use crate::{ poly::{ eq_poly::EqPolynomial, opening_proof::{ - compute_advice_lagrange_factor, DoryOpeningState, OpeningAccumulator, OpeningId, - SumcheckId, VerifierOpeningAccumulator, + compute_advice_lagrange_factor, OpeningAccumulator, OpeningId, SumcheckId, + VerifierOpeningAccumulator, }, }, pprof_scope, @@ -1441,15 +1441,20 @@ where .map(|(gamma, claim)| *gamma * claim) .sum(); - // Build state for computing joint commitment/claim - let state = DoryOpeningState { - opening_point: opening_point.r.clone(), - gamma_powers: gamma_powers.clone(), - polynomial_claims, - }; + // Accumulate gamma coefficients per unique polynomial (BTreeMap for deterministic ordering) + let mut rlc_map = BTreeMap::new(); + for (gamma, (poly, claim)) in gamma_powers.iter().zip(polynomial_claims.iter()) { + let entry = rlc_map.entry(*poly).or_insert((F::zero(), F::zero())); + entry.0 += *gamma; + entry.1 = *claim; + } + + let (poly_ids, coeffs_and_claims): (Vec, Vec<(F, F)>) = + rlc_map.into_iter().collect(); + let (coeffs, sorted_claims): (Vec, Vec) = coeffs_and_claims.into_iter().unzip(); - // Build commitments map - let mut commitments_map = HashMap::new(); + // Build commitments map with length validation + let mut commitments_map: HashMap = HashMap::new(); let expected_polynomials = all_committed_polynomials(&self.one_hot_params); if expected_polynomials.len() != self.proof.commitments.len() { return Err(ProofVerifyError::InvalidInputLength( @@ -1463,38 +1468,30 @@ where { commitments_map.insert(polynomial, commitment.clone()); } - - // Add advice commitments if they're part of the batch if let Some(ref commitment) = self.trusted_advice_commitment { - if state - .polynomial_claims - .iter() - .any(|(p, _)| *p == CommittedPolynomial::TrustedAdvice) - { - commitments_map.insert(CommittedPolynomial::TrustedAdvice, commitment.clone()); - } + commitments_map.insert(CommittedPolynomial::TrustedAdvice, commitment.clone()); } if let Some(ref commitment) = self.proof.untrusted_advice_commitment { - if state - .polynomial_claims - .iter() - .any(|(p, _)| *p == CommittedPolynomial::UntrustedAdvice) - { - commitments_map.insert(CommittedPolynomial::UntrustedAdvice, commitment.clone()); - } + commitments_map.insert(CommittedPolynomial::UntrustedAdvice, commitment.clone()); } - let joint_commitment = self.compute_joint_commitment(&mut commitments_map, &state)?; + let commitment_refs: Vec = poly_ids + .iter() + .map(|id| commitments_map.remove(id).unwrap()) + .collect(); + let commitment_ref_slice: Vec<&PCS::Commitment> = commitment_refs.iter().collect(); let zk_mode = self.opening_accumulator.zk_mode; if zk_mode { - PCS::verify( + let zero_claims = vec![F::zero(); sorted_claims.len()]; + PCS::from_proof(&self.proof.joint_opening_proof).batch_verify( &self.proof.joint_opening_proof, &self.preprocessing.generators, &mut self.transcript, &opening_point.r, - &F::zero(), - &joint_commitment, + &commitment_ref_slice, + &zero_claims, + &coeffs, )?; #[cfg(feature = "zk")] @@ -1508,13 +1505,14 @@ where return Err(ProofVerifyError::ZkFeatureRequired); } } else { - PCS::verify( + PCS::from_proof(&self.proof.joint_opening_proof).batch_verify( &self.proof.joint_opening_proof, &self.preprocessing.generators, &mut self.transcript, &opening_point.r, - &joint_claim, - &joint_commitment, + &commitment_ref_slice, + &sorted_claims, + &coeffs, )?; bind_opening_inputs::(&mut self.transcript, &opening_point.r, &joint_claim); @@ -1525,36 +1523,6 @@ where constraint_coeffs, }) } - - /// Compute joint commitment for the batch opening. - fn compute_joint_commitment( - &self, - commitment_map: &mut HashMap, - state: &DoryOpeningState, - ) -> Result { - let mut rlc_map = HashMap::new(); - for (gamma, (poly, _claim)) in state - .gamma_powers - .iter() - .zip(state.polynomial_claims.iter()) - { - *rlc_map.entry(*poly).or_insert(F::zero()) += *gamma; - } - - let (coeffs, commitments): (Vec, Vec) = rlc_map - .into_iter() - .map(|(k, v)| { - commitment_map - .remove(&k) - .map(|c| (v, c)) - .ok_or(ProofVerifyError::InternalError) - }) - .collect::, _>>()? - .into_iter() - .unzip(); - - Ok(PCS::combine_commitments(&commitments, &coeffs)) - } } #[derive(Debug, Clone)] diff --git a/jolt-core/src/zkvm/witness.rs b/jolt-core/src/zkvm/witness.rs index efcef73652..d017cd978e 100644 --- a/jolt-core/src/zkvm/witness.rs +++ b/jolt-core/src/zkvm/witness.rs @@ -71,6 +71,7 @@ impl CommittedPolynomial { F: JoltField, PCS: StreamingCommitmentScheme, { + let pcs = PCS::default(); match self { CommittedPolynomial::RdInc => { let row: Vec = row_cycles @@ -80,7 +81,7 @@ impl CommittedPolynomial { post_value as i128 - pre_value as i128 }) .collect(); - PCS::process_chunk(setup, &row) + pcs.process_chunk(setup, &row) } CommittedPolynomial::RamInc => { let row: Vec = row_cycles @@ -92,7 +93,7 @@ impl CommittedPolynomial { _ => 0, }) .collect(); - PCS::process_chunk(setup, &row) + pcs.process_chunk(setup, &row) } CommittedPolynomial::InstructionRa(idx) => { let row: Vec> = row_cycles @@ -102,7 +103,7 @@ impl CommittedPolynomial { Some(one_hot_params.lookup_index_chunk(lookup_index, *idx) as usize) }) .collect(); - PCS::process_chunk_onehot(setup, one_hot_params.k_chunk, &row) + pcs.process_chunk_onehot(setup, one_hot_params.k_chunk, &row) } CommittedPolynomial::BytecodeRa(idx) => { let row: Vec> = row_cycles @@ -112,7 +113,7 @@ impl CommittedPolynomial { Some(one_hot_params.bytecode_pc_chunk(pc, *idx) as usize) }) .collect(); - PCS::process_chunk_onehot(setup, one_hot_params.k_chunk, &row) + pcs.process_chunk_onehot(setup, one_hot_params.k_chunk, &row) } CommittedPolynomial::RamRa(idx) => { let row: Vec> = row_cycles @@ -125,7 +126,7 @@ impl CommittedPolynomial { .map(|address| one_hot_params.ram_address_chunk(address, *idx) as usize) }) .collect(); - PCS::process_chunk_onehot(setup, one_hot_params.k_chunk, &row) + pcs.process_chunk_onehot(setup, one_hot_params.k_chunk, &row) } CommittedPolynomial::TrustedAdvice | CommittedPolynomial::UntrustedAdvice => { panic!("Advice polynomials should not use streaming witness generation") diff --git a/jolt-sdk/macros/src/lib.rs b/jolt-sdk/macros/src/lib.rs index a7ac908fdd..ba61951da4 100644 --- a/jolt-sdk/macros/src/lib.rs +++ b/jolt-sdk/macros/src/lib.rs @@ -619,7 +619,7 @@ impl MacroBuilder { let _ctx = jolt::DoryGlobals::with_context(jolt::DoryContext::TrustedAdvice); let poly = MultilinearPolynomial::::from(trusted_advice_vec); - let (commitment, hint) = jolt::PCS::commit(&poly, &preprocessing.generators); + let (commitment, hint) = jolt::PCS::default().commit(&poly, &preprocessing.generators); (Some(commitment), Some(hint)) }