Skip to content

fix(platform): reject contradictory keep-history document deletes - #4218

Merged
QuantumExplorer merged 6 commits into
v4.2-devfrom
fix/keep-history-delete-rejection
Sep 8, 2026
Merged

fix(platform): reject contradictory keep-history document deletes#4218
QuantumExplorer merged 6 commits into
v4.2-devfrom
fix/keep-history-delete-rejection

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 23, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Fixes #3927. A document type could advertise deletion while keeping history, even though Drive cannot delete history-bearing documents. Deletes then surfaced as internal errors. Reject the contradictory combination at protocol 14 while preserving released protocol behavior and providing a repair path for existing contracts.

What was done?

  • Keep the protocol 12 and 13 method maps unchanged. Add the schema check to protocol 14's existing try_from_schema v3 parser and select delete structure validation v1.
  • Reject new keep-history/deletable schemas during full validation, including inherited defaults; keep stored contracts readable without full validation.
  • Permit contract owners to correct canBeDeleted: true to false when both old and new document types keep history. All other configuration, index, and schema compatibility checks remain enforced. The correction does not rewrite stored documents.
  • Add parser, update, and version-map regressions, plus signed ABCI coverage for legacy-contract repair after the 13-to-14 boundary and a subsequent ordinary update.

How Has This Been Tested?

Validation performed locally on macOS arm64. Both new failure regressions were observed before implementing the fixes.

  • cargo test --offline -p platform-version --test keep_history_delete_versions — 2 passed; the assertions failed before moving the gates off protocol 13.
  • cargo test --offline -p dpp --features all_features --lib — 4,141 passed, 6 ignored. Includes 13 focused parser/repair tests covering explicit and omitted defaults, protocol 12/13 compatibility, forbidden flag changes, and schema/config checks during repair. The repair regression failed before implementing the correction path.
  • cargo test --offline -p drive-abci --features mocks --lib batch::tests::document — 164 passed. Covers v12/v13 internal-error replay, the exact v14 paid rejection, indexOnly deletion, and a populated v13 contract repaired and updated through signed transitions after selecting v14.
  • cargo test --offline -p drive-abci --features mocks --lib data_contract — 145 passed, 2 ignored.
  • cargo check --offline -p dpp and cargo clippy --offline -p dpp --features all_features — passed. The default build reports three existing unused-import warnings.
  • Formatting and patch whitespace checks — passed.

Breaking Changes

No change to consensus behavior for released protocols through 13. At protocol 14, new contradictory document types are rejected and legacy delete attempts become paid consensus errors. To update an existing contradictory contract, explicitly set canBeDeleted: false on each keep-history document type in the update. History and the other immutable configuration flags remain unchanged.

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

    • Protocol 14 validates document schemas to prevent incompatible combinations of history retention and deletion permissions.
    • Document deletions for history-retaining document types now return a clear validation error.
  • Bug Fixes

    • Legacy contracts can be repaired during protocol upgrades without losing existing documents or settings.
    • Valid document-type updates continue to support mutability, schema compatibility, and existing property behavior.
  • Compatibility

    • Earlier protocol versions preserve their existing validation and error behavior for replay compatibility.

Document types could set both documentsKeepHistory: true and
canBeDeleted: true, so delete attempts surfaced as InternalError instead
of a clean consensus rejection.

- Reject the combination during DPP schema parsing via a new
  try_from_schema v3, active from protocol version 13 (CONTRACT_VERSIONS_V5).
  v2 stays untouched: protocol version 12 is released and consensus-frozen.
- Add a drive-abci delete-transition structure-validation v1 that rejects
  deletes on keep-history document types as invalid (paid) transitions,
  gated at protocol version 13 via DRIVE_ABCI_VALIDATION_VERSIONS_V9.
- Regression coverage on both sides of the v12/v13 boundary, including a
  contradictory fixture for the already-deployed-contract path.

Fixes #3927

Co-Authored-By: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Warning

Review limit reached

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 5808de51-2555-4857-b790-0fd89588a0a0

📥 Commits

Reviewing files that changed from the base of the PR and between 62a4998 and d4e3dca.

📒 Files selected for processing (9)
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/keep_history_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/methods/validate_update/common/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/methods/validate_update/v1/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/tests/document/deletion.rs
  • packages/rs-drive/src/drive/contract/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v6.rs
  • packages/rs-platform-version/tests/keep_history_delete_versions.rs

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: f0abe675-edd4-4fa8-a08f-d789da1420f6

📥 Commits

Reviewing files that changed from the base of the PR and between d0a9a09 and 62a4998.

📒 Files selected for processing (15)
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v4/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/methods/validate_update/common/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/methods/validate_update/v1/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/src/execution/validation/state_transition/state_transitions/batch/tests/document/keep_history.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/mod.rs
  • packages/rs-drive-abci/tests/supporting_files/contract/note/note-contract-keep-history-and-can-be-deleted.json
  • packages/rs-drive/src/drive/contract/mod.rs
  • packages/rs-drive/src/drive/document/insert/add_document_to_primary_storage/v0/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v6.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v10.rs
  • packages/rs-platform-version/tests/keep_history_delete_versions.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Protocol 14 adds version 4 document schema validation and version 1 delete-transition validation. It rejects contradictory keep-history and deletion flags, supports repair of legacy contracts, classifies keep-history deletes as invalid paid transitions, and preserves protocol 12–13 behavior.

Changes

Keep-history deletion validation

Layer / File(s) Summary
Versioned schema validation
packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/..., packages/rs-platform-version/src/version/dpp_versions/..., packages/rs-platform-version/tests/...
Version 4 parsing rejects full-validation schemas that enable both document history and deletion. Protocol version mappings and parser regression tests cover the new dispatch and legacy behavior.
Legacy schema repair validation
packages/rs-dpp/src/data_contract/document_type/methods/validate_update/..., packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v4/mod.rs
Update validation permits the specific legacy repair from canBeDeleted: true to false when both versions keep history. Other configuration, schema, mutability, and property-name changes remain validated.
Delete transition validation and compatibility tests
packages/rs-drive-abci/src/execution/validation/state_transition/..., packages/rs-drive-abci/tests/supporting_files/..., packages/rs-drive/src/drive/..., packages/rs-platform-version/src/version/drive_abci_versions/...
Protocol 14 rejects deletes for keep-history document types as invalid paid transitions. Protocols 12 and 13 retain the previous internal-error result. Integration tests cover legacy contract repair and document preservation.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 62a49

The change is mergeable with no identified blocking risk.

Sequence Diagram(s)

sequenceDiagram
  participant DeleteTransition
  participant DocumentDeleteTransitionActionStructureValidationV1
  participant DataContract
  DeleteTransition->>DocumentDeleteTransitionActionStructureValidationV1: validate_structure_v1
  DocumentDeleteTransitionActionStructureValidationV1->>DataContract: look up document type
  DocumentDeleteTransitionActionStructureValidationV1-->>DeleteTransition: return invalid paid error for keep-history type
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 38 functions across 14 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR satisfies issue #3927. It rejects contradictory document types during versioned schema parsing, rejects deletes for keep-history document types before storage execution, returns an invalid paid…
Out of Scope Changes check ✅ Passed The changes remain within scope. The update-validation repair path, compatibility handling, fixture changes, and regression tests support backward compatibility and the linked issue requirements.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: rejecting deletes for contradictory keep-history document types.
Full details: Docstring Coverage

Explanation

Docstring coverage is 78.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 38 functions across 14 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/keep-history-delete-rejection

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.

@thepastaclaw

thepastaclaw commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 61st in line, estimated start in ~50 h (commit d4e3dca)
Estimated review time once started: ~1.6 h (two-phase automated review; median of recent runs).

  • Request priority review — tick this box and the review moves to the front of the queue.

@github-actions github-actions Bot added this to the v4.1.0 milestone Jul 23, 2026
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.21053% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.24%. Comparing base (d0a9a09) to head (afa3cca).
⚠️ Report is 1 commits behind head on v4.2-dev.

Files with missing lines Patch % Lines
...ocument_type/methods/validate_update/common/mod.rs 69.23% 8 Missing ⚠️
.../document/document_delete_transition_action/mod.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.2-dev    #4218      +/-   ##
============================================
+ Coverage     85.35%   87.24%   +1.89%     
============================================
  Files          2765     2795      +30     
  Lines        369457   364790    -4667     
============================================
+ Hits         315349   318274    +2925     
+ Misses        54108    46516    -7592     
Components Coverage Δ
dpp 87.36% <84.00%> (+2.90%) ⬆️
drive 86.57% <100.00%> (+1.73%) ⬆️
drive-abci 89.76% <0.00%> (+1.13%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.92% <ø> (ø)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 49.78% <ø> (+8.67%) ⬆️
🚀 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.

@coderabbitai

coderabbitai Bot commented Jul 23, 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 left a comment

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.

Final validation — Codex + Sonnet

This recreation of #3930 against protocol version 13 correctly rejects the contradictory documentsKeepHistory:true + canBeDeleted:true combination in try_from_schema_v3 (gated by full_validation and CONTRACT_VERSIONS_V5/protocol 13) while leaving the consensus-frozen v2 parser (protocol 12) untouched, and adds a matching drive-abci structure-validation guard (v1, gated by DRIVE_ABCI_VALIDATION_VERSIONS_V9/protocol 13) that turns already-deployed contradictory contracts' deletes into invalid-paid consensus errors instead of InternalError. I independently re-verified all four prior findings from PR #3930 against this exact head and confirm all four are FIXED in the recreated implementation. One non-blocking test-coverage gap remains in the paired protocol-12/13 delete regression: it only asserts aggregate result-bucket counts rather than the specific error identity, so an unrelated failure could false-positive the assertion.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — security-auditor (completed), gpt-5.6-sol — rust-quality (completed)
  • Verifier: claude-sonnet-5 — final-verifier
  • Sonnet reviewers: claude-sonnet-5 — general (completed), claude-sonnet-5 — security-auditor (failed), claude-sonnet-5 — rust-quality (failed), claude-sonnet-5 — security-auditor (completed), claude-sonnet-5 — rust-quality (completed)

🟡 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-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:538-554: Paired keep-history delete regression checks aggregate counts, not the specific error
  run_document_delete_on_document_type_that_keeps_history_at_protocol_version only asserts processing_result.invalid_paid_count() == expect_invalid_paid and a separate InternalError count. Since exactly one transition is submitted, any unrelated invalid-paid consensus error (e.g. a nonce or signature failure) at v13, or any unrelated InternalError at v12, would satisfy these assertions while the actual regression this test is meant to pin — the new keep-history structure guard at v13 and the historical Drive-layer InvalidDeletionOfDocumentThatKeepsHistory failure at v12 — silently stops firing. Asserting the concrete error identity (InvalidDocumentTransitionActionError with the keep-history message at v13, and the keep-history Drive error text inside the InternalError at v12) would make this regression fail loudly if the underlying guard regresses or is bypassed by some other error path.

Comment on lines +538 to +554
assert_eq!(
processing_result.invalid_paid_count(),
usize::from(expect_invalid_paid),
"unexpected invalid-paid classification at protocol version {protocol_version}"
);
assert_eq!(processing_result.invalid_unpaid_count(), 0);
assert_eq!(processing_result.valid_count(), 0);
let internal_error_count = processing_result
.execution_results()
.iter()
.filter(|result| matches!(result, StateTransitionExecutionResult::InternalError(_)))
.count();
assert_eq!(
internal_error_count,
usize::from(!expect_invalid_paid),
"unexpected InternalError classification at protocol version {protocol_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: Paired keep-history delete regression checks aggregate counts, not the specific error

run_document_delete_on_document_type_that_keeps_history_at_protocol_version only asserts processing_result.invalid_paid_count() == expect_invalid_paid and a separate InternalError count. Since exactly one transition is submitted, any unrelated invalid-paid consensus error (e.g. a nonce or signature failure) at v13, or any unrelated InternalError at v12, would satisfy these assertions while the actual regression this test is meant to pin — the new keep-history structure guard at v13 and the historical Drive-layer InvalidDeletionOfDocumentThatKeepsHistory failure at v12 — silently stops firing. Asserting the concrete error identity (InvalidDocumentTransitionActionError with the keep-history message at v13, and the keep-history Drive error text inside the InternalError at v12) would make this regression fail loudly if the underlying guard regresses or is bypassed by some other error path.

Suggested change
assert_eq!(
processing_result.invalid_paid_count(),
usize::from(expect_invalid_paid),
"unexpected invalid-paid classification at protocol version {protocol_version}"
);
assert_eq!(processing_result.invalid_unpaid_count(), 0);
assert_eq!(processing_result.valid_count(), 0);
let internal_error_count = processing_result
.execution_results()
.iter()
.filter(|result| matches!(result, StateTransitionExecutionResult::InternalError(_)))
.count();
assert_eq!(
internal_error_count,
usize::from(!expect_invalid_paid),
"unexpected InternalError classification at protocol version {protocol_version}"
);
assert_eq!(
processing_result.invalid_paid_count(),
usize::from(expect_invalid_paid),
"unexpected invalid-paid classification at protocol version {protocol_version}"
);
assert_eq!(processing_result.invalid_unpaid_count(), 0);
assert_eq!(processing_result.valid_count(), 0);
match (expect_invalid_paid, processing_result.execution_results().as_slice()) {
(true, [result]) => {
let message = format!("{result:?}");
assert!(
message.contains("keep history and therefore can not be deleted"),
"expected the keep-history InvalidDocumentTransitionActionError at v13, got: {message}"
);
}
(false, [StateTransitionExecutionResult::InternalError(message)]) => assert!(
message.contains("invalid deletion of document that keeps history")
|| message.to_lowercase().contains("keepshistory"),
"expected the historical keep-history Drive error at v12, got: {message}"
),
(_, results) => panic!(
"unexpected execution result at protocol version {protocol_version}: {results:?}"
),
}

source: ['codex']

@QuantumExplorer
QuantumExplorer changed the base branch from v4.1-dev to v4.2-dev July 24, 2026 20:11
@github-actions github-actions Bot modified the milestones: v4.1.0, v4.2.0 Jul 24, 2026
…y repairs

Activate the schema rejection and delete guard only at protocol 14,
leaving protocol 12 and 13 validation maps unchanged. Wrap the current
ranked-schema parser and retain all existing delete structure checks.

Permit owners to disable the unusable delete flag on legacy keep-history
document types while preserving history, other immutable configuration,
and schema compatibility. Cover repair and a subsequent signed update
against persisted protocol 13 data.

Merge the current v4.2-dev target to use its protocol 14 method maps.

@QuantumExplorer QuantumExplorer left a comment

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.

Approved

@QuantumExplorer
QuantumExplorer merged commit 88b0427 into v4.2-dev Sep 8, 2026
10 of 11 checks passed
@QuantumExplorer
QuantumExplorer deleted the fix/keep-history-delete-rejection branch September 8, 2026 21:33
QuantumExplorer added a commit that referenced this pull request Sep 8, 2026
cargo fmt --check fails on v4.2-dev since #4218 landed this test unformatted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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

2 participants