From bb8644d117700ebb0c5b0aaf6181af083377e8ba Mon Sep 17 00:00:00 2001 From: Ian Joiner <14581281+iajoiner@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:32:22 -0400 Subject: [PATCH] feat: add `SxTClient` --- scripts/count-ethereum-core/src/main.rs | 42 ++++---- sdk/src/args.rs | 13 +++ sdk/src/client.rs | 129 ++++++++++++++++++++++++ sdk/src/lib.rs | 89 +--------------- sdk/src/main.rs | 7 +- 5 files changed, 169 insertions(+), 111 deletions(-) create mode 100644 sdk/src/client.rs diff --git a/scripts/count-ethereum-core/src/main.rs b/scripts/count-ethereum-core/src/main.rs index d644127..30b851d 100644 --- a/scripts/count-ethereum-core/src/main.rs +++ b/scripts/count-ethereum-core/src/main.rs @@ -1,8 +1,8 @@ use futures::StreamExt; use indexmap::IndexMap; use proof_of_sql::base::database::OwnedColumn; -use std::{env, fs::File, io::BufReader}; -use sxt_proof_of_sql_sdk::{query_and_verify, SdkArgs}; +use std::{env, fs::File, io::BufReader, sync::Arc}; +use sxt_proof_of_sql_sdk::SxTClient; #[allow(dead_code)] const ETHEREUM_CORE_COUNTS_FILE: &str = "ethereum-core-counts.json"; @@ -32,17 +32,14 @@ const ETHEREUM_CORE_TABLES: [&str; 21] = [ ]; async fn count_table( - table_ref: String, - base_args: SdkArgs, + client: &SxTClient, + table_ref: &str, ) -> Result> { - let query = format!("SELECT * FROM {table_ref}"); - let args = SdkArgs { - table_ref, - query, - ..base_args - }; - - let table = query_and_verify(&args).await?; + let uppercased_table_ref = table_ref.to_uppercase(); + let query = format!("SELECT * FROM {uppercased_table_ref}"); + let table = client + .query_and_verify(&query, &uppercased_table_ref) + .await?; assert_eq!(table.num_columns(), 1); assert_eq!(table.num_rows(), 1); @@ -74,23 +71,20 @@ async fn main() { env_logger::init(); dotenv::dotenv().unwrap(); - let base_args = SdkArgs { - prover_root_url: env::var("PROVER_ROOT_URL").unwrap_or("api.spaceandtime.dev".to_string()), - auth_root_url: env::var("AUTH_ROOT_URL").unwrap_or("api.spaceandtime.dev".to_string()), - substrate_node_url: env::var("SUBSTRATE_NODE_URL") - .unwrap_or("foo.bar.spaceandtime.dev".to_string()), - verifier_setup: env::var("VERIFIER_SETUP").unwrap_or("verifier_setup.bin".to_string()), - sxt_api_key: env::var("SXT_API_KEY").expect("SXT_API_KEY is required"), - query: "SELECT COUNT(*) FROM PLACEHOLDER".to_string(), - table_ref: "PLACEHOLDER".to_string(), - }; + let client = Arc::new(SxTClient::new( + env::var("PROVER_ROOT_URL").unwrap_or("api.spaceandtime.dev".to_string()), + env::var("AUTH_ROOT_URL").unwrap_or("api.spaceandtime.dev".to_string()), + env::var("SUBSTRATE_NODE_URL").unwrap_or("foo.bar.spaceandtime.dev".to_string()), + env::var("SXT_API_KEY").expect("SXT_API_KEY is required"), + env::var("VERIFIER_SETUP").unwrap_or("verifier_setup.bin".to_string()), + )); let current_counts: IndexMap = futures::stream::iter(ETHEREUM_CORE_TABLES) .filter_map(|table_ref| { - let base_args = base_args.clone(); + let client = client.clone(); async move { log::info!("querying count of {table_ref}"); - let count = count_table(table_ref.to_string().to_uppercase(), base_args) + let count = count_table(client.as_ref(), table_ref) .await .inspect_err(|e| log::error!("failed to query count for {table_ref}: {e}")) .ok()?; diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 8b8481c..f8da3aa 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -1,3 +1,4 @@ +use crate::SxTClient; use clap::Parser; /// Struct to define and parse command-line arguments for Proof of SQL Client. @@ -79,3 +80,15 @@ pub struct SdkArgs { )] pub verifier_setup: String, } + +impl From<&SdkArgs> for SxTClient { + fn from(args: &SdkArgs) -> Self { + Self::new( + args.prover_root_url.clone(), + args.auth_root_url.clone(), + args.substrate_node_url.clone(), + args.sxt_api_key.clone(), + args.verifier_setup.clone(), + ) + } +} diff --git a/sdk/src/client.rs b/sdk/src/client.rs new file mode 100644 index 0000000..c93920e --- /dev/null +++ b/sdk/src/client.rs @@ -0,0 +1,129 @@ +use crate::{get_access_token, query_commitments}; +use proof_of_sql::{ + base::database::{OwnedTable, TableRef}, + proof_primitive::dory::{ + DoryScalar, DynamicDoryCommitment, DynamicDoryEvaluationProof, VerifierSetup, + }, + sql::{parse::QueryExpr, proof::VerifiableQueryResult}, +}; +use prover::{ProverContextRange, ProverQuery, ProverResponse}; +use reqwest::Client; +use std::{collections::HashMap, path::Path}; + +mod prover { + tonic::include_proto!("sxt.core"); +} + +/// Space and Time (SxT) client +#[derive(Debug, Clone)] +pub struct SxTClient { + /// Root URL for the Prover service + /// + /// This URL is used for interacting with the prover service. + pub prover_root_url: String, + + /// Root URL for the Auth service + /// + /// Used for authentication requests. Generally the same as the Prover Root URL. + pub auth_root_url: String, + + /// URL for the Substrate node service + /// + /// Specifies the Substrate node endpoint used for accessing commitment data. + pub substrate_node_url: String, + + /// API Key for Space and Time (SxT) services + /// + /// The API key required for authorization with Space and Time services. + pub sxt_api_key: String, + + /// Path to the verifier setup binary file + /// + /// Specifies the path to the `verifier_setup.bin` file required for verification. + pub verifier_setup: String, +} + +impl SxTClient { + /// Create a new SxT client + pub fn new( + prover_root_url: String, + auth_root_url: String, + substrate_node_url: String, + sxt_api_key: String, + verifier_setup: String, + ) -> Self { + Self { + prover_root_url, + auth_root_url, + substrate_node_url, + sxt_api_key, + verifier_setup, + } + } + + /// Query and verify a SQL query + /// + /// Run a SQL query and verify the result using Dynamic Dory. + pub async fn query_and_verify( + &self, + query: &str, + table: &str, + ) -> Result, Box> { + // Parse table_ref into TableRef struct + let table_ref = TableRef::new(table.parse()?); + let schema = table_ref.schema_id(); + // Load verifier setup + let verifier_setup_path = Path::new(&self.verifier_setup); + let verifier_setup = VerifierSetup::load_from_file(verifier_setup_path)?; + // Accessor setup + let accessor = + query_commitments(&[table_ref.resource_id()], &self.substrate_node_url).await?; + // Parse the SQL query + let query_expr: QueryExpr = + QueryExpr::try_new(query.parse()?, schema, &accessor)?; + let proof_plan = query_expr.proof_expr(); + let serialized_proof_plan = flexbuffers::to_vec(proof_plan)?; + // Send the query to the prover + let mut query_context = HashMap::new(); + let commitment_range = accessor[&table_ref].range(); + query_context.insert( + table_ref.to_string().to_uppercase(), + ProverContextRange { + start: commitment_range.start as u64, + ends: vec![commitment_range.end as u64], + }, + ); + let prover_query = ProverQuery { + proof_plan: serialized_proof_plan, + query_context, + commitment_scheme: 1, + }; + let client = Client::new(); + let access_token = get_access_token(&self.sxt_api_key, &self.auth_root_url).await?; + let response = client + .post(format!("https://{}/v1/prove", &self.prover_root_url)) + .bearer_auth(&access_token) + .json(&prover_query) + .send() + .await? + .error_for_status()?; + let serialized_prover_response = response.text().await?; + let prover_response = serde_json::from_str::(&serialized_prover_response) + .map_err(|_e| { + format!( + "Failed to parse prover response: {}", + &serialized_prover_response + ) + })?; + let stringified_verifiable_result = prover_response.verifiable_result.clone(); + let verifiable_result: VerifiableQueryResult = + flexbuffers::from_slice(&stringified_verifiable_result)?; + // Verify the proof + let proof = verifiable_result.proof.unwrap(); + let serialized_result = verifiable_result.provable_result.unwrap(); + let owned_table_result = proof + .verify(proof_plan, &accessor, &serialized_result, &&verifier_setup)? + .table; + Ok(owned_table_result) + } +} diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 76e4767..e06f226 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -1,90 +1,9 @@ mod args; pub use args::SdkArgs; mod auth; +pub use auth::get_access_token; +mod client; +pub use client::SxTClient; mod substrate; +pub use substrate::query_commitments; mod sxt_chain_runtime; - -use proof_of_sql::{ - base::database::{OwnedTable, TableRef}, - proof_primitive::dory::{ - DoryScalar, DynamicDoryCommitment, DynamicDoryEvaluationProof, VerifierSetup, - }, - sql::{parse::QueryExpr, proof::VerifiableQueryResult}, -}; -use prover::{ProverContextRange, ProverQuery, ProverResponse}; -use reqwest::Client; -use std::{collections::HashMap, path::Path}; - -mod prover { - tonic::include_proto!("sxt.core"); -} - -/// Query and verify a SQL query -/// -/// Run a SQL query and verify the result using Dynamic Dory. -pub async fn query_and_verify( - args: &SdkArgs, -) -> Result, Box> { - // Parse table_ref into TableRef struct - let table_ref = TableRef::new(args.table_ref.parse()?); - let schema = table_ref.schema_id(); - // Load verifier setup - let verifier_setup_path = Path::new(&args.verifier_setup); - let verifier_setup = VerifierSetup::load_from_file(verifier_setup_path)?; - // Accessor setup - let accessor = - substrate::query_commitments(&[table_ref.resource_id()], &args.substrate_node_url).await?; - // Parse the SQL query - let query: QueryExpr = - QueryExpr::try_new(args.query.parse()?, schema, &accessor)?; - let proof_plan = query.proof_expr(); - let serialized_proof_plan = flexbuffers::to_vec(proof_plan)?; - // Send the query to the prover - let mut query_context = HashMap::new(); - let commitment_range = accessor[&table_ref].range(); - - query_context.insert( - table_ref.to_string().to_uppercase(), - ProverContextRange { - start: commitment_range.start as u64, - ends: vec![commitment_range.end as u64], - }, - ); - let prover_query = ProverQuery { - proof_plan: serialized_proof_plan, - query_context, - commitment_scheme: 1, - }; - let client = Client::new(); - let access_token = auth::get_access_token(&args.sxt_api_key, &args.auth_root_url).await?; - let response = client - .post(format!("https://{}/v1/prove", &args.prover_root_url)) - .bearer_auth(&access_token) - .json(&prover_query) - .send() - .await? - .error_for_status()?; - let serialized_prover_response = response.text().await?; - let prover_response = serde_json::from_str::(&serialized_prover_response) - .map_err(|_e| { - format!( - "Failed to parse prover response: {}", - &serialized_prover_response - ) - })?; - let stringified_verifiable_result = prover_response.verifiable_result.clone(); - let verifiable_result: VerifiableQueryResult = - flexbuffers::from_slice(&stringified_verifiable_result)?; - // Verify the proof - let proof = verifiable_result.proof.unwrap(); - let serialized_result = verifiable_result.provable_result.unwrap(); - let owned_table_result = proof - .verify( - query.proof_expr(), - &accessor, - &serialized_result, - &&verifier_setup, - )? - .table; - Ok(owned_table_result) -} diff --git a/sdk/src/main.rs b/sdk/src/main.rs index a72c747..e4aab0e 100644 --- a/sdk/src/main.rs +++ b/sdk/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; use dotenv::dotenv; -use sxt_proof_of_sql_sdk::{query_and_verify, SdkArgs}; +use sxt_proof_of_sql_sdk::{SdkArgs, SxTClient}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -9,9 +9,12 @@ async fn main() -> Result<(), Box> { // Parse command-line arguments let args = SdkArgs::parse(); + let client: SxTClient = (&args).into(); // Execute the query and verify the result - let result = query_and_verify(&args).await?; + let result = client + .query_and_verify(&args.query, &args.table_ref) + .await?; // Print the result of the query println!("Query result: {:?}", result);