NeuroGammon реализует схему commit-reveal на HMAC-SHA256 — ту же, что используют честные крипто-казино. Сервер математически связан обещанием до первого броска, поэтому не может реагировать на позицию игрока. Эту страницу стоит прочитать один раз — и любые подозрения «кости подкручивают» снимаются проверкой в браузере.
Перед партией сервер запечатывает в конверт случайное число S и отдаёт игроку хэш этого конверта. Игрок добавляет своё число C. Все 30+ бросков партии однозначно вычисляются из пары (S, C) функцией HMAC-SHA256. После окончания партии сервер раскрывает S — игрок проверяет, что хэш совпадает, и пересчитывает каждый бросок. Если хэш совпал, сервер не мог менять S в процессе, значит, броски были предопределены ещё до 1-го хода — ни AI, ни сервер не могли знать позицию вперёд.
Представь, что перед игрой судья кладёт в конверт лист с 60 числами от 1 до 6. Заклеивает его, ставит свою печать и показывает тебе фото печати. Конверт лежит на столе. Каждый раз, когда нужен бросок, судья достаёт следующее число из конверта. После партии конверт раскрывают — ты видишь все 60 чисел и проверяешь, что печать была твоя.
Если печать совпадает — судья не мог подменить конверт посреди партии. Значит, числа были выбраны до того, как кто-то начал двигать шашки.
В цифровом мире «конверт» — это случайный server_seed (32 байта), «печать» — это SHA-256 хэш от него (математическая функция, для которой невозможно найти другой вход, дающий тот же выход). Хэш ты видишь до 1-го броска. Сам seed — только после конца партии.
server_seed —
через системный криптографический источник (secrets.token_bytes, тот же,
что используют SSL-библиотеки). Считает SHA-256(server_seed) и отдаёт
тебе только хэш. Сам seed остаётся в БД, скрытый.
client_seed — 16 случайных байт через
crypto.getRandomValues. Этот seed знаешь только ты, но он отправляется
серверу вместе со стартом партии. Без твоего seed детерминированный поток восстановить
невозможно.
server_seed в открытом виде.
Кнопка «✓ Проверить» в твоём браузере делает три вещи:
SHA-256(server_seed) и сравнивает с хэшем, который ты видел
в начале — должны совпасть до символа.Допустим, сервер хочет «подкрутить» бросок №14, чтобы AI получил нужный дубль. Для этого ему пришлось бы найти другой server_seed S′ такой, что:
SHA-256(S′) == SHA-256(S) — иначе хэш не совпадёт при проверке.Найти такое S′ — это нарушить SHA-256 на коллизии, что считается нерешённой задачей с 2002 года. У злоумышленника с гипер-вычислительными ресурсами на это уйдёт ~ 2¹²⁸ операций — больше, чем атомов в наблюдаемой Вселенной. На рабочем сервере за время одной партии — невозможно.
Если веришь больше своему коду, чем нашей кнопке «Проверить» — открой DevTools console (F12) на странице любой партии и скопируй:
Это тот же алгоритм, что использует наш сервер в longgammon/game.py.
Если хэш совпал и броски совпадают с теми, что ты видел в партии — всё честно.
На случай, если хочешь убедиться, что схема — стандартная индустриальная, а не наша выдумка: