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}