Проверка честности
Мы не просим верить на слово. Результат любого раунда можно пересчитать самостоятельно и убедиться, что он не менялся после вашей ставки.
Как это устроено
Сервер фиксирует секрет
Сервер генерирует случайный серверный сид (32 байта) и публикует только его SHA-256 хеш. Сам сид остаётся скрытым — это обязательство, которое нельзя изменить задним числом.
Вы задаёте свой сид
Клиентский сид вы можете изменить в любой момент. Он участвует в вычислении результата, поэтому ни одна сторона не контролирует исход в одиночку.
Раунд вычисляется формулой
Для каждого раунда берётся HMAC-SHA256 от строки «клиентский сид : номер раунда» на ключе серверного сида. Первые 13 шестнадцатеричных символов дают число от 0 до 1.
Сид раскрывается
Когда вы меняете пару сидов, предыдущий серверный сид публикуется полностью. Вы сверяете его хеш с тем, что был опубликован заранее, и пересчитываете каждый раунд.
Формула
hmac = HMAC_SHA256(key = serverSeed, message = clientSeed + ":" + nonce)
roll = parseInt(hmac.slice(0, 13), 16) / 2^52 // число в [0, 1)
// Кейс: предметы отсортированы, веса суммируются
target = roll × Σweights
выпадает первый предмет, у которого накопленная сумма весов > targetБерутся 52 бита, а не 32: при 32 битах возможных значений всего ~4,3 млрд, и на дистанции это заметно искажает шансы предметов с вероятностью 0,01%.
Колесо: сектор картинки
Раунд колеса общий и идёт по таймеру, поэтому пара сидов у него своя: серверный создаётся при открытии раунда — до первой ставки, — а клиентский собирается из ставок раунда. Результат — не множитель, а номер сектора на картинке, и колесо на экране докручивается именно до него. Поэтому стрелка не может остановиться на одном цвете, пока в базе записан другой.
clientSeed = SHA256("<id игрока>:<множитель>:<сумма>|…") // в порядке ставок
nonce = номер раунда
roll = parseInt(HMAC_SHA256(serverSeed, clientSeed + ":" + nonce).slice(0, 13), 16) / 2^52
сектор = floor(roll × 25) // 25 секторов, все одинакового размера
множитель = раскладка[сектор] // та же, по которой нарисовано колесоСекторы равны по размеру, поэтому шанс цвета — это просто его доля на колесе: ×2 занимает 12 секторов из 25, ×21 — один. Отдельного числа вероятности, которое можно было бы поменять, в коде нет; таблица на странице игры считается из той же раскладки.
Джекпот: общий раунд
В джекпоте раунд общий, поэтому и пара сидов у него своя, а не ваша личная. Серверный сид раунда создаётся при его открытии — до первой ставки — и его хеш виден на странице сразу. Клиентская половина собирается из самих ставок: кто поставил и сколько. Обе части каждой пары выбирает игрок, а не сервер, поэтому повлиять на неё можно только одним способом — зайти в раунд со ставкой нужного размера, что видно всем и меняет опубликованные шансы.
clientSeed = SHA256("<id игрока>:<сумма>|<id игрока>:<сумма>|…") // в порядке ставок
nonce = номер раунда
roll = parseInt(HMAC_SHA256(serverSeed, clientSeed + ":" + nonce).slice(0, 13), 16) / 2^52
// Победитель: ставки в том же порядке, вес ставки = её сумма
target = roll × банк
выигрывает первая ставка, у которой накопленная сумма > targetШанс игрока равен его доле в банке — отдельного множителя, который можно было бы подкрутить, в этой механике просто нет. После розыгрыша серверный сид раунда раскрывается сразу: он одноразовый, и скрывать его больше не от чего. Комиссия удерживается из банка и указана до ставки.
Список ставок, по которому считался клиентский сид, раунд отдаёт вместе с результатом: GET /api/games/jackpot возвращает последние разыгранные раунды с полями entries, serverSeed, clientSeed и rollValue. Формула публичная, значит и её входные данные должны быть публичными.
Ваши сиды
Войдите, чтобы увидеть свою пару сидов и управлять ею.
Калькулятор проверки
Введите раскрытый серверный сид, клиентский сид и номер раунда. Расчёт выполняется прямо в браузере — данные никуда не отправляются.
