Skip to content

feat(sdk): read the identifier and bytes property type shorthands in long form on iOS and Android - #5358

Merged
QuantumExplorer merged 3 commits into
v5.0-devfrom
claude/mobile-expand-property-type-shorthands
Oct 9, 2026
Merged

QuantumExplorer merged 3 commits into
v5.0-devfrom
claude/mobile-expand-property-type-shorthands

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Basic explanation

What this does: Since #5355, a data contract may describe a field with a short spelling: "type": "identifier" for a 32-byte identity or document id, and "type": "bytes", "size": 20 for a fixed run of 20 bytes. Platform keeps the contract exactly as written, so the iOS and Android example apps received the short spelling and did not recognise it. This PR asks Rust to rewrite a fetched contract into the long spelling the apps already understand, and the apps read that rewritten copy. The contract they save stays as it was sent.

Value: Contracts using the new shorthands show the right input in the iOS and Android apps: an identifier field asks for a base58 id, a bytes field shows its exact byte count, and a list of identifiers gets the list editor. The meaning of the shorthands stays in one place (Rust), so Swift and Kotlin cannot drift from consensus.

Risks: Low. No consensus or Platform change: only the mobile SDKs and the FFI that serves them. Every contract the apps parse now makes one extra in-process round trip through Rust as JSON; if that ever fails, the apps log it and read the contract as sent, which is today's behaviour. Contracts without a shorthand come back with the same content. The new Android instrumented test was compiled but not run on a device.

Issue being fixed or feature implemented

Follow-up to #5355 (merged). Rust expands the shorthands through DocumentType::expand_property_type_shorthands / expand_schema_defs_property_type_shorthands, but the contract is stored and returned as sent, so the contract JSON the mobile SDKs fetch holds "type": "identifier" / "type": "bytes".

  • iOS: DataContractParser.parseProperties read type, byteArray, contentMediaType, minItems, maxItems straight from that JSON, so a shorthand property was persisted as type identifier or bytes with no byte array flag. DocumentFieldsView then fell back to a generic text box, and DocumentTypedArray did not recognise items: { "type": "identifier" }.
  • Android: ParsedContract (KotlinExampleApp) feeds the same raw schemas to CreateDocumentScreen, TypedArrays and DocumentTypeDetailsScreen, with the same result. (PropertyEntity is never populated, so it is not involved.)

dash_sdk_data_contract_get_schema (rs-sdk-ffi util.rs) also returns the schema as sent, but neither app calls it: both read the JSON returned by the fetch functions.

