feat(platform)!: propertyConstraints, integer rules between document properties (PV14) - #4962
Conversation
…properties (PV14) A document type can now name rules its documents' integer properties must meet, where JSON Schema bounds one property at a time: each rule compares two integer expressions built from property paths, integer values, add, subtract, multiply, divide, modulo and power. A property the document leaves out counts as 0, or as the value of an ifAbsent operand naming it. Arithmetic is exact i128; divide and modulo are Euclidean; an overflow, a zero divisor, a negative exponent or a value that is not an integer refuses the document. Parser generation 3 reads the keyword on every parse (paths must name integer properties that are not transient, operands nest at most 64 deep) and holds the limits under full validation (16 rules per type, 32 nodes per rule). DataContract::validate_document_properties 0, extended in place and inert before protocol version 14, checks the rules after the schema validation, so document create and replace refuse a broken rule with DocumentPropertyConstraintViolatedError (10422) and clients validating a document see the same answer. The rules are frozen on contract update. The moderation charters contract holds a proposal's reward split to 100 with its first rule, rewardSplitIsWhole, so validate_submitted_charter no longer checks the split and error 11001 is no longer produced. With maxBytes holding the description's byte cap, the proposal step now reads a proposal and holds no rule of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 15 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 (49)
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-24T03:42:23.639Z |
|
🕓 Queued for automated review — 7th in line, estimated start in ~1.9 h (commit da2489c)
|
Issue being fixed or feature implemented
JSON Schema bounds one property at a time. A contract could not say "the deposit covers price plus fee times quantity" or "these three shares add up to 100", so rules like that lived in Rust hooks per contract (the moderation charters' reward split) or nowhere.
This adds
propertyConstraints, a document type keyword (protocol version 14) that names rules over a document's integer properties. Each rule compares two integer expressions built from property paths, integer values, arithmetic operators andifAbsent. Consensus checks every rule on each create and replace.What was done?
The keyword
equal,notEqual,lessThan,lessThanOrEqual,greaterThanorgreaterThanOrEqual, listing the left and the right expression.{ "ifAbsent": [path, value] },add/multiplyover two or more operands, orsubtract/divide/modulo/powerover exactly two.ifAbsentgives it another value.100is never confused with the number.Arithmetic (consensus rules)
i128, operands evaluated left to right. An intermediate result that does not fit, a divisor of 0, a negative exponent or a property value that is not an integer (a float with no fractional part passes the schema'sintegertype) refuses the document; nothing wraps.divideandmoduloare Euclidean: the remainder is never negative (-7by2is-4remainder1). For operands that are not negative this is ordinary integer division.0to the power0is1. Only integer properties can be read (no floats), so every node computes the same bits.Registration
Checked on every parse (parser generation 3,
parse_property_constraints0): the shape; every path names an integer property that is neither transient nor inside a transient object; every rule reads at least one property; no literal 0 divisor or negative exponent; no operand deeper thanMAX_PROPERTY_CONSTRAINT_PARSE_DEPTH(64, a constant far above any registrable rule, so a parse without full validation cannot recurse without bound). A literal written as a float with no fractional part (100.0) reads as that integer, since the meta-schema'sintegertype admits it. Under full validation, the limits: at most 16 rules per type (SystemLimits::max_property_constraints) and 32 nodes per rule (max_property_constraint_nodes). The meta-schema v3 grammar reports malformed shapes when a contract registers.Before (protocol version 14 without this change): the keyword is unknown and the meta-schema refuses the contract.
After:
Enforcement
DataContract::validate_document_properties0 callsvalidate_property_constraints(validate_property_constraints0) after the schema validation, so document create and replace (and an indexOnly delete, whose row carries every property) apply the rules, and so does every client that validates a document before sending it. Rules run in name order; the first broken one fails withDocumentPropertyConstraintViolatedError(basic, code 10422), naming the document type, the rule and why (NotMet,Overflow,DivisionByZero,NegativeExponent,NotAnInteger). The check reads no state and changes nothing stored, so it costs no fee.With the
depositCoversOrderrule above:Before this change the same contract could not be registered, so there is no earlier behaviour for these writes.
Updates
The rules are fixed when the document type is created:
propertyConstraintsjoins the frozen doctype rules of the v1 schema compatibility check, so adding, removing or changing a rule (an operator or a value inside one included) isIncompatibleDocumentTypeSchemaError.Moderation charters
The
submittedChartertype declares its first rule, so the reward split is held to 100 by consensus on every create:Before:
{ leader: 10, equal: 40, actions: 40 }was stored (onlyvalidate_submitted_charter, not yet wired into consensus, checked the sum). After: refused with 10422, rulerewardSplitIsWhole.#4957 shipped
maxBytes(code 10421) and droppedsumOfProperties; this rule takes its place for the split, with the next document code, 10422.validate_submitted_charterno longer checks the split, so the split has one definition: 11001 is no longer produced (theBasicErrorvariant keeps its place, the encoding being positional) andModerationCharterRewardSplit::totalis gone. WithmaxBytesholding the description's byte cap,SubmittedCharter::validatenow reads a proposal and holds no rule of its own; its versioned step stays for the path that seats a team.Clients
DocumentPropertyConstraintErrorCodeandConsensusError.documentPropertyConstraintErrorCode, re-exported by@dashevo/evo-sdk.data-model/documents.md, error codes), evo-sdk README and charter docs describe the keyword.In-place changes to shipped generations
DataContract::validate_document_properties0 (dpp), selected at every protocol version, gains thevalidate_property_constraintscall, next to themaxBytescall feat(platform)!: maxBytes, a UTF-8 byte cap on document strings (PV14) #4957 added the same way. Before protocol version 14validate_property_constraintsisNonein every method table, so the call returns an empty result without reading the document, and the function's result is the same as before. That gate is the proof: meta-schema v0 (protocol versions 1 to 11) leaves the document type level open, so the meta-schemas are not. Tested through replace structure validation 0 at protocol version 13 and 14. The drive-abci structure validations are unchanged.SystemLimitsV1 to V3 and the contract version tables V1 to V5 gain the new fields (limits backfilled, gatesNone). Only parser generation 3 reads the limits, and it is never reached before protocol version 14.meta_schema_v0_stray_keyword_testsholds the final census of stray keys on those contracts, andpropertyConstraintsis not one of them, so no stored contract changes meaning.How Has This Been Tested?
ifAbsentoperands, left fault first), generation 3 parser tests (both parse paths, integer and transient checks, limits under full validation only, meta-schema refusals, protocol version 13 refuses the keyword when registering and ignores it when reading, platform serialization round trip, update freeze), schema compatibility test, frozenBasicErrordiscriminant (199), charter rule test throughDataContract::validate_document.ifAbsent, a replace leaving out an operand the stored document had, division by zero, overflow, stored document untouched) and the replace structure gate at protocol version 13 and 14.cargo clippy --all-features --all-targets -D warningson dpp, platform-version and drive-abci; wasm32 checks of wasm-dpp and wasm-dpp2.Breaking Changes
Consensus: protocol version 14 admits a new document type keyword and refuses documents that break its rules, with a new basic error (10422). The moderation charters contract's
submittedChartertype gains a rule.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
PR Hygiene ·
da2489c/skip-botsproceeds without the ones not yet reported/self-reviewedonce the bots are donejs-wasm-sdk(packages/js-evo-sdk/README.md) — shumkovdpp— you own itrs-drive-abci— you own itWhen every box is checked the
PR Hygienecheck passes and this can merge.