Repository navigation
feat(drive): compute a document type's GroveDB layout for the SDKs - #5153
Conversation
`drive::document::layout::document_type_layout` returns the concrete GroveDB layout of one document type: the document type tree, the documents by id (with history when kept), and for each index the property and value trees down to where it ends, each with the tree or element type Drive writes, the zero-contribution wrapper a continuation tree gets under an aggregating value tree, the ranking axes of an indexed tree, the indexes that use the layer and conditions (a unique index's tree of members when a value is null, nullSearchable, skipIfAbsent, preallocated, time windows). Each layer names the node of the structure description it is an instance of. The tree types come from the functions the index walkers call, which now compile under `verify` too (TreeType from grovedb-merk, which grovedb already depends on in both builds). The wrapper matrix of `for_known_path_key_empty_tree_contributing_zero_to_parent` and the continuation condition both walkers inlined are extracted into `zero_contribution_wrapper` and `continuation_contributes_zero`, shared by the walkers, the preallocation path and the layout; decisions and error messages are unchanged. `layout_matches_what_drive_writes` applies 19 contracts covering every index shape, inserts documents and fails on any element whose kind or wrapper the layout does not predict. wasm-sdk (and so evo-sdk) exposes it as `documentTypeLayout(contract, documentTypeName, platformVersion)`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
📖 Book Preview built successfully. Download the preview from the workflow artifacts. Updated at 2026-09-28T20:28:38.302Z |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: dashpay/platform/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (14)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughDrive now builds a recursive layout for a document type from its contract and platform version. The layout records tree and element details, keys, wrappers, indexes, and conditional alternatives. The WASM SDK exposes this layout through ChangesDocument Type Layout
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~60 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant JavaScriptCaller
participant WasmDocumentTypeLayout
participant DriveDocumentTypeLayout
JavaScriptCaller->>WasmDocumentTypeLayout: contract, document type name, platform version
WasmDocumentTypeLayout->>DriveDocumentTypeLayout: compute document type layout
DriveDocumentTypeLayout-->>WasmDocumentTypeLayout: layout model
WasmDocumentTypeLayout-->>JavaScriptCaller: serialized layout object
Suggested reviewers: Merge Risk: ⚪ Minimal · up to No actionable issue is established before merge. The verify-only configuration still needs validation when its dependency is available. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to The new layout API does not appear to expose stored documents or grant storage access. A separate change can alter which index trees are written for some document values, so its compatibility across versions needs confirmation before treating the change as storage-neutral. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 56.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 51 functions across 11 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🕓 Queued for automated review — 1st in line, estimated start in ~25 min (commit f3e69c4)
|
The layout's rangeSeconds / stepSeconds / phaseSeconds were u64 values, which reach JS as BigInts while the TypeScript type says number. Every valid window fits a u32, so they are emitted as u32 (a float above that). The wasm-sdk spec checks a time window level's key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The layout noted "a document that leaves out $ownerId writes nothing" on an indexOnly type's $ownerId level. The walker skips only when the value is absent, and a system property never is where it is indexed. The note now goes on non-system properties the type does not require, as the walker decides; a test covers a required, an optional and a system first property. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t value serde-wasm-bindgen turns a null into undefined, so JS never received the null the TypeScript type promised for a node without a wrapper or an alternative. The value now leaves those keys out, and the type marks them optional. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test both ways Review fixes for the document type layout: - document_type_layout refuses a platform version whose index walkers are not v2 (protocol version 13 and earlier pick other trees for some shapes). - The v1 update walker calls continuation_contributes_zero and level_counts_continuations instead of its own copies, and the v2 top-level insert and delete walkers skip with the new index_only_level_skips_when_absent, which the layout's note uses too. Each call returns exactly the expression it replaces (consensus neutral; these walkers are selected only by protocol version 14). - The conformance test also fails on a layout layer Drive did not write. - One tree-type call per level; the unique null fallback no longer repeats the terminal's indexes and notes; a redundant flat-index guard dropped. - ZeroContributionWrapper re-exported from layout; LayoutRole::name and LayoutNote::code public. - wasm-dpp2 DataContractWasm implements AsRef<DataContract>, so the binding borrows the contract instead of cloning it. - Tests renamed to "should ..."; README marks wrapper/alternative optional; the book states what the fixtures cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dashpay/platform#5153 merged with a review fix: a unique index's null fallback no longer repeats the terminal's indexes and notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: say what the example check runs: the parser, not the meta-schema @dashevo/evo-sdk is built without dpp's validation feature, so DataContract.fromJSON(json, true, 14) runs the contract parser but not the document meta-schema or the parser checks gated behind that feature. The README, the examples registry and the validation script claimed more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: show a document type's GroveDB layout, computed by Drive A "GroveDB layout" button in the inspector opens every tree and element Drive writes for the selected document type: the documents by id (and revisions), and for each index the property and value trees down to the [0] where it ends, with each layer's element (count, sum, ranked, ...), its zero-contribution wrapper, the indexes that use it, conditions (the null fallback of a unique index, skipIfAbsent, preallocated, time window overlap) and a link to that kind of layer in the GroveDB structure viewer. The layout comes from documentTypeLayout in @dashevo/evo-sdk (dashpay/platform#5153), so it follows Drive's own rules. It is feature-detected: with an SDK that predates it, the panel says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: refresh the layout fixtures from the merged documentTypeLayout dashpay/platform#5153 merged with a review fix: a unique index's null fallback no longer repeats the terminal's indexes and notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs: say what the example check runs: the parser, not the meta-schema @dashevo/evo-sdk is built without dpp's validation feature, so DataContract.fromJSON(json, true, 14) runs the contract parser but not the document meta-schema or the parser checks gated behind that feature. The README, the examples registry and the validation script claimed more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: show a document type's GroveDB layout, computed by Drive A "GroveDB layout" button in the inspector opens every tree and element Drive writes for the selected document type: the documents by id (and revisions), and for each index the property and value trees down to the [0] where it ends, with each layer's element (count, sum, ranked, ...), its zero-contribution wrapper, the indexes that use it, conditions (the null fallback of a unique index, skipIfAbsent, preallocated, time window overlap) and a link to that kind of layer in the GroveDB structure viewer. The layout comes from documentTypeLayout in @dashevo/evo-sdk (dashpay/platform#5153), so it follows Drive's own rules. It is feature-detected: with an SDK that predates it, the panel says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: refresh the layout fixtures from the merged documentTypeLayout dashpay/platform#5153 merged with a review fix: a unique index's null fallback no longer repeats the terminal's indexes and notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: show what a document costs, in Dash and dollars, per index Selecting a document type shows about what one of its documents costs; Cost opens the breakdown, computed by Drive (documentCreateCost in @dashevo/evo-sdk, dashpay/platform#5159): the first document with a set of index values and a later one with the same values; the storage of the document itself and of each index on its own (the part it shares with other indexes, paid once, and its own); prepaid trees and a ttl type's expiration entry; the processing (exact parts marked, the rest estimated for a number of stored documents, a key type and a fee increase); the contract's action fee, token cost and contest fund; and the refund on delete. "Adjust the document" gives an on/off switch per optional field and a length per variable-size one; fixed-size fields need no input. The Dash price comes from CoinGecko (Coinbase if that fails), kept five minutes, and can be typed in. Feature-detected: with an SDK that predates documentCreateCost the inspector says so. The layout and cost loaders share one local SDK module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Basic explanation
What this does: Drive stores each document type as a set of nested GroveDB trees: one for the documents and, for every index, a tree per indexed property and per value, down to where the index ends. Which kind of tree each one is depends on the index's keywords: a plain tree, a tree that counts or sums its entries, a ranked tree, and so on. Until now the only way to see this for a real contract was to read the insert code. This PR adds a function,
document_type_layout, that computes the layout of any document type from the contract alone, using the same rules Drive uses when it inserts documents. It is exposed to JavaScript asdocumentTypeLayout(contract, documentTypeName, platformVersion)in wasm-sdk, and so in@dashevo/evo-sdk.Value:
Risks: Low, and no consensus change.
verifyfeature (the one wasm-sdk uses). No new crate enters the wasm build: grovedb already depends on grovedb-merk there.Issue being fixed or feature implemented
The contract visualizer needs a way to show a document type's index structure. The GroveDB structure description (
grovedb-structure.json) covers every document type at once, so its document layers are templates: "an index property" that can be any of 8 tree kinds. The rules that pick the kind for a given index (primary_key_tree_type,property_name_tree_type_and_ranked_axes_for_level,index_level_tree_types_with_continuation_demotion,terminal_member_tree_type, and the wrapper choice for continuation trees) wereserver-only, so no client could compute a concrete layout.What was done?
drive::document::layout(new,serverorverify):document_type_layout(document_type, platform_version) -> DocumentTypeLayoutwalks the document type'sIndexLevelstructure. It refuses (UnknownVersionMismatch) a platform version whose index walkers are not v2, i.e. before protocol version 14. For each layer it gives:[0], a property name, a time-window grid key such as$createdAt#86400#3600, a flat indexOnly key) or a family of keys (the document id, a property's values, the window starts, an indexOnly entry's terminal components, the revision time);Item/ItemWithSumItem/Reference/ReferenceWithSumItem;NonCounted,NotSummed,NotCountedOrSummed), and the ranking axes of an indexed tree;[0]records its alternative: the tree of members Drive writes instead when a value is null. The alternative does not repeat the[0]'s indexes and notes.structure_node()), for linking to the viewer.to_value()gives the JSON shape the SDK returns. A layer that has no wrapper or no alternative leaves the key out, and time-window seconds are plain numbers (not BigInts) in JavaScript.ZeroContributionWrapperis re-exported fromlayout, andLayoutRole::nameandLayoutNote::code(the codesto_valueemits) are public.primary_key_tree_type,ranked_index_tree_typeandindex_level_tree_typescompile underverifyas well asserver, takingTreeTypefrom grovedb-merk (a new optional direct dependency of rs-drive, enabled by both features).time_range_index_keys, which needs server-only key types, staysserver-only.LowLevelDriveOperation::for_known_path_key_empty_tree_contributing_zero_to_parentmoved intozero_contribution_wrapper, which returns a typed refusal; the dispatcher maps each refusal to its existing error text.continuation_contributes_zeroandlevel_counts_continuations.index_only_level_skips_when_absent, which the layout's note uses too.documentTypeLayout(contract, documentTypeName, platformVersion), with aDocumentTypeLayoutTypeScript type. evo-sdk re-exports it. It borrows the contract through a newAsRef<DataContract>for wasm-dpp2'sDataContractWasm, rather than cloning it.In-place changes to shipped generations
add_indices_for_index_level_for_contract_operationsv2 and the preallocation path now callcontinuation_contributes_zero(parent_value_tree_type, parent_counts_continuations, sub_level). The function returns exactly what the inlined condition did:!matches!(parent_value_tree_type, TreeType::NormalTree) && (!parent_counts_continuations || sub_level.count_exempt_branch()).level_counts_continuations(level)is the inlinedlevel.ranked_count_grouping() || level.count_propagating().for_known_path_key_empty_tree_contributing_zero_to_parentnow matches onzero_contribution_wrapper, a direct transcription of its former match:NonCounted,NotCountedOrSummed,NotSummed, or the unwrapped insert);NotSupportederrors, with the same text.update_document_for_contract_operationsv1 (the update walker protocol version 14 selects) now calls the same two functions: at its two continuation dispatches (plain and bucketed) and at the four places it tracks whether a level counts its continuations. Eachmatches!(parent_value_tree_type, TreeType::NormalTree) || (parent_counts_continuations && !level.count_exempt_branch())is!continuation_contributes_zero(parent_value_tree_type, parent_counts_continuations, level), and eachlevel.ranked_count_grouping() || level.count_propagating()islevel_counts_continuations(level).add_indices_for_top_index_level_for_contract_operationsv2 andremove_indices_for_top_index_level_for_contract_operationsv2 now skip withindex_only_level_skips_when_absent(document_type, property_name), which returns exactly the inlineddocument_type.index_only() && !document_type.required_fields().contains(property_name).Before and after
The marketplace example's
listingtype (byShopovershopId,$createdAtcountable;byCategoryPrice;newListings, a daily window stepping hourly over$createdAt, countable;documentsCountable):index_property → value → members / next_property, each "one of" several tree kinds. Which kindlistinggets is only in the insert code.documentTypeLayout(contract, 'listing', v14), printed as a tree:An indexOnly type with a ranked, summed index (the Drive test contract's
tip), and DPNS's unique name index with its fallback:How Has This Been Tested?
cargo test -p drive --lib drive::document::layout: 5 tests.should_lay_out_what_drive_writesapplies 19 contracts fromtests/supporting_files: family (plain, optional fields, countable, with history), dashpay, references, restaurants, trending (with the sibling variant), the Yappr likes (plain and two preallocated variants), the Yappr feed, index-only scalar terminal, tally, tip-jar, and grades (plain, ranked, compound ranked).nullSearchable: false, a preallocated terminal's entries, a skipIfAbsent level's values). It also requires the fixtures to reach every layer role, at least one wrapper, one ranked tree, one time window, one index entry and one null fallback.Terminal "Members" ... Drive wrote CountTree where the layout says Tree. Giving indexOnly types a primary key tree fails it withthe layout has PrimaryKey "PrimaryKey" but Drive wrote nothing there.should_name_a_node_of_the_structure_description_for_every_layerchecks each role'sstructure_node()againstdrive_structure().should_store_each_document_of_a_type_with_history_as_a_tree_of_revisions.should_refuse_a_version_that_selects_other_index_walkers: protocol version 13 (v1 walkers) is refused, the latest is not.should_skip_only_an_optional_property_of_an_index_only_type_when_absent: in the Yappr likes contract, the requiredmark.aand the system$ownerIdcarry no skip note, the optionalmark.bdoes, andtip(not skipping) has none.cargo test -p drive --lib: 3947 passed, 6 ignored, 0 failed.cargo clippy -p drive --all-targets,cargo clippy -p drive --no-default-features --features verify, andcargo clippy -p wasm-sdk -p wasm-dpp2 --target wasm32-unknown-unknown: clean.cargo fmt: clean.yarn workspace @dashevo/wasm-sdk build, thenmocha tests/unit/document-type-layout.spec.ts: 7 passing. The spec covers:NonCounted;ProvableCountIndexedTreewith acountaxis;eslinton the new spec: clean.Breaking Changes
None.
Checklist:
structure.rs, regeneratedgrovedb-structure.json, and checked the structure viewer link posted on this pull requestFor repository code-owners and collaborators only
🤖 Generated with Claude Code
PR Hygiene ·
f3e69c4/skip-botsproceeds without the ones not yet reported/self-reviewedonce the bots are donejs-wasm-sdk(packages/js-evo-sdk/README.md,packages/wasm-sdk/src/document_type_layout.rs,packages/wasm-sdk/src/lib.rsand 1 more) — shumkovrs-drive— you own itWhen every box is checked the
PR Hygienecheck passes and this can merge.Summary by CodeRabbit
documentTypeLayoutAPI, which locally shows how a document type is stored, including its indexes, tree and element types, and conditional layout choices.