pub const PLATFORM_V14: PlatformVersion;Expand description
v14 hosts five consensus changes:
- Contract-level ranked aggregates (this branch): an index can declare that its groups are rankable by an aggregate, so a query like “top 5 restaurants by average grade” is served from an ordered secondary tree in O(log n + k) with a proof, instead of being rejected.
- The shared-prefix aggregate index fix: a data contract declaring
an aggregating (countable / summable) index that terminates at a
property which is also the prefix of a compound index (e.g. summable
[a]next to[a, b]) registered successfully but rejected every document insert for most flag combinations, because Drive could not legally hang the compound continuation tree under the aggregating per-value tree. The v2 document index walkers (plus the v1 update walker) that fix it gate here as well: tree types derive through a shared continuation-demotion helper (provable count-bearing value trees with compound continuations demote toCountSumTree, since grovedb rejects count-suppressed children under provable count parents by design) and continuation inserts route through the completed zero-contribution wrapper matrix. No state migration is needed: shapes without compound continuations produce bit-identical operations, the broken shapes could never hold documents, and the one previously-insertable shape the demotion changes (a provable count-bearing value tree whose continuations were all sum-bearing — insertable pre-v14 only through an unenforced grovedb batch guard) simply getsCountSumTreevalue trees for values first seen at v14+, which readers treat identically. - The contested vote poll index cross-check: the index named by a document create transition’s prefunded voting balance keys the vote poll, its stored info, its end-date entry and its prefunded specialized balance, while the contested index the contender is inserted under always comes from the document type. Up to v13 nothing tied the two together, so a submitter could register and fund a contest under a vote poll describing a different index than the one the contest was created on — which halts the chain when that poll ends — or open a contest for a document that is not a contested resource at all.
- Relative daily withdrawal limit: the flat 2000 Dash per 24 hours that
applied from v8 becomes 15% of the total credits Platform held a day ago
(
SYSTEM_LIMITS_V4.daily_withdrawal_limit_percent, read bydaily_withdrawal_limitv2 throughDPP_METHOD_VERSIONS_V3), never below one maximal withdrawal (max_withdrawal_amount) so every accepted withdrawal eventually fits and cannot block the pooling queue. The base is capped atmax_daily_withdrawal_amount(4000 Dash, Core’s unlock capacity per day under V24 as written); the credit inflows of the active window — every credit mint, recorded per block byrecord_credit_inflows_for_withdrawalsin the credit inflows sum tree — are added after the cap, so the limit counts net outflow and a matching deposit -> withdraw cycle does not consume the capped budget of other users (#4471). Outflow funded by same-window deposits may therefore exceed the cap; this mirrors the net credit-pool rule Core adopts for V24 alongside this change (tracked in #4471), which must land before V24 activates. Both the inflows and the pooled reservations count over the interval after the base snapshot only — an entry the snapshot already reflects is neither added nor subtracted again. The base is the total credits recorded at the latest block at least 24 hours before the current one:DRIVE_ABCI_METHOD_VERSIONS_V10turns onrecord_total_credits_history_for_withdrawals, which checks the total credits every block once fees and epoch rewards are in, writes it under the withdrawals tree keyed by block time whenever it changed (an entry describes the total until the next one) and prunes entries older than the one the limit reads, andDRIVE_VERSION_V9’s identity withdrawal table bumpscalculate_current_withdrawal_limitto 1 to read that lagged value. Until an entry is a day old — the first day after activation — the flat 2000 Dash keeps applying, so the lag cannot be skipped by inflating the total before or at activation. The lag is the guardrail: a sudden jump in the total credits does not raise the limit for a day. Amounts already pooled in the last 24 hours keep counting against the maximum exactly as before. Pre-V24 Core caps unlocks atLimitAmountV22(2000 Dash) per block, with the amount checked only at block level, so any daily total is still minable across blocks; V24’s 4000 Dash per 576-block window matches the capped base and is raised to the same net rule before activation (see above). - Time-range indexes: an index can declare a
timeRangetransform that buckets a required system timestamp ($createdAt/$updatedAt/$transferredAt) into fixed-length, regularly-spaced, optionally overlapping windows declared in seconds (range/step, plus an optionalphase < stepalignment offset). Each grid gets its own index subtree — the level is keyed by the property name qualified with the grid — so several grids may bucket one timestamp side by side. A document is stored once per containing bucket per grid (the v2 insert/delete and v1 update walkers carry the fan-out; the per-document write amplification is capped per index bySystemLimits::max_time_range_overlap_factor), and the v1getDocumentshandler resolves the newIN_TIME_RANGEoperator — bare"newest"/"oldest"on a single-grid field, or a structured[selector, range, step(, phase)]operand naming one grid — into a bucket-start equality from committed block time, making “newest window” trending/leaderboard document and count/sum/avg queries provable.unique: trueis admitted only for non-overlapping windows (range == step) sourced from the immutable$createdAt.
The first two are orthogonal by construction: the ranked upgrade decides the
property-name tree type, the demotion decides the value tree type
one level below it, and a demoted CountSumTree value tree contributes
its (count, sum) to a ranked indexed parent exactly as the provable
variant did — so ranked secondaries keep ranking correctly over
shared-prefix shapes.
Until a contract uses the ranked or time-range grammar, the only v14 behavior changes are the shared-prefix fix, the contested-index cross-check, the index-reorder schema-compatibility fix and the relative daily withdrawal limit; everything else matches v13:
CONTRACT_VERSIONS_V6pointsdocument_type_schemaat the v3 document meta-schema, which hosts the ranked index keywords (rankedCountable/rankedSummable/rankedAverageable), therefersToreference keyword and thetimeRangeindex transform. v13 keeps validating against meta-schema v2, where those keys are rejected as unknown properties, so a pre-v14 contract cannot smuggle them in. It also bumpsvalidate_schema_compatibilityto 1, which strips the top-levelindiceskey before diffing the old and new document type schemas: index immutability is enforced byvalidate_updatev1’s name-keyed comparison, so a contract update that merely reorders theindicesarray validates cleanly instead of hitting the unsupported-keyword hard error (an internal error under v13).DRIVE_VERSION_V9carriesDRIVE_DOCUMENT_METHOD_VERSIONS_V4, adding thedetect_ranked_moderouting slot, plus the grove-method slots for creating the three indexed tree variants and the verify-method slot forverify_ranked_top_k_proof. All are 0 today. The same table bumps the four index walkers to v2 and the document update walker to v1 for the shared-prefix fix; those same walker versions carry the time-range bucket fan-out, so both features gate on one table entry.DRIVE_ABCI_QUERY_VERSIONS_V3bumpsdocument_query_helpers.compute_aggregate_mode_and_check_limit0 → 2, opening two routes on the v1 document-query handler: the ranked path (a grouped aggregate whose singleorder_bynames the selected aggregate —ORDER BY <agg> [ASC|DESC] LIMIT n [OFFSET m]) and the boolean-HAVINGrange path (a grouped aggregate carrying exactly onehavingclause on the selected aggregate —GROUP BY p HAVING <agg> <op> <value> LIMIT n), the latter served as a value-bounded range read of the covering ranked index’s axis secondary. v13 and earlier keep the v1 table and therefore keep rejecting both shapes, so mixed-version networks agree across the upgrade.DRIVE_ABCI_VALIDATION_VERSIONS_V10bumpsdocument_create_transition_structure_validation0 → 1, requiring a contested create transition’s prefunded voting balance to name the same vote poll the document itself resolves to, and rejecting one on a document that resolves to no contested index. It also bumps document create state validation to 2 and document replace state validation to 1, enforcingrefersTodocument references: a document whose reference property names an identity or contract that does not exist is rejected. v13 keeps the v9 table and therefore keeps accepting all of these, so replay of pre-upgrade blocks is unchanged.DOCUMENT_VERSIONS_V4bumpsdocument_serialization_versionto default 3: documents are stamped with the contract version their bytes conform to (a varint after the format prefix), enabling therequiredSinceproperty keyword — a contract update may add a new required property annotated with the version that update creates. Documents stamped below a property’srequiredSincekeep the presence-flagged layout they were written with, so the latest contract alone reconstructs every stamp’s layout and no historical contract lookups are ever needed. Reads dispatch on the byte prefix, so formats 0–2 (all pre-v14 documents) deserialize exactly as before with an unstamped (pre-annotation) layout.
The wire surface changes only additively: GetDocumentsRequestV1
already carries selects / group_by / order_by / limit /
offset; the ranked response is an additive ResultData.ranked
variant, whose skipped field is likewise additive; and the v1
where-clause operator enum gains IN_TIME_RANGE = 11, which pre-v14
servers reject as an unknown operator rather than misread (the v0 wire
has no time-range operator at all).