7108f77bf92ffe8839a5368df6666b431abb76e2
The PvP_Characters table tallied wins and losses but held no token balance and no purchase endpoint: earnings were client-reported (winners credited themselves locally), so verifying only a spend would have been security theater — a cheater could mint tokens by self-reporting battle wins. The server is now the ledger authority (FRE-142): registerCharacter seeds the balance from the client cache exactly once and reconciles it on every sync; battleResult requires the verified Lineage JWT bearer, rejects non-participants, and credits the winner via the reward formula ported from the client (computed from both registered rows); a new purchase mutation checks cost, balance, and cooldown in one conditional UPDATE scoped to the caller-owned row, so the spend is atomic under concurrency and rate limiting survives cold starts. Schema changes ship as an idempotent pvp-schema bootstrap (pvpTokens, ownerUserId, lastPurchaseAt), mirroring the analytics-schema pattern. Client-side counterpart shipped in the lineage repo; design record in its docs/security/pvp-server-side-purchases.md. Tests: pvp-schema (DDL + legacy-column migration + idempotency), pvp-rewards (formula parity with the client port, cost table), pvp-ledger (seed-once reconciliation, owner binding, participant verification, atomic spend, cooldown, refusal reasons) — 31 new tests against in-memory SQLite; suite 572 pass (1 pre-existing audit date-filter failure, unrelated).
Description
No description provided
Languages
TypeScript
64.5%
JavaScript
33.6%
CSS
1.2%
HTML
0.4%
Shell
0.3%