Skip to content

Commit

Permalink
refactor!: remove use of Serialize from QueryProof
Browse files Browse the repository at this point in the history
  • Loading branch information
JayWhite2357 committed Dec 6, 2024
1 parent ff165cd commit b02a1d8
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 110 deletions.
7 changes: 7 additions & 0 deletions crates/proof-of-sql/src/base/proof/transcript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ pub trait Transcript {
fn new() -> Self;
/// Appends the provided messages by appending the reversed raw bytes (i.e. assuming the message is bigendian)
fn extend_as_be<M: FromBytes + AsBytes>(&mut self, messages: impl IntoIterator<Item = M>);
/// Appends the provided messages by appending the reversed raw bytes (i.e. assuming the message is bigendian)
fn extend_as_be_from_refs<'a, M: FromBytes + AsBytes + 'a + Copy>(
&mut self,
messages: impl IntoIterator<Item = &'a M>,
) {
self.extend_as_be(messages.into_iter().copied());
}
/// Appends the provided messages by appending the raw bytes (i.e. assuming the message is littleendian)
fn extend_as_le<M: AsBytes>(&mut self, messages: impl IntoIterator<Item = M>);
/// Appends the provided messages by appending the raw bytes (i.e. assuming the message is littleendian)
Expand Down
54 changes: 41 additions & 13 deletions crates/proof-of-sql/src/sql/proof/query_proof.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use super::{
CountBuilder, FinalRoundBuilder, ProofCounts, ProofPlan, ProvableQueryResult, QueryResult,
SumcheckMleEvaluations, SumcheckRandomScalars, VerificationBuilder,
CountBuilder, FinalRoundBuilder, ProofCounts, ProofPlan, QueryResult, SumcheckMleEvaluations,
SumcheckRandomScalars, VerificationBuilder,
};
use crate::{
base::{
bit::BitDistribution,
commitment::{Commitment, CommitmentEvaluationProof},
database::{
ColumnRef, CommitmentAccessor, DataAccessor, MetadataAccessor, Table, TableRef,
ColumnRef, CommitmentAccessor, DataAccessor, MetadataAccessor, OwnedColumn, OwnedTable,
Table, TableRef,
},
map::{IndexMap, IndexSet},
math::log2_up,
polynomial::{compute_evaluation_vector, CompositePolynomialInfo},
proof::{Keccak256Transcript, ProofError, Transcript},
scalar::Scalar,
},
proof_primitive::sumcheck::SumcheckProof,
sql::proof::{FirstRoundBuilder, QueryData},
Expand Down Expand Up @@ -73,7 +75,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
expr: &(impl ProofPlan + Serialize),
accessor: &impl DataAccessor<CP::Scalar>,
setup: &CP::ProverPublicSetup<'_>,
) -> (Self, ProvableQueryResult) {
) -> (Self, OwnedTable<CP::Scalar>) {
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();
Expand All @@ -95,6 +97,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
// Prover First Round: Evaluate the query && get the right number of post result challenges
let mut first_round_builder = FirstRoundBuilder::new();
let query_result = expr.first_round_evaluate(&mut first_round_builder, &alloc, &table_map);
let owned_table_result = OwnedTable::from(&query_result);
let provable_result = query_result.into();
let one_evaluation_lengths = first_round_builder.one_evaluation_lengths();

Expand All @@ -111,7 +114,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
// construct a transcript for the proof
let mut transcript: Keccak256Transcript = make_transcript(
expr,
&provable_result,
&owned_table_result,
range_length,
min_row_num,
one_evaluation_lengths,
Expand Down Expand Up @@ -207,10 +210,9 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
self,
expr: &(impl ProofPlan + Serialize),
accessor: &impl CommitmentAccessor<CP::Commitment>,
result: ProvableQueryResult,
result: OwnedTable<CP::Scalar>,
setup: &CP::VerifierPublicSetup<'_>,
) -> QueryResult<CP::Scalar> {
let owned_table_result = result.to_owned_table(&expr.get_column_result_fields())?;
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);
Expand Down Expand Up @@ -340,11 +342,11 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
let verifier_evaluations = expr.verifier_evaluate(
&mut builder,
&evaluation_accessor,
Some(&owned_table_result),
Some(&result),
&one_eval_map,
)?;
// compute the evaluation of the result MLEs
let result_evaluations = owned_table_result.mle_evaluations(&subclaim.evaluation_point);
let result_evaluations = result.mle_evaluations(&subclaim.evaluation_point);
// check the evaluation of the result MLEs
if verifier_evaluations.column_evals() != result_evaluations {
Err(ProofError::VerificationError {
Expand Down Expand Up @@ -378,7 +380,7 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {

let verification_hash = transcript.challenge_as_le();
Ok(QueryData {
table: owned_table_result,
table: result,
verification_hash,
})
}
Expand Down Expand Up @@ -414,15 +416,41 @@ impl<CP: CommitmentEvaluationProof> QueryProof<CP> {
/// This function returns a `merlin::Transcript`. The transcript is a record
/// of all the operations and data involved in creating a proof.
/// ```
fn make_transcript<T: Transcript>(
fn make_transcript<S: Scalar, T: Transcript>(
expr: &(impl ProofPlan + Serialize),
result: &ProvableQueryResult,
result: &OwnedTable<S>,
range_length: usize,
min_row_num: usize,
one_evaluation_lengths: &[usize],
) -> T {
let mut transcript = T::new();
transcript.extend_serialize_as_le(result);
for (name, column) in result.inner_table() {
transcript.extend_as_le_from_refs([name.as_str()]);
match column {
OwnedColumn::Boolean(col) => transcript.extend_as_be(col.iter().map(|&b| u8::from(b))),
OwnedColumn::TinyInt(col) => transcript.extend_as_be_from_refs(col),
OwnedColumn::SmallInt(col) => transcript.extend_as_be_from_refs(col),
OwnedColumn::Int(col) => transcript.extend_as_be_from_refs(col),
OwnedColumn::BigInt(col) => transcript.extend_as_be_from_refs(col),
OwnedColumn::VarChar(col) => {
transcript.extend_as_le_from_refs(col.iter().map(String::as_str));
}
OwnedColumn::Int128(col) => transcript.extend_as_be_from_refs(col),
OwnedColumn::Decimal75(precision, scale, col) => {
transcript.extend_as_be([precision.value()]);
transcript.extend_as_be([*scale]);
transcript.extend_as_be(col.iter().map(|&s| Into::<[u64; 4]>::into(s)));
}
OwnedColumn::Scalar(col) => {
transcript.extend_as_be(col.iter().map(|&s| Into::<[u64; 4]>::into(s)));
}
OwnedColumn::TimestampTZ(po_sqltime_unit, po_sqltime_zone, col) => {
transcript.extend_as_be([u64::from(*po_sqltime_unit)]);
transcript.extend_as_be([po_sqltime_zone.offset()]);
transcript.extend_as_be_from_refs(col);
}
}
}
transcript.extend_serialize_as_le(expr);
transcript.extend_serialize_as_le(&range_length);
transcript.extend_serialize_as_le(&min_row_num);
Expand Down
14 changes: 6 additions & 8 deletions crates/proof-of-sql/src/sql/proof/verifiable_query_result.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{ProofPlan, ProvableQueryResult, QueryData, QueryProof, QueryResult};
use super::{ProofPlan, QueryData, QueryProof, QueryResult};
use crate::base::{
commitment::CommitmentEvaluationProof,
database::{
Expand Down Expand Up @@ -69,7 +69,7 @@ use serde::{Deserialize, Serialize};
#[derive(Default, Clone, Serialize, Deserialize)]
pub struct VerifiableQueryResult<CP: CommitmentEvaluationProof> {
/// The result of the query in intermediate form.
pub(super) provable_result: Option<ProvableQueryResult>,
pub(super) result: Option<OwnedTable<CP::Scalar>>,
/// The proof that the query result is valid.
pub(super) proof: Option<QueryProof<CP>>,
}
Expand All @@ -94,14 +94,14 @@ impl<CP: CommitmentEvaluationProof> VerifiableQueryResult<CP> {
.all(|table_ref| accessor.get_length(table_ref) == 0)
{
return VerifiableQueryResult {
provable_result: None,
result: None,
proof: None,
};
}

let (proof, res) = QueryProof::new(expr, accessor, setup);
Self {
provable_result: Some(res),
result: Some(res),
proof: Some(proof),
}
}
Expand All @@ -119,10 +119,8 @@ impl<CP: CommitmentEvaluationProof> VerifiableQueryResult<CP> {
accessor: &impl CommitmentAccessor<CP::Commitment>,
setup: &CP::VerifierPublicSetup<'_>,
) -> QueryResult<CP::Scalar> {
match (self.provable_result, self.proof) {
(Some(provable_result), Some(proof)) => {
proof.verify(expr, accessor, provable_result, setup)
}
match (self.result, self.proof) {
(Some(result), Some(proof)) => proof.verify(expr, accessor, result, setup),
(None, None)
if expr
.get_table_references()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
proof::ProofError,
scalar::Scalar,
},
sql::proof::{FirstRoundBuilder, ProvableQueryResult, QueryData},
sql::proof::{FirstRoundBuilder, QueryData},
};
use bumpalo::Bump;
use serde::Serialize;
Expand Down Expand Up @@ -130,7 +130,7 @@ fn empty_verification_fails_if_the_result_contains_non_null_members() {
(),
);
let res = VerifiableQueryResult::<InnerProductProof> {
provable_result: Some(ProvableQueryResult::default()),
result: Some(owned_table([])),
proof: None,
};
assert!(res.verify(&expr, &accessor, &()).is_err());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
use super::{
verifiable_query_result_test::EmptyTestQueryExpr, ProofPlan, ProvableQueryResult, QueryProof,
VerifiableQueryResult,
};
use super::{ProofPlan, VerifiableQueryResult};
use crate::base::{
commitment::{Commitment, CommittableColumn},
database::{
owned_table_utility::*, Column, CommitmentAccessor, OwnedTableTestAccessor, TableRef,
TestAccessor,
},
scalar::Curve25519Scalar,
database::{owned_table_utility::*, OwnedColumn, OwnedTable, TableRef, TestAccessor},
scalar::{Curve25519Scalar, Scalar},
};
use blitzar::proof::InnerProductProof;
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
Expand All @@ -33,20 +27,20 @@ pub fn exercise_verification(
accessor: &impl TestAccessor<RistrettoPoint>,
table_ref: TableRef,
) {
let verification_result = res.clone().verify(expr, accessor, &());
assert!(
verification_result.is_ok(),
"Verification failed: {:?}",
verification_result.err()
);
res.clone()
.verify(expr, accessor, &())
.expect("Verification failed");

// try changing the result
tamper_result(res, expr, accessor);
let (result, proof) = match (&res.result, &res.proof) {
(Some(result), Some(proof)) => (result, proof),
(None, None) => return,
_ => panic!("verification did not catch a proof/result mismatch"),
};

if res.proof.is_none() {
return;
}
let proof = res.proof.as_ref().unwrap();
// try changing the result
let mut res_p = res.clone();
res_p.result = Some(tampered_table(result));
assert!(res_p.verify(expr, accessor, &()).is_err());

// try changing MLE evaluations
for i in 0..proof.pcs_proof_evaluations.len() {
Expand Down Expand Up @@ -88,74 +82,68 @@ pub fn exercise_verification(
}
}

fn tamper_no_result(
res: &VerifiableQueryResult<InnerProductProof>,
expr: &(impl ProofPlan + Serialize),
accessor: &impl CommitmentAccessor<RistrettoPoint>,
) {
// add a result
let mut res_p = res.clone();
let cols: [Column<'_, Curve25519Scalar>; 1] = [Column::BigInt(&[0_i64; 0])];
res_p.provable_result = Some(ProvableQueryResult::new(0, &cols));
assert!(res_p.verify(expr, accessor, &()).is_err());

// add a proof
let mut res_p = res.clone();
let expr_p = EmptyTestQueryExpr {
length: 1,
..Default::default()
};
let column = vec![1_i64; 1];
let accessor_p = OwnedTableTestAccessor::<InnerProductProof>::new_from_table(
"sxt.test".parse().unwrap(),
owned_table([bigint("bogus_col", column)]),
0,
(),
);
let (proof, _result) = QueryProof::new(&expr_p, &accessor_p, &());
res_p.proof = Some(proof);
assert!(res_p.verify(expr, accessor, &()).is_err());
fn tampered_table<S: Scalar>(table: &OwnedTable<S>) -> OwnedTable<S> {
if table.num_columns() == 0 {
owned_table([bigint("col", [0; 0])])
} else if table.num_rows() == 0 {
append_single_row_to_table(table)
} else {
tamper_first_element_of_table(table)
}
}

fn tamper_empty_result(
res: &VerifiableQueryResult<InnerProductProof>,
expr: &(impl ProofPlan + Serialize),
accessor: &impl CommitmentAccessor<RistrettoPoint>,
) {
// try to add a result
let mut res_p = res.clone();
let cols: [Column<'_, Curve25519Scalar>; 1] = [Column::BigInt(&[123_i64])];
res_p.provable_result = Some(ProvableQueryResult::new(1, &cols));
assert!(res_p.verify(expr, accessor, &()).is_err());
fn append_single_row_to_table<S: Scalar>(table: &OwnedTable<S>) -> OwnedTable<S> {
OwnedTable::try_from_iter(
table
.inner_table()
.iter()
.map(|(name, col)| (*name, append_single_row_to_column(col))),
)
.expect("Failed to create table")
}

/// # Panics
///
/// Will panic if:
/// - `res.provable_result` is `None`, which leads to calling `unwrap()` on it in the subsequent
/// code and may cause an unexpected behavior.
/// - The assertion `assert!(res_p.verify(expr, accessor, &()).is_err())` fails, indicating that the
/// verification did not fail as expected after tampering.
fn tamper_result(
res: &VerifiableQueryResult<InnerProductProof>,
expr: &(impl ProofPlan + Serialize),
accessor: &impl CommitmentAccessor<RistrettoPoint>,
) {
if res.provable_result.is_none() {
tamper_no_result(res, expr, accessor);
return;
fn append_single_row_to_column<S: Scalar>(column: &OwnedColumn<S>) -> OwnedColumn<S> {
let mut column = column.clone();
match &mut column {
OwnedColumn::Boolean(col) => col.push(false),
OwnedColumn::TinyInt(col) => col.push(0),
OwnedColumn::SmallInt(col) => col.push(0),
OwnedColumn::Int(col) => col.push(0),
OwnedColumn::BigInt(col) | OwnedColumn::TimestampTZ(_, _, col) => col.push(0),
OwnedColumn::VarChar(col) => col.push(String::new()),
OwnedColumn::Int128(col) => col.push(0),
OwnedColumn::Decimal75(_, _, col) | OwnedColumn::Scalar(col) => col.push(S::ZERO),
}
let provable_res = res.provable_result.as_ref().unwrap();

if provable_res.table_length() == 0 {
tamper_empty_result(res, expr, accessor);
return;
column
}
fn tamper_first_element_of_table<S: Scalar>(table: &OwnedTable<S>) -> OwnedTable<S> {
OwnedTable::try_from_iter(
table
.inner_table()
.iter()
.enumerate()
.map(|(i, (name, col))| {
(
*name,
if i == 0 {
tamper_first_row_of_column(col)
} else {
col.clone()
},
)
}),
)
.expect("Failed to create table")
}
pub fn tamper_first_row_of_column<S: Scalar>(column: &OwnedColumn<S>) -> OwnedColumn<S> {
let mut column = column.clone();
match &mut column {
OwnedColumn::Boolean(col) => col[0] ^= true,
OwnedColumn::TinyInt(col) => col[0] += 1,
OwnedColumn::SmallInt(col) => col[0] += 1,
OwnedColumn::Int(col) => col[0] += 1,
OwnedColumn::BigInt(col) | OwnedColumn::TimestampTZ(_, _, col) => col[0] += 1,
OwnedColumn::VarChar(col) => col[0].push('1'),
OwnedColumn::Int128(col) => col[0] += 1,
OwnedColumn::Decimal75(_, _, col) | OwnedColumn::Scalar(col) => col[0] += S::ONE,
}

// try to change data
let mut res_p = res.clone();
let mut provable_res_p = provable_res.clone();
provable_res_p.data_mut()[0] += 1;
res_p.provable_result = Some(provable_res_p);
assert!(res_p.verify(expr, accessor, &()).is_err());
column
}

0 comments on commit b02a1d8

Please sign in to comment.