Skip to content

fix(claims): make verifyClaim block-atomic, idempotent, non-swallowing#55

Merged
lucca65 merged 1 commit into
masterfrom
fix/verifyclaim-block-atomic-idempotent
Jul 6, 2026
Merged

fix(claims): make verifyClaim block-atomic, idempotent, non-swallowing#55
lucca65 merged 1 commit into
masterfrom
fix/verifyclaim-block-atomic-idempotent

Conversation

@lucca65

@lucca65 lucca65 commented Jul 6, 2026

Copy link
Copy Markdown
Member

Problem

Claims can end up stuck pending in Postgres while approved on chain, making them "unapprovable": the UI reads the DB status, offers a vote, and the chain rejects it with Can't vote on already verified claim. Confirmed on muda claim 19879 — chain had 2/2 approving checks (majority reached), Postgres had only 1; validator ivamdasilvac's verifyclaim was never indexed.

Root cause

The old verifyClaim ran its writes inside a nested db.withTransaction on a separate connection and swallowed every error with .catch(logError). On a transient failure the check write rolled back while demux's outer block tx still committed _index_state past the block — the vote was lost and never retried.

Fix

  • Block-atomic: write on the block-level db so the check insert, status recompute, and the _processed_actions ledger row commit or roll back together. A transient error now rolls back the whole block → demux retries it.
  • Idempotent: INSERT ... ON CONFLICT (claim_id, validator_id) DO NOTHING (no count-then-insert race; check_added trigger doesn't re-fire on replay).
  • No crash-loop: resolve claim/action/verifier before the insert; skip-and-log if any is missing (id drift / DB behind chain) instead of throwing — a throw here would deterministically crash-loop the indexer under pm2. Matches assignRole/upsertAction.
  • usages_left: guarded with > 0 (no negatives).
  • Tripwire: GetActionsReader._loadNextBatch now logs loudly if get_actions returns a first account_action_seq ahead of the requested cursor (a history hole).

Deploy notes

  • Hard-depends on checks_claim_validator_uniq + _processed_actions (both present in prod).
  • After restart, watch pm2 jlist restart_time per the runbook (crash-loop guard).
  • Recovers stuck claims via reindex of the affected block range (now idempotent), e.g. claim 19879.

Staff-reviewed; verdict SHIP.

🤖 Generated with Claude Code

The old verifyClaim opened a nested db.withTransaction on a SEPARATE
connection and swallowed every error via .catch(logError). On a transient
failure the vote's check write rolled back while demux's outer block tx
still committed _index_state past the block — the vote was lost and never
retried. Root cause of claims stuck `pending` in Postgres while `approved`
on chain (e.g. muda claim 19879, verifier ivamdasilvac vote dropped).

- Write on the block-level `db` so the check insert, status recompute, and
  the _processed_actions ledger row commit or roll back together; a
  transient error now rolls back the whole block and demux retries it.
- Idempotent insert: INSERT ... ON CONFLICT (claim_id, validator_id) DO
  NOTHING — no count-then-insert race, and the check_added trigger does not
  re-fire on replay.
- Resolve claim / action / verifier BEFORE the insert; skip-and-log if any
  is missing (id drift / DB behind chain) instead of throwing. A throw here
  would deterministically crash-loop the indexer under pm2 and halt every
  community. Matches the assignRole / upsertAction skip-and-log pattern.
- Guard the usages_left decrement with `usages_left > 0` (no negatives).

GetActionsReader: add a seq-continuity tripwire in _loadNextBatch that logs
loudly when get_actions returns a first account_action_seq ahead of the
requested cursor (a history hole), so a future gap surfaces immediately
instead of weeks later as a stuck claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lucca65
lucca65 merged commit 68ef3b6 into master Jul 6, 2026
1 check passed
@lucca65
lucca65 deleted the fix/verifyclaim-block-atomic-idempotent branch July 6, 2026 15:50
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.

1 participant