Skip to main content

drive/query/drive_document_sum_query/
execute_point_lookup.rs

1//! Point-lookup executor for sum queries. Parallels count's
2//! `execute_point_lookup.rs`.
3//!
4//! Two entry points: `execute_no_proof` (sum the matched value-trees
5//! via `grove_get_path_query`) and `execute_point_lookup_sum_with_proof`
6//! (proof bytes via `grove.get_proved_path_query`). Verifier side:
7//! `GroveDb::verify_query` + `sum_value_or_default()` extraction on
8//! each verified SumTree element.
9
10use super::{DriveDocumentSumQuery, SumEntry};
11use crate::drive::Drive;
12use crate::error::query::QuerySyntaxError;
13use crate::error::Error;
14use dpp::version::PlatformVersion;
15use grovedb::query_result_type::{QueryResultElement, QueryResultType};
16use grovedb::TransactionArg;
17use grovedb_costs::CostContext;
18
19impl DriveDocumentSumQuery<'_> {
20    /// Executes the sum query without generating a proof.
21    ///
22    /// Returns the total sum as a single `SumEntry` with empty `key`
23    /// (the unified-sum Total shape).
24    ///
25    /// Mirror of count's `execute_no_proof` — runs through the same
26    /// [`Self::point_lookup_sum_path_query`] builder the prove path
27    /// uses, then runs `grove.query` to fetch the matched SumTree
28    /// elements and sums their `sum_value_or_default()`.
29    pub fn execute_no_proof(
30        &self,
31        drive: &Drive,
32        transaction: TransactionArg,
33        platform_version: &PlatformVersion,
34    ) -> Result<Vec<SumEntry>, Error> {
35        let drive_version = &platform_version.drive;
36        let path_query = self.point_lookup_sum_path_query(platform_version)?;
37        let mut drive_operations = vec![];
38        let (results, _) = drive.grove_get_path_query(
39            &path_query,
40            transaction,
41            QueryResultType::QueryElementResultType,
42            &mut drive_operations,
43            drive_version,
44        )?;
45        // Sum across emitted SumTree elements:
46        // - Equal-only: 0 or 1 element (0 when the branch is absent).
47        // - In at any position: one element per In branch that has at
48        //   least one doc; missing branches contribute 0.
49        //
50        // Use `checked_add` so an overflowed aggregate fails
51        // deterministically with a typed query error rather than
52        // panicking (debug) or silently wrapping (release). The
53        // iterator `.sum::<i64>()` form would do `i64::Add` which has
54        // neither property in a stable consensus-level contract.
55        let sum: i64 = results
56            .elements
57            .iter()
58            .map(|e| match e {
59                QueryResultElement::ElementResultItem(elem) => elem.sum_value_or_default(),
60                _ => 0,
61            })
62            .try_fold(0i64, |acc, v| acc.checked_add(v))
63            .ok_or_else(|| {
64                Error::Query(QuerySyntaxError::Unsupported(
65                    "point-lookup sum overflowed i64 when summing per-In branches. \
66                     Narrow the query (smaller In set) or use multiple queries and \
67                     combine client-side."
68                        .to_string(),
69                ))
70            })?;
71        Ok(vec![SumEntry {
72            in_key: None,
73            key: vec![],
74            sum: Some(sum),
75        }])
76    }
77
78    /// Generates a grovedb proof of the SumTree elements covering a
79    /// fully-covered Equal/`In` sum query against a `summable: "<x>"`
80    /// index. Mirrors count's `execute_point_lookup_count_with_proof`.
81    pub fn execute_point_lookup_sum_with_proof(
82        &self,
83        drive: &Drive,
84        transaction: TransactionArg,
85        platform_version: &PlatformVersion,
86    ) -> Result<Vec<u8>, Error> {
87        let drive_version = &platform_version.drive;
88        let path_query = self.point_lookup_sum_path_query(platform_version)?;
89        let CostContext { value, cost: _ } = drive.grove.get_proved_path_query(
90            &path_query,
91            None,
92            transaction,
93            &drive_version.grove_version,
94        );
95        let proof = value.map_err(|e| Error::GroveDB(Box::new(e)))?;
96        Ok(proof)
97    }
98}