diff --git a/crates/proof-of-sql/src/base/polynomial/composite_polynomial.rs b/crates/proof-of-sql/src/base/polynomial/composite_polynomial.rs index 6c9d2e5ea..3d5b45bd0 100644 --- a/crates/proof-of-sql/src/base/polynomial/composite_polynomial.rs +++ b/crates/proof-of-sql/src/base/polynomial/composite_polynomial.rs @@ -22,6 +22,7 @@ use std::{rc::Rc, vec::Vec}; /// $$\sum_{i=0}^{n}C_i\cdot\prod_{j=0}^{m_i}P_{ij}$$ /// /// The result polynomial is used as the prover key. +#[derive(Clone, Debug)] pub struct CompositePolynomial { /// max number of multiplicands in each product pub max_multiplicands: usize, @@ -36,6 +37,7 @@ pub struct CompositePolynomial { /// Stores the number of variables and max number of multiplicands of the added polynomial used by the prover. /// This data structures will is used as the verifier key. +#[derive(Clone, Debug)] pub struct CompositePolynomialInfo { /// max number of multiplicands in each product pub max_multiplicands: usize, diff --git a/crates/proof-of-sql/src/sql/ast/mod.rs b/crates/proof-of-sql/src/sql/ast/mod.rs index 45d75404e..f7aa68dda 100644 --- a/crates/proof-of-sql/src/sql/ast/mod.rs +++ b/crates/proof-of-sql/src/sql/ast/mod.rs @@ -40,6 +40,11 @@ pub(crate) use provable_expr::ProvableExpr; #[cfg(all(test, feature = "blitzar"))] mod provable_expr_test; +mod projection_expr; +pub(crate) use projection_expr::ProjectionExpr; +#[cfg(all(test, feature = "blitzar"))] +mod projection_expr_test; + mod literal_expr; pub(crate) use literal_expr::LiteralExpr; #[cfg(all(test, feature = "blitzar"))] diff --git a/crates/proof-of-sql/src/sql/ast/projection_expr.rs b/crates/proof-of-sql/src/sql/ast/projection_expr.rs new file mode 100644 index 000000000..f6222131f --- /dev/null +++ b/crates/proof-of-sql/src/sql/ast/projection_expr.rs @@ -0,0 +1,125 @@ +use super::{AliasedProvableExprPlan, ProvableExpr, TableExpr}; +use crate::{ + base::{ + commitment::Commitment, + database::{ + ColumnField, ColumnRef, CommitmentAccessor, DataAccessor, MetadataAccessor, OwnedTable, + }, + proof::ProofError, + }, + sql::proof::{ + CountBuilder, Indexes, ProofBuilder, ProofExpr, ProverEvaluate, ResultBuilder, + VerificationBuilder, + }, +}; +use bumpalo::Bump; +use core::iter::repeat_with; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; + +/// Provable expressions for queries of the form +/// ```ignore +/// SELECT , ..., FROM +/// ``` +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct ProjectionExpr { + pub(super) aliased_results: Vec>, + pub(super) table: TableExpr, +} + +impl ProjectionExpr { + /// Creates a new projection expression. + pub fn new(aliased_results: Vec>, table: TableExpr) -> Self { + Self { + aliased_results, + table, + } + } +} + +impl ProofExpr for ProjectionExpr { + fn count( + &self, + builder: &mut CountBuilder, + _accessor: &dyn MetadataAccessor, + ) -> Result<(), ProofError> { + for aliased_expr in self.aliased_results.iter() { + aliased_expr.expr.count(builder)?; + builder.count_result_columns(1); + } + Ok(()) + } + + fn get_length(&self, accessor: &dyn MetadataAccessor) -> usize { + accessor.get_length(self.table.table_ref) + } + + fn get_offset(&self, accessor: &dyn MetadataAccessor) -> usize { + accessor.get_offset(self.table.table_ref) + } + + #[allow(unused_variables)] + fn verifier_evaluate( + &self, + builder: &mut VerificationBuilder, + accessor: &dyn CommitmentAccessor, + _result: Option<&OwnedTable>, + ) -> Result<(), ProofError> { + self.aliased_results + .iter() + .map(|aliased_expr| aliased_expr.expr.verifier_evaluate(builder, accessor)) + .collect::, _>>()?; + let _columns_evals = Vec::from_iter( + repeat_with(|| builder.consume_result_mle()).take(self.aliased_results.len()), + ); + Ok(()) + } + + fn get_column_result_fields(&self) -> Vec { + self.aliased_results + .iter() + .map(|aliased_expr| ColumnField::new(aliased_expr.alias, aliased_expr.expr.data_type())) + .collect() + } + + fn get_column_references(&self) -> HashSet { + let mut columns = HashSet::new(); + self.aliased_results.iter().for_each(|aliased_expr| { + aliased_expr.expr.get_column_references(&mut columns); + }); + columns + } +} + +impl ProverEvaluate for ProjectionExpr { + #[tracing::instrument(name = "ProjectionExpr::result_evaluate", level = "debug", skip_all)] + fn result_evaluate<'a>( + &self, + builder: &mut ResultBuilder<'a>, + alloc: &'a Bump, + accessor: &'a dyn DataAccessor, + ) { + let columns = Vec::from_iter(self.aliased_results.iter().map(|aliased_expr| { + aliased_expr + .expr + .result_evaluate(builder.table_length(), alloc, accessor) + })); + builder.set_result_indexes(Indexes::Dense(0..(builder.table_length() as u64))); + for col in columns { + builder.produce_result_column(col); + } + } + + #[tracing::instrument(name = "ProjectionExpr::prover_evaluate", level = "debug", skip_all)] + #[allow(unused_variables)] + fn prover_evaluate<'a>( + &self, + builder: &mut ProofBuilder<'a, C::Scalar>, + alloc: &'a Bump, + accessor: &'a dyn DataAccessor, + ) { + self.aliased_results.iter().for_each(|aliased_expr| { + aliased_expr.expr.prover_evaluate(builder, alloc, accessor); + }); + } +} diff --git a/crates/proof-of-sql/src/sql/ast/projection_expr_test.rs b/crates/proof-of-sql/src/sql/ast/projection_expr_test.rs new file mode 100644 index 000000000..1f0492eae --- /dev/null +++ b/crates/proof-of-sql/src/sql/ast/projection_expr_test.rs @@ -0,0 +1,350 @@ +use crate::{ + base::{ + database::{ + owned_table_utility::*, ColumnField, ColumnRef, ColumnType, OwnedTable, + OwnedTableTestAccessor, TableRef, TestAccessor, + }, + math::decimal::Precision, + scalar::Curve25519Scalar, + }, + sql::{ + ast::{ + test_utility::*, ColumnExpr, ProjectionExpr, ProofPlan, ProvableExprPlan, TableExpr, + }, + proof::{ + exercise_verification, ProofExpr, ProverEvaluate, ResultBuilder, VerifiableQueryResult, + }, + }, +}; +use arrow::datatypes::{Field, Schema}; +use blitzar::proof::InnerProductProof; +use bumpalo::Bump; +use curve25519_dalek::RistrettoPoint; +use indexmap::IndexMap; +use proof_of_sql_parser::{Identifier, ResourceId}; +use std::{collections::HashSet, sync::Arc}; + +#[test] +fn we_can_correctly_fetch_the_query_result_schema() { + let table_ref = TableRef::new(ResourceId::try_new("sxt", "sxt_tab").unwrap()); + let a = Identifier::try_new("a").unwrap(); + let b = Identifier::try_new("b").unwrap(); + let provable_ast = ProjectionExpr::::new( + vec![ + aliased_plan( + ProvableExprPlan::Column(ColumnExpr::new(ColumnRef::new( + table_ref, + a, + ColumnType::BigInt, + ))), + "a", + ), + aliased_plan( + ProvableExprPlan::Column(ColumnExpr::new(ColumnRef::new( + table_ref, + b, + ColumnType::BigInt, + ))), + "b", + ), + ], + TableExpr { table_ref }, + ); + + let column_fields: Vec = provable_ast + .get_column_result_fields() + .iter() + .map(|v| v.into()) + .collect(); + let schema = Arc::new(Schema::new(column_fields)); + + assert_eq!( + schema, + Arc::new(Schema::new(vec![ + Field::new("a", (&ColumnType::BigInt).into(), false,), + Field::new("b", (&ColumnType::BigInt).into(), false,) + ])) + ); +} + +#[test] +fn we_can_correctly_fetch_all_the_referenced_columns() { + let table_ref = TableRef::new(ResourceId::try_new("sxt", "sxt_tab").unwrap()); + let a = Identifier::try_new("a").unwrap(); + let f = Identifier::try_new("f").unwrap(); + let provable_ast = ProjectionExpr::::new( + vec![ + aliased_plan( + ProvableExprPlan::Column(ColumnExpr::new(ColumnRef::new( + table_ref, + a, + ColumnType::BigInt, + ))), + "a", + ), + aliased_plan( + ProvableExprPlan::Column(ColumnExpr::new(ColumnRef::new( + table_ref, + f, + ColumnType::BigInt, + ))), + "f", + ), + ], + TableExpr { table_ref }, + ); + + let ref_columns = provable_ast.get_column_references(); + + assert_eq!( + ref_columns, + HashSet::from([ + ColumnRef::new( + table_ref, + Identifier::try_new("a").unwrap(), + ColumnType::BigInt + ), + ColumnRef::new( + table_ref, + Identifier::try_new("f").unwrap(), + ColumnType::BigInt + ), + ]) + ); +} + +#[test] +fn we_can_prove_and_get_the_correct_result_from_a_basic_projection() { + let data = owned_table([ + bigint("a", [1_i64, 4_i64, 5_i64, 2_i64, 5_i64, 1, 4, 5, 2, 5]), + bigint("b", [1_i64, 2, 3, 4, 5, 1, 2, 3, 4, 5]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let ast = projection(cols_expr_plan(t, &["b"], &accessor), tab(t)); + let verifiable_res = VerifiableQueryResult::new(&ast, &accessor, &()); + exercise_verification(&verifiable_res, &ast, &accessor, t); + let res = verifiable_res.verify(&ast, &accessor, &()).unwrap().table; + let expected = owned_table([bigint("b", [1_i64, 2, 3, 4, 5, 1, 2, 3, 4, 5])]); + assert_eq!(res, expected); +} + +#[test] +fn we_can_prove_and_get_the_correct_result_from_a_nontrivial_projection() { + let data = owned_table([ + bigint("a", [1_i64, 4_i64, 5_i64, 2_i64, 5_i64]), + bigint("b", [1_i64, 2, 3, 4, 5]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let ast = projection( + vec![ + aliased_plan(add(column(t, "b", &accessor), const_bigint(1)), "b"), + aliased_plan( + multiply(column(t, "a", &accessor), column(t, "b", &accessor)), + "prod", + ), + ], + tab(t), + ); + let verifiable_res = VerifiableQueryResult::new(&ast, &accessor, &()); + exercise_verification(&verifiable_res, &ast, &accessor, t); + let res = verifiable_res.verify(&ast, &accessor, &()).unwrap().table; + let expected = owned_table([ + bigint("b", [2_i64, 3, 4, 5, 6]), + bigint("prod", [1_i64, 8, 15, 8, 25]), + ]); + assert_eq!(res, expected); +} + +#[test] +fn we_can_get_an_empty_result_from_a_basic_projection_on_an_empty_table_using_result_evaluate() { + let data = owned_table([ + bigint("a", [0; 0]), + bigint("b", [0; 0]), + int128("c", [0; 0]), + varchar("d", [""; 0]), + scalar("e", [0; 0]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let expr: ProofPlan = + projection(cols_expr_plan(t, &["b", "c", "d", "e"], &accessor), tab(t)); + let alloc = Bump::new(); + let mut builder = ResultBuilder::new(0); + expr.result_evaluate(&mut builder, &alloc, &accessor); + let fields = &[ + ColumnField::new("b".parse().unwrap(), ColumnType::BigInt), + ColumnField::new("c".parse().unwrap(), ColumnType::Int128), + ColumnField::new("d".parse().unwrap(), ColumnType::VarChar), + ColumnField::new( + "e".parse().unwrap(), + ColumnType::Decimal75(Precision::new(75).unwrap(), 0), + ), + ]; + let res = builder + .make_provable_query_result() + .to_owned_table(fields) + .unwrap(); + let expected: OwnedTable = owned_table([ + bigint("b", [0; 0]), + int128("c", [0; 0]), + varchar("d", [""; 0]), + decimal75("e", 75, 0, [0; 0]), + ]); + + assert_eq!(res, expected); +} + +#[test] +fn we_can_get_no_columns_from_a_basic_projection_with_no_selected_columns_using_result_evaluate() { + let data = owned_table([ + bigint("a", [1, 4, 5, 2, 5]), + bigint("b", [1, 2, 3, 4, 5]), + int128("c", [1, 2, 3, 4, 5]), + varchar("d", ["1", "2", "3", "4", "5"]), + scalar("e", [1, 2, 3, 4, 5]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let expr: ProofPlan = projection(cols_expr_plan(t, &[], &accessor), tab(t)); + let alloc = Bump::new(); + let mut builder = ResultBuilder::new(5); + expr.result_evaluate(&mut builder, &alloc, &accessor); + let fields = &[]; + let res = builder + .make_provable_query_result() + .to_owned_table::(fields) + .unwrap(); + let expected = OwnedTable::try_new(IndexMap::new()).unwrap(); + assert_eq!(res, expected); +} + +#[test] +fn we_can_get_the_correct_result_from_a_basic_projection_using_result_evaluate() { + let data = owned_table([ + bigint("a", [1, 4, 5, 2, 5]), + bigint("b", [1, 2, 3, 4, 5]), + int128("c", [1, 2, 3, 4, 5]), + varchar("d", ["1", "2", "3", "4", "5"]), + scalar("e", [1, 2, 3, 4, 5]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let expr: ProofPlan = projection( + vec![ + aliased_plan(add(column(t, "b", &accessor), const_bigint(1)), "b"), + aliased_plan( + multiply(column(t, "b", &accessor), column(t, "c", &accessor)), + "prod", + ), + col_expr_plan(t, "d", &accessor), + aliased_plan(const_decimal75(1, 0, 3), "e"), + ], + tab(t), + ); + let alloc = Bump::new(); + let mut builder = ResultBuilder::new(5); + expr.result_evaluate(&mut builder, &alloc, &accessor); + let fields = &[ + ColumnField::new("b".parse().unwrap(), ColumnType::BigInt), + ColumnField::new("prod".parse().unwrap(), ColumnType::Int128), + ColumnField::new("d".parse().unwrap(), ColumnType::VarChar), + ColumnField::new( + "e".parse().unwrap(), + ColumnType::Decimal75(Precision::new(1).unwrap(), 0), + ), + ]; + let res = builder + .make_provable_query_result() + .to_owned_table(fields) + .unwrap(); + let expected: OwnedTable = owned_table([ + bigint("b", [2, 3, 4, 5, 6]), + int128("prod", [1, 4, 9, 16, 25]), + varchar("d", ["1", "2", "3", "4", "5"]), + decimal75("e", 1, 0, [3; 5]), + ]); + assert_eq!(res, expected); +} + +#[test] +fn we_can_prove_a_projection_on_an_empty_table() { + let data = owned_table([ + bigint("a", [101; 0]), + bigint("b", [3; 0]), + int128("c", [3; 0]), + varchar("d", ["3"; 0]), + scalar("e", [3; 0]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let expr = projection( + vec![ + aliased_plan(add(column(t, "b", &accessor), const_bigint(1)), "b"), + aliased_plan( + multiply(column(t, "b", &accessor), column(t, "c", &accessor)), + "prod", + ), + col_expr_plan(t, "d", &accessor), + aliased_plan(const_decimal75(1, 0, 3), "e"), + ], + tab(t), + ); + let res = VerifiableQueryResult::new(&expr, &accessor, &()); + exercise_verification(&res, &expr, &accessor, t); + let res = res.verify(&expr, &accessor, &()).unwrap().table; + let expected = owned_table([ + bigint("b", [3; 0]), + int128("prod", [3; 0]), + varchar("d", ["3"; 0]), + decimal75("e", 1, 0, [3; 0]), + ]); + assert_eq!(res, expected); +} + +#[test] +fn we_can_prove_a_projection() { + let data = owned_table([ + bigint("a", [101, 104, 105, 102, 105]), + bigint("b", [1, 2, 3, 4, 7]), + int128("c", [1, 3, 3, 4, 5]), + varchar("d", ["1", "2", "3", "4", "5"]), + scalar("e", [1, 2, 3, 4, 5]), + ]); + let t = "sxt.t".parse().unwrap(); + let mut accessor = OwnedTableTestAccessor::::new_empty_with_setup(()); + accessor.add_table(t, data, 0); + let expr = projection( + vec![ + col_expr_plan(t, "b", &accessor), + col_expr_plan(t, "c", &accessor), + col_expr_plan(t, "d", &accessor), + col_expr_plan(t, "e", &accessor), + aliased_plan(const_int128(105), "const"), + aliased_plan( + equal(column(t, "b", &accessor), column(t, "c", &accessor)), + "bool", + ), + ], + tab(t), + ); + let res = VerifiableQueryResult::new(&expr, &accessor, &()); + exercise_verification(&res, &expr, &accessor, t); + let res = res.verify(&expr, &accessor, &()).unwrap().table; + let expected = owned_table([ + bigint("b", [1, 2, 3, 4, 7]), + int128("c", [1, 3, 3, 4, 5]), + varchar("d", ["1", "2", "3", "4", "5"]), + scalar("e", [1, 2, 3, 4, 5]), + int128("const", [105; 5]), + boolean("bool", [true, false, true, true, false]), + ]); + assert_eq!(res, expected); +} diff --git a/crates/proof-of-sql/src/sql/ast/proof_plan.rs b/crates/proof-of-sql/src/sql/ast/proof_plan.rs index e61ecd013..178a2e944 100644 --- a/crates/proof-of-sql/src/sql/ast/proof_plan.rs +++ b/crates/proof-of-sql/src/sql/ast/proof_plan.rs @@ -1,4 +1,4 @@ -use super::{DenseFilterExpr, FilterExpr, GroupByExpr}; +use super::{DenseFilterExpr, FilterExpr, GroupByExpr, ProjectionExpr}; use crate::{ base::commitment::Commitment, sql::proof::{ProofExpr, ProverEvaluate}, @@ -8,6 +8,11 @@ use serde::{Deserialize, Serialize}; /// The query plan for proving a query #[derive(Debug, PartialEq, Serialize, Deserialize)] pub enum ProofPlan { + /// Provable expressions for queries of the form + /// ```ignore + /// SELECT , ..., FROM
+ /// ``` + Projection(ProjectionExpr), /// Provable expressions for queries of the form, where the result is sent in a sparse form /// ```ignore /// SELECT , ..., FROM
WHERE @@ -37,6 +42,7 @@ impl ProofExpr for ProofPlan { accessor: &dyn crate::base::database::MetadataAccessor, ) -> Result<(), crate::base::proof::ProofError> { match self { + ProofPlan::Projection(expr) => expr.count(builder, accessor), ProofPlan::Filter(expr) => expr.count(builder, accessor), ProofPlan::GroupBy(expr) => expr.count(builder, accessor), ProofPlan::DenseFilter(expr) => expr.count(builder, accessor), @@ -45,6 +51,7 @@ impl ProofExpr for ProofPlan { fn get_length(&self, accessor: &dyn crate::base::database::MetadataAccessor) -> usize { match self { + ProofPlan::Projection(expr) => expr.get_length(accessor), ProofPlan::Filter(expr) => expr.get_length(accessor), ProofPlan::GroupBy(expr) => expr.get_length(accessor), ProofPlan::DenseFilter(expr) => expr.get_length(accessor), @@ -53,6 +60,7 @@ impl ProofExpr for ProofPlan { fn get_offset(&self, accessor: &dyn crate::base::database::MetadataAccessor) -> usize { match self { + ProofPlan::Projection(expr) => expr.get_offset(accessor), ProofPlan::Filter(expr) => expr.get_offset(accessor), ProofPlan::GroupBy(expr) => expr.get_offset(accessor), ProofPlan::DenseFilter(expr) => expr.get_offset(accessor), @@ -67,6 +75,7 @@ impl ProofExpr for ProofPlan { result: Option<&crate::base::database::OwnedTable>, ) -> Result<(), crate::base::proof::ProofError> { match self { + ProofPlan::Projection(expr) => expr.verifier_evaluate(builder, accessor, result), ProofPlan::Filter(expr) => expr.verifier_evaluate(builder, accessor, result), ProofPlan::GroupBy(expr) => expr.verifier_evaluate(builder, accessor, result), ProofPlan::DenseFilter(expr) => expr.verifier_evaluate(builder, accessor, result), @@ -75,6 +84,7 @@ impl ProofExpr for ProofPlan { fn get_column_result_fields(&self) -> Vec { match self { + ProofPlan::Projection(expr) => expr.get_column_result_fields(), ProofPlan::Filter(expr) => expr.get_column_result_fields(), ProofPlan::GroupBy(expr) => expr.get_column_result_fields(), ProofPlan::DenseFilter(expr) => expr.get_column_result_fields(), @@ -83,6 +93,7 @@ impl ProofExpr for ProofPlan { fn get_column_references(&self) -> std::collections::HashSet { match self { + ProofPlan::Projection(expr) => expr.get_column_references(), ProofPlan::Filter(expr) => expr.get_column_references(), ProofPlan::GroupBy(expr) => expr.get_column_references(), ProofPlan::DenseFilter(expr) => expr.get_column_references(), @@ -99,6 +110,7 @@ impl ProverEvaluate for ProofPlan { accessor: &'a dyn crate::base::database::DataAccessor, ) { match self { + ProofPlan::Projection(expr) => expr.result_evaluate(builder, alloc, accessor), ProofPlan::Filter(expr) => expr.result_evaluate(builder, alloc, accessor), ProofPlan::GroupBy(expr) => expr.result_evaluate(builder, alloc, accessor), ProofPlan::DenseFilter(expr) => expr.result_evaluate(builder, alloc, accessor), @@ -113,6 +125,7 @@ impl ProverEvaluate for ProofPlan { accessor: &'a dyn crate::base::database::DataAccessor, ) { match self { + ProofPlan::Projection(expr) => expr.prover_evaluate(builder, alloc, accessor), ProofPlan::Filter(expr) => expr.prover_evaluate(builder, alloc, accessor), ProofPlan::GroupBy(expr) => expr.prover_evaluate(builder, alloc, accessor), ProofPlan::DenseFilter(expr) => expr.prover_evaluate(builder, alloc, accessor), diff --git a/crates/proof-of-sql/src/sql/ast/test_utility.rs b/crates/proof-of-sql/src/sql/ast/test_utility.rs index 991afb239..d160a6ea9 100644 --- a/crates/proof-of-sql/src/sql/ast/test_utility.rs +++ b/crates/proof-of-sql/src/sql/ast/test_utility.rs @@ -1,6 +1,6 @@ use super::{ AliasedProvableExprPlan, ColumnExpr, DenseFilterExpr, FilterExpr, FilterResultExpr, - GroupByExpr, ProofPlan, ProvableExprPlan, TableExpr, + GroupByExpr, ProjectionExpr, ProofPlan, ProvableExprPlan, TableExpr, }; use crate::base::{ commitment::Commitment, @@ -229,6 +229,13 @@ pub fn cols_expr( .collect() } +pub fn projection( + results: Vec>, + table: TableExpr, +) -> ProofPlan { + ProofPlan::Projection(ProjectionExpr::new(results, table)) +} + pub fn dense_filter( results: Vec>, table: TableExpr, 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 be2743384..af28ec85e 100644 --- a/crates/proof-of-sql/src/sql/proof/query_proof.rs +++ b/crates/proof-of-sql/src/sql/proof/query_proof.rs @@ -215,7 +215,9 @@ impl QueryProof { // verify sumcheck up to the evaluation check let poly_info = CompositePolynomialInfo { - max_multiplicands: counts.sumcheck_max_multiplicands, + // This needs to be at least 2 since `CompositePolynomialBuilder::make_composite_polynomial` + // always adds a degree 2 term. + max_multiplicands: core::cmp::max(counts.sumcheck_max_multiplicands, 2), num_variables: num_sumcheck_variables, }; let subclaim = self.sumcheck_proof.verify_without_evaluation(