Skip to content

feat: enforce request_id idempotency in vault deduct#389

Open
Harbduls wants to merge 1 commit into
CalloraOrg:mainfrom
Harbduls:feature/vault-request-id-idempotency
Open

feat: enforce request_id idempotency in vault deduct#389
Harbduls wants to merge 1 commit into
CalloraOrg:mainfrom
Harbduls:feature/vault-request-id-idempotency

Conversation

@Harbduls
Copy link
Copy Markdown

Closes #350

Add persistent processed-request markers so a backend retry with the same request_id is rejected, giving safe at-least-once retry semantics.

Changes:

  • StorageKey::ProcessedRequest(Symbol) new temporary-storage key; written on every successful deduct/batch_deduct where request_id is Some(id); auto-expires after ~30 days (REQUEST_ID_BUMP_AMOUNT).
  • VaultError::DuplicateRequestId (code 28) returned when a deduct is attempted with a request_id that was already processed within the retention window.
  • deduct(): idempotency check before any state mutation; marker written after successful balance update, before token transfer.
  • batch_deduct(): full-pass duplicate check (against storage AND within the batch itself) before any state write; all markers written atomically after the balance update.
  • is_request_processed(request_id): new public view that returns true when the marker is present in temporary storage.
  • REQUEST_ID_BUMP_THRESHOLD / REQUEST_ID_BUMP_AMOUNT constants added.
  • Module declarations added to lib.rs for all test sub-modules.
  • contracts/vault/STORAGE.md updated with ProcessedRequest key, temporary-storage TTL table, retention policy, and version 1.2 entry.
  • contracts/vault/src/test_idempotency.rs: 14 new tests covering duplicate rejection, distinct ids, None pass-through, failed-deduct non-marking, batch atomicity, intra-batch duplicate detection, and is_request_processed view correctness.

Add persistent processed-request markers so a backend retry with the
same request_id is rejected, giving safe at-least-once retry semantics.

Changes:
- StorageKey::ProcessedRequest(Symbol)  new temporary-storage key;
  written on every successful deduct/batch_deduct where request_id is
  Some(id); auto-expires after ~30 days (REQUEST_ID_BUMP_AMOUNT).
- VaultError::DuplicateRequestId (code 28)  returned when a deduct
  is attempted with a request_id that was already processed within the
  retention window.
- deduct(): idempotency check before any state mutation; marker written
  after successful balance update, before token transfer.
- batch_deduct(): full-pass duplicate check (against storage AND within
  the batch itself) before any state write; all markers written atomically
  after the balance update.
- is_request_processed(request_id): new public view that returns true
  when the marker is present in temporary storage.
- REQUEST_ID_BUMP_THRESHOLD / REQUEST_ID_BUMP_AMOUNT constants added.
- Module declarations added to lib.rs for all test sub-modules.
- contracts/vault/STORAGE.md updated with ProcessedRequest key,
  temporary-storage TTL table, retention policy, and version 1.2 entry.
- contracts/vault/src/test_idempotency.rs: 14 new tests covering
  duplicate rejection, distinct ids, None pass-through, failed-deduct
  non-marking, batch atomicity, intra-batch duplicate detection, and
  is_request_processed view correctness.
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 28, 2026

@Harbduls Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Vault: enforce request_id idempotency in deduct to prevent double-metering

1 participant