Skip to content

fix(dpp)!: typed array review fixes: hyphenated list paths, element constraints, untrusted lists, Swift refusal - #4924

Merged
QuantumExplorer merged 5 commits into
v4.2-devfrom
fix/typed-array-review-fixes
Sep 22, 2026
Merged

QuantumExplorer merged 5 commits into
v4.2-devfrom
fix/typed-array-review-fixes

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Follow-up to #4922 (typed scalar arrays, PV14): the fixes from its post-merge review. The review found one functional defect on the client conversion path (settled by tightening the name rule instead, see below), two half-supported item keywords, a blind spot in ExtendedDocument::set_untrusted, an unverified compatibility claim, a duplicated helper, a test gap and a conventions nit.

Merged over #4923 (elements encoded as their scalar property type), #4887 and #4925: the element constraints below sit on the boxed scalar item_type, and random integer elements are produced in the element type's own value kind (a U8 element yields Value::U8), which is what the codec reads back. #4925's integer width guard and this branch touch the property module in different places and merge cleanly.

What was done?

Property and document type names are word characters only from protocol version 14

The review found that is_array_path in platform-value accepted only alphanumeric and _ in a list name while the meta-schema admitted - in a property name, so a hyphenated typed array (member-ids[]) never had its elements converted. Rather than teach the path syntax a character it was never written for, the name rule is tightened: meta-schema v3 refuses - in property names (top-level and nested, and in the property paths of refersTo declarations), and generation 3 of the document type parser refuses it in a document type name, both under full validation. A census of every data contract create and update transition on mainnet (72) and testnet (4593), decoded from the raw bytes with dpp, found no property or document type name carrying -, so no stored contract is affected and none is left unable to update. Stored contracts are read as they are; protocol version 13 keeps admitting the character.

"documentSchemas": {
  "journal-entry": {
    "type": "object",
    "properties": {
      "member-ids": { "type": "array", "maxItems": 4, "items": { "type": "array", "byteArray": true,
                       "minItems": 32, "maxItems": 32, "contentMediaType": "application/x.dash.dpp.identifier" } }
    },
    "additionalProperties": false
  }
}

Before: registered at protocol version 14; a document with "member-ids": ["9k3RE6kH..."] built from JSON kept the string as text (the conversion path member-ids[] was never recognized) and validation refused it.

After: registration at protocol version 14 refuses the contract, JsonSchemaError for the property name and InvalidDocumentTypeNameError for the type name; memberIds and member_ids register as before. At protocol version 13 both names are still admitted.

Item enum, minimum and maximum are parsed and honored

TypedArrayProperty gains item_constraints: ArrayItemConstraints { allowed_values, minimum, maximum }, read from the items schema by parse_typed_array v0 next to the scalar element parse, on both parse paths. Meta-schema v3's documentArrayItem gains matching member-type rules. (The scalar parse already sizes an integer element from these bounds; the constraints are what keeps a random U8 element under a maximum of 100.)

"tags":   { "type": "array", "maxItems": 3, "uniqueItems": true,
            "items": { "type": "string", "maxLength": 20, "enum": ["spam", "abuse", "offTopic"] } },
"scores": { "type": "array", "maxItems": 4,
            "items": { "type": "integer", "minimum": 0, "maximum": 100 } }

Before: random_document drew tags from random alphanumeric strings (["4Dcej73k5Ap5tkyTtz"]) and scores from the whole u8 range ([200, 17]), and validate_document refused both, so fixtures and strategy tests could not use such a type.

After: tags is drawn from the set (["abuse", "spam"], distinct under uniqueItems; the shortest member at the minimum fill, the longest at the maximum) and scores within the bounds ([57, 82], 0 at the minimum fill, 100 at the maximum), and the documents validate and round-trip.

Contract registration, on both parse paths, now also refuses schemas no element could satisfy:

{ "type": "integer", "enum": ["a"] }              // every enum member of a typed array's elements must be a integer value, found "a"
{ "type": "string",  "enum": [] }                 // the enum ... must hold at least one value
{ "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32,
  "contentMediaType": "application/x.dash.dpp.identifier", "enum": [[1]] }
                                                  // enum is not supported on byte array or identifier elements
{ "type": "integer", "minimum": 5, "maximum": 4 } // the minimum ... may not exceed their maximum
{ "type": "number",  "minimum": "low" }           // the minimum ... must be a number value, found "low"

Before: all five registered; the first four could never hold a document, and the last was ignored.

ExtendedDocument::set_untrusted converts the members of a list

extended_document.set_untrusted("reasons", platform_value!(["9k3RE6kH...", "2b994p95..."]))?;
extended_document.set_untrusted("digests", platform_value!(["AQIDBA=="]))?;

Before: reasons matched neither identifier_paths (which holds reasons[]) nor binary_paths, so both lists were stored as Value::Text elements and the document failed validation. The binary branch also read a scalar binary value with to_identifier_bytes, so a base64 string on a binary path was refused.

