diff --git a/Cargo.toml b/Cargo.toml index bb5b05e71..46b96245b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,8 @@ version = "0.0.0" # DO NOT CHANGE THIS LINE! This will be automatically updated license-file = "LICENSE" [workspace.dependencies] -alloy-primitives = { version = "0.8.0" } -alloy-sol-types = { version = "0.8.0" } +alloy-primitives = { version = "0.8.1" } +alloy-sol-types = { version = "0.8.1" } ark-bls12-381 = { version = "0.4.0" } ark-curve25519 = { version = "0.4.0" } ark-ec = { version = "0.4.0", features = [ "parallel" ] } diff --git a/crates/proof-of-sql/src/base/database/column.rs b/crates/proof-of-sql/src/base/database/column.rs index c79fcc784..cefd264fb 100644 --- a/crates/proof-of-sql/src/base/database/column.rs +++ b/crates/proof-of-sql/src/base/database/column.rs @@ -161,6 +161,23 @@ impl<'a, S: Scalar> Column<'a, S> { } } + /// Returns the column as a slice of scalars + pub(crate) fn as_scalar(&self, alloc: &'a Bump) -> &'a [S] { + match self { + Self::Boolean(col) => alloc.alloc_slice_fill_with(col.len(), |i| S::from(col[i])), + Self::SmallInt(col) => alloc.alloc_slice_fill_with(col.len(), |i| S::from(col[i])), + Self::Int(col) => alloc.alloc_slice_fill_with(col.len(), |i| S::from(col[i])), + Self::BigInt(col) => alloc.alloc_slice_fill_with(col.len(), |i| S::from(col[i])), + Self::Int128(col) => alloc.alloc_slice_fill_with(col.len(), |i| S::from(col[i])), + Self::Scalar(col) => col, + Self::Decimal75(_, _, col) => col, + Self::VarChar((_, scals)) => scals, + Self::TimestampTZ(_, _, col) => { + alloc.alloc_slice_fill_with(col.len(), |i| S::from(col[i])) + } + } + } + /// Returns element at index as scalar /// /// Note that if index is out of bounds, this function will return None 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 1becb16c5..53c29f35c 100644 --- a/crates/proof-of-sql/src/sql/proof/proof_plan.rs +++ b/crates/proof-of-sql/src/sql/proof/proof_plan.rs @@ -38,6 +38,7 @@ pub trait ProofPlan: Debug + Send + Sync + ProverEvaluate, accessor: &dyn CommitmentAccessor, result: Option<&OwnedTable>, + is_top_level: bool, ) -> Result, ProofError>; /// Return all the result column fields @@ -67,6 +68,7 @@ pub trait ProverEvaluate { builder: &mut ProofBuilder<'a, S>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + is_top_level: bool, ) -> Vec>; } 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 04be6d2e5..556ed53a7 100644 --- a/crates/proof-of-sql/src/sql/proof/query_proof.rs +++ b/crates/proof-of-sql/src/sql/proof/query_proof.rs @@ -76,7 +76,7 @@ impl QueryProof { let mut builder = ProofBuilder::new(table_length, num_sumcheck_variables, post_result_challenges); - expr.prover_evaluate(&mut builder, &alloc, accessor); + expr.prover_evaluate(&mut builder, &alloc, accessor, true); let num_sumcheck_variables = builder.num_sumcheck_variables(); let table_length = builder.table_length(); @@ -250,7 +250,7 @@ impl QueryProof { post_result_challenges, ); let owned_table_result = result.to_owned_table(&column_result_fields[..])?; - expr.verifier_evaluate(&mut builder, accessor, Some(&owned_table_result))?; + expr.verifier_evaluate(&mut builder, accessor, Some(&owned_table_result), true)?; // perform the evaluation check of the sumcheck polynomial if builder.sumcheck_evaluation() != subclaim.expected_evaluation { 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 75fb2b4cd..297d1007d 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 @@ -58,6 +58,7 @@ impl ProverEvaluate for TrivialTestProofPlan { builder: &mut ProofBuilder<'a, S>, alloc: &'a Bump, _accessor: &'a dyn DataAccessor, + _is_top_level: bool, ) -> Vec> { let col = alloc.alloc_slice_fill_copy(builder.table_length(), self.column_fill_value); builder.produce_sumcheck_subpolynomial( @@ -90,8 +91,12 @@ impl ProofPlan for TrivialTestProofPlan { builder: &mut VerificationBuilder, _accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable>, + _is_top_level: bool, ) -> Result, ProofError> { - assert_eq!(builder.consume_result_mle(), C::Scalar::ZERO); + assert_eq!( + builder.consume_result_or_intermediate_mle(true), + C::Scalar::ZERO + ); builder.produce_sumcheck_subpolynomial_evaluation(&C::Scalar::from(self.evaluation)); Ok(vec![C::Scalar::ZERO]) } @@ -223,6 +228,7 @@ impl ProverEvaluate for SquareTestProofPlan { builder: &mut ProofBuilder<'a, S>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + _is_top_level: bool, ) -> Vec> { let x = accessor.get_column(ColumnRef::new( "sxt.test".parse().unwrap(), @@ -264,8 +270,9 @@ impl ProofPlan for SquareTestProofPlan { builder: &mut VerificationBuilder, accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable>, + _is_top_level: bool, ) -> Result, ProofError> { - let res_eval = builder.consume_result_mle(); + let res_eval = builder.consume_result_or_intermediate_mle(true); let x_commit = C::Scalar::from(self.anchored_commit_multiplier) * accessor.get_commitment(ColumnRef::new( "sxt.test".parse().unwrap(), @@ -401,6 +408,7 @@ impl ProverEvaluate for DoubleSquareTestProofPlan { builder: &mut ProofBuilder<'a, S>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + _is_top_level: bool, ) -> Vec> { let x = accessor.get_column(ColumnRef::new( "sxt.test".parse().unwrap(), @@ -456,13 +464,14 @@ impl ProofPlan for DoubleSquareTestProofPlan { builder: &mut VerificationBuilder, accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable>, + _is_top_level: bool, ) -> Result, ProofError> { let x_commit = accessor.get_commitment(ColumnRef::new( "sxt.test".parse().unwrap(), "x".parse().unwrap(), ColumnType::BigInt, )); - let res_eval = builder.consume_result_mle(); + let res_eval = builder.consume_result_or_intermediate_mle(true); let x_eval = builder.consume_anchored_mle(x_commit); let z_eval = builder.consume_intermediate_mle(); @@ -608,6 +617,7 @@ impl ProverEvaluate for ChallengeTestProofPlan { builder: &mut ProofBuilder<'a, S>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + _is_top_level: bool, ) -> Vec> { let x = accessor.get_column(ColumnRef::new( "sxt.test".parse().unwrap(), @@ -652,10 +662,11 @@ impl ProofPlan for ChallengeTestProofPlan { builder: &mut VerificationBuilder, accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable>, + _is_top_level: bool, ) -> Result, ProofError> { let alpha = builder.consume_post_result_challenge(); let _beta = builder.consume_post_result_challenge(); - let res_eval = builder.consume_result_mle(); + let res_eval = builder.consume_result_or_intermediate_mle(true); let x_commit = accessor.get_commitment(ColumnRef::new( "sxt.test".parse().unwrap(), "x".parse().unwrap(), 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 5b04e52f9..82521a7db 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 @@ -40,6 +40,7 @@ impl ProverEvaluate for EmptyTestQueryExpr { _builder: &mut ProofBuilder<'a, S>, alloc: &'a Bump, _accessor: &'a dyn DataAccessor, + _is_top_level: bool, ) -> Vec> { let zeros = vec![0; self.length]; let res: &[_] = alloc.alloc_slice_copy(&zeros); @@ -66,6 +67,7 @@ impl ProofPlan for EmptyTestQueryExpr { _builder: &mut VerificationBuilder, _accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable<::Scalar>>, + _is_top_level: bool, ) -> Result, ProofError> { Ok(vec![C::Scalar::ZERO]) } 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 6bf07ab4c..bbc600b54 100644 --- a/crates/proof-of-sql/src/sql/proof/verification_builder.rs +++ b/crates/proof-of-sql/src/sql/proof/verification_builder.rs @@ -104,6 +104,18 @@ impl<'a, C: Commitment> VerificationBuilder<'a, C> { self.mle_evaluations.result_evaluations[index] } + /// Consume the evaluation of an intermediate MLE if NOT at the top level + /// and consume the evaluation of a result MLE if at the top level + /// + /// This is used to consume the evaluation of an MLE used in sumcheck + pub fn consume_result_or_intermediate_mle(&mut self, is_top_level: bool) -> C::Scalar { + if is_top_level { + self.consume_result_mle() + } else { + self.consume_intermediate_mle() + } + } + /// Produce the evaluation of a subpolynomial used in sumcheck pub fn produce_sumcheck_subpolynomial_evaluation(&mut self, eval: &C::Scalar) { self.sumcheck_evaluation += 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 5c69c76c0..0373fa85c 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 @@ -120,8 +120,14 @@ fn we_can_consume_result_evaluations() { &[][..], Vec::new(), ); - assert_eq!(builder.consume_result_mle(), Curve25519Scalar::from(123u64)); - assert_eq!(builder.consume_result_mle(), Curve25519Scalar::from(456u64)); + assert_eq!( + builder.consume_result_or_intermediate_mle(true), + Curve25519Scalar::from(123u64) + ); + assert_eq!( + builder.consume_result_or_intermediate_mle(true), + Curve25519Scalar::from(456u64) + ); } #[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 fc905f962..5b0fc04d3 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 @@ -65,11 +65,18 @@ impl ProofPlan for DynProofPlan { builder: &mut crate::sql::proof::VerificationBuilder, accessor: &dyn crate::base::database::CommitmentAccessor, result: Option<&crate::base::database::OwnedTable>, + is_top_level: bool, ) -> Result, crate::base::proof::ProofError> { match self { - DynProofPlan::Projection(expr) => expr.verifier_evaluate(builder, accessor, result), - DynProofPlan::GroupBy(expr) => expr.verifier_evaluate(builder, accessor, result), - DynProofPlan::Filter(expr) => expr.verifier_evaluate(builder, accessor, result), + DynProofPlan::Projection(expr) => { + expr.verifier_evaluate(builder, accessor, result, is_top_level) + } + DynProofPlan::GroupBy(expr) => { + expr.verifier_evaluate(builder, accessor, result, is_top_level) + } + DynProofPlan::Filter(expr) => { + expr.verifier_evaluate(builder, accessor, result, is_top_level) + } } } @@ -111,11 +118,18 @@ impl ProverEvaluate for DynProofPlan { builder: &mut crate::sql::proof::ProofBuilder<'a, C::Scalar>, alloc: &'a bumpalo::Bump, accessor: &'a dyn crate::base::database::DataAccessor, + is_top_level: bool, ) -> Vec> { match self { - DynProofPlan::Projection(expr) => expr.prover_evaluate(builder, alloc, accessor), - DynProofPlan::GroupBy(expr) => expr.prover_evaluate(builder, alloc, accessor), - DynProofPlan::Filter(expr) => expr.prover_evaluate(builder, alloc, accessor), + DynProofPlan::Projection(expr) => { + expr.prover_evaluate(builder, alloc, accessor, is_top_level) + } + DynProofPlan::GroupBy(expr) => { + expr.prover_evaluate(builder, alloc, accessor, is_top_level) + } + DynProofPlan::Filter(expr) => { + expr.prover_evaluate(builder, alloc, accessor, is_top_level) + } } } } 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 b6373ba23..b4e73eb6f 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 @@ -90,6 +90,7 @@ where builder: &mut VerificationBuilder, accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable>, + is_top_level: bool, ) -> Result, ProofError> { // 1. selection let selection_eval = self.where_clause.verifier_evaluate(builder, accessor)?; @@ -107,7 +108,8 @@ where .ok_or(ProofError::VerificationError("invalid indexes"))?; // 4. filtered_columns let filtered_columns_evals = Vec::from_iter( - repeat_with(|| builder.consume_result_mle()).take(self.aliased_results.len()), + repeat_with(|| builder.consume_result_or_intermediate_mle(is_top_level)) + .take(self.aliased_results.len()), ); let alpha = builder.consume_post_result_challenge(); @@ -183,6 +185,7 @@ impl ProverEvaluate for FilterExec { builder: &mut ProofBuilder<'a, C::Scalar>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + is_top_level: bool, ) -> Vec> { // 1. selection let selection_column: Column<'a, C::Scalar> = @@ -199,6 +202,12 @@ impl ProverEvaluate for FilterExec { ); // Compute filtered_columns and indexes let (filtered_columns, result_len) = filter_columns(alloc, &columns, selection); + // 3. Produce MLEs if not top level + if !is_top_level { + filtered_columns.iter().for_each(|column| { + builder.produce_intermediate_mle(column.as_scalar(alloc)); + }); + } let alpha = builder.consume_post_result_challenge(); let beta = builder.consume_post_result_challenge(); 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 14ef39996..7010e9d04 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 @@ -74,6 +74,7 @@ impl ProverEvaluate for DishonestFilterExec { builder: &mut ProofBuilder<'a, Curve25519Scalar>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + is_top_level: bool, ) -> Vec> { // 1. selection let selection_column: Column<'a, Curve25519Scalar> = @@ -104,6 +105,12 @@ impl ProverEvaluate for DishonestFilterExec { &filtered_columns, result_len, ); + // 3. Produce MLEs if not top level + if !is_top_level { + filtered_columns.iter().for_each(|column| { + builder.produce_intermediate_mle(column.as_scalar(alloc)); + }); + } filtered_columns } } 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 e7377e25f..53a2fbc0d 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 @@ -104,6 +104,7 @@ impl ProofPlan for GroupByExec { builder: &mut VerificationBuilder, accessor: &dyn CommitmentAccessor, result: Option<&OwnedTable>, + is_top_level: bool, ) -> Result, ProofError> { // 1. selection let where_eval = self.where_clause.verifier_evaluate(builder, accessor)?; @@ -126,11 +127,14 @@ impl ProofPlan for GroupByExec { // 4. filtered_columns let group_by_result_columns_evals = Vec::from_iter( - repeat_with(|| builder.consume_result_mle()).take(self.group_by_exprs.len()), + repeat_with(|| builder.consume_result_or_intermediate_mle(is_top_level)) + .take(self.group_by_exprs.len()), ); - let sum_result_columns_evals = - Vec::from_iter(repeat_with(|| builder.consume_result_mle()).take(self.sum_expr.len())); - let count_column_eval = builder.consume_result_mle(); + let sum_result_columns_evals = Vec::from_iter( + repeat_with(|| builder.consume_result_or_intermediate_mle(is_top_level)) + .take(self.sum_expr.len()), + ); + let count_column_eval = builder.consume_result_or_intermediate_mle(is_top_level); let alpha = builder.consume_post_result_challenge(); let beta = builder.consume_post_result_challenge(); @@ -260,6 +264,7 @@ impl ProverEvaluate for GroupByExec { builder: &mut ProofBuilder<'a, C::Scalar>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + is_top_level: bool, ) -> Vec> { // 1. selection let selection_column: Column<'a, C::Scalar> = @@ -279,7 +284,7 @@ impl ProverEvaluate for GroupByExec { .iter() .map(|aliased_expr| aliased_expr.expr.prover_evaluate(builder, alloc, accessor)), ); - // Compute filtered_columns and indexes + // 3. Compute filtered_columns and indexes let AggregatedColumns { group_by_columns: group_by_result_columns, sum_columns: sum_result_columns, @@ -291,6 +296,7 @@ impl ProverEvaluate for GroupByExec { let alpha = builder.consume_post_result_challenge(); let beta = builder.consume_post_result_challenge(); + // 4. Prove group by prove_group_by( builder, alloc, @@ -299,13 +305,21 @@ impl ProverEvaluate for GroupByExec { (&group_by_columns, &sum_columns, selection), (&group_by_result_columns, &sum_result_columns, count_column), ); + // 5. Tally results let sum_result_columns_iter = sum_result_columns.iter().map(|col| Column::Scalar(col)); - Vec::from_iter( + let res = Vec::from_iter( group_by_result_columns .into_iter() .chain(sum_result_columns_iter) .chain(std::iter::once(Column::BigInt(count_column))), - ) + ); + // 6. Produce MLEs if not top level + if !is_top_level { + res.iter().for_each(|column| { + builder.produce_intermediate_mle(column.as_scalar(alloc)); + }); + } + res } } 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 6e1020e4f..3a40804d3 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 @@ -67,13 +67,15 @@ impl ProofPlan for ProjectionExec { builder: &mut VerificationBuilder, accessor: &dyn CommitmentAccessor, _result: Option<&OwnedTable>, + is_top_level: bool, ) -> Result, ProofError> { self.aliased_results .iter() .map(|aliased_expr| aliased_expr.expr.verifier_evaluate(builder, accessor)) .collect::, _>>()?; Ok(Vec::from_iter( - repeat_with(|| builder.consume_result_mle()).take(self.aliased_results.len()), + repeat_with(|| builder.consume_result_or_intermediate_mle(is_top_level)) + .take(self.aliased_results.len()), )) } @@ -117,11 +119,20 @@ impl ProverEvaluate for ProjectionExec { builder: &mut ProofBuilder<'a, C::Scalar>, alloc: &'a Bump, accessor: &'a dyn DataAccessor, + is_top_level: bool, ) -> Vec> { - Vec::from_iter( + // 1. Evaluate result expressions + let res = Vec::from_iter( self.aliased_results .iter() .map(|aliased_expr| aliased_expr.expr.prover_evaluate(builder, alloc, accessor)), - ) + ); + // 2. Produce MLEs if not top level + if !is_top_level { + res.clone().into_iter().for_each(|column| { + builder.produce_intermediate_mle(column.as_scalar(alloc)); + }); + } + res } }