Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
734 changes: 342 additions & 392 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ members = [
]

[workspace.dependencies]
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
dash-spv-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" }
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
dash-spv-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "5297d61ac13b4bdfc85aef683e3c46e0597e6741" }

# Optimize heavy crypto crates even in dev/test builds so that
# Halo 2 proof generation and verification run at near-release speed.
Expand Down
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();
}
});
}
})();
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.

## 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