Skip to content

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

Description

@thephez

Expected Behavior

A document type that sets both documentsKeepHistory: true and canBeDeleted: true is self-contradictory: rs-drive unconditionally refuses to delete a document whose type keeps history, so canBeDeleted: true advertises a capability the storage layer will always reject.

The contradiction should be caught at the earliest possible point:

  1. Contract creation — validation should reject a document type that sets both documentsKeepHistory: true and canBeDeleted: true, so the bad contract can never be deployed. (Mirrors the existing cross-flag rule ContestedUniqueIndexOnMutableDocumentTypeError.)
  2. Delete state transition — even for already-deployed contracts, a delete targeting a keep-history document type should be rejected up front by the delete-transition structure validator as a normal invalid (paid) state transition, with a clear consensus error.

In both cases an SDK user should get an immediate, legible error instead of the delete failing as an internal error.

Current Behavior

Nothing catches the contradiction up front, and the delete fails as an internal error rather than a clean rejection:

  • Contract creation accepts the combination. DocumentType::try_from_schema parses documentsKeepHistory: true + canBeDeleted: true with no error. There is no cross-flag validation for this pair. (Live example: testnet contract 5CBPiadGmx3Zsjc26g5onopcx7pdxHPbrRAUD2T2yAbC, document type note, has all of documentsMutable, documentsKeepHistory, and canBeDeleted set to true.)
  • The delete-transition structure validator does not check documents_keep_history(). It only checks documents_can_be_deleted() (document_delete_transition_action/advanced_structure_v0/mod.rs), so the delete passes broadcast/structure validation.
  • At execution, Drive refuses the delete with a DriveError, not a consensus error. force_delete_document_for_contract_operations_v0 returns DriveError::InvalidDeletionOfDocumentThatKeepsHistory when documents_keep_history() is true. In the rs-drive-abci processing path this surfaces as ExecutionResult::InternalError — the transition is classified as neither valid nor invalid (valid_count == 0, invalid_paid_count == 0).

An internal error means "the node failed to process this", not "this transition is invalid". This is the wrong classification for what is really an invalid transition, and it offers no clean consensus accept/reject for a client to act on.

Observed processing-result classification for the doomed delete (from the reproduction test below):

valid=0  invalid_paid=0  invalid_unpaid=0
result[0]: InternalError("storage: drive: invalid deletion of document that keeps history error: this document type keeps history and therefore can not be deleted")

Possible Solution

Add guards at both upper layers:

  1. rs-dpp — DocumentType::try_from_schema: reject documents_keep_history && documents_can_be_deleted in the flag-parsing region, next to the existing ContestedUniqueIndexOnMutableDocumentTypeError cross-flag check. (This is a consensus/validation change and cannot retroactively fix already-deployed contracts.)
  2. rs-drive-abci — delete-transition structure validator: add a documents_keep_history() check beside the existing documents_can_be_deleted() check in document_delete_transition_action/advanced_structure_v0/mod.rs, returning a consensus error so a delete against a keep-history type becomes a normal invalid (paid) transition instead of an internal error. This fixes the classification even for contracts that already exist on-chain.

Together: (1) prevents new contradictory contracts; (2) makes any already-deployed contradictory contract fail fast and legibly as an invalid transition.

Steps to Reproduce (for bugs)

  1. Register (or use the existing testnet) data contract with a document type setting both documentsKeepHistory: true and canBeDeleted: true — e.g. testnet contract 5CBPiadGmx3Zsjc26g5onopcx7pdxHPbrRAUD2T2yAbC, document type note. Contract creation succeeds (it should be rejected).
  2. Create a document of that type. Succeeds.
  3. Submit a delete state transition for that document. It passes structure validation.
  4. At execution, Drive refuses it with InvalidDeletionOfDocumentThatKeepsHistory, surfaced as ExecutionResult::InternalErrorvalid_count == 0 and invalid_paid_count == 0. The transition is neither committed nor cleanly rejected as invalid.

Reproduced end-to-end against testnet via @dashevo/evo-sdk (sdk.documents.create then sdk.documents.delete against the contract above; the delete returns the InvalidDeletionOfDocumentThatKeepsHistory error), and in-process by failing tests that pin both layers:

  • packages/rs-dpp/.../try_from_schema/v2/mod.rs: doctype_keep_history_with_can_be_deleted_rejected (fails today) and doctype_keep_history_with_can_be_deleted_false_accepted (passes — guards against an over-broad fix).
  • packages/rs-drive-abci/.../batch/tests/document/deletion.rs: test_document_delete_on_document_type_that_keeps_history_is_rejected (fails today), with fixture tests/supporting_files/contract/note/note-contract-keep-history-and-can-be-deleted.json.

Context

An SDK user can build and deploy a contract whose schema declares its documents deletable, then find that deletes for that type fail with an internal error rather than a clean, actionable rejection. The contradiction is invisible until runtime and offers no feedback at the point where it could be fixed (contract creation). Catching it early turns a confusing internal-error failure into an immediate validation error naming the conflicting flags.

Your Environment

  • Version used: branch v3.1-dev; reproduced at PlatformVersion::latest() (protocol version 12, try_from_schema v2). Drive-layer guard present since at least the InvalidDeletionOfDocumentThatKeepsHistory test (PR test(drive): improve document module test coverage #3333).
  • Environment name and version (e.g. Chrome 39, node.js 5.4): node.js 22.22; @dashevo/evo-sdk 4.0.0-rc.2
  • Operating System and version (desktop, server, or mobile): Linux (testnet)
  • Link to your project: testnet contract 5CBPiadGmx3Zsjc26g5onopcx7pdxHPbrRAUD2T2yAbC (document type note)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions