diff --git a/Cargo.toml b/Cargo.toml index 0ff1aa9..73003f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/dory.rs b/src/dory.rs index 1855f54..213af4f 100644 --- a/src/dory.rs +++ b/src/dory.rs @@ -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( +pub fn verify_dory_proof( proof: &DoryProof, pubs: &DoryPublicInput, - vk: &VerificationKey, + vk: &VerificationKey, ) -> Result<(), VerifyError> { verify_proof( proof.clone().into_dory(), @@ -45,3 +45,21 @@ pub fn verify_dory_proof( &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(), + ) +} \ No newline at end of file diff --git a/src/pubs.rs b/src/pubs.rs index 6fc96f2..2d10697 100644 --- a/src/pubs.rs +++ b/src/pubs.rs @@ -13,33 +13,48 @@ // 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, +pub struct DoryPublicInput { + expr: ProofPlan, commitments: QueryCommitments, query_data: QueryData, } -impl<'a> DoryPublicInput<'a> { +impl TryFrom<&[u8]> for DoryPublicInput { + type Error = VerifyError; + + fn try_from(bytes: &[u8]) -> Result { + 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. /// @@ -47,12 +62,15 @@ impl<'a> DoryPublicInput<'a> { /// /// A new `DoryPublicInput` instance. pub fn new( - query_expr: &'a QueryExpr, + expr: &ProofPlan, commitments: QueryCommitments, query_data: QueryData, ) -> Self { + // Copy trait is not implemented for ProofPlan, so we serialize and deserialize + let bytes = bincode::serialize(&expr).unwrap(); + let expr: ProofPlan = bincode::deserialize(&bytes).unwrap(); Self { - expr: query_expr.proof_expr(), + expr, commitments, query_data, } @@ -60,7 +78,7 @@ impl<'a> DoryPublicInput<'a> { /// Returns a reference to the proof expression. pub fn expr(&self) -> &ProofPlan { - self.expr + &self.expr } /// Returns a reference to the query commitments. @@ -72,4 +90,242 @@ impl<'a> DoryPublicInput<'a> { pub fn query_data(&self) -> &QueryData { &self.query_data } + + /// Converts the public input into a byte array. + pub fn into_bytes(&self) -> Result, 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 { + let mut cursor = std::io::Cursor::new(bytes); + + // Deserialize the expression + let expr: ProofPlan = bincode::deserialize_from(&mut cursor)?; + + // Deserialize the commitments + let commitments: QueryCommitments = bincode::deserialize_from(&mut cursor)?; + + let table_len: u32 = bincode::deserialize_from(&mut cursor)?; + + // Deserialize the table data + let mut table = IndexMap::>::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 = match column_type { + ColumnType::Boolean => { + let v: Vec = bincode::deserialize_from(&mut cursor)?; + OwnedColumn::Boolean(v) + } + ColumnType::SmallInt => { + let v: Vec = bincode::deserialize_from(&mut cursor)?; + OwnedColumn::SmallInt(v) + } + ColumnType::Int => { + let v: Vec = bincode::deserialize_from(&mut cursor)?; + OwnedColumn::Int(v) + } + ColumnType::BigInt => { + let v: Vec = bincode::deserialize_from(&mut cursor)?; + OwnedColumn::BigInt(v) + } + ColumnType::VarChar => { + let v: Vec = bincode::deserialize_from(&mut cursor)?; + OwnedColumn::VarChar(v) + } + ColumnType::Int128 => { + let v: Vec = 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 = bincode::deserialize_from(&mut cursor)?; + OwnedColumn::Decimal75(precision, scale, v) + } + ColumnType::Scalar => { + let v: Vec = 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 = 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( + query_expr: &QueryExpr, + accessor: &(impl CommitmentAccessor + SchemaAccessor), + ) -> QueryCommitments { + 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( + setup: ::ProverPublicSetup<'_>, + ) -> OwnedTableTestAccessor { + let mut accessor = OwnedTableTestAccessor::::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(accessor: &impl SchemaAccessor) -> QueryExpr { + 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::(prover_setup); + let query = build_query(&accessor); + + // Generate proof + let proof = VerifiableQueryResult::::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()); + } } diff --git a/src/verification_key.rs b/src/verification_key.rs index 7f1e173..08cfcde 100644 --- a/src/verification_key.rs +++ b/src/verification_key.rs @@ -24,13 +24,12 @@ use crate::VerifyError; /// /// This structure wraps a `VerifierSetup` and provides methods for /// creating, deserializing, and converting the verification key. -/// -/// # Type Parameters -/// -/// * `N` - A const generic parameter representing the size of the verification key. -pub struct VerificationKey(VerifierSetup); +pub struct VerificationKey { + setup: VerifierSetup, + max_nu: usize, +} -impl TryFrom<&[u8]> for VerificationKey { +impl TryFrom<&[u8]> for VerificationKey { type Error = VerifyError; /// Attempts to create a VerificationKey from a byte slice. @@ -49,15 +48,12 @@ impl TryFrom<&[u8]> for VerificationKey { // read last usize from the buffer as max_nu is the last field in the struct, and check if it matches N // max_nu is not accessible from the VerifierSetup struct, so we need to check it from the buffer let max_nu = slice_to_usize(&value[value.len() - std::mem::size_of::()..]); - if max_nu != N { - return Err(VerifyError::InvalidVerificationKey); - } - Ok(Self(setup)) + Ok(Self { setup, max_nu }) } } -impl VerificationKey { +impl VerificationKey { /// Creates a new VerificationKey from PublicParameters. /// /// # Arguments @@ -67,8 +63,11 @@ impl VerificationKey { /// # Returns /// /// A new VerificationKey instance. - pub fn new(params: &PublicParameters) -> Self { - Self(VerifierSetup::from(params)) + pub fn new(params: &PublicParameters, max_nu: usize) -> Self { + Self { + setup: VerifierSetup::from(params), + max_nu, + } } /// Converts the VerificationKey into a DoryVerifierPublicSetup. @@ -77,7 +76,7 @@ impl VerificationKey { /// /// A DoryVerifierPublicSetup instance. pub fn into_dory(&self) -> DoryVerifierPublicSetup<'_> { - DoryVerifierPublicSetup::new(&self.0, N) + DoryVerifierPublicSetup::new(&self.setup, self.max_nu) } } @@ -112,7 +111,7 @@ mod test { let mut writer = Vec::new(); vs.serialize_compressed(&mut writer).unwrap(); - let verification_key = VerificationKey::<4>::try_from(writer.as_ref()).unwrap(); + let verification_key = VerificationKey::try_from(writer.as_ref()).unwrap(); let dory_key = verification_key.into_dory(); assert_eq!(dory_key.verifier_setup(), &vs); @@ -125,7 +124,7 @@ mod test { let mut writer = Vec::new(); vs.serialize_compressed(&mut writer).unwrap(); - let verification_key = VerificationKey::<4>::try_from(&writer[..writer.len() - 1]); + let verification_key = VerificationKey::try_from(&writer[..writer.len() - 1]); assert!(verification_key.is_err()); } } diff --git a/src/verify_generic.rs b/src/verify_generic.rs index 8367b8a..a80d81e 100644 --- a/src/verify_generic.rs +++ b/src/verify_generic.rs @@ -14,7 +14,7 @@ // limitations under the License. use proof_of_sql::base::commitment::CommitmentEvaluationProof; -use proof_of_sql::sql::proof::ProofExpr; +use proof_of_sql::sql::proof::ProofExecutionPlan; use proof_of_sql::{ base::commitment::QueryCommitments, sql::{ diff --git a/tests/integration.rs b/tests/integration.rs index 628c4d5..bda30c9 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -14,12 +14,13 @@ // limitations under the License. use proof_of_sql::base::{commitment::QueryCommitments, database::CommitmentAccessor}; +use proof_of_sql::sql::proof::ProofExecutionPlan; pub use proof_of_sql::{ base::{ commitment::{Commitment, CommitmentEvaluationProof, QueryCommitmentsExt}, database::{owned_table_utility::*, OwnedTableTestAccessor, SchemaAccessor, TestAccessor}, }, - sql::{parse::QueryExpr, proof::ProofExpr, proof::VerifiableQueryResult}, + sql::{parse::QueryExpr, proof::VerifiableQueryResult}, }; // Helper functions for setting up test data and queries @@ -180,7 +181,7 @@ mod dory { ); // Get query data and commitments - let vk = VerificationKey::<4>::new(&public_parameters); + let vk = VerificationKey::new(&public_parameters, 4); let query_data = proof .verify(query.proof_expr(), &accessor, &vk.into_dory()) .unwrap(); @@ -188,7 +189,7 @@ mod dory { // Verify proof let proof = DoryProof::new(proof); - let pubs = DoryPublicInput::new(&query, query_commitments, query_data); + let pubs = DoryPublicInput::new(query.proof_expr(), query_commitments, query_data); let result = proof_of_sql_verifier::verify_dory_proof(&proof, &pubs, &vk); assert!(result.is_ok()); @@ -212,7 +213,7 @@ mod dory { &prover_setup, ); - let vk = VerificationKey::<4>::new(&public_parameters); + let vk = VerificationKey::new(&public_parameters, 4); let query_data = proof .verify(non_existant_query.proof_expr(), &accessor, &vk.into_dory()) @@ -220,7 +221,11 @@ mod dory { let query_commitments = compute_query_commitments(&non_existant_query, &accessor); let dory_proof = DoryProof::new(proof); - let pubs = DoryPublicInput::new(&non_existant_query, query_commitments, query_data); + let pubs = DoryPublicInput::new( + non_existant_query.proof_expr(), + query_commitments, + query_data, + ); let result = proof_of_sql_verifier::verify_dory_proof(&dory_proof, &pubs, &vk); assert!(result.is_ok()); @@ -246,14 +251,14 @@ mod dory { ); // Get query data and commitments - let vk = VerificationKey::<4>::new(&public_parameters); + let vk = VerificationKey::new(&public_parameters, 4); let query_data = proof .verify(query.proof_expr(), &accessor, &vk.into_dory()) .unwrap(); let no_commitments = QueryCommitments::new(); let proof = DoryProof::new(proof); - let pubs = DoryPublicInput::new(&query, no_commitments, query_data); + let pubs = DoryPublicInput::new(query.proof_expr(), no_commitments, query_data); let result = proof_of_sql_verifier::verify_dory_proof(&proof, &pubs, &vk); assert!(result.is_err()); @@ -279,7 +284,7 @@ mod dory { ); // Get query data and commitments - let vk = VerificationKey::<4>::new(&public_parameters); + let vk = VerificationKey::new(&public_parameters, 4); let query_data = proof .verify(query.proof_expr(), &accessor, &vk.into_dory()) .unwrap(); @@ -291,7 +296,7 @@ mod dory { // Verify proof let proof = DoryProof::new(proof); - let pubs = DoryPublicInput::new(&query, altered_query_commitments, query_data); + let pubs = DoryPublicInput::new(query.proof_expr(), altered_query_commitments, query_data); let result = proof_of_sql_verifier::verify_dory_proof(&proof, &pubs, &vk); assert!(result.is_err()); @@ -319,7 +324,7 @@ mod dory { ); // Get the result - let vk = VerificationKey::<4>::new(&public_parameters); + let vk = VerificationKey::new(&public_parameters, 4); let query_data = proof .verify(query.proof_expr(), &accessor, &vk.into_dory()) .unwrap(); @@ -328,7 +333,7 @@ mod dory { let query_commitments = compute_query_commitments(&alient_query, &alien_accessor); let proof = DoryProof::new(proof); - let pubs = DoryPublicInput::new(&query, query_commitments, query_data); + let pubs = DoryPublicInput::new(query.proof_expr(), query_commitments, query_data); let result = proof_of_sql_verifier::verify_dory_proof(&proof, &pubs, &vk); assert!(result.is_err());