Skip to main content

DriveDocumentCountQuery

Struct DriveDocumentCountQuery 

Source
pub struct DriveDocumentCountQuery<'a> {
    pub document_type: DocumentTypeRef<'a>,
    pub contract_id: [u8; 32],
    pub document_type_name: String,
    pub index: &'a Index,
    pub where_clauses: Vec<WhereClause>,
}
Expand description

A query to count documents using CountTree elements in the index path.

This struct encapsulates all the information needed to perform a count query on a document type’s countable index.

Fields§

§document_type: DocumentTypeRef<'a>

The document type to count

§contract_id: [u8; 32]

The contract id (32 bytes)

§document_type_name: String

The document type name

§index: &'a Index

The countable index to use

§where_clauses: Vec<WhereClause>

The equality where clauses that match index prefix properties

Implementations§

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn is_range_operator(op: WhereOperator) -> bool

Returns true if op is a range operator that can be served by a range_countable index walking the property-name ProvableCountTree’s children. The non-prefix portion of a range count query carries exactly one range operator on the index’s last property.

Source

pub fn has_unsupported_operator(where_clauses: &[WhereClause]) -> bool

Returns true if any where clause uses an operator the count fast path cannot serve. Callers should treat this as a query-rejection signal.

Source

pub fn detect_mode( where_clauses: &[WhereClause], mode: CountMode, prove: bool, ) -> Result<DocumentCountMode, QuerySyntaxError>

Classify a count query’s mode from its where clauses + request flags.

This is the protocol-version-agnostic shape detection that decides which executor (one of the six DocumentCountMode variants — Total / PerInValue / RangeNoProof / RangeProof / RangeDistinctProof / PointLookupProof) the request maps to. The returned DocumentCountMode discriminates among the dispatcher’s match arms; concrete pagination / index-picker inputs flow through the call sites separately.

All validation that depends only on the where clauses + flags (multiple range clauses, range mixed with In, distinct mode on the prove path, distinct mode without a range clause, etc.) is done here and surfaces as QuerySyntaxError::InvalidWhereClauseComponents. Validation that depends on the contract’s index set (no covering index) stays at the call site since it requires the &BTreeMap<String, Index>.

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn find_countable_index_for_where_clauses<'b>( indexes: &'b BTreeMap<String, Index>, where_clauses: &[WhereClause], ) -> Option<&'b Index>

Finds a countable: true index whose properties exactly match the indexable (Equal/In) where-clause fields — every index property has a corresponding clause AND every clause’s field appears in the index.

Exact coverage is the contract for both no-proof and prove count paths: a countable index counts exactly what it indexes, and queries against partially-covered indexes are rejected with a clear error directing the caller at the index-design fix. This avoids the product-of-uncovered-branching-factors walk that a prefix-match approach would silently fall through to, and keeps the storage’s “count maintained only at the terminal level” trade-off intact (no need to maintain counts at intermediate index levels just to serve partial-coverage queries cheaply).

Returns None if:

  • Any where clause uses an operator other than Equal / In.
  • The set of indexable where-clause fields doesn’t exactly equal the set of properties of any single countable: true index.

For the documents_countable: true case (total count with no where clauses), the dispatcher reads the document-type primary-key tree’s CountTree directly — that path doesn’t use this picker because no index is involved.

Source

pub fn find_range_countable_index_for_where_clauses<'b>( indexes: &'b BTreeMap<String, Index>, where_clauses: &[WhereClause], ) -> Option<&'b Index>

Finds a range_countable index that can serve a range-count query.

Match criteria:

  • All Equal/In where-clause fields form a prefix of the index properties.
  • There is exactly one range-operator where-clause, on a property that is the last property of the index (the IndexLevel terminator). This is the property whose values get walked.
  • The index has range_countable = true and countable.is_countable().

Returns None if no such index exists or if there’s more than one range operator in the where clauses (which would require nested range walks the current model doesn’t support). Pure point-lookup queries (no range operator) should fall back to Self::find_countable_index_for_where_clauses.

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn aggregate_count_path_query( &self, platform_version: &PlatformVersion, ) -> Result<PathQuery, Error>

Build the grovedb PathQuery for an AggregateCountOnRange query against this count query’s range_countable index.

Shared between the server-side prove path (Self::execute_aggregate_count_with_proof) and the client- side verify path (the SDK’s FromProof<DocumentQuery> for DocumentCount, via the shared verify_aggregate_count helper). Both sides must produce the exact same PathQuery for verification to recompute the same merk root.

Aggregate-count specifically restricts prefix props to Equal: grovedb’s AggregateCountOnRange primitive wraps a single inner range and emits one aggregate u64 — there’s no way for it to cartesian-fork over multiple In values at the merk layer. For per-distinct-value counts with In on prefix, use Self::distinct_count_path_query instead.

Errors:

  • No range where-clause / multiple range where-clauses → InvalidWhereClauseComponents
  • In on a prefix property → InvalidWhereClauseComponents (aggregate primitive can’t fork)
  • Missing prefix clause → InvalidWhereClauseComponents
Source

pub fn carrier_aggregate_count_path_query( &self, limit: Option<u16>, platform_version: &PlatformVersion, ) -> Result<PathQuery, Error>

Build the grovedb PathQuery for a carrier AggregateCountOnRange proof — one outer Key per In value, each terminating in an ACOR boundary walk over the per-branch range subtree. Returns one (in_key, u64) pair per resolved In branch via [grovedb::GroveDb::query_aggregate_count_per_key] (no- proof) and [grovedb::GroveDb::verify_aggregate_count_query_per_key] (verify).

Required where-clause shape (validated upstream by Self::detect_mode routing to [DocumentCountMode::RangeAggregateCarrierProof]):

  • Exactly one In clause on the In-property
  • Exactly one range clause on the terminator property of a range_countable: true index whose first property is the In-property
  • Any prefix properties between In and range must use == (mirror of Self::aggregate_count_path_query’s non-In prefix rule)

Path-query structure:

  • Outer path stops one level above the In-bearing property subtree’s children (@/doc_prefix/0x01/doctype/<In-prop>).
  • Outer Query: Key(in_value_0), Key(in_value_1), … in lex-asc serialized order (grovedb’s multi-key walker invariant).
  • subquery_path: the terminator property name (and any trailing == clause names between In and range, in index order).
  • subquery: Query::new_aggregate_count_on_range(range_item).

Enabled by grovedb PR #663. Before that PR, AggregateCountOnRange was required to be the only item in its query and could not appear under a subquery field — the dispatcher rejected this shape with “range count queries with an in clause are not supported on the aggregate prove path”.

Errors:

  • No range where-clause / multiple range where-clauses → InvalidWhereClauseComponents
  • No In where-clause → InvalidWhereClauseComponents
  • In on a non-prefix property → InvalidWhereClauseComponents
  • Prefix property between In and range uses non-Equal → InvalidWhereClauseComponents
Source

pub fn distinct_count_path_query( &self, limit: Option<u16>, left_to_right: bool, platform_version: &PlatformVersion, ) -> Result<PathQuery, Error>

Build the grovedb PathQuery for a regular range query against this count query’s range_countable index — the distinct-counts variant. Used by:

In-on-prefix support via grovedb subqueries. Where Self::aggregate_count_path_query rejects In on prefix (the aggregate merk primitive can’t cartesian-fork), this builder uses grovedb’s native subquery primitive:

  • Flat shape (no In on prefix, only Equal): path includes the range terminator; outer Query has the range item.
  • Compound shape (one In on prefix): path stops at the In-bearing prop’s property-name subtree; outer Query has one Key(value) item per In value; set_subquery_path carries any post-In Equal-clause (name, value) pairs plus the terminator name; set_subquery is the range item.

Both shapes return (path, branched-or-flat Query) and feed the same grove_get_raw_path_query / get_proved_path_query pipelines downstream. The compound shape replaces the pre-existing cartesian-fork loop in execute_range_count_no_proof.

limit IS load-bearing for prove-path verification: the prover bounds the proof at limit matched keys, and the verifier must build the exact same PathQuery (including this cap) for the merk-root recomputation to match. The dispatcher pre-validates limit ≤ max_query_limit on the prove path, so unbounded queries can’t reach this builder with Some(...) greater than the cap. The no-proof path passes None (full walk) so cross-In-fork merging sees every emitted element before the result-set-level limit is applied in post-processing.

left_to_right controls grovedb’s iteration direction: true (the default, used for ascending order_by_ascending) walks the range from low key to high key; false reverses. On the prove path this is load-bearing: the path query’s Query.left_to_right is part of the serialized PathQuery bytes, so the prover and verifier must agree on the value or the merk-root recomputation fails. For compound queries the flag is applied to BOTH the outer In-keys Query and the inner range subquery, so descending iteration walks (in_key_desc, key_desc) tuples (matching what RangeCountOptions::order_by_ascending = false callers expect).

Errors:

  • No range where-clause / multiple range where-clauses
  • Multiple In clauses on prefix props
  • Non-Equal-non-In operator on a prefix prop
  • Missing prefix clause
Source

pub fn point_lookup_count_path_query( &self, platform_version: &PlatformVersion, ) -> Result<PathQuery, Error>

Build the grovedb PathQuery for a point-lookup count proof against a countable: true index. Returns one element per covered branch whose count_value is the per-branch document count.

Shared between the server-side prove path (Self::execute_point_lookup_count_with_proof) and the client-side verify path (Self::verify_point_lookup_count_proof). Both sides must produce the exact same PathQuery for the merk-root recomputation to match.

§Two terminator shapes depending on range_countable

The proof’s terminal element is at one of two layers, picked from Index::range_countable:

  • Normal countable: true (NOT range_countable): the terminator’s value tree is a NormalTree, and the doc-count CountTree sits inside it at the conventional [0] child. Proof targets [..., last_field, last_value, 0].
  • range_countable: true: the terminator’s value tree is itself a CountTree (continuation property-name subtrees sit beneath as Element::NonCounted so they don’t pollute the parent count — see add_indices_for_index_level_for_contract_operations_v0). The value tree’s own count_value_or_default() already IS the per-branch doc count, so the proof targets the value tree directly at [..., last_field, last_value] and saves one merk-path layer per covered branch.

Concretely the optimization replaces a trailing Key([0]) with Key(last_value) against [..., last_field] (Equal- only, no In) — or against the In-bearing prop’s property-name subtree (In on terminator) — or replaces the trailing pair in set_subquery_path (In on prefix + trailing Equals that reach the terminator). The query shape stays in the same Query/ subquery topology so byte-equality across prover and verifier is preserved by construction.

§Shape support

The builder requires the where clauses to fully cover the index — every property in self.index.properties must have a matching Equal or In clause. Partial-coverage shapes (where some index properties have no matching clause) require a recursive subquery enumeration that this builder does not implement (and that the strict picker already rejects upstream).

In may appear at any position in the index. Equal clauses before the In contribute to base_path; Equal clauses after the In feed set_subquery_path on the outer Query so the descent under each matched In value lands at the right CountTree leaf. At most one In clause per query (multiple would cartesian-fork beyond what a single set_subquery expresses).

This is more permissive than the regular document query path’s Index::matches rule (packages/rs-dpp/src/ data_contract/document_type/index/mod.rs:503), which restricts In to the last or before-last index property because its path-construction code positionally zips intermediate index names with Equal-clause values (see DriveDocumentQuery::get_non_primary_key_path_query). The count path doesn’t have that constraint: it’s a pure CountTree element lookup with no document-key terminator descent, no order_by interpretation, and no limit/offset semantics, so set_subquery_path with an arbitrary trailing tail just works. Both no-proof (Self::execute_no_proof) and prove (Self::execute_point_lookup_count_with_proof) executors route through this single builder, so they accept the same query shapes by construction.

