Skip to content

feat(sdk): propertyConstraints discovery and pre-check in the JS SDK - #5051

Merged
QuantumExplorer merged 2 commits into
v4.2-devfrom
claude/property-constraints-sdks
Sep 27, 2026
Merged

QuantumExplorer merged 2 commits into
v4.2-devfrom
claude/property-constraints-sdks

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Basic explanation

What this does: The JavaScript SDK (wasm-dpp2, re-exported by js-evo-sdk) can now list a document type's propertyConstraints rules and check a document against them before sending it. The check runs the same Rust code consensus runs, so every operator added in #5036 through #5048 works with no JS reimplementation.

Value: An app can tell a user "this offer breaks rule discountBelowPrice" before broadcasting, instead of paying for a transition consensus refuses with error 10422. It can also show which rules a type declares without hand-parsing the contract's raw JSON. TypeScript types spell out the full rule grammar.

Risks: Low. The change is additive: three new DataContract members and TypeScript types. Nothing changes in consensus, Drive or the existing JS API.

Issue being fixed or feature implemented

The propertyConstraints series (#4962, then #5036 through #5048) gave consensus a rich rule language, but the SDKs only exposed the 10422 error code. This is the first of three SDK PRs: JS/WASM here, with Swift (and the iOS example app) and Kotlin (and the Android example app) to follow, both calling the Rust evaluator rather than reimplementing it. It follows the pattern distinctFrom (#4917) and typed arrays (#4922) set for wasm-dpp2.

What was done?

New module wasm-dpp2/src/data_contract/document_type_property_constraints.rs, and three DataContract members:

// Every rule of one document type, in name order (the order consensus checks them)
contract.documentTypePropertyConstraints('offer');
// [{ name: 'discountBelowPrice',
//    rule: { lessThan: ['discount', 'price'] },            // as the schema declares it
//    reads: [{ path: 'discount', kind: 'value' }, { path: 'price', kind: 'value' }],
//    readsOwner: false },
//  { name: 'sellerIsOwner',
//    rule: { anyOf: [{ absent: 'sellerId' }, { equal: ['sellerId', '$ownerId'] }] },
//    reads: [{ path: 'sellerId', kind: 'presence' }, { path: 'sellerId', kind: 'identifier' }],
//    readsOwner: true }, ...]

// Every document type that declares rules
contract.documentPropertyConstraints;   // Map<string, DocumentPropertyConstraint[]>

// The first rule a Document breaks, as consensus would report it, or undefined
contract.checkDocumentPropertyConstraints(document);
// { rule: 'discountBelowPrice', violation: 'NotMet', message: 'it does not hold' }
  • checkDocumentPropertyConstraints: evaluates the rules with PropertyConstraint::violation(properties, Some(owner)), the function consensus calls on a create or replace. It takes a Document rather than a plain object so that integers, identifiers and the owner arrive typed as consensus sees them. It checks the rules only, not the JSON schema. It throws for a document of another contract or an unknown document type.
  • Reads: reads and readsOwner come from PropertyConstraint::property_reads and reads_owner. readsOwner tells an app that a transfer or purchase is judged against the rule too.
  • Large integers: a rule is reported as declared, with an integer literal as a number while it is exact in JavaScript and a bigint past Number.MAX_SAFE_INTEGER (a comparison, an ifAbsent default or an in value), so one large literal never hides a type's rules.
  • TypeScript types: PropertyConstraintExpression, PropertyConstraintEqualityOperand, PropertyConstraintCondition, PropertyConstraintReadKind, DocumentPropertyConstraint, PropertyConstraintViolationKind and DocumentPropertyConstraintViolation. They cover every operator: comparisons, arithmetic, ifAbsent (integer and string defaults), in (integers, strings, base58 identifiers), const, $ownerId, present / absent, and anyOf / allOf / not.
  • Older protocol versions: a contract read at a protocol version before 14 reports no rules and a clean check, which is what consensus enforced there.
  • js-evo-sdk README: the Property constraints section shows discovery and the pre-check.

How Has This Been Tested?

  • New wasm-dpp2/tests/unit/DocumentPropertyConstraints.spec.ts (11 tests):
    • listing rules in name order with the declared rule, reads and readsOwner (integer, string const, present, absent, identifier and $ownerId, arithmetic, in);
    • an empty list, an unknown type throwing, the per-type map, and a pre-v14 contract reporting none;
    • literals past Number.MAX_SAFE_INTEGER (comparison, negative ifAbsent default, in value) reported exactly as bigint by both discovery APIs;
    • the pre-check: a valid document, the first broken rule in name order (NotMet, DivisionByZero), $ownerId read from the document's owner, a type without rules, and a foreign-contract or unknown-type document throwing.
  • Built wasm-dpp2 (yarn workspace @dashevo/wasm-dpp2 build). The new spec passes (11), and the whole wasm-dpp2 mocha suite passes: 1338, 7 pending (karma/browser run left to CI).
  • cargo clippy -p wasm-dpp2 --all-targets -- -D warnings, native and --target wasm32-unknown-unknown, and eslint on the spec.

Breaking Changes

None. Additive JS API only.

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 · fab5826

  • Bots — coderabbitai skipped after its own rate limit · thepastaclaw ✓, 1 thread unresolved — resolve it
  • 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 failed
  • Approvals
    • js-wasm-sdk (packages/js-evo-sdk/README.md) — shumkov
    • files with no dedicated owner — you own it

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

wasm-dpp2's DataContract gains documentTypePropertyConstraints(name),
the documentPropertyConstraints map getter, and
checkDocumentPropertyConstraints(document): the first rule a Document
breaks, evaluated with the Rust code consensus runs (properties plus the
owner for $ownerId), or undefined. TypeScript types spell out the full
rule grammar (comparisons, arithmetic, ifAbsent, in, const, $ownerId,
present/absent, anyOf/allOf/not) and the violation kinds.

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

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 33 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: 8886b0da-6a7a-47de-bd30-d57ca3457e08

📥 Commits

Reviewing files that changed from the base of the PR and between 4d4f38d and 802b4df.

📒 Files selected for processing (5)
  • packages/js-evo-sdk/README.md
  • packages/wasm-dpp2/src/data_contract/document_type_property_constraints.rs
  • packages/wasm-dpp2/src/data_contract/mod.rs
  • packages/wasm-dpp2/src/data_contract/model.rs
  • packages/wasm-dpp2/tests/unit/DocumentPropertyConstraints.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 27, 2026
@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 27, 2026
@thepastaclaw

thepastaclaw commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit fab5826) · triage: normal

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Phase 1 + Phase 2

Verified the supplied findings against fab5826. One concrete discovery bug remains: valid large integer literals cause both new discovery APIs to throw, confirmed by source tracing and a runtime probe against the available WASM bundle. This is a non-consensus SDK correctness issue, classified as a suggestion under the project's severity policy; the other observations do not establish additional actionable defects.

🟡 1 suggestion(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: architecture-layering); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: ffi-engineer); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 6: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 7: gpt-6-astra (agent: phase2-reviewer, role: ffi-engineer); reviewer 8: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: normal by gpt-6-astra (effort low) — The additive SDK APIs introduce nontrivial Rust/WASM bindings, rule discovery, typed document pre-checking, and TypeScript grammar definitions across 648 lines, but reuse the existing evaluator without changing consensus rules or another critical surface.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — architecture-layering (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — ffi-engineer (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 13% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort high); agent phase2-reviewer, gpt-6-astra — architecture-layering (completed, effort high); agent phase2-reviewer, gpt-6-astra — ffi-engineer (completed, effort high); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort high); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-dpp2/src/data_contract/document_type_property_constraints.rs`:
- [SUGGESTION] packages/wasm-dpp2/src/data_contract/document_type_property_constraints.rs:214: Preserve large integer literals when serializing discovered rules
  A fully validated PV14 contract can declare `{ lessThan: ['price', 9007199254740993n] }`, and its document pre-check succeeds, but both `documentTypePropertyConstraints('offer')` and `documentPropertyConstraints` throw `9007199254740993 can't be represented as a JavaScript number`. The constructor accepts BigInt through `platform_value_from_object`, whereas this discovery path converts the declaration through `serde_json::Value` and `Serializer::json_compatible()`, which rejects integers outside JavaScript's safe range. Consequently, one valid literal prevents discovery of the entire document type or contract. Serialize rule literals losslessly, using bigint for integers outside the safe range, update the exported numeric TypeScript operands accordingly, and add regression coverage for both discovery APIs, including large comparison literals, defaults, and membership values.

Comment thread packages/wasm-dpp2/src/data_contract/document_type_property_constraints.rs Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Your move: thepastaclaw left review threads unresolved; resolve them.
Full checklist in the description.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. and removed waiting-bots Waiting for the review bots to report on this head labels Sep 27, 2026
…gint

Discovery converted each declared rule through serde_json and the
JSON-compatible serializer, which throws on an integer past
Number.MAX_SAFE_INTEGER, so one such literal (a comparison, an ifAbsent
default or an `in` value) made documentTypePropertyConstraints and
documentPropertyConstraints throw for the whole type or contract. Rules
now convert directly: an integer is a number while it is exact in
JavaScript and a bigint past that. The TypeScript operand types admit
bigint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer merged commit 1b30651 into v4.2-dev Sep 27, 2026
18 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/property-constraints-sdks branch September 27, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. waiting-self-review Waiting for the author to post /self-reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants