Skip to content

feat(disputes): dispute workflow, evidence and authorized on-chain resolution API (#247) - #268

Open
garbaaandekuba-source wants to merge 1 commit into
Lumina-eX:mainfrom
garbaaandekuba-source:feature/dispute-resolution-247
Open

garbaaandekuba-source wants to merge 1 commit into
Lumina-eX:mainfrom
garbaaandekuba-source:feature/dispute-resolution-247

Conversation

@garbaaandekuba-source

Copy link
Copy Markdown

Summary

Adds a secure backend workflow for disputes: submission, evidence, authorized resolution, and tracking of on-chain resolution state with retry support.

Closes #247

Changes

  • Migration scripts/00N_disputes.sql: disputes, dispute_evidence, dispute_events (audit trail). Unique partial index allows one unresolved dispute per contract; UNIQUE (dispute_id, uri) prevents duplicate evidence
  • API
    • POST /api/contracts/:id/disputes (participants only; contract status must be eligible)
    • POST /api/disputes/:id/evidence (participants; https/ipfs references with optional sha256; duplicates are no-ops)
    • GET /api/disputes/:id (participants and arbitrators; returns evidence and audit events)
    • POST /api/disputes/:id/resolve (arbitrators only, not a party to the contract; reason and metadata required)
  • Resolution state machine: open → resolution_pending (tx hash stored) → resolved (confirmed), with resolution_failed as a recoverable state. Pending and confirmed are tracked separately (resolution_submitted_at vs resolution_confirmed_at)
  • No double resolution: an atomic conditional UPDATE claims the dispute; concurrent or repeated calls get 409/202 and never submit twice. Retries after failure must use the same outcome; stuck pending claims (crash before submission) become retryable after 5 minutes
  • Consistency with the chain: lib/disputes.ts#reconcileDispute checks pending transactions and moves the dispute to resolved or resolution_failed; it runs on GET and on repeated resolve
  • Escrow boundary: lib/escrow-chain.ts (submitResolution, getTxStatus). Backend checks are an extra layer; the contract remains the final authority on who may resolve
  • Every lifecycle step (created, evidence_added, resolution_started/submitted/confirmed/failed) is written to dispute_events in the same statement as the state change
  • Consistent JSON errors (400/401/403/404/409/502); private data is returned only to participants and arbitrators
  • Docs: docs/dispute-resolution-api.md; DISPUTE_ARBITRATOR_IDS added to env.example

Testing

  • Vitest suite for authorization, eligibility, validation, duplicate dispute/evidence, resolve success, pending confirmation, chain failure and concurrent resolution
  • npx vitest run, npm run lint and npm run build pass locally
  • DB constraints verified manually in Neon (duplicate dispute, duplicate evidence, single claim)

Notes

  • [Describe the escrow adapter status: wired to , or left as the integration boundary]
  • Arbitrators are configured via DISPUTE_ARBITRATOR_IDS unless the repo gets a role system
  • Out of scope: updating the contract's own status after resolution and notifications

@SudiptaPaul-31

Copy link
Copy Markdown
Collaborator

@garbaaandekuba-source fix ci

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.

[Feature]: Dispute Resolution Workflow & Authorization API

2 participants