Объяснение без кода: как игрок получает результат спина, где лежат деньги, и что именно может (и не может) сделать наш сервер. Для нетехнического читателя.
Коротко:
ШАГ 1 — ОТКРЫТИЕ СЕССИИ (один раз)
┌──────────┐ «хочу играть, вот бюджет $200» ┌─────────────────┐
│ ИГРОК │ ───────────────────────────────────────▶ │ НАШ СЕРВЕР │
│ (браузер)│ │ │
└──────────┘ сервер придумал СЕКРЕТНЫЙ СИД │ секретный сид ──┼──▶ спрятан, никто не видит
▲ │
│ на чейн уходит только ХЭШ сида (= «обещание»: │
│ поменять сид уже невозможно) │
│ ┌─────────────────────────────────────────────┴───┐
│ │ БЛОКЧЕЙН: openSession(хэш сида, бюджет) │
│ │ · замок на $200 игрока │
│ └─────────────────────────────────────────────────┘
│
ШАГ 2 — ИГРА (много быстрых спинов)
┌──────────┐ спин №5: «ставлю $2, вот моя монета-число» ┌─────────────────┐
│ ИГРОК │ ──────────────────────────────────────────────▶ │ НАШ СЕРВЕР │
│ │ ◀── «выпало 3 вишни, выигрыш $40» (мгновенно) ── │ считает и │
└──────────┘ это ещё НЕ факт в блокчейне — │ показывает │
просто мгновенная калькуляция; └─────────────────┘
анимация крутится уже после расчёта
│
ШАГ 3 — ФИНАЛ (когда игрок закончил)
│ ┌────────────────────────────────────────────────────┐
│ │ БЛОКЧЕЙН: settleSession(СЕКРЕТНЫЙ СИД + все спины) │
└───────▶│ чейн САМ пересчитывает каждый спин: │
│ барабаны = SHA(секретный сид, монета игрока, №) │
│ совпало с показанным? → выплата 💰 из хранилища │
└────────────────────────────────────────────────────┘
Ключевая мысль: результат спина — арифметика над тремя числами:
сид сервера + число игрока + номер спина.
Сид спрятан до самого конца, его хэш зафиксирован заранее. Обмануть эту математику нельзя — её в финале проверяет блокчейн.
Сервер знает исход каждого спина заранее (он же его считает) и может молча перестать отправлять финальную транзакцию. Игрок увидел джекпот в браузере, но забрать его можно только когда сервер раскроет сид. Пока сервер молчит — выигрыш «висит».
Что это даёт злоумышленнику: не украсть выигрыш, а заморозить его. Деньги не исчезают и не перенаправляются — они ждут под замком. Убыток игроку = время и доверие, прямого воровства нет.
ЧЕСТНОСТЬ (результат) СВОЕВРЕМЕННОСТЬ (выплата)
┌───────────────────────────┐ ┌───────────────────────────────┐
│ 🔒 РЕШЕНО НАВСЕГДА │ │ ⏳ ОТКРЫТЫЙ ВОПРОС │
│ │ │ │
│ изменить исход ✅ нет │ │ сервер может тянуть с │
│ украсть бюджет ✅ нет │ │ финальной транзакцией │
│ удалить сессию ✅ нет │ │ неограниченно долго │
│ подкрутить джекпот ✅ нет│ │ │
│ │ │ лечение спроектировано ⚙️ │
└───────────────────────────┘ └───────────────────────────────┘
Важно для маркетинга: честность исходов можно заявлять смело. Заявлять «мы не можем задержать выплату» — пока нельзя: публичная страничка честности прямо говорит «оператор может мешать работе, но не может менять исходы».
Кажется очевидным: «сервер молчит 15 минут → чейн сам возвращает игроку выигрыш». Но у таймаута должна быть сумма, и любую простую версию ломает либо жулик-сервер, либо жулик-игрок:
| Вариант «по таймауту» | Почему ломается |
|---|---|
| Вернуть игроку ставку | Сервер-жулик выбирает, когда молчать: молчит только если игрок выиграл. Джекпот не выплачивается никогда. Проблема осталась. |
| Заплатить максимум | Становится выгодно ронять наш сервер на 15 минут — все открытые сессии платятся по максимуму. Прямой убыток и мотив для атак. |
| Заплатить честный результат | Требует секретного сида — а он есть только у сервера. Круг замкнулся: чтобы обойтись без сервера, нужен его секрет. |
Вывод: таймаут без решения «как достать сид без сервера» — не лекарство, а перевод проблемы в другую плоскость.
Идея: секрет перестаёт быть единым. Сид делят на части несколько независимых хранителей — как сейф с несколькими ключами. Пока сервер работает, он действует сам; замолчал — любые двое хранителей собирают секрет и завершают сессию вместо него.
СЕГОДНЯ: один держатель секрета
ИГРОК ──спины──▶ СЕРВЕР(секрет) ──финал──▶ БЛОКЧЕЙН
│
└─ замолчал? ⇒ сессия висит ∞ ← единственная слабость
ПЛАН: секрет разделён (порог 2-из-N)
ИГРОК ──спины──▶ СЕРВЕР(доля) ──финал──▶ БЛОКЧЕЙН
│ ▲
│ замолчал > N мин? │
▼ │
ХРАНИТЕЛИ (доля, доля) ─────┘
любые 2 собирают секрет и досчитывают сами
⇒ задержка выплаты ограничена таймаутом, вечно молчать больше нельзя
Игрок видит ~3 секунды анимации барабанов. Но исход сервер считает мгновенно — это локальная арифметика, без запросов к внешним сервисам. Анимация крутится уже после расчёта и просто доезжает до нужных символов. Поэтому:
«А почему не нагенерировать все числа заранее?» Мы именно так и делаем — но одного этого мало, и вот почему:
В рулетке ставки и результат идут напрямую через блокчейн, а случайное число даёт Chainlink VRF — внешний сервис, который никто из нас не контролирует. Один раунд = одно случайное число сразу для всех игроков, поэтому это дёшево. Слоты устроены иначе ради скорости и цены: сотни мгновенных спинов без транзакции на каждый — отсюда «секрет на сервере».
| Рулетка | Слоты | |
|---|---|---|
| Случайность | Chainlink VRF (внешний оракул) | commit-reveal (наш сид + число игрока) |
| Кто знает исход заранее | никто | сервер (изменить не может) |
| Задержать выплату | нет (авторефанд по дедлайну) | пока да — до включения пороговой схемы |
| Заявление для игроков | «VRF, всё на чейне» | «provably fair commit-reveal» (как Stake и др.) |
Внешний консультант предложил: записывать каждый спин на чейн отдельной транзакцией, собирать их в батч, через 5–10 секунд запечатывать и запрашивать одно случайное число Chainlink VRF на батч, а выплату делать permissionless. Идея выглядит «максимально доверенной», но на наших цифрах она не работает:
| Пункт предложения | Что не так на практике |
|---|---|
| «Дешёвая on-chain запись на каждый спин» | Не бывает дешёвой. Это превращает слоты в мини-рулетку: 100 спинов = 100 транзакций вместо одного финального расчёта. Плюс газ для одноразовых ключей — новая операционная нагрузка. |
| «Запечатывать батч за 5–10 сек» | Игрок в слотах один. За 5–10 секунд он сделает 1–3 спина — «батч» вырождается, и VRF приходится платить почти на каждый спин. Экономии нет. |
| «VRF на батч, результат на чейне» | Цикл VRF занимает секунды: игрок увидит результат через 10–20 секунд после остановки барабанов. Сейчас — мгновенно, анимация полностью закрывает расчёт. |
| «Автозапечатывание и авторефанд» | «Авто» = наш же сервис-хранитель. Если он может выбрать момент молчания — рычаг задержки не исчезает, а переезжает с одного места на другое. |
Что в предложении верно — и уже есть у нас, даже строже. Ключевой принцип консультанта: «после приёма ставки и до того, как исход стал известен, участие и деньги под выплату должны стать неизменными». Этот инвариант у нас выполнен:
Технические источники: contracts/contracts/SlotsV6SettlementModule.sol (settleSession / forceSettleIdle / cancelSession · CancellationClosed при totalStake≠0) · contracts/contracts/SlotsV6.sol (openSession коммитит только хэш сида) · contracts/contracts/SlotsV6Math.sol (jackpotResult — джекпот из того же commit-reveal) · contracts/contracts/SlotsAcceptanceAnchor.sol + SlotsSeedProtocol (пороговое восстановление, свидетели, hard expiry — написано, не активировано) · ChainlinkVrfResultSourceV6 (VRF — только рулетка).