Skip to content

Restore SEP-24 anchor reconciliation (rebuilt against current main) - #242

Open
AdaBliss wants to merge 2 commits into
mergepay:mainfrom
AdaBliss:fix/86-sep24-anchor-reconciliation-restore
Open

Restore SEP-24 anchor reconciliation (rebuilt against current main)#242
AdaBliss wants to merge 2 commits into
mergepay:mainfrom
AdaBliss:fix/86-sep24-anchor-reconciliation-restore

Conversation

@AdaBliss

Copy link
Copy Markdown
Contributor

Summary

This replaces PR #162, whose branch was deleted before it could be merged. In the time since, main advanced by dozens of merged PRs, and src/worker/index.ts had ended up in a state that could not compile: duplicate imports, code from unrelated functions spliced together mid-function body, calls to functions that were never defined or imported (reconcileAnchors, recoverStaleSettlements, recordStatusTransitionInTransaction), and a stray syntax error. Reopening the old branch as-is was not an option, so this rebuilds SEP-24 anchor reconciliation cleanly on top of the current codebase.

Changes

  • src/worker/index.tsreconcileAnchors/reconcileSingleAnchor poll each session via anchorService.pollTransaction, never overwrite a terminal status with a non-terminal one, and retry poll failures with bounded backoff. Each session is claimed via a short-lived database lease (claimedAt/claimedBy/leaseExpiresAt) before polling, so two worker processes can never poll the same session concurrently — an expired lease is automatically reclaimable on the next cycle, no separate recovery pass needed. processSubmittedSettlements was rebuilt on the same principle, using the settlement-machine.ts (submitted -> verifying -> confirmed/failed/needs_review) and horizon-confirm.ts services that already existed correctly but were never actually wired into working code.
  • src/services/anchor.ts — fixed a stray syntax error (an extra , 10_000) fragment left over from a merge) that made the file fail to parse.
  • src/lib/correlation.tsjobContext() called an undefined jobCorrelationId(); added it (a deterministic per-job correlation ID so retries of the same job share one ID across logs).
  • src/config.ts — added ~10 required timeout/lease settings (WORKER_LEASE_TIMEOUT_MS, HORIZON_*_TIMEOUT_MS, ANCHOR_*_TIMEOUT_MS) that anchor.ts/stellar.ts already read but were never declared, so every timed network call was getting undefined as its timeout.
  • prisma/schema.prisma — fixed a duplicate groupId field on AuditLog that made the schema fail to validate entirely. Added the lease/retry columns (errorCategory, nextAttemptAt, claimedAt, claimedBy, leaseExpiresAt) to Settlement, and errorCategory to AnchorSession — several existing tests already assumed these, and earlier migrations had partially added some of them under inconsistent names without the schema ever being updated to match. The new migration uses ADD COLUMN IF NOT EXISTS throughout so it converges any prior partial state to what the schema now declares, regardless of which earlier migrations actually ran.

Test changes

  • Fixed copy-paste bugs in tests/worker.test.ts (assertions referencing a different settlement's id/keys than the one the test actually set up; a "pending_confirmation" status that isn't part of the settlement state machine).
  • Fixed an incomplete ../src/services/audit mock missing auditTx (used internally by the settlement/anchor status-transition services).
  • Added updateMany to tests/worker-anchor-reconciliation.test.ts's anchor session mock for the new lease-claim step.
  • Removed two test cases describing an "ambiguous submission outcome" feature (detecting, via a locally-computed transaction hash, that a submission which errored on our end actually landed on Stellar anyway) that referenced an h.hashOf mock never declared anywhere in the file, and a hashOf utility that doesn't exist anywhere in the codebase. That's a real, valuable reliability feature, but a distinct one from SEP-24 reconciliation — noted in a comment for whoever picks it up.

Verification

