Skip to main content

dash_platform_queries/documents/
document_split_counts.rs

1//! `FromProof` + `Fetch` for [`DocumentSplitCounts`] — the
2//! per-group-entry view of the unified `getDocuments` endpoint.
3//!
4//! Backed by the same [`DocumentQuery`] as
5//! [`drive_proof_verifier::DocumentCount`]; the only difference
6//! is response shape — `DocumentSplitCounts` returns the full
7//! `entries` list keyed by the splitting property's serialized
8//! value, while `DocumentCount` returns the sum.
9//!
10//! Per-shape proof dispatch lives in
11//! [`super::count_proof_helpers::verify_count_query`] — this
12//! impl passes the verified entries through unchanged.
13//!
14//! Shapes emitted by `verify_count_query`:
15//! - `group_by = []` (aggregate): one entry with empty `key`
16//!   carrying the verified total. `AggregateCountOnRange`,
17//!   primary-key CountTree, and point-lookup-on-Equal-only paths
18//!   all collapse to this shape.
19//! - `group_by = [in_field]` (per-In entries): one entry per
20//!   **present** queried In value, with `count: Some(n)`. Absent
21//!   In branches are omitted from `entries` — the current
22//!   point-lookup path query doesn't request absence proofs,
23//!   so grovedb's `verify_query` surfaces only present
24//!   `(path, key, Some(Element))` triples. Callers that need to
25//!   distinguish "verified zero" from "queried but absent" diff
26//!   their request's In array against the returned entries by
27//!   `key` (each entry's `key` is `serialize_value_for_key(in_field, v)`).
28//! - `group_by = [range_field]` / `[in_field, range_field]`
29//!   (distinct walk): one entry per distinct value in the range
30//!   (compound queries: per `(in_key, key)` pair). A value with no
31//!   documents is absent, except on an index that can hold an empty
32//!   group (Drive's `index_keeps_empty_groups`: one a preallocated or
33//!   an `outlivesDelete` index shares its levels with, a
34//!   `summableOffCountIndex` counter at zero included): there it comes
35//!   back with `count: Some(0)`, counted in the page's limit. A short
36//!   page is not proof the range ended: across an `IN`, grovedb also
37//!   charges the limit for an `IN` value whose range holds nothing, and
38//!   a `nullSearchable: false` index's empty null-key group is counted
39//!   and left out when the range includes the empty key.
40
41use crate::documents::count_proof_helpers::{
42    assert_select_is_count, check_count_request, verify_count_query,
43};
44use crate::documents::document_query::DocumentQuery;
45use dapi_grpc::platform::v0::{GetDocumentsResponse, Proof, ResponseMetadata};
46use dash_context_provider::ContextProvider;
47use dpp::dashcore::Network;
48use dpp::version::PlatformVersion;
49use drive_proof_verifier::{DocumentSplitCounts, FromProof};
50
51impl FromProof<DocumentQuery> for DocumentSplitCounts {
52    type Request = DocumentQuery;
53    type Response = GetDocumentsResponse;
54
55    fn check_request(
56        request: &Self::Request,
57        platform_version: &PlatformVersion,
58    ) -> Result<(), drive_proof_verifier::Error> {
59        check_count_request(request, false, platform_version)
60    }
61
62    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
63        request: I,
64        response: O,
65        _network: Network,
66        platform_version: &PlatformVersion,
67        provider: &'a dyn ContextProvider,
68    ) -> Result<(Option<Self>, ResponseMetadata, Proof), drive_proof_verifier::Error>
69    where
70        Self: 'a,
71    {
72        let request: Self::Request = request.into();
73        assert_select_is_count(&request)?;
74        let response: Self::Response = response.into();
75        let (entries, mtd, proof) =
76            verify_count_query(request, response, false, platform_version, provider)?;
77        Ok((entries.map(DocumentSplitCounts::from_verified), mtd, proof))
78    }
79}