Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
9de6dac
feat(platform)!: dpns contract v3 checks name registration with schem…
QuantumExplorer Oct 8, 2026
08a5679
fix(platform): a pre-upgrade preorder meets only a one-block minimum age
QuantumExplorer Oct 8, 2026
7fa7b0a
Merge remote-tracking branch 'origin/v5.0-dev' into claude/wizardly-d…
QuantumExplorer Oct 8, 2026
521761b
test(sdk): pin the dpns preorder hash of a mixed-case parent domain name
QuantumExplorer Oct 8, 2026
bdc9d9e
Merge remote-tracking branch 'origin/v5.0-dev' into claude/wizardly-d…
QuantumExplorer Oct 8, 2026
d3518c7
chore(contract): shorten the dpns v3 schema descriptions
QuantumExplorer Oct 9, 2026
d93f498
feat(platform)!: dpns v3 preorders are unique per owner and hash the …
QuantumExplorer Oct 9, 2026
e6c1911
Merge remote-tracking branch 'origin/v5.0-dev' into claude/wizardly-d…
QuantumExplorer Oct 9, 2026
b66668f
fix(sdk): hash DPNS preorders against the contract the network stores
QuantumExplorer Oct 9, 2026
fbd02c9
fix(sdk): take the DPNS contract only from a response proved at the p…
QuantumExplorer Oct 9, 2026
b720af0
fix(drive): seed the block cache with the contract a rebuild stores
QuantumExplorer Oct 9, 2026
51403d3
fix(sdk): skip a DPNS contract response when the SDK version moved me…
QuantumExplorer Oct 9, 2026
4905cf3
Merge remote-tracking branch 'origin/v5.0-dev' into claude/wizardly-d…
QuantumExplorer Oct 9, 2026
1f1a8b0
feat(contract): write the DPNS v3 byte arrays with the type shorthands
QuantumExplorer Oct 9, 2026
45309b9
fix(platform): DPNS v3 review fixes
QuantumExplorer Oct 9, 2026
a9b1ee5
refactor(dpp): read a reference's findBy hash key through one accessor
QuantumExplorer Oct 9, 2026
0e3ae00
test(sdk): give the records.identity refusal spec two distinct identi…
QuantumExplorer Oct 9, 2026
c2d30d9
docs(book): the findBy function exception for $ownerId; DPNS preorder…
QuantumExplorer Oct 9, 2026
b43fcec
test(drive): run the protocol 13 halves of three tests on DPNS v2
QuantumExplorer Oct 9, 2026
c158a7d
fix(sdk): parse a proof the SDK's version can not read under the vers…
QuantumExplorer Oct 9, 2026
3961808
Merge branch 'v5.0-dev' into claude/wizardly-driscoll-44c981
QuantumExplorer Oct 9, 2026
521bfa2
test(sdk): cover the protocol version fallback through the quorum key…
QuantumExplorer Oct 10, 2026
193c42f
test(sdk): fetch a missing quorum key and read the proof again throug…
QuantumExplorer Oct 10, 2026
35696a2
Merge remote-tracking branch 'origin/v5.0-dev' into claude/wizardly-d…
QuantumExplorer Oct 11, 2026
4be5d8c
Merge remote-tracking branch 'origin/v5.0-dev' into claude/wizardly-d…
QuantumExplorer Oct 11, 2026
759d679
feat(platform)!: dpns v3 preorders hash the owner and the name, witho…
QuantumExplorer Oct 11, 2026
aaa0bb7
fix(sdk): dpns registration decodes under the contract it built again…
QuantumExplorer Oct 11, 2026
b1ae90f
fix(wasm-sdk): the trusted context forwards a registered contract to …
QuantumExplorer Oct 11, 2026
ec975f7
Merge branch 'v5.0-dev' into claude/wizardly-driscoll-44c981
QuantumExplorer Oct 12, 2026
8a35b1a
Merge branch 'v5.0-dev' into claude/wizardly-driscoll-44c981
QuantumExplorer Oct 12, 2026
a7e6bf7
Merge v5.1-dev (with v5.0-dev) into the DPNS v3 branch
QuantumExplorer Oct 13, 2026
1cfe150
feat(platform)!: move DPNS v3 to protocol version 15 and choose the f…
QuantumExplorer Oct 13, 2026
4146766
fix(drive-abci): keep DPNS v3 for its rebuild on an upgrade that skip…
QuantumExplorer Oct 13, 2026
388dd5f
Merge v5.1-dev into the DPNS v3 branch
QuantumExplorer Oct 20, 2026
e46c134
feat(platform)!: hold a DPNS name to the preorders a reveal reads, wi…
QuantumExplorer Oct 21, 2026
b9eecc3
fix(sdk): delete a pending DPNS preorder that expires before its reve…
QuantumExplorer Oct 21, 2026
0541601
test(drive-abci): run the contest-fund upgrade test at the version it…
QuantumExplorer Oct 21, 2026
823e3af
test(drive-abci): re-pin the nullifier batch guard roots for the DPNS…
QuantumExplorer Oct 21, 2026
7a92326
fix(sdk): leave the DPNS records.identity rule to DPP in js-dash-sdk
QuantumExplorer Oct 21, 2026
97c7e85
test(sdk): cover the public DPNS registration up to its pending-preor…
QuantumExplorer Oct 21, 2026
b3db3ce
fix(sdk): js-dash-sdk stops on an expiring pending DPNS preorder inst…
QuantumExplorer Oct 21, 2026
5e53930
test(sdk): judge a pending DPNS preorder at the time its response rep…
QuantumExplorer Oct 21, 2026
1e44cf6
Merge v5.1-dev into the DPNS v3 branch
QuantumExplorer Oct 21, 2026
e78a7ca
refactor(dpp): version the countOf byte-array binding on the platform…
QuantumExplorer Oct 21, 2026
db3ae3b
Merge v5.1-dev into the DPNS v3 branch
QuantumExplorer Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions book/src/contract-keywords/deletion.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,21 @@ A commenter may take a comment down at any time. Since `true` is the usual defau
"documentsMutable": false,
"canBeDeleted": "onlyWhenConsumed",
"indices": [
{ "name": "saltedHash", "properties": [{ "saltedDomainHash": "asc" }], "unique": true }
{
"name": "ownerAndNameHash",
"properties": [{ "$ownerId": "asc" }, { "ownerNameHash": "asc" }],
"unique": true
}
],
"properties": {
"saltedDomainHash": {
"type": "array", "byteArray": true, "minItems": 32, "maxItems": 32, "position": 0
}
"ownerNameHash": { "type": "bytes", "size": 32, "position": 0 }
},
"required": ["$createdAtBlockHeight", "saltedDomainHash"],
"required": ["$createdAtBlockHeight", "ownerNameHash"],
"additionalProperties": false
}
```

A preorder stays until the name registration that reveals it consumes it (the `preorderSalt` declaration in [Commit and reveal](refers-to-lookup.md#commit-and-reveal)). Its owner can not take it back with a delete.
A preorder of this type stays until the name registration that reveals it consumes it (the domain's `creatorRefersTo` in [Commit and reveal](refers-to-lookup.md#commit-and-reveal)). Its owner can not take it back with a delete. DPNS v3's own `preorder` declares the same index but `canBeDeleted: true`, so its owner may still delete a preorder it no longer wants, and a `ttl` of an hour, after which the platform deletes it; this example is the stricter variant.

- A delete transition of such a document is refused (`InvalidDocumentTransitionActionError`, 10404), as for `false`.
- A consume deletes it as a delete by its owner would, its storage refunded to the owner.
Expand Down
2 changes: 1 addition & 1 deletion book/src/contract-keywords/generated-from.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ A client creates `{ "label": "Bob" }` and the stored document holds `{ "label":

The generated property needs no `pattern` of its own. Every value it can hold is what the function generates from params that passed their own patterns: here `label` admits ASCII letters, digits and `-`, so `normalizedLabel` can only ever hold `a` to `z` without `i`, `l` and `o`, digits and `-`. It keeps `maxLength` because it is indexed, and an indexed string declares one of at most 63.

This is the rule the DPNS `domain` type's data trigger checks today for `normalizedLabel` and `normalizedParentDomainName`, written into the schema.
The DPNS `domain` type declares this rule for `normalizedLabel` and `normalizedParentDomainName` from protocol version 14 (DPNS contract v3). Before it, the type's create data trigger checked the same rule.

## Functions

Expand Down
2 changes: 1 addition & 1 deletion book/src/contract-keywords/ownership-and-trading.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Whether an owner may give a document to another identity.

- The owner sends a transfer transition naming the document, the recipient and a `$revision` one higher than the stored one (`InvalidDocumentRevisionError`, 40106). Only the owner may transfer (`DocumentOwnerIdMismatchError`, 40102). The recipient does not have to agree.
- The document gets the recipient as its `$ownerId` and a new `$revision`. A price it was listed at is removed, so a transferred document is no longer for sale. When the type lists `$transferredAt` in `required`, it is set to the block's time, and likewise `$transferredAtBlockHeight` and `$transferredAtCoreBlockHeight` to the block heights.
- A transfer carries no property values, and `$creatorId` keeps naming the identity that created the document. The one change the platform makes itself: from protocol version 13, a DPNS `domain` that is transferred or sold has its `records.identity` pointed at the new owner.
- A transfer carries no property values, and `$creatorId` keeps naming the identity that created the document. The one change the platform makes itself: from protocol version 13, a DPNS `domain` that is transferred or sold has its `records.identity` pointed at the new owner. From protocol version 14 the type's `recordsIdentityIsOwner` rule holds a new name's `records.identity` to its owner until it first changes hands (see [propertyConstraints](property-constraints.md)), so a name can not be transferred or sold in the block that created it.
- The document is checked as it will be stored, with its new owner: against the type's unique indexes (`DuplicateUniqueIndexError`, 40105), against a `distinctFrom: "$ownerId"` property (`DocumentPropertyNotDistinctError`, 10419), and against `propertyConstraints` rules that read `$ownerId` (`DocumentPropertyConstraintViolatedError`, 10422).
- On a moderated contract, a transfer to a banned or suspended identity is refused (`ContractModerationCounterpartyBarredError`, 41114).
- A transfer of a document past its `ttl` expiry is refused (`DocumentExpiredError`, 40140).
Expand Down
1 change: 1 addition & 0 deletions book/src/contract-keywords/property-constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ A rule can read when the document was created, last updated and last transferred
- `{ "lessThanOrEqual": [{ "subtract": ["endsAt", "$createdAt"] }, 604800000] }` keeps a listing to a week from its creation.
- `{ "lessThanOrEqual": ["$updatedAt", "endsAt"] }` refuses a replace or a price update after the listing ends.
- `{ "lessThanOrEqual": ["$transferredAt", "endsAt"] }` refuses a transfer or a purchase after it ends.
- `{ "ifThen": [{ "equal": ["$transferredAt", "$createdAt"] }, { "equal": ["records.identity", "$ownerId"] }] }` holds `records.identity` to the owner until the document first changes hands. A create records its block's time as the transfer too, so the condition holds on the create, and a transfer or a purchase in a later block records a later time, so it no longer reads the property. One in the block that created the document records that block's time, so the rule still applies to it: a new owner other than `records.identity` is refused. The DPNS `domain` type's `recordsIdentityIsOwner` (protocol version 14) is this rule.

A rule may read one only when the document type records it by listing it in `required`, so every stored document holds it. None takes an `ifAbsent` default, `present` and `absent` refuse them, and an [index-only](index-only.md) type reads none. Each write is judged with the values the stored document ends up with: a create with its block's time and heights for all three events; a replace with the stored creation and transfer values and its block's as the update; a price update with its block's as the update; a transfer or a purchase with its block's as the transfer.

Expand Down
47 changes: 23 additions & 24 deletions book/src/contract-keywords/refers-to-lookup.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ A `permanentDocument` found by `findBy` never dangles. Its referenced documents
- when a property `findBy` reads changed, and then every value, every element included;
- when the referring value of a `where` entry changed, or on every replace for a `where` entry whose value is `"$ownerId"`.

A key part read from `"$ownerId"` never triggers a check: only a type whose documents keep their writer may read it.
A key part read from `"$ownerId"` never triggers a check: only a type whose documents keep their writer may read it, or a key with a [function](#commit-and-reveal), judged on the create alone.

A `deletableDocument` found by `findBy` promises less. Once the document a key found is deleted, a new document filed later under the same key makes the reference hold again, with different content, where an id reference to a deleted document stays dead. So it means "a document with this key exists now", which is what a membership gate needs. Every replace checks it again, whether or not the replace touched it, and no `immutable` property may hold one. The moderation charters contract's `resignationRequest` uses one to accept a writer who has an `addedModerator` document for the charter at the time of writing, as the alternative to being an elected member (see [Writer and Creator References](owner-refers-to.md)).

Expand Down Expand Up @@ -138,49 +138,48 @@ The value of a reference found by `findBy` is a key part, not a document id. So

## Commit and reveal

One `findBy` entry may hold a function: `"<referenced property>": { "function": "sys.hash.sha256d", "params": [...] }` says the referenced document's property holds the SHA-256 of the SHA-256 of the params' bytes, joined in order. The platform fills that property of the key with the hash to find the document, like any other entry. The document it finds is a commitment made earlier, one that stored that hash, so the document being created may exist only while a commitment to values it carries exists. This is how a name registration that preorders a salted hash works, written as a declaration on the salt:
One `findBy` entry may hold a function: `"<referenced property>": { "function": "sys.hash.sha256d", "params": [...] }` says the referenced document's property holds the SHA-256 of the SHA-256 of the params' bytes, joined in order. The platform fills that property of the key with the hash to find the document, like any other entry. The document it finds is a commitment made earlier, one that stored that hash, so the document being created may exist only while a commitment to values it carries exists. This is how the DPNS `domain` type registers a name from protocol version 14 (DPNS contract v3), a declaration on the domain's creator:

```json
"preorderSalt": {
"type": "array", "byteArray": true, "minItems": 32, "maxItems": 32, "position": 4,
"refersTo": {
"type": "deletableDocument",
"documentType": "preorder",
"findBy": {
"saltedDomainHash": {
"function": "sys.hash.sha256d",
"params": ["preorderSalt", "normalizedLabel", { "const": "." }, "parentDomainName"]
}
},
"where": { "$ownerId": "$ownerId" },
"minimumAgeBlocks": 1,
"consume": true
}
"creatorRefersTo": {
"type": "deletableDocument",
"documentType": "preorder",
"findBy": {
"$ownerId": "$ownerId",
"ownerNameHash": {
"function": "sys.hash.sha256d",
"params": ["$ownerId", "normalizedLabel", { "const": "." }, "normalizedParentDomainName"]
}
},
"minimumAgeBlocks": 1,
"consume": true
}
```

The salt must reveal a `preorder` whose `saltedDomainHash` is the sha256d of the salt, the normalized label, a dot and the parent. `findBy` names `saltedDomainHash` alone, the whole of the `preorder` type's unique `saltedHash` index. The `where` entry makes it the writer's own preorder, `minimumAgeBlocks: 1` one created in an earlier block, and `consume: true` deletes it with the create. As SQL:
The creator must hold a `preorder` of the name, whose `ownerNameHash` is the sha256d of the creator's id, the normalized label, a dot and the normalized parent. `findBy` names `$ownerId` and `ownerNameHash`, the whole of the `preorder` type's unique `ownerAndNameHash` index, so every identity has its own preorders, a copy of someone else's never collides with theirs, and an identity holds one pending preorder of a name. `minimumAgeBlocks: 1` asks for one created in an earlier block, and `consume: true` deletes it with the create. As SQL:

```sql
SELECT * FROM preorder
WHERE saltedDomainHash = -- findBy
sha256d(:preorderSalt || :normalizedLabel || '.' || :parentDomainName)
AND $ownerId = :writer -- where
WHERE $ownerId = :creator -- findBy
AND ownerNameHash = -- findBy
sha256d(:creator || :normalizedLabel || '.' || :normalizedParentDomainName)
AND $createdAtBlockHeight <= :createHeight - 1 -- minimumAgeBlocks
-- then the create deletes the row found -- consume
```

- **Params.** A property path reads the document being created, the property carrying the reference included; `{ "const": text }` is fixed text of 1 to 64 bytes; `"."` is the value carrying the reference where it has no path (each element of a typed array, the writer, the creator); 1 to 16 params. Strings count as their UTF-8, byte arrays as their bytes, identifiers as their 32 bytes. Two values of no fixed length must be separated by a one-byte `const`, and a value holding that byte is refused, so the joined bytes split back one way only.
Hashing the creator matters as much as keying by it. A copy of a pending preorder, stored under the copier's own key, commits to the identity that made it, so the copier, who learns the name from that identity's reveal, still finds nothing when revealing it. The params hold no salt: the hash commits to the name itself. A DPNS preorder also holds `nameHash`, the same hash without the creator, indexed with its creation height, and lives an hour (`ttl`). A contract that wants the name hidden until the reveal puts a salt, a transient property of the referring type, among the params instead.

- **Params.** A property path reads the document being created, the property carrying the reference included; `{ "const": text }` is fixed text of 1 to 64 bytes; `"."` is the value carrying the reference where it has no path (each element of a typed array, the writer, the creator); `"$ownerId"` is the writer's id; 1 to 16 params. Strings count as their UTF-8, byte arrays as their bytes, identifiers as their 32 bytes. Two values of no fixed length must be separated by a one-byte `const`, and a value holding that byte is refused, so the joined bytes split back one way only.
- **One function.** `findBy` holds at most one function, and the property it fills must be a byte array of exactly 32 bytes.
- **Carrier.** A string or byte array property may carry a `refersTo` this way, or with its value read as is (see [A string or byte array key](#a-string-or-byte-array-key)), and keeps its type. The value carrying the reference fills the key exactly once, as a `"."` entry or a param; a param names a property by its path, never as `"."`. In `ownerRefersTo` and `creatorRefersTo` the value may be left out beside a function.
- **Judged once.** A function's key is checked when the document is created, never on a replace, so every stored value it reads must be fixed once written, the carrier included (listed under `immutable` on a mutable type, and required when it is a `deletableDocument` reference by id, which a replace may otherwise clear once its document is deleted). The `where` entries are judged with it, so each referring property they name must be fixed once written too, or transient. On a type whose documents can be replaced it may not be an operand of an `anyOf`, which would then hold on every replace. Params may be transient: they are read from the create. The hash is billed as the double SHA-256 it is, by the blocks it hashes, beside the document fetch.

`minimumAgeBlocks` and `consume` sit on the `refersTo`, beside `findBy`: they describe the document `findBy` finds when the referring document is created, so the reference must be judged on the create alone. A function in `findBy` makes it so, and so does a referring type whose documents are never replaced (`documentsMutable: false`): a replace is the one write that checks a reference again. Any other `findBy` declaring either is refused at registration (10231), and so is either beside [`inList`](refers-to-list-element.md), which checks that a list holds the value rather than finding a document by a key.

- **`minimumAgeBlocks`**: the commitment's `$createdAtBlockHeight` is at least that many blocks below the create's height. The commitment type must list `$createdAtBlockHeight` in `required`.
- **`consume: true`**: the create deletes the commitment, its storage refunded to its owner. Only on a `deletableDocument` reference with the `where` entry `"$ownerId": "$ownerId"`, into a type of the same contract whose owner may delete its documents (`canBeDeleted: true`) or whose documents only a consume deletes (`canBeDeleted: "onlyWhenConsumed"`, see [Deletion](deletion.md#canbedeleted)), that declares no delete token cost or delete action fee and no [`deleteConstraints`](deletion.md#deleteconstraints) (no delete transition charges or judges them), and that requires no stricter signature security level than the revealing type. A contract-bound key signing the create must be allowed to act on the consumed type too (`ContractBoundedKeyOutOfBoundsError`, 20014).
- **`consume: true`**: the create deletes the commitment, its storage refunded to its owner (a commitment type with a [`ttl`](ttl.md) refunds nothing, as DPNS v3's preorder). Only on a `deletableDocument` reference that makes the writer the commitment's owner, by the `where` entry `"$ownerId": "$ownerId"` or the `findBy` entry `"$ownerId": "$ownerId"`, into a type of the same contract whose owner may delete its documents (`canBeDeleted: true`) or whose documents only a consume deletes (`canBeDeleted: "onlyWhenConsumed"`, see [Deletion](deletion.md#canbedeleted)), that declares no delete token cost or delete action fee and no [`deleteConstraints`](deletion.md#deleteconstraints) (no delete transition charges or judges them), and that requires no stricter signature security level than the revealing type. A contract-bound key signing the create must be allowed to act on the consumed type too (`ContractBoundedKeyOutOfBoundsError`, 20014).

A create missing a param, or with a value holding its separator, is refused before any read (`DocumentReferencePreimageInvalidError`, 10423). No commitment is `ReferencedEntityNotFoundError` (40120), another identity's `ReferencedDocumentPropertyMismatchError` (40127), and one too young `ReferencedDocumentRequirementNotMetError` (40142). The [Documents chapter](../data-model/documents.md#commit-and-reveal-a-findby-function) has the full rules.
A create missing a param, or with a value holding its separator, is refused before any read (`DocumentReferencePreimageInvalidError`, 10423). No commitment is `ReferencedEntityNotFoundError` (40120), another identity's `ReferencedDocumentPropertyMismatchError` (40127) with the owner entry in `where` and 40120 with it in `findBy` (as in DPNS, whose key then finds none of the writer's), and one too young `ReferencedDocumentRequirementNotMetError` (40142). The [Documents chapter](../data-model/documents.md#commit-and-reveal-a-findby-function) has the full rules.

## Rules at registration

Expand Down
Loading
Loading