proof_of_sql/sql/proof/
verifiable_query_result.rs

1use super::{ProofPlan, QueryData, QueryProof, QueryResult};
2use crate::{
3    base::{
4        commitment::CommitmentEvaluationProof,
5        database::{CommitmentAccessor, DataAccessor, LiteralValue, OwnedTable},
6        proof::PlaceholderResult,
7    },
8    utils::log,
9};
10use serde::{Deserialize, Serialize};
11
12/// The result of an sql query along with a proof that the query is valid. The
13/// result and proof can be verified using commitments to database columns.
14///
15/// Note: the query result is stored in an intermediate form rather than the final form
16/// the end-user sees. The final form is obtained after verification. Using an
17/// intermediate form allows us to handle overflow and certain cases where the final
18/// result might use floating point numbers (e.g. `SELECT STDDEV(A) FROM T WHERE B = 0`).
19///
20/// Below we demonstrate typical usage of [`VerifiableQueryResult`] with pseudo-code.
21///
22/// Here we assume that a verifier only has access to the commitments of database columns. To
23/// process a query, the verifier forwards the query to an untrusted
24/// prover. The prover has full access to the database and constructs a [`VerifiableQueryResult`] that
25/// it sends back to the verifier. The verifier checks that the result is valid using its
26/// commitments, and constructs the finalized form of the query result.
27///
28/// ```ignore
29/// prover_process_query(database_accessor) {
30///       query <- receive_query_from_verifier()
31///
32///       verifiable_result <- VerifiableQueryResult::new(query, database_accessor)
33///             // When we construct VerifiableQueryResult from a query expression, we compute
34///             // both the result of the query in intermediate form and the proof of the result
35///             // at the same time.
36///
37///       send_to_verifier(verifiable_result)
38/// }
39///
40/// verifier_process_query(query, commitment_accessor) {
41///    verifiable_result <- send_query_to_prover(query)
42///
43///    verify_result <- verifiable_result.verify(query, commitment_accessor)
44///    if verify_result.is_error() {
45///         // The prover did something wrong. Perhaps the prover tried to tamper with the query
46///         // result or maybe its version of the database was out-of-sync with the verifier's
47///         // version.
48///         do_verification_error()
49///    }
50///
51///    query_result <- verify_result.query_result()
52///    if query_result.is_error() {
53///         // The prover processed the query correctly, but the query resulted in an error.
54///         // For example, perhaps the query added two 64-bit integer columns together that
55///         // resulted in an overflow.
56///         do_query_error()
57///    }
58///
59///    do_query_success(query_result)
60///         // The prover correctly processed a query and the query succeeded. Now, we can
61///         // proceed to use the result.
62/// }
63/// ```
64///
65/// Note: Because the class is deserialized from untrusted data, it
66/// cannot maintain any invariant on its data members; hence, they are
67/// all public so as to allow for easy manipulation for testing.
68#[derive(Clone, Serialize, Deserialize)]
69pub struct VerifiableQueryResult<CP: CommitmentEvaluationProof> {
70    /// The result of the query in intermediate form.
71    pub result: OwnedTable<CP::Scalar>,
72    /// The proof that the query result is valid.
73    pub proof: QueryProof<CP>,
74}
75
76impl<CP: CommitmentEvaluationProof> VerifiableQueryResult<CP> {
77    /// Form a `VerifiableQueryResult` from a query expression.
78    ///
79    /// This function both computes the result of a query and constructs a proof of the results
80    /// validity.
81    #[tracing::instrument(name = "VerifiableQueryResult::new", level = "info", skip_all)]
82    pub fn new(
83        expr: &(impl ProofPlan + Serialize),
84        accessor: &impl DataAccessor<CP::Scalar>,
85        setup: &CP::ProverPublicSetup<'_>,
86        params: &[LiteralValue],
87    ) -> PlaceholderResult<Self> {
88        log::log_memory_usage("Start");
89        let (proof, res) = QueryProof::new(expr, accessor, setup, params)?;
90        log::log_memory_usage("End");
91        Ok(Self { result: res, proof })
92    }
93
94    /// Verify a `VerifiableQueryResult`. Upon success, this function returns the finalized form of
95    /// the query result.
96    ///
97    /// Note: a verified result can still respresent an error (e.g. overflow), but it is a verified
98    /// error.
99    ///
100    /// Note: This does NOT transform the result!
101    #[tracing::instrument(name = "VerifiableQueryResult::verify", level = "info", skip_all)]
102    pub fn verify(
103        self,
104        expr: &(impl ProofPlan + Serialize),
105        accessor: &impl CommitmentAccessor<CP::Commitment>,
106        setup: &CP::VerifierPublicSetup<'_>,
107        params: &[LiteralValue],
108    ) -> QueryResult<CP::Scalar> {
109        log::log_memory_usage("Start");
110        let QueryData {
111            table,
112            verification_hash,
113        } = self
114            .proof
115            .verify(expr, accessor, self.result, setup, params)?;
116        Ok(QueryData {
117            table: table.try_coerce_with_fields(expr.get_column_result_fields())?,
118            verification_hash,
119        })
120    }
121}