After: reasons holds Value::Identifier elements, digests holds Value::Bytes(vec![1, 2, 3, 4]), a non-list set at a list path is an error, and a scalar binary path reads base64 as binary.

One leaf replacement helper

Value::replace_at_path now uses the same replace_leaf as the map replacer.

value.replace_at_path("digests[]", ReplacementType::BinaryBytes)?;   // digests: [Bytes32([8; 32])]

Before: Bytes32([8; 32]) became Bytes(vec![8; 32]) on the value path and stayed Bytes32 on the map path, so a document converted through ExtendedDocument::from_untrusted_platform_value, which runs both, ended with whichever ran last.

After: Bytes32 on both paths.

uniqueItems on identifiers: census

The review flagged the PV14 refusal of uniqueItems on identifier properties as unverified against live contracts. A census of every data contract create and update transition on mainnet (72) and testnet (4593), pulled from the pshenmic explorers and decoded from the raw bytes with dpp, found no uniqueItems on any property of any contract, and items only on 22 testnet creates that were refused. The refusal stays; the census is recorded on the test that pins the rule.

wasm-dpp2 reports the element constraints

contract.documentTypeTypedArrays('charter')

Before: { path: 'scores', items: { type: 'integer' }, maxItems: 4, uniqueItems: false }

After: { path: 'scores', items: { type: 'integer', minimum: 0, maximum: 100 }, maxItems: 4, uniqueItems: false } and items: { type: 'string', minLength: 1, maxLength: 20, enum: ['spam', 'abuse', 'offTopic'] } for labels; the TypeScript types and the spec are updated.

Swift refuses typed arrays explicitly

try DataContractParser.parse(contractJSON, ...)   // a document type with the `member-ids` list above

Before: parseProperties persisted the property as a bare array row (no element type), so a later document with that property was decoded with the byte-array codec.

After: it throws DataContractParser.ParseError.unsupportedTypedArray(documentType: "charter", property: "member-ids") with the message "typed arrays (an array property declared by an items schema) are not supported by the Swift SDK yet". Three tests in DataContractParserTypedArrayTests.swift cover a string list, an identifier list and a plain byte array that still parses. Nested typed arrays are neither refused nor mis-parsed there, since the Swift parser keeps nested schemas as raw JSON. Kotlin needs nothing: the Kotlin SDK has no schema parser of its own, and the example app already treats an array without byteArray as a list of values.

Smaller

  • Conventions: the platform-value test helper imports Identifier and Encoding instead of spelling crate:: paths inline.
  • Docs: the typed arrays section of the documents chapter describes the element constraints and the name rule; the v14 changelog gains item 26 for the name rule and describes the constraints in item 25.

How Has This Been Tested?

New tests (all names start with should):

  • rs-dpp, name_rules_tests.rs: a hyphen in a top-level or nested property name is refused at protocol version 14 and admitted at 13 (JsonSchemaError), a hyphen in a document type name is refused at 14 and admitted at 13 (InvalidDocumentTypeNameError), the stored path reads both as they are, and word-character names stay admitted.
  • rs-dpp, typed_array_tests.rs: should_convert_the_elements_of_typed_arrays_when_creating_a_document_from_data now covers lists nested in an object (team.leads[], team.digests[]) and validates the result; should_convert_the_members_of_a_typed_array_set_on_an_extended_document (identifier and byte array lists through set_untrusted, a non-list refused); should_refuse_element_constraints_no_element_could_satisfy_on_both_paths (a wrong-typed or empty enum, an enum on an identifier element, a minimum above the maximum, a non-numeric minimum, on the validating and the stored path); the charter fixture gains an enum list, a bounded integer list and a bounded number list, so should_generate_random_documents_that_validate_against_their_own_schema now proves random elements stay inside enum, minimum and maximum at every fill size and round-trip in the element's own value kind; should_refuse_items_on_a_byte_array_and_unique_items_on_an_identifier carries the census.
  • rs-dpp, parse_typed_array/v0: should_parse_the_enum_minimum_and_maximum_of_the_elements, should_refuse_element_constraints_that_no_element_could_satisfy.
  • rs-platform-value: should_keep_the_fixed_size_kind_of_a_replaced_member.
  • swift-sdk: DataContractParserTypedArrayTests.swift (a string list and an identifier list refused with the new error and nothing persisted, a plain byte array still parses).

Run locally:

  • cargo test -p dpp --all-features --lib: 4600 passed after the merge of feat(dpp)!: encode typed array elements as their scalar property type (PV14) #4923; the typed array, parse, property, extended document and validate_update modules again after merging feat(platform)!: a document batch proof carries the owner's credit balance #4887 and fix(dpp)!: contract updates may not change an integer property's width or signedness (PV14) #4925 (632 passed)
  • cargo test -p platform-value --lib: 1053 passed
  • 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
  • yarn workspace @dashevo/wasm-dpp2 build then mocha tests/unit/DocumentTypedArrays.spec.ts: 6 passing
  • Swift: the SwiftDashSDK module type-checked and the parser test bundle run (44 tests, 3 new) in a scratch harness against an existing xcframework; this worktree has no DashSDKFFI.xcframework, so swift test and the SwiftExampleApp build were not run
  • The censuses (uniqueItems, and - in names): every contract create and update transition listed by the mainnet and testnet explorers (4665 rows, 0 decode failures) decoded with a throwaway rs-drive test that was removed afterwards

Breaking Changes

Protocol version 14 (unreleased) only: contract registration now refuses - in a property name (meta-schema v3) and in a document type name (parser generation 3), a character every earlier version admitted and no live contract uses; and it refuses, on a typed array's items, an enum with no member or with a member of another type, an enum on a byte array or identifier element, a non-numeric minimum / maximum on a number element, and a minimum above the maximum. Every such schema was unsatisfiable or contradictory before. TypedArrayProperty gains the item_constraints field.

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 · 88487cb

  • 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
    • dpp — you own it
    • swift-sdk (packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DataContractParser.swift, packages/swift-sdk/SwiftTests/SwiftDashSDKTests/DataContractParserTypedArrayTests.swift) — llbartekll or romchornyi

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

Summary by CodeRabbit

  • New Features
    • Typed arrays now support element-level enum, minimum, and maximum constraints. These bounds are also available through the JavaScript interface and are respected when generating sample values.
    • Values set on typed-array paths are converted element by element, including within nested lists.
  • Compatibility
    • From protocol version 14, document type and property names cannot contain hyphens. Earlier protocol versions retain their existing behavior.
    • The Swift SDK now reports an error when parsing typed arrays, which it does not support.

…onstraints, untrusted lists, Swift refusal

Follow-up to #4922 from its review.

- platform-value: `is_array_path` accepts `-` in a list name, as a document
  property name may carry it; `member-ids[]` was never converted before. One
  `replace_leaf` serves the Value and the map path replacers, so a `Bytes32`
  member replaced as binary bytes keeps its kind on both.
- `TypedArrayProperty::item_constraints` carries the `enum`, `minimum` and
  `maximum` of the items schema, parsed on both paths with the shape rules
  the meta-schema now states for elements (member types, no enum on byte
  array or identifier elements, minimum not above maximum); random document
  generation draws enum members and bounded numbers, so random documents
  validate against schemas with such elements.
- `ExtendedDocument::set_untrusted` converts the members of a list set at a
  `path[]` path, and reads binary values as binary.
- Census of every contract create and update transition on mainnet (72)
  and testnet (4593), decoded with dpp: no `uniqueItems` anywhere, so the
  PV14 refusal on identifiers stays; recorded on the test that pins it.
- wasm-dpp2 reports an element's `enum`, `minimum` and `maximum`.
- The Swift DataContractParser refuses a typed array explicitly instead of
  persisting it as a bare array.

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

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

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

Review profile: CHILL

Plan: Advanced

Run ID: 83e455cd-2074-4ecb-ae17-3bcdab3810ee

📥 Commits

Reviewing files that changed from the base of the PR and between a358ff7 and 057c391.

📒 Files selected for processing (17)
  • 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/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/v3/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/name_rules_tests.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/property/array.rs
  • packages/rs-dpp/src/data_contract/document_type/property/mod.rs
  • packages/rs-dpp/src/document/extended_document/v0/mod.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/v14.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DataContractParser.swift
  • packages/swift-sdk/SwiftTests/SwiftDashSDKTests/DataContractParserTypedArrayTests.swift
  • packages/wasm-dpp2/src/data_contract/document_type_typed_arrays.rs
  • packages/wasm-dpp2/tests/unit/DocumentTypedArrays.spec.ts
 _________________________________________________
< Undefined behavior is my favorite horror genre. >
 -------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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 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-22T23:29:31.236Z

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 22, 2026
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 22, 2026
@thepastaclaw

thepastaclaw commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

⚠️ DEGRADED — Queued for automated review — 27th in line, estimated start in ~27 h (commit 88487cb)
Estimated review time once started: ~2.0 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.

QuantumExplorer and others added 4 commits September 23, 2026 05:47
…pe) into fix/typed-array-review-fixes

The element constraints now sit on the boxed scalar item type and random
integer elements come back in the element type's own value kind.
…from PV14

Instead of teaching the path syntax the `-` it was never written for, the
name rule is tightened: meta-schema v3 refuses `-` in a property name (top
level, nested, and in the property paths of refersTo declarations), and
generation 3 of the document type parser refuses it in a document type
name, both under full validation. Every earlier version admitted the
character; a census of every data contract create and update transition on
mainnet (72) and testnet (4593) found no name carrying one, and neither
does any repository fixture, so nothing stored is affected. Stored
contracts are read as they are; protocol version 13 is unchanged.

The `is_array_path` widening is reverted accordingly. v14 changelog item
26, book note, and `name_rules_tests` pinning 14 refuses / 13 admits.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Keeps the distinctFrom changelog entry as item 26 and moves the
word-characters-only name rule to item 27.

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