Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5c047d2
feat(dpp)!: typed scalar arrays in document schemas (PV14) (#4922)
QuantumExplorer Sep 22, 2026
f2c39aa
feat(dpp)!: encode typed array elements as their scalar property type…
QuantumExplorer Sep 22, 2026
1d6497e
feat(platform)!: a document batch proof carries the owner's credit ba…
QuantumExplorer Sep 22, 2026
a358ff7
fix(dpp)!: contract updates may not change an integer property's widt…
QuantumExplorer Sep 22, 2026
afa67e6
feat(platform)!: distinctFrom on identifier properties (PV14) (#4917)
QuantumExplorer Sep 22, 2026
97dd34b
feat(platform)!: encryptedFor envelope declaration on byte properties…
QuantumExplorer Sep 22, 2026
328605f
fix(dpp)!: typed array review fixes: hyphenated list paths, element c…
QuantumExplorer Sep 22, 2026
107534d
feat(platform)!: keyRequirements on identity key references (PV14) (#…
QuantumExplorer Sep 23, 2026
ba7b075
feat(sdk): typed arrays in the Swift SDK and both mobile example apps…
QuantumExplorer Sep 23, 2026
f02c9b3
feat(platform)!: key references on the writer's own identity (PV14) (…
QuantumExplorer Sep 23, 2026
4527e5f
fix(rs-sdk-ffi)!: sanitize properties in dash_sdk_document_set_proper…
QuantumExplorer Sep 23, 2026
e4d4b7c
feat(platform)!: refersTo on typed array elements (PV14) (#4928)
QuantumExplorer Sep 23, 2026
db5cc32
feat(platform)!: yes/no masternode vote poll kind with supermajority …
QuantumExplorer Sep 23, 2026
d6c4092
fix(dpp): a resource vote naming a yes/no poll is inactive before pro…
QuantumExplorer Sep 23, 2026
14f9fb0
feat(dpp): exact and untagged untrusted state transition decode via t…
QuantumExplorer Sep 23, 2026
af350a9
feat(platform)!: document references resolved through a unique index …
QuantumExplorer Sep 23, 2026
44545be
Merge v4.2-dev (#4930, #4931) into the yes/no vote poll branch
QuantumExplorer Sep 23, 2026
d7f783e
fix(platform): yes/no poll review fixes: end-date clean-up, proof rou…
QuantumExplorer Sep 23, 2026
b8fb0a1
feat(platform)!: a yes/no poll's minimum is a fixed voting power or a…
QuantumExplorer Sep 23, 2026
49df27f
feat(dpp): round a yes/no poll's share minimum down plus one or up mi…
QuantumExplorer Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Platform uses data contracts to define application data schemas:
Use LF, UTF‑8, and a final newline.
- JS/TS: ESLint (Airbnb/TypeScript rules via package configs). Use camelCase for variables/functions, PascalCase for classes; prefer kebab-case filenames within JS packages.
- Rust: Follow rustfmt defaults; keep code clippy-clean. Modules `snake_case`, types `PascalCase`, constants `SCREAMING_SNAKE_CASE`.
- Rust architecture rules live in The Dash Platform Book (`book/`). Read [book/src/contributing/coding-conventions.md](book/src/contributing/coding-conventions.md) before changing versioned behaviour, validation, errors, fees, or limits; it states each rule, why it exists, and links to the chapter with the mechanics. Key rules: shipped `vN` modules are frozen and new behaviour is a new `vN` selected only by the unreleased protocol version's tables; numbers go in `SystemLimits`, fees in named `FEE_VERSION*` schedules; `platform_version` is the last parameter; no `unwrap`/`expect` on block-execution paths (a panic halts the chain); imports at the top, no inline `crate::` paths; latest-generation tests use `PlatformVersion::latest()`.
- Rust architecture rules live in The Dash Platform Book (`book/`). Read [book/src/contributing/coding-conventions.md](book/src/contributing/coding-conventions.md) before changing versioned behaviour, validation, errors, fees, or limits; it states each rule, why it exists, and links to the chapter with the mechanics. Key rules: new behaviour is a new `vN` selected only by the unreleased protocol version's tables, and a shipped `vN` is edited in place only when the edit provably cannot modify consensus there, with the reason in the PR description's "In-place changes to shipped generations" section; numbers go in `SystemLimits`, fees in named `FEE_VERSION*` schedules; `platform_version` is the last parameter; no `unwrap`/`expect` on block-execution paths (a panic halts the chain); imports at the top, no inline `crate::` paths; latest-generation tests use `PlatformVersion::latest()`.

## Testing Guidelines
- Unit/integration tests live alongside each package (e.g., `packages/<name>/tests`). E2E lives in `packages/platform-test-suite`.
Expand Down
58 changes: 41 additions & 17 deletions book/src/contributing/coding-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,38 @@ The mechanics of `PlatformVersion`, feature version tables, and the dispatcher
shape are covered in the [Versioning](../versioning/platform-version.md)
chapters. The rules here are about what to do with those mechanics.

### Shipped generations are frozen
### Shipped generations are frozen unless the change cannot modify consensus

A behaviour change to a versioned method means a new `vN` module selected only
by the tables of the unreleased protocol version. It never means editing a
shipped `vN`, and that includes "harmless" edits: threading a new parameter
through it, adding a version-table check inside it, or computing a gate that is
always false for old versions. Inside the new generation the capability is a
constant fact (`Index::try_from_value_map(map, true)`), not a runtime check.

Why: replay safety becomes structural instead of something a reviewer has to
prove about a diff. A dead version check inside `v1` misleads the next reader
into thinking `v1` can take that path. Shipped files should stay byte-identical
to what shipped.

How: copy the previous generation into the new module, make the change there,
move the tests that exercise the new behaviour into the new module, and bump
the method's number in the new protocol version's tables only. Duplication
between generations is the accepted cost; it is cheaper than a drift-prone
flag.
by the tables of the unreleased protocol version. A shipped `vN` may be edited
in place only when we are sure the edit cannot modify consensus at any protocol
version that selects it: the new code is unreachable there by construction
(the data it acts on cannot exist under those versions, such as a keyword every
one of their meta-schemas refuses and their parser ignores, judged through a
dpp method whose own gate is `None` there), or the edit is a pure refactor with
identical output. "Probably inert" is not enough. If the argument takes more
than a sentence, or rests on a runtime check inside the shipped module, add a
generation instead. Inside a new generation the capability is a constant fact
(`Index::try_from_value_map(map, true)`), not a runtime check.

Why: replay safety is structural when a shipped file stays byte-identical, and
becomes a proof the reviewer has to check the moment it does not. An in-place
edit is acceptable when that proof is short and written down; a dead version
check inside `v1` that misleads the next reader into thinking `v1` can take
that path is not.

How, new generation: copy the previous generation into the new module, make
the change there, move the tests that exercise the new behaviour into the new
module, and bump the method's number in the new protocol version's tables
only. Duplication between generations is the accepted cost; it is cheaper than
a drift-prone flag.

How, in place: make the edit, leave a comment at the edited lines naming why
they are inert for every protocol version that selects the module, and give
the pull request description an "In-place changes to shipped generations"
section that lists each edited generation, the protocol versions that select
it, and the reason consensus cannot change there. Reviewers read that section
first.

### Table versions follow protocol-version boundaries, not PRs

Expand Down Expand Up @@ -472,6 +485,17 @@ now does.
`PlatformVersion::latest()`; pin `vN/`'s tests to `PlatformVersion::get(n)`.
5. Add a test that runs both versions through the dispatcher.

**Editing a shipped generation in place**

1. Only when the edit cannot modify consensus at any protocol version that
selects the module: unreachable by construction there, or output-identical.
2. Comment the edited lines with why they are inert for those versions.
3. Add a test that runs the module at the last shipped protocol version and
shows the outcome unchanged.
4. Add an "In-place changes to shipped generations" section to the pull
request description: each edited generation, the versions that select it,
and the reason consensus cannot change there.

**Changing a limit or a fee**

1. A number: add or update the `SystemLimits` (or `*_constants`) field, backfill
Expand Down
2 changes: 2 additions & 0 deletions book/src/data-model/contract-moderation.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ The declaration lives in `packages/rs-dpp/src/data_contract/config/moderation/el

**Referencing an elected contract.** A document type that must point at a contract of this kind says so in its reference: `"refersTo": { "type": "contract", "contractRequirements": { "moderation": "elected" } }`. `contractRequirements` holds what the referenced contract must declare beyond existing, each key an aspect of the contract with a closed set of values or a bound: `moderation: "elected"`, or `moderation: "electionOpen"`, which also requires the contract's own election delay to have passed since its creation, or the contract to declare none (the delay between a contract's creation and the first charter against it, so a team cannot be seated before anyone has seen the contract, set by each contract for itself). Both have a user in the charter contract: a charter proposal only needs the target to be `elected`, so teams can form during the notice, and the charter that opens the contest needs its election `electionOpen`; `minimumAgeSeconds`, a number of seconds the reference fixes, which requires the contract's recorded creation time to be at least that far before the block time of the write; `minimumSecondsSinceUpdate`, the same of the later of the contract's creation and last update times (any update restarts the clock; an elected declaration can not be added by an update, so this one is for other uses than the charter); `owner`, `"self"` requiring the referenced contract to be owned by the writer of the referring document (its `$ownerId`, a write gate like the `$ownerId` property agreement of a document reference) and `"other"` by anyone else (so a charter may forbid an owner from chartering its own team); `readonly: true`, requiring the referenced contract's config to be read-only, one that can never be updated again (which makes `minimumSecondsSinceUpdate` moot for the same target); `keepsHistory: true`, requiring its config to keep history (only `true` is declarable for either flag); and `ownerProtected`, requiring the contract's elected moderation declaration to protect the owner from the team (`true`) or to leave it unprotected (`false`), which implies elected moderation without the schema having to say so, a contract without an elected declaration meeting neither value. A contract created before contracts recorded their creation time never meets a duration, its own election delay included. Consensus checks them when the referring document is written, against the contract it has already fetched for the existence check and the write itself (its owner and block time), so they cost no further read; a contract that exists but does not meet a requirement refuses the write, paid, with `ReferencedContractRequirementNotMetError` (40135) naming the requirement, where a contract that does not exist is still 40120. A changed `contractRequirements` is an incompatible schema change on update, like the rest of a `refersTo`. The charter system contract's `targetContractId` is the first user.

**Referencing an identity key with requirements.** The same shape serves the key references the charter contract needs: `"refersTo": { "type": "identityPublicKey", "keyIdProperty": "recipientKeyId", "keyRequirements": { "purpose": "decryption", "boundTo": "submittedCharter" } }`. `keyRequirements` holds what the referenced key must be beyond existing and not being disabled, each key an aspect of the key: `purpose`, the key's purpose by its wire name (`authentication`, `encryption`, `decryption`, `transfer`, `voting` or `owner`; never `system`), and `boundTo`, the name of a document type of the declaring contract, which requires the key's contract bounds to be exactly the declaring contract and that document type; a whole-contract bound or a contract group bound never meets it, even where the group holds the type, since the check reads nothing beyond the key. Registration (`create_document_types_from_document_schemas` 1, a post-pass edited in place since it is inert before protocol version 14, under full validation like the meta-schema) checks that `boundTo` names a document type the contract has, so the write-time check never needs a second contract fetch, and that a key meeting the pair can exist at all: only authentication, encryption and decryption keys carry a document type bound, and Drive registers an encryption or decryption key bound to a document type only when that type declares `requiresIdentityEncryptionBoundedKey` or `requiresIdentityDecryptionBoundedKey`, so a `boundTo` paired with `transfer`, `voting` or `owner`, or with an encryption purpose on a type without the matching keyword, is refused as a requirement no key could ever meet. Consensus checks the requirements when the referring document is written, against the key it has already fetched for the existence check, so they cost no further read; a key that exists and is enabled but does not meet one refuses the write, paid, with `ReferencedIdentityKeyRequirementNotMetError` (40136) naming the document type, the property, the requirement and what the key has, where a missing key is still 40123 and a disabled one 40124. A replace that repoints the reference at another key, through either the identity id or the key id, re-checks them. A changed `keyRequirements` is an incompatible schema change on update, like the rest of a `refersTo`. New requirements (a security level, say) are new keys of the same object, never a new reference type. The charter contract's `joinRequest.recipientId` (a decryption key bound to `submittedCharter`) is the first user.

**What comes next.** The charter system contract, applications and the election (new vote poll kinds), the seated team under the contract with its per-ability powers, charter-priced moderators amounts within the maximums, and challenges and amendments. Issue #4865 holds the design.

## Versioning Touchpoints
Expand Down
Loading
Loading