Commit Graph
3 Commits
Author SHA1 Message Date
jwinterm 89398335c7 feat: productionize paid modes and PvP capture 2026-07-21 20:41:27 -04:00
jwinterm db5ed2e8aa payouts: defer when Monero outputs are locked instead of failing
Spent outputs (including change) lock for ~10 blocks on Monero. When a
payout batch has no unlocked balance to cover it, attempting transfer_split
fails noisily and, for batches, gets parked in needs_review for an operator.

Add a pre-flight unlocked-balance check that reverts the batch to 'pending'
(re-checked every new-block tick and batch interval) so it sends as soon as
outputs unlock — no manual intervention. Also classify insufficient-funds
RPC errors as pre-broadcast/safe-to-retry: single -> pending (not failed),
batch -> pending without an operator alert (not needs_review), since no tx
went on-chain and there's no double-pay risk.
2026-06-01 08:17:05 -04:00
jwinterm d07967c3d1 security: fix batch-payout tx_hash collision and double-pay risk
transfer_split sends one on-chain tx to many destinations, so payout rows
in a batch share a tx_hash. The old UNIQUE index on payouts.tx_hash made
the 2nd+ row fail to record, leaving funds sent but rows stranded in
'processing' and re-sendable by the retry service.

- Migration 015 drops the payouts tx_hash unique index (idempotency is
  enforced by idx_payouts_one_per_game) and extends that guard to cover
  'processing' so no second payable row can be inserted mid-flight.
- Batch completion now updates the whole batch in a single transaction via
  id = ANY(...), so rows can't be left half-completed. Per-user stats use
  BIGINT arithmetic in SQL (no JS float).
- Ambiguous batch failures are marked 'needs_review' (which the retry
  service does NOT pick up) instead of 'failed', preventing a double-send
  when transfer_split may have already broadcast. Operator is alerted.

Adds batchPayout.test.js covering the shared-tx_hash completion and the
needs_review failure path. Full suite: 12 suites / 115 tests.
2026-05-31 09:26:14 -04:00