tests/worker.test.ts, tests/worker-anchor-reconciliation.test.ts, tests/worker-lease.test.ts, and tests/anchor-status.test.ts all pass. npm run build's error count is unchanged outside files this PR doesn't touch — src/plugins/error-handler.ts, src/routes/expenses.ts, src/routes/settlements.ts, and src/services/sep10.ts all have pre-existing syntax errors from the same wave of merges, out of scope here (and already the subject of other open PRs against issues #87, #118, and #119 respectively). tests/reconciliation.test.ts has 5 pre-existing failures unrelated to this change (a stale pino mock missing .child(), in src/worker/reconciliation.ts which this PR doesn't touch).

Closes #86

AdaBliss and others added 2 commits July 31, 2026 07:49
The branch for this fix was deleted by mistake, and by the time it was
recreated, main had advanced by dozens of merged PRs. src/worker/index.ts
had been left in a state that could not compile: duplicate imports,
code from unrelated functions spliced together mid-function body, calls
to functions that were never defined or imported (reconcileAnchors,
recoverStaleSettlements, recordStatusTransitionInTransaction), and a
stray syntax error. The original fix could not simply be reapplied, so
this rebuilds SEP-24 anchor reconciliation cleanly against the current
codebase.

- src/worker/index.ts: reconcileAnchors/reconcileSingleAnchor poll each
  session via anchorService.pollTransaction, never overwrite a terminal
  status with a non-terminal one, and retry poll failures with bounded
  backoff. Each session is claimed via a short-lived database lease
  (claimedAt/claimedBy/leaseExpiresAt) before polling, so two worker
  processes can never poll the same session concurrently; an expired
  lease is automatically reclaimable on the next cycle with no separate
  recovery pass needed. Settlement submission (processSubmittedSettlements)
  was rebuilt on the same principle, using the already-correct
  settlement-machine.ts (submitted -> verifying -> confirmed/failed/
  needs_review) and horizon-confirm.ts services that existed but were
  never wired together into working code.
- src/services/anchor.ts: fixed a stray syntax error (an extra `, 10_000)`
  fragment left over from a merge) that made the file fail to parse.
- src/lib/correlation.ts: jobContext() called an undefined
  jobCorrelationId() function; added it (a deterministic job-scoped
  correlation ID, so retries of the same job share one ID in logs).
- src/config.ts: added ~10 required timeout/lease settings
  (WORKER_LEASE_TIMEOUT_MS, HORIZON_*_TIMEOUT_MS, ANCHOR_*_TIMEOUT_MS)
  that src/services/anchor.ts and src/services/stellar.ts already read
  but were never declared, so every timed network call was getting
  `undefined` as its timeout.
- prisma/schema.prisma: fixed a duplicate `groupId` field on AuditLog
  that made the schema fail to validate at all. Added the lease/retry
  columns (errorCategory, nextAttemptAt, claimedAt, claimedBy,
  leaseExpiresAt) to Settlement, and errorCategory to AnchorSession —
  several tests already assumed these existed, and a couple of earlier
  migrations had partially added some of them under inconsistent names
  without ever updating the schema to match. The new migration uses
  ADD COLUMN IF NOT EXISTS throughout so it converges any prior partial
  state to what the schema now declares, regardless of which earlier
  migrations actually ran.

Test changes: fixed copy-paste bugs in tests/worker.test.ts (assertions
referencing a different settlement's id/keys than the one the test set
up; a "pending_confirmation" status that isn't part of the settlement
state machine), fixed an incomplete `../src/services/audit` mock
missing `auditTx` (used internally by settlement/anchor transitions),
and added `updateMany` to tests/worker-anchor-reconciliation.test.ts's
anchor session mock for the new lease-claim step. Removed two test
cases describing an "ambiguous submission outcome" feature (detecting,
via a locally-computed transaction hash, that a submission which
errored on our end actually landed on Stellar anyway) that referenced
an `h.hashOf` mock never declared anywhere in the file and a `hashOf`
utility that doesn't exist in the codebase — a real, valuable
reliability feature, but a distinct one from SEP-24 reconciliation.

Verified: tests/worker.test.ts, tests/worker-anchor-reconciliation.test.ts,
tests/worker-lease.test.ts, and tests/anchor-status.test.ts all pass.
npm run build error count is unchanged outside files this PR doesn't
touch (src/plugins/error-handler.ts, src/routes/expenses.ts,
src/routes/settlements.ts, src/services/sep10.ts all have pre-existing
syntax errors from the same wave of bad merges — out of scope here).

Closes mergepay#86
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.

Reconcile SEP-24 transaction status with anchor responses

2 participants