Why Rust expands, not the Swift and Kotlin parsers

  • packages/swift-sdk/CLAUDE.md and packages/kotlin-sdk/CLAUDE.md forbid re-implementing protocol rules on the mobile side. What a shorthand means (32 bytes, the identifier media type, that it is read on object members at any depth, typed array items and definitions but not inside enum or refersTo) is consensus logic DPP already versions.
  • One Rust function serves both apps; a parser mapping would be two more copies to keep in step with consensus.
  • It follows the propertyConstraints precedent (feat(platform)!: let property constraint rules read the bytes of byte arrays (PV14) #5336, feat(swift-sdk): decode the "bytes" propertyConstraints read kind #5339): the FFI derives a view from the contract with DPP's own code and the apps only marshal it.
  • The FFI calls the existing expansion functions; it validates nothing itself.

What was done?

  • rs-sdk-ffi: new dash_sdk_data_contract_json_expand_property_type_shorthands(contract_json) (data_contract/property_type_shorthands.rs). It takes a contract JSON and returns it with each document type schema (under documentSchemas or documents) passed through DocumentType::expand_property_type_shorthands and the definitions (under schemaDefs or $defs) through expand_schema_defs_property_type_shorthands. Everything else is passed through. It takes no SDK handle and runs at PlatformVersion::latest() without full validation: the contract was already accepted by the network, a shorthand can only be in a contract accepted at protocol version 14 or later, and needing no handle lets code that holds only JSON call it. dash_sdk_data_contract_get_schema keeps returning the schema as sent; its doc now says so and points at the new function.
  • swift-sdk: DataContractParser.parseDataContract first replaces contractData with the long-form view (expandingPropertyTypeShorthands(in:), which only marshals and falls back to the input on failure). schemaJSON, propertiesJSON, the PersistentProperty rows and typedArrays all read the long form, so DocumentFieldsView and DocumentTypeDetailsView needed no change. A top-level property written { "$ref": "#/$defs/<name>" } is now read from that definition, as DPP reads it; before, it was persisted as type unknown. No @Model stored property changed (doc comments only), so there is no schema version bump.
  • rs-unified-sdk-jni / kotlin-sdk: JNI export QueriesNative.dataContractJsonExpandPropertyTypeShorthands, public Contracts.expandPropertyTypeShorthands(contractJson) (static, no Sdk needed), and ParsedContract.from in KotlinExampleApp reads the expanded JSON. JVM unit tests, which never load the native library, fall back to the JSON as sent.
  • What stays as sent: PersistentDataContract.serializedContract / binarySerialization (iOS), DataContractEntity.serializedContract (Android, still shown raw on the contract details screen), and Rust's own stored contract.

Before / after

Contract fragment, fetched from Platform exactly as registered:

"documentSchemas": { "payment": { "properties": {
  "recipient": { "type": "identifier", "position": 0 },
  "txHash":    { "type": "bytes", "size": 20, "position": 1 },
  "cosigners": { "type": "array", "items": { "type": "identifier" }, "maxItems": 3, "position": 2 },
  "owner":     { "$ref": "#/$defs/owner", "position": 3 }
} } },
"schemaDefs": { "owner": { "type": "identifier" } }

Before, the iOS parser persisted:

recipient  type "identifier"  byteArray false  contentMediaType nil   -> generic text box
txHash     type "bytes"       byteArray false  minItems nil           -> generic text box
cosigners  typed array? no (items type "identifier" not recognised)  -> comma-separated text box
owner      type "unknown"                                             -> generic text box

After:

recipient  type "array"  byteArray true  minItems 32  maxItems 32  contentMediaType identifier -> base58 identifier field
txHash     type "array"  byteArray true  minItems 20  maxItems 20                              -> 20-byte hex field
cosigners  typed array of .identifier, up to 3                                                 -> list editor, base58 rows
owner      type "array"  byteArray true  minItems 32  maxItems 32  contentMediaType identifier -> base58 identifier field

The FFI call itself:

dash_sdk_data_contract_json_expand_property_type_shorthands(
  {"documentSchemas":{"payment":{"properties":{"txHash":{"type":"bytes","size":20,"position":1}}}}})
-> {"documentSchemas":{"payment":{"properties":{"txHash":{"type":"array","byteArray":true,"minItems":20,"maxItems":20,"position":1}}}}}

How Has This Been Tested?

  • cargo test -p rs-sdk-ffi --lib property_type_shorthands: 9 new tests pass (identifier and bytes at top level, object members, typed array items, schemaDefs, the documents / $defs keys, a contract without shorthands returned as given, every other value passed through, null / non-UTF-8 / non-object input refused). Re-run after rebasing onto refactor(dpp)!: keep the property type shorthand expansion out of shipped generations #5357.
  • cargo clippy -p rs-sdk-ffi --all-targets, cargo fmt --all -- --check: clean.
  • cargo test -p rs-unified-sdk-jni: 43 pass; cargo check -p rs-unified-sdk-jni after the rebase.
  • Swift (build_ios.sh --target sim --profile dev, iPhone 17 simulator): new DataContractParserPropertyTypeShorthandTests (12 tests: each shorthand as a top-level property, an object member, typed array items and a $ref definition, plus the contract's own documents / $defs keys, a long-form contract parsed exactly as before, the caller's contract left as sent, and the fallback for input that is not JSON) together with the existing DataContractParser*Tests, DocumentTypeImmutabilityTests and DocumentTypedArrayElementInputTests: 102 tests, 0 failures. freeze_schema_models.py --check passes. SwiftExampleApp builds.
  • Kotlin: :sdk:compileDebugKotlin, :sdk:compileDebugAndroidTestKotlin, :app:compileDebugKotlin pass; :app:testDebugUnitTest --tests 'org.dashfoundation.example.ui.contracts.*': 46 pass. New instrumented PropertyTypeShorthandsFfiTest (expansion through JNI at every location, a long-form contract unchanged, error code mapping) compiles; not run, no device or emulator in this session.

Breaking Changes

None. One new FFI and JNI function; existing functions are unchanged.

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 · 748d12d

  • Bots — coderabbitai not yet · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed
  • Reviewer requests paused — your 5 review slots are occupied; this PR is excluded from reviewers' queues. Required approvals still count without a slot.
  • Build running
  • Approvals
    • kotlin-sdk (packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/contracts/ContractJson.kt, packages/kotlin-sdk/sdk/src/androidTest/kotlin/org/dashfoundation/dashsdk/PropertyTypeShorthandsFfiTest.kt, packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/ffi/QueriesNative.kt and 1 more) — HashEngineering
    • rust-sdk-ffi (packages/rs-sdk-ffi/src/data_contract/mod.rs, packages/rs-sdk-ffi/src/data_contract/property_type_shorthands.rs, packages/rs-sdk-ffi/src/data_contract/util.rs) — lklimek or shumkov
    • files with no dedicated owner — you own it
    • swift-sdk (packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DataContractParser.swift, packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DocumentTypedArray.swift, packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentDocumentType.swift and 1 more) — llbartekll or romchornyi

When every merge requirement is met, the PR Hygiene check passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.

QuantumExplorer and others added 3 commits October 9, 2026 14:49
…long form

`dash_sdk_data_contract_json_expand_property_type_shorthands` takes a
contract as JSON and returns it with every `"type": "identifier"` and
`"type": "bytes", "size": n` (protocol version 14) written as the byte
array it stands for, in each document type schema and in the definitions,
by DPP's own `DocumentType::expand_property_type_shorthands`. Platform
stores and returns a contract as sent, so mobile clients reading property
types from the fetched JSON saw an unknown type. The function is a view:
the contract a client stores and sends is unchanged.

It takes no SDK handle and runs at the latest protocol version without
full validation, so a client holding only the JSON can call it. A
shorthand can only be in a contract the network accepted at protocol
version 14 or later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y type shorthands

`DataContractParser.parseDataContract` now reads the contract through the
Rust long-form view, so an `identifier` or `bytes` property (top level,
object member, typed array `items` or definition) is persisted as a byte
array row and schema, and SwiftExampleApp offers the identifier or byte
input for it. A top-level property written as `$ref` to `#/$defs/<name>`
is read from that definition, as DPP reads it. The caller-stored
`serializedContract` and `binarySerialization` keep the contract as sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y type shorthands

`Contracts.expandPropertyTypeShorthands` bridges the rs-sdk-ffi view
through a new JNI export, and KotlinExampleApp's `ParsedContract` reads
the document type schemas from it, so the create and detail screens treat
an `identifier` or `bytes` property as the byte array it stands for. The
stored contract JSON stays as sent; without the native library (JVM unit
tests) the view falls back to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v5.0.0 milestone Oct 9, 2026
@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next included review available in 26 minutes.

Check out review usage here.

View limit details

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: dashpay/platform/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0964b733-d07a-4c3f-b5a5-8f03663d1eb5

📥 Commits

Reviewing files that changed from the base of the PR and between be390fd and 748d12d.


📒 Files selected for processing (12)
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/contracts/ContractJson.kt
  • packages/kotlin-sdk/sdk/src/androidTest/kotlin/org/dashfoundation/dashsdk/PropertyTypeShorthandsFfiTest.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/ffi/QueriesNative.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/queries/PlatformQueries.kt
  • packages/rs-sdk-ffi/src/data_contract/mod.rs
  • packages/rs-sdk-ffi/src/data_contract/property_type_shorthands.rs
  • packages/rs-sdk-ffi/src/data_contract/util.rs
  • packages/rs-unified-sdk-jni/src/queries.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DataContractParser.swift
  • packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DocumentTypedArray.swift
  • packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentDocumentType.swift
  • packages/swift-sdk/SwiftTests/SwiftDashSDKTests/DataContractParserPropertyTypeShorthandTests.swift


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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 Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Oct 9, 2026
@QuantumExplorer
QuantumExplorer merged commit 3118b76 into v5.0-dev Oct 9, 2026
22 of 23 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/mobile-expand-property-type-shorthands branch October 9, 2026 12:54
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.

1 participant