Skip to content

Commit

Permalink
feat: compute min and max of indices for multi-table queries
Browse files Browse the repository at this point in the history
- add `indexset!` macro
- compute min and max of indices for multi-table queries
  • Loading branch information
iajoiner committed Nov 8, 2024
1 parent 107fa10 commit ac881cb
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 46 deletions.
1 change: 1 addition & 0 deletions crates/proof-of-sql/src/base/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub use expression_evaluation_error::{ExpressionEvaluationError, ExpressionEvalu
mod test_accessor;
pub use test_accessor::TestAccessor;
#[cfg(test)]
#[allow(unused_imports)]
pub(crate) use test_accessor::UnimplementedTestAccessor;

#[cfg(test)]
Expand Down
18 changes: 18 additions & 0 deletions crates/proof-of-sql/src/base/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,22 @@ macro_rules! indexmap_macro {
};
}

/// Create an [`IndexSet`][self::IndexSet] from a list of values
#[cfg(test)]
macro_rules! indexset_macro {
($($value:expr,)+) => { $crate::base::map::indexset!($($value),+) };
($($value:expr),*) => {
{
const CAP: usize = <[()]>::len(&[$({ stringify!($value); }),*]);
let mut set = $crate::base::map::IndexSet::with_capacity_and_hasher(CAP, <_>::default());
$(
set.insert($value);
)*
set
}
};
}

pub(crate) use indexmap_macro as indexmap;
#[cfg(test)]
pub(crate) use indexset_macro as indexset;
15 changes: 15 additions & 0 deletions crates/proof-of-sql/src/sql/proof/proof_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ pub trait ProofPlan: Debug + Send + Sync + ProverEvaluate {

/// Return all the tables referenced in the Query
fn get_table_references(&self) -> IndexSet<TableRef>;

/// Return the row number range of tables referenced in the Query
///
/// 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(&self, accessor: &dyn MetadataAccessor) -> (usize, usize) {
self.get_table_references().iter().fold(
(usize::MAX, 0),
|(min, max): (usize, usize), table_ref| {
let length = accessor.get_length(*table_ref);
let offset = accessor.get_offset(*table_ref);
(min.min(offset), max.max(offset + length))
},
)
}
}

#[enum_dispatch::enum_dispatch(DynProofPlan)]
Expand Down
54 changes: 27 additions & 27 deletions crates/proof-of-sql/src/sql/proof/query_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
accessor: &impl DataAccessor<CP::Scalar>,
setup: &CP::ProverPublicSetup<'_>,
) -> (Self, ProvableQueryResult) {
let table_length = expr.get_length(accessor);
let num_sumcheck_variables = cmp::max(log2_up(table_length), 1);
let generator_offset = expr.get_offset(accessor);
let (min_row_num, max_row_num) = expr.get_index_range(accessor);
let range_length = max_row_num - min_row_num;
let num_sumcheck_variables = cmp::max(log2_up(range_length), 1);
assert!(num_sumcheck_variables > 0);

let alloc = Bump::new();

// Evaluate query result
let result_cols = expr.result_evaluate(table_length, &alloc, accessor);
let result_cols = expr.result_evaluate(range_length, &alloc, accessor);
let output_length = result_cols.first().map_or(0, Column::len);
let provable_result = ProvableQueryResult::new(output_length as u64, &result_cols);

Expand All @@ -65,7 +65,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {

// construct a transcript for the proof
let mut transcript: Keccak256Transcript =
make_transcript(expr, &provable_result, table_length, generator_offset);
make_transcript(expr, &provable_result, range_length, min_row_num);

// These are the challenges that will be consumed by the proof
// Specifically, these are the challenges that the verifier sends to
Expand All @@ -78,14 +78,13 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
.collect();

let mut builder =
FinalRoundBuilder::new(table_length, num_sumcheck_variables, post_result_challenges);
FinalRoundBuilder::new(range_length, num_sumcheck_variables, post_result_challenges);
expr.final_round_evaluate(&mut builder, &alloc, accessor);

let num_sumcheck_variables = builder.num_sumcheck_variables();
let table_length = builder.table_length();

// commit to any intermediate MLEs
let commitments = builder.commit_intermediate_mles(generator_offset, setup);
let commitments = builder.commit_intermediate_mles(min_row_num, setup);

// add the commitments and bit distributions to the proof
extend_transcript(&mut transcript, &commitments, builder.bit_distributions());
Expand All @@ -98,7 +97,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
.collect();
let poly = builder.make_sumcheck_polynomial(&SumcheckRandomScalars::new(
&random_scalars,
table_length,
range_length,
num_sumcheck_variables,
));

Expand All @@ -107,7 +106,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
let sumcheck_proof = SumcheckProof::create(&mut transcript, &mut evaluation_point, &poly);

// evaluate the MLEs used in sumcheck except for the result columns
let mut evaluation_vec = vec![Zero::zero(); table_length];
let mut evaluation_vec = vec![Zero::zero(); range_length];
compute_evaluation_vector(&mut evaluation_vec, &evaluation_point);
let pcs_proof_evaluations = builder.evaluate_pcs_proof_mles(&evaluation_vec);

Expand All @@ -127,7 +126,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
&mut transcript,
&folded_mle,
&evaluation_point,
generator_offset as u64,
min_row_num as u64,
setup,
);

Expand All @@ -150,12 +149,13 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
result: &ProvableQueryResult,
setup: &CP::VerifierPublicSetup<'_>,
) -> QueryResult<CP::Scalar> {
let input_length = expr.get_length(accessor);
let output_length = result.table_length();
let generator_offset = expr.get_offset(accessor);
let num_sumcheck_variables = cmp::max(log2_up(input_length), 1);
let (min_row_num, max_row_num) = expr.get_index_range(accessor);
let range_length = max_row_num - min_row_num;
let num_sumcheck_variables = cmp::max(log2_up(range_length), 1);
assert!(num_sumcheck_variables > 0);

let output_length = result.table_length();

// validate bit decompositions
for dist in &self.bit_distributions {
if !dist.is_valid() {
Expand All @@ -181,7 +181,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {

// construct a transcript for the proof
let mut transcript: Keccak256Transcript =
make_transcript(expr, result, input_length, generator_offset);
make_transcript(expr, result, range_length, min_row_num);

// These are the challenges that will be consumed by the proof
// Specifically, these are the challenges that the verifier sends to
Expand All @@ -203,7 +203,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
.take(num_random_scalars)
.collect();
let sumcheck_random_scalars =
SumcheckRandomScalars::new(&random_scalars, input_length, num_sumcheck_variables);
SumcheckRandomScalars::new(&random_scalars, range_length, num_sumcheck_variables);

// verify sumcheck up to the evaluation check
let poly_info = CompositePolynomialInfo {
Expand Down Expand Up @@ -232,14 +232,14 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {

// pass over the provable AST to fill in the verification builder
let sumcheck_evaluations = SumcheckMleEvaluations::new(
input_length,
range_length,
output_length,
&subclaim.evaluation_point,
&sumcheck_random_scalars,
&self.pcs_proof_evaluations,
);
let mut builder = VerificationBuilder::new(
generator_offset,
min_row_num,
sumcheck_evaluations,
&self.bit_distributions,
&self.commitments,
Expand Down Expand Up @@ -279,8 +279,8 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
builder.inner_product_multipliers(),
&product,
&subclaim.evaluation_point,
generator_offset as u64,
input_length,
min_row_num as u64,
range_length,
setup,
)
.map_err(|_e| ProofError::VerificationError {
Expand Down Expand Up @@ -315,9 +315,9 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
/// * `result` - A reference to a `ProvableQueryResult`, which is the result
/// of a query that needs to be proven.
///
/// * `table_length` - The length of the table used in the proof, as a `usize`.
/// * `range_length` - The length of the range of the generator used in the proof, as a `usize`.
///
/// * `generator_offset` - The offset of the generator used in the proof, as a `usize`.
/// * `min_row_num` - The smallest offset of the generator used in the proof, as a `usize`.
///
/// # Returns
/// This function returns a `merlin::Transcript`. The transcript is a record
Expand All @@ -326,14 +326,14 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
fn make_transcript<T: Transcript>(
expr: &(impl ProofPlan + Serialize),
result: &ProvableQueryResult,
table_length: usize,
generator_offset: usize,
range_length: usize,
min_row_num: usize,
) -> T {
let mut transcript = T::new();
transcript.extend_serialize_as_le(result);
transcript.extend_serialize_as_le(expr);
transcript.extend_serialize_as_le(&table_length);
transcript.extend_serialize_as_le(&generator_offset);
transcript.extend_serialize_as_le(&range_length);
transcript.extend_serialize_as_le(&min_row_num);
transcript
}

Expand Down
44 changes: 32 additions & 12 deletions crates/proof-of-sql/src/sql/proof/query_proof_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ use crate::{
database::{
owned_table_utility::{bigint, owned_table},
Column, ColumnField, ColumnRef, ColumnType, CommitmentAccessor, DataAccessor,
MetadataAccessor, OwnedTable, OwnedTableTestAccessor, TableRef, TestAccessor,
UnimplementedTestAccessor,
MetadataAccessor, OwnedTable, OwnedTableTestAccessor, TableRef,
},
map::IndexSet,
map::{indexset, IndexSet},
proof::ProofError,
scalar::{Curve25519Scalar, Scalar},
},
Expand Down Expand Up @@ -59,7 +58,7 @@ impl ProverEvaluate for TrivialTestProofPlan {
alloc: &'a Bump,
_accessor: &'a dyn DataAccessor<S>,
) -> Vec<Column<'a, S>> {
let col = alloc.alloc_slice_fill_copy(builder.table_length(), self.column_fill_value);
let col = alloc.alloc_slice_fill_copy(self.length, self.column_fill_value);
builder.produce_intermediate_mle(col as &[_]);
builder.produce_sumcheck_subpolynomial(
SumcheckSubpolynomialType::Identity,
Expand Down Expand Up @@ -106,7 +105,7 @@ impl ProofPlan for TrivialTestProofPlan {
unimplemented!("no real usage for this function yet")
}
fn get_table_references(&self) -> IndexSet<TableRef> {
unimplemented!("no real usage for this function yet")
indexset! {TableRef::new("sxt.test".parse().unwrap())}
}
}

Expand All @@ -116,7 +115,13 @@ fn verify_a_trivial_query_proof_with_given_offset(n: usize, offset_generators: u
offset: offset_generators,
..Default::default()
};
let accessor = UnimplementedTestAccessor::new_empty();
let column: Vec<i64> = vec![0_i64; n];
let accessor = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("a1", column)]),
offset_generators,
(),
);
let (proof, result) = QueryProof::<InnerProductProof>::new(&expr, &accessor, &());
let QueryData {
verification_hash,
Expand Down Expand Up @@ -149,7 +154,12 @@ fn verify_fails_if_the_summation_in_sumcheck_isnt_zero() {
column_fill_value: 123,
..Default::default()
};
let accessor = UnimplementedTestAccessor::new_empty();
let accessor = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("a1", [123_i64; 2])]),
0,
(),
);
let (proof, result) = QueryProof::<InnerProductProof>::new(&expr, &accessor, &());
assert!(proof.verify(&expr, &accessor, &result, &()).is_err());
}
Expand All @@ -162,7 +172,12 @@ fn verify_fails_if_the_sumcheck_evaluation_isnt_correct() {
evaluation: 123,
..Default::default()
};
let accessor = UnimplementedTestAccessor::new_empty();
let accessor = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("a1", [123_i64; 2])]),
0,
(),
);
let (proof, result) = QueryProof::<InnerProductProof>::new(&expr, &accessor, &());
assert!(proof.verify(&expr, &accessor, &result, &()).is_err());
}
Expand All @@ -175,7 +190,12 @@ fn verify_fails_if_counts_dont_match() {
anchored_mle_count: 1,
..Default::default()
};
let accessor = UnimplementedTestAccessor::new_empty();
let accessor = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("a1", [0_i64; 2])]),
0,
(),
);
let (proof, result) = QueryProof::<InnerProductProof>::new(&expr, &accessor, &());
assert!(proof.verify(&expr, &accessor, &result, &()).is_err());
}
Expand Down Expand Up @@ -274,7 +294,7 @@ impl ProofPlan for SquareTestProofPlan {
unimplemented!("no real usage for this function yet")
}
fn get_table_references(&self) -> IndexSet<TableRef> {
unimplemented!("no real usage for this function yet")
indexset! {TableRef::new("sxt.test".parse().unwrap())}
}
}

Expand Down Expand Up @@ -476,7 +496,7 @@ impl ProofPlan for DoubleSquareTestProofPlan {
unimplemented!("no real usage for this function yet")
}
fn get_table_references(&self) -> IndexSet<TableRef> {
unimplemented!("no real usage for this function yet")
indexset! {TableRef::new("sxt.test".parse().unwrap())}
}
}

Expand Down Expand Up @@ -671,7 +691,7 @@ impl ProofPlan for ChallengeTestProofPlan {
unimplemented!("no real usage for this function yet")
}
fn get_table_references(&self) -> IndexSet<TableRef> {
unimplemented!("no real usage for this function yet")
indexset! {TableRef::new("sxt.test".parse().unwrap())}
}
}

Expand Down
22 changes: 17 additions & 5 deletions crates/proof-of-sql/src/sql/proof/verifiable_query_result_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use crate::{
database::{
owned_table_utility::{bigint, owned_table},
Column, ColumnField, ColumnRef, ColumnType, CommitmentAccessor, DataAccessor,
MetadataAccessor, OwnedTable, TableRef, TestAccessor, UnimplementedTestAccessor,
MetadataAccessor, OwnedTable, OwnedTableTestAccessor, TableRef,
},
map::IndexSet,
map::{indexset, IndexSet},
proof::ProofError,
scalar::Scalar,
},
Expand Down Expand Up @@ -86,7 +86,7 @@ impl ProofPlan for EmptyTestQueryExpr {
}

fn get_table_references(&self) -> IndexSet<TableRef> {
unimplemented!("no real usage for this function yet")
indexset! {TableRef::new("sxt.test".parse().unwrap())}
}
}

Expand All @@ -96,7 +96,13 @@ fn we_can_verify_queries_on_an_empty_table() {
columns: 1,
..Default::default()
};
let accessor = UnimplementedTestAccessor::new_empty();
let column: Vec<i64> = vec![0_i64; 0];
let accessor = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("a1", column)]),
0,
(),
);
let res = VerifiableQueryResult::<InnerProductProof>::new(&expr, &accessor, &());
let QueryData {
verification_hash: _,
Expand All @@ -112,7 +118,13 @@ fn empty_verification_fails_if_the_result_contains_non_null_members() {
columns: 1,
..Default::default()
};
let accessor = UnimplementedTestAccessor::new_empty();
let column: Vec<i64> = vec![0_i64; 0];
let accessor = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("a1", column)]),
0,
(),
);
let res = VerifiableQueryResult::<InnerProductProof> {
provable_result: Some(ProvableQueryResult::default()),
proof: None,
Expand Down
Loading

0 comments on commit ac881cb

Please sign in to comment.