From 5dc08ff9a70e684362993ce8c9029e86e00b22ce Mon Sep 17 00:00:00 2001
From: Alon Haramati <91828241+alonh5@users.noreply.github.com>
Date: Tue, 20 Feb 2024 17:43:27 +0200
Subject: [PATCH] Contain queries in FRI. (#355)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This change is [](https://reviewable.io/reviews/starkware-industries/prover-research/355)
---
src/core/fri.rs | 112 ++++++++++++++++++++++++++++++-------------
src/core/queries.rs | 7 +--
src/fibonacci/mod.rs | 33 +++----------
3 files changed, 90 insertions(+), 62 deletions(-)
diff --git a/src/core/fri.rs b/src/core/fri.rs
index 60ef4131c..bc7d28641 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,31 @@ 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 unique and in descending order.
+// TODO(AlonH): Consider returning a mapping instead of a vector.
fn get_opening_positions(
queries: &Queries,
domain_log_sizes: &[u32],
@@ -897,7 +938,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 +953,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 +1037,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 +1054,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 +1062,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 +1136,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 +1159,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 +1204,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 +1229,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 +1260,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
}