diff --git a/crates/prover/src/core/fri.rs b/crates/prover/src/core/fri.rs index 6bcb3cb2a..7291856be 100644 --- a/crates/prover/src/core/fri.rs +++ b/crates/prover/src/core/fri.rs @@ -208,7 +208,6 @@ impl<'a, B: FriOps + MerkleOps, MC: MerkleChannel> FriProver<'a, B, MC> { v.len() >> CIRCLE_TO_LINE_FOLD_STEP } - let circle_poly_folding_alpha = channel.draw_felt(); let first_inner_layer_log_size = folded_size(&columns[0]).ilog2(); let first_inner_layer_domain = LineDomain::new(Coset::half_odds(first_inner_layer_log_size)); @@ -216,21 +215,17 @@ impl<'a, B: FriOps + MerkleOps, MC: MerkleChannel> FriProver<'a, B, MC> { let mut layer_evaluation = LineEvaluation::new_zero(first_inner_layer_domain); let mut columns = columns.iter().peekable(); let mut layers = Vec::new(); + let mut folding_alpha = channel.draw_felt(); while layer_evaluation.len() > config.last_layer_domain_size() { // Check for circle polys in the first layer that should be combined in this layer. while let Some(column) = columns.next_if(|c| folded_size(c) == layer_evaluation.len()) { - B::fold_circle_into_line( - &mut layer_evaluation, - column, - circle_poly_folding_alpha, - twiddles, - ); + B::fold_circle_into_line(&mut layer_evaluation, column, folding_alpha, twiddles); } let layer = FriInnerLayerProver::new(layer_evaluation); MC::mix_root(channel, layer.merkle_tree.root()); - let folding_alpha = channel.draw_felt(); + folding_alpha = channel.draw_felt(); let folded_layer_evaluation = B::fold_line(&layer.evaluation, folding_alpha, twiddles); layer_evaluation = folded_layer_evaluation; @@ -447,10 +442,11 @@ impl FriVerifier { queries: &Queries, first_layer_query_evals: ColumnVec>, ) -> Result<(), FriVerificationError> { - let (inner_layer_queries, first_layer_folded_evals) = + let first_layer_sparse_evals = self.decommit_first_layer(queries, first_layer_query_evals)?; + let inner_layer_queries = queries.fold(CIRCLE_TO_LINE_FOLD_STEP); let (last_layer_queries, last_layer_query_evals) = - self.decommit_inner_layers(&inner_layer_queries, first_layer_folded_evals)?; + self.decommit_inner_layers(&inner_layer_queries, first_layer_sparse_evals)?; self.decommit_last_layer(last_layer_queries, last_layer_query_evals) } @@ -462,9 +458,8 @@ impl FriVerifier { &self, queries: &Queries, first_layer_query_evals: ColumnVec>, - ) -> Result<(Queries, ColumnVec>), FriVerificationError> { - self.first_layer - .verify_and_fold(queries, first_layer_query_evals) + ) -> Result, FriVerificationError> { + self.first_layer.verify(queries, first_layer_query_evals) } /// Verifies all inner layer decommitments. @@ -473,41 +468,46 @@ impl FriVerifier { fn decommit_inner_layers( &self, queries: &Queries, - first_layer_folded_evals: ColumnVec>, + first_layer_sparse_evals: ColumnVec, ) -> Result<(Queries, Vec), FriVerificationError> { - let first_layer_fold_alpha = self.first_layer.folding_alpha; - let first_layer_fold_alpha_pow_fold_factor = first_layer_fold_alpha.square(); - let mut layer_queries = queries.clone(); let mut layer_query_evals = vec![SecureField::zero(); layer_queries.len()]; - let mut first_layer_folded_evals = first_layer_folded_evals.into_iter(); - let mut first_layer_column_bounds = self.first_layer.column_bounds.iter().peekable(); + let mut first_layer_sparse_evals = first_layer_sparse_evals.into_iter(); + let first_layer_column_bounds = self.first_layer.column_bounds.iter(); + let first_layer_column_domains = self.first_layer.column_commitment_domains.iter(); + let mut first_layer_columns = first_layer_column_bounds + .zip_eq(first_layer_column_domains) + .peekable(); + let mut previous_folding_alpha = self.first_layer.folding_alpha; for layer in self.inner_layers.iter() { // Check for evals committed in the first layer that need to be folded into this layer. - while first_layer_column_bounds - .next_if(|b| b.fold_to_line() == layer.degree_bound) - .is_some() + while let Some((_, column_domain)) = + first_layer_columns.next_if(|(b, _)| b.fold_to_line() == layer.degree_bound) { - let folded_column_evals = first_layer_folded_evals.next().unwrap(); - - for (curr_layer_eval, folded_column_eval) in - zip_eq(&mut layer_query_evals, folded_column_evals) - { - // TODO(andrew): As Ilya pointed out using the first layer's folding - // alpha here might not be sound. Investigate. - *curr_layer_eval *= first_layer_fold_alpha_pow_fold_factor; - *curr_layer_eval += folded_column_eval; - } + // Use the previous layer's folding alpha to fold the circle's sparse evals into + // the current layer. + let folded_column_evals = first_layer_sparse_evals + .next() + .unwrap() + .fold_circle(previous_folding_alpha, *column_domain); + + accumulate_line( + &mut layer_query_evals, + &folded_column_evals, + previous_folding_alpha, + ); } + // Verify the layer and fold it using the current layer's folding alpha. (layer_queries, layer_query_evals) = layer.verify_and_fold(layer_queries, layer_query_evals)?; + previous_folding_alpha = layer.folding_alpha; } // Check all values have been consumed. - assert!(first_layer_column_bounds.is_empty()); - assert!(first_layer_folded_evals.is_empty()); + assert!(first_layer_columns.is_empty()); + assert!(first_layer_sparse_evals.is_empty()); Ok((layer_queries, layer_query_evals)) } @@ -552,6 +552,18 @@ impl FriVerifier { } } +fn accumulate_line( + layer_query_evals: &mut [SecureField], + column_query_evals: &[SecureField], + folding_alpha: SecureField, +) { + let folding_alpha_squared = folding_alpha.square(); + for (curr_layer_eval, folded_column_eval) in zip_eq(layer_query_evals, column_query_evals) { + *curr_layer_eval *= folding_alpha_squared; + *curr_layer_eval += *folded_column_eval; + } +} + /// Returns the column query positions mapped by sample domain log size. /// /// The column log sizes must be unique and in descending order. @@ -674,7 +686,8 @@ struct FriFirstLayerVerifier { } impl FriFirstLayerVerifier { - /// Verifies the layer's merkle decommitment and returns the the folded queries and query evals. + /// Verifies the first layer's merkle decommitment, and returns the evaluations needed for + /// folding the columns to their corresponding layer. /// /// # Errors /// @@ -687,18 +700,18 @@ impl FriFirstLayerVerifier { /// Panics if: /// * The queries are sampled on the wrong domain. /// * There are an invalid number of provided column evals. - fn verify_and_fold( + fn verify( &self, queries: &Queries, query_evals_by_column: ColumnVec>, - ) -> Result<(Queries, ColumnVec>), FriVerificationError> { + ) -> Result, FriVerificationError> { // Columns are provided in descending order by size. let max_column_log_size = self.column_commitment_domains[0].log_size(); assert_eq!(queries.log_domain_size, max_column_log_size); let mut fri_witness = self.proof.fri_witness.iter().copied(); let mut decommitment_positions_by_log_size = BTreeMap::new(); - let mut folded_evals_by_column = Vec::new(); + let mut sparse_evals_by_column = Vec::new(); let mut decommitmented_values = vec![]; for (&column_domain, column_query_evals) in @@ -728,9 +741,7 @@ impl FriFirstLayerVerifier { .flatten() .flat_map(|qm31| qm31.to_m31_array()), ); - - let folded_evals = sparse_evaluation.fold_circle(self.folding_alpha, column_domain); - folded_evals_by_column.push(folded_evals); + sparse_evals_by_column.push(sparse_evaluation); } // Check all proof evals have been consumed. @@ -754,9 +765,7 @@ impl FriFirstLayerVerifier { ) .map_err(|error| FriVerificationError::FirstLayerCommitmentInvalid { error })?; - let folded_queries = queries.fold(CIRCLE_TO_LINE_FOLD_STEP); - - Ok((folded_queries, folded_evals_by_column)) + Ok(sparse_evals_by_column) } }