Skip to content

feat(dpp)!: typed scalar arrays in document schemas (PV14) - #4920

Closed
QuantumExplorer wants to merge 3 commits into
v4.2-devfrom
feat/typed-scalar-arrays-pv14
Closed

QuantumExplorer wants to merge 3 commits into
v4.2-devfrom
feat/typed-scalar-arrays-pv14

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Document schemas could only declare one kind of type: array: a byte array (byteArray: true). A list of scalars (the reasons list of a submitted moderation charter, the members list of an elected one, any list of identifiers, integers or strings) had no spelling, and the codec's DocumentPropertyType::Array(ArrayItemType) variant existed without a parser to reach it.

What was done?

Protocol version 14 admits typed scalar arrays: an array property declared by an items schema instead of byteArray: true.

"reasons": {
  "type": "array",
  "minItems": 0,
  "maxItems": 64,
  "uniqueItems": true,
  "items": {
    "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32,
    "contentMediaType": "application/x.dash.dpp.identifier"
  },
  "position": 2
}

Storage is inline in the document: the property's value is a varint element count followed by the elements, each encoded by its ArrayItemType (integer, number and date 8 bytes; boolean 1; string, byte array and identifier length-prefixed). No per-element index entry, subtree or reference exists.

  • Parser (rs-dpp): a new versioned method, class_methods/parse_typed_array (parse_typed_array slot of DocumentTypeSchemaVersions: None in CONTRACT_VERSIONS_V1 to V5, Some(0) in V6), runs before the scalar parser in the property insert path and returns None for everything that is not a typed array. The scalar parser try_from_value_map is untouched, so below PV14 an array without byteArray still reaches its historical "only byte arrays are supported now" refusal byte-for-byte. Generation 0 reads items through ArrayItemType::try_from_item_schema, minItems / maxItems as element counts and uniqueItems. Refused on both paths with a clear error: arrays of objects, arrays of arrays, $ref or refersTo on the items, minItems above maxItems, and contentMediaType on the array. The item schema is parsed whole so a per-item refersTo can be added later.
  • Bound: registration (full validation, in the generation 3 driver) requires maxItems and refuses one above SystemLimits::max_typed_array_items (Some(1024) in SYSTEM_LIMITS_V4, None in V1 to V3 and the mock table). Full validation only, like the other registration limits, so a stored contract is never re-judged by a later, lower cap; max_items is therefore Option<u16> on the type and only absent on a contract parsed without validation.
  • Representation: DocumentPropertyType is append-only (CI diffs the block), so the bounds could not go on the existing Array(ArrayItemType) payload. A TypedArray(TypedArrayProperty { items, min_items, max_items, unique_items }) variant is appended and is the parsed form; Array stays as the never-produced variant it always was, documented as such, sharing the codec.
  • Codec arms: read_optionally_from mirrors the encoding (count, then elements, no allocation sized by the count); a byte array item whose bounds pin 20, 32 or 36 bytes reads back as Bytes20 / Bytes32 / Bytes36 exactly as a fixed-size scalar byte array does, an identifier item must be 32 bytes and a boolean item 0 or 1; min_byte_size / max_byte_size are the count prefix plus the bound times the item bound (saturating, u16::MAX for an unbounded item or bound); the three random generators fill within the bounds and respect uniqueItems (a bounded retry, so an unsatisfiable schema such as three unique booleans yields a shorter list rather than never returning); sanitize_value_mut sanitizes every element; tree-key encoding and value_from_string keep refusing arrays.
  • Document-type level: an index on a typed array is refused with InvalidIndexPropertyTypeError (Drive's query conditions give the type no operator; the ranked key-length check skips the type so that error is the one reported), an indexOnly entryPayload cannot name one, and the Drive query type check treats it like the other containers. drive-abci's contract reference validation refuses a propertyAgreement on a typed array on either side: the write-time check compares index key encodings, which a list does not have, so such an agreement would register and then fail every write. find_identifier_and_binary_paths gets a v1 (selected by CONTRACT_VERSIONS_V6) that registers identifier and byte array items as path[] conversion paths.
  • Meta-schema v3 (edited in place, unreleased generation): the "allow only byte arrays" rule becomes a oneOf between the byte-array form (unchanged, items refused) and the typed form (items and maxItems required, byteArray and contentMediaType refused). items is a new property keyword, allowed on type: array only (a dependent schema), bound to a typedArrayItemSchema definition: one scalar type with its bounds, additionalProperties: false, the same identifier / pattern / format dependent rules as a top-level property. uniqueItems stays admitted on both forms, as it was.
  • platform-value: Value::replace_at_path did not replace the members when the path ended in list[] (it walked into the list and returned), so a document built from JSON would never have converted base58 identifier items. The array component now replaces the members when it is the last one, an absent list is treated as an absent optional property, and the indexed form's inverted bounds check (which would have panicked with unwrap on an index past the end) returns an error.
  • wasm-dpp2: DataContract.documentTypeArrayProperties(name) and the documentArrayProperties getter expose the parsed lists with their item type and bounds (DocumentTypedArrayProperty, DocumentPropertyArrayItemType TypeScript types), mirroring the reference accessors, with a mocha spec.
  • Docs: v14 changelog item 25, a "Typed Scalar Arrays" section in the data contracts chapter of the book, and the array row of the document serialization table.

This revision takes from #4922 (the independent implementation of the same task) what it did better: the separate versioned parse method with the scalar parser untouched, the bound as a full-validation registration limit with an optional max_items, the propertyAgreement refusal with its fixture and test, the ranked-check skip, Bytes32 read-back and strict booleans, the items dependent rule, and the wasm-dpp2 spec. Kept from this branch: the path[] conversion paths and the platform-value fixes that make identifier lists in JSON documents convert (the document factory goes through identifier_paths, not the sanitizer), and uniqueItems staying admitted on byte arrays.

Not in this PR: the Swift and Kotlin contract parsers still read every type: array as a byte array; typed arrays reach those SDKs in a follow-up. packages/wasm-dpp embeds the meta-schema in its dist, so a JS spec there would need yarn workspace @dashevo/wasm-dpp build first; none is added.

How Has This Been Tested?

New rs-dpp tests (all names start with should):

  • try_from_schema/v3/typed_array_tests.rs (31 tests): parse a typed identifier array and a bounded integer array, every scalar item type with its bounds, byte arrays parse exactly as before; refuse arrays of objects, arrays of arrays, a typed array that is also a byte array, refersTo on the items, an index on a typed array property and a ranked index on one (both InvalidIndexPropertyTypeError); a missing maxItems and one over the cap are refused at registration and admitted on the stored path; every parser rule also holds on the stored path without the meta-schema; a document with typed arrays round-trips through serialize / from_bytes (including an empty list and an absent optional one); base58 identifier items are converted when a document is built from data; document validation accepts a list within the bounds and refuses one over maxItems, under minItems, with a repeated element under uniqueItems, with a wrong-typed element, and with an element outside the item bounds, each as the existing JsonSchemaError; random documents (any, min and max fill) validate against their own schema; byte sizes follow the bounds; the contract round-trips through serialize_to_bytes_with_platform_version / versioned_deserialize_untrusted; the contract is refused at protocol version 13 on both paths and accepted at PlatformVersion::latest().
  • class_methods/parse_typed_array: the dispatcher parses at 14 and leaves the property alone at 13; generation 0 leaves byte arrays and scalars to the scalar parser, parses optional bounds, and refuses a missing items, contentMediaType on the array, and minItems above maxItems.
  • property/array.rs and property/mod.rs: item schema parsing and refusals, every item type reads back what it encodes (fixed-size byte array items as Bytes20/32/36), short and wrong-sized items and a boolean other than 0 or 1 are refused, item sizes, random items within bounds, the typed array codec round trip, random lists under uniqueItems.
  • rs-platform-value: list[] and list[i] replacement at the value level and the map level, absent lists, out-of-range indexes.
  • rs-drive-abci: should_reject_agreement_on_typed_array_properties with a new reference-validation fixture.
  • wasm-dpp2: tests/unit/DocumentTypeArrayProperties.spec.ts (6 passing after yarn workspace @dashevo/wasm-dpp2 build).

Run locally:

  • cargo test -p dpp --all-features --lib: 4605 passed
  • cargo test -p platform-value (all unit tests pass; one pre-existing doctest in value_serialization/ser.rs needs the optional json feature), cargo test -p platform-version, cargo test -p drive -- query::conditions (207 passed), cargo test -p drive-abci --lib -- should_reject_agreement_on_typed_array_properties
  • cargo check -p drive -p drive-abci -p dash-platform-queries --all-targets, cargo check -p wasm-dpp2 --target wasm32-unknown-unknown, cargo check -p wasm-sdk --target wasm32-unknown-unknown
  • cargo clippy -p dpp -p drive -p drive-abci -p platform-value -p platform-version --all-targets --all-features -- -D warnings, cargo clippy -p wasm-dpp2 --target wasm32-unknown-unknown -- -D warnings, cargo fmt --all

Breaking Changes

Protocol version 14 (unreleased): a data contract may declare type: array with an items schema, which every earlier protocol version refuses, and drive-abci refuses a propertyAgreement on a typed array property. DocumentPropertyType gains the appended TypedArray variant, and DocumentTypeSchemaVersions and SystemLimits gain one field each.

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 · 266ef6a

  • 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
  • Build running
  • Approvals — you own every area touched; none needed

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

An array property may be declared by an `items` schema instead of
`byteArray: true`: a list of one scalar type (integer, number, string,
boolean, byte array or identifier) stored inline in the document as a
varint element count followed by the elements. `minItems` and `maxItems`
count elements, `maxItems` is required and capped by the new
`SystemLimits::max_typed_array_items` (1024), `uniqueItems` refuses a
repeated element; the JSON schema validator enforces them on every write.

The parsed form is the appended `DocumentPropertyType::TypedArray`
variant (the enum is append-only, so the bounds could not go on the
never-produced `Array` payload), gated by the new `parse_typed_array`
schema version slot (`Some(0)` in CONTRACT_VERSIONS_V6). Arrays of
objects, arrays of arrays, `refersTo` on the items and an index on an
array property are refused at registration. `find_identifier_and_binary_paths`
v1 registers identifier and byte array items as `path[]` conversion paths,
and platform-value's path replacement now understands a trailing `list[]`
(and no longer panics on an out-of-range `list[i]`).

Meta-schema v3 is edited in place: the byte-array-only rule becomes a
`oneOf` over the byte array and typed forms, with `items` bound to a
scalar item definition. wasm-dpp2 exposes the parsed lists through
`documentTypeArrayProperties` and `documentArrayProperties`. Swift and
Kotlin parsers still read every `type: array` as bytes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 1 minute.

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: 4bd65c38-6c91-4948-8e2b-ac49fa83f78a

📥 Commits

Reviewing files that changed from the base of the PR and between 8a0befd and 266ef6a.

📒 Files selected for processing (40)
  • book/src/data-model/data-contracts.md
  • book/src/serialization/document-serialization.md
  • packages/rs-dpp/schema/meta_schemas/document/v3/document-meta.json
  • packages/rs-dpp/src/data_contract/document_type/class_methods/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/parse_typed_array/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/parse_typed_array/v0/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/common/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/typed_array_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/property/array.rs
  • packages/rs-dpp/src/data_contract/document_type/property/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/schema/find_identifier_and_binary_paths/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/schema/find_identifier_and_binary_paths/v1/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/v0/random_document_type.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-agreement-typed-array.json
  • packages/rs-drive/src/query/conditions.rs
  • packages/rs-platform-value/src/btreemap_extensions/btreemap_field_replacement.rs
  • packages/rs-platform-value/src/replace.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v5.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v6.rs
  • packages/rs-platform-version/src/version/mocks/v2_test.rs
  • packages/rs-platform-version/src/version/system_limits/mod.rs
  • packages/rs-platform-version/src/version/system_limits/v1.rs
  • packages/rs-platform-version/src/version/system_limits/v2.rs
  • packages/rs-platform-version/src/version/system_limits/v3.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_array.rs
  • packages/wasm-dpp2/src/data_contract/mod.rs
  • packages/wasm-dpp2/src/data_contract/model.rs
  • packages/wasm-dpp2/tests/unit/DocumentTypeArrayProperties.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 22, 2026
@github-actions

github-actions Bot commented Sep 22, 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-22T20:32:42.228Z

@thepastaclaw

thepastaclaw commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

⚠️ DEGRADED — Queued for automated review — 31st in line, estimated start in ~36 h (commit 266ef6a)
Estimated review time once started: ~2.3 h (two-phase automated review; median of recent runs).
The primary review models are currently out of quota; this review will run on stand-in models and be marked as degraded.

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

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 22, 2026
QuantumExplorer added a commit that referenced this pull request Sep 22, 2026
…y parse

Takes the parts #4920 did better:

- find_identifier_and_binary_paths 1 (selected at protocol version 14)
  registers a typed array's identifier and byte array elements as `path[]`
  conversion paths, so a document built from JSON or a value map converts
  every element. platform-value's path helpers now make a trailing `list[]`
  or `list[i]` name the members to replace, treat an absent list as nothing
  to replace, and return an error instead of reaching the `unwrap` behind an
  inverted bounds check on `list[i]`; the BTreeMap helper learns the `list[]`
  syntax it never parsed. Drive and drive-abci never call these helpers.
- TypedArrayProperty::max_items is a required u16: the parse refuses a
  typed array without maxItems, with minItems above it or with
  contentMediaType on the array, on every path. The 1024 cap stays a
  registration limit under full validation.
- Element bounds are covered at document validation, and the meta-schema
  header and the serialization chapter mention typed arrays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…istration, agreement refused (#4922 port)

Takes from the independent implementation in #4922 what it did better:

- `class_methods/parse_typed_array` is its own versioned method run before
  the scalar parser, whose signature and "array" arm are restored
  byte-for-byte; below protocol version 14 the historical refusal is reached
  exactly as before.
- The `maxItems` requirement and its `SystemLimits::max_typed_array_items`
  cap move to the generation 3 driver under full validation, like the other
  registration limits, so a later, lower cap can never make a stored
  contract unreadable; `TypedArrayProperty::max_items` is `Option<u16>`.
- drive-abci refuses a `propertyAgreement` on a typed array on either side
  (fixture + test): the write-time check compares index key encodings, which
  a list does not have.
- The ranked key-length check skips typed arrays so the type error is the
  one reported; a test pins it.
- Byte array items whose bounds pin 20, 32 or 36 bytes read back as
  `Bytes20/32/36`, boolean items must be 0 or 1, item bounds are read at u16
  like the scalar bounds.
- Meta-schema v3: `items` is allowed on `type: array` only.
- wasm-dpp2: `maxItems` is optional on the surface; a mocha spec covers the
  accessors.

Merges v4.2-dev (#4915).

Co-Authored-By: Claude Fable 5.1 <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