Skip to content

feat(platform)!: present and absent tests in propertyConstraints rules (PV14) - #5037

Merged
QuantumExplorer merged 1 commit into
v4.2-devfrom
claude/property-constraints-presence
Sep 27, 2026
Merged

QuantumExplorer merged 1 commit into
v4.2-devfrom
claude/property-constraints-presence

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Basic explanation

What this does: A propertyConstraints rule can now test whether a document holds a property at all: { "present": "discount" } or { "absent": "discount" }. Until now a rule could only compare numbers, and a property the document leaves out read as 0. So "no discount given" and "a discount of 0" looked the same.

Value: Contract authors can write rules such as "a discount is optional, but when given it must be above 0" or "a closed order (status 3) must have a closedAt". A document schema can't say these today: dependentRequired only ties one property's presence to another's, and there is no JSON Schema if/then. The test works on properties of any type (strings, identifiers, objects), not only integers.

Risks: Low. This changes consensus validation, but only in protocol version 14, which is not on mainnet. It only widens what is accepted: every rule that registered before still registers and evaluates the same way. Devnets on an earlier 4.2 build would refuse a contract using the new tests, so their nodes need to upgrade together.

Issue being fixed or feature implemented

Follow-up to #4962 and #5036. An operand reads a missing property as 0 (or as its ifAbsent default), so a rule could not tell "left out" from "set to 0". The only workaround was an ifAbsent default outside the property's range, and that only works for integers.

What was done?

Grammar

Two new conditions sit next to the comparisons, and they nest under anyOf, allOf and not:

  • { "present": path } holds if the document holds the property with a value other than null.
  • { "absent": path } holds if the document leaves it out or sets it to null. That is exactly when an operand would read 0 or its ifAbsent value.

Before, this rule was refused when the contract registered (the meta-schema didn't know absent). Now it registers:

"discountGivenAboveZero": {
  "anyOf": [{ "absent": "discount" }, { "greaterThan": ["discount", 0] }]
}
Document Result
{ "price": 100 } (no discount) accepted
{ "price": 100, "discount": 10 } accepted
{ "price": 100, "discount": 0 } refused, 10422 discountGivenAboveZero NotMet

Written with an operand alone, { "greaterThan": ["discount", 0] } refuses the first document too, because the missing discount reads as 0.

What a presence test may name

  • Any property of the document type, of any type, including an object ("meta") or a member of one ("meta.tag"). An operand still needs an integer property.
  • Not a transient property, nor one inside a transient object, for the same reason as operands: the value is never stored.
  • Not a system property ($ownerId): the meta-schema refuses the $ on registration, and the parser refuses the path on a stored parse.
  • Each test counts as one node against max_property_constraint_nodes, and it never faults.

Code

  • property_constraints/mod.rs:
    • PropertyConstraint gains Present(String) and Absent(String), evaluated by is_present, which matches property_value's idea of absent.
    • A new PropertyRead (Value or Presence) and PropertyConstraint::property_reads() tell the two kinds of read apart. property_paths() still lists every path.
  • apply_property_constraints_v0: value reads keep the integer check. Presence reads resolve through the property tree with a new property_at_path, which walks objects. Both kinds get the transient check.
  • Meta-schema v3: present and absent on the propertyConstraint def, and the propertyConstraintPath and keyword descriptions updated.
  • Docs: v14 note 39, the book's Property Constraints section, the js-evo-sdk README, and the SystemLimits, DocumentTypeV2 and PROPERTY_CONSTRAINTS docs.

How Has This Been Tested?

  • property_constraints/tests.rs:
    • parsing and refusals, including a non-string path;
    • presence against absent, null, 0, a string, an object, a nested member and a non-object intermediate;
    • the optional-but-positive rule;
    • node count and read kinds;
    • repeated presence tests.
  • try_from_schema/v3/property_constraints_tests.rs: on both paths, presence of a string, a float, an array, an object and a nested member, refusal of a missing path and of a path through a non-object, refusal of a transient property or object, $ownerId refused, and the meta-schema refusing malformed shapes.
  • drive-abci batch/tests/document/property_constraints.rs: the offer fixture gains discountGivenAboveZero, and should_tell_a_property_left_out_from_one_set_to_zero runs real creates (one refused with 10422, two accepted).
  • cargo test -p dpp --lib (4851 passed), cargo test -p drive-abci --lib property_constraints (10 passed), cargo clippy -p dpp --all-features --all-targets -- -D warnings, cargo clippy -p drive-abci --all-targets -- -D warnings.

Breaking Changes

Consensus, protocol version 14 only: contracts may now register rules using present and absent, which earlier 4.2 builds refuse. Meta-schema v3, parse_property_constraints 0 and validate_property_constraints 0 were introduced in protocol version 14, which is not released on mainnet, so they are extended in place. Every declaration valid before parses and evaluates identically. Protocol version 13 and earlier ignore the keyword.

Rust API: PropertyConstraint gains two variants, so an exhaustive match on it needs two new arms.

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

  • 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.

…s (PV14)

A propertyConstraints condition may now be `{ "present": path }` or
`{ "absent": path }`: whether the document holds the property, the one
way to tell a property left out from one set to 0. A presence test may
name a property of any type, an object included, but not a transient
one; it counts as one node and never faults.

Extended in place in meta-schema v3 and parse_property_constraints 0:
every declaration valid before parses and evaluates as it did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 27, 2026
@coderabbitai

coderabbitai Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 9 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: 6f0e347c-9439-46fe-9b43-95536d410c59

📥 Commits

Reviewing files that changed from the base of the PR and between 8936d44 and c8ccb09.

📒 Files selected for processing (13)
  • 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/class_methods/try_from_schema/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/property_constraints_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/property_constraints/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/property_constraints/tests.rs
  • packages/rs-dpp/src/data_contract/document_type/v2/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/property_constraints.rs
  • packages/rs-platform-version/src/version/system_limits/mod.rs
  • packages/rs-platform-version/src/version/v14.rs
  • packages/wasm-dpp2/src/consensus_error.rs

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

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-27T07:00:50.160Z

@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

🔍 Review in progress — actively reviewing now (commit c8ccb09) · triage: normal

@QuantumExplorer
QuantumExplorer merged commit e8c4d1b into v4.2-dev Sep 27, 2026
38 of 40 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/property-constraints-presence branch September 27, 2026 07:56
QuantumExplorer added a commit that referenced this pull request Sep 27, 2026
#5037 squash-merged into v4.2-dev as e8c4d1b, whose tree is identical
to c8ccb09, the #5037 commit this branch is built on; every conflict
resolved to this branch's side, which is that commit plus this PR's own
changes. The merged tree equals 8c94d74's.

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