feat(platform)!: anyOf, allOf and not in propertyConstraints rules (PV14) - #5036
Conversation
…V14) A propertyConstraints rule is now a condition: a comparison of two integer expressions, as before, or anyOf / allOf over two or more conditions, or not over one, nesting freely. Conditions are checked in declared order and no further than the outcome needs, a fault in one that is checked refuses the document, and not never turns a fault into a pass, so an earlier condition guards a later one. 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>
|
Warning Review limit reachedNext included review available in 39 minutes. View limit detailsLimit 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. Review configuration: ⚙️ Run configurationConfiguration used: Repository: dashpay/platform/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (15)
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. Comment |
|
📖 Book Preview built successfully. Download the preview from the workflow artifacts. Updated at 2026-09-27T06:31:17.348Z |
|
🕓 Queued for automated review — 2nd in line, estimated start in ~10 min (commit 2310939)
|
…tration, refresh docs - Move the "no two alike conditions" check from every parse to full validation (PropertyConstraint::repeated_condition, called by apply_property_constraints_v0 next to the node limit), like refersTo operand uniqueness: the node limit bounds the lists it compares. - Test that a comparison reads a property without allocating a path list. - Tests: the condition-level depth guard, logical nodes against the node limit at registration, a transient read inside a nested condition, and repeated conditions on both parse paths. - Docs that still described every rule as a comparison: wasm-dpp2 10422 code, SystemLimits::max_property_constraint_nodes, DocumentTypeV2 field, PROPERTY_CONSTRAINTS, the drive-abci test module, v14 note, book. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rators - notIn's meta-schema description said what an anyOf of equals says; it now says an allOf of notEquals, a not over the in. - A not directly holding a notIn (an in of the same values, one node more) is refused like a not holding a not, by the parser and the meta-schema. - min and max fold from their identities, as add and multiply do: no allocation per evaluation and no stand-in error for an empty list. - abs given a list says it takes a single operand; a notIn over strings names itself in its operand error. - Docs: SystemLimits max_property_constraint_nodes, the PROPERTY_CONSTRAINTS constant, the DocumentTypeV2 field and PropertyConstraint::holds cover the operators added since #5036, and implies' evaluation order. - v14 note 39 is back to v4.2-dev's wrapping with only the touched lines reflowed, so its diff is the change. - Tests: notIn and implies over $ownerId and the update and transfer times answer to transfers and price updates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Basic explanation
What this does: A contract can already declare
propertyConstraints, named rules that a document's integer properties must meet, such asprice * quantity <= deposit. Until now a rule could only be a single comparison. This PR addsanyOf(or),allOf(and) andnot, so a rule can say things like "ais 0 orbis 4".Value: Contract authors can state either/or rules in the contract instead of in application code, and consensus enforces them on every create and replace. One workaround existed before:
a * (b - 4) == 0for "a is 0 or b is 4". It only works for equalities and reads poorly.Risks: Low. It changes consensus validation, but only in protocol version 14, which is not on mainnet. It also only widens what is accepted: every rule that registered before still registers and evaluates exactly as it did. Devnets running 4.2.0-beta.4 would refuse a contract that uses the new keywords, so their nodes need to upgrade together. The error message for a broken rule changes wording; the error code and its encoded data do not.
Issue being fixed or feature implemented
propertyConstraints(#4962) had no way to say "or". The request was to expressa == 0 || b == 4directly.What was done?
Grammar
A rule is now a condition, an object with one key:
anyOf: two or more conditions, at least one holds;allOf: two or more conditions, every one holds;not: one condition, which does not hold.Conditions nest freely. Before, the rule below was refused when the contract registered (the meta-schema took only the six comparison keys). Now it registers:
With
aIsZeroOrBIsFour, a document{ "a": 1, "b": 4 }is accepted.{ "a": 1, "b": 5 }is refused withDocumentPropertyConstraintViolatedError(10422), ruleaIsZeroOrBIsFour, violationNotMet.Evaluation order
anyOfat the first that holds,allOfat the first that fails.notnever turns a fault into a pass.||in most languages:{ "anyOf": [{ "equal": ["b", 0] }, { "equal": [{ "divide": ["a", "b"] }, 2] }] }For
{ "a": 6, "b": 0 }this holds without dividing. With the two conditions swapped, the same document is refused withDivisionByZero.Parse rules (every parse, stored contracts included)
anyOfandallOflist two or more conditions.anyOfmay not sit directly in ananyOf, and anallOfmay not sit directly in anallOf: one flat list says the same. This matchesrefersToexpressions. Anotmay not sit directly in anot.{ "equal": [1, 1] }inside ananyOfwould make the rule hold for every document.MAX_PROPERTY_CONSTRAINT_PARSE_DEPTH(64) now counts conditions and operands together.Registration rules (full validation, with the existing limits)
max_property_constraint_nodes(32) counts every comparison and logical operator.anyOforallOfmay list the same condition twice (PropertyConstraint::repeated_condition). Conditions are compared as they parse, so1and1.0are alike, and so are"price"and{ "ifAbsent": ["price", 0] }. The meta-schema'suniqueItemscatches identical JSON first. LikerefersTooperand uniqueness, this runs only under full validation, where the node limit bounds the lists it compares.rule "r" must be an object with one key, its comparison: .... After:rule "r" at anyOf[1] must be an object with one key: a comparison (equal, ...), anyOf, allOf or not, orrule "r" at allOf[1].not.lessThan[0].divide divides by 0.Code
property_constraints/mod.rs:PropertyConstraintchanges from a struct into an enum:Compare { comparison, left, right },AnyOf,AllOf,Not. It gainsholds()andrepeated_condition(), andviolation(),node_count()andproperty_paths()recurse through conditions.parse_rulebecomes the recursiveparse_conditionpluscondition_list.apply_property_constraints_v0(parser generation 3) refuses a repeated condition next to the node limit.propertyConstraintdef gainsanyOf/allOf(newpropertyConstraintConditions:minItems2,uniqueItems, no directly nested same operator) andnot(nonotdirectly inside). The keyword description is updated.PropertyConstraintViolation::NotMetmessage. Before:its two sides do not compare as it requires. After:it does not hold. The encoded error is unchanged.How Has This Been Tested?
property_constraints/tests.rs: parsing of all three operators, every new refusal with its error location, the nesting cap (both the operand and the condition guard), finding repeated conditions, short-circuit order and fault behaviour, thea == 0 || b == 4truth table, node counts and paths.try_from_schema/v3/property_constraints_tests.rs: the meta-schema accepts nested conditions on registration and refuses each malformed shape. Both the full-validation and stored-contract paths are covered. Also covered: a non-integer or transient property read deep inside a condition is refused, logical nodes count against the node limit, and a repeated condition is refused on registration only.batch/tests/document/property_constraints.rs: theofferfixture gainsfeeWaivedOrAtLeastTen(anyOf) andfeeWaivedOnlyWithDiscount(notoverallOf). The newshould_judge_any_of_all_of_and_notruns real create transitions: two refused with 10422, one accepted.cargo test -p dpp --lib(4846 passed),cargo test -p drive-abci --lib property_constraints(9 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
anyOf,allOfandnot, which earlier builds of 4.2 refuse. Meta-schema v3,parse_property_constraints0 andvalidate_property_constraints0 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 are unaffected: they ignore the keyword.Rust API:
PropertyConstraintis now an enum. Its old fields live in theComparevariant.Checklist:
structure.rs, regeneratedgrovedb-structure.json, and checked the structure viewer link posted on this pull requestFor repository code-owners and collaborators only
🤖 Generated with Claude Code