Skip to main content

DriveDocumentRankedQuery

Struct DriveDocumentRankedQuery 

Source
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: String

The document type name — a path segment.

§index: &'a Index

The 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: RankedAxis

Which aggregate the groups are ranked by. Must be covered by index’s matching ranked_* flag.

§descending: bool

true 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: u16

How 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: u32

How 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<'_>

Source

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<'_>

Source

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.

Source

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<'_>

Source

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<'_>

Source

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>

Source§

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

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for DriveDocumentRankedQuery<'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
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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).

§

impl<T, U> IntoPlatformVersioned<U> for T
where U: FromPlatformVersioned<T>,

§

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
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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.
§

impl<T, U> TryIntoPlatformVersioned<U> for T
where U: TryFromPlatformVersioned<T>,

§

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

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

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