Description
Invoices that are cancelled or abandoned have their storage entries left in an ambiguous terminal state or removed outright, destroying the audit trail needed for dispute resolution and off-chain reporting. A soft-delete mechanism must mark an invoice as Deleted in its status enum while writing an immutable tombstone record capturing the deletion ledger and acting address.
Technical Context
contracts/split/src/types.rs — add InvoiceStatus::Deleted variant and a Tombstone { invoice_id: u64, deleted_at_ledger: u32, deleted_by: Address } struct; contracts/split/src/storage.rs — add DataKey::Tombstone(u64) persistent key; contracts/split/src/lib.rs — delete_invoice(invoice_id: u64) entry point that requires auth from the creator, transitions status to Deleted, and writes Tombstone atomically; contracts/split/src/error.rs — add Error::InvoiceDeleted returned by all mutating entry points when status is Deleted, and Error::FundsUnclaimed to block deletion of invoices with non-zero balances.
Acceptance Criteria
Description
Invoices that are cancelled or abandoned have their storage entries left in an ambiguous terminal state or removed outright, destroying the audit trail needed for dispute resolution and off-chain reporting. A soft-delete mechanism must mark an invoice as
Deletedin its status enum while writing an immutable tombstone record capturing the deletion ledger and acting address.Technical Context
contracts/split/src/types.rs— addInvoiceStatus::Deletedvariant and aTombstone { invoice_id: u64, deleted_at_ledger: u32, deleted_by: Address }struct;contracts/split/src/storage.rs— addDataKey::Tombstone(u64)persistent key;contracts/split/src/lib.rs—delete_invoice(invoice_id: u64)entry point that requires auth from the creator, transitions status toDeleted, and writesTombstoneatomically;contracts/split/src/error.rs— addError::InvoiceDeletedreturned by all mutating entry points when status isDeleted, andError::FundsUnclaimedto block deletion of invoices with non-zero balances.Acceptance Criteria
delete_invoice()writes bothInvoiceStatus::Deletedand theTombstonerecord in the same invocation with no intermediate observable statepay(),release_funds(),update_split_ratio()) returnError::InvoiceDeletedimmediately when invoice status isDeleteddelete_invoice()returnsError::FundsUnclaimedand makes no state changes whenTotalContributed > TotalReleasedget_tombstone(invoice_id: u64) -> Tombstoneview function is exposed for off-chain queries and returnsError::NotDeletedif the invoice is not in theDeletedstatecontracts/split/src/tests/soft_delete_tests.rsverify tombstone field correctness, post-delete mutation rejection, query availability, and the unclaimed-funds guard