pub struct DriveDocumentRankedQuery<'a> {
pub document_type: DocumentTypeRef<'a>,
pub contract_id: [u8; 32],
pub document_type_name: String,
pub index: &'a Index,
pub axis: RankedAxis,
pub descending: bool,
pub k: u16,
pub offset: u32,
/* private fields */
}Expand description
A resolved ranked query. Shared by the prover and the verifier — both
build the grove path through
DriveDocumentRankedQuery::indexed_property_name_tree_path, so the
two cannot drift on which subtree the proof is about.
Construction is normally left to
crate::drive::Drive::execute_document_ranked_request (server) or to
the SDK’s proof helpers (client); both go through
index_picker::find_ranked_index_for_axis to resolve index.
Fields§
§document_type: DocumentTypeRef<'a>The document type being ranked.
contract_id: [u8; 32]The contract id (32 bytes). Separate from document_type so the
verifier can build the query without the full contract.
document_type_name: StringThe document type name — a path segment.
index: &'a IndexThe covering ranked index. Its last property is the GROUP BY property and the final path segment; any leading properties
are pinned by Self::prefix_branches.
axis: RankedAxisWhich aggregate the groups are ranked by. Must be covered by
index’s matching ranked_* flag.
descending: booltrue walks the secondary from the largest aggregate down
(ORDER BY <agg> DESC); false walks from the smallest up
(ORDER BY <agg> ASC).
Tie ordering. The secondary’s keys are (sort_key ‖ group_key), and the walk is a plain directional scan of that
keyspace — so groups with equal aggregates come back in group-key
order in the direction of the walk: ascending group key when
descending == false, and descending group key when
descending == true. The reversal is a property of the scan,
not a separate tie-break rule; it is pinned by the
ties_break_by_group_key_in_the_walk_direction test.
k: u16How many groups to return — the request’s LIMIT.
1 ..= MAX_RANKED_LIMIT, validated in mode_detection. Fewer
entries come back when the index has fewer groups than
offset + k; that is not an error.
offset: u32How many ranks to skip before the returned page — the request’s
OFFSET. 0 for an unpaginated ranking.
Unbounded above (any u32), on purpose. grovedb skips by
counting rather than walking — descending the secondary on each
subtree’s aggregate count (HashWithCount /
HashWithCountAndSum) and collapsing any subtree that fits
inside the remaining offset — so work and proof size stay
O(log n + k) at any offset, and an offset of 4 and an
offset of four billion cost the same order of work, the deeper
one in fact slightly less. Both executors go through that
descent, the unproved one without building a proof, so there is
no denial-of-service lever to cap on either path and capping
would only stop honest deep pagination.
An offset past the end of the secondary is a provable answer, not
an error: the page comes back empty and
RankedPage::skipped is the secondary’s entire population.
Implementations§
Source§impl DriveDocumentRankedQuery<'_>
impl DriveDocumentRankedQuery<'_>
Sourcepub fn indexed_property_name_tree_path(
&self,
branch: usize,
) -> Result<Vec<Vec<u8>>, Error>
pub fn indexed_property_name_tree_path( &self, branch: usize, ) -> Result<Vec<Vec<u8>>, Error>
Path of the terminal property-name tree — the indexed tree whose primary holds one value tree per group and whose per-axis secondaries hold the ranking.
For a single-property index:
[ RootTree::DataContractDocuments as u8 ] // 0x01
/ <contract_id: 32 bytes>
/ [ 0x01 ] // "documents", not "contract"
/ <document_type_name: utf-8>
/ <index property name: utf-8>For a compound index [p1, …, pn], each leading property
contributes two segments — its name and the encoded index-key
bytes of its pinned value (from
Self::prefix_branches) — and the terminal property
name closes the path:
… / <p1 name> / <p1 pinned value bytes> / … / <pn name>so the ranking read lands on that prefix’s indexed tree: the per-prefix secondary orders only the pinned prefix’s groups.
The children of the terminal tree are the groups, keyed by the
raw index-key bytes of the terminal property value — the same
bytes that come back as super::RankedEntry::key.
Errors when the number of encoded prefix values does not match the index’s leading-property count — the fail-closed backstop for a caller that resolved the query against the wrong index.
branch indexes into Self::prefix_branches; single-branch
queries (no IN pin) always pass 0.
Source§impl DriveDocumentRankedQuery<'_>
impl DriveDocumentRankedQuery<'_>
Sourcepub fn execute_top_k_no_proof(
&self,
drive: &Drive,
transaction: TransactionArg<'_, '_>,
platform_version: &PlatformVersion,
) -> Result<RankedPage, Error>
pub fn execute_top_k_no_proof( &self, drive: &Drive, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<RankedPage, Error>
Read one page of the ranking directly from the axis secondary:
the k groups starting at rank offset. Entries come back in
ranking order — see DriveDocumentRankedQuery::descending for
the direction and the tie contract.
Fewer than k entries is normal (the index simply has fewer
groups than offset + k) and is not an error. On an IN-pinned
request, an element whose branch chain is missing at ANY depth —
the branch key itself, or any deeper pinned segment under a
present key — contributes an empty branch (union
semantics), exactly as the proved envelope authenticates it, and
the union is served from one committed state: the branched
read always runs under a grovedb snapshot read transaction, so
every per-branch probe and walk reads the same RocksDB snapshot
(a caller transaction is rejected on this shape, mirroring the
branched prover — read per prefix element under a transaction).
A missing path under a single == pin is an error rather than
an empty result: the indexed property-name tree is created when
the contract is registered, so its absence means the
contract-level state is not what the request claims, not that
the ranking is empty. (An index with no documents yet has the
tree, with an empty secondary, and yields an empty entry list.)
The paginated grovedb primitive is used unconditionally, with
offset = 0 standing in for an unpaginated request, so the
no-proof and prove paths read the same code path in grovedb and
cannot drift on the walk’s semantics for offset-free queries.
§The offset is counted, not walked
grovedb descends the secondary reading each subtree’s aggregate
count off its link, and collapses any subtree that fits entirely
inside the remaining offset instead of stepping through it. The
skip therefore costs O(log n) at any offset rather than one
iterator step and one decode per skipped entry, and an offset at
or past the population is answered from the root’s own count with
no descent at all — the cheapest request on this surface rather
than the most expensive. offset = 0 keeps the plain iterator
path and never touches the tree, so the common unpaginated
request costs exactly what it always did.
That is what makes an uncapped OFFSET safe rather than merely
tolerated. Ranked queries carry no fee, cannot be cancelled once
dispatched, and share their rate budget with state transitions
rather than having one of their own, so a skip whose cost grew
with the offset would be an unmetered lever for any
unauthenticated caller. It does not grow.
RankedPage::skipped comes back from grovedb rather than being
echoed from the request: it is the requested offset when the skip
succeeded, and the secondary’s whole population when the walk ran
out of groups first. That is the same quantity the proved path
attests, so the two no longer disagree — though on this path it is
the node’s unverified claim rather than an attested value, exactly
like the entries beside it. See RankedPage::skipped.
Sourcepub fn execute_top_k_with_proof(
&self,
drive: &Drive,
transaction: TransactionArg<'_, '_>,
platform_version: &PlatformVersion,
) -> Result<Vec<u8>, Error>
pub fn execute_top_k_with_proof( &self, drive: &Drive, transaction: TransactionArg<'_, '_>, platform_version: &PlatformVersion, ) -> Result<Vec<u8>, Error>
Generate the grovedb indexed-axis paginated top-k proof for this query.
The envelope commits the walked secondary entries, the number of
entries skipped to reach them, the primary’s root hash, the
sibling axes’ root hashes, and a per-ancestor attestation chain
up to the grovedb root — so the client reconstructs the platform
root hash from it. (axis, k, offset, descending) bind by
RECONSTRUCTION, not echo: the verifier rebuilds the same
PathQuery from the request and
[grovedb::GroveDb::verify_path_query] re-executes the proof
against that traversal, so a proof for a different ranking or a
different page fails to cover it; that is why k is validated
rather than clamped upstream (a clamped k would produce a page
the client’s reconstruction did not ask for).
The paginated primitive is used unconditionally, with
offset = 0 for offset-free requests, so there is exactly one
proof shape on this surface: a client never has to guess which of
two envelope formats a server produced.
Verified by
DriveDocumentRankedQuery::verify_ranked_top_k_proof.
§Empty rankings prove fine
An index holding no documents has an empty axis secondary. The
older non-paginated prover refused that outright (“Cannot create
proof for empty tree”), which made a freshly registered contract
unqueryable with prove = true; the paginated prover emits a
guaranteed-empty range against the secondary instead, so the
proved and unproven paths agree on empty state. Pinned by the
ranking_an_empty_index_reads_empty_and_proves_empty test.
Source§impl DriveDocumentRankedQuery<'_>
impl DriveDocumentRankedQuery<'_>
Sourcepub fn prefix_branches(&self) -> &[Vec<Vec<u8>>]
pub fn prefix_branches(&self) -> &[Vec<Vec<u8>>]
The resolved prefix branches, in canonical order — one per IN
element (a single branch without an IN). Read-only: the field is
crate-private so the resolver’s encoder invariants cannot be
bypassed by construction or mutation.
Source§impl DriveDocumentRankedQuery<'_>
impl DriveDocumentRankedQuery<'_>
Sourcepub fn verify_ranked_top_k_proof(
&self,
proof: &[u8],
platform_version: &PlatformVersion,
) -> Result<(RootHash, RankedPage), Error>
pub fn verify_ranked_top_k_proof( &self, proof: &[u8], platform_version: &PlatformVersion, ) -> Result<(RootHash, RankedPage), Error>
Verifies a grovedb indexed-axis paginated top-k proof and returns
(root_hash, page).
Counterpart to the prover-side
execute_top_k_with_proof.
Both sides derive the proved subtree from
indexed_property_name_tree_path,
so the verifier cannot drift from the prover on which ranking
it is checking.
The returned entries are in ranking order, exactly as the
unproven execute_top_k_no_proof
would return them, and
RankedPage::skipped
carries the attested starting rank so entry i is the group at
rank skipped + i. The caller combines root_hash with the
surrounding tenderdash signature — see rs-drive-proof-verifier
for the canonical composition.
§Arguments
proof— raw grovedb proof bytes.platform_version— selects the method version.
Trait Implementations§
Source§impl<'a> Clone for DriveDocumentRankedQuery<'a>
impl<'a> Clone for DriveDocumentRankedQuery<'a>
Source§fn clone(&self) -> DriveDocumentRankedQuery<'a>
fn clone(&self) -> DriveDocumentRankedQuery<'a>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl<'a> Freeze for DriveDocumentRankedQuery<'a>
impl<'a> RefUnwindSafe for DriveDocumentRankedQuery<'a>
impl<'a> Send for DriveDocumentRankedQuery<'a>
impl<'a> Sync for DriveDocumentRankedQuery<'a>
impl<'a> Unpin for DriveDocumentRankedQuery<'a>
impl<'a> UnsafeUnpin for DriveDocumentRankedQuery<'a>
impl<'a> UnwindSafe for DriveDocumentRankedQuery<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> CostsExt for T
impl<T> CostsExt for T
§fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>where
Self: Sized,
fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>where
Self: Sized,
CostContext object with provided costs.§fn wrap_fn_cost(
self,
f: impl FnOnce(&Self) -> OperationCost,
) -> CostContext<Self>where
Self: Sized,
fn wrap_fn_cost(
self,
f: impl FnOnce(&Self) -> OperationCost,
) -> CostContext<Self>where
Self: Sized,
CostContext object with costs computed using the
value getting wrapped.§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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 Twhere
U: FromOnNetwork<T>,
impl<T, U> IntoOnNetwork<U> for Twhere
U: FromOnNetwork<T>,
§fn into_on_network(self, network: Network) -> U
fn into_on_network(self, network: Network) -> U
Calls U::from_on_network(self).
§impl<T, U> IntoPlatformVersioned<U> for Twhere
U: FromPlatformVersioned<T>,
impl<T, U> IntoPlatformVersioned<U> for Twhere
U: FromPlatformVersioned<T>,
§fn into_platform_versioned(self, platform_version: &PlatformVersion) -> U
fn into_platform_versioned(self, platform_version: &PlatformVersion) -> U
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
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) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
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
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
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
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
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
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.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
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.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
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.