docs(sec): SEC-16 — безвозвратная утечка ренты PDA-ставки с кошелька игрока

Найдено при приёмке боевого домена smart-updown.binom.pw (симптом «SOL не обновляется»).

- SEC-16: при create_bet ренту PDA-аккаунта ставки (1 559 040 lamports = (96+32+128)*6960)
  платит БЕТТОР, а не шлюз; баланс шлюза-админа при этом не меняется (замер whose-fee.cjs).
  Аккаунт не закрывается никогда (в контракте 9ALs четыре инструкции, ни одной close = bettor)
  → рента теряется безвозвратно. В цепи 75 аккаунтов = 0.116928 SOL заперто, растёт линейно.
  Вердикт: на тестах терпимо (маскируется краном, 5 SOL = ~3207 раундов), перед mainnet — блокер.
- TASKS.md: фича «если цена не изменилась — подождать ещё 15 с» (НЕ доделана), заметка про
  боевой домен на общем TS-шлюзе и лаг крана 8–13 с.
- tools: доказательные скрипты (whose-fee, bet-pda-rent, faucet-latency, sol-balance-cycle,
  settle-math-test, backend-full-test) + README с питфоллами замера.
- k8s/smart-updown-api-ingress.yaml: /api,/ws вынесены вне helm (релиз игры их сносил).
This commit is contained in:
Hermes Agent
2026-09-15 05:57:34 +03:00
parent b8a1f8dc39
commit 101a4b60a9
10 changed files with 603 additions and 4 deletions
+28
View File
@@ -91,6 +91,34 @@
## Ссылки
- Решения/экономика: `docs/DECISIONS.md`, `docs/ECONOMY.md`
- Карта репозиториев: `README.md`
## ⭐ Добавлено 2026-09-15 — боевой домен и находки приёмки
**Где мы:** боевой домен `smart-updown.binom.pw` переведён на **тот же TS-шлюз** (`:8895`),
что и `testfront.binom.pw` (некастодиальный Kotlin-бэк выключен, обратимо). Пути `/api`,`/ws`
вынесены в **отдельный ingress вне helm** — релиз игры их больше не сносит.
**Найдено при приёмке живьём:**
- `SEC-16` — безвозвратная утечка ренты PDA-аккаунта ставки: **1 559 040 lamports с кошелька
игрока за каждую ставку**, аккаунт не закрывается никогда. Подробности — `docs/SECURITY.md`.
*Вердикт: на тестах терпимо (маскируется краном), перед mainnet — блокер.*
- Расчёт (выплаты) — **корректен 3/3**: выигрыш `1.3×`, проигрыш `0`, ничья — возврат залога.
- Кран зачисляет на цепь с лагом **8–13 с** (шлюз рапортует мгновенно) — отсюда ложный симптом
«SOL не обновился».
**Фича на будущее — «ждать, если цена не изменилась» (НЕ ДОДЕЛАНА):**
- [ ] **TODO** `[pump.meta]` Если по сделанной ставке цена на момент экспирации **не изменилась**
(`exit == entry`, ничья), нужно **подождать ещё 15 секунд** и попробовать взять цену заново —
чтобы ставка не закрывалась возвратом залога на «плоской» цене, а получала шанс на реальный исход.
**Суть:** ставка не меняется → ждём (не финализируем сразу возвратом).
- [ ] **TODO** Вынести окно ожидания в конфиг: сейчас `PRICE_WAIT_TIMEOUT_MS` (default `15000`)
отвечает лишь за **ожидание первой цены от NATS** (`relayer/src/priceSource.ts:9`) и к ничьей
отношения не имеет — нужен **отдельный** параметр (напр. `TIE_WAIT_TIMEOUT_MS`) и логика
отложенного ретрая в `settle.ts` (`priceAtOrBefore(expireTime*1000)` — сейчас берётся
фиксированный момент, ретрай каждые 2 с вечно получит ту же пару, см. `SEC-9`).
- [ ] **TODO** Решить, что делать по истечении окна ожидания: возврат залога (текущее поведение
`SEC-9`) или иной исход.
## Фаза 7 — Цена из NATS и деплой в куб (2026-09-13)
- [x] **DONE** ADR-007: цена SOL из NATS (`market.price.solusdt`), заглушка удалена
- [x] **DONE** `natsPrice.ts` — подписка NATS, последняя цена, `waitForPrice`, ×1e8 строковым парсером