Skip to content

fix(dpp): add additionalProperties: false to document meta-schema#3475

Merged
QuantumExplorer merged 11 commits intov3.1-devfrom
fix/document-schema-additional-properties
Apr 12, 2026
Merged

fix(dpp): add additionalProperties: false to document meta-schema#3475
QuantumExplorer merged 11 commits intov3.1-devfrom
fix/document-schema-additional-properties

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer commented Apr 10, 2026

Summary

Introduces a versioned document meta-schema and a v12 migration to prevent pre-v12 contracts from carrying unknown top-level properties that could change storage semantics after a protocol upgrade.

Motivation

A scan of all 46 data contracts currently deployed on mainnet confirmed that at least one contract carries an unknown top-level property ("mutable" on a document type schema, which should be "documentsMutable"). Because the v0 document meta-schema did not enforce additionalProperties: false, these invalid keys were silently accepted and persisted. If left in place, future protocol versions that read new schema keys (e.g. documentsCountable, rangeCountable from PR #3457) could misinterpret properties that were never intended by the contract author, leading to storage-semantic mismatches such as creating CountTree structures where NormalTree was expected.

Approach

Version Protocol Meta-schema Behavior
v0 ≤ 11 document/v0/document-meta.json Allows any top-level properties (unchanged)
v1 ≥ 12 document/v1/document-meta.json additionalProperties: false — rejects unknown keys

v1 meta-schema additions

Standard JSON Schema keywords added to the allowed list:

  • required, $comment, description
  • minProperties, maxProperties
  • dependentRequired

Note: documentsCountable and rangeCountable are NOT added here — they come in PR #3457.

v12 Migration

On activation of protocol version 12, transition_to_version_12 now:

  1. Queries all data contract IDs from the DataContractDocuments root tree
  2. For each contract, deserializes the raw bytes
  3. Iterates each document type schema, stripping any top-level key not in the allowed set
  4. Re-serializes and writes back only contracts that were modified
  5. Clears the data contract cache so subsequent fetches reload the cleaned contracts
  6. Handles both non-historical (Item) and historical (Tree+Reference) contract storage

The migration logic lives as a method on Drive (strip_unknown_document_schema_properties) in rs-drive, called by the drive-abci protocol upgrade handler.

Script: check-contract-properties

A new rs-scripts binary (check-contract-properties) was added to verify contracts on any network. It:

  1. Fetches all contract IDs from the Platform Explorer API
  2. Discovers DAPI node addresses from the explorer's validator list
  3. Fetches each contract via gRPC and checks for unknown top-level properties

Usage: cargo run -p rs-scripts --bin check-contract-properties

Changes

  • New: schema/meta_schemas/document/v1/document-meta.json
  • New: DOCUMENT_META_SCHEMA_V1 compiled validator
  • New: document_type_schema version field in DocumentTypeSchemaVersions
  • New: Drive::strip_unknown_document_schema_properties migration method in rs-drive
  • New: document_schemas_mut() accessor on DataContractInSerializationFormat
  • New: check-contract-properties script in rs-scripts
  • CONTRACT_VERSIONS_V4 sets document_type_schema: 1
  • try_from_schema v0/v1 dispatch to correct validator based on version

Test plan

  • v0_schema_allows_unknown_properties — pre-v12 behavior preserved
  • v1_schema_rejects_unknown_properties — v12+ rejects unknown keys
  • v1_schema_accepts_known_properties — standard keys still pass
  • test_v12_migration_strips_unknown_document_schema_properties — non-historical contracts
  • test_v12_migration_strips_unknown_properties_from_historical_contract — historical contracts with cache verification
  • Verified against mainnet: 45/46 contracts clean, 1 contract has "mutable" (stripped by migration)
  • All dpp tests pass
  • cargo check -p drive-abci clean
  • cargo fmt --all clean
  • CI

🤖 Generated with Claude Code

Locks down the document type meta-schema to reject unknown top-level
properties. This prevents pre-v12 contracts from smuggling in
v12-only keys (like documentsCountable/rangeCountable) that would
change storage semantics after a protocol upgrade.

Added missing standard JSON Schema properties to the allowed list:
- required, $comment, description
- minProperties, maxProperties, dependentRequired

TODO: On v12 activation, consider scanning existing contracts in
state and stripping any unknown top-level keys from document schemas
as a safety migration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added this to the v3.1.0 milestone Apr 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a v1 document meta-schema and compiles it, selects meta-schema version during document-type validation, extends platform/contract versioning to expose the schema feature flag (introducing CONTRACT_VERSIONS_V4), exposes a mutable accessor for document_schemas, and adds a protocol-upgrade migration that strips unknown top-level schema properties from stored contracts.

Changes

Cohort / File(s) Summary
New Document Meta Schema
packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json
Added v1 JSON Schema (draft-2020-12) that enforces additionalProperties: false at top level, defines reusable $defs (documentSchema, documentProperties, documentSchemaArray), constrains property names/counts, media/type-specific rules, and action token cost structures.
Schema Compilation & Export
packages/rs-dpp/src/validation/meta_validators/mod.rs
Embedded DOCUMENT_META_JSON_V1 and exposed compiled DOCUMENT_META_SCHEMA_V1 using existing compilation settings and custom byteArray keyword.
Validation Version Selection
packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs, packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs
Select meta-schema based on platform_version.dpp.contract_versions.document_type_versions.schema.document_type_schema (0 → v0, 1 → v1); return UnknownVersionMismatch for other values; added unit tests for v1 behavior.
Allowed Top-Level Properties & Stripping
packages/rs-dpp/src/data_contract/document_type/schema/allowed_top_level_properties.rs, packages/rs-dpp/src/data_contract/document_type/schema/mod.rs
Added allowlist ALLOWED_DOCUMENT_SCHEMA_V1_PROPERTIES and strip_unknown_properties_from_document_schema(schema: &mut Value) -> bool that removes unknown string keys from map-valued schemas; unit tests added.
DataContract Serialization Accessor
packages/rs-dpp/src/data_contract/serialized_version/mod.rs
Added document_schemas_mut(&mut self) -> &mut BTreeMap<DocumentName, Value> to permit in-place mutation of document_schemas.
Protocol Upgrade Migration
packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs
During transition_to_version_12, full-state pass over DataContractDocuments reads contracts (handles historical and current layouts), deserializes, strips unknown top-level document schema properties, and re-writes only modified contracts; errors are logged and skipped.
Platform / Contract Versioning Surface
packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs, .../v1.rs, .../v2.rs, .../v3.rs, .../v4.rs, packages/rs-platform-version/src/version/v12.rs
Added document_type_schema: FeatureVersion to DocumentTypeSchemaVersions, added v4 module and CONTRACT_VERSIONS_V4 (sets document_type_schema = 1), set document_type_schema: 0 in v1–v3 constants, and switched PLATFORM_V12 to CONTRACT_VERSIONS_V4.

Sequence Diagram(s)

sequenceDiagram
    participant Upgrade as PlatformUpgrade
    participant DB as GroveDB
    participant Reader as ContractReader
    participant Deser as Deserializer
    participant Strip as SchemaStripper
    participant Writer as ContractWriter

    Upgrade->>DB: iterate DataContractDocuments entries
    DB->>Reader: fetch element bytes
    Reader->>Deser: deserialize to DataContractInSerializationFormat
    alt deserialization succeeds
        Deser->>Strip: get & iterate document_schemas
        Strip->>Strip: strip_unknown_properties_from_document_schema(...)
        alt schema modified
            Strip->>Writer: serialize updated contract
            Writer->>DB: write back updated bincode value
        end
    else deserialization fails
        Reader->>Upgrade: log & skip entry
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • chore: clean dpp clippy #2764 — Modifies DocumentType::try_from_schema implementations in rs-dpp; likely overlaps with meta-schema selection and validation changes.

Poem

🐰 I nibbled through schemas, v0 to v1 bright,
I stripped unknown leaves by the pale moonlight,
Contracts now tidy, indices in queue—
Hop, hop, patch applied, a clean-schema view!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: introducing additionalProperties: false to the document meta-schema v1, which is the core objective of this PR addressing unknown top-level properties in document schemas.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/document-schema-additional-properties

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 and usage tips.

@thepastaclaw
Copy link
Copy Markdown
Collaborator

thepastaclaw commented Apr 10, 2026

✅ Review complete (commit f0ccff7)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json`:
- Around line 627-628: The inline "dependentRequired" definition is too
permissive; replace it with the Draft 2020-12 validation meta-schema for
dependentRequired so values are enforced as arrays of strings (or explicitly set
"type":"object" plus "additionalProperties":
{"type":"array","items":{"type":"string"}}). Locate the "dependentRequired"
property in the document-meta.json and either change its value to a $ref that
points to the Draft 2020-12 dependentRequired meta-schema or update the object
to include "type":"object" and "additionalProperties":
{"type":"array","items":{"type":"string"}} to match the rest of the file's
meta-schema pattern. Ensure the new definition mirrors other properties' use of
Draft 2020-12 vocabularies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 36caaa25-3498-49eb-9c07-f7e9c6ae155c

📥 Commits

Reviewing files that changed from the base of the PR and between bb4c91d and 582af94.

📒 Files selected for processing (1)
  • packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json

Comment thread packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json Outdated
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

This PR correctly adds additionalProperties: false to the document-type meta-schema, closing a property-smuggling vector. The allowlist of newly-defined top-level properties (required, $comment, description, minProperties, maxProperties, dependentRequired) is appropriate for enabling the constraint without breaking existing schemas. One minor schema consistency issue and a test-coverage gap remain.

Reviewed commit: 582af94

🟡 2 suggestion(s)

🤖 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/rs-dpp/schema/meta_schemas/document/v0/document-meta.json`:
- [SUGGESTION] lines 627-629: Top-level `dependentRequired` definition is weaker than the inner one
  The `dependentRequired` added at line 627 only validates `"type": "object"`, while the equivalent definition inside `documentSchema` (line 112-117) also enforces `minProperties: 1` and constrains each value to be a string array via `$ref: ".../$defs/stringArray"`. Per JSON Schema spec, `dependentRequired` values must be arrays of property-name strings. With the current top-level definition, a document type could pass meta-schema validation with `"dependentRequired": {"foo": 42}`, which would cause unpredictable behavior at runtime.
- [SUGGESTION] line 637: No test verifying that unknown top-level properties are now rejected
  The PR's stated purpose is to block property smuggling (e.g., a pre-v12 contract including `documentsCountable` that would later be honored). While 2451 existing tests pass (proving no regressions), none explicitly submits a document type schema with an unknown top-level property and asserts rejection. A targeted test like `{"type": "object", ..., "unknownProp": true}` would directly verify the security fix and guard against accidental removal of the `additionalProperties: false` constraint.

Comment thread packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json Outdated
Comment thread packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json Outdated
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 11, 2026

Codecov Report

❌ Patch coverage is 90.94737% with 86 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.79%. Comparing base (0fa0d5c) to head (f0ccff7).
⚠️ Report is 1 commits behind head on v3.1-dev.

Files with missing lines Patch % Lines
...ration/strip_unknown_document_schema_properties.rs 75.18% 34 Missing ⚠️
...events_on_first_block_of_protocol_change/v0/mod.rs 96.83% 17 Missing ⚠️
...ument_type/class_methods/try_from_schema/v1/mod.rs 87.37% 13 Missing ⚠️
...ment_type/schema/enrich_with_base_schema/v1/mod.rs 80.00% 8 Missing ⚠️
...ument_type/class_methods/try_from_schema/v0/mod.rs 56.25% 7 Missing ⚠️
...cument_type/schema/allowed_top_level_properties.rs 95.71% 3 Missing ⚠️
...ocument_type/schema/enrich_with_base_schema/mod.rs 92.68% 3 Missing ⚠️
...rs-dpp/src/data_contract/serialized_version/mod.rs 80.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v3.1-dev    #3475      +/-   ##
============================================
+ Coverage     84.77%   84.79%   +0.02%     
============================================
  Files          2473     2476       +3     
  Lines        266762   267709     +947     
============================================
+ Hits         226139   227000     +861     
- Misses        40623    40709      +86     
Components Coverage Δ
dpp 81.82% <87.31%> (+0.02%) ⬆️
drive 84.21% <75.18%> (-0.02%) ⬇️
drive-abci 87.46% <96.83%> (+0.06%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.10% <ø> (ø)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 55.66% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

The new meta-schema and regression test harden validation for newly checked schemas, but the PR still does not mitigate the stated pre-upgrade smuggling case for contracts already persisted in state.

Reviewed commit: 831d973

🔴 1 blocking

🤖 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/rs-dpp/schema/meta_schemas/document/v0/document-meta.json`:
- [BLOCKING] lines 627-644: Persisted contracts still bypass the new top-level schema-key restriction
  This closes the hole only for schemas that go through full meta-schema validation. `DocumentType::try_from_schema()` applies `DOCUMENT_META_SCHEMA_V0` only inside `if full_validation` (`packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs:94-140`), but contracts loaded from state are still deserialized with `DataContract::versioned_deserialize(..., false, ...)` in Drive (`packages/rs-drive/src/drive/contract/get_fetch/fetch_contract/v0/mod.rs:81-84`), and both serialization paths pass that `full_validation = false` through when rebuilding document types (`packages/rs-dpp/src/data_contract/v0/serialization/mod.rs:94-105`, `packages/rs-dpp/src/data_contract/v1/serialization/mod.rs:94-105`). So any contract that already smuggled an unknown top-level document-schema key before upgrade will continue to load unchanged after this patch, which is exactly the threat model described in the PR. Please add a load-time rejection/migration path for persisted contracts, or this fix remains incomplete for the targeted security case.

Comment thread packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json Outdated
@QuantumExplorer QuantumExplorer force-pushed the fix/document-schema-additional-properties branch from 831d973 to f9b8ab6 Compare April 11, 2026 13:57
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/rs-dpp/src/validation/meta_validators/mod.rs (1)

148-199: Consider extracting a shared document meta-schema builder.

DOCUMENT_META_SCHEMA_V0 and DOCUMENT_META_SCHEMA_V1 are almost identical; centralizing the common options/docs setup will reduce drift and make future changes safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-dpp/src/validation/meta_validators/mod.rs` around lines 148 -
199, DOCUMENT_META_SCHEMA_V0 and DOCUMENT_META_SCHEMA_V1 duplicate the same
JSONSchema::options() setup; extract the repeated chain into a shared builder
function (e.g., build_document_meta_schema_options or document_meta_schema_base)
that configures the common options and .with_document entries (including the
regex engine, Draft, and all DRAFT202012_* documents) and returns the configured
builder or options object; then replace the duplicated chains in the constants
(DOCUMENT_META_SCHEMA_V0 and DOCUMENT_META_SCHEMA_V1) by calling that shared
builder and only applying schema-specific tweaks (like
.compile(&DOCUMENT_META_JSON_V1) / .compile(&DOCUMENT_META_JSON_V0) or any
version-specific overrides) to avoid drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json`:
- Around line 343-346: The "$schema" property in the v1 document meta-schema
currently requires the v0 URI; update the const value for the "$schema" schema
node to the v1 URI
"https://github.com/dashpay/platform/blob/master/packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json"
so the meta-schema's own "$schema" check matches its "$id" (i.e., change the
const on the "$schema" property in the document-meta.json from the v0 path to
the v1 path).

In
`@packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs`:
- Around line 776-805: The test currently uses PlatformVersion::latest(), which
is unstable for version-sensitive assertions; replace that call with an
explicit, fixed PlatformVersion instance (e.g., construct the concrete version
used by these tests — PlatformVersion::new(<concrete_major_or_index>) or the
same explicit platform version constant used elsewhere in tests) and use that
value when calling DataContractConfig::default_for_version(...) and
DocumentTypeV1::try_from_schema(...). Update both occurrences noted (around the
first PlatformVersion::latest() and the other block at 824-854) so the
assertions are pinned to a specific platform version rather than latest().

In `@packages/rs-dpp/src/validation/meta_validators/mod.rs`:
- Around line 153-160: The duplicate builder call to with_patterns_regex_engine
overwrites the earlier size-limited RegexEngine configuration and removes the 5
MB size_limit; remove the second
with_patterns_regex_engine(RegexEngine::Regex(Default::default())) so the
RegexOptions with size_limit (set via RegexEngine::Regex(RegexOptions {
size_limit: Some(5 * (1 << 20)), ..Default::default() })) remains effective,
retaining the ReDoS mitigation while leaving the other builder calls
(should_ignore_unknown_formats, should_validate_formats, with_draft) unchanged.

---

Nitpick comments:
In `@packages/rs-dpp/src/validation/meta_validators/mod.rs`:
- Around line 148-199: DOCUMENT_META_SCHEMA_V0 and DOCUMENT_META_SCHEMA_V1
duplicate the same JSONSchema::options() setup; extract the repeated chain into
a shared builder function (e.g., build_document_meta_schema_options or
document_meta_schema_base) that configures the common options and .with_document
entries (including the regex engine, Draft, and all DRAFT202012_* documents) and
returns the configured builder or options object; then replace the duplicated
chains in the constants (DOCUMENT_META_SCHEMA_V0 and DOCUMENT_META_SCHEMA_V1) by
calling that shared builder and only applying schema-specific tweaks (like
.compile(&DOCUMENT_META_JSON_V1) / .compile(&DOCUMENT_META_JSON_V0) or any
version-specific overrides) to avoid drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c25d9692-23e4-4798-acce-0a5beb3b60ea

📥 Commits

Reviewing files that changed from the base of the PR and between 831d973 and f9b8ab6.

📒 Files selected for processing (10)
  • packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs
  • packages/rs-dpp/src/validation/meta_validators/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-platform-version/src/version/v12.rs
✅ Files skipped from review due to trivial changes (1)
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs

Comment thread packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json
Comment thread packages/rs-dpp/src/validation/meta_validators/mod.rs
@QuantumExplorer QuantumExplorer force-pushed the fix/document-schema-additional-properties branch from f9b8ab6 to b52aed1 Compare April 11, 2026 14:15
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs (2)

1054-1119: Consider adding test coverage for the property stripping logic.

The existing test verifies that shielded pool trees are created but doesn't exercise the strip_unknown_document_schema_properties logic. Consider adding a test that:

  1. Creates a contract with an unknown top-level property in its document schema
  2. Runs transition_to_version_12
  3. Verifies the unknown property was stripped and the contract was re-serialized
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs`
around lines 1054 - 1119, Add a new test alongside
test_transition_to_version_12_creates_shielded_pool_trees that constructs a
contract document containing an unexpected top-level property, inserts/commits
that contract into the platform state before calling transition_to_version_12,
invokes platform.transition_to_version_12(&transaction, platform_version), then
reads back the contract document and asserts the unknown property is removed
(verifying strip_unknown_document_schema_properties ran) and that the contract
was re-serialized/updated in storage; reference the existing test function name
for placement and use transition_to_version_12 and
strip_unknown_document_schema_properties as the behaviors under test.

686-713: Add a comment linking this allowlist to the v1 meta-schema source.

This allowlist must stay synchronized with the v1 document meta-schema's top-level properties. Consider adding a comment pointing to schema/meta_schemas/document/v1/document-meta.json so future maintainers know to update both in sync.

📝 Suggested documentation
+    /// The set of top-level property names that the v1 document meta-schema allows on
+    /// a document type schema object.
+    ///
+    /// IMPORTANT: This list must match the `properties` keys defined in
+    /// `packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json`.
     const ALLOWED_DOCUMENT_SCHEMA_PROPERTIES: &[&str] = &[
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs`
around lines 686 - 713, Add a brief comment immediately above the
ALLOWED_DOCUMENT_SCHEMA_PROPERTIES constant that points to the canonical v1
meta-schema source (schema/meta_schemas/document/v1/document-meta.json) and
states that this allowlist must be kept in sync with that file; reference the
constant name ALLOWED_DOCUMENT_SCHEMA_PROPERTIES so maintainers know which list
to update when the v1 document-meta.json changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs`:
- Around line 1054-1119: Add a new test alongside
test_transition_to_version_12_creates_shielded_pool_trees that constructs a
contract document containing an unexpected top-level property, inserts/commits
that contract into the platform state before calling transition_to_version_12,
invokes platform.transition_to_version_12(&transaction, platform_version), then
reads back the contract document and asserts the unknown property is removed
(verifying strip_unknown_document_schema_properties ran) and that the contract
was re-serialized/updated in storage; reference the existing test function name
for placement and use transition_to_version_12 and
strip_unknown_document_schema_properties as the behaviors under test.
- Around line 686-713: Add a brief comment immediately above the
ALLOWED_DOCUMENT_SCHEMA_PROPERTIES constant that points to the canonical v1
meta-schema source (schema/meta_schemas/document/v1/document-meta.json) and
states that this allowlist must be kept in sync with that file; reference the
constant name ALLOWED_DOCUMENT_SCHEMA_PROPERTIES so maintainers know which list
to update when the v1 document-meta.json changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f0f6ed05-6e23-44e6-b448-999597ab0ea7

📥 Commits

Reviewing files that changed from the base of the PR and between f9b8ab6 and b52aed1.

📒 Files selected for processing (12)
  • packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs
  • packages/rs-dpp/src/data_contract/serialized_version/mod.rs
  • packages/rs-dpp/src/validation/meta_validators/mod.rs
  • packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-platform-version/src/version/v12.rs
✅ Files skipped from review due to trivial changes (2)
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs
  • packages/rs-platform-version/src/version/v12.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs
  • packages/rs-dpp/src/validation/meta_validators/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs

@QuantumExplorer QuantumExplorer force-pushed the fix/document-schema-additional-properties branch 2 times, most recently from 9b66880 to fe631a8 Compare April 11, 2026 14:40
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/rs-dpp/src/data_contract/document_type/schema/allowed_top_level_properties.rs`:
- Around line 45-50: The retain closure currently keeps non-string keys by
returning true; change it to drop non-text keys so malformed non-text keys are
removed. In the map.retain(|(key, _)| { ... }) closure, replace the `_ => return
true` branch with `_ => return false` (or equivalently treat only Value::Text(s)
and check ALLOWED_DOCUMENT_SCHEMA_V1_PROPERTIES.contains(&s.as_str()) while
returning false for all other variants) so non-text keys are not preserved by
the filter; ensure the logic still uses ALLOWED_DOCUMENT_SCHEMA_V1_PROPERTIES to
allow valid text keys.

In
`@packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs`:
- Around line 724-835: The migration currently silently skips contracts on
unexpected storage layouts or deserialization failures (branches around
maybe_element/maybe_history_element and the Err(e) from
bincode::borrow_decode_from_slice), which can leave the node in a mixed state;
change those continue paths to surface failures: replace the tracing::warn +
continue behavior in the match arms that handle unexpected reference types,
unresolved historical elements, unexpected element types, and bincode
deserialization Err(e) to instead return a descriptive error (including
contract_id_bytes hex and the underlying error or a specific enum/variant), or
accumulate and then return an aggregated error from the outer function so the
protocol upgrade aborts rather than proceeding silently; update any callers of
this function to propagate the error (or handle the aggregated error) so
migration fails fast on any problematic contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1cc04a72-64ad-4dee-b1a7-821276e4b62c

📥 Commits

Reviewing files that changed from the base of the PR and between b52aed1 and fe631a8.

📒 Files selected for processing (14)
  • packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/schema/allowed_top_level_properties.rs
  • packages/rs-dpp/src/data_contract/document_type/schema/mod.rs
  • packages/rs-dpp/src/data_contract/serialized_version/mod.rs
  • packages/rs-dpp/src/validation/meta_validators/mod.rs
  • packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-platform-version/src/version/v12.rs
✅ Files skipped from review due to trivial changes (4)
  • packages/rs-platform-version/src/version/v12.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-dpp/src/data_contract/serialized_version/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs
  • packages/rs-dpp/src/validation/meta_validators/mod.rs
  • packages/rs-dpp/schema/meta_schemas/document/v1/document-meta.json

@QuantumExplorer QuantumExplorer force-pushed the fix/document-schema-additional-properties branch 4 times, most recently from 862bd4b to a006d20 Compare April 11, 2026 15:48
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

I re-reviewed the pinned a006d20 commit directly after all six reviewer ACP lanes and a codex verifier lane stalled before first output. The new v12 migration closes the earlier stored-contract gap by stripping unknown top-level document schema keys during protocol upgrade, the version switch now selects the strict v1 document meta-schema at protocol 12, and the targeted DPP + drive-abci tests pass on this checkout. I did not find additional actionable issues in the current diff.

Reviewed commit: a006d20

…ies from v12+

Introduces a versioned document meta-schema to prevent pre-v12
contracts from smuggling unknown top-level properties that could
change storage semantics after a protocol upgrade.

- v0 meta-schema (pre-v12): allows any top-level properties (unchanged)
- v1 meta-schema (v12+): adds additionalProperties: false, plus all
  standard JSON Schema keywords used by existing contracts (required,
  $comment, description, minProperties, maxProperties, dependentRequired)

Changes:
- New: schema/meta_schemas/document/v1/document-meta.json
- New: DOCUMENT_META_SCHEMA_V1 compiled validator
- New: document_type_schema version field in DocumentTypeSchemaVersions
- CONTRACT_VERSIONS_V4 uses document_type_schema: 1
- try_from_schema v0/v1 dispatch to correct validator based on version
- 3 tests: v0 allows unknown props, v1 rejects unknown, v1 accepts known

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer force-pushed the fix/document-schema-additional-properties branch from a006d20 to 538c847 Compare April 11, 2026 17:06
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

Direct verification against 538c847e9b6126b3923cad23978d0d5d98401cbf confirms two blocking issues remain. The PR does change the active pre-v12 document/v0 meta-schema in this checkout, and the new v12 migration only rewrites the latest stored revision for keeps-history contracts even though history fetch/verification still deserializes every historical snapshot with the current platform version. The remaining non-blocking findings are minor cleanup and test-coverage concerns.

Reviewed commit: 538c847

🔴 2 blocking | 🟡 1 suggestion(s) | 💬 1 nitpick(s)

1 additional finding

🔴 blocking: The PR relaxes pre-v12 document-type validation on the live v0 schema path

packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json (lines 602-612)

This checkout removes the root-level additionalProperties: false guard from document/v0/document-meta.json, so protocol versions that still dispatch document_type_schema version 0 now accept unknown top-level document schema keys that older nodes reject. DocumentTypeV1::try_from_schema selects DOCUMENT_META_SCHEMA_V0 for the pre-v12 path, and the new v0_schema_allows_unknown_properties test proves the behavior changed. That is not just a v12-only migration aid: it alters consensus validation before the fork boundary and creates mixed-version rollout/replay risk.

🤖 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/rs-dpp/schema/meta_schemas/document/v0/document-meta.json`:
- [BLOCKING] lines 602-612: The PR relaxes pre-v12 document-type validation on the live v0 schema path
  This checkout removes the root-level `additionalProperties: false` guard from `document/v0/document-meta.json`, so protocol versions that still dispatch `document_type_schema` version `0` now accept unknown top-level document schema keys that older nodes reject. `DocumentTypeV1::try_from_schema` selects `DOCUMENT_META_SCHEMA_V0` for the pre-v12 path, and the new `v0_schema_allows_unknown_properties` test proves the behavior changed. That is not just a v12-only migration aid: it alters consensus validation before the fork boundary and creates mixed-version rollout/replay risk.

In `packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs`:
- [BLOCKING] lines 753-799: The v12 migration rewrites only the latest historical contract revision
  When the contract root contains a history tree, the migration resolves key `[0]` to a single sibling timestamp and rewrites only that one `Element::Item`. Older timestamped revisions in the same history tree are never visited. That leaves pre-v12 revisions with smuggled top-level schema keys on disk, but both history fetch and proof verification still deserialize each historical entry with `DataContract::versioned_deserialize(..., platform_version)`, so querying or verifying contract history after protocol12 can still fail on those untouched older snapshots.

In `packages/rs-dpp/src/data_contract/document_type/schema/allowed_top_level_properties.rs`:
- [SUGGESTION] lines 9-34: The migration allowlist is not checked against the v1 JSON schema
  `ALLOWED_DOCUMENT_SCHEMA_V1_PROPERTIES` is the source of truth for stripping unknown keys during the v12 migration, but there is no test that compares this list to the top-level `properties` declared in `schema/meta_schemas/document/v1/document-meta.json`. If a later change updates one side without the other, the migration can silently delete a field that the validator accepts or preserve one that the validator rejects.

Comment thread packages/rs-dpp/src/validation/meta_validators/mod.rs Outdated
QuantumExplorer and others added 2 commits April 12, 2026 04:23
…cker, fix review items

- Move strip_unknown_document_schema_properties from Platform<C> in
  drive-abci to a method on Drive in rs-drive
- Add check-contract-properties script in rs-scripts that fetches all
  contracts from Platform Explorer API, discovers DAPI nodes from
  validators, and checks for unknown top-level document schema properties
- Remove duplicate applicator registration in DRAFT_202012, V0, and V1
  meta-schema validators
- Add allowlist_matches_v1_meta_schema_properties test ensuring the
  migration allowlist stays in sync with the v1 JSON meta-schema
- Fix script error counting to distinguish fetch errors from checked contracts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert the migration's use of fetch_contract_ids (which will be in a
separate PR) back to the original inline grove_get_raw_path_query
approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

The checkout matches pinned head 66b862d32a63b43c4ccf050540a5297fe18970e0, and the earlier pre-v12 concerns called out in the coordinator notes do appear resolved. One blocking issue remains: the v12 migration rewrites only the latest history-enabled contract revision, while older timestamped revisions are still reachable through the history fetch/proof APIs and remain unsanitized.

Reviewed commit: 66b862d

🔴 1 blocking

🤖 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/rs-drive/src/drive/contract/migration/strip_unknown_document_schema_properties.rs`:
- [BLOCKING] lines 83-140: v12 migration sanitizes only the latest revision of history-enabled contracts
  When `keeps_history` contracts are encountered, this migration resolves the `[contract]/[0]/[0]` sibling reference and rewrites only the single timestamped item it points to. Older revisions remain stored under other timestamp keys in the same history subtree, and `fetch_contract_history_query` still ranges over those keys directly while `fetch_contract_with_history_v0` and `prove_contract_history_v0` expose them to callers. That leaves reachable historical contract revisions with the pre-v12 schema shape after the upgrade, so the migration does not actually sanitize all observable contract state.

Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

The checkout matches pinned HEAD 27e50d9f1f74faee524669d61b96df05210a9d7d, and the only substantive candidate issue remains valid. The v12 migration still sanitizes only the timestamp currently targeted by the [0] reference for history-enabled contracts, while the history query/proof APIs continue to expose older timestamped revisions unchanged.

Reviewed commit: 27e50d9

🔴 1 blocking

🤖 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/rs-drive/src/drive/contract/migration/strip_unknown_document_schema_properties.rs`:
- [BLOCKING] lines 81-124: Historical contract migration rewrites only the latest stored revision
  In the `Element::Tree(..)` branch, the migration reads `[DataContractDocuments, contract_id,0,0]`, resolves that sibling reference to a single `timestamp_key`, and rewrites only that one item. History-enabled contract updates append a new item at each encoded timestamp and then move the `[0]` reference to the latest entry (`packages/rs-drive/src/drive/contract/insert/add_contract_to_storage/v0/mod.rs:93-126`), so older revisions remain stored under the same history subtree. The history APIs query the whole `[DataContractDocuments, contract_id,0]` subtree by timestamp range rather than following the `[0]` reference (`packages/rs-drive/src/drive/contract/queries.rs:201-224`, `packages/rs-drive/src/drive/contract/get_fetch/fetch_contract_with_history/v0/mod.rs:62-89`), which means pre-latest revisions with unknown schema properties remain reachable after the migration.

QuantumExplorer and others added 3 commits April 12, 2026 13:47
Replace inline grove_get_raw_path_query with fetch_contract_ids_v0
for enumerating contracts during the v12 migration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The v12 migration now iterates every revision stored in a historical
contract's history subtree (keyed by encoded timestamps), stripping
unknown top-level properties from each one. Previously only the latest
revision (via the [0] reference) was cleaned, leaving older revisions
with smuggled properties reachable through history queries.

Extract shared deserialization/strip/rewrite logic into
strip_and_rewrite_contract_element helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QuantumExplorer and others added 4 commits April 12, 2026 14:32
Test that the v12 migration strips unknown properties from ALL
revisions of a historical contract, not just the latest. Creates 3
revisions each with a different smuggled property, runs migration,
verifies all 3 are cleaned and the [0] reference is preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…visions

Give each historical revision distinct element flags and assert they
are preserved after migration strips unknown properties.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The v1 document meta-schema's $schema const now correctly points to
the v1 path. A new enrich_with_base_schema v1 injects the v1 URI
into document schemas during enrichment. CONTRACT_VERSIONS_V4 now
uses enrich_with_base_schema: 1.

Pre-v12 contracts continue using the v0 URI via enrich_with_base_schema v0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify that enrich_with_base_schema injects v0 URI for pre-v12
protocol versions and v1 URI for v12+.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

The PR's migration logic and test coverage are solid. The only finding — that fetch_contract_ids_v0 is called with u16::MAX without a pagination loop — is technically correct but practically irrelevant given mainnet's ~46 contracts and the economic cost of creating 65,000+ contracts before a v12 upgrade. Codex agents independently confirmed no issues, and all test assertions are correct.

Reviewed commit: 0ade3fe

🟡 1 suggestion(s)

🤖 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/rs-drive/src/drive/contract/migration/strip_unknown_document_schema_properties.rs`:
- [SUGGESTION] lines 28-29: Migration fetches at most u16::MAX contracts without pagination
  The migration calls `fetch_contract_ids_v0(None, u16::MAX, ...)` as a single unpaginated query. Since `limit` is `u16`, the absolute maximum is 65,535 contracts. If more exist at migration time, the remainder would be silently skipped and never cleaned.

Mainnet currently has ~46 contracts, and creating ~65,490 more before the v12 upgrade activates is economically prohibitive given per-contract fees. The `fetch_contract_ids_v0` function already supports pagination via its `start_at` parameter (with full test coverage for pagination in the v0 module tests), so wrapping this in a pagination loop would be a low-cost hardening measure for a one-shot consensus-critical migration.

Comment on lines +28 to +29
let contract_ids =
self.fetch_contract_ids_v0(None, u16::MAX, Some(transaction), drive_version)?;
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.

🟡 Suggestion: Migration fetches at most u16::MAX contracts without pagination

The migration calls fetch_contract_ids_v0(None, u16::MAX, ...) as a single unpaginated query. Since limit is u16, the absolute maximum is 65,535 contracts. If more exist at migration time, the remainder would be silently skipped and never cleaned.

Mainnet currently has ~46 contracts, and creating ~65,490 more before the v12 upgrade activates is economically prohibitive given per-contract fees. The fetch_contract_ids_v0 function already supports pagination via its start_at parameter (with full test coverage for pagination in the v0 module tests), so wrapping this in a pagination loop would be a low-cost hardening measure for a one-shot consensus-critical migration.

source: ['claude']

🤖 Fix this 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/rs-drive/src/drive/contract/migration/strip_unknown_document_schema_properties.rs`:
- [SUGGESTION] lines 28-29: Migration fetches at most u16::MAX contracts without pagination
  The migration calls `fetch_contract_ids_v0(None, u16::MAX, ...)` as a single unpaginated query. Since `limit` is `u16`, the absolute maximum is 65,535 contracts. If more exist at migration time, the remainder would be silently skipped and never cleaned.

Mainnet currently has ~46 contracts, and creating ~65,490 more before the v12 upgrade activates is economically prohibitive given per-contract fees. The `fetch_contract_ids_v0` function already supports pagination via its `start_at` parameter (with full test coverage for pagination in the v0 module tests), so wrapping this in a pagination loop would be a low-cost hardening measure for a one-shot consensus-critical migration.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is fine

Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

All agent findings converge on a single pre-existing defense-in-depth gap: the v12 migration fetches contract IDs in one u16::MAX-bounded call without pagination. The head commit (f0ccff7) itself is test-only and correct — the schema URI version dispatch, CONTRACT_VERSIONS_V4, and enrich_with_base_schema_v1 are all properly wired. No blocking issues found.

Reviewed commit: f0ccff7

🟡 1 suggestion(s)

🤖 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/rs-drive/src/drive/contract/migration/strip_unknown_document_schema_properties.rs`:
- [SUGGESTION] lines 27-29: Migration fetches at most 65,535 contract IDs without pagination
  `strip_unknown_document_schema_properties()` calls `fetch_contract_ids_v0(None, u16::MAX, ...)` once and never pages through the result set. Because `fetch_contract_ids_v0` wraps the limit in `SizedQuery::new(query, Some(limit), None)`, the query genuinely stops at 65,535 entries — any contracts beyond that boundary are silently skipped and left unsanitized.

This matters because the migration is specifically designed to strip unknown top-level document schema properties before protocol v12 starts interpreting them. Contract creation is permissionless, so the total count is unbounded in principle. If the contract set ever exceeds 65,535, the migration's invariant ("all persisted schemas are normalized") breaks silently — no error, no log, just contracts that retain properties the migration was supposed to remove.

The function already supports cursor-based pagination via its `start_at: Option<([u8; 32], bool)>` parameter. A paged loop using the last returned ID as the next cursor would close the gap without changing the function signature. Current mainnet has ~46 contracts, so this is not urgent, but it's the kind of silent truncation that becomes very hard to diagnose if it ever triggers.

Comment on lines +27 to +29
// 1. Fetch all contract IDs.
let contract_ids =
self.fetch_contract_ids_v0(None, u16::MAX, Some(transaction), drive_version)?;
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.

🟡 Suggestion: Migration fetches at most 65,535 contract IDs without pagination

strip_unknown_document_schema_properties() calls fetch_contract_ids_v0(None, u16::MAX, ...) once and never pages through the result set. Because fetch_contract_ids_v0 wraps the limit in SizedQuery::new(query, Some(limit), None), the query genuinely stops at 65,535 entries — any contracts beyond that boundary are silently skipped and left unsanitized.

This matters because the migration is specifically designed to strip unknown top-level document schema properties before protocol v12 starts interpreting them. Contract creation is permissionless, so the total count is unbounded in principle. If the contract set ever exceeds 65,535, the migration's invariant ("all persisted schemas are normalized") breaks silently — no error, no log, just contracts that retain properties the migration was supposed to remove.

The function already supports cursor-based pagination via its start_at: Option<([u8; 32], bool)> parameter. A paged loop using the last returned ID as the next cursor would close the gap without changing the function signature. Current mainnet has ~46 contracts, so this is not urgent, but it's the kind of silent truncation that becomes very hard to diagnose if it ever triggers.

source: ['claude', 'codex']

🤖 Fix this 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/rs-drive/src/drive/contract/migration/strip_unknown_document_schema_properties.rs`:
- [SUGGESTION] lines 27-29: Migration fetches at most 65,535 contract IDs without pagination
  `strip_unknown_document_schema_properties()` calls `fetch_contract_ids_v0(None, u16::MAX, ...)` once and never pages through the result set. Because `fetch_contract_ids_v0` wraps the limit in `SizedQuery::new(query, Some(limit), None)`, the query genuinely stops at 65,535 entries — any contracts beyond that boundary are silently skipped and left unsanitized.

This matters because the migration is specifically designed to strip unknown top-level document schema properties before protocol v12 starts interpreting them. Contract creation is permissionless, so the total count is unbounded in principle. If the contract set ever exceeds 65,535, the migration's invariant ("all persisted schemas are normalized") breaks silently — no error, no log, just contracts that retain properties the migration was supposed to remove.

The function already supports cursor-based pagination via its `start_at: Option<([u8; 32], bool)>` parameter. A paged loop using the last returned ID as the next cursor would close the gap without changing the function signature. Current mainnet has ~46 contracts, so this is not urgent, but it's the kind of silent truncation that becomes very hard to diagnose if it ever triggers.

],
"additionalProperties": false
},
"countable": {
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.

Are we adding countable in this PR?

@QuantumExplorer
Copy link
Copy Markdown
Member Author

there's countable in here, next PR will fix this.

@QuantumExplorer QuantumExplorer merged commit c061bd4 into v3.1-dev Apr 12, 2026
38 checks passed
@QuantumExplorer QuantumExplorer deleted the fix/document-schema-additional-properties branch April 12, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants