From 049669a577396142e99b1785de8e1dc524125df1 Mon Sep 17 00:00:00 2001 From: Alon Haramati Date: Sun, 18 Feb 2024 15:32:14 +0200 Subject: [PATCH] Contain queries in FRI. --- src/core/fri.rs | 109 ++++++++++++++++++++++++++++++------------- src/core/queries.rs | 7 +-- src/fibonacci/mod.rs | 33 +++---------- 3 files changed, 87 insertions(+), 62 deletions(-) diff --git a/src/core/fri.rs b/src/core/fri.rs index 60ef4131c..e2d98164d 100644 --- a/src/core/fri.rs +++ b/src/core/fri.rs @@ -246,6 +246,7 @@ impl> FriProver { } pub struct FriVerifier { + config: FriConfig, /// Alpha used to fold all circle polynomials to univariate polynomials. circle_poly_alpha: SecureField, /// Domain size queries should be sampled from. @@ -255,6 +256,9 @@ pub struct FriVerifier { inner_layers: Vec>, last_layer_domain: LineDomain, last_layer_poly: LinePoly, + /// The queries used for decommitment. Initialized when calling + /// [`FriVerifier::column_opening_positions`]. + queries: Option, } impl> FriVerifier { @@ -328,12 +332,14 @@ impl> FriVerifier { channel.mix_felts(&last_layer_poly); Ok(Self { + config, circle_poly_alpha, column_bounds, expected_query_log_domain_size, inner_layers, last_layer_domain, last_layer_poly, + queries: None, }) } @@ -344,23 +350,36 @@ impl> FriVerifier { /// # Panics /// /// Panics if: + /// * The queries were not yet sampled. /// * The queries were sampled on the wrong domain size. - /// * There aren't the same number of decommited values as degree bounds. + /// * There aren't the same number of decommitted values as degree bounds. // TODO(andrew): Finish docs. pub fn decommit( + mut self, + decommitted_values: Vec>, + ) -> Result<(), VerificationError> + where + F: ExtensionOf, + SecureField: ExtensionOf, + { + let queries = self.queries.take().expect("queries not sampled"); + self.decommit_on_queries(&queries, decommitted_values) + } + + fn decommit_on_queries( self, queries: &Queries, - decommited_values: Vec>, + decommitted_values: Vec>, ) -> Result<(), VerificationError> where F: ExtensionOf, SecureField: ExtensionOf, { assert_eq!(queries.log_domain_size, self.expected_query_log_domain_size); - assert_eq!(decommited_values.len(), self.column_bounds.len()); + assert_eq!(decommitted_values.len(), self.column_bounds.len()); let (last_layer_queries, last_layer_query_evals) = - self.decommit_inner_layers(queries, decommited_values)?; + self.decommit_inner_layers(queries, decommitted_values)?; self.decommit_last_layer(last_layer_queries, last_layer_query_evals) } @@ -371,7 +390,7 @@ impl> FriVerifier { fn decommit_inner_layers( &self, queries: &Queries, - decommited_values: Vec>, + decommitted_values: Vec>, ) -> Result<(Queries, Vec), VerificationError> where F: ExtensionOf, @@ -380,7 +399,7 @@ impl> FriVerifier { let circle_poly_alpha = self.circle_poly_alpha; let circle_poly_alpha_sq = circle_poly_alpha * circle_poly_alpha; - let mut decommited_values = decommited_values.into_iter(); + let mut decommitted_values = decommitted_values.into_iter(); let mut column_bounds = self.column_bounds.iter().copied().peekable(); let mut layer_queries = queries.fold(CIRCLE_TO_LINE_FOLD_STEP); let mut layer_query_evals = vec![SecureField::zero(); layer_queries.len()]; @@ -391,7 +410,7 @@ impl> FriVerifier { .next_if(|b| b.fold_to_line() == layer.degree_bound) .is_some() { - let sparse_evaluation = decommited_values.next().unwrap(); + let sparse_evaluation = decommitted_values.next().unwrap(); let folded_evals = sparse_evaluation.fold(circle_poly_alpha); assert_eq!(folded_evals.len(), layer_query_evals.len()); @@ -406,7 +425,7 @@ impl> FriVerifier { // Check all values have been consumed. assert!(column_bounds.is_empty()); - assert!(decommited_values.is_empty()); + assert!(decommitted_values.is_empty()); Ok((layer_queries, layer_query_evals)) } @@ -433,9 +452,28 @@ impl> FriVerifier { Ok(()) } + + /// Samples queries and returns the opening positions for each unique column size. + /// The order of the opening positions corresponds to the order of the column commitment. + pub fn column_opening_positions( + &mut self, + channel: &mut impl Channel, + ) -> Vec { + let column_log_sizes = self + .column_bounds + .iter() + .dedup() + .map(|b| b.log_degree_bound + self.config.log_blowup_factor) + .collect_vec(); + let queries = Queries::generate(channel, column_log_sizes[0], self.config.n_queries); + let positions = get_opening_positions(&queries, &column_log_sizes); + self.queries = Some(queries); + positions + } } /// Returns the column opening positions needed for verification. +/// The domain log sizes must be in unique and in descending order. fn get_opening_positions( queries: &Queries, domain_log_sizes: &[u32], @@ -897,7 +935,7 @@ mod tests { use num_traits::{One, Zero}; - use super::{SparseCircleEvaluation, VerificationError}; + use super::{get_opening_positions, SparseCircleEvaluation, VerificationError}; use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher}; use crate::core::channel::{Blake2sChannel, Channel}; use crate::core::circle::{CirclePointIndex, Coset}; @@ -912,8 +950,7 @@ mod tests { use crate::core::poly::circle::{CircleDomain, CircleEvaluation, CirclePoly}; use crate::core::poly::line::{LineDomain, LineEvaluation, LinePoly}; use crate::core::poly::{BitReversedOrder, NaturalOrder}; - use crate::core::queries::Queries; - use crate::core::utils::bit_reverse_index; + use crate::core::queries::{Queries, SparseSubCircleDomain}; /// Default blowup factor used for tests. const LOG_BLOWUP_FACTOR: u32 = 2; @@ -997,7 +1034,7 @@ mod tests { let bound = vec![CirclePolyDegreeBound::new(LOG_DEGREE)]; let verifier = FriVerifier::commit(&mut test_channel(), config, proof, bound).unwrap(); - verifier.decommit(&queries, vec![decommitment_value]) + verifier.decommit_on_queries(&queries, vec![decommitment_value]) } #[test] @@ -1014,7 +1051,7 @@ mod tests { let bounds = LOG_DEGREES.map(CirclePolyDegreeBound::new).to_vec(); let verifier = FriVerifier::commit(&mut test_channel(), config, proof, bounds).unwrap(); - verifier.decommit(&queries, decommitment_values) + verifier.decommit_on_queries(&queries, decommitment_values) } #[test] @@ -1022,16 +1059,19 @@ mod tests { const LOG_DEGREES: [u32; 3] = [6, 5, 4]; let polynomials = LOG_DEGREES.map(|log_d| polynomial_evaluation::(log_d, LOG_BLOWUP_FACTOR)); - let max_column_log_size = polynomials[0].domain.log_size(); let config = FriConfig::new(2, LOG_BLOWUP_FACTOR, 3); - let queries = Queries::generate(&mut test_channel(), max_column_log_size, config.n_queries); let prover = FriProver::commit(&mut test_channel(), config, &polynomials); - let decommitment_values = polynomials.map(|p| query_polynomial(&p, &queries)).to_vec(); - let (proof, _) = prover.decommit(&mut test_channel()); + let (proof, prover_opening_positions) = prover.decommit(&mut test_channel()); + let decommitment_values = zip(&polynomials, &prover_opening_positions) + .map(|(poly, positions)| open_polynomial(poly, positions)) + .collect(); let bounds = LOG_DEGREES.map(CirclePolyDegreeBound::new).to_vec(); - let verifier = FriVerifier::commit(&mut test_channel(), config, proof, bounds).unwrap(); - verifier.decommit(&queries, decommitment_values) + let mut verifier = FriVerifier::commit(&mut test_channel(), config, proof, bounds).unwrap(); + let verifier_opening_positions = verifier.column_opening_positions(&mut test_channel()); + + assert_eq!(prover_opening_positions, verifier_opening_positions); + verifier.decommit(decommitment_values) } #[test] @@ -1093,7 +1133,7 @@ mod tests { proof.inner_layers[1].evals_subset.pop(); let verifier = FriVerifier::commit(&mut test_channel(), config, proof, bound).unwrap(); - let verification_result = verifier.decommit(&queries, vec![decommitment_value]); + let verification_result = verifier.decommit_on_queries(&queries, vec![decommitment_value]); assert!(matches!( verification_result, @@ -1116,7 +1156,7 @@ mod tests { proof.inner_layers[1].evals_subset[0] += BaseField::one(); let verifier = FriVerifier::commit(&mut test_channel(), config, proof, bound).unwrap(); - let verification_result = verifier.decommit(&queries, vec![decommitment_value]); + let verification_result = verifier.decommit_on_queries(&queries, vec![decommitment_value]); assert!(matches!( verification_result, @@ -1161,7 +1201,7 @@ mod tests { proof.last_layer_poly[0] += BaseField::one(); let verifier = FriVerifier::commit(&mut test_channel(), config, proof, bound).unwrap(); - let verification_result = verifier.decommit(&queries, vec![decommitment_value]); + let verification_result = verifier.decommit_on_queries(&queries, vec![decommitment_value]); assert!(matches!( verification_result, @@ -1186,7 +1226,7 @@ mod tests { let mut invalid_queries = queries.clone(); invalid_queries.log_domain_size -= 1; - let _ = verifier.decommit(&invalid_queries, vec![decommitment_value]); + let _ = verifier.decommit_on_queries(&invalid_queries, vec![decommitment_value]); } /// Returns an evaluation of a random polynomial with degree `2^log_degree`. @@ -1217,19 +1257,24 @@ mod tests { polynomial: &CircleEvaluation, queries: &Queries, ) -> SparseCircleEvaluation { - let domain = polynomial.domain; + let positions = get_opening_positions( + queries, + &[queries.log_domain_size, polynomial.domain.log_size()], + ); + open_polynomial(polynomial, &positions[1]) + } + + fn open_polynomial>( + polynomial: &CircleEvaluation, + positions: &SparseSubCircleDomain, + ) -> SparseCircleEvaluation { let polynomial = polynomial.clone().bit_reverse(); let oracle = EvalByEvaluation::new(CirclePointIndex::zero(), &polynomial); - // Get queries on the polynomial - let polynomial_queries = queries.fold(queries.log_domain_size - domain.log_size()); - - let coset_evals = polynomial_queries + let coset_evals = positions .iter() - .map(|&query| { - let position = bit_reverse_index(query, domain.log_size()); - let p = domain.index_at(position); - let coset_domain = CircleDomain::new(Coset::new(p, 0)); + .map(|position| { + let coset_domain = position.to_circle_domain(&polynomial.domain); let evals = coset_domain .iter_indices() .map(|p| oracle.get_at(p)) diff --git a/src/core/queries.rs b/src/core/queries.rs index f06d32b45..673536644 100644 --- a/src/core/queries.rs +++ b/src/core/queries.rs @@ -74,7 +74,7 @@ impl Queries { }) .dedup() .collect(), - large_domain_size: self.log_domain_size, + large_domain_log_size: self.log_domain_size, } } } @@ -87,9 +87,10 @@ impl Deref for Queries { } } +#[derive(Debug, Eq, PartialEq)] pub struct SparseSubCircleDomain { pub domains: Vec, - pub large_domain_size: u32, + pub large_domain_log_size: u32, } impl SparseSubCircleDomain { @@ -110,7 +111,7 @@ impl Deref for SparseSubCircleDomain { /// Represents a circle domain relative to a larger circle domain. The `initial_index` is the bit /// reversed query index in the larger domain. -#[derive(PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct SubCircleDomain { pub coset_index: usize, pub log_size: u32, diff --git a/src/fibonacci/mod.rs b/src/fibonacci/mod.rs index 6a437eeca..bb77dda01 100644 --- a/src/fibonacci/mod.rs +++ b/src/fibonacci/mod.rs @@ -21,7 +21,6 @@ use crate::core::oods::{get_oods_quotient, get_pair_oods_quotient, quotient_log_ use crate::core::poly::circle::{CanonicCoset, CircleEvaluation, CirclePoly}; use crate::core::poly::BitReversedOrder; use crate::core::proof_of_work::{ProofOfWork, ProofOfWorkProof}; -use crate::core::queries::Queries; type Channel = Blake2sChannel; type MerkleHasher = Blake2sHasher; @@ -237,31 +236,13 @@ pub fn verify_proof(proof: FibonacciProof) -> bool { )]; bounds.append(&mut quotient_log_bounds(fib.component)); let fri_config = FriConfig::new(LOG_LAST_LAYER_DEGREE_BOUND, LOG_BLOWUP_FACTOR, N_QUERIES); - let fri_verifier = FriVerifier::commit(channel, fri_config, proof.fri_proof, bounds).unwrap(); + let mut fri_verifier = + FriVerifier::commit(channel, fri_config, proof.fri_proof, bounds).unwrap(); ProofOfWork::new(PROOF_OF_WORK_BITS).verify(channel, &proof.proof_of_work); - let composition_polynomial_queries = Queries::generate( - channel, - fib.composition_polynomial_commitment_domain.log_size(), - N_QUERIES, - ); - let trace_queries = composition_polynomial_queries.fold( - fib.composition_polynomial_commitment_domain.log_size() - - fib.trace_commitment_domain.log_size(), - ); - // TODO(AlonH): Get sub circle domains from FRI. - const FRI_STEP_SIZE: u32 = 1; - let composition_polynomial_opening_positions = - composition_polynomial_queries.opening_positions(FRI_STEP_SIZE); - let trace_opening_positions = trace_queries.opening_positions(FRI_STEP_SIZE); - assert_eq!( - trace_opening_positions.len(), - proof.trace_opened_values.len() >> FRI_STEP_SIZE - ); - assert_eq!( - composition_polynomial_opening_positions.len(), - proof.composition_polynomial_opened_values.len() >> FRI_STEP_SIZE - ); + let opening_positions = fri_verifier.column_opening_positions(channel); + let composition_polynomial_opening_positions = &opening_positions[0]; + let trace_opening_positions = &opening_positions[1]; assert!(trace_commitment_scheme.verify( &proof.trace_decommitments[0], &trace_opening_positions.flatten() @@ -315,9 +296,7 @@ pub fn verify_proof(proof: FibonacciProof) -> bool { sparse_circle_evaluations.push(SparseCircleEvaluation::new(evaluation)); } - fri_verifier - .decommit(&composition_polynomial_queries, sparse_circle_evaluations) - .unwrap(); + fri_verifier.decommit(sparse_circle_evaluations).unwrap(); true }