Output shapes (countable / range_countable differ only in whether the trailing Key([0]) is replaced by Key(last_value)):

  • Equal-only, fully covered:
    • countable: path [..., last_field, last_value], single Key([0]).
    • range_countable: path [..., last_field], single Key(last_value).
  • Equal prefix + In (any position) [+ trailing Equals]: compound query with base_path ending at the In-bearing property’s property-name subtree (Equal clauses before the In are baked into base_path); outer Query has one Key per In value (sorted lex-asc for prove/no-proof parity and pushed-limit safety — same convention as Self::distinct_count_path_query).
    • In on terminator:
      • countable: subquery Key([0]) under each In value’s value tree (set_subquery_path unset).
      • range_countable: outer Keys already point at the CountTree value trees themselves; no subquery is set.
    • In on a prefix + trailing Equals reaching the terminator: set_subquery_path carries the post-In Equal (name, value) pairs in index order:
      • countable: full pairs, subquery Key([0]).
      • range_countable: last pair’s value is hoisted out as the subquery’s single Key(value); set_subquery_path ends at the terminator’s property-name segment.
§Errors

Rejects shapes the builder doesn’t support:

  • Partial coverage (uncovered index property)
  • More than one In clause
  • Any non-Equal / non-In operator (defense-in-depth; mode detection already filters these out)
Source

pub fn primary_key_count_tree_path_query( contract_id: [u8; 32], document_type_name: &str, ) -> PathQuery

Build the grovedb PathQuery for proving the document type’s primary-key CountTree element at [contract_doc, contract_id, 1, doctype, 0]. Used for unfiltered total counts when the document type has documents_countable: true — the type-level CountTree’s count_value IS the total document count, no index walk needed.

Shared between the server-side prove path ([Drive::execute_document_count_point_lookup_proof]’s documents_countable fast path) and the client-side verify path (Self::verify_primary_key_count_tree_proof). Both sides produce the exact same PathQuery for merk-root recomputation.

Free function rather than a method on DriveDocumentCountQuery because the documents_countable case isn’t tied to any index — it operates at the doctype level directly.

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn execute_no_proof( &self, drive: &Drive, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<SplitCountEntry>, Error>

Executes the count query without generating a proof.

Returns the total count as a single SplitCountEntry with empty key (the unified-count Total shape).

Implementation goes through the same Self::point_lookup_count_path_query builder the prove path uses, then runs grove.query to fetch the matched CountTree elements and sums their count_value_or_default() values. The builder handles all three structural cases (Equal-only fully covered, In at any index position, In with trailing Equals via set_subquery_path) — there’s no need for a separate recursive walker on the no-proof side.

Source

pub fn execute_point_lookup_count_with_proof( &self, drive: &Drive, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<u8>, Error>

Generates a grovedb proof of the CountTree elements covering a fully-covered Equal/In count query against a countable: true index. Returns the raw proof bytes; the SDK-side Self::verify_point_lookup_count_proof walks the proof and extracts count_value_or_default() from each verified CountTree element.

Builds the path query via Self::point_lookup_count_path_query (shared with the verifier AND with Self::execute_no_proof above, so all three sites see byte-identical path queries). Errors surface from the builder when the query shape isn’t supported — partial coverage, more than one In, etc. — see that builder’s docstring for the exhaustive contract.

Proof size is O(k × log n) where k is the number of covered (Equal/In) branches and n is the tree depth: one merk path proof per CountTree element, not per matching document. Avoids the materialize-and-count alternative used by the regular document-query path, which scales with the number of matching docs and is capped at u16::MAX.

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn execute_range_count_no_proof( &self, drive: &Drive, options: &RangeCountOptions, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<SplitCountEntry>, Error>

Executes a range-aware count query against a range_countable index. Path layout is [contract_doc, doctype, prefix..., range_prop_name], whose children are the per-value CountTree leaves keyed by the range property’s serialized value.

The caller picks the index via Self::find_range_countable_index_for_where_clauses; this method assumes:

  • self.index.range_countable == true
  • All Equal / In where clauses cover the index prefix
  • Exactly one range-operator where clause hits the index’s last property
§Execution strategies by mode
  • Flat summed (no In, distinct = false): single query_aggregate_count call against the merk-level AggregateCountOnRange primitive. O(log n).
  • Compound summed (In on prefix, distinct = false): per-In-value fan-out — one query_aggregate_count call per matched In branch, summed in Rust. Bounded by the In array’s 100-element cap (enforced by WhereClause::in_values) times O(log n), so worst-case work is 100 × O(log n) regardless of how many documents the range actually matches. Closes the request-amplification surface a pre-fix walk-and-sum implementation had: that path materialized every matched (in_key, key) element even though the response was still a single aggregate u64.
  • Distinct mode (distinct = true, with or without In on prefix): walks the unified Self::distinct_count_path_query and emits one entry per matched (in_key, key) pair. The path query carries options.limit (clamped to max_query_limit upstream by the dispatcher) and options.order_by_ascending, so per-query work is O(limit × log n). Cross-fork aggregation is intentionally NOT performed server-side; callers reduce by key client-side if they want a flat histogram. See the book chapter (“No-Merge Compound Semantics”) for the rationale.
§Returned entry shape

When options.distinct = false, returns a single entry with in_key = None, empty key, and count equal to the sum of all matched per-value counts. When options.distinct = true, returns one entry per emitted (in_key, key) pair, after applying order_by_ascending and limit over the lexicographic (in_key, key) tuple.

Source

pub fn execute_aggregate_count_with_proof( &self, drive: &Drive, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<u8>, Error>

Generates a grovedb AggregateCountOnRange proof for a range-count query against a range_countable index. The returned proof bytes can be verified client-side via GroveDb::verify_aggregate_count_query, which yields (root_hash, count) — replacing the materialize-and-count proof path that capped at u16::MAX documents.

Limitations vs. Self::execute_range_count_no_proof:

  • Returns ONLY the total count (a single number, no per-distinct-value entries) — AggregateCountOnRange is a single-aggregate primitive at the merk layer.
  • Requires the prefix to resolve to exactly one path. In on prefix properties is not supported because grovedb’s aggregate primitive only lifts a single inner range.
Source

pub fn execute_distinct_count_with_proof( &self, drive: &Drive, limit: u16, left_to_right: bool, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<u8>, Error>

Generates a regular grovedb range proof against this count query’s range_countable index — the distinct-counts-with- proof companion to Self::execute_aggregate_count_with_proof.

No new prover code: the leaf is a ProvableCountTree and merk’s existing prove_query already emits KVCount(key, value, count) per matched in-range key (via to_kv_count_node). Each count is hash-bound to the merk root via node_hash_with_count, so the per-key correctness guarantee comes for free with the standard hash-chain check — the SDK-side [drive_proof_verifier::verify_distinct_count_proof] just pulls the counts out of the proof’s op stream after the integrity check passes.

Trade-off vs. the aggregate prove path:

  • Returns per-distinct-value counts (one (key, count) per matched lot value), not just a single sum.
  • Proof size is O(distinct values matched), not O(log n) — so ~1 KVCount op per matched key instead of subtree collapse via HashWithCount. Still strictly smaller than materialize-and-count, which would emit each underlying doc.
Source

pub fn execute_carrier_aggregate_count_with_proof( &self, drive: &Drive, limit: Option<u16>, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<u8>, Error>

Generates a grovedb carrier AggregateCountOnRange proof for In + range queries with group_by = [in_field]. The proof commits one aggregate count per resolved In branch via grovedb’s carrier-subquery composition (PR #663).

Path query: see Self::carrier_aggregate_count_path_query.

Trade-off vs. the alternative Self::execute_distinct_count_with_proof (GroupByCompound shape):

  • This (carrier-ACOR): O(|In| · (log B + log C’)) proof bytes. One commit per merk-tree boundary node per In branch — preserves the per-branch aggregate granularity that group_by = [in_field, range_field] can’t express (the compound shape commits per-distinct-value-pair entries).
  • Alternative (distinct compound): O(|In| · R · log C’) where R is distinct in-range values per branch. Carries strictly more information (one (in_key, range_key) pair per resolved doc) at substantially larger bytes.

Verified client-side via [grovedb::GroveDb::verify_aggregate_count_query_per_key], which returns (RootHash, Vec<(Vec<u8>, u64)>).

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn verify_aggregate_count_proof( &self, proof: &[u8], platform_version: &PlatformVersion, ) -> Result<(RootHash, u64), Error>

Verifies an AggregateCountOnRange proof and returns (root_hash, count).

Counterpart to the prover-side execute_aggregate_count_with_proof: rebuilds the same PathQuery via aggregate_count_path_query and calls GroveDb::verify_aggregate_count_query. The caller is responsible for combining the returned root_hash with the surrounding tenderdash signature — see rs-drive-proof-verifier’s verify_aggregate_count_proof wrapper for the canonical composition.

§Arguments
  • proof — raw grovedb proof bytes.
  • platform_version — selects the method version.
Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn verify_carrier_aggregate_count_proof( &self, proof: &[u8], limit: Option<u16>, platform_version: &PlatformVersion, ) -> Result<(RootHash, Vec<(Vec<u8>, u64)>), Error>

Verifies a carrier AggregateCountOnRange proof and returns (root_hash, per_key_counts) — one (in_key, u64) pair per resolved In branch in serialized lex-asc order.

Counterpart to the prover-side execute_carrier_aggregate_count_with_proof: rebuilds the same PathQuery via carrier_aggregate_count_path_query and calls [grovedb::GroveDb::verify_aggregate_count_query_per_key]. The caller is responsible for combining the returned root_hash with the surrounding tenderdash signature — see rs-drive-proof-verifier’s wrapper for the canonical composition.

§Arguments
  • proof — raw grovedb proof bytes.
  • platform_version — selects the method version.

The Vec<(Vec<u8>, u64)> payload mirrors grovedb’s per-key carrier shape — see the v0 inner method for the rationale.

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn verify_distinct_count_proof( &self, proof: &[u8], limit: u16, left_to_right: bool, platform_version: &PlatformVersion, ) -> Result<(RootHash, Vec<SplitCountEntry>), Error>

Verifies a regular grovedb range proof against a ProvableCountTree and returns (root_hash, entries). Each entry’s count is bound to the merk root via node_hash_with_count(kv_hash, l_hash, r_hash, count), so once this returns Ok every count is cryptographically committed to the same root_hash the caller can pass to a tenderdash signature check.

Counterpart to the prover-side execute_distinct_count_with_proof: rebuilds the same PathQuery via distinct_count_path_query and calls GroveDb::verify_query. Caller is responsible for combining the returned root_hash with the surrounding tenderdash signature — see rs-drive-proof-verifier’s verify_distinct_count_proof wrapper for the canonical composition.

Entries are emitted unmerged: for compound (In-on-prefix) queries each entry retains its in_key (the In value for that fork) alongside the terminator key. See SplitCountEntry’s doc for the no-merge rationale.

§Arguments
  • proof — raw grovedb proof bytes.
  • limit — the same limit the prover applied (also used to reconstruct the matching path query).
  • left_to_right — same iteration direction the prover used.
  • platform_version — selects the method version.
Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn verify_point_lookup_count_proof( &self, proof: &[u8], platform_version: &PlatformVersion, ) -> Result<(RootHash, Vec<SplitCountEntry>), Error>

Verifies a grovedb proof of CountTree elements produced by the point-lookup count proof path and returns (root_hash, entries).

Counterpart to the prover-side execute_point_lookup_count_with_proof: rebuilds the same PathQuery via point_lookup_count_path_query and calls GroveDb::verify_query. Each verified element’s count_value is cryptographically bound to the merk root via node_hash_with_count(kv_hash, l_hash, r_hash, count), so once this returns Ok every count is committed to the same root_hash the caller can pass to a tenderdash signature check. Caller is responsible for combining the returned root_hash with the surrounding tenderdash signature — see rs-drive-proof-verifier’s verify_point_lookup_count_proof wrapper for the canonical composition.

Entry shape:

  • Equal-only, fully covered: a single entry with in_key: None, key: vec![], and count equal to the covered branch’s CountTree count_value.
  • In at any index position (with any number of trailing Equals): one entry per In value, with in_key: None, key: <serialized_in_value>, and count equal to that In branch’s CountTree count_value. When the In has trailing Equal clauses after it (e.g. a IN [..] AND b = y AND c = z on index [a, b, c]), those Equals are part of the descent so each branch’s count is “docs with in_field == in_value AND <every trailing Equal>”; the entry’s key still records just the In value because the trailing Equals are fixed across all entries. Matches the no-proof PerInValue shape (in_key is reserved for the range-distinct compound case where In sits on a prefix of a range index).

Branches with no documents at the covered path don’t appear in the result (CountTree element is absent → no entry emitted).

Source§

impl DriveDocumentCountQuery<'_>

Source

pub fn verify_primary_key_count_tree_proof( proof: &[u8], contract_id: [u8; 32], document_type_name: &str, platform_version: &PlatformVersion, ) -> Result<(RootHash, u64), Error>

Verifies a grovedb proof of the document type’s primary-key CountTree element and returns (root_hash, count). Used by the SDK to verify the response from the prove path’s documents_countable: true fast path — unfiltered total counts on a doctype whose primary-key tree is itself a CountTree.

Free-function on the type rather than &self because the documents_countable case isn’t tied to any index — it operates on the doctype primary-key tree directly. The contract_id + document_type_name are all the verifier needs to reconstruct the same PathQuery the prover used via Self::primary_key_count_tree_path_query.

The verified count is cryptographically bound to the merk root via node_hash_with_count(kv_hash, l_hash, r_hash, count) — same forge-resistance guarantee the other count- proof verifiers rely on. Once this returns Ok, the count is committed to the root_hash the caller passes to the tenderdash signature check.

Returns count = 0 when the CountTree element is absent (fresh doctype with no documents inserted). The documents_countable storage layout creates the type-level CountTree at contract apply time, so absence really does mean “zero docs”; callers can rely on it.

Trait Implementations§

Source§

impl<'a> Clone for DriveDocumentCountQuery<'a>

Source§

fn clone(&self) -> DriveDocumentCountQuery<'a>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for DriveDocumentCountQuery<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> Conv for T

§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
§

impl<T> CostsExt for T

§

fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>
where Self: Sized,

Wraps any value into a CostContext object with provided costs.
§

fn wrap_fn_cost( self, f: impl FnOnce(&Self) -> OperationCost, ) -> CostContext<Self>
where Self: Sized,

Wraps any value into CostContext object with costs computed using the value getting wrapped.
§

impl<T> FmtForward for T

§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T, U> IntoOnNetwork<U> for T
where U: FromOnNetwork<T>,

§

fn into_on_network(self, network: Network) -> U

Calls U::from_on_network(self).

Source§

impl<T, U> IntoPlatformVersioned<U> for T

Source§

fn into_platform_versioned(self, platform_version: &PlatformVersion) -> U

Performs the conversion.
§

impl<T> Pipe for T
where T: ?Sized,

§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> Tap for T

§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
§

impl<T> TryConv for T

§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryFromVersioned<U> for T
where T: TryFrom<U>,

§

type Error = <T as TryFrom<U>>::Error

The type returned in the event of a conversion error.
§

fn try_from_versioned( value: U, _grove_version: &GroveVersion, ) -> Result<T, <T as TryFromVersioned<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> TryIntoPlatformVersioned<U> for T

Source§

type Error = <U as TryFromPlatformVersioned<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into_platform_versioned( self, platform_version: &PlatformVersion, ) -> Result<U, <U as TryFromPlatformVersioned<T>>::Error>

Performs the conversion.
§

impl<T, U> TryIntoVersioned<U> for T
where U: TryFromVersioned<T>,

§

type Error = <U as TryFromVersioned<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into_versioned( self, grove_version: &GroveVersion, ) -> Result<U, <U as TryFromVersioned<T>>::Error>

Performs the conversion.
§

impl<T, U> TryIntoWithBlockHashLookup<U> for T
where U: TryFromWithBlockHashLookup<T>,

§

type Error = <U as TryFromWithBlockHashLookup<T>>::Error

§

fn try_into_with_block_hash_lookup<F>( self, block_hash_lookup: F, network: Network, ) -> Result<U, <T as TryIntoWithBlockHashLookup<U>>::Error>
where F: Fn(&BlockHash) -> Option<u32>,

Converts self into T, using a block hash lookup function.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more