Skip to content

Commit

Permalink
Update dependencies; move deserialization from zkVerify
Browse files Browse the repository at this point in the history
  • Loading branch information
tarassh committed Aug 28, 2024
1 parent a9fd139 commit a4eb7d4
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 46 deletions.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ edition = "2021"
ark-serialize = { version = "0.4.0", default-features = false }
bincode = "1"
blitzar = { version = "3.1.0", default-features = false, optional = true }
indexmap = "2.4.0"
curve25519-dalek = { version = "4", optional = true }
proof-of-sql = { version = "0.15.0", default-features = false }
proof-of-sql-parser = { version = "0.15.0" }
proof-of-sql = { version = "0.16.0", default-features = false }
proof-of-sql-parser = { version = "0.16.0" }

[dev-dependencies]
proof-of-sql = { version = "0.15.0", default-features = false, features = ["test"] }
proof-of-sql = { version = "0.16.0", default-features = false, features = ["test"] }

[features]
inner-product = ["dep:blitzar", "dep:curve25519-dalek", "proof-of-sql/blitzar"]
22 changes: 20 additions & 2 deletions src/dory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ use crate::{
/// # Returns
///
/// * `Result<(), VerifyError>` - Ok(()) if the proof is valid, or an error if verification fails.
pub fn verify_dory_proof<const N: usize>(
pub fn verify_dory_proof(
proof: &DoryProof,
pubs: &DoryPublicInput,
vk: &VerificationKey<N>,
vk: &VerificationKey,
) -> Result<(), VerifyError> {
verify_proof(
proof.clone().into_dory(),
Expand All @@ -45,3 +45,21 @@ pub fn verify_dory_proof<const N: usize>(
&vk.into_dory(),
)
}

pub fn verify(
proof: &[u8],
public_input: &[u8],
verification_key: &[u8],
) -> Result<(), VerifyError> {
let proof = DoryProof::try_from(proof)?;
let pubs = DoryPublicInput::try_from(public_input)?;
let vk = VerificationKey::try_from(verification_key)?;

verify_proof(
proof.clone().into_dory(),
pubs.expr(),
pubs.commitments(),
pubs.query_data(),
&vk.into_dory(),
)
}
282 changes: 269 additions & 13 deletions src/pubs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,72 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use indexmap::IndexMap;
use proof_of_sql::{
base::commitment::QueryCommitments,
base::{
commitment::QueryCommitments,
database::{ColumnType, OwnedColumn, OwnedTable},
math::decimal::Precision,
},
proof_primitive::dory::{DoryCommitment, DoryScalar},
sql::{ast::ProofPlan, parse::QueryExpr, proof::QueryData},
sql::{ast::ProofPlan, proof::QueryData},
};
use proof_of_sql_parser::{
posql_time::{PoSQLTimeUnit, PoSQLTimeZone},
Identifier,
};

use crate::VerifyError;

/// Represents the public input for a Dory proof.
///
/// This structure encapsulates the necessary public information required
/// for verifying a Dory proof, including the proof expression, commitments,
/// and query data.
///
/// # Type Parameters
///
/// * `'a` - The lifetime of the referenced `ProofPlan`.
pub struct DoryPublicInput<'a> {
expr: &'a ProofPlan<DoryCommitment>,
pub struct DoryPublicInput {
expr: ProofPlan<DoryCommitment>,
commitments: QueryCommitments<DoryCommitment>,
query_data: QueryData<DoryScalar>,
}

impl<'a> DoryPublicInput<'a> {
impl TryFrom<&[u8]> for DoryPublicInput {
type Error = VerifyError;

fn try_from(bytes: &[u8]) -> Result<Self, VerifyError> {
DoryPublicInput::from_bytes(bytes).map_err(|_| VerifyError::InvalidInput)
}
}

impl DoryPublicInput {
/// Creates a new `DoryPublicInput` instance.
///
/// # Arguments
///
/// * `query_expr` - A reference to the query expression.
/// * `expr` - The query plan for proving a query.
/// * `commitments` - The query commitments.
/// * `query_data` - The query data.
///
/// # Returns
///
/// A new `DoryPublicInput` instance.
pub fn new(
query_expr: &'a QueryExpr<DoryCommitment>,
expr: &ProofPlan<DoryCommitment>,
commitments: QueryCommitments<DoryCommitment>,
query_data: QueryData<DoryScalar>,
) -> Self {
// Copy trait is not implemented for ProofPlan, so we serialize and deserialize
let bytes = bincode::serialize(&expr).unwrap();
let expr: ProofPlan<DoryCommitment> = bincode::deserialize(&bytes).unwrap();
Self {
expr: query_expr.proof_expr(),
expr,
commitments,
query_data,
}
}

/// Returns a reference to the proof expression.
pub fn expr(&self) -> &ProofPlan<DoryCommitment> {
self.expr
&self.expr
}

/// Returns a reference to the query commitments.
Expand All @@ -72,4 +90,242 @@ impl<'a> DoryPublicInput<'a> {
pub fn query_data(&self) -> &QueryData<DoryScalar> {
&self.query_data
}

/// Converts the public input into a byte array.
pub fn into_bytes(&self) -> Result<Vec<u8>, VerifyError> {
let mut expr_bytes = Vec::new();

// Serialize the expression
bincode::serialize_into(&mut expr_bytes, &self.expr).unwrap();

// Serialize the commitments
bincode::serialize_into(&mut expr_bytes, &self.commitments).unwrap();

// Serialize the table data
let table = self.query_data.table.inner_table();

// usize is serialized as u32, as usize is platform dependent
bincode::serialize_into(&mut expr_bytes, &(table.len() as u32)).unwrap();

for (k, v) in table {
bincode::serialize_into(&mut expr_bytes, k).unwrap();
bincode::serialize_into(&mut expr_bytes, &v.column_type()).unwrap();

match v {
OwnedColumn::Boolean(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::SmallInt(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::Int(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::BigInt(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::VarChar(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::Int128(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::Decimal75(precision, scale, v) => {
bincode::serialize_into(&mut expr_bytes, precision).unwrap();
bincode::serialize_into(&mut expr_bytes, scale).unwrap();
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::Scalar(v) => {
bincode::serialize_into(&mut expr_bytes, v).unwrap();
}
OwnedColumn::TimestampTZ(unit, zone, vv) => {
bincode::serialize_into(&mut expr_bytes, unit).unwrap();
bincode::serialize_into(&mut expr_bytes, zone).unwrap();
bincode::serialize_into(&mut expr_bytes, vv).unwrap();
},
&_ => {
return Err(VerifyError::InvalidInput);
}
}
}

bincode::serialize_into(&mut expr_bytes, &self.query_data.verification_hash).unwrap();

Ok(expr_bytes)
}

/// Converts a byte array into a `DoryPublicInput` instance.
fn from_bytes(bytes: &[u8]) -> Result<Self, bincode::Error> {
let mut cursor = std::io::Cursor::new(bytes);

// Deserialize the expression
let expr: ProofPlan<DoryCommitment> = bincode::deserialize_from(&mut cursor)?;

// Deserialize the commitments
let commitments: QueryCommitments<DoryCommitment> = bincode::deserialize_from(&mut cursor)?;

let table_len: u32 = bincode::deserialize_from(&mut cursor)?;

// Deserialize the table data
let mut table = IndexMap::<Identifier, OwnedColumn<_>>::new();
while cursor.position() < bytes.len() as u64 && table.len() < table_len as usize {
let k: String = bincode::deserialize_from(&mut cursor)?;
let column_type: ColumnType = bincode::deserialize_from(&mut cursor)?;
let identifier =
Identifier::try_new(k).map_err(|e| bincode::ErrorKind::Custom(e.to_string()))?;

let column: OwnedColumn<DoryScalar> = match column_type {
ColumnType::Boolean => {
let v: Vec<bool> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::Boolean(v)
}
ColumnType::SmallInt => {
let v: Vec<i16> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::SmallInt(v)
}
ColumnType::Int => {
let v: Vec<i32> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::Int(v)
}
ColumnType::BigInt => {
let v: Vec<i64> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::BigInt(v)
}
ColumnType::VarChar => {
let v: Vec<String> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::VarChar(v)
}
ColumnType::Int128 => {
let v: Vec<i128> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::Int128(v)
}
ColumnType::Decimal75(_, _) => {
let precision: Precision = bincode::deserialize_from(&mut cursor)?;
let scale: i8 = bincode::deserialize_from(&mut cursor)?;
let v: Vec<DoryScalar> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::Decimal75(precision, scale, v)
}
ColumnType::Scalar => {
let v: Vec<DoryScalar> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::Scalar(v)
}
ColumnType::TimestampTZ(_, _) => {
let unit: PoSQLTimeUnit = bincode::deserialize_from(&mut cursor)?;
let zone: PoSQLTimeZone = bincode::deserialize_from(&mut cursor)?;
let vv: Vec<i64> = bincode::deserialize_from(&mut cursor)?;
OwnedColumn::TimestampTZ(unit, zone, vv)
}
};

table.insert(identifier, column);
}

let verification_hash: [u8; 32] = bincode::deserialize_from(&mut cursor)?;
let query_data = QueryData {
table: OwnedTable::try_new(table)
.map_err(|e| bincode::ErrorKind::Custom(e.to_string()))?,
verification_hash,
};

Ok(DoryPublicInput {
expr,
commitments,
query_data,
})
}
}

#[cfg(test)]
mod test {

use proof_of_sql::sql::proof::ProofExecutionPlan;
use proof_of_sql::{
base::{
commitment::{Commitment, CommitmentEvaluationProof, QueryCommitmentsExt},
database::{
owned_table_utility::*, CommitmentAccessor, OwnedTableTestAccessor, SchemaAccessor,
TestAccessor,
},
},
proof_primitive::dory::{
test_rng, DoryEvaluationProof, DoryProverPublicSetup, ProverSetup, PublicParameters,
},
sql::{parse::QueryExpr, proof::VerifiableQueryResult},
};

use crate::{DoryProof, VerificationKey};

use super::*;

/// Computes query commitments for a given query expression and accessor.
fn compute_query_commitments<C: Commitment>(
query_expr: &QueryExpr<C>,
accessor: &(impl CommitmentAccessor<C> + SchemaAccessor),
) -> QueryCommitments<C> {
let columns = query_expr.proof_expr().get_column_references();
QueryCommitments::from_accessor_with_max_bounds(columns, accessor)
}

/// Builds a test accessor with sample data.
fn build_accessor<T: CommitmentEvaluationProof>(
setup: <T as CommitmentEvaluationProof>::ProverPublicSetup<'_>,
) -> OwnedTableTestAccessor<T> {
let mut accessor = OwnedTableTestAccessor::<T>::new_empty_with_setup(setup);
accessor.add_table(
"sxt.table".parse().unwrap(),
owned_table([
bigint("a", [1, 2, 3, 2]),
varchar("b", ["hi", "hello", "there", "world"]),
]),
0,
);
accessor
}

/// Builds a sample query for testing.
fn build_query<T: Commitment>(accessor: &impl SchemaAccessor) -> QueryExpr<T> {
QueryExpr::try_new(
"SELECT b FROM table WHERE a = 2".parse().unwrap(),
"sxt".parse().unwrap(),
accessor,
)
.unwrap()
}

#[test]
fn test_dory_public_input() {
// Initialize setup
let public_parameters = PublicParameters::rand(6, &mut test_rng());
let ps = ProverSetup::from(&public_parameters);
let prover_setup = DoryProverPublicSetup::new(&ps, 4);
let vk = VerificationKey::new(&public_parameters, 4);

// Build table accessor and query
let accessor = build_accessor::<DoryEvaluationProof>(prover_setup);
let query = build_query(&accessor);

// Generate proof
let proof = VerifiableQueryResult::<DoryEvaluationProof>::new(
query.proof_expr(),
&accessor,
&prover_setup,
);

// Get query data and commitments
let query_data = proof
.verify(query.proof_expr(), &accessor, &vk.into_dory())
.unwrap();
let query_commitments = compute_query_commitments(&query, &accessor);

// Verify proof
let pubs = DoryPublicInput::new(query.proof_expr(), query_commitments, query_data);

let bytes = pubs.into_bytes().unwrap();

let pubs = DoryPublicInput::try_from(&bytes[..]).unwrap();
let proof = DoryProof::new(proof);
let result = crate::verify_dory_proof(&proof, &pubs, &vk);

assert!(result.is_ok());
}
}
Loading

0 comments on commit a4eb7d4

Please sign in to comment.