Skip to content

fix(platform): reject keep-history document deletes cleanly#3930

Open
thepastaclaw wants to merge 6 commits into
dashpay:v3.1-devfrom
thepastaclaw:fix/document-history-delete-error
Open

fix(platform): reject keep-history document deletes cleanly#3930
thepastaclaw wants to merge 6 commits into
dashpay:v3.1-devfrom
thepastaclaw:fix/document-history-delete-error

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

fix(platform): reject keep-history document deletes cleanly

Issue being fixed or feature implemented

Fixes #3927.

Document types could set both documentsKeepHistory: true and
canBeDeleted: true. That combination advertises deletability even though
Drive refuses to delete keep-history documents, so delete attempts surfaced as
InternalError instead of a clear consensus rejection.

What was done?

  • Reject new document types that resolve to both keep-history and deletable
    during DPP schema parsing.
  • Return that rejection as a consensus error in validator/full-validation mode,
    while preserving protocol/data-contract error behavior for non-validation
    builds.
  • Add a Drive ABCI delete-transition structure-validation guard so
    already-deployed contradictory contracts fail as invalid-paid transitions
    instead of internal errors.
  • Version-gate the delete-transition guard at protocol version 12 so prior
    replay behavior is preserved.
  • Add focused DPP and Drive ABCI regression coverage, including a contradictory
    keep-history/deletable fixture for the existing-contract path.

How Has This Been Tested?

Local worktree on macOS arm64, branch fix/document-history-delete-error.

  • cargo test -p dpp --features all_features data_contract::document_type::class_methods::try_from_schema::v2

    • 13 passed, 0 failed
  • Broader DPP try_from_schema sweep across v0/v1/v2

    • 53 passed, 0 failed
  • cargo test -p dpp --features all_features

    • 3574 passed, 0 failed
  • cargo test -p drive-abci --features mocks test_document_delete_on_document_type_that_keeps_history_is_rejected

    • passed
  • cargo test -p drive-abci --features mocks data_contract_create::state::v0::tests

    • 6 passed, 0 failed
  • cargo test -p drive-abci --features mocks focused batch
    state-transition/deletion coverage

    • 293 passed, 0 failed
  • DPP no-default-features parser regression:

    cargo test -p dpp \
      doctype_keep_history_with_can_be_deleted_rejected \
      --no-default-features
    • 1 passed, 0 failed
  • DPP all-features parser regression:

    cargo test -p dpp --features all_features \
      doctype_keep_history_with_can_be_deleted_rejected
    • 1 passed, 0 failed
  • Pre-commit hook:

    • cargo fmt --all -- --check
    • cargo check workspace

Pre-PR code-review gate was run after fixes and returned ship.

Breaking Changes

None.

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

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Enhanced validation for document type configurations to prevent contradictory settings where documents simultaneously keep history and allow deletion.
    • Added stricter enforcement rules for document deletion operations, rejecting deletes on document types with history-keeping enabled.
  • Bug Fixes

    • Improved error handling to distinguish document configuration conflicts as consensus errors rather than generic data contract errors.

thepastaclaw and others added 5 commits June 17, 2026 17:12
…schema

documentsKeepHistory: true together with canBeDeleted: true is
self-contradictory — rs-drive's force-delete path unconditionally
refuses to delete documents whose type keeps history
(InvalidDeletionOfDocumentThatKeepsHistory). Catch the combination at
contract creation time in the V2 document-type schema parser, mirroring
the existing cross-flag rule for
ContestedUniqueIndexOnMutableDocumentTypeError, so SDK users get a
clean InvalidContractStructure error instead of a runtime delete
failure.

Gated by full_validation so already-deployed contradictory contracts
continue to load through the V2 parser at v12+ (the rs-drive-abci
delete-transition guard surfaces them as clean invalid-paid
transitions in a follow-up commit).

Refs dashpay#3927

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without this guard, a delete state transition against a document type
whose schema sets documentsKeepHistory: true passes the structure
validator (which only checks documents_can_be_deleted()) and reaches
rs-drive's force-delete path, where it triggers
InvalidDeletionOfDocumentThatKeepsHistory. The processor reclassifies
that drive-layer error as ExecutionResult::InternalError — the
transition is neither valid nor invalid-paid, leaving the SDK with no
clean accept/reject signal.

Add a documents_keep_history() check beside the existing
documents_can_be_deleted() check in the delete-transition advanced
structure validator so the contradiction surfaces as a normal invalid
(paid) consensus error. This fixes the classification even for
already-deployed contradictory contracts (e.g. testnet
5CBPiadGmx3Zsjc26g5onopcx7pdxHPbrRAUD2T2yAbC), which the DPP cross-flag
rule from the previous commit cannot retroactively fix.

The new note-contract-keep-history-and-can-be-deleted.json fixture is
loaded with full_validation=false so it bypasses the DPP guard and
exercises this layer directly.

Refs dashpay#3927

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit added the documents_keep_history() check directly
to advanced_structure_v0, which is the dispatched validator for ALL
historical protocol versions (1-11) as well as v12. Mutating v0 would
alter the consensus result of any historical block containing a delete
against a keep-history doctype — pre-fix those returned
ExecutionResult::InternalError (no fees, no state change); post-fix
they would be classified as invalid-paid (fees deducted, state delta
differs), breaking bit-for-bit chain replay at PROTOCOL_VERSION_11
and earlier.

Move the check into a new advanced_structure_v1 module, restore v0 to
its original behavior, extend the trait dispatcher to handle both
versions, and bump document_delete_transition_structure_validation
from 0 to 1 in DRIVE_ABCI_VALIDATION_VERSIONS_V8 (used by
PROTOCOL_VERSION_12, the v3.1 hard fork). V1-V7 still dispatch v0 so
pre-v12 chain history stays reproducible.

Refs dashpay#3927

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap the v2 DocumentType keep-history + canBeDeleted contradiction
through `consensus_or_protocol_data_contract_error` so that with the
`validation` feature it returns `ProtocolError::ConsensusError` instead
of bare `ProtocolError::DataContractError`. drive-abci's
`transform_into_action_v0` only converts the consensus variant into a
clean invalid (paid) transition with a bump action — the
DataContractError variant propagated as an internal execution error in
validator mode, defeating the parse-time guard's whole purpose for
contract create/update.

Pin the variant in the existing regression test so the classification
can't silently regress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@thepastaclaw, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 5 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a9fc9522-dfcf-41dd-9769-c5fcebb54997

📥 Commits

Reviewing files that changed from the base of the PR and between ed57f33 and 078c233.

📒 Files selected for processing (2)
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs
📝 Walkthrough

Walkthrough

Enforces that documentsKeepHistory: true and canBeDeleted: true cannot coexist in a document type. The V2 schema parser now rejects this combination with InvalidContractStructure at contract creation. A new V1 delete-transition structure validator rejects delete attempts against keep-history document types as clean paid consensus errors, gated at PROTOCOL_VERSION_12 in the v3.1 hard fork version table.

Changes

Fix documentsKeepHistory + canBeDeleted contradiction

Layer / File(s) Summary
Schema parse-time contradiction check
packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs
Imports consensus_or_protocol_data_contract_error and adds a full_validation-gated check rejecting documentsKeepHistory && canBeDeleted with InvalidContractStructure, surfaced as ProtocolError::ConsensusError when the validation feature is enabled.
DPP unit test alignment and new regression tests
packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs
Fixes existing keep-history accepted tests to set canBeDeleted: false. Adds new tests for the rejected combination, keep-history with canBeDeleted: false (accepted), and canBeDeleted: true without keep-history (accepted).
V1 delete-transition structure validator and version wiring
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/advanced_structure_v1/mod.rs, .../document_delete_transition_action/mod.rs
Adds DocumentDeleteTransitionActionStructureValidationV1 trait and implementation rejecting deletes when documents_can_be_deleted() is false or documents_keep_history() is true. Wires it into validate_structure dispatch at platform version 1, updating UnknownVersionMismatch to list [0, 1].
Protocol version gating
packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v8.rs
Enables document_delete_transition_structure_validation at version 1 starting from PROTOCOL_VERSION_12 in DRIVE_ABCI_VALIDATION_VERSIONS_V8.
Integration test fixture and regression test
packages/rs-drive-abci/tests/supporting_files/contract/note/note-contract-keep-history-and-can-be-deleted.json, packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs
Adds a self-contradictory JSON contract fixture loaded with full_validation: false and a regression test verifying that document creation succeeds while deletion yields invalid_paid_count == 1, valid_count == 0, and no InternalError in execution_results.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

ready for final review

Suggested reviewers

  • shumkov
  • QuantumExplorer

Poem

🐇 Two flags at odds, a contract's flaw,
Keep history AND delete — oh raw!
The schema now says "pick just one,"
And delete transitions? Cleanly done.
No more InternalError shame,
Just consensus errors, clear as flame! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(platform): reject keep-history document deletes cleanly' directly summarizes the main objective: preventing document deletes on keep-history types from failing as internal errors.
Linked Issues check ✅ Passed The PR fully addresses both coding objectives from issue #3927: (1) DPP schema validation rejects documentsKeepHistory+canBeDeleted flag combination; (2) Drive ABCI structure validation checks documents_keep_history() to surface deletes as clean invalid transitions.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue objectives: DPP schema parsing validation, Drive ABCI structure validation guards, protocol version gating for consensus replay, and comprehensive test coverage. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw thepastaclaw marked this pull request as ready for review June 17, 2026 23:53
@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw

thepastaclaw commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

✅ Review complete (commit 078c233)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs (1)

986-1019: 💤 Low value

Consider adding a test for full_validation: false to verify the gating.

The validation logic is correctly gated by full_validation, but there's no test verifying that contradictory contracts are accepted when full_validation: false (the scenario for re-parsing already-deployed contracts). A test would confirm the gate works as documented in lines 317-321.

📝 Suggested test addition
/// `full_validation: false` must allow the contradictory combination so
/// already-deployed contracts (e.g. testnet `5CBPiadGmx3Zsjc26g5onopcx7pdxHPbrRAUD2T2yAbC`)
/// continue to load when re-parsed at v12+.
#[test]
fn doctype_keep_history_with_can_be_deleted_accepted_without_full_validation() {
    let schema = platform_value!({
        "type": "object",
        "properties": {
            "label": {
                "type": "string",
                "maxLength": 50,
                "position": 0,
            },
        },
        "additionalProperties": false,
        "documentsKeepHistory": true,
        "canBeDeleted": true,
    });
    let platform_version = PlatformVersion::latest();
    let config = DataContractConfig::default_for_version(platform_version)
        .expect("default config available on latest platform version");
    let result = DocumentTypeV2::try_from_schema(
        Identifier::new([1; 32]),
        1,
        config.version(),
        "test_doc",
        schema,
        None,
        &BTreeMap::new(),
        &config,
        false, // full_validation = false
        &mut vec![],
        platform_version,
    );
    assert!(
        result.is_ok(),
        "contradictory flags must be accepted when full_validation=false \
         to allow already-deployed contracts to load; got {:?}",
        result.unwrap_err()
    );
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs`
around lines 986 - 1019, Add a new test function after the existing
doctype_keep_history_with_can_be_deleted_rejected test to verify that when
full_validation is set to false, the contradictory combination of
documentsKeepHistory: true and canBeDeleted: true is accepted. The test should
use the same schema structure as the existing rejection test, call
DocumentTypeV2::try_from_schema with full_validation parameter set to false, and
assert that the result is Ok instead of Err. This confirms the validation gating
mechanism works correctly for re-parsing already-deployed contracts as
documented.
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs (1)

381-390: ⚡ Quick win

Pin this regression to protocol version 12 to keep the gate test deterministic.

Using current_platform_version() makes this test float with latest, so it can stop validating the exact v12 gate behavior over time. Prefer explicitly setting protocol version 12 here (and optionally add a paired v11 assertion for legacy classification).

Suggested change
-        let mut platform = TestPlatformBuilder::new()
-            .build_with_mock_rpc()
-            .set_initial_state_structure();
+        let protocol_version = 12;
+        let mut platform = TestPlatformBuilder::new()
+            .with_initial_protocol_version(protocol_version)
+            .build_with_mock_rpc()
+            .set_initial_state_structure();

         let contract_path = "tests/supporting_files/contract/note/note-contract-keep-history-and-can-be-deleted.json";

-        let platform_state = platform.state.load();
-        let platform_version = platform_state
-            .current_platform_version()
-            .expect("expected to get current platform version");
+        let platform_state = platform.state.load();
+        let platform_version = PlatformVersion::get(protocol_version)
+            .expect("expected platform version for protocol version 12");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs`
around lines 381 - 390, The test is currently using current_platform_version()
to retrieve the platform version, which will always use the latest protocol
version and cause the test to float over time, making it unable to reliably
validate v12 gate behavior. Replace the call to current_platform_version() with
an explicit protocol version 12 assignment, ensuring the test pins to protocol
version 12 to maintain deterministic gate validation. Optionally, add a paired
assertion for legacy v11 classification to provide additional coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs`:
- Around line 986-1019: Add a new test function after the existing
doctype_keep_history_with_can_be_deleted_rejected test to verify that when
full_validation is set to false, the contradictory combination of
documentsKeepHistory: true and canBeDeleted: true is accepted. The test should
use the same schema structure as the existing rejection test, call
DocumentTypeV2::try_from_schema with full_validation parameter set to false, and
assert that the result is Ok instead of Err. This confirms the validation gating
mechanism works correctly for re-parsing already-deployed contracts as
documented.

In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs`:
- Around line 381-390: The test is currently using current_platform_version() to
retrieve the platform version, which will always use the latest protocol version
and cause the test to float over time, making it unable to reliably validate v12
gate behavior. Replace the call to current_platform_version() with an explicit
protocol version 12 assignment, ensuring the test pins to protocol version 12 to
maintain deterministic gate validation. Optionally, add a paired assertion for
legacy v11 classification to provide additional coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 25cb4c10-52f5-41b2-a9ed-9d76c405cfe4

📥 Commits

Reviewing files that changed from the base of the PR and between 2f1bcb8 and ed57f33.

📒 Files selected for processing (6)
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/advanced_structure_v1/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs
  • packages/rs-drive-abci/tests/supporting_files/contract/note/note-contract-keep-history-and-can-be-deleted.json
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v8.rs

…tests

- Add DPP test asserting documentsKeepHistory + canBeDeleted is accepted
  when try_from_schema runs with full_validation=false (the restore /
  migration / cache-warmup path), pinning the gating behavior that lets
  already-deployed contradictory contracts continue to load at v12+.
- Pin the drive-abci keep-history delete regression test to protocol
  version 12 via with_initial_protocol_version + PlatformVersion::get
  instead of floating on current_platform_version(), so the assertion
  stays bit-for-bit reproducible as new protocol versions ship.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

Addressed both CodeRabbit nitpicks in 078c233:

  • Added full_validation: false coverage proving contradictory keep-history/deletable doctypes still load for already-deployed contracts.
  • Pinned the keep-history delete regression test to protocol version 12 instead of floating on latest/current.

Focused validation after the commit:

  • cargo test -p dpp doctype_keep_history_with_can_be_deleted_accepted_without_full_validation --features all_features — passed
  • cargo test -p drive-abci --features mocks test_document_delete_on_document_type_that_keeps_history_is_rejected — passed

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review

Targeted fix is correct: DPP parser cross-flag guard is gated by full_validation and uses consensus_or_protocol_data_contract_error to surface as ConsensusError in validator builds; drive-abci v1 validator is wired only at PROTOCOL_VERSION_12 so v11 replay is preserved; tests cover the rejected/accepted combinations. Three minor follow-ups: a misleading docstring about the canBeDeleted config default, a missing regression test for the omitted-default boundary, and a missing pre-v12 paired test for the new delete guard. No blockers.

🟡 2 suggestion(s) | 💬 1 nitpick(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/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v2/mod.rs:329-339: Add regression test for omitted `canBeDeleted` (default=true) with `documentsKeepHistory: true`
  The guard fires on resolved `DocumentTypeV1` fields, and `documents_can_be_deleted_contract_default()` is `true`, so a schema with `documentsKeepHistory: true` and `canBeDeleted` omitted is now rejected — the most likely real-world misuse. Every other branch is pinned (true+true rejected, true+false accepted, false+true accepted), but the omitted-defaults case is not. A future refactor that inspects raw schema keys instead of resolved values could pass the current tests while silently reopening the contradiction. Add a `doctype_keep_history_with_can_be_deleted_omitted_rejected` test that omits `canBeDeleted` to pin the resolved-default behavior.

In `packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs:379-390: Pin pre-v12 keep-history delete classification with a paired test
  The new regression runs only against the current platform version, but this PR's correctness depends on `document_delete_transition_structure_validation` activating at v12 while v11 preserves the v0 path for chain replay. Adjacent version-gated deletion behavior in this file already has paired v11/latest helpers; adding the same v11 case for the contradictory keep-history fixture would catch an accidental change to the v11 version table or to the v0 validator that would otherwise alter consensus replay silently.

Comment on lines +1021 to +1026
/// Guard against an over-broad fix: `documentsKeepHistory: true` +
/// `canBeDeleted: false` is consistent (the doctype is append-only)
/// and must continue to parse cleanly. Same for `documentsKeepHistory:
/// true` with `canBeDeleted` omitted — covered by the existing
/// `doctype_keep_history_without_summable_accepted` test, which
/// leaves `canBeDeleted` at its config default (false).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

💬 Nitpick: Docstring misstates canBeDeleted default and sibling test coverage

The comment claims the sibling doctype_keep_history_without_summable_accepted test leaves "canBeDeleted" at its config default (false). Two errors: (1) DEFAULT_CONTRACT_DOCUMENTS_CAN_BE_DELETED is true (packages/rs-dpp/src/data_contract/config/fields.rs:6), not false; (2) the sibling test (line 803-828) explicitly sets "canBeDeleted": false (line 817) rather than omitting it. The docstring implies the omitted-default path is covered and benign, but in fact under the new guard documentsKeepHistory: true with canBeDeleted omitted resolves to the default true and is now rejected at v12+. Correct the comment so future readers don't assume the omitted-default case has coverage.

source: ['claude']

Comment on lines +329 to +339
if full_validation && v1.documents_keep_history && v1.documents_can_be_deleted {
return Err(consensus_or_protocol_data_contract_error(
DataContractError::InvalidContractStructure(format!(
"document type \"{}\" sets both `documentsKeepHistory: true` and \
`canBeDeleted: true`, but the storage layer unconditionally refuses to \
delete a document whose type keeps history. Set one of the two flags to \
false (or omit it).",
name,
)),
));
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Add regression test for omitted canBeDeleted (default=true) with documentsKeepHistory: true

The guard fires on resolved DocumentTypeV1 fields, and documents_can_be_deleted_contract_default() is true, so a schema with documentsKeepHistory: true and canBeDeleted omitted is now rejected — the most likely real-world misuse. Every other branch is pinned (true+true rejected, true+false accepted, false+true accepted), but the omitted-defaults case is not. A future refactor that inspects raw schema keys instead of resolved values could pass the current tests while silently reopening the contradiction. Add a doctype_keep_history_with_can_be_deleted_omitted_rejected test that omits canBeDeleted to pin the resolved-default behavior.

source: ['codex', 'claude']

Comment on lines +379 to +390
#[tokio::test]
async fn test_document_delete_on_document_type_that_keeps_history_is_rejected() {
let mut platform = TestPlatformBuilder::new()
.build_with_mock_rpc()
.set_initial_state_structure();

let contract_path = "tests/supporting_files/contract/note/note-contract-keep-history-and-can-be-deleted.json";

let platform_state = platform.state.load();
let platform_version = platform_state
.current_platform_version()
.expect("expected to get current platform version");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Pin pre-v12 keep-history delete classification with a paired test

The new regression runs only against the current platform version, but this PR's correctness depends on document_delete_transition_structure_validation activating at v12 while v11 preserves the v0 path for chain replay. Adjacent version-gated deletion behavior in this file already has paired v11/latest helpers; adding the same v11 case for the contradictory keep-history fixture would catch an accidental change to the v11 version table or to the v0 validator that would otherwise alter consensus replay silently.

source: ['codex']

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 078c233Pin pre-v12 keep-history delete classification with a paired test no longer present.

Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Correction: this was auto-resolved incorrectly by the review dedupe/reconciliation step. The latest push does pin the regression test to protocol version 12, but the paired pre-v12/v11 classification coverage is still missing, so this finding remains valid at 078c233. Reopening/keeping this thread active.

@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

CI note: Rust workspace tests / Tests (macOS) is red on an unrelated pre-existing SDK test, not on the keep-history/delete changes in this PR.

Failure:

  • dash-sdk::main fetch::document_query_v0_v1::sdk_builder_default_seeds_atomic_to_floor
  • assertion at packages/rs-sdk/tests/fetch/document_query_v0_v1.rs:227: left: 11, right: 10

Why unrelated:

  • This PR only changes DPP/drive-abci keep-history delete validation plus focused regression tests and the gated drive-abci validation feature version.
  • The failing SDK test/file is unchanged by this PR.
  • The same expectation exists on upstream/v3.1-dev, while SdkBuilder::new_mock() defaults to mainnet and build() clamps to max(DEFAULT_INITIAL_PROTOCOL_VERSION=10, min_protocol_version(Mainnet)=11), so the observed value 11 follows the current base SDK code.

Local focused checks for this PR still pass:

  • cargo test -p dpp doctype_keep_history_with_can_be_deleted_accepted_without_full_validation --features all_features
  • cargo test -p drive-abci --features mocks test_document_delete_on_document_type_that_keeps_history_is_rejected

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.

Document type allowing documentsKeepHistory: true + canBeDeleted: true: delete fails as an internal error, not a clean rejection

1 participant