Skip to content

feat(platform)!: ownerRefersTo and creatorRefersTo, references on the document's writer and creator (PV14) - #4941

Merged
QuantumExplorer merged 6 commits into
v4.2-devfrom
feat/owner-refers-to
Sep 23, 2026
Merged

QuantumExplorer merged 6 commits into
v4.2-devfrom
feat/owner-refers-to

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

The moderation charters contract (#4898) needs rules about who writes a document, not about a value the writer chose. A resignationRequest must come from a moderator of the team it resigns from. Every refersTo form so far constrains a property's value, so the only way to state that rule was a property the writer fills with their own id, a lookup on it, and a propertyAgreement binding it to the writer:

"resignationRequest": {
  "properties": {
    "electedCharterId": { "...": "..." },
    "self": {
      "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32,
      "contentMediaType": "application/x.dash.dpp.identifier",
      "refersTo": {
        "type": "permanentDocument",
        "documentType": "addedModerator",
        "propertyAgreement": { "$ownerId": "memberId" },
        "lookup": {
          "index": "byElectedCharterMember",
          "keys": { "electedCharterId": "electedCharterId", "memberId": "." }
        }
      }
    }
  },
  "required": ["electedCharterId", "self"]
}

This adds ownerRefersTo at protocol version 14. It is a document type keyword holding one refersTo declaration whose value is the document's $ownerId, the writer:

"resignationRequest": {
  "type": "object",
  "ownerRefersTo": {
    "type": "permanentDocument",
    "documentType": "addedModerator",
    "lookup": {
      "index": "byElectedCharterMember",
      "keys": { "electedCharterId": "electedCharterId", "memberId": "." }
    }
  },
  "properties": { "electedCharterId": { "...": "..." } },
  "required": ["electedCharterId"]
}

reads: the writer must be the memberId of an addedModerator for this document's electedCharterId. In a lookup, "." is the writer.

ownerRefersTo is only admitted on a type whose documents can be neither transferred nor traded, since a transfer or a purchase would hand a document to an owner the declaration never checked. For types whose documents can change hands this also adds its counterpart, creatorRefersTo. Its value is the document's $creatorId, which a transfer or a purchase never changes:

"moderatorBadge": {
  "type": "object",
  "transferable": 1,
  "creatorRefersTo": {
    "type": "permanentDocument",
    "documentType": "addedModerator",
    "lookup": {
      "index": "byElectedCharterMember",
      "keys": { "electedCharterId": "electedCharterId", "memberId": "." }
    }
  },
  "properties": { "electedCharterId": { "...": "..." }, "label": { "...": "..." } }
}

reads: only a seated moderator may mint a badge, and whoever holds it later, it stays a badge a moderator minted. The charters' full rule also needs listElement (members listed in the elected charter), which comes in its own PR, and anyOf (either source). anyOf landed as #4942, and this PR composes with it (see below). This PR does not edit the charters contract.

What was done?

The declaration (rs-dpp; meta-schema v3 and parser generation 3, both unreleased)

  • Meta-schema v3: a new document type keyword ownerRefersTo, which reuses the property refersTo definition by $ref and admits two forms: type: identity, and type: permanentDocument with a lookup. ownerRefersTo is not among the stray keys pinned by the meta-schema v0 census guard (meta_schema_v0_stray_keyword_tests), so no stored contract can carry it.
  • Parser: generation 3 reads the keyword from the stored schema once the core parse has run the meta-schema, on every parse, validating or not, like its other document type keywords. At registration a malformed declaration is therefore reported by the meta-schema (JsonSchemaError), as a malformed refersTo on a property is. It goes through parse_owner_reference, which hands the declaration to the same apply_property_reference 0 an identifier property's refersTo goes through, propertyAgreement and lookup included (in a lookup "." is the writer). Only two targets can hold a writer, and the parser refuses every other on the stored path too:
    • contract, token, and a permanentDocument or deletableDocument by id: the writer's identity id is never a contract, token or document id, so a type declaring one could never be written, and the keyword cannot change on update;
    • identityPublicKey in either form: it pairs the value with a key id, which the writer does not carry.
  • Owner rule: refused on a document type whose documents can be transferred or traded, checked on every parse. A transfer or a purchase is not a write, so it would hand the document to an owner the declaration never checked. With neither possible, the owner of every document is the writer that was checked.
  • Where it lives: DocumentTypeV2::owner_reference: Option<DocumentPropertyReferenceTarget>, read through a new DocumentTypeV2Getters::owner_reference(). It is None on DocumentType V0/V1 and on every type that declares none. Property types are unchanged.
  • One enumeration: DocumentTypeRef::reference_declarations() yields every reference declaration with its ReferenceHolder: Owner first, then Property(path) for each property's own. It covers the reference count, the lookup checks, both drive-abci validators and wasm-dpp2, so none can skip the owner reference.
  • Lookup: the referring side follows the property rules unchanged (referring_side_error, with the path $ownerId; a "$ownerId" key part passes its owner rule because of the point above). A lookup into a type of the same contract is checked in create_document_types_from_document_schemas 1 (see the in-place section below), and one into another contract at registration.
  • Reference count: it counts one against SystemLimits::max_references_per_document.

Before (v4.2-dev), the contract above was refused at registration: meta-schema v3 closes the document type level (additionalProperties: false) and had no ownerRefersTo. After, it parses:

contract.document_type_for_name("resignationRequest")?.owner_reference()
// Some(DocumentPropertyReferenceTarget::PermanentDocumentLookup {
//     contract_id: None,
//     document_type_name: "addedModerator",
//     property_agreement: {},
//     lookup: DocumentReferenceLookup {
//         index: "byElectedCharterMember",
//         keys: { "electedCharterId": Property("electedCharterId"), "memberId": ReferenceValue },
//     },
// })

Refused forms (registration: the meta-schema's JsonSchemaError; stored path: the parser):

"ownerRefersTo": { "type": "permanentDocument", "documentType": "profile" }
// ownerRefersTo takes a permanentDocument reference only with a lookup: its value, the
// writer's identity id, is never a document id

"ownerRefersTo": { "type": "token" }
// ownerRefersTo does not take a token reference: its value, the writer's identity id, is
// never a token id

"ownerRefersTo": { "type": "identityPublicKey", "identityProperty": "$ownerId" }
// ownerRefersTo does not take an identityPublicKey reference: it pairs the value with a key
// id, which the writer does not carry

"resignationRequest": { "transferable": 1, "ownerRefersTo": { "type": "identity" }, ... }
// document type "resignationRequest" declares ownerRefersTo, but its documents can be
// transferred or traded: a transfer or a purchase would hand a document to an owner the
// declaration never checked

Composition with anyOf / allOf (#4942)

Both keywords take a reference expression in place of a single target, provided every leaf is one the identity can be: an identity or a permanentDocument lookup. A leaf by id is refused as it is alone, named by where it sits.

"stepDownNotice": {
  "ownerRefersTo": { "anyOf": [
    { "type": "permanentDocument", "documentType": "addedModerator", "lookup": { "index": "byElectedCharterMember", "keys": { "electedCharterId": "electedCharterId", "memberId": "." } } },
    { "type": "permanentDocument", "documentType": "founderSeat", "lookup": { "index": "byElectedCharterFounder", "keys": { "electedCharterId": "electedCharterId", "founderId": "." } } }
  ] },
  "...": "..."
}
written by an added moderator of C1        -> ok (first operand)
written by the founder seated for C1       -> ok (second operand)
written by anyone else                     -> 40120 at $ownerId, entity_type: the founderSeat lookup
"anyOf": [<lookup>, { "type": "permanentDocument", "documentType": "addedModerator" }]
  -> ownerRefersTo anyOf[1] takes a permanentDocument reference only with a lookup

creatorRefersTo, the creator's counterpart

  • Where it is allowed: only on a type that records creator ids (should_use_creator_id: a transferable or tradeable type of a format-1 contract), checked on every parse. That is exactly where ownerRefersTo is refused, so a type declares at most one of the two.
  • Same declaration: the same meta-schema shape and parser (parse_doctype_reference), the same two targets, "." the creator in a lookup. It is stored as DocumentTypeV2::creator_reference (DocumentTypeV2Getters::creator_reference()) and yielded by reference_declarations() as ReferenceHolder::Creator.
  • Lookup: a "$ownerId" key part in its lookup is refused, as in any lookup on a type whose owner moves.
  • Checks:
    • on create, against the writer, who is the creator;
    • on replace, against the stored creator whoever writes, under the same rules as the owner reference;
    • never on a transfer or a purchase, which don't change the creator.
  • Errors: named $creatorId, and <documentType>.$creatorId at registration.
  • Other rules: an identity target reads nothing. It counts one against max_references_per_document and is frozen on update.
creatorRefersTo on a non-transferable, non-tradeable type
  -> document type "resignationRequest" declares creatorRefersTo, but it records no creator ids
ownerRefersTo on a transferable type
  -> ... declares ownerRefersTo, but its documents can be transferred or traded ...;
     creatorRefersTo checks the creator, who never changes
mint moderatorBadge { electedCharterId: C1 } by M (seated for C1)  -> ok
mint moderatorBadge { electedCharterId: C1 } by S                  -> 40120 { entity_id: S, path: "$creatorId" }
transfer the badge M -> S                                          -> ok (no check needed)
replace by S changing `label`                                      -> ok, nothing read
replace by S moving electedCharterId to C2 (S seated there, M not) -> 40120 { entity_id: M, path: "$creatorId" }

Registration (drive-abci data_contract_reference_validation 0)

The owner reference goes through the same loop as the property references (reference_declarations), first, named <documentType>.$ownerId. Every existing check applies unchanged: the referenced contract and type exist, the type is permanent, the lookup into another contract resolves, and propertyAgreement pairs are valid. The one difference is that its propertyAgreement may name $ownerId on the referring side, the same writer as its value; a property reference may not name itself there.

"note": { "ownerRefersTo": { "type": "permanentDocument", "documentType": "ghost", "lookup": {..} } }
  -> ReferencedDocumentTypeNotFoundError { document_type_name: "ghost", path: "note.$ownerId" }
lookup into a deletable type of the same contract
  -> ReferencedDocumentTypeDeletableError { path: "note.$ownerId" }
lookup naming an index another contract's type does not have
  -> ReferencedDocumentLookupInvalidError { path: "note.$ownerId", index: "byMessage" }
propertyAgreement { "missing": "memberId" }
  -> ReferencedDocumentPropertyAgreementInvalidError { path: "note.$ownerId" }

Before, each of these was refused by the meta-schema.

Document create and replace (drive-abci document_reference_validation 0)

validate_document_type_references_v0 walks reference_declarations. For the owner holder the value is the writer's id instead of a property's, and $ownerId is the path; everything else is the code a property reference goes through.

  • Create: always checked.
  • Replace: under the replace rules of its target, as a property's. It is re-validated when a property its lookup or a propertyAgreement reads changed, and on every replace for a pair keyed by $ownerId (a writer gate). Nothing else can change the outcome: the writer is the owner, the target can never be deleted, and its key is fixed. A replace that touches nothing bound reads nothing.
  • Errors: a failure is the error the target reports for a property: 40120 for a lookup that finds nothing, 40127 for an agreement mismatch, and so on.
  • identity target: fetches nothing, since the transition already proved the writer exists.

With addedModerator { electedCharterId: C1, memberId: M } in state:

create resignationRequest { electedCharterId: C1 } by M          -> ok
create resignationRequest { electedCharterId: C1 } by S          -> 40120 ReferencedEntityNotFoundError
                                                                     { entity_id: S, path: "$ownerId",
                                                                       entity_type: PermanentDocumentLookup {..} }
create resignationRequest { electedCharterId: C2 } by M          -> 40120, entity_id: M
replace changing `reason` only, by M                             -> ok, nothing read
replace moving electedCharterId to C2, by M                      -> 40120, entity_id: M

Before, none of these could be expressed; the contract did not register.

Contract update (rs-dpp validate_schema_compatibility 1, unreleased)

Adding, removing or changing ownerRefersTo is an incompatible schema change, as for refersTo on a property. The v14 compatibility generation registers a copy of the shared refersTo rule under ownerRefersTo through its override_rules, so the shared rule set, which generation 0 also reads, is untouched. The copy is looked up fallibly: were the shared rule ever missing, a diff under ownerRefersTo would fail as an unsupported keyword, an error rather than a panic.

update: add    "ownerRefersTo": { "type": "identity" }
  before: registration refused the keyword in the first place
  after:  IncompatibleDocumentTypeSchemaError { document_type: "resignationRequest",
                                                operation: "add", path: "/ownerRefersTo" }
update: { "type": "identity" } -> a permanentDocument lookup   -> operation "replace" (and "add"s) under "/ownerRefersTo"
update: remove                                               -> operation "remove", path "/ownerRefersTo"

Clients (wasm-dpp2, js-evo-sdk README)

documentTypeReferences(name) and documentReferences list the owner reference first, with the path $ownerId, the path consensus errors carry. It is not a property path, and the js-evo-sdk README now says so next to its reasons[] note:

contract.documentTypeReferences('resignation')
// before: [{ path: 'author', type: 'identity' }]
// after:  [{ path: '$ownerId', type: 'permanentDocument', contractId, documentType: 'joinRequest',
//            lookup: { index: 'bySubmittedCharter', keys: { $ownerId: '.', submittedCharterId: 'submittedCharterId' } } },
//          { path: 'author', type: 'identity' }]

Swift and Kotlin are out of scope.

Docs

Design calls to review

  1. Stricter than the spec, after review. Only identity and a permanentDocument lookup are admitted, and only on types that can be neither transferred nor traded. The spec admitted every target an identifier property takes, and did not restrict transferable types.
  2. Replace follows the target's rules. The spec said to check on every replace. That was needed while the owner could change by transfer; with the owner rule it cannot, so a replace re-validates only when a bound property changed (or always for a $ownerId agreement pair), as a property's reference does.
  3. identity target is free. It does no read, where a property's identity reference is a billed identity fetch. It still counts one against max_references_per_document.
  4. Update rule placement. The spec pointed at validate_update/common. Its config checks also serve the shipped validate_update v0, so the rule went into the unreleased compatibility generation instead. The error is IncompatibleDocumentTypeSchemaError, the one a property refersTo change gives.
  5. Getter on DocumentTypeV2Getters, beside action_fees(), since the data exists only on DocumentTypeV2. The spec said DocumentTypeV0Getters.
  6. Order: the owner or creator reference is checked before the property references, at registration and at write time.
  7. creatorRefersTo follows $creatorId recording, not only transferability. You asked for transferable or tradeable types only. The rule is should_use_creator_id, which also needs a format-1 contract with a config version above 0, since elsewhere no creator id is stored to check. Every contract created at protocol version 14 meets that.

In-place changes to shipped generations

  • create_document_types_from_document_schemas v1 (selected by protocol versions 2 to 14): the same-contract lookup loop now walks reference_declarations(). Every protocol version up to 13 parses with generations 0 to 2, which never set an owner reference, so there the iterator yields exactly the properties' references, in the order the old loop walked them. Its error text for a property (property "<path>" refersTo) is unchanged. Shown at protocol version 13 through the dispatcher by should_refuse_owner_refers_to_before_protocol_version_14_and_read_it_at_14: a registering parse refuses the keyword (meta-schema v2), and a stored parse ignores it, with no owner reference.
  • data_contract_reference_validation v0 (every table carries 0): its only callers are contract create and update state validation 1, selected from protocol version 14. It now walks reference_declarations(), whose owner arm cannot exist before 14.
  • document_reference_validation v0 (every table carries 0): its only callers are document create state validation 2 and replace state validation 1, both selected from protocol version 14. Same reasoning as the previous item.

Each edited loop carries a comment stating why it is inert before 14. owner_can_change in reference_lookup.rs only became pub(crate). Everything else is unreleased and edited in place: meta-schema v3, parser generation 3, validate_schema_compatibility 1 and SYSTEM_LIMITS_V4 (docs only).

How Has This Been Tested?

  • rs-dpp, try_from_schema/v3/owner_reference_tests.rs:
    • parse of both admitted targets on both paths, with propertyAgreement naming $ownerId;
    • refusal of contract (with and without requirements), identityPublicKey in both forms, token, permanentDocument by id and deletableDocument: the parser's message on the stored path, a meta-schema JsonSchemaError at registration;
    • refusal on a transferable and on a tradeable type, on both paths;
    • refusal of a lookup without . or with it twice;
    • referring-side rules, including a $ownerId key part;
    • the same-contract referenced side (missing and non-unique index);
    • the count against max_references_per_document (256 plus one);
    • the protocol version 13/14 gate;
    • platform serialization round trip with and without the keyword;
    • update: add, remove and change refused, unchanged accepted;
    • creatorRefersTo:
      • parse on transferable and on tradeable types, both paths;
      • refusal on a type that records no creator ids, and of both keywords on one type;
      • the same refused targets as the owner form;
      • refusal of a lookup reading $ownerId;
      • the protocol version 13/14 gate, the round trip, the count, and add or change refused on update;
    • expressions: an anyOf of two lookups on the owner and an allOf on the creator parse, and a leaf by id is refused at anyOf[1].
  • rs-drive-abci:
    • batch/tests/document/owner_reference.rs, full ABCI pipeline, fixture reference-validation-contract-owner-refers-to.json:
      • a writer who meets the target succeeds;
      • one who does not is refused (40120 at $ownerId), including for another charter;
      • a replace changing reason succeeds, and one moving electedCharterId to a charter the writer is not seated for is refused;
      • an agreement mismatch is refused (40127 at $ownerId);
      • an identity target refuses nobody;
      • a badge minted by a seated moderator succeeds and one by a stranger is refused at $creatorId;
      • after a transfer, the new owner's relabel succeeds, and a key move is refused for the creator (entity id: the minting member);
      • ownerRefersTo anyOf: the writer is admitted through either operand, and anyone else is refused with the last operand's error.
    • Direct validator calls pin the reads:
      • an identity target bills nothing, on create or replace;
      • a replace leaving the lookup's keys alone bills nothing;
      • one moving a key part bills the lookup;
      • an identity creator target bills nothing.
    • data_contract_create: registration of the fixture, and at note.$ownerId: 40121 (unknown type), 40122 (deletable target), 40137 (foreign lookup), agreement invalid; 40121 at note.$creatorId.
    • Mutation checks:
      • without the identity skip, the no-read test fails;
      • without the replace check, the replace test fails;
      • with the creator's value taken from the writer, the after-transfer test fails.
  • Local runs:
    • cargo test -p dpp --lib;
    • cargo test -p drive-abci --lib filtered on owner, creator, reference, lookup, agreement, data_contract_create, data_contract_update, typed_array, distinct_from;
    • cargo test -p wasm-dpp2 and, after yarn workspace @dashevo/wasm-dpp2 build, the mocha unit suite;
    • cargo clippy -p dpp -p drive-abci --all-features --all-targets -- -D warnings, cargo clippy -p wasm-dpp2 --target wasm32-unknown-unknown -- -D warnings, cargo fmt --all -- --check.

Breaking Changes

Consensus: protocol version 14 (unreleased) admits a new document type keyword, and a document write can be refused by it. DocumentTypeV2Getters gains a required method, owner_reference. No shipped protocol version changes behaviour.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

PR Hygiene · 8ea1ce1

  • Bots — coderabbitai not yet · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed once the bots are done
  • Within your 5 open PRs — this one is beyond the limit; it waits until one merges
  • Build running
  • Approvals
    • files with no dedicated owner — you own it
    • js-wasm-sdk (packages/js-evo-sdk/README.md) — shumkov
    • dpp — you own it
    • rs-drive-abci — you own it

When every box is checked the PR Hygiene check passes and this can merge.

…(PV14)

A document type may declare one refersTo declaration of its own under the
doctype-level ownerRefersTo keyword, whose value is the document's $ownerId,
the writer, instead of a property's value. It takes every target an
identifier property's refersTo takes but contract and identityPublicKey,
propertyAgreement and lookup included (in a lookup "." is the writer).

- meta-schema v3 and parser generation 3 read it through the same
  apply_property_reference 0, onto DocumentTypeV2::owner_reference
  (DocumentTypeV2Getters::owner_reference); its lookup is checked on both
  sides like a property's, and it counts one against
  max_references_per_document
- contract registration checks it as a property's declaration, named
  <documentType>.$ownerId
- document create and every replace check the writer against the target,
  refusing with the target's own error (40120 and the rest) at $ownerId;
  transfers and purchases are not checked; an identity target reads nothing
- adding, removing or changing it is an incompatible schema change on update
  (validate_schema_compatibility 1)
- wasm-dpp2 lists it first in documentTypeReferences / documentReferences

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: a06234b9-3048-4b25-a9fb-7c73cecc3316

📥 Commits

Reviewing files that changed from the base of the PR and between c114ce4 and 8ea1ce1.

📒 Files selected for processing (33)
  • book/src/data-model/documents.md
  • packages/js-evo-sdk/README.md
  • packages/rs-dpp/schema/meta_schemas/document/v3/document-meta.json
  • packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/accessors/v2/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/create_document_types_from_document_schemas/v1/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/owner_reference_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/property/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/property/reference_lookup.rs
  • packages/rs-dpp/src/data_contract/document_type/schema/validate_schema_compatibility/v1/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/v2/accessors.rs
  • packages/rs-dpp/src/data_contract/document_type/v2/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_reference_validation/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_reference_validation/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/owner_reference.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_common/data_contract_reference_validation/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/mod.rs
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-creator-refers-to-registration-unknown-type.json
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-owner-refers-to-registration-agreement-invalid.json
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-owner-refers-to-registration-deletable-target.json
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-owner-refers-to-registration-foreign-lookup-invalid.json
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-owner-refers-to-registration-unknown-type.json
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-owner-refers-to.json
  • packages/rs-platform-version/src/version/system_limits/mod.rs
  • packages/rs-platform-version/src/version/system_limits/v4.rs
  • packages/rs-platform-version/src/version/v14.rs
  • packages/wasm-dpp2/src/data_contract/document_type_reference.rs
  • packages/wasm-dpp2/src/data_contract/model.rs
  • packages/wasm-dpp2/tests/unit/DocumentPropertyReference.spec.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 23, 2026
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-09-23T14:15:31.195Z

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 23, 2026
@thepastaclaw

thepastaclaw commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 8th in line, estimated start in ~50 min (commit 8ea1ce1)
Estimated review time once started: ~15 min (two-phase automated review; median of recent runs).

  • Request priority review — click to move this review to the front of the queue.

…ransfers, one enumeration

- only identity or a permanentDocument lookup: contract, token, a document by
  id (never the writer's identity id) and identityPublicKey are refused, by
  meta-schema v3 and on the stored path
- refused on a document type whose documents can be transferred or traded,
  so the writer stays the owner; owner lookups then use the property lookup
  rules unchanged (the $ownerId exemption is gone)
- parsed from the stored schema after the core parse, so the meta-schema
  reports a malformed declaration first; the gate is checked first
- DocumentTypeRef::reference_declarations (with ReferenceHolder) is the one
  enumeration of a type's references: reference count, lookup checks, both
  drive-abci validators and wasm-dpp2
- a replace re-validates the owner reference under its target's rules (a
  bound property changed, or a $ownerId agreement pair), not on every replace
- the ownerRefersTo compatibility rule is looked up without expect
- inert-before-14 comments on the in-place loops; js-evo-sdk README notes the
  $ownerId path; tests for no reads and the registration error paths

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or (PV14)

The counterpart of ownerRefersTo for document types whose documents can be
transferred or traded: one refersTo declaration whose value is the
document's $creatorId, which a transfer or a purchase never changes.

- only on a type that records creator ids (should_use_creator_id: a
  transferable or tradeable type of a format-1 contract), exactly where
  ownerRefersTo is refused, so a type declares at most one of the two
- the same meta-schema shape and parser (parse_doctype_reference, now shared
  by both keywords), the same two targets (identity, or a permanentDocument
  lookup with "." the creator); stored as DocumentTypeV2::creator_reference and
  yielded by reference_declarations as ReferenceHolder::Creator
- checked against the writer on a create and the stored creator on a replace,
  under the target's replace rules, never on a transfer or a purchase; errors
  name $creatorId (<documentType>.$creatorId at registration); an identity
  target reads nothing
- counts one against max_references_per_document and is frozen on update

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer changed the title feat(platform)!: ownerRefersTo, a reference on the document's writer (PV14) feat(platform)!: ownerRefersTo and creatorRefersTo, references on the document's writer and creator (PV14) Sep 23, 2026
QuantumExplorer and others added 3 commits September 23, 2026 20:57
# Conflicts:
#	book/src/data-model/documents.md
#	packages/rs-dpp/schema/meta_schemas/document/v3/document-meta.json
#	packages/rs-dpp/src/data_contract/document_type/class_methods/create_document_types_from_document_schemas/v1/mod.rs
#	packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs
#	packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/mod.rs
#	packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_reference_validation/v0/mod.rs
#	packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_common/data_contract_reference_validation/v0/mod.rs
#	packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/mod.rs
#	packages/rs-platform-version/src/version/v14.rs
#	packages/wasm-dpp2/tests/unit/DocumentPropertyReference.spec.ts
… and allOf

An anyOf of two lookups on the owner and an allOf on the creator parse, and a
leaf by id is refused at its expression path; through the full pipeline a
stepDownNotice writer is admitted through either operand of an anyOf (added
moderator or founder seat) and anyone else is refused with the last
operand's error at $ownerId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/rs-dpp/src/data_contract/document_type/property/mod.rs
@QuantumExplorer
QuantumExplorer merged commit 1143b4b into v4.2-dev Sep 23, 2026
42 of 43 checks passed
@QuantumExplorer
QuantumExplorer deleted the feat/owner-refers-to branch September 23, 2026 14:41
QuantumExplorer added a commit that referenced this pull request Sep 23, 2026
…fersTo

Merges ownerRefersTo/creatorRefersTo (#4941) and the shared is_transient
helper (#4949).

- ownerRefersTo and creatorRefersTo take a listElement as a target (alone or
  as a leaf of an expression): an identity id can be an element of a list of
  identities, the charters' "the writer is a seated member". Meta-schema,
  parser leaf check and docs updated; enforcement needed no change, the owner
  and creator references go through the same validator.
- The listElement registration checks walk reference_declarations(), so an
  owner- or creator-held list element is checked as a property's is.
- list_element_reference uses the shared is_transient; the changelog item is
  now 35, after ownerRefersTo's 34.
- Composing tests: dpp (ownerRefersTo listElement, alone and in an anyOf,
  with a bad $id pair refused) and ABCI (a seatedNote a member may write and
  a stranger may not, refused at $ownerId).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QuantumExplorer added a commit that referenced this pull request Sep 23, 2026
… the leader

Built on #4940 (listElement), #4941 (ownerRefersTo) and #4942 (anyOf),
merged in from v4.2-dev. The resignation request:

- may be filed only by a member of the seated team: ownerRefersTo holds
  an anyOf of a listElement (the writer is in the elected charter's
  members, the charter found through the electedCharterId $id pair) and
  a lookup of an addedModerator for the charter keyed by the writer; the
  leader is in neither list;
- carries a message encrypted to the leader, with the leader's
  decryption key bound to submittedCharter and the member's encryption
  key bound to joinRequest, the keys join requests already use;
- is deletable (Sam), so it is a request the leader acts on with a
  removal and the member withdraws by deleting it. It changes the team
  by itself no longer: ElectedCharter::active_members drops its
  resignations input.

The charter changelog item is renumbered 36 after the three new items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants