diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 1c3ecadba..50d45e760 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -128,6 +128,8 @@ jobs: run: cargo run --example avocado-prices - name: Run plastics example run: cargo run --example plastics + - name: Run sushi example + run: cargo run --example sushi - name: Run posql_db example (With Blitzar) run: bash crates/proof-of-sql/examples/posql_db/run_example.sh - name: Run posql_db example (Without Blitzar) diff --git a/crates/proof-of-sql/Cargo.toml b/crates/proof-of-sql/Cargo.toml index f11121a68..cfa73da37 100644 --- a/crates/proof-of-sql/Cargo.toml +++ b/crates/proof-of-sql/Cargo.toml @@ -123,6 +123,10 @@ required-features = [ "arrow" ] name = "avocado-prices" required-features = [ "arrow" ] +[[example]] +name = "sushi" +required-features = [ "arrow" ] + [[bench]] name = "posql_benches" harness = false diff --git a/crates/proof-of-sql/examples/sushi/fish.csv b/crates/proof-of-sql/examples/sushi/fish.csv new file mode 100644 index 000000000..e0a14ebc0 --- /dev/null +++ b/crates/proof-of-sql/examples/sushi/fish.csv @@ -0,0 +1,13 @@ +nameEn,nameJa,kindEn,kindJa,pricePerPound +Tuna,Maguro,Lean Red Meat,Akami,25 +Tuna,Maguro,Medium Fat Red Meat,Toro,65 +Tuna,Maguro,Fatty Red Meat,Otoro,115 +Bonito,Katsuo,Red Meat,Akami,20 +Yellowtail,Hamachi,Red Meat,Akami,27 +Salmon,Salmon,White Fish,Shiromi,17 +Sea Bream,Tai,White Fish,Shiromi,32 +Sea Bass,Suzuki,White Fish,Shiromi,28 +Mackerel,Aji,Silver Skinned,Hikarimono,14 +Sardine,Iwashi,Silver Skinned,Hikarimono,11 +Scallops,Hotate,Shellfish,Kai,26 +Ark-shell clams,Akagai,Shellfish,Kai,29 diff --git a/crates/proof-of-sql/examples/sushi/main.rs b/crates/proof-of-sql/examples/sushi/main.rs new file mode 100644 index 000000000..0c7f89545 --- /dev/null +++ b/crates/proof-of-sql/examples/sushi/main.rs @@ -0,0 +1,141 @@ +//! This is an non-interactive example of using Proof of SQL with some sushi related datasets. +//! To run this, use `cargo run --example sushi`. + +//! NOTE: If this doesn't work because you do not have the appropriate GPU drivers installed, +//! you can run `cargo run --release --example sushi --no-default-features --features="arrow cpu-perf"` instead. It will be slower for proof generation. +use arrow::datatypes::SchemaRef; +use arrow_csv::{infer_schema_from_files, ReaderBuilder}; +use proof_of_sql::{ + base::database::{OwnedTable, OwnedTableTestAccessor, TestAccessor}, + proof_primitive::dory::{ + DynamicDoryCommitment, DynamicDoryEvaluationProof, ProverSetup, PublicParameters, + VerifierSetup, + }, + sql::{parse::QueryExpr, proof::QueryProof}, +}; +use rand::{rngs::StdRng, SeedableRng}; +use std::{fs::File, time::Instant}; + +const DORY_SETUP_MAX_NU: usize = 8; +const DORY_SEED: [u8; 32] = *b"sushi-is-the-best-food-available"; + +/// # Panics +/// Will panic if the query does not parse or the proof fails to verify. +fn prove_and_verify_query( + sql: &str, + accessor: &OwnedTableTestAccessor, + prover_setup: &ProverSetup, + verifier_setup: &VerifierSetup, +) { + // Parse the query: + println!("Parsing the query: {sql}..."); + let now = Instant::now(); + let query_plan = QueryExpr::::try_new( + sql.parse().unwrap(), + "sushi".parse().unwrap(), + accessor, + ) + .unwrap(); + println!("Done in {} ms.", now.elapsed().as_secs_f64() * 1000.); + // Generate the proof and result: + print!("Generating proof..."); + let now = Instant::now(); + let (proof, provable_result) = QueryProof::::new( + query_plan.proof_expr(), + accessor, + &prover_setup, + ); + println!("Done in {} ms.", now.elapsed().as_secs_f64() * 1000.); + // Verify the result with the proof: + print!("Verifying proof..."); + let now = Instant::now(); + let result = proof + .verify( + query_plan.proof_expr(), + accessor, + &provable_result, + &verifier_setup, + ) + .unwrap(); + println!("Verified in {} ms.", now.elapsed().as_secs_f64() * 1000.); + // Display the result + println!("Query Result:"); + println!("{:?}", result.table); +} + +fn main() { + let mut rng = StdRng::from_seed(DORY_SEED); + let public_parameters = PublicParameters::rand(DORY_SETUP_MAX_NU, &mut rng); + let prover_setup = ProverSetup::from(&public_parameters); + let verifier_setup = VerifierSetup::from(&public_parameters); + + let filename = "./crates/proof-of-sql/examples/sushi/fish.csv"; + let fish_batch = ReaderBuilder::new(SchemaRef::new( + infer_schema_from_files(&[filename.to_string()], b',', None, true).unwrap(), + )) + .with_header(true) + .build(File::open(filename).unwrap()) + .unwrap() + .next() + .unwrap() + .unwrap(); + println!("{fish_batch:?}"); + + // Load the table into an "Accessor" so that the prover and verifier can access the data/commitments. + let mut accessor = + OwnedTableTestAccessor::::new_empty_with_setup(&prover_setup); + accessor.add_table( + "sushi.fish".parse().unwrap(), + OwnedTable::try_from(fish_batch).unwrap(), + 0, + ); + + prove_and_verify_query( + "SELECT * FROM fish", + &accessor, + &prover_setup, + &verifier_setup, + ); + + prove_and_verify_query( + "SELECT COUNT(*) FROM fish WHERE nameEn = 'Tuna'", + &accessor, + &prover_setup, + &verifier_setup, + ); + + prove_and_verify_query( + "SELECT kindEn FROM fish WHERE kindJa = 'Otoro'", + &accessor, + &prover_setup, + &verifier_setup, + ); + + prove_and_verify_query( + "SELECT kindEn FROM fish WHERE kindJa = 'Otoro'", + &accessor, + &prover_setup, + &verifier_setup, + ); + + prove_and_verify_query( + "SELECT * FROM fish WHERE pricePerPound > 25 AND pricePerPound < 75", + &accessor, + &prover_setup, + &verifier_setup, + ); + + prove_and_verify_query( + "SELECT kindJa, COUNT(*) FROM fish GROUP BY kindJa", + &accessor, + &prover_setup, + &verifier_setup, + ); + + prove_and_verify_query( + "SELECT kindJa, pricePerPound FROM fish WHERE nameEn = 'Tuna' ORDER BY pricePerPound ASC", + &accessor, + &prover_setup, + &verifier_setup, + ); +}