diff --git a/crates/proof-of-sql/src/base/database/mod.rs b/crates/proof-of-sql/src/base/database/mod.rs index b7447947c..3a9bbc4cd 100644 --- a/crates/proof-of-sql/src/base/database/mod.rs +++ b/crates/proof-of-sql/src/base/database/mod.rs @@ -77,6 +77,9 @@ pub use table::{Table, TableOptions}; mod table_test; pub mod table_utility; +mod table_evaluation; +pub use table_evaluation::TableEvaluation; + /// TODO: add docs pub(crate) mod expression_evaluation; mod expression_evaluation_error; diff --git a/crates/proof-of-sql/src/base/database/table_evaluation.rs b/crates/proof-of-sql/src/base/database/table_evaluation.rs new file mode 100644 index 000000000..173772b16 --- /dev/null +++ b/crates/proof-of-sql/src/base/database/table_evaluation.rs @@ -0,0 +1,34 @@ +use crate::base::scalar::Scalar; +use alloc::vec::Vec; + +/// The result of evaluating a table +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct TableEvaluation { + /// Evaluation of each column in the table + column_evals: Vec, + /// Evaluation of an all-one column with the same length as the table + one_eval: S, +} + +impl TableEvaluation { + /// Creates a new [`TableEvaluation`]. + #[must_use] + pub fn new(column_evals: Vec, one_eval: S) -> Self { + Self { + column_evals, + one_eval, + } + } + + /// Returns the evaluation of each column in the table. + #[must_use] + pub fn column_evals(&self) -> &[S] { + &self.column_evals + } + + /// Returns the evaluation of an all-one column with the same length as the table. + #[must_use] + pub fn one_eval(&self) -> &S { + &self.one_eval + } +} diff --git a/crates/proof-of-sql/src/sql/proof/first_round_builder.rs b/crates/proof-of-sql/src/sql/proof/first_round_builder.rs index c290c08a9..232520089 100644 --- a/crates/proof-of-sql/src/sql/proof/first_round_builder.rs +++ b/crates/proof-of-sql/src/sql/proof/first_round_builder.rs @@ -5,29 +5,18 @@ pub struct FirstRoundBuilder { /// the prover after the prover sends the result, but before the prover /// send commitments to the intermediate witness columns. num_post_result_challenges: usize, +} - /// Used to determine the indices of generators we use - range_length: usize, +impl Default for FirstRoundBuilder { + fn default() -> Self { + Self::new() + } } impl FirstRoundBuilder { - pub fn new(range_length: usize) -> Self { + pub fn new() -> Self { Self { num_post_result_challenges: 0, - range_length, - } - } - - pub fn range_length(&self) -> usize { - self.range_length - } - - /// Used if a `ProofPlan` can cause output `table_length` to be larger - /// than the largest of the input ones e.g. unions and joins since it will - /// force us to update `range_length`. - pub fn update_range_length(&mut self, table_length: usize) { - if table_length > self.range_length { - self.range_length = table_length; } } diff --git a/crates/proof-of-sql/src/sql/proof/proof_plan.rs b/crates/proof-of-sql/src/sql/proof/proof_plan.rs index 0a21b6556..e86bdf943 100644 --- a/crates/proof-of-sql/src/sql/proof/proof_plan.rs +++ b/crates/proof-of-sql/src/sql/proof/proof_plan.rs @@ -1,6 +1,6 @@ use super::{CountBuilder, FinalRoundBuilder, FirstRoundBuilder, VerificationBuilder}; use crate::base::{ - database::{ColumnField, ColumnRef, OwnedTable, Table, TableRef}, + database::{ColumnField, ColumnRef, OwnedTable, Table, TableEvaluation, TableRef}, map::{IndexMap, IndexSet}, proof::ProofError, scalar::Scalar, @@ -21,7 +21,8 @@ pub trait ProofPlan: Debug + Send + Sync + ProverEvaluate { builder: &mut VerificationBuilder, accessor: &IndexMap, result: Option<&OwnedTable>, - ) -> Result, ProofError>; + one_eval_map: &IndexMap, + ) -> Result, ProofError>; /// Return all the result column fields fn get_column_result_fields(&self) -> Vec; @@ -40,7 +41,7 @@ pub trait ProverEvaluate { &self, alloc: &'a Bump, table_map: &IndexMap>, - ) -> Table<'a, S>; + ) -> (Table<'a, S>, Vec); /// Evaluate the query and modify `FirstRoundBuilder` to form the query's proof. fn first_round_evaluate(&self, builder: &mut FirstRoundBuilder); diff --git a/crates/proof-of-sql/src/sql/proof/query_proof.rs b/crates/proof-of-sql/src/sql/proof/query_proof.rs index 873d6c060..d88182fd9 100644 --- a/crates/proof-of-sql/src/sql/proof/query_proof.rs +++ b/crates/proof-of-sql/src/sql/proof/query_proof.rs @@ -27,15 +27,15 @@ use serde::{Deserialize, Serialize}; /// /// Basically we are looking for the smallest offset and the largest offset + length /// so that we have an index range of the table rows that the query is referencing. -fn get_index_range( +fn get_index_range<'a>( accessor: &dyn MetadataAccessor, - table_refs: impl IntoIterator, + table_refs: impl IntoIterator, ) -> (usize, usize) { table_refs .into_iter() .map(|table_ref| { - let length = accessor.get_length(table_ref); - let offset = accessor.get_offset(table_ref); + let length = accessor.get_length(*table_ref); + let offset = accessor.get_offset(*table_ref); (offset, offset + length) }) .reduce(|(min_start, max_end), (start, end)| (min_start.min(start), max_end.max(end))) @@ -52,6 +52,8 @@ fn get_index_range( pub struct QueryProof { /// Bit distributions pub bit_distributions: Vec, + /// One evaluation lengths + pub one_evaluation_lengths: Vec, /// Commitments pub commitments: Vec, /// Sumcheck Proof @@ -72,7 +74,7 @@ impl QueryProof { accessor: &impl DataAccessor, setup: &CP::ProverPublicSetup<'_>, ) -> (Self, ProvableQueryResult) { - let (min_row_num, max_row_num) = get_index_range(accessor, expr.get_table_references()); + let (min_row_num, max_row_num) = get_index_range(accessor, &expr.get_table_references()); let initial_range_length = max_row_num - min_row_num; let alloc = Bump::new(); @@ -91,18 +93,30 @@ impl QueryProof { .collect(); // Evaluate query result - let provable_result = expr.result_evaluate(&alloc, &table_map).into(); + let (query_result, one_evaluation_lengths) = expr.result_evaluate(&alloc, &table_map); + let provable_result = query_result.into(); // Prover First Round - let mut first_round_builder = FirstRoundBuilder::new(initial_range_length); + let mut first_round_builder = FirstRoundBuilder::new(); expr.first_round_evaluate(&mut first_round_builder); - let range_length = first_round_builder.range_length(); + let range_length = one_evaluation_lengths + .iter() + .copied() + .chain(core::iter::once(initial_range_length)) + .max() + .expect("Will always have at least one element"); // safe to unwrap because we have at least one element + let num_sumcheck_variables = cmp::max(log2_up(range_length), 1); assert!(num_sumcheck_variables > 0); // construct a transcript for the proof - let mut transcript: Keccak256Transcript = - make_transcript(expr, &provable_result, range_length, min_row_num); + let mut transcript: Keccak256Transcript = make_transcript( + expr, + &provable_result, + range_length, + min_row_num, + &one_evaluation_lengths, + ); // These are the challenges that will be consumed by the proof // Specifically, these are the challenges that the verifier sends to @@ -127,7 +141,7 @@ impl QueryProof { // commit to any intermediate MLEs let commitments = builder.commit_intermediate_mles(min_row_num, setup); - // add the commitments and bit distributions to the proof + // add the commitments, bit distributions and one evaluation lengths to the proof extend_transcript(&mut transcript, &commitments, builder.bit_distributions()); // construct the sumcheck polynomial @@ -178,6 +192,7 @@ impl QueryProof { let proof = Self { bit_distributions: builder.bit_distributions().to_vec(), + one_evaluation_lengths, commitments, sumcheck_proof, pcs_proof_evaluations, @@ -197,7 +212,8 @@ impl QueryProof { setup: &CP::VerifierPublicSetup<'_>, ) -> QueryResult { let owned_table_result = result.to_owned_table(&expr.get_column_result_fields())?; - let (min_row_num, _) = get_index_range(accessor, expr.get_table_references()); + let table_refs = expr.get_table_references(); + let (min_row_num, _) = get_index_range(accessor, &table_refs); let num_sumcheck_variables = cmp::max(log2_up(self.range_length), 1); assert!(num_sumcheck_variables > 0); @@ -226,8 +242,13 @@ impl QueryProof { } // construct a transcript for the proof - let mut transcript: Keccak256Transcript = - make_transcript(expr, result, self.range_length, min_row_num); + let mut transcript: Keccak256Transcript = make_transcript( + expr, + result, + self.range_length, + min_row_num, + &self.one_evaluation_lengths, + ); // These are the challenges that will be consumed by the proof // Specifically, these are the challenges that the verifier sends to @@ -274,14 +295,29 @@ impl QueryProof { .take(self.pcs_proof_evaluations.len()) .collect(); + // Always prepend input lengths to the one evaluation lengths + let table_length_map = table_refs + .iter() + .map(|table_ref| (table_ref, accessor.get_length(*table_ref))) + .collect::>(); + + let one_evaluation_lengths = table_length_map + .values() + .chain(self.one_evaluation_lengths.iter()) + .copied(); + // pass over the provable AST to fill in the verification builder let sumcheck_evaluations = SumcheckMleEvaluations::new( self.range_length, - owned_table_result.num_rows(), + one_evaluation_lengths, &subclaim.evaluation_point, &sumcheck_random_scalars, &self.pcs_proof_evaluations, ); + let one_eval_map: IndexMap = table_length_map + .iter() + .map(|(table_ref, length)| (**table_ref, sumcheck_evaluations.one_evaluations[length])) + .collect(); let mut builder = VerificationBuilder::new( min_row_num, sumcheck_evaluations, @@ -289,6 +325,7 @@ impl QueryProof { sumcheck_random_scalars.subpolynomial_multipliers, &evaluation_random_scalars, post_result_challenges, + self.one_evaluation_lengths.clone(), ); let pcs_proof_commitments: Vec<_> = column_references @@ -305,11 +342,12 @@ impl QueryProof { &mut builder, &evaluation_accessor, Some(&owned_table_result), + &one_eval_map, )?; // compute the evaluation of the result MLEs let result_evaluations = owned_table_result.mle_evaluations(&subclaim.evaluation_point); // check the evaluation of the result MLEs - if verifier_evaluations != result_evaluations { + if verifier_evaluations.column_evals() != result_evaluations { Err(ProofError::VerificationError { error: "result evaluation check failed", })?; @@ -371,6 +409,8 @@ impl QueryProof { /// /// * `min_row_num` - The smallest offset of the generator used in the proof, as a `usize`. /// +/// * `one_evaluation_lengths` - A slice of `usize` values that represent unexpected intermediate table lengths +/// /// # Returns /// This function returns a `merlin::Transcript`. The transcript is a record /// of all the operations and data involved in creating a proof. @@ -380,12 +420,14 @@ fn make_transcript( result: &ProvableQueryResult, range_length: usize, min_row_num: usize, + one_evaluation_lengths: &[usize], ) -> T { let mut transcript = T::new(); transcript.extend_serialize_as_le(result); transcript.extend_serialize_as_le(expr); transcript.extend_serialize_as_le(&range_length); transcript.extend_serialize_as_le(&min_row_num); + transcript.extend_serialize_as_le(one_evaluation_lengths); transcript } diff --git a/crates/proof-of-sql/src/sql/proof/query_proof_test.rs b/crates/proof-of-sql/src/sql/proof/query_proof_test.rs index 66e0e7ad6..3d71de7f3 100644 --- a/crates/proof-of-sql/src/sql/proof/query_proof_test.rs +++ b/crates/proof-of-sql/src/sql/proof/query_proof_test.rs @@ -8,7 +8,7 @@ use crate::{ owned_table_utility::{bigint, owned_table}, table_utility::*, ColumnField, ColumnRef, ColumnType, OwnedTable, OwnedTableTestAccessor, Table, - TableRef, + TableEvaluation, TableRef, }, map::{indexset, IndexMap, IndexSet}, proof::ProofError, @@ -46,9 +46,12 @@ impl ProverEvaluate for TrivialTestProofPlan { &self, alloc: &'a Bump, _table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { let col = vec![self.column_fill_value; self.length]; - table([borrowed_bigint("a1", col, alloc)]) + ( + table([borrowed_bigint("a1", col, alloc)]), + vec![self.length], + ) } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {} @@ -85,13 +88,17 @@ impl ProofPlan for TrivialTestProofPlan { builder: &mut VerificationBuilder, _accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + _one_eval_map: &IndexMap, + ) -> Result, ProofError> { assert_eq!(builder.consume_intermediate_mle(), S::ZERO); builder.produce_sumcheck_subpolynomial_evaluation( &SumcheckSubpolynomialType::ZeroSum, S::from(self.evaluation), ); - Ok(vec![S::ZERO]) + Ok(TableEvaluation::new( + vec![S::ZERO], + builder.consume_one_evaluation(), + )) } /// /// # Panics @@ -219,8 +226,8 @@ impl ProverEvaluate for SquareTestProofPlan { &self, alloc: &'a Bump, _table_map: &IndexMap>, - ) -> Table<'a, S> { - table([borrowed_bigint("a1", self.res, alloc)]) + ) -> (Table<'a, S>, Vec) { + (table([borrowed_bigint("a1", self.res, alloc)]), vec![2]) } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {} @@ -261,7 +268,8 @@ impl ProofPlan for SquareTestProofPlan { builder: &mut VerificationBuilder, accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + _one_eval_map: &IndexMap, + ) -> Result, ProofError> { let x_eval = S::from(self.anchored_commit_multiplier) * *accessor .get(&ColumnRef::new( @@ -275,7 +283,10 @@ impl ProofPlan for SquareTestProofPlan { &SumcheckSubpolynomialType::Identity, res_eval - x_eval * x_eval, ); - Ok(vec![res_eval]) + Ok(TableEvaluation::new( + vec![res_eval], + builder.consume_one_evaluation(), + )) } fn get_column_result_fields(&self) -> Vec { vec![ColumnField::new("a1".parse().unwrap(), ColumnType::BigInt)] @@ -396,8 +407,8 @@ impl ProverEvaluate for DoubleSquareTestProofPlan { &self, alloc: &'a Bump, _table_map: &IndexMap>, - ) -> Table<'a, S> { - table([borrowed_bigint("a1", self.res, alloc)]) + ) -> (Table<'a, S>, Vec) { + (table([borrowed_bigint("a1", self.res, alloc)]), vec![2]) } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {} @@ -451,7 +462,8 @@ impl ProofPlan for DoubleSquareTestProofPlan { builder: &mut VerificationBuilder, accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + _one_eval_map: &IndexMap, + ) -> Result, ProofError> { let x_eval = *accessor .get(&ColumnRef::new( "sxt.test".parse().unwrap(), @@ -473,7 +485,10 @@ impl ProofPlan for DoubleSquareTestProofPlan { &SumcheckSubpolynomialType::Identity, res_eval - z_eval * z_eval, ); - Ok(vec![res_eval]) + Ok(TableEvaluation::new( + vec![res_eval], + builder.consume_one_evaluation(), + )) } fn get_column_result_fields(&self) -> Vec { vec![ColumnField::new("a1".parse().unwrap(), ColumnType::BigInt)] @@ -603,8 +618,8 @@ impl ProverEvaluate for ChallengeTestProofPlan { &self, alloc: &'a Bump, _table_map: &IndexMap>, - ) -> Table<'a, S> { - table([borrowed_bigint("a1", [9, 25], alloc)]) + ) -> (Table<'a, S>, Vec) { + (table([borrowed_bigint("a1", [9, 25], alloc)]), vec![2]) } fn first_round_evaluate(&self, builder: &mut FirstRoundBuilder) { @@ -650,7 +665,8 @@ impl ProofPlan for ChallengeTestProofPlan { builder: &mut VerificationBuilder, accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + _one_eval_map: &IndexMap, + ) -> Result, ProofError> { let alpha = builder.consume_post_result_challenge(); let _beta = builder.consume_post_result_challenge(); let x_eval = *accessor @@ -665,7 +681,10 @@ impl ProofPlan for ChallengeTestProofPlan { &SumcheckSubpolynomialType::Identity, alpha * res_eval - alpha * x_eval * x_eval, ); - Ok(vec![res_eval]) + Ok(TableEvaluation::new( + vec![res_eval], + builder.consume_one_evaluation(), + )) } fn get_column_result_fields(&self) -> Vec { vec![ColumnField::new("a1".parse().unwrap(), ColumnType::BigInt)] diff --git a/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations.rs b/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations.rs index 9fabd9e13..14a937250 100644 --- a/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations.rs +++ b/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations.rs @@ -1,30 +1,24 @@ use super::SumcheckRandomScalars; use crate::base::{ + map::{IndexMap, IndexSet}, polynomial::{ compute_truncated_lagrange_basis_inner_product, compute_truncated_lagrange_basis_sum, }, scalar::Scalar, }; +use core::iter::IntoIterator; /// Evaluations for different MLEs at the random point chosen for sumcheck #[derive(Default)] pub struct SumcheckMleEvaluations<'a, S: Scalar> { - /// The length of the input table for a basic filter. When we support more complex queries, this may need to split. - pub input_length: usize, - /// The length of the output table for a basic filter. When we support more complex queries, this may need to split. - pub output_length: usize, /// The number of sumcheck variables. pub num_sumcheck_variables: usize, /// The evaluation (at the random point generated by sumcheck) of an MLE `{x_i}` where - /// `x_i = 1` if `i < input_length;` + /// `x_i = 1` if `i < length;` /// = 0, otherwise - pub input_one_evaluation: S, - - /// The evaluation (at the random point generated by sumcheck) of an MLE `{x_i}` where - /// `x_i = 1` if `i < output_length;` - /// = 0, otherwise - pub output_one_evaluation: S, - + pub one_evaluations: IndexMap, + /// The evaluation (at the random point generated by sumcheck) of the MLE formed from all ones with length 1. + pub singleton_one_evaluation: S, /// The evaluation (at the random point generated by sumcheck) of the MLE formed from entrywise random scalars. /// /// This is used within sumcheck to establish that a given expression @@ -46,8 +40,8 @@ impl<'a, S: Scalar> SumcheckMleEvaluations<'a, S> { /// - `sumcheck_random_scalars` - the random scalars used to batch the evaluations that are proven via IPA /// - `pcs_proof_evaluations` - the evaluations of the MLEs that are proven via IPA pub fn new( - input_length: usize, - output_length: usize, + range_length: usize, + one_evaluation_lengths: impl IntoIterator, evaluation_point: &[S], sumcheck_random_scalars: &SumcheckRandomScalars, pcs_proof_evaluations: &'a [S], @@ -56,22 +50,28 @@ impl<'a, S: Scalar> SumcheckMleEvaluations<'a, S> { evaluation_point.len(), sumcheck_random_scalars.entrywise_point.len() ); - assert_eq!(input_length, sumcheck_random_scalars.table_length); + assert_eq!(range_length, sumcheck_random_scalars.table_length); let random_evaluation = compute_truncated_lagrange_basis_inner_product( - input_length, + range_length, evaluation_point, sumcheck_random_scalars.entrywise_point, ); - let input_one_evaluation = - compute_truncated_lagrange_basis_sum(input_length, evaluation_point); - let output_one_evaluation = - compute_truncated_lagrange_basis_sum(output_length, evaluation_point); + let unique_one_evaluation_lengths: IndexSet = + one_evaluation_lengths.into_iter().collect(); + let one_evaluations = unique_one_evaluation_lengths + .iter() + .map(|&length| { + ( + length, + compute_truncated_lagrange_basis_sum(length, evaluation_point), + ) + }) + .collect(); + let singleton_one_evaluation = compute_truncated_lagrange_basis_sum(1, evaluation_point); Self { - input_length, - output_length, num_sumcheck_variables: evaluation_point.len(), - input_one_evaluation, - output_one_evaluation, + one_evaluations, + singleton_one_evaluation, random_evaluation, pcs_proof_evaluations, } diff --git a/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations_test.rs b/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations_test.rs index 5baaf54c3..4e60e5e47 100644 --- a/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations_test.rs +++ b/crates/proof-of-sql/src/sql/proof/sumcheck_mle_evaluations_test.rs @@ -15,7 +15,7 @@ fn we_can_track_the_evaluation_of_mles_used_within_sumcheck() { let pcs_proof_evaluations = [Curve25519Scalar::from(42u64)]; let evals = SumcheckMleEvaluations::new( 3, - 3, + [3, 3], &evaluation_point, &sumcheck_random_scalars, &pcs_proof_evaluations, @@ -38,6 +38,8 @@ fn we_can_track_the_evaluation_of_mles_used_within_sumcheck() { * (Curve25519Scalar::one() - evaluation_point[1]) + (evaluation_point[0]) * (Curve25519Scalar::one() - evaluation_point[1]) + (Curve25519Scalar::one() - evaluation_point[0]) * (evaluation_point[1]); - assert_eq!(evals.input_one_evaluation, expected_eval); - assert_eq!(evals.output_one_evaluation, expected_eval); + assert_eq!( + *evals.one_evaluations.values().next().unwrap(), + expected_eval + ); } diff --git a/crates/proof-of-sql/src/sql/proof/verifiable_query_result_test.rs b/crates/proof-of-sql/src/sql/proof/verifiable_query_result_test.rs index 0900ae1ff..7c01fbee2 100644 --- a/crates/proof-of-sql/src/sql/proof/verifiable_query_result_test.rs +++ b/crates/proof-of-sql/src/sql/proof/verifiable_query_result_test.rs @@ -9,7 +9,7 @@ use crate::{ owned_table_utility::{bigint, owned_table}, table_utility::*, ColumnField, ColumnRef, ColumnType, OwnedTable, OwnedTableTestAccessor, Table, - TableRef, + TableEvaluation, TableRef, }, map::{indexset, IndexMap, IndexSet}, proof::ProofError, @@ -30,11 +30,14 @@ impl ProverEvaluate for EmptyTestQueryExpr { &self, alloc: &'a Bump, _table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { let zeros = vec![0_i64; self.length]; - table_with_row_count( - (1..=self.columns).map(|i| borrowed_bigint(format!("a{i}"), zeros.clone(), alloc)), - self.length, + ( + table_with_row_count( + (1..=self.columns).map(|i| borrowed_bigint(format!("a{i}"), zeros.clone(), alloc)), + self.length, + ), + vec![self.length], ) } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {} @@ -66,13 +69,17 @@ impl ProofPlan for EmptyTestQueryExpr { builder: &mut VerificationBuilder, _accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + _one_eval_map: &IndexMap, + ) -> Result, ProofError> { let _ = std::iter::repeat_with(|| { assert_eq!(builder.consume_intermediate_mle(), S::ZERO); }) .take(self.columns) .collect::>(); - Ok(vec![S::ZERO]) + Ok(TableEvaluation::new( + vec![S::ZERO; self.columns], + builder.consume_one_evaluation(), + )) } fn get_column_result_fields(&self) -> Vec { diff --git a/crates/proof-of-sql/src/sql/proof/verification_builder.rs b/crates/proof-of-sql/src/sql/proof/verification_builder.rs index dc94c002d..7385bf353 100644 --- a/crates/proof-of-sql/src/sql/proof/verification_builder.rs +++ b/crates/proof-of-sql/src/sql/proof/verification_builder.rs @@ -11,6 +11,7 @@ pub struct VerificationBuilder<'a, S: Scalar> { sumcheck_evaluation: S, bit_distributions: &'a [BitDistribution], folded_pcs_proof_evaluation: S, + consumed_one_evaluations: usize, consumed_pcs_proof_mles: usize, consumed_intermediate_mles: usize, produced_subpolynomials: usize, @@ -22,6 +23,7 @@ pub struct VerificationBuilder<'a, S: Scalar> { /// Note: this vector is treated as a stack and the first /// challenge is the last entry in the vector. post_result_challenges: Vec, + one_evaluation_length_queue: Vec, } impl<'a, S: Scalar> VerificationBuilder<'a, S> { @@ -36,6 +38,7 @@ impl<'a, S: Scalar> VerificationBuilder<'a, S> { subpolynomial_multipliers: &'a [S], inner_product_multipliers: &'a [S], post_result_challenges: Vec, + one_evaluation_length_queue: Vec, ) -> Self { assert_eq!( inner_product_multipliers.len(), @@ -49,15 +52,28 @@ impl<'a, S: Scalar> VerificationBuilder<'a, S> { inner_product_multipliers, sumcheck_evaluation: S::zero(), folded_pcs_proof_evaluation: S::zero(), + consumed_one_evaluations: 0, consumed_pcs_proof_mles: 0, consumed_intermediate_mles: 0, produced_subpolynomials: 0, post_result_challenges, + one_evaluation_length_queue, } } - pub fn table_length(&self) -> usize { - self.mle_evaluations.input_length + /// Consume the evaluation of a one evaluation + /// + /// # Panics + /// It should never panic, as the length of the one evaluation is guaranteed to be present + pub fn consume_one_evaluation(&mut self) -> S { + let index = self.consumed_one_evaluations; + let length = self.one_evaluation_length_queue[index]; + self.consumed_one_evaluations += 1; + *self + .mle_evaluations + .one_evaluations + .get(&length) + .expect("One evaluation not found") } pub fn generator_offset(&self) -> usize { diff --git a/crates/proof-of-sql/src/sql/proof/verification_builder_test.rs b/crates/proof-of-sql/src/sql/proof/verification_builder_test.rs index 1b8c90090..ba71c2087 100644 --- a/crates/proof-of-sql/src/sql/proof/verification_builder_test.rs +++ b/crates/proof-of-sql/src/sql/proof/verification_builder_test.rs @@ -5,7 +5,6 @@ use num_traits::Zero; #[test] fn an_empty_sumcheck_polynomial_evaluates_to_zero() { let mle_evaluations = SumcheckMleEvaluations { - input_length: 1, num_sumcheck_variables: 1, ..Default::default() }; @@ -16,6 +15,7 @@ fn an_empty_sumcheck_polynomial_evaluates_to_zero() { &[][..], &[][..], Vec::new(), + Vec::new(), ); assert_eq!(builder.sumcheck_evaluation(), Curve25519Scalar::zero()); assert_eq!(builder.inner_product_multipliers(), &[]); @@ -24,7 +24,6 @@ fn an_empty_sumcheck_polynomial_evaluates_to_zero() { #[test] fn we_build_up_a_sumcheck_polynomial_evaluation_from_subpolynomial_evaluations() { let mle_evaluations = SumcheckMleEvaluations { - input_length: 1, num_sumcheck_variables: 1, ..Default::default() }; @@ -39,6 +38,7 @@ fn we_build_up_a_sumcheck_polynomial_evaluation_from_subpolynomial_evaluations() &subpolynomial_multipliers, &[][..], Vec::new(), + Vec::new(), ); builder.produce_sumcheck_subpolynomial_evaluation( &SumcheckSubpolynomialType::ZeroSum, @@ -60,7 +60,6 @@ fn we_build_up_the_folded_pcs_proof_commitment() { Curve25519Scalar::from(456u64), ]; let mle_evaluations = SumcheckMleEvaluations { - input_length: 1, num_sumcheck_variables: 1, pcs_proof_evaluations: &pcs_proof_evaluations, ..Default::default() @@ -76,6 +75,7 @@ fn we_build_up_the_folded_pcs_proof_commitment() { &[][..], &inner_product_multipliers, Vec::new(), + Vec::new(), ); let eval = builder.consume_anchored_mle(); assert_eq!(eval, Curve25519Scalar::from(123u64)); @@ -107,6 +107,7 @@ fn we_can_consume_post_result_challenges_in_proof_builder() { Curve25519Scalar::from(456), Curve25519Scalar::from(789), ], + Vec::new(), ); assert_eq!( Curve25519Scalar::from(789), diff --git a/crates/proof-of-sql/src/sql/proof_exprs/add_subtract_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/add_subtract_expr.rs index 7188c2aa2..96e89271e 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/add_subtract_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/add_subtract_expr.rs @@ -87,9 +87,10 @@ impl ProofExpr for AddSubtractExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let lhs_eval = self.lhs.verifier_evaluate(builder, accessor)?; - let rhs_eval = self.rhs.verifier_evaluate(builder, accessor)?; + let lhs_eval = self.lhs.verifier_evaluate(builder, accessor, one_eval)?; + let rhs_eval = self.rhs.verifier_evaluate(builder, accessor, one_eval)?; let lhs_scale = self.lhs.data_type().scale().unwrap_or(0); let rhs_scale = self.rhs.data_type().scale().unwrap_or(0); let res = diff --git a/crates/proof-of-sql/src/sql/proof_exprs/aggregate_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/aggregate_expr.rs index a4450a258..af721a9dd 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/aggregate_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/aggregate_expr.rs @@ -65,8 +65,9 @@ impl ProofExpr for AggregateExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - self.expr.verifier_evaluate(builder, accessor) + self.expr.verifier_evaluate(builder, accessor, one_eval) } fn get_column_references(&self, columns: &mut IndexSet) { diff --git a/crates/proof-of-sql/src/sql/proof_exprs/and_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/and_expr.rs index 2159c7bf6..6cd6dcdcb 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/and_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/and_expr.rs @@ -86,9 +86,10 @@ impl ProofExpr for AndExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let lhs = self.lhs.verifier_evaluate(builder, accessor)?; - let rhs = self.rhs.verifier_evaluate(builder, accessor)?; + let lhs = self.lhs.verifier_evaluate(builder, accessor, one_eval)?; + let rhs = self.rhs.verifier_evaluate(builder, accessor, one_eval)?; // lhs_and_rhs let lhs_and_rhs = builder.consume_intermediate_mle(); diff --git a/crates/proof-of-sql/src/sql/proof_exprs/column_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/column_expr.rs index 3e865dbe8..aae7ffd0c 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/column_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/column_expr.rs @@ -91,6 +91,7 @@ impl ProofExpr for ColumnExpr { &self, _builder: &mut VerificationBuilder, accessor: &IndexMap, + _one_eval: S, ) -> Result { Ok(*accessor .get(&self.column_ref) diff --git a/crates/proof-of-sql/src/sql/proof_exprs/equals_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/equals_expr.rs index bd9f73787..c3ee789e9 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/equals_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/equals_expr.rs @@ -79,13 +79,14 @@ impl ProofExpr for EqualsExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let lhs_eval = self.lhs.verifier_evaluate(builder, accessor)?; - let rhs_eval = self.rhs.verifier_evaluate(builder, accessor)?; + let lhs_eval = self.lhs.verifier_evaluate(builder, accessor, one_eval)?; + let rhs_eval = self.rhs.verifier_evaluate(builder, accessor, one_eval)?; let lhs_scale = self.lhs.data_type().scale().unwrap_or(0); let rhs_scale = self.rhs.data_type().scale().unwrap_or(0); let res = scale_and_add_subtract_eval(lhs_eval, rhs_eval, lhs_scale, rhs_scale, true); - Ok(verifier_evaluate_equals_zero(builder, res)) + Ok(verifier_evaluate_equals_zero(builder, res, one_eval)) } fn get_column_references(&self, columns: &mut IndexSet) { @@ -150,11 +151,12 @@ pub fn prover_evaluate_equals_zero<'a, S: Scalar>( pub fn verifier_evaluate_equals_zero( builder: &mut VerificationBuilder, lhs_eval: S, + one_eval: S, ) -> S { // consume mle evaluations let lhs_pseudo_inv_eval = builder.consume_intermediate_mle(); let selection_not_eval = builder.consume_intermediate_mle(); - let selection_eval = builder.mle_evaluations.input_one_evaluation - selection_not_eval; + let selection_eval = one_eval - selection_not_eval; // subpolynomial: selection * lhs builder.produce_sumcheck_subpolynomial_evaluation( diff --git a/crates/proof-of-sql/src/sql/proof_exprs/inequality_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/inequality_expr.rs index fb99e9f92..4e463d0d4 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/inequality_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/inequality_expr.rs @@ -122,10 +122,10 @@ impl ProofExpr for InequalityExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let one_eval = builder.mle_evaluations.input_one_evaluation; - let lhs_eval = self.lhs.verifier_evaluate(builder, accessor)?; - let rhs_eval = self.rhs.verifier_evaluate(builder, accessor)?; + let lhs_eval = self.lhs.verifier_evaluate(builder, accessor, one_eval)?; + let rhs_eval = self.rhs.verifier_evaluate(builder, accessor, one_eval)?; let lhs_scale = self.lhs.data_type().scale().unwrap_or(0); let rhs_scale = self.rhs.data_type().scale().unwrap_or(0); let diff_eval = if self.is_lte { @@ -135,7 +135,7 @@ impl ProofExpr for InequalityExpr { }; // diff == 0 - let equals_zero = verifier_evaluate_equals_zero(builder, diff_eval); + let equals_zero = verifier_evaluate_equals_zero(builder, diff_eval, one_eval); // sign(diff) == -1 let sign = verifier_evaluate_sign(builder, diff_eval, one_eval)?; diff --git a/crates/proof-of-sql/src/sql/proof_exprs/literal_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/literal_expr.rs index ea39fdfd5..a942fc67d 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/literal_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/literal_expr.rs @@ -59,17 +59,17 @@ impl ProofExpr for LiteralExpr { alloc: &'a Bump, table: &Table<'a, S>, ) -> Column<'a, S> { - Column::from_literal_with_length(&self.value, table.num_rows(), alloc) + let table_length = table.num_rows(); + Column::from_literal_with_length(&self.value, table_length, alloc) } fn verifier_evaluate( &self, - builder: &mut VerificationBuilder, + _builder: &mut VerificationBuilder, _accessor: &IndexMap, + one_eval: S, ) -> Result { - let mut commitment = builder.mle_evaluations.input_one_evaluation; - commitment *= self.value.to_scalar(); - Ok(commitment) + Ok(one_eval * self.value.to_scalar()) } fn get_column_references(&self, _columns: &mut IndexSet) {} diff --git a/crates/proof-of-sql/src/sql/proof_exprs/multiply_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/multiply_expr.rs index 017f04667..f4af9209e 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/multiply_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/multiply_expr.rs @@ -88,9 +88,10 @@ impl ProofExpr for MultiplyExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let lhs = self.lhs.verifier_evaluate(builder, accessor)?; - let rhs = self.rhs.verifier_evaluate(builder, accessor)?; + let lhs = self.lhs.verifier_evaluate(builder, accessor, one_eval)?; + let rhs = self.rhs.verifier_evaluate(builder, accessor, one_eval)?; // lhs_times_rhs let lhs_times_rhs = builder.consume_intermediate_mle(); diff --git a/crates/proof-of-sql/src/sql/proof_exprs/not_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/not_expr.rs index b540def23..c52b4e987 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/not_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/not_expr.rs @@ -61,9 +61,10 @@ impl ProofExpr for NotExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let eval = self.expr.verifier_evaluate(builder, accessor)?; - Ok(builder.mle_evaluations.input_one_evaluation - eval) + let eval = self.expr.verifier_evaluate(builder, accessor, one_eval)?; + Ok(one_eval - eval) } fn get_column_references(&self, columns: &mut IndexSet) { diff --git a/crates/proof-of-sql/src/sql/proof_exprs/or_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/or_expr.rs index a8336a372..5e25866ce 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/or_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/or_expr.rs @@ -69,9 +69,10 @@ impl ProofExpr for OrExpr { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result { - let lhs = self.lhs.verifier_evaluate(builder, accessor)?; - let rhs = self.rhs.verifier_evaluate(builder, accessor)?; + let lhs = self.lhs.verifier_evaluate(builder, accessor, one_eval)?; + let rhs = self.rhs.verifier_evaluate(builder, accessor, one_eval)?; Ok(verifier_evaluate_or(builder, &lhs, &rhs)) } diff --git a/crates/proof-of-sql/src/sql/proof_exprs/proof_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/proof_expr.rs index f2fec6742..d3bfdef5e 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/proof_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/proof_expr.rs @@ -44,6 +44,7 @@ pub trait ProofExpr: Debug + Send + Sync { &self, builder: &mut VerificationBuilder, accessor: &IndexMap, + one_eval: S, ) -> Result; /// Insert in the [`IndexSet`] `columns` all the column diff --git a/crates/proof-of-sql/src/sql/proof_exprs/sign_expr.rs b/crates/proof-of-sql/src/sql/proof_exprs/sign_expr.rs index 87ddecc53..0ffccaa84 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/sign_expr.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/sign_expr.rs @@ -152,7 +152,7 @@ pub fn verifier_evaluate_sign( if dist.num_varying_bits() == 1 { verify_constant_abs_decomposition(&dist, eval, one_eval, bit_evals[0])?; } else { - verify_bit_decomposition(builder, eval, &bit_evals, &dist); + verify_bit_decomposition(builder, eval, one_eval, &bit_evals, &dist); } Ok(*bit_evals.last().unwrap()) @@ -241,12 +241,13 @@ fn prove_bit_decomposition<'a, S: Scalar>( fn verify_bit_decomposition( builder: &mut VerificationBuilder, expr_eval: S, + one_eval: S, bit_evals: &[S], dist: &BitDistribution, ) { let mut eval = expr_eval; let sign_eval = bit_evals.last().unwrap(); - let sign_eval = builder.mle_evaluations.input_one_evaluation - S::TWO * *sign_eval; + let sign_eval = one_eval - S::TWO * *sign_eval; let mut vary_index = 0; eval -= sign_eval * S::from(dist.constant_part()); dist.for_each_abs_varying_bit(|int_index: usize, bit_index: usize| { diff --git a/crates/proof-of-sql/src/sql/proof_exprs/sign_expr_test.rs b/crates/proof-of-sql/src/sql/proof_exprs/sign_expr_test.rs index ee00ba1e8..67ae7c25c 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/sign_expr_test.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/sign_expr_test.rs @@ -58,17 +58,25 @@ fn we_can_verify_a_constant_decomposition() { let evaluation_point = [Curve25519Scalar::from(324), Curve25519Scalar::from(97)]; let sumcheck_evaluations = SumcheckMleEvaluations::new( data.len(), - data.len(), + [data.len()], &evaluation_point, &sumcheck_random_scalars, &[], ); - let one_eval = sumcheck_evaluations.input_one_evaluation; + let one_evals = sumcheck_evaluations.one_evaluations.clone(); + let one_eval = one_evals.values().next().unwrap(); - let mut builder = - VerificationBuilder::new(0, sumcheck_evaluations, &dists, &[], &[], Vec::new()); + let mut builder = VerificationBuilder::new( + 0, + sumcheck_evaluations, + &dists, + &[], + &[], + Vec::new(), + Vec::new(), + ); let data_eval = (&data).evaluate_at_point(&evaluation_point); - let eval = verifier_evaluate_sign(&mut builder, data_eval, one_eval).unwrap(); + let eval = verifier_evaluate_sign(&mut builder, data_eval, *one_eval).unwrap(); assert_eq!(eval, Curve25519Scalar::zero()); } @@ -82,17 +90,25 @@ fn verification_of_constant_data_fails_if_the_commitment_doesnt_match_the_bit_di let evaluation_point = [Curve25519Scalar::from(324), Curve25519Scalar::from(97)]; let sumcheck_evaluations = SumcheckMleEvaluations::new( data.len(), - data.len(), + [data.len()], &evaluation_point, &sumcheck_random_scalars, &[], ); - let one_eval = sumcheck_evaluations.input_one_evaluation; + let one_evals = sumcheck_evaluations.one_evaluations.clone(); + let one_eval = one_evals.values().next().unwrap(); - let mut builder = - VerificationBuilder::new(0, sumcheck_evaluations, &dists, &[], &[], Vec::new()); + let mut builder = VerificationBuilder::new( + 0, + sumcheck_evaluations, + &dists, + &[], + &[], + Vec::new(), + Vec::new(), + ); let data_eval = Curve25519Scalar::from(2) * (&data).evaluate_at_point(&evaluation_point); - assert!(verifier_evaluate_sign(&mut builder, data_eval, one_eval).is_err()); + assert!(verifier_evaluate_sign(&mut builder, data_eval, *one_eval).is_err()); } #[test] diff --git a/crates/proof-of-sql/src/sql/proof_plans/dyn_proof_plan.rs b/crates/proof-of-sql/src/sql/proof_plans/dyn_proof_plan.rs index e402708aa..e117ed8d3 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/dyn_proof_plan.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/dyn_proof_plan.rs @@ -1,7 +1,7 @@ use super::{EmptyExec, FilterExec, GroupByExec, ProjectionExec, TableExec}; use crate::{ base::{ - database::{ColumnField, ColumnRef, OwnedTable, Table, TableRef}, + database::{ColumnField, ColumnRef, OwnedTable, Table, TableEvaluation, TableRef}, map::{IndexMap, IndexSet}, proof::ProofError, scalar::Scalar, diff --git a/crates/proof-of-sql/src/sql/proof_plans/empty_exec.rs b/crates/proof-of-sql/src/sql/proof_plans/empty_exec.rs index 9f5dd8ed9..157b8f409 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/empty_exec.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/empty_exec.rs @@ -1,6 +1,8 @@ use crate::{ base::{ - database::{ColumnField, ColumnRef, OwnedTable, Table, TableOptions, TableRef}, + database::{ + ColumnField, ColumnRef, OwnedTable, Table, TableEvaluation, TableOptions, TableRef, + }, map::{IndexMap, IndexSet}, proof::ProofError, scalar::Scalar, @@ -10,7 +12,7 @@ use crate::{ VerificationBuilder, }, }; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use bumpalo::Bump; use serde::{Deserialize, Serialize}; @@ -41,11 +43,15 @@ impl ProofPlan for EmptyExec { #[allow(unused_variables)] fn verifier_evaluate( &self, - _builder: &mut VerificationBuilder, + builder: &mut VerificationBuilder, _accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { - Ok(Vec::new()) + _one_eval_map: &IndexMap, + ) -> Result, ProofError> { + Ok(TableEvaluation::new( + Vec::::new(), + builder.mle_evaluations.singleton_one_evaluation, + )) } fn get_column_result_fields(&self) -> Vec { @@ -67,10 +73,13 @@ impl ProverEvaluate for EmptyExec { &self, _alloc: &'a Bump, _table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { // Create an empty table with one row - Table::<'a, S>::try_new_with_options(IndexMap::default(), TableOptions::new(Some(1))) - .unwrap() + ( + Table::<'a, S>::try_new_with_options(IndexMap::default(), TableOptions::new(Some(1))) + .unwrap(), + vec![], + ) } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {} diff --git a/crates/proof-of-sql/src/sql/proof_plans/filter_exec.rs b/crates/proof-of-sql/src/sql/proof_plans/filter_exec.rs index 4040ff225..720a2a2af 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/filter_exec.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/filter_exec.rs @@ -3,7 +3,7 @@ use crate::{ base::{ database::{ filter_util::filter_columns, Column, ColumnField, ColumnRef, OwnedTable, Table, - TableOptions, TableRef, + TableEvaluation, TableOptions, TableRef, }, map::{IndexMap, IndexSet}, proof::ProofError, @@ -78,14 +78,24 @@ where builder: &mut VerificationBuilder, accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + one_eval_map: &IndexMap, + ) -> Result, ProofError> { + let input_one_eval = *one_eval_map + .get(&self.table.table_ref) + .expect("One eval not found"); // 1. selection - let selection_eval = self.where_clause.verifier_evaluate(builder, accessor)?; + let selection_eval = + self.where_clause + .verifier_evaluate(builder, accessor, input_one_eval)?; // 2. columns let columns_evals = Vec::from_iter( self.aliased_results .iter() - .map(|aliased_expr| aliased_expr.expr.verifier_evaluate(builder, accessor)) + .map(|aliased_expr| { + aliased_expr + .expr + .verifier_evaluate(builder, accessor, input_one_eval) + }) .collect::, _>>()?, ); // 3. filtered_columns @@ -97,15 +107,22 @@ where let alpha = builder.consume_post_result_challenge(); let beta = builder.consume_post_result_challenge(); + let output_one_eval = builder.consume_one_evaluation(); + verify_filter( builder, alpha, beta, + input_one_eval, + output_one_eval, &columns_evals, selection_eval, &filtered_columns_evals, )?; - Ok(filtered_columns_evals) + Ok(TableEvaluation::new( + filtered_columns_evals, + output_one_eval, + )) } fn get_column_result_fields(&self) -> Vec { @@ -141,7 +158,7 @@ impl ProverEvaluate for FilterExec { &self, alloc: &'a Bump, table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { let table = table_map .get(&self.table.table_ref) .expect("Table not found"); @@ -161,14 +178,15 @@ impl ProverEvaluate for FilterExec { // Compute filtered_columns and indexes let (filtered_columns, _) = filter_columns(alloc, &columns, selection); - Table::<'a, S>::try_from_iter_with_options( + let res = Table::<'a, S>::try_from_iter_with_options( self.aliased_results .iter() .map(|expr| expr.alias) .zip(filtered_columns), TableOptions::new(Some(output_length)), ) - .expect("Failed to create table from iterator") + .expect("Failed to create table from iterator"); + (res, vec![output_length]) } fn first_round_evaluate(&self, builder: &mut FirstRoundBuilder) { @@ -232,18 +250,17 @@ impl ProverEvaluate for FilterExec { } } -#[allow(clippy::unnecessary_wraps)] +#[allow(clippy::unnecessary_wraps, clippy::too_many_arguments)] fn verify_filter( builder: &mut VerificationBuilder, alpha: S, beta: S, + one_eval: S, + chi_eval: S, c_evals: &[S], s_eval: S, d_evals: &[S], ) -> Result<(), ProofError> { - let one_eval = builder.mle_evaluations.input_one_evaluation; - let chi_eval = builder.mle_evaluations.output_one_evaluation; - let c_fold_eval = alpha * one_eval + fold_vals(beta, c_evals); let d_bar_fold_eval = alpha * one_eval + fold_vals(beta, d_evals); let c_star_eval = builder.consume_intermediate_mle(); diff --git a/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test.rs b/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test.rs index 2f6fa40ba..94bc43401 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test.rs @@ -208,7 +208,7 @@ fn we_can_get_an_empty_result_from_a_basic_filter_on_an_empty_table_using_result ), ]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected: OwnedTable = owned_table([ @@ -253,7 +253,7 @@ fn we_can_get_an_empty_result_from_a_basic_filter_using_result_evaluate() { ), ]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected: OwnedTable = owned_table([ @@ -286,7 +286,7 @@ fn we_can_get_no_columns_from_a_basic_filter_with_no_selected_columns_using_resu let expr = filter(cols_expr_plan(t, &[], &accessor), tab(t), where_clause); let fields = &[]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected = OwnedTable::try_new(IndexMap::default()).unwrap(); @@ -325,7 +325,7 @@ fn we_can_get_the_correct_result_from_a_basic_filter_using_result_evaluate() { ), ]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected: OwnedTable = owned_table([ diff --git a/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test_dishonest_prover.rs b/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test_dishonest_prover.rs index 69d37a017..149a2064d 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test_dishonest_prover.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/filter_exec_test_dishonest_prover.rs @@ -38,7 +38,7 @@ impl ProverEvaluate for DishonestFilterExec { &self, alloc: &'a Bump, table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { let table = table_map .get(&self.table.table_ref) .expect("Table not found"); @@ -57,14 +57,15 @@ impl ProverEvaluate for DishonestFilterExec { // Compute filtered_columns let (filtered_columns, _) = filter_columns(alloc, &columns, selection); let filtered_columns = tamper_column(alloc, filtered_columns); - Table::<'a, S>::try_from_iter_with_options( + let res = Table::<'a, S>::try_from_iter_with_options( self.aliased_results .iter() .map(|expr| expr.alias) .zip(filtered_columns), TableOptions::new(Some(output_length)), ) - .expect("Failed to create table from iterator") + .expect("Failed to create table from iterator"); + (res, vec![output_length]) } fn first_round_evaluate(&self, builder: &mut FirstRoundBuilder) { diff --git a/crates/proof-of-sql/src/sql/proof_plans/group_by_exec.rs b/crates/proof-of-sql/src/sql/proof_plans/group_by_exec.rs index b1e95b83e..800c1ad3e 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/group_by_exec.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/group_by_exec.rs @@ -4,7 +4,8 @@ use crate::{ database::{ group_by_util::{aggregate_columns, AggregatedColumns}, order_by_util::compare_indexes_by_owned_columns, - Column, ColumnField, ColumnRef, ColumnType, OwnedTable, Table, TableRef, + Column, ColumnField, ColumnRef, ColumnType, OwnedTable, Table, TableEvaluation, + TableRef, }, map::{IndexMap, IndexSet}, proof::ProofError, @@ -91,22 +92,31 @@ impl ProofPlan for GroupByExec { builder: &mut VerificationBuilder, accessor: &IndexMap, result: Option<&OwnedTable>, - ) -> Result, ProofError> { + one_eval_map: &IndexMap, + ) -> Result, ProofError> { + let input_one_eval = *one_eval_map + .get(&self.table.table_ref) + .expect("One eval not found"); // 1. selection - let where_eval = self.where_clause.verifier_evaluate(builder, accessor)?; + let where_eval = self + .where_clause + .verifier_evaluate(builder, accessor, input_one_eval)?; // 2. columns let group_by_evals = self .group_by_exprs .iter() - .map(|expr| expr.verifier_evaluate(builder, accessor)) + .map(|expr| expr.verifier_evaluate(builder, accessor, input_one_eval)) .collect::, _>>()?; let aggregate_evals = self .sum_expr .iter() - .map(|aliased_expr| aliased_expr.expr.verifier_evaluate(builder, accessor)) + .map(|aliased_expr| { + aliased_expr + .expr + .verifier_evaluate(builder, accessor, input_one_eval) + }) .collect::, _>>()?; // 3. filtered_columns - let group_by_result_columns_evals: Vec<_> = repeat_with(|| builder.consume_intermediate_mle()) .take(self.group_by_exprs.len()) @@ -123,6 +133,7 @@ impl ProofPlan for GroupByExec { builder, alpha, beta, + input_one_eval, (group_by_evals, aggregate_evals, where_eval), ( group_by_result_columns_evals.clone(), @@ -151,11 +162,13 @@ impl ProofPlan for GroupByExec { None => todo!("GroupByExec currently only supported at top level of query plan."), } - Ok(group_by_result_columns_evals + let column_evals = group_by_result_columns_evals .into_iter() .chain(sum_result_columns_evals) .chain(iter::once(count_column_eval)) - .collect::>()) + .collect::>(); + let output_one_eval = builder.consume_one_evaluation(); + Ok(TableEvaluation::new(column_evals, output_one_eval)) } #[allow(clippy::redundant_closure_for_method_calls)] @@ -199,7 +212,7 @@ impl ProverEvaluate for GroupByExec { &self, alloc: &'a Bump, table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { let table = table_map .get(&self.table.table_ref) .expect("Table not found"); @@ -230,7 +243,7 @@ impl ProverEvaluate for GroupByExec { } = aggregate_columns(alloc, &group_by_columns, &sum_columns, &[], &[], selection) .expect("columns should be aggregatable"); let sum_result_columns_iter = sum_result_columns.iter().map(|col| Column::Scalar(col)); - Table::<'a, S>::try_from_iter( + let res = Table::<'a, S>::try_from_iter( self.get_column_result_fields() .into_iter() .map(|field| field.name()) @@ -241,7 +254,8 @@ impl ProverEvaluate for GroupByExec { .chain(iter::once(Column::BigInt(count_column))), ), ) - .expect("Failed to create table from column references") + .expect("Failed to create table from column references"); + (res, vec![count_column.len()]) } fn first_round_evaluate(&self, builder: &mut FirstRoundBuilder) { @@ -326,11 +340,10 @@ fn verify_group_by( builder: &mut VerificationBuilder, alpha: S, beta: S, + one_eval: S, (g_in_evals, sum_in_evals, sel_in_eval): (Vec, Vec, S), (g_out_evals, sum_out_evals, count_out_eval): (Vec, Vec, S), ) -> Result<(), ProofError> { - let one_eval = builder.mle_evaluations.input_one_evaluation; - // g_in_fold = alpha + sum beta^j * g_in[j] let g_in_fold_eval = alpha * one_eval + fold_vals(beta, &g_in_evals); // g_out_bar_fold = alpha + sum beta^j * g_out_bar[j] diff --git a/crates/proof-of-sql/src/sql/proof_plans/projection_exec.rs b/crates/proof-of-sql/src/sql/proof_plans/projection_exec.rs index 11be0d941..64cdf9608 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/projection_exec.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/projection_exec.rs @@ -1,6 +1,8 @@ use crate::{ base::{ - database::{ColumnField, ColumnRef, OwnedTable, Table, TableOptions, TableRef}, + database::{ + ColumnField, ColumnRef, OwnedTable, Table, TableEvaluation, TableOptions, TableRef, + }, map::{IndexMap, IndexSet}, proof::ProofError, scalar::Scalar, @@ -13,7 +15,7 @@ use crate::{ proof_exprs::{AliasedDynProofExpr, ProofExpr, TableExpr}, }, }; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use bumpalo::Bump; use core::iter::repeat_with; use serde::{Deserialize, Serialize}; @@ -53,14 +55,24 @@ impl ProofPlan for ProjectionExec { builder: &mut VerificationBuilder, accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { + one_eval_map: &IndexMap, + ) -> Result, ProofError> { + // For projections input and output have the same length and hence the same one eval + let one_eval = *one_eval_map + .get(&self.table.table_ref) + .expect("One eval not found"); self.aliased_results .iter() - .map(|aliased_expr| aliased_expr.expr.verifier_evaluate(builder, accessor)) + .map(|aliased_expr| { + aliased_expr + .expr + .verifier_evaluate(builder, accessor, one_eval) + }) .collect::, _>>()?; - Ok(repeat_with(|| builder.consume_intermediate_mle()) + let column_evals = repeat_with(|| builder.consume_intermediate_mle()) .take(self.aliased_results.len()) - .collect::>()) + .collect::>(); + Ok(TableEvaluation::new(column_evals, one_eval)) } fn get_column_result_fields(&self) -> Vec { @@ -89,20 +101,23 @@ impl ProverEvaluate for ProjectionExec { &self, alloc: &'a Bump, table_map: &IndexMap>, - ) -> Table<'a, S> { + ) -> (Table<'a, S>, Vec) { let table = table_map .get(&self.table.table_ref) .expect("Table not found"); - Table::<'a, S>::try_from_iter_with_options( - self.aliased_results.iter().map(|aliased_expr| { - ( - aliased_expr.alias, - aliased_expr.expr.result_evaluate(alloc, table), - ) - }), - TableOptions::new(Some(table.num_rows())), + ( + Table::<'a, S>::try_from_iter_with_options( + self.aliased_results.iter().map(|aliased_expr| { + ( + aliased_expr.alias, + aliased_expr.expr.result_evaluate(alloc, table), + ) + }), + TableOptions::new(Some(table.num_rows())), + ) + .expect("Failed to create table from iterator"), + vec![], ) - .expect("Failed to create table from iterator") } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {} diff --git a/crates/proof-of-sql/src/sql/proof_plans/projection_exec_test.rs b/crates/proof-of-sql/src/sql/proof_plans/projection_exec_test.rs index 5a6ec565a..b1b5a503b 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/projection_exec_test.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/projection_exec_test.rs @@ -181,7 +181,7 @@ fn we_can_get_an_empty_result_from_a_basic_projection_on_an_empty_table_using_re ), ]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected: OwnedTable = owned_table([ @@ -213,7 +213,7 @@ fn we_can_get_no_columns_from_a_basic_projection_with_no_selected_columns_using_ let expr: DynProofPlan = projection(cols_expr_plan(t, &[], &accessor), tab(t)); let fields = &[]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected = OwnedTable::try_new(IndexMap::default()).unwrap(); @@ -258,7 +258,7 @@ fn we_can_get_the_correct_result_from_a_basic_projection_using_result_evaluate() ), ]; let res: OwnedTable = - ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map)) + ProvableQueryResult::from(expr.result_evaluate(&alloc, &table_map).0) .to_owned_table(fields) .unwrap(); let expected: OwnedTable = owned_table([ diff --git a/crates/proof-of-sql/src/sql/proof_plans/table_exec.rs b/crates/proof-of-sql/src/sql/proof_plans/table_exec.rs index 36d3a4d2c..db1fdda7b 100644 --- a/crates/proof-of-sql/src/sql/proof_plans/table_exec.rs +++ b/crates/proof-of-sql/src/sql/proof_plans/table_exec.rs @@ -1,6 +1,6 @@ use crate::{ base::{ - database::{ColumnField, ColumnRef, OwnedTable, Table, TableRef}, + database::{ColumnField, ColumnRef, OwnedTable, Table, TableEvaluation, TableRef}, map::{indexset, IndexMap, IndexSet}, proof::ProofError, scalar::Scalar, @@ -10,7 +10,7 @@ use crate::{ VerificationBuilder, }, }; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use bumpalo::Bump; use serde::{Deserialize, Serialize}; @@ -44,15 +44,20 @@ impl ProofPlan for TableExec { builder: &mut VerificationBuilder, accessor: &IndexMap, _result: Option<&OwnedTable>, - ) -> Result, ProofError> { - Ok(self + one_eval_map: &IndexMap, + ) -> Result, ProofError> { + let column_evals = self .schema .iter() .map(|field| { let column_ref = ColumnRef::new(self.table_ref, field.name(), field.data_type()); *accessor.get(&column_ref).expect("Column does not exist") }) - .collect::>()) + .collect::>(); + let one_eval = *one_eval_map + .get(&self.table_ref) + .expect("One eval not found"); + Ok(TableEvaluation::new(column_evals, one_eval)) } fn get_column_result_fields(&self) -> Vec { @@ -77,11 +82,14 @@ impl ProverEvaluate for TableExec { &self, _alloc: &'a Bump, table_map: &IndexMap>, - ) -> Table<'a, S> { - table_map - .get(&self.table_ref) - .expect("Table not found") - .clone() + ) -> (Table<'a, S>, Vec) { + ( + table_map + .get(&self.table_ref) + .expect("Table not found") + .clone(), + vec![], + ) } fn first_round_evaluate(&self, _builder: &mut FirstRoundBuilder) {}