Доказуемая честность в азартных играх решила проблему, которая возникала с момента появления онлайн-казино: как игрок может быть уверен, что результат игры не был сфальсифицирован после ее завершения? Ответ кроется в схеме обязательств, используемой в криптографии, когда двум сторонам, не доверяющим друг другу, необходимо договориться о чем-либо.
Ниже подробно описывается весь процесс, используемый в игре Cloudbet Mines — от генерации сида до размещения мин — а также объясняется, как любой игрок, обладающий минимальными знаниями в программировании может самостоятельно проверить каждый результат, полученный за время игры.
Эта статья — первая в запланированной серии технических обзоров механизмов доказуемой честности, лежащих в основе оригинальных игр Cloudbet. Чтобы самостоятельно проверить результаты любого раунда (включая игру Mines), воспользуйтесь калькулятором доказуемой честности от Cloudbet.
Contents
Приверженность результату до начала игры
Перед каждым раундом сервер генерирует случайный 32-байтовый сид сервера и немедленно публикует его хеш SHA3-256 — обязательство (commitment). Этот хеш представляет собой односторонний отпечаток: он подтверждает, что сид существовал в тот момент, не раскрывая при этом его значение.
После окончания игры Cloudbet раскрывает исходный сид сервера. Затем любой желающий может вычислить хеш и проверить, совпадает ли он с хешем, зафиксированным до начала игры. Это доказывает, что сид — и, следовательно, расположение мин — было определено еще до выбора первой ячейки. У казино нет возможности обменять его на более выгодный вариант.
Учитывайте энтропию игрока
Один только сид сервера оставил бы игроков в полной зависимости от случайности казино. Чтобы этого избежать, в систему включаются два дополнительных параметра: сид клиента (устанавливается игроком) и nonce (целое число, которое увеличивается с каждой ставкой).
Все три значения объединяются и хешируются с помощью SHA3-256 для получения подписи раунда:
roundSignature = SHA3-256(`${serverSeed}:${clientSeed}:${nonce}`)
Поскольку сид клиента неизвестен на момент генерации сида сервера, это не позволяет казино предсказать результат в момент подтверждения ставки. Nonce гарантирует, что в каждом раунде генерируется уникальная подпись, даже если оба сида остаются неизменными от одной ставки к другой.
Генерация случайности с помощью SHAKE256
Подпись раунда передается в SHAKE256 — функцию с расширяемым выходом (XOF) из семейства SHA-3. В отличие от стандартного хеша, XOF может генерировать псевдослучайный поток байтов желаемой длины из фиксированного входного значения. Это полезно, поскольку для размещения нескольких мин требуется несколько независимых случайных значений.
В данной реализации значения извлекаются из этого потока по 4 байта. После исчерпания начального выходного потока система добавит "next" к состоянию, и процесс повторится. Однако, на практике, сетка из 25 ячеек с реалистичным количеством мин остается в пределах первых 256 бит.
Беспристрастное размещение мин: выборочный отбор по признаку отклонения
Преобразование случайного 32-битного целого числа в позицию сетки с помощью простой операции по модулю вносит тонкую, но реальную проблему: если 2^32 не делится на количество оставшихся позиций (P), то вероятность выбора некоторых позиций немного выше, чем других.
Cloudbet устраняет эту проблему с помощью метода выборочного отбора по признаку отклонения:
const maxAcceptable = Math.floor(0x100000000 / P) * P;
let rand;
do { rand = rng.next().value; } while (rand >= maxAcceptable);
const index = rand % P;
Любое значение, выходящее за пределы наибольшего кратного (P), которое помещается в 32 бита, отбрасывается, и вместо него выбирается следующее значение из потока SHAKE256. В результате получается равномерное распределение ровно по (P) позициям.
После размещения каждой мины данная позиция удаляется из множества, а значение (P) уменьшается на 1 — это частичная перетасовка по алгоритму Фишера-Йетса на плоской сетке из 25 элементов.
Проверка раунда
Для проверки результатов игры требуются четыре значения, все из которых публикует Cloudbet после завершения раунда:
serverSeed (сид сервера)clientSeed (сид клиента)nonceМеста размещения мин
Этапы проверки следующие:
- Хэшируйте
serverSeedс помощью SHA3-256, а затем подтвердите, что он соответствует предматчевому обязательству. - Вычислите
roundSignature = SHA3-256(serverSeed:clientSeed:nonce). - Инициализируйте поток SHAKE256 с помощью
roundSignatureи запустите цикл отбраковки выборок, чтобы независимо воспроизвестиminePositions.
Матч подтверждает, что сетка не была изменена после начала раунда.
Пример реализации (Node.js)
npm install js-sha3
const crypto = require('crypto');
const { sha3_256, shake256 } = require('js-sha3');
function generateServerSeed() {
const seed = crypto.randomBytes(32).toString('hex');
return { serverSeed: seed, commitment: sha3_256(seed) };
}
function createRoundSignature(serverSeed, clientSeed, nonce) {
return sha3_256(`${serverSeed}:${clientSeed}:${nonce}`);
}
function* createShake256Stream(signature) {
const byteStream = shake256.create(32);
byteStream.update(signature);
while (true) {
const buf = Buffer.from(byteStream.digest({ buffer: true }));
for (let i = 0; i < buf.length; i += 4) {
if (i + 4 <= buf.length) yield buf.readUInt32BE(i);
}
byteStream.update('next');
}
}
function pickUniquePositions(count, totalTiles, rng) {
const available = Array.from({ length: totalTiles }, (_, i) => i);
const result = [];
for (let i = 0; i < count; i++) {
const P = available.length;
const maxAcceptable = Math.floor(0x100000000 / P) * P;
let rand;
do { rand = rng.next().value; } while (rand >= maxAcceptable);
const index = rand % P;
result.push(available[index]);
available.splice(index, 1);
}
return result;
}
const { serverSeed, commitment } = generateServerSeed();
const clientSeed = 'player-provided-seed';
const nonce = 0;
const sig = createRoundSignature(serverSeed, clientSeed, nonce);
const rng = createShake256Stream(sig);
const mines = pickUniquePositions(5, 25, rng);
console.log('Commitment:', commitment);
console.log('Server seed (post-game):', serverSeed);
console.log('Mine positions:', mines);
Несколько моментов, на которые стоит обратить внимание
Почему SHA3, а не SHA2? Для простого обязательства подойдет любой из этих вариантов. Губчатая структура SHA3 эффективно исключает атаки с увеличением длины, и ее использование на протяжении всего процесса — от этапа подтверждения до основы SHAKE256 — обеспечивает согласованность криптографической обработки.
Почему SHAKE256, а не PRNG? Детерминизм и возможность проверки. SHAKE256 — это стандартизированный примитив с четко определенной спецификацией. Это означает, что независимые реализации на любом языке программирования будут выдавать одинаковый результат при одинаковых входных данных. Использование PRNG приводит к поведению, определяемому реализацией, что затрудняет проверку сторонними организациями.
Может ли игрок обойти систему, выбрав выгодный сид клиента? В принципе, да — игрок может попробовать разные сиды клиента, чтобы найти тот, который создаст благоприятную сетку. Это считается приемлемым с точки зрения проектирования системы, поскольку право игрока выбирать сид является преимуществом, а не недостатком. Риски казино симметричны и особенно важно отметить, что тот же механизм, который позволяет игроку выбрать выгодный сид, также позволяет ему доказать, что казино никогда не манипулировало его.
Игра Mines представляет собой простой и понятный способ внедрения принципов доказуемой честности именно потому, что ее механизм генерации случайных чисел относительно самодостаточен: один цикл подтверждения и раскрытия, простой поток байтов и хорошо изученная методика выборки. По мере развития серии мы увидим, как другие оригинальные игры Cloudbet адаптируют и расширяют эти же основы — иногда с дополнительными источниками энтропии, различными способами отображения выходных данных или многоступенчатой проверкой — сохраняя при этом ту же основную гарантию: исход не может быть определен после того, как игрок подтвердил свое действие.


