Skip to main content

platform_version/version/
v14.rs

1use crate::version::consensus_versions::ConsensusVersions;
2use crate::version::dpp_versions::dpp_asset_lock_versions::v1::DPP_ASSET_LOCK_VERSIONS_V1;
3use crate::version::dpp_versions::dpp_contract_versions::v6::CONTRACT_VERSIONS_V6;
4use crate::version::dpp_versions::dpp_costs_versions::v1::DPP_COSTS_VERSIONS_V1;
5use crate::version::dpp_versions::dpp_document_versions::v4::DOCUMENT_VERSIONS_V4;
6use crate::version::dpp_versions::dpp_factory_versions::v1::DPP_FACTORY_VERSIONS_V1;
7use crate::version::dpp_versions::dpp_identity_versions::v1::IDENTITY_VERSIONS_V1;
8use crate::version::dpp_versions::dpp_method_versions::v3::DPP_METHOD_VERSIONS_V3;
9use crate::version::dpp_versions::dpp_state_transition_conversion_versions::v2::STATE_TRANSITION_CONVERSION_VERSIONS_V2;
10use crate::version::dpp_versions::dpp_state_transition_method_versions::v2::STATE_TRANSITION_METHOD_VERSIONS_V2;
11use crate::version::dpp_versions::dpp_state_transition_serialization_versions::v3::STATE_TRANSITION_SERIALIZATION_VERSIONS_V3;
12use crate::version::dpp_versions::dpp_state_transition_versions::v4::STATE_TRANSITION_VERSIONS_V4;
13use crate::version::dpp_versions::dpp_token_versions::v3::TOKEN_VERSIONS_V3;
14use crate::version::dpp_versions::dpp_validation_versions::v5::DPP_VALIDATION_VERSIONS_V5;
15use crate::version::dpp_versions::dpp_voting_versions::v2::VOTING_VERSION_V2;
16use crate::version::dpp_versions::DPPVersion;
17use crate::version::drive_abci_versions::drive_abci_checkpoint_parameters::v1::DRIVE_ABCI_CHECKPOINT_PARAMETERS_V1;
18use crate::version::drive_abci_versions::drive_abci_method_versions::v10::DRIVE_ABCI_METHOD_VERSIONS_V10;
19use crate::version::drive_abci_versions::drive_abci_query_versions::v2::DRIVE_ABCI_QUERY_VERSIONS_V2;
20use crate::version::drive_abci_versions::drive_abci_structure_versions::v2::DRIVE_ABCI_STRUCTURE_VERSIONS_V2;
21use crate::version::drive_abci_versions::drive_abci_validation_versions::v10::DRIVE_ABCI_VALIDATION_VERSIONS_V10;
22use crate::version::drive_abci_versions::drive_abci_withdrawal_constants::v3::DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V3;
23use crate::version::drive_abci_versions::DriveAbciVersion;
24use crate::version::drive_versions::v9::DRIVE_VERSION_V9;
25use crate::version::fee::v3::FEE_VERSION3;
26use crate::version::protocol_version::PlatformVersion;
27use crate::version::system_data_contract_versions::v3::SYSTEM_DATA_CONTRACT_VERSIONS_V3;
28use crate::version::system_limits::v4::SYSTEM_LIMITS_V4;
29use crate::version::ProtocolVersion;
30
31pub const PROTOCOL_VERSION_14: ProtocolVersion = 14;
32
33/// v14 hosts thirty-one consensus changes:
34///
35/// 1. **Contract-level ranked aggregates**: an index can
36///    declare that its groups are rankable by an aggregate, so a query like
37///    "top 5 restaurants by average grade" is served from an ordered
38///    secondary tree in O(log n + k) with a proof, instead of being rejected.
39/// 2. **The shared-prefix aggregate index fix**: a data contract declaring
40///    an aggregating (countable / summable) index that terminates at a
41///    property which is also the prefix of a compound index (e.g. summable
42///    `[a]` next to `[a, b]`) registered successfully but rejected every
43///    document insert for most flag combinations, because Drive could not
44///    legally hang the compound continuation tree under the aggregating
45///    per-value tree. The v2 document index walkers (plus the v1 update
46///    walker) that fix it gate here as well: tree types derive through a
47///    shared continuation-demotion helper (provable count-bearing value
48///    trees with compound continuations demote to `CountSumTree`, since
49///    grovedb rejects count-suppressed children under provable count
50///    parents by design) and continuation inserts route through the
51///    completed zero-contribution wrapper matrix. No state migration is
52///    needed: shapes without compound continuations produce bit-identical
53///    operations, the broken shapes could never hold documents, and the
54///    one previously-insertable shape the demotion changes (a provable
55///    count-bearing value tree whose continuations were all sum-bearing —
56///    insertable pre-v14 only through an unenforced grovedb batch guard)
57///    simply gets `CountSumTree` value trees for values first seen at
58///    v14+, which readers treat identically.
59/// 3. **The contested vote poll index cross-check**: the index named by a
60///    document create transition's prefunded voting balance keys the vote
61///    poll, its stored info, its end-date entry and its prefunded
62///    specialized balance, while the contested index the contender is
63///    inserted under always comes from the document type. Up to v13 nothing
64///    tied the two together, so a submitter could register and fund a
65///    contest under a vote poll describing a different index than the one
66///    the contest was created on — which halts the chain when that poll
67///    ends — or open a contest for a document that is not a contested
68///    resource at all. State validation also prevents a non-contested create
69///    from occupying a live contested document's id before the contest winner
70///    is awarded into primary storage. Drive's contested insert also recreates
71///    an abstain or lock vote tree over the storage an earlier poll's cleanup
72///    left orphaned (it only removed the trees that received votes), so a
73///    resource can be contested again instead of failing with
74///    `CorruptedContractIndexes`.
75/// 4. **Relative daily withdrawal limit**: the flat 2000 Dash per 24 hours that
76///    applied from v8 becomes 15% of the total credits Platform held a day ago
77///    (`SYSTEM_LIMITS_V4.daily_withdrawal_limit_percent`, read by
78///    `daily_withdrawal_limit` v2 through `DPP_METHOD_VERSIONS_V3`), never below
79///    one maximal withdrawal (`max_withdrawal_amount`) so every accepted
80///    withdrawal eventually fits and cannot block the pooling queue. The base has
81///    no fixed cap: what Core will mine bounds pooling through the Core-anchored
82///    limit of note 74 instead. The credit inflows of the active window — every
83///    credit mint, recorded per block by `record_credit_inflows_for_withdrawals`
84///    in the credit inflows sum tree — are added to the base, so the limit
85///    counts net outflow and a matching deposit -> withdraw cycle does not
86///    consume the budget of other users (#4471), mirroring Core v24's net
87///    credit-pool rule. Both the inflows and the pooled reservations count over the
88///    interval after the base snapshot only — an entry the snapshot already
89///    reflects is neither added nor subtracted again. The base is
90///    the total credits recorded at the latest block at least 24 hours before
91///    the current one: `DRIVE_ABCI_METHOD_VERSIONS_V10` turns on
92///    `record_total_credits_history_for_withdrawals`, which checks the total
93///    credits every block once fees and epoch rewards are in, writes it under
94///    the withdrawals tree keyed by block time whenever it changed (an entry
95///    describes the total until the next one) and prunes entries older than the
96///    one the limit reads, and `DRIVE_VERSION_V9`'s identity withdrawal table
97///    bumps `calculate_current_withdrawal_limit` to 1 to read that lagged
98///    value. Until an entry is a day old — the first day after activation — the
99///    flat 2000 Dash keeps applying, so the lag cannot be skipped by inflating
100///    the total before or at activation. The lag is the guardrail: a sudden
101///    jump in the total credits does not raise the limit for a day. Amounts
102///    already pooled in the last 24 hours keep counting against the maximum
103///    exactly as before. Pre-V24 Core caps unlocks at `LimitAmountV22` (2000
104///    Dash) per *block*, with the amount checked only at block level, so any
105///    daily total is still minable across blocks; V24 limits the net drop of
106///    its credit pool per 576-block window, which note 74 follows.
107/// 5. **Time-range indexes**: an index can declare a `timeRange` transform
108///    that buckets a required system timestamp (`$createdAt` /
109///    `$updatedAt` / `$transferredAt`) into fixed-length, regularly-spaced,
110///    optionally overlapping windows declared in seconds (`range` / `step`,
111///    plus an optional `phase < step` alignment offset). Each grid gets its
112///    own index subtree — the level is keyed by the property name qualified
113///    with the grid — so several grids may bucket one timestamp side by
114///    side. A document is stored once per containing bucket per grid (the
115///    v2 insert/delete and v1 update walkers carry the fan-out; the
116///    per-document write amplification is capped per index by
117///    `SystemLimits::max_time_range_overlap_factor`), and the v1
118///    `getDocuments` handler resolves the new `IN_TIME_RANGE` operator —
119///    a typed `TimeRangeSelection` operand: `NEWEST`/`OLDEST` (resolved to
120///    a bucket-start equality from committed block time) or `BY_START`
121///    (naming any window, current or historic, by its grid-aligned start),
122///    with a `grid` member naming one grid where several bucket the field
123///    — making trending/leaderboard document and count/sum/avg queries
124///    provable over the current or any named window. `unique: true` is
125///    admitted only for non-overlapping windows (`range == step`) sourced
126///    from the immutable `$createdAt`.
127/// 6. **Deterministic token reward math**: `DistributionFunction::evaluate`
128///    (logarithmic, inverted-logarithmic, exponential and polynomial perpetual
129///    distributions) computes `ln`/`exp`/`pow` through the pinned pure-Rust
130///    `libm` crate instead of the platform C library. musl's `log` takes an
131///    FMA path on aarch64 and a non-FMA path on x86_64, so the two disagree by
132///    1 ulp on some inputs; a contract owner could pick parameters whose reward
133///    sat within that ulp of an integer, and `floor` then minted different
134///    amounts on the two architectures, splitting the app hash both at claim
135///    time and at contract registration (validation evaluates the start
136///    value). Gated on `distribution_function_evaluate_version` so both
137///    architectures switch at the same height; pre-v14 blocks replay on the
138///    old math byte-for-byte. `log`/`exp` have no architecture dispatch and
139///    `pow`'s only arch-touching call is the correctly-rounded `sqrt`, so the
140///    result is bit-identical on every target Platform builds for. The goal
141///    is determinism, not correct rounding: on a boundary tuple the host
142///    libm (glibc, macOS) can still be 1 ulp away, so anything predicting
143///    rewards with host math may differ from consensus by one unit.
144///
145/// The first two are orthogonal by construction: the ranked upgrade decides the
146/// *property-name* tree type, the demotion decides the *value* tree type
147/// one level below it, and a demoted `CountSumTree` value tree contributes
148/// its (count, sum) to a ranked indexed parent exactly as the provable
149/// variant did — so ranked secondaries keep ranking correctly over
150/// shared-prefix shapes.
151///
152/// Until a contract uses the ranked or time-range grammar, the only v14
153/// behavior changes are the shared-prefix fix, the contested-index
154/// cross-check, the index-reorder schema-compatibility fix and the relative
155/// daily withdrawal limit; everything else matches v13:
156///
157/// * `CONTRACT_VERSIONS_V6` points `document_type_schema` at the v3 document
158///   meta-schema, which hosts the ranked index keywords
159///   (`rankedCountable` / `rankedSummable` / `rankedAverageable`), the
160///   `refersTo` reference keyword and the `timeRange` index transform. v13
161///   keeps validating against meta-schema v2, where those keys are rejected
162///   as unknown properties, so a pre-v14 contract cannot smuggle them in.
163///   It also bumps `validate_schema_compatibility` to 1, which strips the
164///   top-level `indices` key before diffing the old and new document type
165///   schemas: index immutability is enforced by `validate_update` v1's
166///   name-keyed comparison, so a contract update that merely reorders the
167///   `indices` array validates cleanly instead of hitting the
168///   unsupported-keyword hard error (an internal error under v13).
169/// * `DRIVE_VERSION_V9` carries `DRIVE_DOCUMENT_METHOD_VERSIONS_V4`, adding
170///   the `detect_ranked_mode` routing slot, plus the grove-method slots for
171///   creating the three indexed tree variants and the verify-method slot for
172///   `verify_ranked_top_k_proof`. All are 0 today. The same table bumps the
173///   four index walkers to v2 and the document update walker to v1 for the
174///   shared-prefix fix; those same walker versions carry the time-range
175///   bucket fan-out, so both features gate on one table entry. It also sets
176///   `insert_contested.fetch_charter_election_windows` to `Some(0)`: a
177///   moderation election (an `electedCharter` contest) runs on its target
178///   contract's join and vote windows, which the document create join check
179///   and the contested insert read.
180/// * `DRIVE_ABCI_QUERY_VERSIONS_V2` bumps
181///   `document_query_helpers.compute_aggregate_mode_and_check_limit` 0 → 2,
182///   opening two routes on the v1 document-query handler: the ranked path
183///   (a grouped aggregate whose single `order_by` names the selected
184///   aggregate — `ORDER BY <agg> [ASC|DESC] LIMIT n [OFFSET m]`) and the
185///   boolean-`HAVING` range path (a grouped aggregate carrying exactly one
186///   `having` clause on the selected aggregate — `GROUP BY p HAVING <agg>
187///   <op> <value> LIMIT n`), the latter served as a value-bounded range
188///   read of the covering ranked index's axis secondary. v13 and earlier
189///   keep the v1 table and therefore keep rejecting both shapes, so
190///   mixed-version networks agree across the upgrade.
191/// * `DRIVE_ABCI_VALIDATION_VERSIONS_V10` bumps
192///   `document_create_transition_structure_validation` 0 → 1, requiring a
193///   contested create transition's prefunded voting balance to name the
194///   same vote poll the document itself resolves to, and rejecting one on a
195///   document that resolves to no contested index. It also bumps document
196///   create state validation to 2, enforcing `refersTo` document references
197///   and rejecting a non-contested create whose id is already present in the
198///   contested tree. Document replace state validation 1 enforces the same
199///   reference checks, re-validates a `refersTo: deletableDocument`
200///   reference on every replace (a dead one must be repointed or cleared),
201///   and lets an `immutable` one be cleared once its target is deleted.
202///   Document create structure validation 1 and replace structure
203///   validation 0 (extended in place) refuse a `distinctFrom` identifier
204///   property equal to the value it must differ from
205///   (`DocumentPropertyNotDistinctError`, 10419); transfer and purchase
206///   structure validation 0, extended in place, judge the stored document's
207///   `$ownerId` declarations against the new owner.
208///   v13 keeps the v9 table and therefore keeps accepting all of these, so
209///   replay of pre-upgrade blocks is unchanged.
210/// * `DRIVE_ABCI_VALIDATION_VERSIONS_V10` also bumps the identity create from
211///   addresses `advanced_structure` 0 → 1: a key whose proof of possession fails
212///   is refused unpaid instead of charging the inputs a penalty, since the
213///   address witnesses do not sign those proofs. v13 keeps the paid refusal of v0.
214/// * `DOCUMENT_VERSIONS_V4` bumps `document_serialization_version` to
215///   default 3: documents are stamped with the contract version their bytes
216///   conform to (a varint after the format prefix), enabling the
217///   `requiredSince` property keyword — a contract update may add a new
218///   required property annotated with the version that update creates.
219///   Documents stamped below a property's `requiredSince` keep the
220///   presence-flagged layout they were written with, so the latest contract
221///   alone reconstructs every stamp's layout and no historical contract
222///   lookups are ever needed. Reads dispatch on the byte prefix, so
223///   formats 0–2 (all pre-v14 documents) deserialize exactly as before with
224///   an unstamped (pre-annotation) layout.
225/// 7. **Client-side GroveDB proof envelope floor (not a version-table
226///    entry)**: clients refuse the legacy V0 proof envelope at every protocol
227///    version through
228///    `drive::verify::grovedb_proof_envelope::MINIMUM_GROVEDB_PROOF_ENVELOPE_VERSION`,
229///    so nothing about it is gated on v14. The note keeps its number so the
230///    later notes keep theirs.
231/// 8. **Epoch-based perpetual distribution claims stop wrapping**:
232///    `RewardDistributionType::max_cycle_moment` (the cap on how far one claim
233///    may redeem, selected by
234///    `TOKEN_VERSIONS_V3.reward_distribution_max_cycle_moment_version` 1)
235///    computes `start + interval * cycles` in `u64` with saturating
236///    arithmetic and narrows back to `EpochIndex` only after capping at the
237///    last completed cycle moment (`current cycle moment - interval`, the
238///    previous epoch for an interval of one as before; for wider intervals the
239///    same cycles are paid, but the cap now sits on a cycle boundary, the only
240///    shape in which `evaluate_interval`'s fixed-amount step count and its
241///    per-cycle loop agree). Up to v13 the sum was taken in `u16`: a
242///    fixed-amount function allows 32,767 cycles, so any epoch interval of
243///    three or more with a nonzero start (or two with a start at epoch two
244///    or later) pushed the cap past `u16::MAX`. Release builds wrap, the cap landed below the
245///    start, `evaluate_interval` saw an empty range and the claim was
246///    refused with `InvalidTokenClaimNoCurrentRewards` on every attempt. The
247///    v0 arithmetic is kept, wrapping explicitly, so those refusals replay.
248/// 9. **Evonode reward cycles weighted by the epochs they span**: the
249///    per-cycle evaluator in `DistributionFunction::evaluate_interval` asks
250///    the participation ratio for the epochs a cycle covers
251///    (`TOKEN_VERSIONS_V3.distribution_function_cycle_epochs_version` 1:
252///    `cycle moment - interval + 1 ..= cycle moment`). Up to v13 it passed the
253///    cycle's step index as if it were an epoch, which coincides only for an
254///    interval of one; for a wider interval it named epochs before the
255///    distribution started, outside the epoch window the claim loads, and an
256///    `EvonodesByParticipation` claim with a function other than a fixed
257///    amount failed as an internal error (reachable only once item 8 let the
258///    cap stop wrapping). Interval-one distributions are unchanged.
259/// 10. **A zero epoch interval is rejected at registration**:
260///     `RewardDistributionType::validate_structure_interval` v1
261///     (`CONTRACT_VERSIONS_V6.token_versions.validate_structure_interval`)
262///     refuses an `EpochBasedDistribution` with `interval: 0` with the new
263///     `InvalidTokenDistributionEpochIntervalTooShortError` (code 10828) on
264///     contract create and update. Up to v13 the epoch arm enforced nothing,
265///     so such a contract registered and every claim on it failed as an
266///     internal error, since no cycle can be computed from a zero step. Block
267///     and time minimums are unchanged.
268/// 11. **Gas paid by the contract owner**: a token-paid document action's
269///     `gasFeesPaidBy` (offered by the document type's token cost, asked for by
270///     the transition's `$tokenPaymentInfo`) is acted on. Both values were
271///     carried but ignored up to v13, where the signer always paid. Batch
272///     transform v2 resolves one payer for the batch (`GasFeesPaidBy::resolve`)
273///     and reads the contract owner's balance into the action; batch advanced
274///     structure v1 refuses a request the document type does not offer
275///     (`GasFeesPaidByNotAllowedError`, 40129) or a batch naming two payers
276///     (`InconsistentGasFeesPaidByInBatchError`, 40130); `validate_fees_of_event`
277///     v1 judges the fee against the sponsor's balance, refusing an insisting
278///     batch unpaid when it falls short (`GasSponsorInsufficientBalanceError`,
279///     40222) and handing a preferring one back to the signer; `execute_event`
280///     v1 charges whoever was admitted. The batch's signer only funds the
281///     principal, and its minimum balance pre-check v1
282///     (`identity_minimum_balance_pre_check`) asks no more of a batch that
283///     requests sponsorship. A failed batch is never sponsored, so check tx
284///     validates the state of a sponsored batch whose signer is under the fee
285///     minimum in full, on the first check and on every recheck (mempool
286///     policy, not consensus).
287/// 12. **Optional token costs**: a document type's token cost may declare
288///     `optional: true` (v3 meta-schema). A transition that leaves
289///     `$tokenPaymentInfo` out then pays no token and its signer pays the gas
290///     in credits, as on an action without a token cost (the base action
291///     transformer waives the cost, and no sponsorship applies). With the
292///     payment info present the token is charged exactly as for a required
293///     cost, and too small a token balance stays a rejection. Contracts up to
294///     v13 cannot carry the flag, so the waiver is inert before this version.
295/// 13. **Pre-programmed distribution amounts are bounded**:
296///     `TokenPreProgrammedDistribution::validate_amounts` rejects a release
297///     whose amounts total more than `i64::MAX` with the new
298///     `PreProgrammedDistributionAmountOverLimitError` (code 10277). It runs
299///     on contract create (`DRIVE_ABCI_VALIDATION_VERSIONS_V10`'s create
300///     `basic_structure` 2) and, for the tokens an update adds, on contract
301///     update (`CONTRACT_VERSIONS_V6`'s `validate_update` 1). A release is
302///     stored as a sum tree, so up to v13 such a create passed validation and
303///     failed inside Drive as an internal error: never paid for, and stripped
304///     from every proposal. An update failed the same way on a single amount
305///     over the limit (its fee estimation takes the insert path), but was
306///     accepted when only the total overflowed, since it wrote no distribution
307///     storage; from v14 it writes it (`update_contract` 2) and would fail.
308///     Tokens a contract already has are not judged, so a contract holding
309///     such a token stays updatable.
310/// 14. **Tokens of one contract sharing a pre-programmed release time**:
311///     `DRIVE_TOKEN_METHOD_VERSIONS_V2` bumps
312///     `add_pre_programmed_distributions` to 1, which queues the release-time
313///     tree the tokens share once instead of once per token. Queued twice,
314///     the batch is refused as an internal error by a node with
315///     `batching_consistency_verification` on; the default is off, and there
316///     GroveDB folds the identical inserts, so the stored state is unchanged
317///     and only the processing fee drops, by the existence read the later
318///     tokens no longer make.
319///
320/// 15. **Tokens added by a contract update are set up like registered ones**:
321///     `update_contract` v2 (`DRIVE_CONTRACT_METHOD_VERSIONS_V4`) creates the
322///     perpetual, pre-programmed and once-per-identity distribution storage
323///     of a token the update adds, and mints its base supply to the token's
324///     `newTokensDestinationIdentity`, or to the contract owner without one,
325///     with the total supply starting at the base supply. v1 did neither: a
326///     claim on such a token failed as an internal error, and the token sat at
327///     a total supply of zero with nobody holding any of it. Nothing is minted
328///     retroactively for a token an update added under an earlier version.
329///
330/// 16. **Contract moderation**: a data contract may declare, in its config,
331///     a banlist and/or a suspension list of identities and who edits them
332///     (the owner, or the owner and up to `SystemLimits::max_contract_moderators`
333///     named identities, each of which must exist). `CONTRACT_VERSIONS_V6`
334///     makes config V2 the config of every new contract (`max_version` and
335///     `default_current_version` 2), which carries
336///     the declaration; a contract create or update carrying a V2 config is
337///     inactive before this version (`StateTransition::active_version_range`).
338///     `DPP_VALIDATION_VERSIONS_V5.validate_config_update = 2` fixes the lists
339///     a contract keeps at its creation: an update turns none on and none off,
340///     and may only change the moderators.
341///     `ContractUserModeration` (state transition type 24, gated by
342///     `CONTRACT_USER_MODERATION_INITIAL_PROTOCOL_VERSION`) bans, unbans,
343///     suspends until a block time (at most
344///     `SystemLimits::max_contract_suspension_until`) and unsuspends one
345///     identity, signed by the owner or a moderator with a CRITICAL key; a
346///     ban and a suspension carry a reason, stored with the entry: a text of
347///     at most `SystemLimits::max_contract_moderation_reason_length` bytes,
348///     an optional code nothing checks, reserved for ban codes a contract may
349///     declare in a later version, and up to
350///     `SystemLimits::max_contract_moderation_reason_documents` documents the
351///     reason is about, named by type and id and not looked up. A contract may also keep a warning list
352///     (`[64, contract, 2] / 224`): a warn appends a warning, the block time and
353///     a reason, to the identity's entry, at most
354///     `SystemLimits::max_contract_warnings_per_identity` at a time, and a
355///     clearWarnings deletes the entry; warnings bar nothing and are what a
356///     status query and the identity's clients read;
357///     `DRIVE_ABCI_VALIDATION_VERSIONS_V10` turns its gates on, moves the
358///     contract update's basic structure to 2 and the contract create and
359///     update state validation (already 1 here) checks the named moderators.
360///     `batch_state_transition.contract_moderation_gate = Some(0)` makes the
361///     batch transformer refuse, paid, the document transitions of a banned or
362///     suspended signer, its deletions excepted (and its retractions, item 72),
363///     and collect a lapsed
364///     suspension, which
365///     `documents_batch_transition` 1 (`DRIVE_STATE_TRANSITION_METHOD_VERSIONS_V4`)
366///     deletes when the batch executes; the same field gates the other
367///     party of a transfer or a purchase, so a barred identity neither
368///     receives nor sells a document. Token transitions are not gated.
369///     `DRIVE_CONTRACT_METHOD_VERSIONS_V4` bumps `insert_contract` to 2,
370///     which creates the list trees (`[64, contract, 2] / 128`, `/ 192` and `/ 224`, inside the contract's other tree), and
371///     adds the `moderation` method table; the verify and
372///     query tables gain the status and entries methods.
373///
374/// 17. **Document action fees and the contract fee claim**: a document type
375///     may charge a fixed fee in credits for an action on one of its documents
376///     (the `actionFees` keyword of the v3 document meta-schema, read by
377///     `try_from_schema` 3), split between the contract's owner pot and its
378///     moderators pot and priced as written or scaled by the fee multiplier of
379///     the epoch. The fees of a document type never change (document type
380///     `validate_update` 1), and a `moderators` part needs declared moderation
381///     (contract create and update basic structure 2). Whoever pays the gas
382///     pays the fee, the contract owner never into their own owner pot, and
383///     only for an action that executes: `validate_fees_of_event` 1 and
384///     `execute_event` 1 (`DRIVE_ABCI_METHOD_VERSIONS_V10`) settle the payer
385///     and move the credits with the batch's own operations, outside the fee;
386///     `apply_drive_operations` 1 merges every write of one identity balance,
387///     fee pot or prefunded specialized balance in a batch into one, so a fee
388///     leaving the balance a purchase price or a voting fund also leaves takes
389///     both, and refuses a batch writing one token balance or supply twice.
390///     Fee validation estimates for the payer it settles on and hands that
391///     payer to `execute_event` 1.
392///     `DRIVE_CONTRACT_METHOD_VERSIONS_V4` gains the `fee_pots` method table:
393///     the pots are sum items under two sum trees of the prefunded specialized
394///     balances (`[40, 64]` and `[40, 192]`), which `create_initial_state_structure`
395///     4 and the upgrade to this version create, so they stay inside the total
396///     credits the platform checks every block, and the epoch each pot was
397///     last claimed in is an item of the contract's other tree (`32` and `96`).
398///     `ContractFeeClaim` (state transition type 25, gated by
399///     `CONTRACT_FEE_CLAIM_INITIAL_PROTOCOL_VERSION`) pays a pot out, at most
400///     once per epoch each: the owner pot to the contract owner, the moderators
401///     pot in equal shares to the moderation team, what the split leaves over
402///     staying in the pot. `DRIVE_ABCI_VALIDATION_VERSIONS_V10` turns its gates
403///     on, `DRIVE_STATE_TRANSITION_METHOD_VERSIONS_V4` adds its converter, and
404///     the verify table gains `verify_contract_fee_pots`.
405/// 18. **Document ids commit to the identity contract nonce**: up to v13 a new
406///     document's id hashed the contract, owner, document type and the entropy
407///     of the create transition, and the create check only asks whether a
408///     document exists under the id right now. The owner of a deleted
409///     document could therefore create another one under the same id by
410///     reusing the entropy, with different content, and everything that
411///     referenced the id (a `refersTo` property, a like, a moderation removal
412///     record) then pointed at the new content, which defeats
413///     `documentsMutable: false` for a deletable document type.
414///     `DOCUMENT_VERSIONS_V4` sets `generate_document_id` to 1: the id also
415///     hashes a domain tag and the identity contract nonce of the create
416///     transition, which is consumed at most once, so an id can be produced
417///     at most once. The entropy stays in the hash (ids remain
418///     unpredictable) and on the wire (the transition format is unchanged);
419///     batch advanced structure validation 1, which only this version
420///     selects, recomputes the id through `Document::generate_document_id`
421///     and bills both passes of the double SHA-256 by the real preimage
422///     length (4 blocks for most document type names) where v13 bills a
423///     flat 2.
424///     Ids of documents created before the upgrade can not be produced by
425///     the new derivation either. A client that still derives the entropy
426///     only id has every create rejected with
427///     `InvalidDocumentTransitionIdError`. Every create path of the clients
428///     in this repository derives through `Document::generate_document_id`:
429///     `DocumentCreateTransitionV0::from_document` for dpp, rs-sdk and the
430///     bindings built on them, and in wasm-dpp2 the `DocumentCreateTransition`
431///     constructor (which also writes the id back onto the JavaScript
432///     `Document`), `Document.generateId` with its `identityContractNonce`
433///     argument, `setIdForCreation` and the `identityContractNonce`
434///     constructor option.
435/// 19. **Document deletion by moderators**: a document type of a contract
436///     that declares moderation may set `moderatorAbilities.delete` (meta-schema
437///     v3, fixed when the type is created, refused on a type that keeps
438///     history, is indexOnly or restricts creation; for references such a type
439///     is no longer permanent, so a permanentDocument reference refuses it, and
440///     a moderatedDocument or a deletableDocument reference takes it, see 64). A
441///     moderation declaration may then keep
442///     no list at all. `ContractUserModeration` gains the `DeleteDocument`
443///     action: the owner or a moderator deletes a document of such a type,
444///     except the owner's and the moderators' own, with a reason like a
445///     ban's. The deletion leaves a record under the contract
446///     (`[64, contract, 2] / 16 / <document type> / <document id>`: the
447///     document's owner, the moderator, the block time and the reason), paid
448///     for by the moderator and never deleted; `insert_contract` 2 creates
449///     the records tree of each such document type, `update_contract` 2 the
450///     tree of one an update adds, and either the tree above them with the
451///     contract's first. The deleted document's
452///     owner gets no storage refund: `apply_drive_operations = 1`
453///     (`DRIVE_VERSION_V9`) attributes the removal of a batch that carries
454///     the forfeiture to nobody, so the credits stay in the storage pools.
455///     A record is final, since a document id is produced at most once (18).
456///     Neither the type's deletion token cost nor its `actionFees` deletion
457///     fee is charged.
458///     The moderation method table, the verify table and the query table gain
459///     the document removal methods (`getContractDocumentRemovals`).
460///     `moderatorAbilities.deleteWithin` bounds the deletion in time: so many
461///     seconds after a document's last modification (`$updatedAt`, or
462///     `$createdAt` on a type whose documents never change; the type must
463///     require its clock), past which no moderator deletes it, the
464///     contract owner included (`DocumentModerationWindowElapsedError`); a
465///     document's own owner still deletes it as `canBeDeleted` allows. A
466///     replace opens the window again. Fixed with the type, like the flag.
467///
468/// 20. **Document transitions agree to their action fee**: version 2 of the
469///     document base transition, the default from this version
470///     (`STATE_TRANSITION_SERIALIZATION_VERSIONS_V3`) and inactive before it
471///     (`StateTransition::active_version_range`, since earlier software
472///     cannot decode it), carries an action fee agreement: the owner and moderators amounts the signer saw declared,
473///     which must match the document type's exactly, and for a fee priced by
474///     the fee multiplier the multiplier they knew with the increase, in
475///     percent, they accept. Batch advanced structure 1
476///     (`DRIVE_ABCI_VALIDATION_VERSIONS_V10`) refuses, as a paid nonce bump
477///     that charges no fee, an action that charges a fee without an agreement
478///     (40132), with one to other amounts or another pricing (40133), or
479///     whose epoch's multiplier rose beyond the tolerance (40134), so a
480///     contract whose fees change cannot make a signed transition pay them.
481///     Check tx judges the agreements again on every recheck, off the action
482///     the transformer rebuilt with the contract and the multiplier as they
483///     are then, so a batch a block would refuse leaves the mempool instead
484///     of failing there (mempool policy, not consensus).
485///
486/// 21. **Document restore by moderators**: the removal record a moderator's
487///     deletion leaves (19) also holds a double SHA-256 of the document as
488///     serialized under its type at the deletion (`ContractDocumentRemoval::
489///     document_hash`), and `ContractUserModeration` gains the
490///     `RestoreDocument` action: the owner or any current moderator brings
491///     the document back, as it was, within
492///     `SystemLimits::contract_document_restore_window_ms` (a week) of the
493///     removal. The bytes must decode under the type and hash to what the
494///     record holds; refused otherwise, or without a record (41119), past the
495///     window (41120), on a hash mismatch (41121), once restored (41122), or
496///     when another document took a value of one of the type's unique indexes
497///     meanwhile (40105). The document goes back through the ordinary insert,
498///     its storage flags naming its owner (the signer pays, the owner keeps
499///     the refund of a later deletion), and the record is marked restored in
500///     place (`ContractDocumentRemoval::restoration`: who, when) rather than
501///     deleted; a restored document deleted again gets a fresh record in place
502///     of the marked one, which the deletion transform reads to know. Neither
503///     the type's creation token cost nor its `actionFees` creation fee is
504///     charged, and no fee agreement is asked. `moderatorAbilities.delete` is
505///     now also refused on a type with a contested index, whose deletions
506///     could never be undone. The record grows on the wire
507///     (`getContractDocumentRemovals`: `document_hash`, `restoration`).
508///
509/// 22. **Elected moderation teams, the declaration and the interim**: a data
510///     contract may declare, when it is created, that its moderators are a team
511///     elected by masternodes and evonodes (`ContractModerators::Elected`, a third kind
512///     beside the owner and an appointed set, in the same config V2). The
513///     declaration is frozen: the join and vote windows (at most four weeks, at
514///     least one day on mainnet and 0 elsewhere, one week by default), in
515///     seconds and bounded by `SYSTEM_LIMITS_V4`;
516///     whether the seat can be contested again once a team is seated
517///     (`seatContestable`, required with no default), and for a contestable
518///     seat the challenge cool-down (`challengeCoolDown`, in seconds, two weeks
519///     to three years, refused on a seat that can not be contested; in Rust
520///     one `Option<u32>`), which nothing reads until challenges come after
521///     this version, a seat never being contested again here; an optional,
522///     unbounded election delay in seconds after the contract's creation
523///     before the first charter may be filed (`electionDelay`, read by the
524///     `moderation: "electionOpen"` reference requirement of item 24); how many
525///     members a seated team's leader may add after the election
526///     (`maxAddedModerators`, 0 when left out, at most
527///     `SYSTEM_LIMITS_V4.max_contract_moderation_added_moderators`, 15); the
528///     document types the team moderates, each with the abilities the seated
529///     team holds on it; who moderates until the first team is seated (the owner, an
530///     appointed set, or nobody, with the moderated types not yet usable or
531///     used unmoderated meanwhile); and whether the owner is protected from the
532///     team. `validate_moderation_config` v0 checks
533///     it against the contract's document types (10900), and
534///     `validate_config_update` 2 refuses every change to it, and entering or
535///     leaving elected moderation, with `DataContractConfigUpdateError`. The
536///     interim moderators moderate and claim the pot as the merged kinds do;
537///     with nobody named, nobody may claim the moderators pot, which
538///     accumulates for the team to come, and with the types not yet usable
539///     `contract_moderation_gate` v0 refuses, paid, every document transition
540///     of a moderated type (`ContractModeratedDocumentTypeNotYetUsableError`,
541///     41200) until a charter is seated (item 40).
542///
543/// 23. **Contested indexes without a Lock choice, and ties to the earliest
544///     contender**: a contested unique index may declare `"resolution": 1`,
545///     `ContestedIndexResolution::MasternodeVoteNoLocking` (meta-schema v3,
546///     parser generation 3). Such a contest offers no Lock choice
547///     (`VoteChoiceNotAllowedForVotePollError`, 40307, from `validate_state` 1
548///     of the masternode vote) and always ends with a winner. Its end date is
549///     the end of the join window until a second contender joins, when
550///     `add_contested_document_for_contract_operations` 1 moves it to the full
551///     poll duration, removing the join window's end date when no other contest
552///     ends then, so a contest with a single contender is awarded without the
553///     vote window. `check_for_ended_vote_polls` 1 awards a tie to the
554///     **earliest** contender (creation time, block height, core height,
555///     document id) for every resolution, where the shipped rule awarded the
556///     latest; DPNS contests ending from this version on follow the new rule.
557///     Before reading the contests due, it removes any end date left with no
558///     contest among the first `maximum_vote_polls_to_process` due, since each
559///     would take a slot of that read and end nothing.
560///
561/// 24. **Contract references may require elected moderation, a minimum age, a
562///     minimum time since the last update, an owner relation to the writer or
563///     config flags of the referenced contract**: a `contract` `refersTo`
564///     declaration may carry `contractRequirements`, what the referenced
565///     contract must declare beyond existing, with `moderation: "elected"` or
566///     `"electionOpen"` (elected, and the contract's own `electionDelay` since
567///     its creation has passed, or it declares none),
568///     `minimumAgeSeconds` (the contract's recorded creation time must be at
569///     least that many seconds before the block time of the write),
570///     `minimumSecondsSinceUpdate` (the same of the later of its creation and
571///     last update times; a contract without a recorded creation time never
572///     meets either), `owner` (`"self"`: the contract is owned by the
573///     `$ownerId` of the referring document, `"other"`: by anyone else),
574///     `readonly: true` (its config is read-only, so it can never be updated
575///     again), `keepsHistory: true` (its config keeps history) and
576///     `ownerProtected` (its elected moderation declaration protects the owner
577///     from the team, or does not, as the value says; a contract without
578///     elected moderation meets neither value) as the requirements
579///     (meta-schema v3, `apply_property_reference` 0,
580///     `ContractReferenceRequirements` on
581///     `DocumentPropertyReferenceTarget::Contract`). The document reference
582///     validation checks them against the contract it fetched for the
583///     existence check and the write itself (its owner and block time), so
584///     they cost no further read, and refuses the first unmet requirement with
585///     `ReferencedContractRequirementNotMetError` (40135). A replace re-checks
586///     them when it changes the reference. `owner` is judged against the
587///     writer, which a transfer or a purchase changes without any write, so
588///     on a document type whose documents can be transferred or traded a
589///     declaration carrying it is re-checked, whole, on every replace, as a
590///     `$ownerId` writer gate is: the new owner has to repoint the reference,
591///     so registration refuses one held by an `immutable` property of such a
592///     type. The other requirements are facts about the referenced contract and
593///     never bring a reference back. A changed `contractRequirements` is an
594///     incompatible schema change on update.
595///
596/// 25. **Typed arrays of scalars in document schemas**: a document property
597///     may be `type: "array"` with an `items` element schema instead of
598///     `byteArray` (meta-schema v3, `parse_typed_array` 0,
599///     `DocumentPropertyType::TypedArray`). An element is an integer, a
600///     number, a string, a boolean, a byte array or an identifier; objects
601///     and arrays of arrays are refused. On the array `minItems` and
602///     `maxItems` count elements, `maxItems` is required (with `minItems`
603///     not above it) and at most `SYSTEM_LIMITS_V4.max_typed_array_items`
604///     (1024), and `uniqueItems` refuses a document repeating an element. An
605///     element's `enum` has members of the element type only (none on a byte
606///     array or identifier element), and an integer element's `minimum` and
607///     `maximum` are integers; the parser reads them so random documents stay
608///     inside them. The array is stored inline, a varint element count followed by the
609///     elements, each encoded exactly as a required scalar property of its
610///     type: an identifier element is 32 raw bytes, an integer element takes
611///     the width its bounds give it, a fixed-size byte array element is raw.
612///     A contract update may not change how an element encodes
613///     (`validate_update` 1). The array cannot be an index property or one
614///     side of a `propertyAgreement`. Its identifier and byte array elements
615///     are conversion paths (`find_identifier_and_binary_paths` 1). A byte array
616///     refuses `items`, and an identifier (a byte array with the identifier
617///     `contentMediaType`) now refuses `uniqueItems`, which would demand that
618///     no byte repeat.
619///
620/// 26. **Distinct identifier properties**: the `distinctFrom` property
621///     keyword (meta-schema v3, `apply_distinct_from` 0, `DistinctFrom` on
622///     `DocumentProperty`) requires an identifier property's value to differ
623///     from the value of a named property of the same document, or from the
624///     document's `$ownerId`; on the `items` of a typed array of identifiers
625///     it binds every element. A pure structure rule: document create
626///     structure validation 1 and replace structure validation 0 call
627///     `validate_distinct_from_properties` (`validate_distinct_from` 0) on the
628///     transition's data and owner id after the schema validation, transfer
629///     and purchase structure validation 0 call it on the stored document and
630///     its new owner (the three generation-0 modules were extended in place:
631///     the call is inert before this version, where no property carries the
632///     keyword), and each refuses an equal pair with
633///     `DocumentPropertyNotDistinctError` (10419); an absent named property
634///     passes. The parser checks the target at contract
635///     registration and update (it must exist, be an identifier and not be
636///     the declaring property), and a changed `distinctFrom` is an
637///     incompatible schema change on update.
638///
639/// 27. **`encryptedFor` on byte array properties**: a byte array property may
640///     declare how its ciphertext was produced, so wallets read the recipe
641///     from the contract instead of a side channel: `recipient` (an identifier
642///     property of the same document type, or `$ownerId`), `recipientKey` and
643///     `senderKey` (integer properties of the same type bounded to u32,
644///     carrying key ids) and `scheme` (`ecdh-secp256k1-aes256-cbc`, the
645///     dashpay contact request scheme: a 16-byte IV followed by AES-256-CBC
646///     with PKCS7 padding under the ECDH shared key). Meta-schema v3 admits it
647///     on byte arrays that are not identifiers, `apply_encrypted_for` 0 parses
648///     it onto `DocumentProperty::encrypted_for` and checks the three named
649///     properties exist with the right types at registration. Document create
650///     structure validation 1 and replace structure validation 0 (extended in
651///     place, inert before this version) call
652///     `validate_encrypted_property_shapes` (`validate_encrypted_property_shapes`
653///     0, `None` before this version) to check the ciphertext shape of every declared property a transition supplies,
654///     at least the IV plus one block and a multiple of the block, and refuse
655///     it with `InvalidEncryptedPropertyShapeError` (10420). Nothing else about
656///     the ciphertext is verifiable on chain. A changed `encryptedFor` is an
657///     incompatible schema change on update.
658///
659/// 28. **Property and document type names are word characters only**:
660///     meta-schema v3 refuses `-` in a property name (top-level or nested,
661///     and in the property paths of `refersTo` declarations) and generation
662///     3 of the document type parser refuses it in a document type name,
663///     under full validation. Every earlier meta-schema and generation
664///     admitted `-`, which the dotted and `list[]` path syntax was never
665///     written for; a census of every contract create and update on mainnet
666///     and testnet (2026-09-23) found no name carrying one, so nothing stored
667///     is affected. Stored contracts are read as they are.
668///
669/// 29. **Identity key references may require a purpose and a document type
670///     bound**: an `identityPublicKey` `refersTo` declaration may carry
671///     `keyRequirements`, what the referenced key must be beyond existing and
672///     not being disabled, with `purpose` (the key's purpose, by its wire name,
673///     any but `system`) and `boundTo` (the key's contract bounds must be
674///     exactly the declaring contract and the named document type of it) as
675///     the requirements (meta-schema v3, `apply_property_reference` 0,
676///     `IdentityKeyReferenceRequirements` on
677///     `DocumentPropertyReferenceTarget::IdentityPublicKey`).
678///     `create_document_types_from_document_schemas` 1, edited in place (the
679///     check is inert before this version, where no parsed reference carries
680///     requirements), refuses a contract whose `boundTo` names a document type
681///     it does not have or one no key of the required purpose can be bound
682///     to, so the check never needs a second contract fetch and a declared
683///     requirement can be met. The document reference validation
684///     checks the requirements against the key it fetched for the existence
685///     check, so they cost no further read, and refuses the first unmet one
686///     with `ReferencedIdentityKeyRequirementNotMetError` (40136). A changed
687///     `keyRequirements` is an incompatible schema change on update.
688///
689/// 30. **Key references on the key id property**: an `identityPublicKey`
690///     `refersTo` declaration may sit on the key id property itself, an
691///     integer with `minimum` 0 and `maximum` 4294967295 (a `KeyID` is a
692///     `u32`), naming through `identityProperty` whose key the value is:
693///     `"$ownerId"` (the writer), `"$creatorId"` (the document's creator,
694///     only on a document type that records creator ids) or the path of an
695///     identifier property of the same document type (which must exist, be
696///     an identifier and not carry an `identityPublicKey` reference of its
697///     own); the last two are checked at contract registration (40125). The
698///     declaration takes no `keyIdProperty`; the identifier form is unchanged
699///     and every other `refersTo` form stays identifier-only (meta-schema v3,
700///     `apply_property_reference` 0, `DocumentPropertyType::KeyIdWithReference`
701///     over `KeyReferenceIdentityProperty`). At document create and replace the
702///     reference validation reads the key id from the property, resolves the
703///     identity (the writer, the creator the action carries, or the named
704///     property's value; a key id set while that property is unset, or on a
705///     document that records no creator, one written before its type recorded
706///     creator ids, being refused with 40125) and fetches that key, so the key
707///     fetch is the only read; a key that does not exist refuses the write,
708///     paid, with
709///     `ReferencedIdentityKeyNotFoundError` (40123) and a disabled one with
710///     `ReferencedIdentityKeyDisabledError` (40124), as for the identifier
711///     form. A replace re-validates `$ownerId` touched or not, as the
712///     `$ownerId` writer gate is, since the writer may not be the one who
713///     wrote the key id; `$creatorId` when the key id changed; a property
714///     path when the key id or that property changed (a transfer itself is
715///     never checked: the reference governs writing, not holding). A
716///     `keyIdProperty` may not name a property carrying this form (40125 at
717///     registration). `keyRequirements` (item 29) sit on this form exactly
718///     as on the identifier form, checked by the same key check and by the
719///     same `boundTo` registration rule. Adding it to, removing it from or
720///     changing it on an existing property is an incompatible schema change
721///     on update, like the rest of a `refersTo`; a property an update adds
722///     may carry it, so a `$creatorId` one can meet documents written before
723///     their type recorded creator ids.
724///
725/// 31. **`refersTo` on the elements of a typed array**: an identifier element
726///     of a typed array may carry a `refersTo` declaration on its `items`,
727///     which every element then declares (meta-schema v3 `documentArrayItem`
728///     reuses the property `refersTo` definition by `$ref` and refuses
729///     `identityPublicKey` in both forms, which pair one key id with the
730///     reference).
731///     `parse_typed_array` 0 folds it into the element through the same
732///     `apply_property_reference` 0 a scalar identifier goes through, so the
733///     element is `IdentifierWithReference(target)` inside `item_type`, and
734///     `DocumentPropertyType::reference` reports either kind. Contract
735///     registration (`data_contract_reference_validation` 0) checks the
736///     declaration as a single one, and document create state validation 2
737///     and replace state validation 1 (`document_reference_validation` 0,
738///     extended in place: both are only reached from protocol version 14,
739///     where the element arm is the only new path) check every element as a
740///     single reference, refusing the first that fails with that
741///     reference's error (40120, 40127, 40135 and the rest), its path the
742///     element's list path (`reasons[2]`). A replace re-validates the
743///     elements of a changed list the stored list did not hold (the replace
744///     action carries `stored_changed_values`), and all of them when a
745///     property bound by a `propertyAgreement` changed, for a `$ownerId`
746///     agreement or for `deletableDocument` elements. A repeated element and
747///     a foreign contract holding the referenced document type are fetched
748///     once per list. Registration caps the references one document
749///     can carry at `SYSTEM_LIMITS_V4.max_references_per_document` (256; one
750///     per property declaring a reference, key id references of item 30
751///     included, `maxItems` per typed array of referencing elements;
752///     backfilled into the earlier tables), and
753///     refuses an `immutable` property holding a `deletableDocument`
754///     reference no replace could clear (a typed array of them, or a single
755///     one inside an immutable object), which could never be replaced once
756///     a target is deleted, and a single top-level one frozen only under a
757///     condition (see 66), which a replace could clear once its target is
758///     deleted and a later one the condition leaves free set to another
759///     document. A changed
760///     element `refersTo` is an incompatible schema change on update.
761///
762/// 32. **Document references resolved through a unique index**: a
763///     `permanentDocument` `refersTo`, on an identifier property or on the
764///     elements of a typed array (item 31), may carry a `lookup`
765///     (meta-schema v3, `apply_property_reference` 0, parsed to the appended
766///     `DocumentPropertyReferenceTarget::PermanentDocumentLookup`, so an id
767///     reference keeps its variant and its encoding): the value is then
768///     not the referenced document's id, and the referenced document is the
769///     one the named unique index of the referenced document type finds for
770///     a key assembled from the referring document. `keys` maps every index
771///     property to a property path of the referring type, `$ownerId` or `.`
772///     (the value, or the element, exactly once). A `deletableDocument`
773///     reference may take one too (`DeletableDocumentLookup`, appended): it
774///     then means a document with this key exists now, since the key may find
775///     a later document once the one it found is deleted, so every replace
776///     re-validates it, an immutable property may not hold it, and it is the
777///     one deletable form a reference expression and `ownerRefersTo` (never
778///     `creatorRefersTo`) take. Generation 3 of the parser
779///     checks on every parse that each property a key reads is a stored,
780///     required, single value of the referring type;
781///     `create_document_types_from_document_schemas` 1, edited in place like
782///     for item 29 (inert before this version, where no parsed reference
783///     carries a lookup), checks a lookup into a document type of the same
784///     contract under full validation (the index exists, is unique, carries
785///     no `timeRange` and is not on an indexOnly type, the keys cover it
786///     exactly, every source shares its index property's value kind, and the
787///     key cannot move off the document it found: its schema properties are
788///     immutable, none an optional `deletableDocument` reference by id, which
789///     a replace may clear once its document is deleted (item 73), and
790///     `$ownerId` is only a part on a type that is neither transferable nor
791///     tradeable), and the contract reference validation
792///     checks one into another contract, refusing it with
793///     `ReferencedDocumentLookupInvalidError` (40137). The document
794///     reference validation (generation 0, reached only from this version)
795///     queries the index for each value's key, billed as a document fetch,
796///     refuses a write with no match with `ReferencedEntityNotFoundError`
797///     (40120, an element named by its list path), checks a
798///     `propertyAgreement` against the document found, and on replace
799///     re-validates when a property the key reads changed. A key may read
800///     `$ownerId` only on a referring type that is neither transferable nor
801///     tradeable, checked on every parse. A changed `lookup` is an
802///     incompatible schema change on update. Chained queries and composite
803///     by-id joins refuse a lookup reference as a join property, and
804///     preallocated indexes are never bound through one.
805/// 33. **Reference expressions (`anyOf` / `allOf`)**: a `refersTo`, on an
806///     identifier property or on the elements of a typed array (item 31), may
807///     be `{ "anyOf": [operand, ...] }`, holding if at least one operand
808///     holds, or `{ "allOf": [operand, ...] }`, holding if every operand holds
809///     for the same value, in place of one target (meta-schema v3, which
810///     admits either combinator only as the declaration's one key,
811///     `apply_property_reference` 0, parsed to the appended
812///     `DocumentPropertyReferenceTarget::AnyOf` and `AllOf`, so every single
813///     target keeps its variant and its encoding; decoding refuses a nesting
814///     deeper than `MAX_REFERENCE_EXPRESSION_DECODE_DEPTH`, 16, so the bytes of
815///     a consensus error cannot recurse without bound). An operand is a leaf,
816///     an `identity`, a `permanentDocument` (by id or with a `lookup`, item
817///     32), a `listElement`, a `deletableDocument` with a `lookup` (which
818///     re-validates the expression on every replace), or an expression of the
819///     other combinator; a list names two or more operands. `contract`,
820///     `token`, `deletableDocument` by id and
821///     `identityPublicKey` leaves, the key id form, a combinator directly
822///     inside the same combinator and keys beside a combinator are refused on
823///     every parse. Registration caps a list at
824///     `SYSTEM_LIMITS_V4.max_reference_operands` (4) and the nesting at
825///     `max_reference_expression_depth` (4 combinators on any path to a leaf),
826///     both backfilled into the earlier tables, refuses two alike operands of
827///     one list (a leaf naming the declaring contract explicitly counting as
828///     the one omitting it), counts every leaf against
829///     `max_references_per_document`, and checks each leaf as the same
830///     declaration alone (`create_document_types_from_document_schemas` 1 and
831///     `data_contract_reference_validation` 0, both walking
832///     `DocumentPropertyReferenceTarget::leaves_with_paths`, which is the
833///     declaration itself at an empty path for a single target, so their
834///     output is unchanged where no expression can parse), a failing leaf
835///     named by where it sits (`resignation.memberId.anyOf[1].allOf[0]`). The
836///     document reference validation (`document_reference_validation` 0,
837///     reached only from this version) evaluates each value operand by operand
838///     in declared order: an `anyOf` stops at the first operand that holds and
839///     otherwise refuses with the last operand's error, an `allOf` stops at the
840///     first that fails and refuses with its error, so a refusal is always a
841///     leaf's own error and no new error exists; every read is billed, the
842///     failed operands' included. A `propertyAgreement` belongs to its leaf and
843///     is checked only against that leaf's document. A replace re-validates an
844///     expression when its value, or a property one of its leaves binds,
845///     changed. A changed expression is an incompatible schema change on
846///     update. Chained queries and composite by-id joins refuse an expression
847///     join property, and preallocated indexes are never bound through one.
848/// 34. **References on the document's writer or creator (`ownerRefersTo`,
849///     `creatorRefersTo`)**: a document type may declare one `refersTo`
850///     declaration of its own, under the doctype-level `ownerRefersTo`
851///     keyword (meta-schema v3, which reuses the property declaration by
852///     `$ref`), whose value is the document's `$ownerId`, the writer, instead
853///     of a property's: a single target, or a reference expression (item 33)
854///     whose every leaf is one of the targets that can hold a writer:
855///     `identity`, and a `permanentDocument` found through a `lookup`, where
856///     `.` is the writer (and, for the writer alone, a `deletableDocument`
857///     found through one, item 32); `contract`, `token` and a document by id (which the
858///     writer's identity id never is) and `identityPublicKey` (which needs a
859///     key id) are refused, as a leaf too. Parser generation 3 reads it from
860///     the stored schema once the core parse has run the meta-schema, on
861///     every parse, through the same `apply_property_reference` 0 an
862///     identifier property's goes through, onto
863///     `DocumentTypeV2::owner_reference`, and refuses it on a type whose
864///     documents can be transferred or traded, since neither is a write. Every
865///     enumeration of a type's references goes through
866///     `DocumentTypeRef::reference_declarations`, which yields it first: its
867///     lookup's referring side is checked on every parse, a lookup into a
868///     type of the same contract by
869///     `create_document_types_from_document_schemas` 1 (edited in place like
870///     for item 29, inert before this version, whose parsers never set an
871///     owner reference), and the whole declaration at registration by the
872///     contract reference validation (`data_contract_reference_validation` 0,
873///     extended in place, only reached from this version), which names it
874///     `<documentType>.$ownerId` and lets its `propertyAgreement` name the
875///     writer on the referring side. It counts one against
876///     `max_references_per_document`. Document create state validation 2 and
877///     replace state validation 1 (`document_reference_validation` 0, extended
878///     in place, both only reached from this version) check the writer against
879///     the target exactly as a property's value is checked: on every create,
880///     and on a replace under the rules of its target (a changed property its
881///     lookup or a `propertyAgreement` reads, every replace for a `$ownerId`
882///     pair), and refuse the write with the error the target reports for a
883///     property (40120 and the rest) at the path `$ownerId`; an `identity`
884///     target fetches nothing, the transition having proved the writer exists.
885///     Adding, removing or changing it is an incompatible schema change on
886///     update (`validate_schema_compatibility` 1 freezes it as the shared rule
887///     set freezes `refersTo`). Its counterpart for a type whose documents can
888///     be transferred or traded is `creatorRefersTo`, whose value is the
889///     document's `$creatorId`, the creator, which never changes: the same
890///     two targets (`.` the creator), only on a type that records creator ids
891///     (`should_use_creator_id`: a transferable or tradeable type of a
892///     format-1 contract), so a type declares at most one of the two; stored
893///     as `DocumentTypeV2::creator_reference`, enumerated second by
894///     `reference_declarations`, named `$creatorId` (and
895///     `<documentType>.$creatorId` at registration), checked against the
896///     writer on a create and the stored creator on a replace under the same
897///     rules, never on a transfer or a purchase, and frozen on update the same
898///     way.
899/// 35. **References to an element of a list of a referenced document**: a
900///     new `refersTo` target, `listElement` (meta-schema v3,
901///     `apply_property_reference` 0, parsed to the appended
902///     `DocumentPropertyReferenceTarget::ListElement`, so every earlier
903///     variant keeps its encoding), on an identifier property, on the
904///     elements of a typed array (item 31), as a leaf of a reference
905///     expression (item 33), or on the writer or the creator (item 34, whose
906///     identity then must be listed; a third target those two take next to
907///     `identity` and a `permanentDocument` lookup, since an identity id can
908///     be an element of a list of identities): the value must be an element
909///     of the typed array
910///     of identifiers `inList` held by one document of `documentType`, the
911///     document whose `$id` the `propertyAgreement` pair with `$id` on the
912///     referenced side reads from an identifier property of the referring
913///     type (stored, optional or not; generation 3 of the parser checks it
914///     under full validation). `$id` joins `$ownerId` and `$creatorId` as a
915///     referenced-side agreement name for every document reference. In every
916///     other respect a list element is a document reference: `contractId`,
917///     `documentType` and its other agreement pairs are checked at
918///     registration as a `permanentDocument`'s are (the type must forbid
919///     deletion), and the list must be a stored typed array of identifiers
920///     fixed once a document is written (the type is immutable or lists the
921///     list's top-level property under `immutable`).
922///     `create_document_types_from_document_schemas` 1, edited in place like
923///     for items 29 and 32 (inert before this version, where no parsed
924///     reference is a list element), checks a list in the same contract
925///     under full validation, and the contract reference validation checks
926///     one in another contract, refusing it with
927///     `ReferencedDocumentListInvalidError` (40138). The document reference
928///     validation (generation 0, reached only from this version) fetches the
929///     list's document by the `$id` pair's value, once per write and shared
930///     with any other reference of the same document (every by-id document
931///     fetch of one write is now memoized), checks the other pairs against it,
932///     and refuses a value the list does not hold, or one set while the `$id`
933///     property is not, with `ReferencedEntityNotFoundError` (40120, the list
934///     element declaration as its entity type, an element named by its list
935///     path); the list is collected once, each value a set lookup. A replace
936///     checks it again when its value or a referring side of any pair
937///     changed, as every agreement is. Each value counts against
938///     `SystemLimits::max_references_per_document` like every other
939///     reference. A changed `listElement` is an incompatible schema change on
940///     update.
941///
942///
943/// 36. **Transient properties are never stored**: a transient property is
944///     judged on the transition and dropped before its document is stored.
945///     Up to v13 only a create dropped it and a replace stored whatever it
946///     carried; `document_from_replace_transition_action` 1 (paired with the
947///     contract-version stamp, edited in place, only selected by this
948///     version) drops the transient values of a replace by top-level name as
949///     a create does. The rules that read a stored value refuse a transient
950///     one, by the property's path and every enclosing object's
951///     (`is_transient`): at registration (parser generation 3 under full
952///     validation) every `transient` entry must name a top-level property,
953///     since Drive drops values by top-level name, and no index may read a
954///     transient property, which every document would leave in the index's
955///     null branch; a lookup's referenced side refuses such an index too
956///     (`referenced_side_error`); the contract reference validation
957///     (`data_contract_reference_validation` 0, extended in place, only
958///     reached from this version) refuses a `propertyAgreement` whose
959///     referenced property is transient, which no stored document carries,
960///     and a key reference that stores the key id while its identity is
961///     transient, in either form (`identityProperty` on the key id,
962///     `keyIdProperty` on the identity). A transient referring side of an
963///     agreement stays allowed: it is a write gate, judged on the
964///     transition. Changing the `transient` list on contract update was an
965///     unsupported keyword to the schema compatibility check, an internal
966///     error that dropped the transition unpaid; `validate_schema_compatibility`
967///     1 freezes the set of names it lists (sorted and deduplicated before
968///     the diff, so a reordering is no change) as it freezes `refersTo`, an
969///     incompatible schema change.
970///     A census of every mainnet and testnet contract (2026-09-23) found
971///     `transient` only on DPNS-shaped `domain` types, which are immutable,
972///     index no transient property and list top-level properties only.
973///
974/// 37. **The moderation charters system contract**
975///     (`SystemDataContract::ModerationCharters`, schema v1, the first piece of
976///     decentralized moderation teams) carries seven document types, all
977///     immutable, the four a charter is made of undeletable and the three team
978///     changes deletable. A `reason` is a ground for a moderation
979///     action, keyed by its owner and a three-letter `code` unique among the
980///     owner's reasons. A `submittedCharter` is a leader's proposal to
981///     moderate one contract on that contract's own terms: its
982///     `targetContractId` refers to a contract declaring elected moderation
983///     (item 24, `moderation: "elected"`, so teams form during the contract's
984///     election delay), its `reasons` are a typed array (item 25) of
985///     references to reasons (item 31), and it carries an optional
986///     `moderatorsShare` and a `rewardSplit`. A `joinRequest` is an identity's
987///     offer to serve on a proposal, one per identity per proposal, whose
988///     `recipientId` must be the proposal's owner (`propertyAgreement`) and
989///     name a decryption key bound to `submittedCharter` (item 29), whose
990///     `senderKeyId` is an encryption key of the writer bound to `joinRequest`
991///     (item 30) and whose `encryptedMessage` declares its envelope (item 27).
992///     An `electedCharter` is a proposal put to the vote with its team: only
993///     the proposal's owner may create one, for the proposal's own target
994///     (`propertyAgreement`), its `targetContractId` requires
995///     `moderation: "electionOpen"`, and its `members` are identities each of
996///     which filed a join request for that proposal (item 32, a lookup through
997///     the join request's unique index) and none of which is the leader
998///     (item 26). Once a charter is seated, its leader adds members from the
999///     same join requests (`addedModerator`, the same lookup) and takes them
1000///     back by deleting the addition, and removes elected members
1001///     (`removedModerator`, whose `memberId` is a `listElement` of the
1002///     charter's `members`), putting one back by deleting the removal; each
1003///     exists at most once per member and charter (unique indexes), so the
1004///     team that acts is the leader plus the elected members less the
1005///     removals plus the additions (`ElectedCharter::active_members`). A
1006///     member asks to leave with a deletable `resignationRequest`, which only
1007///     a member may file (`ownerRefersTo` with an `anyOf` of a `listElement`
1008///     into the elected charter's `members` and a `deletableDocument` lookup
1009///     of an `addedModerator`, items 32 to 35) and which carries a message
1010///     encrypted to the leader; the leader acts on it by deleting the addition
1011///     or removing an elected member. The cap on
1012///     additions, the target's `maxAddedModerators`, is a consensus rule of
1013///     item 40.
1014///     Its `byTargetContract` index is a contested unique index
1015///     with `"resolution": 1`, the masternode vote without a Lock choice of
1016///     item 23, so an elected charter create opens or joins the contest for
1017///     its target. `SYSTEM_DATA_CONTRACT_VERSIONS_V3` registers it
1018///     (`moderation_charters: 1`). A proposal holds no rule beyond its schema:
1019///     the reward split sums to 100 through the contract's
1020///     `propertyConstraints` rule (item 39), so 11001 is never produced, and
1021///     the description fits 4096 bytes through the schema's own `maxBytes`
1022///     (item 38); every document validation checks both. Genesis registers it
1023///     on chains born at this version (`create_genesis_state` v1, behind the
1024///     app-connect branch), `transition_to_version_14` inserts it on upgrade,
1025///     and the Drive system contract cache serves it from this version
1026///     (`MODERATION_CHARTERS_CONTRACT_INITIAL_PROTOCOL_VERSION`). Item 40 seats
1027///     the winning team.
1028///
1029/// 38. **`maxBytes` on strings**: a property keyword for the bound plain JSON
1030///     Schema cannot count, the most UTF-8 bytes a string may take
1031///     (`maxLength` counts characters, which are up to four bytes each). It
1032///     goes on a string property, or on the `items` of a typed array of
1033///     strings where it bounds every element, and is 1 to 65535 and no lower
1034///     than `minLength`, checked at registration. Meta-schema v3 admits it and
1035///     `apply_max_bytes` 0 folds it into `StringPropertySizes::max_bytes`, so
1036///     `max_byte_size`, `max_size` and random documents respect it. The
1037///     document validation (`DataContract::validate_document_properties` 0,
1038///     extended in place, inert before this version) calls
1039///     `validate_max_bytes_properties` (`validate_max_bytes` 0, `None` before
1040///     this version) after the JSON schema, on every create and replace and in
1041///     every client that validates a document, and refuses a longer value with
1042///     `DocumentPropertyMaxBytesExceededError` (10421, naming the element as
1043///     `tags[2]` for an item). On update it moves like `maxLength`: it may be
1044///     raised or removed, not added or lowered. The moderation charters
1045///     contract (item 37) declares it on the proposal's description, replacing
1046///     the charter-specific description check, its error 11002 and
1047///     `SystemLimits::max_moderation_charter_description_length`.
1048///
1049/// 39. **Property constraints**: the doctype-level `propertyConstraints`
1050///     keyword (meta-schema v3, `parse_property_constraints` 0) names rules a
1051///     document's properties must meet, each a condition: a comparison
1052///     (`equal`, `notEqual`, `lessThan`, `lessThanOrEqual`, `greaterThan`,
1053///     `greaterThanOrEqual`) of two integer expressions built from integer
1054///     literals, paths of integer or boolean properties (a boolean reading as 1
1055///     for true and 0 for false), `add`, `subtract`, `multiply`, `divide`,
1056///     `modulo` and `power`, `min` and `max` over two or more operands and
1057///     `abs` over one, and sizes: `length` and `byteLength`, the characters and
1058///     UTF-8 bytes of a string property, and `count`, the items
1059///     of an array or byte array property, each 0 for a property the document
1060///     leaves out, `countPresent`, how many of two or more distinct properties
1061///     of any type the document holds, each as `present` tests it, so a rule
1062///     bounds how many of a group are set, and the system times and heights `$createdAt`, `$updatedAt`
1063///     and `$transferredAt` (block times in milliseconds), each also with
1064///     `BlockHeight` or `CoreBlockHeight` appended, of the document's creation,
1065///     last update (create, replace, price update) and last transfer (create,
1066///     transfer, purchase), which a rule may read only on a type listing them
1067///     in `required`; `in`, whether an integer expression takes one of two or
1068///     more distinct integer values; `equal` or `notEqual` of a string property
1069///     and a `{ "const": string }` or of two bare paths naming string
1070///     properties, or `in` of a string property and two or more distinct
1071///     strings, a string the document leaves out equalling no constant and no
1072///     other string unless an `ifAbsent` gives it a string default
1073///     (`{ "ifAbsent": ["status", "open"] }`, whose default an `enum` must list
1074///     too); `equal`, `notEqual` or `in` of an identifier property (one
1075///     declaring `refersTo` included) or of `$ownerId`, the document's owner,
1076///     likewise, with base58 identifier constants or another identifier operand
1077///     and no default, an identifier the document leaves out equalling none;
1078///     `startsWith` or `endsWith`, whether a string (a constant or a string
1079///     property, at least one a property) starts or ends with another, byte for
1080///     byte; `contains`, whether a typed array property holds an element equal
1081///     to an integer expression, a string or an identifier operand (a constant,
1082///     a property, or `$ownerId`), as its elements are, an array the document
1083///     leaves out holding nothing; `present` or `absent` naming a property of
1084///     any type, whether the document holds it (the one way to tell a property
1085///     left out from one set to 0); `anyOf` or `allOf` over two or more
1086///     conditions; `not` over one; `ifThen` over two (the second holding
1087///     whenever the first does, evaluated only then) or `ifThenElse` over three
1088///     (the second when the first holds, the third when it does not, only the
1089///     branch taken evaluated), no two alike; `notIn`, an `in` negated in as
1090///     many nodes. In an operand, a property the document leaves out counts as
1091///     0, or as the value of an `ifAbsent` operand naming it. `countOf` and
1092///     `sumOf` operands read a total from state: how many documents of a type
1093///     of the same contract match a filter (keys of that type or `$ownerId`,
1094///     values read from the document written), or an integer property's total
1095///     over them, as the count or sum tree will keep it once the write is done;
1096///     the batch transformer reads them into the action
1097///     (`Drive::fetch_property_constraint_aggregate`, billed; in place in
1098///     transformer 0, reading nothing before this version), a transfer or
1099///     purchase reads again those depending on the owner, a price update those
1100///     of the rules it judges, and a rule reading one it is not given is not
1101///     judged by an SDK pre-check and an error in consensus, which reads them
1102///     all.
1103///     Arithmetic is exact `i128`: `divide` and `modulo` are Euclidean (the
1104///     remainder is never negative), and an overflow, a zero divisor, a
1105///     negative exponent or a value that is not an integer refuses the document
1106///     rather than wrapping. Conditions are checked in declared order and no
1107///     further than the outcome needs (`anyOf` stops at the first that holds,
1108///     `allOf` at the first that fails), a fault in one that is checked refuses
1109///     the document whatever the others say, and `not` never turns a fault into
1110///     a pass, so an earlier condition guards a later one. The parser checks
1111///     that every path an operand reads names an integer or boolean property,
1112///     every path a `length` or `byteLength` measures a string property, every
1113///     path a `count` counts an array or byte array property, every string
1114///     `startsWith` or `endsWith` tests a string property (a constant tested
1115///     against one with an `enum` starting or ending one of its values), every
1116///     array a `contains` looks in a typed array of the kind it looks for (a
1117///     string constant in the elements' `enum` when they declare one), every
1118///     system time or height a rule reads one the type lists in `required`
1119///     (none on an indexOnly type), every path compared with identifiers an
1120///     identifier property, every path compared with strings a string property
1121///     (whose `enum`, if it declares one, lists every constant it is compared
1122///     with), and every path `present`, `absent` or `countPresent` tests a
1123///     property of any type, none transient nor inside a transient object; that every
1124///     comparison and `in` reads a property or the owner; that nothing is
1125///     compared with itself; that strings and identifiers are only compared for
1126///     equality, and never with each other; that no `in` lists a value twice
1127///     and no `countPresent` a path twice;
1128///     that an `anyOf` or `allOf` holds none directly of its own kind and a
1129///     `not` no `not` or `notIn`; that an indexOnly type, whose deletes carry
1130///     no owner, reads no `$ownerId`; and that no condition or operand nests
1131///     deeper than
1132///     `MAX_PROPERTY_CONSTRAINT_PARSE_DEPTH` (64), on every parse. Under full
1133///     validation it holds the limits `SystemLimits::max_property_constraints`
1134///     (16 rules) and `max_property_constraint_nodes` (32 per rule, every
1135///     comparison, `in`, listed value, `const`, presence test and logical
1136///     operator counting as one, a `countPresent` as one plus one per path), and that no `anyOf` or `allOf` lists the same
1137///     condition twice, and at most `max_property_constraint_aggregates` (4)
1138///     distinct totals per type; once every type is parsed, that a tree keeps
1139///     each total (`documentsCountable` or `documentsSummable`, or an index
1140///     whose properties are exactly the filter's keys) and that no type with a
1141///     contested index totals its own documents, in
1142///     `create_document_types_from_document_schemas` 1, in place and inert
1143///     before this version. `DataContract::validate_document_properties` 0
1144///     (extended in place, inert before this version, and taking the document's
1145///     owner for `$ownerId`) calls `validate_property_constraints`
1146///     (`validate_property_constraints` 0) after the schema validation, so
1147///     document create and replace, and any client validating a document,
1148///     refuse a broken rule with `DocumentPropertyConstraintViolatedError`
1149///     (10422), naming the rule and why. A transfer and a purchase, which give
1150///     the document a new owner, are judged against the rules reading
1151///     `$ownerId` or the transfer's time and heights, with the new values, and
1152///     a price update, which sets the update's time and heights, against the
1153///     rules reading those (`validate_property_constraints_for_system_change`,
1154///     in their structure validation, in place and inert before this version;
1155///     the price update's call is new there). `validate_document_properties`
1156///     takes the document version's system values (`DocumentSystemValues`):
1157///     consensus gives the writer and the block's time and heights on create,
1158///     and on replace the stored creation and transfer values with the block's
1159///     as the update. The rules change nothing stored and read no state but
1160///     their totals. They
1161///     are fixed when the document type is created: a changed
1162///     `propertyConstraints` is an incompatible schema change on update. The
1163///     moderation charters contract declares its first one: a
1164///     `submittedCharter`'s `rewardSplit` members add up to 100, replacing the
1165///     charter-specific check, whose error 11001 keeps its place in
1166///     `BasicError` but is never produced.
1167///
1168/// 40. **Elected moderation teams moderate from their stored charter**: seating
1169///     writes nothing. Awarding the contest of item 37 writes the winning
1170///     `electedCharter`, the only one ever stored for its target, so the
1171///     charter seated on an elected contract is the one the charter contract's
1172///     `byTargetContract` index finds, and the moderation paths read it, each
1173///     read a billed document query of the system contract. Once one is seated,
1174///     only its team moderates the contract: the leader (the charter's owner)
1175///     and the active members (its `members` less removals, plus additions),
1176///     each alone, found by one point read of the unique `removedModerator`
1177///     index for an elected member or of `addedModerator` for anyone else; the
1178///     interim moderators
1179///     are refused (41101). The team holds the abilities the declaration gives
1180///     it: a deletion or restore needs `deleteDocuments` on the type, a list
1181///     action the ability on some moderated type
1182///     (`ContractModerationAbilityNotGrantedError`, 41201). The leader and the
1183///     active members are protected (41102), with the owner when the
1184///     declaration says so, and the interim moderators no longer are. A
1185///     `notYetUsable` interim stops blocking the moderated types
1186///     (`contract_moderation_gate` v0). An `addedModerator` past the target's
1187///     `maxAddedModerators` additions the charter holds is refused,
1188///     paid (`ModerationCharterAddedModeratorLimitReachedError`, 41202), by a
1189///     hook in the batch's `validate_state` v0 that only a create of the
1190///     charter contract reaches. A document action on a moderated type may
1191///     agree to the seated proposal's `moderatorsShare` of the declared
1192///     moderators part (rounded down) instead of the whole, and is charged
1193///     that: the batch transformer (state v2) reads the charter and its
1194///     proposal only for such an agreement, and advanced structure validation
1195///     and every recheck judge it (`DocumentActionFeeModeratorsShareMismatchError`,
1196///     40139, for any other lower amount or with no seated charter). The
1197///     interim team's claim of the moderators pot is refused once a charter is
1198///     seated (41113). No table moves: every generation involved is unreleased,
1199///     but for the shipped batch `validate_state` v0, which no batch of an
1200///     earlier version reaches through the new hook.
1201///
1202/// 41. **A seated team's pot, action counts and reasons**: the leader or an
1203///     active member of a seated team claims the moderators pot for the team,
1204///     and it is split by the proposal's `rewardSplit`: the leader share to the
1205///     leader, the equal share between the other members (the leader's when it
1206///     has none), and the action share between the whole team by each one's
1207///     count of bans, suspensions, warnings and document deletions since the
1208///     last settle, equally when nobody acted. Every part rounds down and the
1209///     remainder stays in the pot. The counts are `member id -> u32` items
1210///     without storage flags under key `48` of an elected contract's other tree,
1211///     created with the contract (`insert_contract_moderation_trees` v0), and
1212///     every settle deletes them. An `addedModerator` or `removedModerator`
1213///     created or deleted settles the pot first, to the team as it was, by a
1214///     hook in the batch's `validate_state` v0 beside the cap on additions: it
1215///     ignores the once-per-epoch limit and writes no last claim. The proof of a
1216///     claim by a seated team's member, whom the contract does not name as a
1217///     recipient, shows the claimant's balance alone. A moderation reason gains
1218///     `reasonDocumentId` (tag bit 2 where it is stored), and a seated team's
1219///     ban, suspension, warning or deletion must name a `reason` document its
1220///     proposal lists (`ModerationReasonNotListedError`, 41203). No table moves
1221///     but the four
1222///     new Drive method slots, `0` at every version. The counts are read with
1223///     the `getContractModerationActionCounts` query (an elected contract
1224///     only), whose proof reads the whole counts tree; it adds a fifth contract
1225///     method slot (`prove_contract_moderation_action_counts`), a verify slot
1226///     (`verify_contract_moderation_action_counts`) and the query's bounds
1227///     (`contract_moderation_action_counts`), `0` at every version as well.
1228///
1229/// 42. **Repaid identity debt reaches the processing fee pool**: an identity
1230///     whose fee the balance could not fully cover keeps the unpaid processing
1231///     part as a debt (its negative credit balance), and credits it receives
1232///     while its balance is empty still repay that debt first. The repaid part
1233///     now goes to the processing fee pool of the epoch it is repaid in, where
1234///     the unpaid fee would have gone; before, it reached no balance the credit
1235///     sum counts. `add_to_identity_balance` 1 marks it with a
1236///     `LowLevelDriveOperation::RepaidIdentityDebt`, and every apply routes it:
1237///     `apply_drive_operations` 1 writes it to the pool after the batch (so it
1238///     adds to the end of block fee distribution the same batch may write,
1239///     unbilled), `apply_balance_change_from_fee_to_identity` 1, which now takes
1240///     the block info, writes it in its own batch (the fee paid is unchanged),
1241///     and `add_epoch_pool_to_proposers_payout_operations` 1 hands the epoch
1242///     payouts to the block's `apply_drive_operations` instead of converting
1243///     them to a plain grove batch, skips a share whose `payToId` has no
1244///     balance and caps each share at what is left of its masternode's payout.
1245///     An apply that meets one it does not route fails (`CorruptedCodeExecution`)
1246///     instead of dropping it. `apply_drive_operations` 1 also merges every
1247///     credit and debit one batch makes to an identity's balance into one net
1248///     write: each converts against the balance committed before the batch, so
1249///     a second write replaced the first and two credits to an indebted
1250///     identity repaid its debt twice.
1251///
1252/// 43. **The end-date cleanup of ended contested vote polls**: a block ends at
1253///     most `maximum_vote_polls_to_process` vote polls, the earliest end date
1254///     first, and removes their entries from the end-date queries.
1255///     `remove_contested_resource_vote_poll_end_date_query_operations` 2
1256///     (`DRIVE_VOTE_METHOD_VERSIONS_V3`) removes an end date only once none of
1257///     its vote polls remain: it reads the entries under the date and keeps the
1258///     date while any of them is not removed in the same batch, so the polls
1259///     left end in a later block.
1260///
1261/// 44. **The total supply ceiling bounds every mint and direct purchase**: a
1262///     token's total supply is stored in a sum item, so it can never pass
1263///     `i64::MAX`. Token mint and token direct purchase state validation 1
1264///     treat `i64::MAX` as the max supply when the token configures none (and
1265///     cap a configured one there), refusing a mint or purchase past it with
1266///     `TokenMintPastMaxSupplyError`, as a paid consensus error. State
1267///     validation 0 checked only a configured max supply, so such a transition
1268///     failed in execution as an internal error instead. Both now read the
1269///     total supply on every mint and purchase, and pay for that read.
1270///
1271/// 45. **A masternode vote towards an identity names a contender**: a
1272///     `ResourceVoteChoice::TowardsIdentity` vote for an identity that is not a
1273///     contender of the poll is refused, unpaid, with
1274///     `VoteChoiceNotAllowedForVotePollError` (40307) by `validate_state` 1 of
1275///     the masternode vote, which reads the contender's document reference
1276///     under the poll. The reserved keys of the poll's stored info, abstain
1277///     tree and lock tree are refused before that read. Before, a vote for an
1278///     unknown identity failed with an internal error, and a vote towards a
1279///     reserved key was counted as Lock or Abstain. `check_for_ended_vote_polls`
1280///     1 also ignores the lock tally of a contest resolved without locking. No
1281///     table moves: both generations are selected by this version alone.
1282///
1283/// 46. **A raw state transition is exactly one encoded transition**:
1284///     `decode_raw_state_transitions` 1 (`DRIVE_ABCI_METHOD_VERSIONS_V10`)
1285///     decodes with `StateTransition::deserialize_from_bytes_untrusted_exact_in_version`,
1286///     so bytes left over after the transition are an invalid encoding
1287///     (`SerializedObjectParsingError`, 10002), refused unpaid in `check_tx` and
1288///     in block processing. Version 0 ignored them, so the transition with
1289///     anything appended executed as the original under another transaction
1290///     hash. Version 1 also reports a transition whose version is outside its
1291///     active range as `StateTransitionNotActiveError` (10603) instead of a
1292///     decode failure, naming whichever boundary of that range was missed: its
1293///     start for a version not active yet, its end for one already superseded.
1294///
1295/// 47. **A storage refund is clawed back from the epochs it was priced for**:
1296///     removing data in epoch E refunds its owner the shares of epochs E+1
1297///     onward, and the refund waits for the next epoch change to be taken out of
1298///     the epoch storage pools. `add_distribute_storage_fee_to_epochs_operations`
1299///     1 (`DRIVE_ABCI_METHOD_VERSIONS_V10`) restores and subtracts it from the
1300///     epoch after the previous block's epoch, the one every pending refund was
1301///     priced in, so each of those epochs gives back its own share. The shares
1302///     of epochs that closed before the current one, skipped by a halt, and the
1303///     rounding leftovers come out of the current epoch. Version 0 started from
1304///     the epoch after the current one, so the current epoch kept most of its
1305///     refunded share and the later epochs gave back more than theirs. The
1306///     total taken out equals the refund in both.
1307///
1308/// 48. **An evonode's token claim covers only the epochs it read**: an
1309///     `EvonodesByParticipation` perpetual distribution weighs each cycle by the
1310///     claimant's share of the blocks proposed in the epochs it spans, from their
1311///     finalized epoch infos. Up to v13 the claim read at most
1312///     `drive_abci.query.max_returned_elements` (100) of them but evaluated its
1313///     whole range, up to 128 cycles (32,767 for a fixed amount), from the last
1314///     paid moment or, on a first claim, from the start of the distribution. An
1315///     evonode more than 100 epochs behind (about 2.5 years on mainnet, 4 days on
1316///     testnet) had every claim of a function other than a fixed amount fail as an
1317///     internal error, with its last paid moment never advancing, while a fixed
1318///     amount applied the share of the epochs read to the whole range. A claim
1319///     reaching an epoch whose info the fee distribution of the block had not
1320///     written yet (the previous epoch, in the first block of an epoch) failed or
1321///     was weighed the same way. `evonode_participation_rewards` 1
1322///     (`DRIVE_TOKEN_METHOD_VERSIONS_V2`) reads the epochs after the cycle start of
1323///     the last paid moment, at most `SYSTEM_LIMITS_V4.max_evonode_reward_claim_epochs`
1324///     (100, backfilled into the earlier tables) or one whole cycle when a cycle is
1325///     longer, and pays through the last whole cycle it read, which the claim stores
1326///     as the last paid moment, so an evonode that is behind is paid over several
1327///     claims. An epoch without finalized info before the last one read, one in which
1328///     no block was produced, counts as an epoch without blocks, and a claim that
1329///     read no whole cycle is refused, paid, with `InvalidTokenClaimNoCurrentRewards`.
1330///     `get_finalized_epoch_infos` now takes its limit from the caller; every other
1331///     caller passes the query bound it read before.
1332///
1333/// 49. **Documents with a time to live**: the doctype-level `ttl` keyword (meta-schema v3,
1334///     document type parser generation 3) makes the platform delete each document of the
1335///     type `ttl` seconds after its `$createdAt`, at most
1336///     `max_document_expirations_per_block` (128, `SYSTEM_LIMITS_V4`) weighing at most
1337///     `max_document_expiration_weight_per_block` (1,024 in documents plus their index levels)
1338///     per block after the block's state transitions (`expire_documents` 0 in
1339///     `DRIVE_ABCI_METHOD_VERSIONS_V10`). The keyword requires `$createdAt`, is refused
1340///     with `documentsKeepHistory`, `indexOnly` and a contested index, is at least
1341///     `min_document_ttl_seconds` (one hour) and at most `max_document_ttl_seconds` (one
1342///     year) at registration, and is fixed on update (`validate_update` 1). References treat such a type as deletable. Its
1343///     documents are stored without storage flags, indexed in the documents expirations
1344///     tree under `Misc` (created by `create_initial_state_structure` 4 and
1345///     `transition_to_version_14`), and pay the `document_ttl` group of `FEE_VERSION3`: a
1346///     price per byte for the time they live (tiers up to seven days, then per 9.125 days),
1347///     paid out to the epochs they live in (at most one era) through the lifetime storage fee
1348///     pools under `Pools` (`add_distribute_block_fees_into_pools_operations` 1),
1349///     plus their deletion prepaid as processing (per index level and per document byte; a
1350///     change that grows a document prepays its added bytes). From its expiry on, a
1351///     document can no longer be replaced, transferred, bought, repriced or restored by a
1352///     moderator (`DocumentExpiredError`, 40140), judged from its own `$createdAt`; its
1353///     owner may still delete it where `canBeDeleted` allows. See
1354///     `book/src/data-model/document-ttl.md`.
1355///
1356/// 50. **A contest accepts at most 1,000 contenders, and its end reaches every
1357///     one**: document create state validation 2 (`DRIVE_ABCI_VALIDATION_VERSIONS_V10`)
1358///     refuses, paid, a document that would add a contender to a contest holding
1359///     `max_contenders_per_contest` (`SYSTEM_LIMITS_V4`, 1,000) already
1360///     (`DocumentContestMaximumContendersReachedError`, 40141).
1361///     `add_contested_indices_for_contract_operations` 1
1362///     (`DRIVE_DOCUMENT_METHOD_VERSIONS_V4`) writes the last index value of a
1363///     poll started from this version as a count tree, so the join reads the
1364///     count in one element fetch; a poll started before keeps its plain tree
1365///     and has its contenders counted by a keys query of at most 1,000.
1366///     `maximum_contenders_to_consider` rises from 100 to 10,000, so the tally
1367///     of an ended poll, and the cleanup built from it, cover every contender
1368///     of a poll within the cap, and up to 10,000 of one that grew past it
1369///     before this version. `check_for_ended_vote_polls` 1 compares every tied
1370///     contender; version 0 compared at most 100.
1371///
1372/// 51. **The fund a contender pays doubles for every 50 contenders a contest
1373///     holds past 250**: the fund to join a contest is its fund doubled once
1374///     the contest holds `contested_document_contenders_before_fund_doubling`
1375///     (`FEE_VERSION3`, 250) contenders and again for every
1376///     `contested_document_contenders_per_fund_doubling` (50) more: 0.1 DASH
1377///     for the first 250 DPNS contenders, 0.2 for the next 50, up to 3,276.8
1378///     for the 951st to the 1,000th, so filling a contest costs 327,695 DASH
1379///     where it cost 100. A contender's prefunded voting balance is the most it
1380///     pays: document create state validation 2 refuses, paid, one stating less
1381///     than the fund to join (`DocumentContestNotPaidForError`, carrying that
1382///     fund), the first contender of a new contest included, and charges one
1383///     stating more only the fund to join, the rest staying with it. Document
1384///     create structure validation 1 leaves the amount to state validation;
1385///     version 0 wants exactly the contest's fund.
1386///
1387/// 52. **Property constraints judge what is stored, and read `$defs`**: to
1388///     `present` and `absent` (item 39), an object none of whose members is
1389///     present (`{}`, or `{ "inner": {} }` around one) is absent, since a
1390///     stored document reads it back as no object at all. A create or replace
1391///     carrying `meta: {}` was judged with `meta` present, and a later
1392///     transfer, purchase or price update, judged on the stored document, with
1393///     it absent. The parser (generation 3) reads the schema of a property
1394///     given as a `$ref` to the contract's `$defs` from the definition, as the
1395///     core parse does, when it checks a rule's string constants and defaults
1396///     against the property's `enum` and an `encryptedFor` key id's bounds; it
1397///     refused every such contract with a decoding error before.
1398///
1399/// 53. **Properties the platform generates (`generatedFrom`)**: the property
1400///     keyword (meta-schema v3, `apply_generated_from` 0, `GeneratedFrom` on
1401///     `DocumentProperty`) names a built-in `function` and its `params`,
1402///     properties of the same document type, as
1403///     `{ "function": "sys.stringTransformations.homographSafeASCII", "params": ["label"] }`.
1404///     System functions are named under `sys.`, leaving other names to
1405///     functions a contract may bring later. The `sys.stringTransformations`
1406///     functions take one string and change ASCII characters only, keeping
1407///     every other character, without Unicode tables: `lowercase`,
1408///     `uppercase`, `capitalize`, `camelCase`, `snakeCase`, and
1409///     `homographSafeASCII`, which lowercases, then maps `o` to `0` and `i`
1410///     and `l` to `1`, DPNS's label normalization over ASCII. The parser
1411///     checks at registration and update that `params` holds as many
1412///     properties as the function takes, each another string property that
1413///     is not generated itself, that neither the property nor a param is
1414///     transient or inside a transient object, and that every param sits
1415///     inside every object holding the property; a changed declaration is an
1416///     incompatible schema change, and `validate_update` 1 refuses a property
1417///     an update adds over params that all already existed
1418///     (`DocumentTypeUpdateError`, 40212); meta-schema v3 refuses the keyword
1419///     beside `$ref`, whose definition would replace it.
1420///     `fill_generated_properties` (0) writes a declared property a document
1421///     leaves out, from its params, in the action transformers of document
1422///     create, replace and index-only delete, before the contest resolution
1423///     and every check read the data, in `Document::try_from_create_transition`
1424///     and `try_from_replace_transition`, and in
1425///     `index_only_transition_entry_path_query`, the builder the prover and
1426///     the verifier share, with which proofs are built and checked. The client
1427///     transition builders, the SDK's contest fund lookup and the JS and FFI
1428///     property-constraint pre-checks call `regenerate_generated_properties`
1429///     (same slot) instead, which replaces a value the document holds and
1430///     removes it when a param is absent, so a transition built from a
1431///     fetched and edited document carries the value of its new params and
1432///     its contest is detected from it.
1433///     `DataContract::validate_document_properties` 0 calls
1434///     `validate_generated_from_properties` (`validate_generated_from` 0)
1435///     after the schema and `maxBytes`, and refuses a supplied value that is
1436///     not what the function generates, one without its params, or a document
1437///     repeating a key on the way to the property or a param, with
1438///     `DocumentPropertyNotGeneratedError` (10424). Every call site was
1439///     extended in place and is inert before this version, where the three
1440///     slots are `None` and the meta-schemas refuse the keyword.
1441///
1442/// 54. **An aggregate keyword names a top-level property**: `summable` and
1443///     `averageable` on an index, and `documentsSummable` and
1444///     `documentsAverageable` on a document type, name the integer property
1445///     each document adds to the sum. Drive reads its value from the top level
1446///     of the document, but the parser resolves the name among the flattened
1447///     properties and required fields, which also hold the dotted path of a
1448///     property nested in an object, and meta-schemas v1 and v2 bound only the
1449///     name's length. A contract naming `payment.amount` registered, and every
1450///     document create of the type then failed in Drive as an internal error.
1451///     Meta-schema v3 (`CONTRACT_VERSIONS_V6`) gives the four keywords the
1452///     property-name pattern `^[a-zA-Z0-9_]{1,64}$`, so a create or an update
1453///     carrying a dotted name is refused under full validation
1454///     (`JsonSchemaError`, 10101, paid in a block; `check_tx` does not fully
1455///     validate a contract). A contract stored with one still loads, since a
1456///     stored contract is parsed without full validation, but can no longer be
1457///     updated; no contract on mainnet or testnet names one.
1458/// 55. **A preallocated index's agreement source fits a tree key**: contract
1459///     create and update state validation 1 refuse, paid, a
1460///     `propertyAgreement` pair through which a preallocated index is keyed
1461///     when its referenced property can hold a value over 255 bytes
1462///     (`ReferencedDocumentPropertyAgreementInvalidError`, 40126): creating a
1463///     referenced document writes that value as a tree key, which failed with
1464///     an internal error for a value over 255 bytes, and for any value once
1465///     the property's midway size, which sized the estimate, passed 255
1466///     bytes. `add_document_for_contract_operations` 1 now estimates that
1467///     layer from the referring property, as an entry insert does, and
1468///     preallocates nothing for a referenced value wider than the referring
1469///     property can hold, which no referring document can agree with.
1470///
1471/// 56. **A `propertyAgreement` pair compares values, not index keys**:
1472///     document reference validation 0 judges each pair as two single values
1473///     (`Value::same_scalar_data`): strings as text, byte arrays and
1474///     identifiers as bytes, integers as numbers at any width, floats by
1475///     their `f64` bits (an integer against a float read as the float it
1476///     converts to, as a `number` carried as an integer is stored), booleans
1477///     as booleans. An identifier or byte array carried as an array of
1478///     `U8`s is the bytes it lists, as before; any other array agrees with
1479///     nothing. It compared the two sides' index key encodings, under which
1480///     `""` agreed with `"\0"`, and two equal values over 255 bytes, which
1481///     an unindexed string of 64 characters or more can hold, were refused
1482///     (`ReferencedDocumentPropertyMismatchError`, 40127).
1483///
1484/// 57. **Moderator abilities, and fields only moderators write**: the two
1485///     doctype keywords of moderator deletion (19) become one object,
1486///     `moderatorAbilities` (meta-schema v3): `delete` for
1487///     `canBeDeletedByModerators`, `deleteWithin` for
1488///     `canBeDeletedByModeratorsFor`, and `changeFields`, the top-level
1489///     properties only the contract's moderators write. The whole object is
1490///     fixed with the type (40212). `deleteKeepsRecord` (default true) says
1491///     whether a moderator's deletion leaves its removal record: without one the
1492///     type has no records tree, the query refuses it, a restore is refused
1493///     (41119) and the deletion is proved by the document's absence, which the
1494///     verifier learns from the contract. `deleteRefundsOwner` (default false)
1495///     says whether the owner is refunded its storage instead of forfeiting it
1496///     (the batch then carries no `ForfeitStorageRefunds`). `deleteKeepsFields`
1497///     lists property paths at any depth, and the timestamps and block heights
1498///     the type requires, whose values the removal record keeps, copied from
1499///     the document as it was deleted: what stays public once it is gone. It
1500///     needs a record, and is fixed with the type. A record keeping any
1501///     carries them behind bit 1 of its tag byte, encoded as the document
1502///     encodes its properties (a presence byte and the value per kept path,
1503///     the paths themselves not written) and read under the document's type,
1504///     and a record keeping none is written as before; the removals
1505///     response carries the bytes as `kept_fields` (field 8), and a
1506///     `moderatedDocument` reference's `where` pair on a kept property is
1507///     checked against the record's value. A property `changeFields` lists must be declared,
1508///     optional, stored, not immutable, neither a reference nor read by one,
1509///     neither generated nor a generation parameter, and in no contested index,
1510///     on a type that is not indexOnly; a type listing any keeps `$revision` even
1511///     when `documentsMutable` is false, and a lookup key or a list element's
1512///     list may not read such a field of the type it refers to.
1513///     `ContractUserModeration` gains the `ChangeDocumentFields` action: a
1514///     moderator (for a seated team, holding the new `changeDocumentFields`
1515///     ability, appended to `ModerationAbility`, on the type, and citing a
1516///     listed reason) sets or removes those fields on any document of the type,
1517///     whoever owns it. The changed document is judged as a replace judges one
1518///     (schema, `propertyConstraints` with their totals, `distinctFrom`,
1519///     `encryptedFor` shapes, unique indexes through
1520///     `validate_moderated_document_uniqueness`, the restore's check
1521///     generalized and renamed), its references are not checked again, and it
1522///     is stored with the replace's update, `$revision` one higher and
1523///     `$updatedAt` untouched; the moderator pays, refunds of what the change
1524///     frees stay the owner's. A change that changes nothing is refused (10905),
1525///     and a seated team's change does not count toward its action share. An
1526///     elected declaration must give its team `changeDocumentFields` on every
1527///     type that lists fields. The proof
1528///     is the document as it now stands. The batch transformer (in place,
1529///     inert before 14) refuses a document's owner who sets, changes or removes
1530///     such a field without moderating the contract, in the mempool too. New errors:
1531///     `InvalidContractModerationDocumentFieldsError` (10905),
1532///     `DocumentFieldNotChangeableByModeratorsError` (41123) and
1533///     `DocumentModeratorFieldNotWritableError` (41124), appended.
1534/// 58. **The last moderator's stamp (`$moderatedAt`, `$moderatedBy`)**: two
1535///     system properties of a document whose type keeps fields for its
1536///     moderators (57), the block time and the identity of the last moderator
1537///     to write them. A `changeDocumentFields` sets both, and so does the batch
1538///     transformer (in place, inert before 14) for a create or replace whose
1539///     signer moderates the contract and writes such a field; a replace that
1540///     leaves the fields alone carries them over, and transfers, purchases,
1541///     price updates and restores keep them. Document serialization format 3
1542///     (this version's) records them behind bits 512 and 1024 of its time
1543///     field flags, so a document without them is written as before. Parser
1544///     generation 3 lets an index name either on such a type, never in a
1545///     unique index (10231); the shipped index key, query value and size
1546///     arms for the two names (`get_raw_for_document_type` v0,
1547///     `serialize_value_for_key` v0, Drive's estimated key sizes) are reached
1548///     only through such an index.
1549///
1550/// 59. **`skipIfAbsent` at any position, `true` or an array, on every type**:
1551///     document meta-schema v3 and the generation-3 parser take
1552///     `skipIfAbsent: true` (skip on every optional property of the index)
1553///     or an array naming the skip set, and a skip property may sit at any
1554///     position of the index, under a `timeRange` window too. A stored type
1555///     may declare it (the array may then leave some optional properties on
1556///     the null key), except on a contested index or next to
1557///     `nullSearchable: false`; on an indexOnly type the skip set is every
1558///     optional property of the index and each optional property needs a
1559///     skip index of its own, without a `timeRange`. No `rankedCountable`
1560///     `at` level may sit above a skip property; on a stored type a ranking
1561///     at a skip property may not share its level with an index keeping the
1562///     null layout for it, and a skip property that is a byte array needs
1563///     `minItems` of at least 1. The v2 insert and delete walkers write an index's
1564///     entry only for a document carrying its skip set and build a level
1565///     only when an entry of the document sits at or below it; update 1
1566///     moves a replaced document into or out of a skip index; every index
1567///     picker (server and verifier) admits a skip index only for a query
1568///     binding each skip property, on a stored type with a constraint no
1569///     missing value can meet. A contract valid before keeps its layout and
1570///     queries: it could only skip on an indexOnly index's first property,
1571///     where both rules agree.
1572///
1573/// 60. **Integer-range indexes**: an index can declare an `integerRange`
1574///     transform (`on`, `range`, `step`, optional `phase < step`) that
1575///     buckets a required user integer property of at most 64 bits into
1576///     windows starting at `phase + k * step`; a start below the lowest
1577///     value of the property's integer type is clamped to it, so every
1578///     value is in at least one window. It shares the time-range machinery:
1579///     the grid-qualified level key, the walkers' fan-out (the insert, delete
1580///     and update walkers read one `IndexBucketing`), the overlap cap
1581///     (`SystemLimits::max_time_range_overlap_factor`) and the resolution
1582///     provenance that keeps raw queries off a bucketed index. The v1
1583///     `getDocuments` handler resolves the new `IN_INTEGER_RANGE` operator,
1584///     a typed `IntegerRangeSelection` naming one window by its start, to a
1585///     window-start equality from the query alone. `unique: true` needs
1586///     non-overlapping windows; the uniqueness probe (v1) looks in the
1587///     candidate's window and lets a document change its value within its
1588///     own window. An indexOnly type cannot declare one (its entries would
1589///     collide across rows that share a window); neither kind of bucketed
1590///     index can be a `refersTo` lookup target or a `propertyConstraints`
1591///     answering index; a nested source must sit in required objects and
1592///     the grid-qualified level key fits 255 bytes; and a document `ttl`
1593///     prices every window an integer-range index writes.
1594///
1595/// 61. **A `refersTo` may find its document by a hash the document reveals**:
1596///     a `findBy` entry may be a function, `"<referenced property>": {
1597///     "function": "sys.hash.sha256d", "params": [...] }` (meta-schema v3
1598///     `findByFunction`, parser generation 3, `apply_property_reference` 0):
1599///     the document is found by that property holding the SHA-256 of the
1600///     SHA-256 of the params' bytes joined in order, a property path of the
1601///     document (the property carrying the reference included),
1602///     `{ "const": text }`, or `"."` for a value without a path (each element
1603///     of a typed array, the writer, the creator), a string counting as its
1604///     UTF-8, a byte array as its bytes, an identifier as its 32 bytes. The
1605///     parser holds the function as the lookup's computed key
1606///     (`LookupKeySource::Hash`). The function is `SystemFunction::Hash`, a
1607///     `sys.hash` namespace beside the string transformations of
1608///     `generatedFrom`, which refuses it. A string or byte array property may
1609///     now carry a `refersTo` whose function reads its value
1610///     (`DocumentProperty::revealed_reference`, `PropertyReference::Revealed`);
1611///     the property keeps its type. The document such a key finds is a
1612///     commitment made earlier, so the reference is judged when the document is
1613///     created only: its params may be transient or optional, every stored value
1614///     it reads must be fixed once written, and a replace leaves it alone.
1615///     Document create structure validation 1 refuses a create missing a param,
1616///     repeating a key on the way to one, or whose variable-length param holds
1617///     the one-byte separator that must follow it
1618///     (`DocumentReferencePreimageInvalidError`, 10423). Beside a `findBy`
1619///     function the reference may declare `minimumAgeBlocks`, judged by
1620///     document create state validation 2 against the found document's
1621///     `$createdAtBlockHeight` (`ReferencedDocumentRequirementNotMetError`,
1622///     40142), and `consume`, which deletes the found document with the create
1623///     (`DocumentCreateTransitionAction` `consumed_documents`, a batch touching
1624///     it elsewhere refused with 40120). The hash is computed once per key and
1625///     billed as `ValidationOperation::DoubleSha256` by the blocks it hashes,
1626///     beside the document fetch. Such a `deletableDocument` reference, judged
1627///     on the create alone, may sit on an `immutable` property, which
1628///     `validate_no_immutable_deletable_element_references` otherwise refuses.
1629///     A `where` entry `{"$ownerId": "$ownerId"}` makes the commitment the
1630///     writer's own, and `consume` requires it, into the declaring contract,
1631///     on a type whose owners may delete, that keeps no history, declares no
1632///     delete token cost or delete action fee and requires no stricter
1633///     signature security level than the declaring type; batch advanced
1634///     structure 1 refuses a contract-bound key whose bounds leave out a type
1635///     the created type may consume (`ContractBoundedKeyOutOfBoundsError`,
1636///     20014). The consumed deletes are converted with the create's own
1637///     operations pending, so a type may consume its own documents. The
1638///     `where` entries beside a function are judged with it, on the create
1639///     alone, so the properties they read must be fixed once written or
1640///     transient; on a mutable type such a reference may not be an `anyOf`
1641///     operand; and no property a function reads may be listed under
1642///     `moderatorAbilities.changeFields` (57). `creatorRefersTo` takes a
1643///     `deletableDocument` target through a function, and the `findBy` of an
1644///     `ownerRefersTo` or `creatorRefersTo` may leave the value out beside
1645///     one. The declaration reproduces the DPNS preorder hash of a name under
1646///     a parent byte for byte; the DPNS contract and its create trigger are
1647///     unchanged. See `book/src/data-model/documents.md`.
1648///
1649/// 62. **Null flags follow each index's own path**: the v2 index-level
1650///     insert and delete walkers give each sub-level the null flags of its
1651///     parent and its own value. They carried the flags from one sibling
1652///     sub-level into the next, so a unique index could store its entry in
1653///     the `[0]` tree meant for a missing value, and a `nullSearchable:
1654///     false` index an entry for a document missing all of its values,
1655///     because of another index's values; update 1 and the document cost
1656///     model already judged each index alone. Entries written before
1657///     (by that carrying, or by update 0, which laid out a unique index
1658///     with some values missing as the bare reference and never skipped a
1659///     `nullSearchable: false` entry) are found where they are stored: where
1660///     an earlier writer could disagree with the rule, the v2 delete walker
1661///     and update 1 read the stored `[0]`, unbilled, and remove or refresh
1662///     the entry there; a type with a `ttl` skips the read.
1663///
1664/// 63. **`refersTo` finds its document with `findBy` and checks it with
1665///     `where`**: the reference keywords the notes above describe are spelled
1666///     anew in meta-schema v3 and parser generation 3 (`apply_property_reference`
1667///     0), in place, before this version reaches a network. `lookup: { index,
1668///     keys }` is `findBy`, the key parts directly (`{ "<index property>":
1669///     <source> }`, a function entry included, see 61), without the index
1670///     name: the index is the unique one of the referenced document type whose
1671///     properties are exactly those `findBy` names, not bucketing its first
1672///     property (`DocumentReferenceLookup::resolve_index`, run at contract
1673///     parse or registration and when the document is fetched; a type's
1674///     indexes never change after it is registered). A plain `propertyAgreement`
1675///     pair `{ "<referring>": "<referenced>" }` is a `where` entry keyed the
1676///     other way, `{ "<referenced>": "<referring>" }`, so every map of a
1677///     `refersTo` is keyed by the referenced document's property; the parsed
1678///     model keeps `property_agreement` keyed by the referring property, and a
1679///     referring value may be compared once. `listElement` is a
1680///     `permanentDocument` with `inList`, found by `findBy { "$id":
1681///     "<property>" }`, the one `findBy` entry that names `$id`. Without
1682///     `findBy` the value is the document's `$id`, as before. The parser refuses
1683///     `lookup`, `propertyAgreement` and the `listElement` type on every parse,
1684///     naming what replaced each, so a contract written with them never loads
1685///     with another meaning. `ReferencedDocumentLookupInvalidError` (40137)
1686///     carries the properties `findBy` names in place of an index name. The
1687///     moderation charters system contract is written in the new keywords;
1688///     the parsed declarations, and so validation and execution, are
1689///     unchanged.
1690/// 64. **`refersTo: moderatedDocument`**: a third kind of document reference,
1691///     between `permanentDocument` and `deletableDocument` and disjoint from
1692///     both (`DocumentReferenceKind`, `DocumentTypeV2Getters::document_reference_kind`),
1693///     in place in meta-schema v3, parser generation 3 and the generation 0
1694///     reference validators. Its target is a document type whose documents
1695///     leave state only when the contract's moderators remove them, each
1696///     removal on the record: `canBeDeleted: false`, no `ttl`, and
1697///     `moderatorAbilities.delete` with `deleteKeepsRecord` not false. Such a
1698///     type is no longer a `deletableDocument` target
1699///     (`ReferencedDocumentTypeModeratedError`, 40144), and a
1700///     `moderatedDocument` reference to any other type is refused
1701///     (`ReferencedDocumentTypeNotModeratedError`, 40143), at registration
1702///     and at write time. The id form only: no `findBy`, `inList` or operand
1703///     of an expression. The document must exist when the reference is
1704///     written; a replace re-validates it as a permanent one (the value or a
1705///     property its `where` reads changed, or always for a writer gate), and a
1706///     value the stored document held whose document a moderator removed
1707///     resolves to the removal record, read and billed: a `where` pair asked
1708///     about again compares the record's document owner for `$ownerId` and the
1709///     id for `$id`, and refuses any other property
1710///     (`ReferencedDocumentRemovedError`, 40145); a writer gate may compare
1711///     only those two (parser, 10231), being asked about on every replace. A
1712///     value is held when it is the stored document's at its path, compared
1713///     through the stored values the replace action carries for a changed
1714///     top-level property. The write-time kind check still lets a
1715///     `deletableDocument` reference to a moderated type through, which a
1716///     contract registered before this note may hold. Chained and composite joins
1717///     through it prove, as one more component of the merged proof, the
1718///     removal records of the joined ids beside their documents, and report
1719///     each removed document by its record (`removed_outer_documents = 4` on
1720///     `ChainedDocuments`, `removed = 4` on a composite `SubQueryResult`,
1721///     additive); a joined id with neither a document nor a record is refused
1722///     as a missing permanent target is. StateError discriminants 156-158.
1723///
1724/// 65. **An index may hold a value of the document a reference points at**: an
1725///     index property `"<reference property>.<field>"` (`DerivedIndexProperty`,
1726///     `DocumentTypeV2Getters::derived_index_properties`), such as a reply's
1727///     `postId.$ownerId`, in place in parser generation 3 (`admit_derived_index_properties`,
1728///     `apply_derived_index_properties`) and `create_document_types_from_document_schemas`
1729///     1 (`resolve_derived_index_properties`, which gives a schema field its type on the
1730///     referenced type on every parse). The document never stores the value: before Drive
1731///     keys a document of such a type, on insert (`add_document` 1), update (`update_document`
1732///     1, one read for both versions) and delete (`delete_read_document`, shared by owner and
1733///     moderator deletes and `ttl` expiry), it reads the referenced document, billed with the
1734///     write, or, for a `moderatedDocument` target a moderator removed, the owner and the
1735///     values its removal record keeps (`ContractDocumentRemoval::kept_values`, read at a
1736///     path by `kept_value_at`), and puts the values into the document's properties under
1737///     the derived names, where `get_raw_for_document_type` 0 reads them (a missing one is
1738///     refused, never keyed under null) and the serialization ignores them. A create reads nothing more: the
1739///     document reference validation 0, given a map, records the values from the documents it
1740///     fetched, and the create action carries them to Drive. A dry run keys the document
1741///     under a value of each field's type. `serialize_value_for_key` 0,
1742///     `deserialize_value_for_key` 0 and Drive's estimated key sizes take a derived name's type
1743///     from the declaration. Registration (full validation, `InvalidContractStructure`)
1744///     admits one only where the value can not change once written: a same-contract
1745///     `permanentDocument` or `moderatedDocument` reference by id, on a reference property
1746///     fixed once written; `$ownerId` of a type that can not change hands, `$creatorId` of a
1747///     type recording it, or a stored schema property fixed once written and indexable;
1748///     through `moderatedDocument`, `$ownerId` or a schema property the referenced type keeps
1749///     under `moderatorAbilities.deleteKeepsFields` (a kept path or one inside a kept object,
1750///     `is_path_listed`, checked in every build); not `$id`; not in a unique or contested
1751///     index, or as a `timeRange` or `integerRange` source; not on an indexOnly type. A
1752///     `skipIfAbsent` array may name one (`reads_through_reference`), which `skipIfAbsent:
1753///     true` leaves out; `resolve_derived_index_properties` refuses one that is never absent
1754///     (a required reference reading `$ownerId`, `$creatorId`, or a field required with every
1755///     object around it) or a byte array that may be empty. The v2 walkers, update 1 and the
1756///     SDK cost walker count a null skip value as absent (`document_carries`), as a derived
1757///     value is when its reference or field is. A `startAt` or `startAfter` cursor, placed by
1758///     what the named document stores, is refused on an index whose derived properties the
1759///     query does not fix with `==`. Every step is inert without a derived index property,
1760///     which only generation 3 declares.
1761///
1762/// 66. **Properties frozen under a condition**: an `immutable` entry of
1763///     meta-schema v3 and parser generation 3, in place, may be
1764///     `{ "property", "when" }` beside a property name. The condition takes
1765///     the grammar of a `propertyConstraints` rule, reads no `countOf` or
1766///     `sumOf`, and is judged on the document the replace writes (its
1767///     `$updatedAt` the replace's block time, so `$updatedAt - $createdAt`
1768///     is the document's age), with the stored document's properties read
1769///     through `$old.<path>` (`STORED_DOCUMENT_PREFIX`), which only such a
1770///     condition may read. Document replace state validation 1, extended in
1771///     place, refuses a replace changing, adding or removing a property whose
1772///     condition holds, or faults, with `DocumentImmutablePropertyChangedError`
1773///     (40128); the stored properties are rebuilt from the written ones and
1774///     `stored_changed_values`, and the replace action's `added_data_fields`
1775///     is gone. `immutableAllowSetting`, which `{ "present": "$old.<p>" }`
1776///     now says, is refused on every parse, naming its replacement. A
1777///     conditional property is not fixed once written
1778///     (`schema_property_is_fixed_once_written`), a `deletableDocument`
1779///     reference by id may be listed only without a condition, and on
1780///     contract update (document type update validation 1) a condition is
1781///     kept as it is or dropped for listing the property without one.
1782///
1783/// 67. **A seated team deletes a settled document together**: past a type's
1784///     `deleteWithin` window no moderator deletes a document alone (41116); the
1785///     new `moderatorAbilities.deleteSettled: { leader, approvals,
1786///     approversPredateDocument }` (meta-schema
1787///     v3, `DocumentTypeV2::moderator_settled_deletion`, fixed with the type,
1788///     40212) lets the members of an elected contract's seated team delete it
1789///     once `approvals` of them approve, the leader among them when `leader` is
1790///     set. It needs `deleteWithin` and an elected declaration giving the team
1791///     `deleteDocuments` on the type (10231, 10900), `approvals` from 1 to the
1792///     members the declared team can hold (its leader,
1793///     `SystemLimits::max_moderation_charter_elected_members` and the
1794///     declaration's `maxAddedModerators`), the upper bound checked at
1795///     registration only; a seated team whose charter elects fewer members,
1796///     and so holds fewer than the rule asks for, must have all it can hold
1797///     approve. Its `approversPredateDocument` (default `true` when `approvals`
1798///     is above 1, which then needs `$createdAt` in `required` at
1799///     registration, 10231) counts a member the leader added only for
1800///     documents created after its addition (the `addedModerator`'s
1801///     `$createdAt` earlier than the document's): a proposal or approval by a
1802///     later one is refused, checked before an approval already given, and an
1803///     approval that reads the team drops the approval of a member taken off
1804///     and added again too late; the leader and the elected members always
1805///     count.
1806///     `ContractUserModeration` gains two actions (appended), shaped like a
1807///     token group's action: `DeleteSettledDocument` proposes the deletion, kept
1808///     under the contract as a team action (other tree key `24`, `M` active and
1809///     `X` closed, each `action id -> { I: the action, S: SumTree(member ->
1810///     SumItem(1)) }`, created with the contract) by an id computed from the
1811///     contract, the proposer, its nonce, the document and the reason,
1812///     naming the document as it is (its last modification and `$revision`)
1813///     and the reason; and
1814///     `ApproveTeamAction { action_id }` approves it. Nothing lapses, but an
1815///     approval of a document changed since (its `$revision` moved, a
1816///     moderator's change of its fields included) is refused. Each approval is
1817///     its own sum item, never rewritten; when the approvals given could meet
1818///     the rule the team is read and the approvals of members who left are
1819///     dropped, refunded to them, and the one whose counted approvals meet it
1820///     deletes the document as `DeleteDocument` does, moves the action with the
1821///     approvals that counted to the closed actions (refunding each), and counts
1822///     toward the action share for every counted approver. The storage refund
1823///     forfeiture of a moderator's deletion now takes the document operations
1824///     alone (the document's bytes and any index subtree the deletion empties,
1825///     whoever paid for them), applied as a GroveDB batch of their own when the
1826///     batch also frees moderation storage someone is owed (a restored removal
1827///     record replaced, approvals moved or dropped), which is refunded as ever.
1828///     The proof is the signer's approval, active or closed
1829///     (`VerifiedContractTeamActionSignature`, appended); the new
1830///     `getContractTeamActions` (each action with its approval count, the sum
1831///     of its approvals tree) and `getContractTeamActionSigners` queries read
1832///     them. New errors, appended: `DocumentTypeNotDeletableOnceSettledError`
1833///     (41204), `ContractModerationTeamNotSeatedError` (41205),
1834///     `DocumentNotSettledError` (41206), `ContractTeamActionDoesNotExistError`
1835///     (41207), `ContractTeamActionAlreadySignedError` (41208),
1836///     `SettledDeletionNotRestorableError` (41209): a deletion the team approved
1837///     is never restored, by the leader or any member,
1838///     `ContractTeamActionAlreadyCompletedError` (41210),
1839///     `ContractTeamActionDocumentChangedError` (41211) and
1840///     `ContractTeamMemberAddedAfterDocumentError` (41212).
1841///
1842/// 68. **A preallocated index may be bound through `moderatedDocument`**:
1843///     `Index::preallocation_bindings`, in place, binds through a same-contract
1844///     `moderatedDocument` reference as through a `permanentDocument` one, and a
1845///     binding records its kind (`PreallocationBinding::kind`). Through a moderated
1846///     reference a binding holds only when the removal record of the referenced
1847///     document keeps every key it binds, the referenced `$id`, `$ownerId` or a property
1848///     the referenced type lists under `moderatorAbilities.deleteKeepsFields`
1849///     (`is_path_listed`), never `$creatorId`
1850///     (`PreallocationBinding::is_kept_on_removal`). `create_document_types_from_document_schemas`
1851///     1 and `set_document_schema` refuse, under full validation, a preallocated index
1852///     with no binding that holds (`validate_preallocated_indexes_kept_on_removal`,
1853///     `InvalidContractStructure`); the reference validation 0 checks the key width of a
1854///     `where` pair only through a binding that holds; and the Drive insert of a referenced document
1855///     (`add_document_for_contract_operations` 1) and the document cost model
1856///     preallocate only through one that holds
1857///     (`Index::preallocation_bindings_for_target`, now given the referenced type).
1858///     A moderator's removal leaves the trees, like its record, and a restore,
1859///     which puts the document back through the create path, finds them in place.
1860///     Inert for every contract that could be registered before: a moderated
1861///     reference never bound a preallocated index.
1862///
1863/// 69. **Index entries that outlive a delete (`outlivesDelete`)**: an index
1864///     keyword of meta-schema v3 and parser generation 3, in place
1865///     (`Index::outlives_delete`, `IndexLevelTypeInfo::outlives_delete`,
1866///     `IndexLevel::outlives_delete_at_or_below`), admitted only on a
1867///     `timeRange` index with a `ttl` of an indexOnly type, without a sum and
1868///     on a type without `entryPayload`; every schema property must also sit
1869///     in an index that neither skips nor outlives deletes, the proof index
1870///     may not outlive deletes (`index_only_proof_index`), and the flag is
1871///     fixed with the index (`find_first_outlives_delete_change`). A delete
1872///     leaves such an index's entries to expire with their window: document
1873///     index-only delete state validation 0 and Drive's row-integrity gate do
1874///     not probe it, and the delete walkers (top and index level 2) skip it
1875///     (`level_removes_entry`, with `IndexLevel::cleared_on_delete_at_or_below`). The row commitment leaves `$createdAt` out when
1876///     every index involving it outlives deletes
1877///     (`index_only_row_commits_created_at`, read off the index structure's
1878///     root, `IndexLevel::created_at_indexed_only_by_outliving`, and shared by
1879///     the commitment, the delete transition's construction, advanced
1880///     structure validation 0, which then refuses a carried `$createdAt`, and
1881///     Drive's indexOnly delete, which refuses one too). Document create state
1882///     validation 1 and the within-batch collision tracker do not probe such
1883///     an index, and the indexOnly terminal insert writes over an entry
1884///     already standing there, without reading it. Registration requires the
1885///     index's key (its properties but `$createdAt`, and its terminal) to hold
1886///     the key of an index a delete clears that skips nothing, so no two
1887///     documents in state share one of its entries. Inert for every contract
1888///     without the keyword, which every earlier grammar refuses.
1889/// 70. **A contested type sums only small values**: parser generation 3, in
1890///     place, refuses under full validation a document type with a contested
1891///     index and a summed property (`summable`, `averageable`,
1892///     `documentsSummable` or `documentsAverageable`) unless the property's
1893///     schema declares a `minimum` of at least -2^27 and a `maximum` of at most
1894///     2^27 (`SYSTEM_LIMITS_V4.max_contested_summed_value_magnitude`, `None` in
1895///     the earlier tables). The end of a contest writes the winner's document
1896///     into the type's sums with no transition to refuse, so the values must be
1897///     small enough that the sums stay in `i64`, which they do short of 2^36
1898///     documents. A stored contract still parses.
1899/// 71. **Documents deleted only when consumed (`canBeDeleted:
1900///     "onlyWhenConsumed"`)**: a third `canBeDeleted` value of meta-schema v3
1901///     and parser generation 3, in place
1902///     (`parse_can_be_deleted_only_when_consumed_keyword`, passed to the core
1903///     parse as `indexOnly` is, `false` for generations 1 and 2;
1904///     `DocumentTypeV2Getters::documents_deleted_only_when_consumed`). The
1905///     owner's delete reads it as `false` (document delete advanced structure
1906///     validation refuses it, 10404), and a `refersTo` with `consume` may
1907///     target the type (`DocumentReferenceLookup::referenced_side_error`,
1908///     which refused every type its owner can not delete). Its documents can
1909///     leave state, so the type is a `deletableDocument` target, never a
1910///     `permanentDocument` or `moderatedDocument` one
1911///     (`documents_can_disappear`, `document_reference_kind`). A consumed
1912///     document is deleted without its owner's `canBeDeleted` guard
1913///     (`ForceDeleteDocument` beside a contested create,
1914///     `force_delete_document_for_contract_operations` in
1915///     `AddDocumentAndDeleteConsumed`), so Drive's delete guard stays strict
1916///     for the owner's delete. Refused on a type that keeps history or is
1917///     indexOnly (10231), and fixed on update (`validate_update` v1, 40212).
1918///     Inert for every contract without the value, which every earlier grammar
1919///     refuses.
1920///
1921/// 72. **A barred author may still retract (`retractedWhen`)**: a document
1922///     type of meta-schema v3 and parser generation 3, in place, may declare
1923///     `retractedWhen`, one condition in the grammar of an `immutable` entry's
1924///     `when` (`$old.` reads, no `countOf` or `sumOf`), only on a mutable type
1925///     of a contract keeping a banlist or a suspension list (10231 on every
1926///     parse), and fixed on update (document type update validation 1, 40212).
1927///     `contract_moderation_gate` v0, in place, lets a banned or suspended
1928///     signer's replaces on such a type through with its bar
1929///     (`ContractModerationRefusal::retraction_bar`, `refused` now optional),
1930///     and the shared transformer, after fetching the stored document, refuses
1931///     with the bar (41107, 41108), paid with the nonce bumped, each whose
1932///     written document does not meet the condition or whose condition
1933///     faults. Every other rule of the type still judges the replace. So an
1934///     author whose documents can not be deleted can still take one back. Inert
1935///     before this version: the gate and the keyword exist only here.
1936///
1937/// 73. **An index that counts another index's entries
1938///     (`summableOffCountIndex`)**: an index keyword of meta-schema v3 and
1939///     parser generation 3, in place (`Index::summable_off_count_index`,
1940///     `IndexLevelTypeInfo::summable_off_count_index`), admitted only on an
1941///     indexOnly type with `rangeSummable`, naming a source index of the type
1942///     that holds every document once; its other properties must be fixed by
1943///     the source through unchanging `where` values of same-contract
1944///     `permanentDocument` or `moderatedDocument` references
1945///     (`validate_summable_off_count_indexes_lossless`, judging a value as a
1946///     lookup's key part is judged, `why_value_can_change`: an optional
1947///     `deletableDocument` reference by id a replace may clear once its
1948///     document is deleted is not fixed, and from this version neither is a
1949///     findBy key part, a findBy function's param or a `where` value beside
1950///     one), and one summed value per type is kept. Such an index keeps one `Element::SumItem` per group
1951///     in place of a value tree and entries: the index walkers (insert and
1952///     delete index level 2) move it by one per document, preallocation
1953///     creates it at zero, and document create state validation 1, document index-only delete
1954///     state validation 0, the within-batch collision tracker and the proof
1955///     index never use it. `rankedSummable` and `rankedAverageable` gain the
1956///     `{ "at": ... }` form on such an index only, stamped on the index
1957///     levels (`IndexLevel::ranked_sum_grouping`, `ranked_average_grouping`,
1958///     `sum_propagating`) and laid out by Drive as sum chains, count-and-sum
1959///     chains where an average ranking or `rangeCountable` adds counts
1960///     (`property_name_tree_type_and_ranked_axes_for_level`,
1961///     `ranked_chain_value_tree_type`); its `rankedCountable` is parsed into
1962///     that Sum ranking, since a document count there is its sums (no
1963///     `rangeCountable` needed). Sum, average and ranked queries name
1964///     the source index for the summed value, and a count query reads such
1965///     an index's sums, its document counts: a point read
1966///     (`document_count_of_element`), and a ranked or having-range read on
1967///     its Sum secondaries (`read_axis_for`), and a range read through the
1968///     sum surface's range forms (`counter_sums_query`). A range total
1969///     through any index whose path passes through a ranked level (its own,
1970///     or one another index ranks at a shared level) is refused cleanly
1971///     (`refuse_a_range_total_through_a_ranked_index`). Drive's batch methods,
1972///     `apply_drive_operations` and `convert_drive_operations_to_grove_operations`
1973///     at version 1, refuse a batch moving one document type's counters for
1974///     more than one document (`refuse_repeated_counter_moves`). Needs
1975///     grovedb's `GROVE_V4`, which admits a bare `SumItem` under a
1976///     `ProvableCountProvableSumIndexedTree`. Inert for every contract without
1977///     the keyword, which every earlier grammar refuses. For any index, the
1978///     range-total verifiers at version 1 (`DRIVE_VERIFY_METHOD_VERSIONS_V3`:
1979///     `verify_aggregate_count_proof`, `verify_carrier_aggregate_count_proof`,
1980///     `verify_aggregate_sum_proof`, `verify_carrier_aggregate_sum_proof`,
1981///     `verify_aggregate_count_and_sum_proof` and
1982///     `verify_carrier_aggregate_count_and_sum_proof`) verify a proof showing
1983///     the range holds nothing (an equality value no document holds, or an
1984///     empty tree of a kind the read does not aggregate), which grovedb's
1985///     aggregate verifiers refuse, as a zero total or no carrier branch
1986///     (`or_empty_range_total`), and the unproven range totals, keyed on the
1987///     same verifier versions, read an absent value as zero
1988///     (`aggregate_or_zero_when_absent`); and
1989///     `verify_composite_documents_proof` 1 reads the sum-bearing items of a
1990///     `documentsSummable` type as documents. Their
1991///     version 0, which every earlier protocol version selects, refuses both
1992///     proofs, and the unproven total fails, as released; the prover is
1993///     unchanged.
1994///
1995/// 74. **Withdrawals also fit a Core-anchored limit**: pooling
1996///     (`pool_withdrawals_into_transactions_queue` 2, which reuses version 1's
1997///     pooling through a shared helper) admits withdrawals up to the smaller of
1998///     the daily withdrawal limit (note 4) and
1999///     `calculate_core_anchored_withdrawal_limit`, a stricter copy of Core v24's
2000///     relative net unlock rule (dash#7712) read from Core's own credit pool
2001///     balances at chain locked heights: the pool may drop by at most
2002///     `core_credit_pool_unlock_limit_percent` (15; Core allows 20) of its
2003///     highest balance at a window start Core may use for the unlock (Core's
2004///     window, `core_credit_pool_window_blocks` 576 or
2005///     `regtest_core_credit_pool_window_blocks` 100, back from the chain locked
2006///     height, up to Core's asset unlock validity, `core_expiration_blocks` 48,
2007///     later), at least
2008///     `core_credit_pool_unlock_limit_floor` (1500 Dash; Core's floor is 2000),
2009///     less what is queued or broadcast and not completed yet. The formula is
2010///     `core_credit_pool_unlock_limit` 0 in `DPP_METHOD_VERSIONS_V3`. Before
2011///     pooling, `scan_core_blocks_for_withdrawals` reads the Core blocks the
2012///     chain locked height passed (at most `core_blocks_scanned_per_block_limit`,
2013///     32, per block) and records each one's credit pool balance, read from the
2014///     block's coinbase alone (`getspecialtxes`), under the withdrawals tree. The
2015///     Platform-side accounting can grant more than Core will mine (an asset lock published to
2016///     Platform after Core mined it, a whole epoch of Core rewards minted in one
2017///     block); over Core's limit an unlock waits unmined and is re-signed, and
2018///     while Core's mempool holds more than the limit Core InstantSend-locks no
2019///     withdrawal at all. The balance tree is created at genesis and by
2020///     `transition_to_version_14`, and `cleanup_expired_locks_of_withdrawal_amounts`
2021///     1 prunes it by Core height.
2022///
2023/// 75. **No reference by id to an indexOnly document type**: the contract
2024///     reference validation 0 (`validate_data_contract_references`), in place,
2025///     refuses a `permanentDocument`, `deletableDocument` or
2026///     `moderatedDocument` reference without `findBy` (or with `inList`) whose
2027///     referenced document type, in the declaring contract or another, is
2028///     indexOnly (`ReferencedDocumentTypeIndexOnlyError`, 40146, StateError
2029///     discriminant 171). Such a type's documents exist only as index entries,
2030///     and Drive refuses to fetch one by id, so every write resolving the
2031///     reference failed with an internal error, dropped unpaid. A `findBy`
2032///     into one keeps its own refusal (40137, or 10231 in the declaring
2033///     contract). Inert before this version: only parser generation 3 admits
2034///     an indexOnly document type.
2035///
2036/// 76. **Every revealed nullifier is recorded once**: each action of an
2037///     outputs-only Orchard bundle reveals a nullifier (that of a dummy spend,
2038///     which becomes the new note's `rho`). The spends already recorded and
2039///     checked theirs; now `Shield`, `ShieldFromAssetLock` and
2040///     `ShieldFromIdentity` do too. `transform_into_action` 1 of the shield and
2041///     the shield from asset lock (`DRIVE_ABCI_VALIDATION_VERSIONS_V10`), and
2042///     `transform_into_action` 0 of the shield from identity in place, refuse a
2043///     nullifier repeated inside the bundle or already recorded, with
2044///     `NullifierAlreadySpentError`: unpaid for the first two, as for the
2045///     spends, and a paid nonce bump for the identity-signed one. The
2046///     high-level operations of the shield and the shield from asset lock 1
2047///     (`DRIVE_STATE_TRANSITION_METHOD_VERSIONS_V4`), and of the shield from
2048///     identity 0 in place, record the nullifiers. Recording them is metered
2049///     storage for the shield and the shield from identity; the shield from
2050///     asset lock's flat pool fee already prices a note and a nullifier write
2051///     per action. The shield from identity's admission floor
2052///     (`compute_shielded_identity_balance_write_fee` 0, the client's estimate
2053///     of its complete fee) uses versioned allowances of 400 effective bytes
2054///     per action and 500 flat bytes, covering the complete execution-event
2055///     admission estimate. Actual fees remain metered. Nullifiers revealed by
2056///     shields before this version are not added.
2057///
2058/// 77. **Owner identities for shared and extended-address masternodes**: from
2059///     v24 on, Dash Core lists shared masternodes, which have no owner, payout
2060///     or collateral address, and extended-address masternodes, which have a
2061///     `payouts` list instead of a `payoutAddress`. `create_owner_identity` 1
2062///     needs both addresses and fails on such a masternode with
2063///     `DashCoreBadResponseError`, which fails the block. With
2064///     `create_owner_identity` 2 and `update_masternode_identities` 1
2065///     (`DRIVE_ABCI_METHOD_VERSIONS_V10`), a masternode without an owner
2066///     address gets no owner identity, only its voter and operator identities;
2067///     one with an owner address and either a legacy payout address or a sole
2068///     payout with a matching P2PKH script gets the version 1 identity,
2069///     TRANSFER key id 0 and OWNER key id 1, byte for byte; other payout shapes
2070///     get only OWNER key id 1. Legacy payout-address rotation is unchanged.
2071///     Payout-list changes retain, re-enable or add the sole supported P2PKH
2072///     TRANSFER key and disable obsolete TRANSFER keys. Split, empty or
2073///     unsupported lists disable all TRANSFER authority while preserving OWNER
2074///     and balance. Historical updaters keep their payout-list policy. This
2075///     version must be active on a network before its Dash Core activates V24,
2076///     since earlier versions keep failing on these masternodes.
2077///
2078/// 78. **Versioned Core masternode address resolution**: `update_masternode_list` 1
2079///     resolves nested platform addresses first, then falls back to legacy ports,
2080///     before storing the masternode state. Earlier protocol versions keep their
2081///     flat-field interpretation. The stored layout and validator construction
2082///     remain unchanged: new validators read the resolved stored ports, and an
2083///     existing validator is refreshed on a ban, service or P2P-port change.
2084///     Each diff starts from the old persisted representation so transient address
2085///     data retained before activation cannot make a running node disagree with
2086///     a restarted one. Payout lists remain outside the persisted representation.
2087///
2088/// 80. **A BLS12_381 signature must verify**: `verify_identity_signed_signature`
2089///     1 (`STATE_TRANSITION_METHOD_VERSIONS_V2`), the signature check that
2090///     identity-signature validation runs for every identity-signed
2091///     transition, refuses a signature by a BLS12_381 key that does not verify
2092///     (`InvalidStateTransitionSignatureError`, unpaid, as for ECDSA keys).
2093///     Generation 0 refused one only when the key or the signature could not be
2094///     read, and earlier versions replay through it. Identity-signature
2095///     validation v0, in place, passes the platform version to the check; the
2096///     tables of every earlier version select generation 0, the code it called
2097///     before.
2098///
2099/// 81. **Token shielded pools**: a token configuration in format version 1
2100///     (`TokenConfiguration::V1`, admitted by `CONTRACT_VERSIONS_V6`'s
2101///     `token_configuration_format` bounds) can set `hasShieldedPool`, which
2102///     gives the token its own Orchard pool under
2103///     `[Tokens, TOKEN_SHIELDED_POOLS_KEY, token_id]` laid out like the credit
2104///     pool. A pooled token must leave its freeze, unfreeze and destroy-frozen-
2105///     funds rules unassigned, since notes have no owner to freeze. Seven batch
2106///     token transitions (`TokenShield`, `TokenUnshield`,
2107///     `TokenShieldedTransfer`, `TokenMintToPool`, `TokenBurnFromPool`,
2108///     `TokenClaimToPool` and `TokenDirectPurchaseToPool`, validated through
2109///     `DRIVE_ABCI_VALIDATION_VERSIONS_V10` and gated by
2110///     `TOKEN_SHIELDED_POOL_INITIAL_PROTOCOL_VERSION`) move tokens between an
2111///     identity balance, the supply and the pool or inside it; the identity
2112///     signs and pays the fee in credits, and every bundle binds its pool into
2113///     the Orchard sighash, since all pools share the empty-tree anchor an
2114///     unbound bundle would verify against: a spend bundle binds the token id
2115///     and the batch owner (a burn binds the burner: the batch owner, or the
2116///     proposer of a group action), plus the recipient and amount where tokens
2117///     leave the pool; an outputs-only bundle (`TokenShield`,
2118///     `TokenMintToPool`, `TokenClaimToPool`, `TokenDirectPurchaseToPool`),
2119///     whose anchor is never checked against a pool, binds a per-kind tag,
2120///     the token id and the batch owner (for a group action mint, the
2121///     proposer).
2122///     A batch carrying any of these bundles, or a document whose token cost
2123///     is paid out of a pool, has to hold the compute fee the bundles will be
2124///     charged (`compute_shielded_verification_fee` per bundle-carrying
2125///     sub-transition): the batch minimum balance pre-check v1
2126///     (`identity_minimum_balance_pre_check`) reserves it on top of the flat
2127///     per-sub-transition minimum, which is orders of magnitude smaller. A
2128///     batch without a bundle is asked for the flat minimum, unchanged, and
2129///     one that asks the contract owner to pay its gas is asked for its
2130///     principal alone as in item 11, the compute fee being gas. The floor
2131///     refuses only what fee validation would refuse later, but it refuses it
2132///     before the Halo 2 work: `TokenClaimToPool`'s proof is skipped in check
2133///     tx, since its claimable amount is only known against state, so without
2134///     the floor a signer between the two numbers cleared the mempool with no
2135///     verification run and every validator then did the verification inside
2136///     block validation, only to refuse the batch unpaid, leaving the same
2137///     bytes replayable. The same holds for the bundle of a group action's
2138///     non-proposing signer, whose proof check tx also skips.
2139///     The pool balances are a term of the token conservation check
2140///     (`calculate_total_tokens_balance` v1 in `DRIVE_TOKEN_METHOD_VERSIONS_V2`).
2141///     `record_token_shielded_pool_anchors`
2142///     (`DRIVE_ABCI_METHOD_VERSIONS_V10`) records and prunes the anchors of the
2143///     pools a block touched. The pools root tree is inserted by
2144///     `transition_to_version_14` and by `create_initial_state_structure` v4;
2145///     the six shielded queries accept an optional `token_id` to target a token
2146///     pool.
2147///
2148/// 82. **An expiring type sums only values that keep its sums in range**:
2149///     parser generation 3, in place, refuses under full validation a document
2150///     type with a `ttl` and a summed property (`summable`, `averageable`,
2151///     `documentsSummable` or `documentsAverageable`) unless the property's
2152///     schema declares a `minimum` of at least 0, or a `minimum` of at least
2153///     -2^27 and a `maximum` of at most 2^27
2154///     (`SYSTEM_LIMITS_V4.max_expiring_signed_summed_value_magnitude`, `None` in
2155///     the earlier tables). The cleanup deletes expired documents at the end of
2156///     a block with no transition to refuse, and removing a negative value
2157///     raises the sums it was in; removing values that are never negative only
2158///     lowers them, and values within ±2^27 keep them in `i64` short of 2^36
2159///     documents. A stored contract still parses. No earlier version parses
2160///     `ttl`.
2161///
2162/// 83. **Unambiguous document properties**: `validate_document` generation 1 rejects
2163///     repeated text keys in nested maps, including maps inside arrays, before property
2164///     size, path and schema validation. Create, replace, indexOnly delete and moderator
2165///     field changes use the same check. Rejection uses ValueError (10103) through the
2166///     existing paid-invalid flow; generation 0 remains selected before this version.
2167///
2168/// 84. **Document token payments obey the issuer's movement policy**:
2169///     document-base state validation 2 supersedes 1
2170///     (`DRIVE_ABCI_VALIDATION_VERSIONS_V10`). A transparent transfer or burn
2171///     payment of a paused token is refused (`TokenIsPausedError`, 40711).
2172///     A transfer to a frozen document contract owner is refused
2173///     (`IdentityTokenAccountFrozenError`, 40702, naming that owner) only when
2174///     the token issuer's `allowTransferToFrozenBalance` is false; its default
2175///     is true, and external issuers are included. Reads are billed in order:
2176///     payer freeze, payer balance, pause, recipient info, then issuer metadata
2177///     only if frozen, and the issuer contract only when external. Owner
2178///     self-payments emit no transfer and retain only their payer checks.
2179///     Shielded payments retain their separate pool validation; native burn
2180///     policy and earlier protocol tables are unchanged.
2181///
2182/// 85. **Rules that gate the owner's delete (`deleteConstraints`), and `$id`
2183///     in a total's filter**: a document type of meta-schema v3 and parser
2184///     generation 3, in place, may declare `deleteConstraints`, named rules in
2185///     the `propertyConstraints` grammar (`parse_delete_constraints`,
2186///     `apply_delete_constraints_v0`, run by `apply_property_constraints` 0
2187///     on `parse_property_constraints`; `DocumentTypeV2Getters::delete_constraints`).
2188///     Document delete state validation 1 (`DRIVE_ABCI_VALIDATION_VERSIONS_V10`)
2189///     runs the checks of version 0, then reads the totals the rules read
2190///     (`read_delete_constraint_aggregates`, billed, each as it will be once
2191///     the document is gone) and judges every rule on the stored document
2192///     (`validate_delete_constraints`, versioned with
2193///     `validate_property_constraints`), refusing the first one broken with
2194///     `DocumentDeleteConstraintViolatedError` (state code 40147, discriminant
2195///     172), paid. A `countOf` or `sumOf` filter may match by `"$id"`, the
2196///     document's own id (`AggregateBinding::Id`, an identifier key, in
2197///     `propertyConstraints` too: the shared batch transformer's aggregate read,
2198///     in place, passes the document's id), so a poll is deleted only while no
2199///     vote names it. Refused on a type with `canBeDeleted: false`,
2200///     `"onlyWhenConsumed"` or `indexOnly` (10231, every parse), held to the rule
2201///     limits apart from `propertyConstraints`, frozen on update (10246), and a
2202///     `refersTo` with `consume` may not target such a type
2203///     (`DocumentReferenceLookup::referenced_side_error`). Moderator deletes and
2204///     `ttl` expiries are not judged. Inert before this version: the earlier
2205///     meta-schemas refuse the keyword and the `$id` filter value, and their
2206///     tables select delete state validation 0.
2207///
2208/// 86. **Bounded schema depth check**: `validate_max_depth` 1
2209///     (`CONTRACT_VERSIONS_V6`) no longer walks a `$ref` whose target is a
2210///     scalar and never clears its visited set, so every ref target is
2211///     expanded at most once and the check is bounded in schema size. Before
2212///     this a crafted `$defs` chain with `$ref`s to scalars cleared the cycle
2213///     guard and made the check, run during contract registration in block
2214///     execution, exponential. Verdict, depth and size are unchanged for
2215///     schemas without a scalar `$ref` target; earlier versions replay
2216///     through generation 0.
2217///
2218/// 87. **Moderator document restores obey `propertyConstraints`**: moderation state
2219///     validation 0, in place, judges every rule of the restored type after uniqueness
2220///     and before constructing restoration operations. The retained document supplies
2221///     its original id, owner, properties, times and heights. The shared aggregate reader
2222///     adds it to the live totals as an insertion, with no contribution from its removal
2223///     record. A failing rule returns paid `DocumentPropertyConstraintViolatedError`
2224///     (10422) in a block, charging the moderator and consuming its nonce while leaving
2225///     the document absent and the removal record unrestored. Mempool admission refuses
2226///     it without persisting fees or a nonce change. Types without rules retain their fees.
2227///     Full property schema validation and `deleteConstraints` are not added to restore.
2228///     Earlier versions are unchanged: contract moderation is inactive before version 14.
2229///
2230/// The app-connect system contract (`SystemDataContract::AppConnect`, schema v1)
2231/// carries only the wallet's `loginKeyResponse`: a flat indexOnly entry keyed by
2232/// the app's ephemeral key hash and the responding identity, with the wallet's
2233/// ephemeral key and encrypted grant in `entryPayload`. Genesis registers it on
2234/// chains born at this version; `transition_to_version_14` inserts it on upgrade.
2235/// The Drive and trusted SDK caches serve it only from protocol version 14.
2236///
2237///
2238/// * `ShieldFromIdentity` (state transition type 21) activates:
2239///   `SHIELD_FROM_IDENTITY_INITIAL_PROTOCOL_VERSION = 14` gates it in
2240///   `is_allowed`, and `DRIVE_ABCI_VALIDATION_VERSIONS_V10` is the first
2241///   table whose `shield_from_identity_state_transition` row enables basic
2242///   structure, identity signature, and nonce validation. It moves credits
2243///   from an identity balance straight into the shielded pool: the funding
2244///   side is identity-signed like `IdentityCreditTransferToAddresses`, the
2245///   pool side is an outputs-only Orchard bundle like `Shield`, bound to the
2246///   funding identity (next item), and the fee is metered plus the shielded
2247///   compute fee, paid from the identity.
2248///
2249/// * The credit pool's outputs-only bundles bind `kind tag || owner` into their
2250///   Orchard sighash (`DPP_METHOD_VERSIONS_V3` sets `credit_pool_bundle_binding`
2251///   to `Some(0)`): `Shield` (`0x84`) the SHA-256 of its input addresses,
2252///   checked by `validate_shielded_proof` v1; `ShieldFromIdentity` (`0x85`) its
2253///   identity id; `ShieldFromAssetLock` (`0x86`) its asset lock identifier,
2254///   checked by the `transform_into_action` v1 that
2255///   `DRIVE_ABCI_VALIDATION_VERSIONS_V10` selects. A third party can no longer
2256///   wrap a proved bundle in a transition of their own. v13 keeps both checks
2257///   unbound. A sender rebuilding the same notes (Faerie Gold) is not stopped:
2258///   that needs the bundles' dummy nullifiers recorded and checked.
2259///   `ShieldFromAssetLock` also gains transition version 1
2260///   (`STATE_TRANSITION_SERIALIZATION_VERSIONS_V3`), the only version 14
2261///   admits: version 0 is refused at decode by `active_version_range`, before
2262///   any proof work, uncharged and with its asset lock left unspent, so one
2263///   still waiting when 14 activates is not burned by the bound check.
2264///
2265/// * `IdentityTopUpFromShieldedPool` (state transition type 22) activates at the
2266///   same gate (`IDENTITY_TOP_UP_FROM_SHIELDED_POOL_INITIAL_PROTOCOL_VERSION = 14`,
2267///   `DRIVE_ABCI_VALIDATION_VERSIONS_V10` row). It spends shielded notes like
2268///   `Unshield` and credits an EXISTING identity's balance instead of a platform
2269///   address: pool-paid flat fee (`compute_shielded_identity_top_up_fee`), no
2270///   platform signature, the target identity and gross amount bound into the
2271///   Orchard sighash, and no system-credit adjustment (pool and identity balances
2272///   are both conservation-equation terms).
2273///
2274/// The wire surface changes only additively: `GetDocumentsRequestV1`
2275/// already carries `selects` / `group_by` / `order_by` / `limit` /
2276/// `offset`; the ranked response is an additive `ResultData.ranked`
2277/// variant, whose `skipped` field is likewise additive; and the v1
2278/// where-clause operator enum gains `IN_TIME_RANGE = 11` and
2279/// `IN_INTEGER_RANGE = 12`, which pre-v14 servers reject as unknown
2280/// operators rather than misread (the v0 wire has neither).
2281/// Contract-bound authentication keys activate through contract-bounds validation v2,
2282/// identity-signature validation v1 and batch advanced-structure v1. Identity creation
2283/// validates key bounds (state v1) and identity-update state v1 retains the contract
2284/// lookup fees; Drive identity methods v2 index and refresh the bound keys. The same v1
2285/// contract-info methods also store the current-key alias of a contract-level encryption or
2286/// decryption key bound under `MultipleReferenceToLatest` in its purpose subtree, where the
2287/// current-key query reads it; v0 wrote it one level up, where its sibling reference could
2288/// not resolve, so registering such a key failed inside Drive on every earlier version.
2289/// Contract group bounds on authentication keys ride the same versions: contract-bounds
2290/// validation v2 admits them, batch transform v2 resolves the member contract's group
2291/// memberships into the action (only for a group-bound signing key) for advanced-structure v1 to judge, and shielded-proof validation v1 refuses them in identity creation from the
2292/// shielded pool, whose sighash preimage layout predates them.
2293/// A transition carrying such a key is inactive before this version (`active_version_range`),
2294/// so earlier protocol versions reject it without charging, as a binary that cannot decode it does.
2295/// Authentication keys may carry a budget and an expiry (the version 1 public key format, which
2296/// `StateTransition::active_version_range` admits from 14). Key structure validation v1
2297/// (`STATE_TRANSITION_METHOD_VERSIONS_V2`) and `validate_identity_public_keys_limits` decide
2298/// which keys may carry them; Drive identity methods v2 write the remaining budget when the key
2299/// is added; identity-signature validation v1 refuses a key whose budget is spent;
2300/// `validate_fees_of_event` v1 refuses an expired key and a spend the remaining budget does not
2301/// cover (only metered processing may overshoot); `execute_event` v1 deducts what was spent.
2302/// Shielded-proof validation v1 refuses a key that carries a budget or an expiry in identity
2303/// creation from the shielded pool, whose sighash preimage does not cover the limits.
2304/// `IdentityKeyLimitsUpdate` (state transition type 23, gated by
2305/// `IDENTITY_KEY_LIMITS_UPDATE_INITIAL_PROTOCOL_VERSION`) raises a key's total budget, and the
2306/// remaining budget with it, or moves its expiry later; it only ever loosens limits. Signed by a
2307/// MASTER key or by a CRITICAL key without limits (`DRIVE_ABCI_VALIDATION_VERSIONS_V10` turns
2308/// its gates on; Drive identity methods v2 rewrite the key and raise the remaining budget).
2309pub const PLATFORM_V14: PlatformVersion = PlatformVersion {
2310    protocol_version: PROTOCOL_VERSION_14,
2311    drive: DRIVE_VERSION_V9, // changed: drive document method versions v4 — v2 index walkers (shared-prefix aggregate indexes become insertable) + the detect_ranked_mode slot; contract method versions v4: the moderation list trees, the document removal record trees and the moderation method table; apply_drive_operations 1 (a moderator's document deletion refunds nobody for what its document operations remove unless its type sets `deleteRefundsOwner`, those operations applied as a GroveDB batch of their own when the batch also frees moderation storage someone is owed, a restored removal record replaced or team action approvals moved or dropped, which is refunded to whoever its flags name; every write of one identity balance, fee pot or prefunded specialized balance in a batch merged into one; a batch writing one token balance or supply twice refused; a batch moving one document type's summableOffCountIndex counters for more than one document refused; repaid identity debt credited to the processing fee pool); convert_drive_operations_to_grove_operations 1 (refuses that counter batch too, then converts as before); index uniqueness gains validate_moderated_document_uniqueness (a moderator's document restore or field change); vote method versions v3: the end-date cleanup of ended contested vote polls removes an end date only once none of its polls remain; token method versions v2: calculate_total_tokens_balance 1 (token shielded pool balances join token conservation) and evonode_participation_rewards 1 (an evonode's token claim covers only the epochs it read); add_contested_indices_for_contract_operations 1: a poll's last index value is a count tree
2312    drive_abci: DriveAbciVersion {
2313        structs: DRIVE_ABCI_STRUCTURE_VERSIONS_V2, // changed: saved platform state structure 1 keeps masternodes and validator sets as one aux entry each
2314        methods: DRIVE_ABCI_METHOD_VERSIONS_V10, // changed: records the per-block total credits history for the daily withdrawal limit; record_token_shielded_pool_anchors records and prunes the anchors of the token pools a block touched; decode_raw_state_transitions, execute_event, validate_fees_of_event and add_distribute_storage_fee_to_epochs_operations each move to 1 — the table's own per-slot comments carry the full list
2315        validation_and_processing: DRIVE_ABCI_VALIDATION_VERSIONS_V10, // changed: contested-index cross-check + refersTo document reference validation; the ContractUserModeration gates and the batch transformer's contract_moderation_gate; a contest accepts at most max_contenders_per_contest contenders and maximum_contenders_to_consider rises to 10,000; a contender's fund doubles past 250 contenders and for every 50 more; the three shielded-fee token pool transitions gain basic structure validation and document_base_transition_state_validation 2 admits a document token cost paid from a token pool and enforces pause and the issuer's frozen-recipient policy on transparent payments; the ShieldFromAssetLock transform_into_action 1 checks its bundle against the bound preimage
2316        withdrawal_constants: DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V3, // changed: prune bound for the total credits history
2317        query: DRIVE_ABCI_QUERY_VERSIONS_V2, // changed: ranked + boolean-HAVING routing gate; the v1 handler also resolves IN_TIME_RANGE from committed block time
2318        checkpoints: DRIVE_ABCI_CHECKPOINT_PARAMETERS_V1,
2319    },
2320    dpp: DPPVersion {
2321        costs: DPP_COSTS_VERSIONS_V1,
2322        validation: DPP_VALIDATION_VERSIONS_V5, // changed: validate_config_update 2 admits the contract moderation declaration of config V2
2323        state_transition_serialization_versions: STATE_TRANSITION_SERIALIZATION_VERSIONS_V3, // changed: the indexOnly delete-by-values kind (documentIndexOnlyDelete) joins the wire; ShieldFromAssetLock moves to version 1 alone; the ContractUserModeration transition
2324        state_transition_conversion_versions: STATE_TRANSITION_CONVERSION_VERSIONS_V2,
2325        state_transition_method_versions: STATE_TRANSITION_METHOD_VERSIONS_V2, // changed: public keys in creation may carry a budget or an expiry; verify_identity_signed_signature 1: a BLS12_381 signature must verify
2326        state_transitions: STATE_TRANSITION_VERSIONS_V4,
2327        contract_versions: CONTRACT_VERSIONS_V6, // changed: token_configuration_format max_version 1 admits the shielded pool opt-in; v3 document meta-schema hosts the ranked, refersTo, requiredSince and timeRange keywords; validate_structure_interval v1 rejects a zero epoch interval; config max_version 2 (the contract moderation declaration) and validate_moderation_config; validate_document 1 rejects repeated nested text keys
2328        document_versions: DOCUMENT_VERSIONS_V4, // changed: document serialization format 3 — the contract version stamp that enables `requiredSince` properties
2329        identity_versions: IDENTITY_VERSIONS_V1,
2330        voting_versions: VOTING_VERSION_V2,
2331        token_versions: TOKEN_VERSIONS_V3, // changed: distribution_function_evaluate v1 — deterministic libm for token reward math; reward_distribution_max_cycle_moment v1: the epoch claim cap no longer wraps; distribution_function_cycle_epochs v1: evonode cycles weighted by the epochs they span
2332        asset_lock_versions: DPP_ASSET_LOCK_VERSIONS_V1,
2333        methods: DPP_METHOD_VERSIONS_V3, // changed: daily_withdrawal_limit v2 — a percentage of the total credits a day ago; credit_pool_bundle_binding Some(0) — the credit pool's outputs-only bundles bind a kind tag and their owner
2334        factory_versions: DPP_FACTORY_VERSIONS_V1,
2335    },
2336    system_data_contracts: SYSTEM_DATA_CONTRACT_VERSIONS_V3, // changed: DashPay v2 adds profile payment address fields (DIP-33); withdrawals v2 admits the terminal FAILED status
2337    // The TTL ephemeral-bytes rate (270 credits/byte to processing) rides
2338    // the shared storage table; it is dead below v14 (the `ttl` grammar
2339    // does not parse), so no table fork is needed.
2340    fee_version: FEE_VERSION3, // changed: contested document contribution reduced to 0.1 DASH; masternode vote cost reduced to 0.00002 DASH; moderation election fund of 0.5 DASH; a contender's fund doubles past 250 contenders and for every 50 more; registration surcharge for once-per-identity token distributions
2341    system_limits: SYSTEM_LIMITS_V4, // changed: daily withdrawal limit becomes 15% of the total credits a day ago + time-range overlap-factor cap (24) + time-range TTL cap (1 week) and per-write drop cap (32); max_contract_moderators, max_contract_suspension_until, max_contract_moderation_reason_length, max_contract_warnings_per_identity, max_contract_moderation_reason_documents and contract_document_restore_window_ms (a week); max_contenders_per_contest (1,000)
2342    consensus: ConsensusVersions {
2343        tenderdash_consensus_version: 1,
2344    },
2345};
2346
2347#[cfg(test)]
2348mod tests {
2349    use super::*;
2350    use crate::version::v13::PLATFORM_V13;
2351
2352    #[test]
2353    fn should_change_only_the_contested_document_and_once_per_identity_fees_at_protocol_14() {
2354        for protocol_version in 1..14 {
2355            let version = PlatformVersion::get(protocol_version).expect("known protocol version");
2356            let fund_fees = &version.fee_version.vote_resolution_fund_fees;
2357            assert_eq!(
2358                fund_fees.contested_document_vote_resolution_fund_required_amount, 20_000_000_000,
2359                "protocol {protocol_version} must preserve the 0.2 DASH contribution"
2360            );
2361            assert_eq!(
2362                version
2363                    .fee_version
2364                    .vote_resolution_fund_fees
2365                    .contested_document_single_vote_cost,
2366                10_000_000,
2367                "protocol {protocol_version} must preserve the 0.0001 DASH vote"
2368            );
2369            // No moderation election exists before 14; its amount is the contested one, so
2370            // a shipped path choosing between the two cannot change what it charges
2371            assert_eq!(
2372                fund_fees.moderation_vote_resolution_fund_required_amount,
2373                fund_fees.contested_document_vote_resolution_fund_required_amount,
2374                "protocol {protocol_version}"
2375            );
2376            assert_eq!(
2377                (
2378                    fund_fees.contested_document_contenders_before_fund_doubling,
2379                    fund_fees.contested_document_contenders_per_fund_doubling
2380                ),
2381                (0, 0),
2382                "protocol {protocol_version}: every contender paid the same fund"
2383            );
2384        }
2385
2386        let mut expected_fees = PLATFORM_V13.fee_version.clone();
2387        expected_fees
2388            .vote_resolution_fund_fees
2389            .contested_document_vote_resolution_fund_required_amount = 10_000_000_000;
2390        // A masternode vote costs a fifth of what it did: 0.00002 DASH from the contest's fund
2391        expected_fees
2392            .vote_resolution_fund_fees
2393            .contested_document_single_vote_cost = 2_000_000;
2394        // An application in a moderation election prefunds its masternode votes with 0.5 DASH
2395        expected_fees
2396            .vote_resolution_fund_fees
2397            .moderation_vote_resolution_fund_required_amount = 50_000_000_000;
2398        // The fund a contender pays doubles once the contest holds 250 contenders, and again for
2399        // every 50 more
2400        expected_fees
2401            .vote_resolution_fund_fees
2402            .contested_document_contenders_before_fund_doubling = 250;
2403        expected_fees
2404            .vote_resolution_fund_fees
2405            .contested_document_contenders_per_fund_doubling = 50;
2406        // The once-per-identity token distribution exists from protocol version 14 on, and a
2407        // token that uses it pays the surcharge of the other distribution kinds.
2408        assert_eq!(
2409            expected_fees
2410                .data_contract_registration
2411                .token_uses_once_per_identity_distribution_fee,
2412            0
2413        );
2414        expected_fees
2415            .data_contract_registration
2416            .token_uses_once_per_identity_distribution_fee = 10_000_000_000;
2417        assert_eq!(PLATFORM_V14.fee_version, expected_fees);
2418    }
2419
2420    /// The ranked / boolean-HAVING routing gate lives in v14's own query
2421    /// table, so flipping it touches only v14: a v13 node keeps running
2422    /// the v0 helper, which rejects every non-empty HAVING, so a
2423    /// mixed-version network agrees until the upgrade vote carries.
2424    ///
2425    /// v14 selects the v2 helper, which routes the ranked shape
2426    /// (`ORDER BY <agg> LIMIT n`) to `dispatch_ranked_v1` and the
2427    /// boolean-HAVING range shape (exactly one `having` clause on the
2428    /// selected aggregate) to `dispatch_having_v1`. A change that made
2429    /// v13 non-zero here would be consensus-breaking for
2430    /// already-deployed nodes, which is exactly what the v13 half of
2431    /// this assertion guards.
2432    #[test]
2433    fn ranked_having_routing_gate_is_v14_only() {
2434        assert_eq!(
2435            PLATFORM_V13
2436                .drive_abci
2437                .query
2438                .document_query_helpers
2439                .compute_aggregate_mode_and_check_limit,
2440            0
2441        );
2442        assert_eq!(
2443            PLATFORM_V14
2444                .drive_abci
2445                .query
2446                .document_query_helpers
2447                .compute_aggregate_mode_and_check_limit,
2448            2
2449        );
2450    }
2451
2452    /// Contested indexes without a Lock choice (item 23): the three method
2453    /// versions that read the resolution are selected by v14 only, so a v13
2454    /// replay keeps the shipped rules (a full poll for every contest, ties to
2455    /// the latest contender, a Lock vote accepted on any contest).
2456    #[test]
2457    fn no_locking_contests_are_selected_by_v14_only() {
2458        assert_eq!(
2459            PLATFORM_V13
2460                .drive_abci
2461                .methods
2462                .voting
2463                .check_for_ended_vote_polls,
2464            0
2465        );
2466        assert_eq!(
2467            PLATFORM_V14
2468                .drive_abci
2469                .methods
2470                .voting
2471                .check_for_ended_vote_polls,
2472            1
2473        );
2474        assert_eq!(
2475            PLATFORM_V13
2476                .drive_abci
2477                .validation_and_processing
2478                .state_transitions
2479                .masternode_vote_state_transition
2480                .state,
2481            0
2482        );
2483        assert_eq!(
2484            PLATFORM_V14
2485                .drive_abci
2486                .validation_and_processing
2487                .state_transitions
2488                .masternode_vote_state_transition
2489                .state,
2490            1
2491        );
2492        assert_eq!(
2493            PLATFORM_V13
2494                .drive
2495                .methods
2496                .document
2497                .insert_contested
2498                .add_contested_document_for_contract_operations,
2499            0
2500        );
2501        assert_eq!(
2502            PLATFORM_V14
2503                .drive
2504                .methods
2505                .document
2506                .insert_contested
2507                .add_contested_document_for_contract_operations,
2508            1
2509        );
2510    }
2511
2512    /// The ranked index keywords are gated by the meta-schema version, so v14
2513    /// must select meta-schema v3 while v13 stays on v2.
2514    #[test]
2515    fn ranked_index_keywords_are_gated_by_meta_schema_v3() {
2516        assert_eq!(
2517            PLATFORM_V13
2518                .dpp
2519                .contract_versions
2520                .document_type_versions
2521                .schema
2522                .document_type_schema,
2523            2
2524        );
2525        assert_eq!(
2526            PLATFORM_V14
2527                .dpp
2528                .contract_versions
2529                .document_type_versions
2530                .schema
2531                .document_type_schema,
2532            3
2533        );
2534    }
2535
2536    /// The ranked grammar lives in its own document-type parser generation
2537    /// rather than behind a version gate inside a shipped one, so v14 must
2538    /// select generation 3 while v13 stays on generation 2. Pinned here
2539    /// because it is the whole reason generations 0/1/2 can stay byte-identical
2540    /// to what consensus already ran: a historical block replayed at v13 is
2541    /// parsed by a generation that has never heard of the ranked keywords.
2542    /// The grove v4 cleanup gates (batch overwrite inspection + delete-tree
2543    /// actual-type cleanup) exist for the indexed trees that ranked indexes
2544    /// lay down, so v14 must select grove protocol 4 while v13 stays on 3.
2545    /// The gates are cost-neutral — they derive the old element from data the
2546    /// merk apply already loads — and the fee-constant tests pin identical
2547    /// fees on both sides of the boundary. Platform flows cannot themselves
2548    /// overwrite a ranked index (the flags are immutable on contract update
2549    /// and new indexes cannot be added to an existing document type), so the
2550    /// cleanup behavior itself is exercised by grovedb's own overwrite suites
2551    /// at the pinned revision; this test pins that v14 actually activates
2552    /// them.
2553    #[test]
2554    fn grove_v4_cleanup_gates_activate_at_v14() {
2555        assert_eq!(PLATFORM_V13.drive.grove_version.protocol_version, 3);
2556        assert_eq!(PLATFORM_V14.drive.grove_version.protocol_version, 4);
2557    }
2558
2559    #[test]
2560    fn ranked_grammar_gets_its_own_parser_generation() {
2561        assert_eq!(
2562            PLATFORM_V13
2563                .dpp
2564                .contract_versions
2565                .document_type_versions
2566                .class_method_versions
2567                .try_from_schema,
2568            2
2569        );
2570        assert_eq!(
2571            PLATFORM_V14
2572                .dpp
2573                .contract_versions
2574                .document_type_versions
2575                .class_method_versions
2576                .try_from_schema,
2577            3
2578        );
2579    }
2580
2581    /// The contested vote poll index cross-check changes accept/reject
2582    /// behavior for document create transitions, so it lives in v14's own
2583    /// validation table: a v13 node keeps running structure validation v0,
2584    /// which validates only the prefunded amount and ignores the index name.
2585    /// A change that made v13 non-zero here would retroactively reject
2586    /// transitions already in the chain.
2587    #[test]
2588    fn contested_index_cross_check_is_v14_only() {
2589        assert_eq!(
2590            PLATFORM_V13
2591                .drive_abci
2592                .validation_and_processing
2593                .state_transitions
2594                .batch_state_transition
2595                .document_create_transition_structure_validation,
2596            0
2597        );
2598        assert_eq!(
2599            PLATFORM_V14
2600                .drive_abci
2601                .validation_and_processing
2602                .state_transitions
2603                .batch_state_transition
2604                .document_create_transition_structure_validation,
2605            1
2606        );
2607        assert_eq!(
2608            PLATFORM_V13
2609                .drive_abci
2610                .validation_and_processing
2611                .state_transitions
2612                .batch_state_transition
2613                .document_create_transition_state_validation,
2614            1
2615        );
2616        assert_eq!(
2617            PLATFORM_V14
2618                .drive_abci
2619                .validation_and_processing
2620                .state_transitions
2621                .batch_state_transition
2622                .document_create_transition_state_validation,
2623            2
2624        );
2625        assert_eq!(
2626            PLATFORM_V13
2627                .drive
2628                .methods
2629                .document
2630                .insert_contested
2631                .add_contested_vote_subtree_for_non_identities_operations,
2632            0
2633        );
2634        assert_eq!(
2635            PLATFORM_V14
2636                .drive
2637                .methods
2638                .document
2639                .insert_contested
2640                .add_contested_vote_subtree_for_non_identities_operations,
2641            1
2642        );
2643    }
2644}