Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
f784f43
feat(platform): unify count + split-count into one endpoint
QuantumExplorer May 9, 2026
4fe3ce5
chore(deps): bump grovedb to 347bd9b5 (NonCounted + AggregateCountOnR…
QuantumExplorer May 9, 2026
e9e06b0
feat(dpp): add range_countable per-index property (schema-level plumb…
QuantumExplorer May 10, 2026
2ce2139
feat(drive): NonCounted-empty-tree helper + range_countable insert guard
QuantumExplorer May 10, 2026
6c58fff
feat(drive): add batch_insert_empty_non_counted_normal_tree_if_not_ex…
QuantumExplorer May 10, 2026
0943719
feat(drive): wire range_countable storage layout in insert walker
QuantumExplorer May 10, 2026
ebdd5e8
feat(drive): mirror range_countable storage layout in delete walker
QuantumExplorer May 10, 2026
97b4687
test(drive): add range_countable index e2e coverage
QuantumExplorer May 10, 2026
4c9ad44
feat(drive): add range-aware count picker for range_countable indexes
QuantumExplorer May 10, 2026
0dad2df
feat(drive): implement range count executor on range_countable indexes
QuantumExplorer May 10, 2026
e807df1
feat(drive-abci): route range count queries through range_countable path
QuantumExplorer May 10, 2026
d0cdcce
chore(dapi-grpc): regenerate clients for unified count endpoint
QuantumExplorer May 10, 2026
692b53d
docs(book): document range count + AggregateCountOnRange proof path
QuantumExplorer May 10, 2026
4619cb3
fix(dpp): satisfy clippy 1.92 doc-lazy-continuation on IndexLevelType…
QuantumExplorer May 10, 2026
b637852
fix(drive): satisfy clippy 1.92 doc-overindented-list-items
QuantumExplorer May 10, 2026
99ee2cc
test: improve range count coverage in drive + drive-abci
QuantumExplorer May 10, 2026
80e668a
refactor(dpp): drop dead protocol-version guards in v1 try_from_schema
QuantumExplorer May 10, 2026
749fbc4
docs(dpp): clarify range_countable affects only last index property
QuantumExplorer May 10, 2026
7e741ce
fix(dpp): restore protocol-version guards in v1 try_from_schema
QuantumExplorer May 10, 2026
973d424
refactor(drive,drive-abci): extract count-mode detection into rs-drive
QuantumExplorer May 10, 2026
59a9149
refactor(drive,drive-abci): extract per-mode count executors into rs-…
QuantumExplorer May 10, 2026
46ec92a
refactor(drive,drive-abci): collapse count handler into one drive call
QuantumExplorer May 10, 2026
7fd9369
fix(drive): satisfy clippy 1.92 in count executor signatures + docstr…
QuantumExplorer May 10, 2026
8c1f872
feat(sdk,wasm-sdk,book): expose new count-query fields + sync indexes.md
QuantumExplorer May 10, 2026
93f332b
fix(rs-sdk-ffi): populate new DocumentCountQuery fields
QuantumExplorer May 10, 2026
22594d7
feat(drive,sdk): expose AggregateCountOnRange path-builder + clear SD…
QuantumExplorer May 10, 2026
b5cee1d
fix(drive): clippy 1.92 doc-overindented-list-items in path-builder doc
QuantumExplorer May 10, 2026
10e34a7
fix(dpp,drive): address CodeRabbit findings on count-query PR
QuantumExplorer May 10, 2026
3ef2ca3
fix(drive): restore prove+In via materialize-and-count, thread option…
QuantumExplorer May 10, 2026
8fb7a47
feat(sdk,drive-proof-verifier): wire up AggregateCountOnRange proof v…
QuantumExplorer May 10, 2026
a288a89
fix(drive): cap PerInValue In array at 100 + delete dead split-count …
QuantumExplorer May 10, 2026
aab3377
autogen
QuantumExplorer May 10, 2026
4913d38
fix(sdk): route DocumentSplitCounts total-count through DocumentCount…
QuantumExplorer May 10, 2026
d4bf97b
docs(book),test(dpp): clarify In+range layering, fix casing, pin rang…
QuantumExplorer May 10, 2026
2b42989
test(drive): drop most-common-color test, keep helper for future cove…
QuantumExplorer May 10, 2026
647f27f
refactor(drive): drop dead split_by_property field on DriveDocumentCo…
QuantumExplorer May 10, 2026
2bb5517
test(drive): broaden aggregate-count prove-path coverage to all 8 ran…
QuantumExplorer May 10, 2026
d8e5b9d
test(drive): add parking-lot scenario test for aggregate-count prove …
QuantumExplorer May 10, 2026
c3d6c4b
test(drive): show decoded proof envelope + add distinct-mode per-lot …
QuantumExplorer May 10, 2026
93a1b0c
feat(drive,sdk,book): wire distinct-counts-with-proof for range_count…
QuantumExplorer May 10, 2026
a811605
test(drive): inline-print decoded distinct-count proof under --nocapture
QuantumExplorer May 10, 2026
0960613
refactor(drive,drive-abci): move count where-clause parsing into drive
QuantumExplorer May 10, 2026
06a6354
refactor(drive-proof-verifier): collapse verify_distinct_count_proof …
QuantumExplorer May 10, 2026
3bd6731
fix(drive-proof-verifier): keep verify_proof_succinctness on for dist…
QuantumExplorer May 10, 2026
f0c5de9
fix(drive,sdk): thread limit through prove-distinct path with validat…
QuantumExplorer May 10, 2026
61de82c
fix(drive-proof-verifier): use strict VerifyOptions::default() now th…
QuantumExplorer May 10, 2026
8445f92
fix(drive-proof-verifier): use GroveDb::verify_query matching docs ha…
QuantumExplorer May 10, 2026
82dda09
docs(drive-proof-verifier): clarify invariants behind verify_query ch…
QuantumExplorer May 10, 2026
6d044b3
fix(drive-abci): update stale test that expected distinct + prove to …
QuantumExplorer May 10, 2026
dbaf371
feat(drive,sdk): allow In on prefix for distinct-count via grovedb su…
QuantumExplorer May 10, 2026
dbeb9b4
feat(dapi-grpc,drive): split CountResults into explicit aggregate vs …
QuantumExplorer May 11, 2026
5d336b3
fix(drive): make contract-insertion cost estimation count-tree-aware …
QuantumExplorer May 11, 2026
bb323cb
feat(drive): support startsWith on the range_countable count fast path
QuantumExplorer May 11, 2026
665a86f
feat(drive,sdk)!: drop cross-fork merge from distinct-count, expose p…
QuantumExplorer May 11, 2026
4dcc0d6
Merge branch 'v3.1-dev' into claude-unified-count-and-range
QuantumExplorer May 11, 2026
d457321
refactor(drive,sdk)!: remove start_after_split_key — pagination via r…
QuantumExplorer May 11, 2026
5a14a6a
fix(drive,sdk): plumb order_by_ascending through the prove-distinct path
QuantumExplorer May 11, 2026
8e802f5
chore(dapi-grpc): drop start_after_split_key proto reservation, renum…
QuantumExplorer May 11, 2026
5e7ac07
test(drive-proof-verifier): add unit tests for count-proof helpers (1…
QuantumExplorer May 11, 2026
ebd1aeb
refactor(drive,drive-proof-verifier,sdk)!: move count-proof verifiers…
QuantumExplorer May 11, 2026
54982a2
refactor(drive): split drive_document_count_query into 7 focused subm…
QuantumExplorer May 11, 2026
0fded21
refactor(drive,sdk,wasm-sdk): dedup clippy attr + unify count APIs wi…
QuantumExplorer May 11, 2026
c63f474
refactor(drive): drop unused batch_insert_empty_non_counted_normal_tr…
QuantumExplorer May 11, 2026
809cfb1
feat(platform)!: replace order_by_ascending with bytes order_by on Ge…
QuantumExplorer May 11, 2026
2f482ae
refactor(rs-sdk-ffi): drop allow(unused_imports) on count re-export
QuantumExplorer May 11, 2026
ac590cf
docs: rewrite count-query comments for future readers, not PR archaeo…
QuantumExplorer May 11, 2026
5e1b140
docs: bring book + proto comments up to date with current count API
QuantumExplorer May 11, 2026
b306f24
fix(drive): bound no-proof distinct range count + sort In keys for pr…
QuantumExplorer May 11, 2026
941f543
feat(drive): wire no-proof query_aggregate_count for flat summed rang…
QuantumExplorer May 11, 2026
c73d3a8
chore(deps): bump grovedb to a917d92d (merged grovedb#662)
QuantumExplorer May 11, 2026
1cec252
feat(drive,sdk)!: PointLookupProof uses CountTree element proof + sym…
QuantumExplorer May 11, 2026
61c80e5
docs(book): bring count chapter up to date with PointLookupProof rewrite
QuantumExplorer May 11, 2026
2b4b7e2
test(drive): drop unused build_widget_with_two_range_countable_indexe…
QuantumExplorer May 11, 2026
e5b891c
feat(drive,sdk)!: strict count-index coverage + documents_countable f…
QuantumExplorer May 11, 2026
b8887d3
docs(book): finish strict-coverage sweep in count chapter + indexes c…
QuantumExplorer May 11, 2026
b973d37
feat(drive): allow In on before-last in prove count builder
QuantumExplorer May 11, 2026
eeb1bbc
fix(drive): anchor RangeDistinctProof limit fallback to compile-time …
QuantumExplorer May 11, 2026
18c13b0
feat(drive): allow In at any index position in prove count builder
QuantumExplorer May 11, 2026
ba9a797
refactor(drive): collapse no-proof count executor onto shared path-qu…
QuantumExplorer May 12, 2026
0f52734
docs(count), feat(rs-sdk-ffi): post-review polish across count-query …
QuantumExplorer May 12, 2026
7065dc4
fix(drive)!: bound compound range+In no-proof count via per-In aggreg…
QuantumExplorer May 12, 2026
05b22cd
fix(sdk,drive): route DocumentCount distinct-range to distinct verifi…
QuantumExplorer May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
688 changes: 318 additions & 370 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions book/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ preferred-dark-theme = "navy"
git-repository-url = "https://github.com/dashpay/platform"
additional-js = ["mermaid.min.js", "mermaid-init.js"]

[preprocessor]

[preprocessor.mermaid]
command = "mdbook-mermaid"

34 changes: 15 additions & 19 deletions book/mermaid-init.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Mermaid initialization for mdBook (without preprocessor)
// Converts ```mermaid code blocks into rendered diagrams.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

(() => {
const darkThemes = ['ayu', 'navy', 'coal'];
const lightThemes = ['light', 'rust'];
Expand All @@ -15,29 +17,23 @@
}

const theme = lastThemeWasLight ? 'default' : 'dark';

// Convert code blocks with language-mermaid into mermaid divs
document.querySelectorAll('pre code.language-mermaid').forEach((codeBlock) => {
const pre = codeBlock.parentElement;
const div = document.createElement('div');
div.className = 'mermaid';
div.textContent = codeBlock.textContent;
pre.parentElement.replaceChild(div, pre);
});

mermaid.initialize({ startOnLoad: true, theme });

// Re-render on theme switch
// Simplest way to make mermaid re-render the diagrams in the new theme is via refreshing the page

for (const darkTheme of darkThemes) {
const el = document.getElementById(darkTheme);
if (el) el.addEventListener('click', () => {
if (lastThemeWasLight) window.location.reload();
document.getElementById(darkTheme).addEventListener('click', () => {
if (lastThemeWasLight) {
window.location.reload();
}
});
}

for (const lightTheme of lightThemes) {
const el = document.getElementById(lightTheme);
if (el) el.addEventListener('click', () => {
if (!lastThemeWasLight) window.location.reload();
document.getElementById(lightTheme).addEventListener('click', () => {
if (!lastThemeWasLight) {
window.location.reload();
}
});
}
Comment on lines 24 to 38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add null checks before calling addEventListener.

document.getElementById() returns null if the element doesn't exist. Calling addEventListener on null will throw a TypeError at runtime. The current code assumes all theme button IDs ('ayu', 'navy', 'coal', 'light', 'rust') exist in the DOM, but if any are missing (due to configuration changes or incomplete HTML), the script will crash.

🛡️ Proposed fix to add null-safety guards
 for (const darkTheme of darkThemes) {
-    document.getElementById(darkTheme).addEventListener('click', () => {
-        if (lastThemeWasLight) {
-            window.location.reload();
-        }
-    });
+    const darkButton = document.getElementById(darkTheme);
+    if (darkButton) {
+        darkButton.addEventListener('click', () => {
+            if (lastThemeWasLight) {
+                window.location.reload();
+            }
+        });
+    }
 }

 for (const lightTheme of lightThemes) {
-    document.getElementById(lightTheme).addEventListener('click', () => {
-        if (!lastThemeWasLight) {
-            window.location.reload();
-        }
-    });
+    const lightButton = document.getElementById(lightTheme);
+    if (lightButton) {
+        lightButton.addEventListener('click', () => {
+            if (!lastThemeWasLight) {
+                window.location.reload();
+            }
+        });
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@book/mermaid-init.js` around lines 24 - 38, The loops over darkThemes and
lightThemes call document.getElementById(...) and immediately addEventListener,
which can throw if the element is null; update both loops in mermaid-init.js to
first assign the result to a variable (e.g., btn), check for null/undefined, and
only call addEventListener when btn exists, preserving the existing callback
logic that uses lastThemeWasLight; this prevents TypeError crashes when some
theme IDs are missing.

})();
214 changes: 146 additions & 68 deletions book/src/drive/document-count-trees.md

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions book/src/drive/indexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,17 @@ With the layout above, a query like `WHERE color BETWEEN 'red' AND 'tomato'` res

No leaf-level enumeration of distinct color values, no enumeration of individual documents — the count is computed entirely from the tree's pre-aggregated structure.

#### Compound indexes (open question)
#### Compound indexes

What `range_countable` means on a compound index — e.g., `byColorShape = [color, shape]` with `range_countable: true` — is left for later design. The natural reading is "the parent of the *terminating* level of this index", i.e., the `'shape'` tree under each color value, which would itself become a `ProvableCountTree` (and `'circle'` / `'square'` would become `CountTree`s). When that compound's leading prefix is itself another index (`byColor`), the layering of `NonCounted` and counted variants needs to be worked out so neither index's counts pollute the other. We'll cross that bridge when we actually need range queries on a compound index.
`range_countable: true` on a compound index applies at the index's *terminating* level (its last property). For `byColorShape = [color, shape]` with `range_countable: true`:

- `'shape'` (the property-name tree under each color value) becomes a `ProvableCountTree`.
- Each `'circle'` / `'square'` value tree becomes a `CountTree`.
- Documents are referenced as `Element::Reference` leaves under those `CountTree`s, contributing 1 each to the count aggregate.

When the compound's leading prefix is also indexed by another `range_countable` index (e.g. `byColor` is also `range_countable`), sibling continuations under each color `CountTree` are wrapped with `Element::NonCounted` so a doc routed via `byColorShape` doesn't double-count under `byColor`'s color aggregate. The walker (`add_indices_for_index_level_for_contract_operations`) threads a `parent_value_tree_is_range_countable` flag down the recursion to decide when to wrap, regardless of whether the inner tree is itself a `ProvableCountTree`, `CountTree`, or plain `NormalTree`.

End-to-end coverage in `range_countable_index_e2e_tests` (in `packages/rs-drive/src/drive/contract/insert/insert_contract/v0/mod.rs`) pins the storage layout against a real grovedb — including the `count_tree_value_count_excludes_compound_continuation_via_non_counted` test that proves NonCounted-wrapping is load-bearing for compound-index correctness.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
## Tree Type at the Terminal Level

Expand Down Expand Up @@ -518,7 +526,7 @@ Quick checklist for contract authors:
- **Don't index what you won't query.** Each index costs storage on every insert/delete and counts against the per-document-type index limit (10 indexes per type currently).
- **Order index properties from most-selective to least-selective.** A `[country, city]` index is more useful than `[city, country]` for queries like `where country = "FR"`.
- **`unique: true`** when the platform should reject duplicates at the consensus layer. This is the right place for "this should be unique" invariants — don't enforce them application-side.
- **`countable: "countable"`** when you'll regularly call `GetDocumentsCount` filtered by this index's leading columns. Adds a constant-factor overhead on insert/delete; reads become O(1).
- **`countable: "countable"`** when you'll regularly call `GetDocumentsCount` with `==` (or `in`) clauses on **exactly** this index's properties. Adds a constant-factor overhead on insert/delete; reads become O(1). A `countable: true` index counts only queries whose where clauses match its properties exactly — partial-prefix queries are rejected with `WhereClauseOnNonIndexedProperty`, not falling through to a slow scan. Define a separate index per distinct count-query shape you want to support, or set `documentsCountable: true` on the document type for unfiltered totals.
- **`countable: "countableAllowingOffset"`** when you'll *also* want offset / range queries on this index in a future release. Strictly more expensive than plain `"countable"`; only worth it if you need the capability.
- **`null_searchable: true`** (the default) is right for almost all cases. Set to `false` only when documents with all-null indexed values shouldn't be findable through this index — typically a niche optimization to avoid a hot all-null prefix.

Expand Down
6 changes: 2 additions & 4 deletions packages/dapi-grpc/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,12 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig {
// Derive features for versioned messages
//
// "GetConsensusParamsRequest" is excluded as this message does not support proofs
const VERSIONED_REQUESTS: [&str; 58] = [
const VERSIONED_REQUESTS: [&str; 57] = [
"GetDataContractHistoryRequest",
"GetDataContractRequest",
"GetDataContractsRequest",
"GetDocumentsRequest",
"GetDocumentsCountRequest",
"GetDocumentsSplitCountRequest",
"GetIdentitiesByPublicKeyHashesRequest",
"GetIdentitiesRequest",
"GetIdentitiesBalancesRequest",
Expand Down Expand Up @@ -163,13 +162,12 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig {
// - "GetIdentityByNonUniquePublicKeyHashResponse"
//
// "GetEvonodesProposedEpochBlocksResponse" is used for 2 Requests
const VERSIONED_RESPONSES: [&str; 56] = [
const VERSIONED_RESPONSES: [&str; 55] = [
"GetDataContractHistoryResponse",
"GetDataContractResponse",
"GetDataContractsResponse",
"GetDocumentsResponse",
"GetDocumentsCountResponse",
"GetDocumentsSplitCountResponse",
"GetIdentitiesByPublicKeyHashesResponse",
"GetIdentitiesResponse",
"GetIdentitiesBalancesResponse",
Expand Down
Loading
Loading