diff options
| author | Adam Soltys <[email protected]> | 2026-05-17 23:21:39 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-05-17 23:21:39 -0700 |
| commit | 609fcebe634b499c1133ea26f162c24e1c76d6d4 (patch) | |
| tree | b1f483eaaaae6951385479b579ae77639e1c4f44 /lib/sockets.js.map | |
| parent | 3c4d0b6914bbc7ecf49312a264d55af3c2e2d7a6 (diff) | |
| download | coinos-server-609fcebe634b499c1133ea26f162c24e1c76d6d4.tar.gz coinos-server-609fcebe634b499c1133ea26f162c24e1c76d6d4.zip | |
serialize limit check + reservation under per-asset mutex
The previous check at debit() read ${type}:limit, compared amount, and
deferred any decrement to the next freezeCheck tick (every 10s). That made
the check non-atomic: a burst of concurrent withdrawals could each pass
the same stale limit value before any reconciliation happened. Combined
with the /gateway abuse, this allowed the hot wallet to be drained.
withLimitLock() serializes the check + db.decrBy inside a per-type async
mutex, and freezeCheck also takes the lock when overwriting limits so its
10s reconciliation can't race in-flight checks. Single-process only;
distributed lock or TigerBeetle reservation needed if app ever scales out.
Trade-off: decremented limits are not refunded on payment failure — they
get reset by the next freezeCheck within 10s. Acceptable false-deny
window in exchange for correctness against bursts.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Diffstat (limited to 'lib/sockets.js.map')
0 files changed, 0 insertions, 0 deletions
