Содержание
Provably Fair — это протокол проверки вокруг детерминированного получения случайных результатов: когда исходные данные раскрыты, ты можешь повторить вычисления и сравнить результат с историей игры. Это не обещание выигрыша. Общую идею разбирает объяснение Provably Fair, а здесь мы пройдём техническую цепочку: хэш-обязательство → HMAC → байты → число → правило игры.
Важно разделять две задачи. Проверка хэш-обязательства устанавливает, соответствует ли раскрытый серверный сид ранее сохранённому хэшу. Воспроизведение результата устанавливает, получается ли из данных исторического раунда именно тот результат, который записан в игре. Для второй задачи нужны точный протокол, его версия и правила преобразования байтов.
Чем ГСЧ отличается от Provably Fair
ГСЧ создаёт случайные или псевдослучайные значения, а Provably Fair позволяет проверить оговорённый способ получения результата. Эти понятия не конкурируют: генератор может быть частью проверяемого протокола.
| Механизм | Что он делает | Что само по себе остаётся недоказанным |
|---|---|---|
| ГСЧ — генератор случайных чисел | Общее название механизмов получения случайных значений | Качество источника, предсказуемость и доступность проверки конкретного результата |
| Генератор псевдослучайных чисел | Детерминированно расширяет начальное состояние в последовательность | Не каждый такой генератор криптографически стойкий, но и не каждый слабый |
| Криптографически стойкий генератор | При выполнении требований к начальному состоянию и реализации предназначен противостоять предсказанию | Правильность конкретной реализации и достаточная непредсказуемость исходного секрета |
| Поток на основе HMAC | Получает воспроизводимые байты из ключа и сообщений | Само повторение HMAC ещё не означает реализацию HMAC_DRBG |
| Provably Fair | Связывает заранее опубликованное обязательство, раскрытие данных и проверку результата | Честный выбор исходных данных, выплаты и отсутствие всех возможных атак |
HMAC_DRBG — конкретная конструкция со строго определёнными операциями создания состояния, генерации и обновления исходных данных. Нельзя назвать любой цикл HMAC реализацией этого стандарта: это следует из требований NIST SP 800-90A Rev. 1.
Безопасность зависит не только от выбранной функции. Серверный сид должен происходить из достаточно непредсказуемого источника и оставаться секретным, пока используется. Статистически аккуратные числа не заменяют эти условия. Например, требования UKGC к ГСЧ рассматривают и статистическое качество, и непредсказуемость; это требования конкретной юрисдикции, а не свидетельство сертификации любого названного оператора.
Зачем нужны и хэш, и HMAC
Хэш фиксирует раскрываемую строку, а HMAC использует секретный ключ для получения воспроизводимых байтов. Это разные операции, и ни одна из них не является расшифровкой.
В нашем примере хэш-обязательство — SHA-256 от серверного сида. До ставки ты сохраняешь этот хэш; после раскрытия сида вычисляешь SHA-256 ещё раз и сравниваешь полные значения.
HMAC-SHA256 принимает два отдельных входа: ключ и сообщение. Здесь ключом служит серверный сид, а сообщением — строка из клиентского сида, nonce (счётчика) и счётчика блоков HMAC. Различие между обычным хэшем и HMAC определено в RFC 2104.
Хэш, полученный только после ставки, не подтверждает, что обязательство существовало заранее. Поэтому время сохранения — часть проверки, а не необязательная деталь.
Какие данные нужны для получения байтов
Для описанной схемы нужны оба сида, nonce (счётчик), счётчик блоков HMAC и точное представление данных для хэширования. Если игра читает поток с определённого места, нужна также позиция в потоке байтов.
В документированной реализации Stake HMAC-SHA256 использует серверный сид как ключ и сообщение вида clientSeed:nonce:block. Это описание стадий получения байтов, а не подтверждение правил расчёта текущей игры Dice или аудит работающего казино.
| Данные | Роль | Значение в нашем учебном примере |
|---|---|---|
| Серверный сид | Секретный ключ HMAC; после раскрытия позволяет повторить вычисления | tg-rng-demo-server |
| Клиентский сид | Дополнительный вход в сообщение | reader-example |
| nonce (счётчик) | Различает вычисления в последовательности | 7 |
| Счётчик блоков HMAC | Выбирает следующий 32-байтовый блок | 0 |
| Позиция в потоке байтов | Определяет блок и смещение внутри него | 0: начало первого блока |
| Точное представление данных для хэширования | Определяет кодировку, разделители и порядок полей | Буквальная строка UTF-8 reader-example:7:0 |
| Хэш-обязательство | Фиксирует серверный сид для последующей проверки | Полный SHA-256, приведённый ниже |
Один блок HMAC-SHA256 содержит 32 байта, то есть восемь непересекающихся четырёхбайтовых значений. В указанной реализации для позиции cursor:
currentRound = floor(cursor / 32)
withinBlockOffset = cursor % 32
Здесь currentRound выбирает блок, а withinBlockOffset — байт внутри блока. Например, позиция 32 означает блок 1 и смещение 0, а не nonce (счётчик) 32.
Кодировка тоже влияет на результат. Буквальная строка ab в UTF-8 и один байт, декодированный из шестнадцатеричного ab, — разные данные. Пробел, перенос строки или другой разделитель также меняют вычисление.
Не переноси эту схему на любого провайдера. BGaming описывает протокол с Result и Secret, а Gamba преобразует мнемонические строки в двоичные данные. Это не те же буквальные строки UTF-8, которые мы используем ниже.
Точный пример: от серверного сида до числа 6.73
Из наших синтетических входных данных получается HMAC, первые четыре байта которого дают значение 0.06736633484251797; по нашему учебному правилу это число превращается в 6.73. Это не реальная ставка и не утверждение о текущих правилах Stake Dice.
Мы явно задаём учебное преобразование:
floor(u * 10000) / 100
Оно содержит 10000 возможных значений: от 0.00 до 99.99. Это отдельное правило примера; официальная документация Stake здесь служит источником только для получения байтов и их преобразования в дробь.
Полные входы и результаты
Для точного повторения используй строки без кавычек, пробелов и переносов в конце. Серверный сид передаётся как буквальная строка UTF-8, а не как декодированное шестнадцатеричное значение.
Серверный сид: tg-rng-demo-server
Клиентский сид: reader-example
nonce (счётчик): 7
Счётчик блоков HMAC: 0
Сообщение: reader-example:7:0
SHA-256 серверного сида:
af44fbb9b3710c9bb38aec8a7ac0b25140f34033376c7bb894626d34cfa5a026
HMAC-SHA256:
113eeb8d4b56244a2674b08b1332e4baf7552c927271d397b5963d868cd0b0bb
Первые четыре байта — 17, 62, 235, 141. Если старший байт идёт первым, целое число равно:
17 × 256³ + 62 × 256² + 235 × 256 + 141 = 289336205
Документация преобразований Stake описывает сумму четырёх дробей:
u = 17/256 + 62/256² + 235/256³ + 141/256⁴
= 289336205 / 4294967296
= 0.06736633484251797
u × 10000 = 673.6633484251797
floor(u × 10000) / 100 = 6.73
Здесь применяется округление вниз на стадии выбора учебного интервала, а не обычное округление до двух знаков. Значение u лежит в диапазоне [0, 1), но не на непрерывной шкале: четыре байта задают конечную сетку с шагом 1 / 4294967296.
Код для браузера и повторения выборки
Этот код вычисляет хэш-обязательство, HMAC, дробь и учебное число, а затем повторяет расчёт для nonce (счётчика) от 0 до 9999. Запусти его в консоли современного браузера на HTTPS-странице или на localhost, где доступен Web Crypto API.
(async () => {
const encoder = new TextEncoder();
const serverSeed = "tg-rng-demo-server";
const clientSeed = "reader-example";
const nonce = 7;
const blockIndex = 0;
function toHex(bytes) {
return Array.from(bytes, byte =>
byte.toString(16).padStart(2, "0")
).join("");
}
// Хэшируем буквальную строку UTF-8.
const commitmentBytes = new Uint8Array(
await crypto.subtle.digest(
"SHA-256",
encoder.encode(serverSeed)
)
);
// Серверный сид — ключ HMAC, а не часть сообщения.
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(serverSeed),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
async function calculate(counter, block) {
const message = `${clientSeed}:${counter}:${block}`;
const bytes = new Uint8Array(
await crypto.subtle.sign(
"HMAC",
key,
encoder.encode(message)
)
);
// Первые четыре байта: старший байт идёт первым.
const first4 = Array.from(bytes.slice(0, 4));
const integer =
first4[0] * 256 ** 3 +
first4[1] * 256 ** 2 +
first4[2] * 256 +
first4[3];
const u = first4.reduce(
(sum, byte, index) => sum + byte / 256 ** (index + 1),
0
);
// Наше учебное правило, не правило расчёта конкретного казино.
const teachingRoll = Math.floor(u * 10000) / 100;
return {
message,
hmac: toHex(bytes),
first4,
integer,
u,
scaled: u * 10000,
teachingRoll
};
}
console.log("Хэш-обязательство:", toHex(commitmentBytes));
console.log("Учебный пример:", await calculate(nonce, blockIndex));
// Десять интервалов: [0, 0.1), ..., [0.9, 1).
const counts = Array(10).fill(0);
for (let counter = 0; counter < 10000; counter++) {
const { u } = await calculate(counter, 0);
counts[Math.floor(u * 10)]++;
}
console.log("Частоты для счётчиков 0–9999:", counts);
// Все 256 равновероятных значений учебного 8-битного источника.
const remainderCounts = Array(6).fill(0);
const acceptedCounts = Array(6).fill(0);
for (let value = 0; value < 256; value++) {
remainderCounts[value % 6]++;
if (value < 252) acceptedCounts[value % 6]++;
}
console.log("Частоты остатков:", remainderCounts);
console.log("Частоты после отбрасывания:", acceptedCounts);
})();
Ожидаемые результаты первого расчёта — полный хэш и HMAC выше, байты [17, 62, 235, 141], целое 289336205, дробь 0.06736633484251797 и учебное число 6.73. Для выборки ожидается массив [977, 1012, 1037, 1027, 998, 1026, 973, 969, 1006, 975].
При nonce (счётчике) 8 сообщение меняется на reader-example:8:0, поэтому изменяется и HMAC. Этот опыт возможен потому, что учебный серверный сид уже известен. Он не показывает, как предсказывать будущие результаты при неизвестном активном серверном сиде.
Почему одни байты дают разные игровые результаты
Байты становятся результатом только после применения правил конкретной игры. Одного совпадения HMAC недостаточно, если ты используешь другое преобразование, порядок потребления байтов или параметры выплат.
| Тип игры | Что нужно восстановить после получения байтов | Почему нет универсальной формулы |
|---|---|---|
| Dice | Выбор дискретного значения и правила сравнения с прогнозом | Число интервалов и округление могут отличаться |
| Crash | Нелинейное преобразование в множитель | Важны параметры распределения, преимущество казино и границы |
| Карточная игра | Выбор индекса карты либо перемешивание колоды | Независимый выбор индекса и выбор без возвращения — разные процедуры |
| Mines | Набор уникальных позиций | Нужно воспроизвести исключение повторов и порядок выбора |
| Plinko | Последовательность направлений и таблицу выплат | Путь сам по себе не определяет выплату без настроек игры |
Документация Stake о преобразованиях показывает, что разные игры по-разному используют исходные значения, в том числе выбор без возвращения. Это не означает, что во всех карточных играх карты обязательно уникальны или что все провайдеры используют одинаковый HMAC.
Для реального раунда нужны версия протокола, правила округления и параметры расчёта выплаты. Текущие правила расчёта Stake Dice в этом примере не подтверждены: число 6.73 относится только к объявленной учебной модели.
Что частоты говорят о распределении — и чего не говорят
Частоты позволяют заметить некоторые ошибки преобразования, но не доказывают безопасность генератора. Это видно и на простом примере с остатками, и на нашей синтетической выборке.
Предположим, источник выдаёт каждое 8-битное значение от 0 до 255 с одинаковой вероятностью. Если взять остаток от деления на 6, получатся такие количества:
| Остаток | Значений источника |
|---|---|
| 0 | 43 |
| 1 | 43 |
| 2 | 43 |
| 3 | 43 |
| 4 | 42 |
| 5 | 42 |
Причина проста: 256 не делится на 6 без остатка. Если отбросить значения 252–255 и при каждом таком случае получать новое значение источника, останется 252 допустимых значения — по 42 на каждый остаток. Равенство здесь справедливо при заявленном условии равновероятного источника и корректном повторном получении значения. Это учебный пример, а не обвинение конкретного провайдера.
Воспроизводимая выборка из 10000 вычислений
Наша выборка даёт близкие частоты в десяти интервалах, но это независимые синтетические вычисления, а не наблюдения за казино. Используются те же буквальные сиды, блок 0, nonce (счётчик) от 0 до 9999 включительно и первые четыре байта каждого HMAC.
Номер интервала равен floor(u * 10). Код выше воспроизводит весь расчёт.
Интервал для u | Количество |
|---|---|
[0.0, 0.1) | 977 |
[0.1, 0.2) | 1012 |
[0.2, 0.3) | 1037 |
[0.3, 0.4) | 1027 |
[0.4, 0.5) | 998 |
[0.5, 0.6) | 1026 |
[0.6, 0.7) | 973 |
[0.7, 0.8) | 969 |
[0.8, 0.9) | 1006 |
[0.9, 1.0) | 975 |
Ориентир для равных вероятностей — 1000 значений в каждом интервале. Однако близость к этому ориентиру не устанавливает непредсказуемость исходного секрета, безопасность реализации, реальный RTP или будущие результаты. Известная детерминированная последовательность тоже может выглядеть ровной.
Как проверить исторический раунд
Сначала сохрани обязательство до ставки, затем после предусмотренного протоколом раскрытия проверь серверный сид и отдельно воспроизведи результат. Не заменяй эти две задачи одной зелёной отметкой инструмента.
- До ставки сохрани хэш-обязательство. Зафиксируй, когда и для какого серверного сида оно опубликовано.
- Сохрани условия вычисления. Нужны протокол и версия, клиентский сид, nonce (счётчик), позиция в потоке байтов или счётчик блоков HMAC, а также параметры игры и выплат.
- Дождись документированного прекращения использования и раскрытия серверного сида. Выбери реальный исторический раунд, который относится именно к этой паре сидов.
- Проверь полное хэш-обязательство. Вычисли хэш с точным представлением данных для хэширования, указанным провайдером.
- Воспроизведи результат. Повтори получение байтов, их потребление, преобразование и округление по протоколу этой версии игры.
- Сопоставь историю и расчёт выплаты. Проверь не только показанное число, но и прогноз, сумму ставки, параметры выплаты и зафиксированный итог.
В описании Gamba также разделены проверка обязательства и проверка игрового результата. При этом собственное преобразование мнемонических строк в двоичные данные не совпадает с нашим примером UTF-8.
Два уровня проверки Provably Fair в документации Gamba: обязательство и игровой результат
Два отдельных уровня проверки; собственный протокол Gamba нельзя подменять учебной схемой.
Для общей последовательности действий пригодится гайд по проверке Provably Fair. Здесь главный технический критерий — совпадение всей цепочки вычислений, а не название кнопки.
Что проверить при несовпадении
При несовпадении сначала проверь входные данные и правила преобразования: техническая ошибка ещё не является доказательством мошенничества.
- Серверный и клиентский сиды относятся к нужному историческому раунду.
- nonce (счётчик) не сдвинут на единицу.
- Не пропущен счётчик блоков HMAC или начальная позиция в потоке байтов.
- Буквальная строка UTF-8 не подменена декодированными шестнадцатеричными байтами.
- Совпадают разделители, порядок полей, пробелы и переносы строк.
- Используются правильные версия протокола, преобразование и округление.
- Параметры выплаты соответствуют сохранённой истории.
Если расхождение осталось, сохрани исходные данные, историю, вычисления и снимки экрана, затем запроси объяснение у провайдера. Не отправляй активные секреты или фразу восстановления кошелька.
Что проверяет встроенный инструмент ToolsGambling
Встроенный инструмент подходит для проверки SHA-256 раскрытой буквальной строки на совпадение с предоставленным хэш-обязательством. Его игровые числа рассчитываются по другой схеме: он не воспроизводит наше учебное число 6.73 и не подтверждает совместимость с текущими играми Stake или Spribe.
Для повторения снимка экрана заполни четыре поля. Ниже английские подписи приведены дословно как названия элементов интерфейса:
| Элемент интерфейса | Что вставить |
|---|---|
| «SERVER SEED HASH (ДО СТАВКИ)» | af44fbb9b3710c9bb38aec8a7ac0b25140f34033376c7bb894626d34cfa5a026 |
| «SERVER SEED (РАСКРЫТЫЙ)» | tg-rng-demo-server |
| «CLIENT SEED» | reader-example |
| «NONCE» | 7 |
В группе интерфейса «АЛГОРИТМ HMAC» выбери «HMAC-SHA256» и нажми кнопку «Проверить раунд». Не добавляй :0 в поле клиентского сида: отдельного поля счётчика блоков HMAC здесь нет.
Четыре поля встроенного инструмента с синтетическим хэшем, серверным сидом, клиентским сидом и счётчиком 7
Синтетические входы на локальной странице инструмента, не данные ставки в казино.
Надписи с брендами рядом с выбором алгоритма — подсказки интерфейса, а не проверенное подтверждение совместимости. То же относится к слову «универсальный» и пояснению об общих формулах в самом компоненте.
Почему инструмент показывает 36.03, а не 6.73
Инструмент использует сообщение reader-example:7 без счётчика блоков :0, а затем другое преобразование HMAC в число. Поэтому совпадение хэша-обязательства здесь сочетается с другим HMAC и другим числом.
Для встроенного инструмента:
Сообщение:
reader-example:7
HMAC-SHA256:
164923c03bdddda005a0f12178e6b92855e8368c9b77a85d9db3ff63f4187df8
Для показанного числа Dice инструмент берёт первые десять шестнадцатеричных знаков HMAC, вычисляет остаток от деления на 1000000 и делит на 10000. Получается 36.0251; интерфейс отображает его как 36.03. Это не наше учебное правило с первыми четырьмя байтами и 10000 интервалами.
Результат локальной проверки: статус PASS, полный вычисленный хэш, HMAC и показанное число Dice 36.03
Совпадение хэша подтверждено; показанное число Dice относится только к расчёту этого инструмента.
Видимый статус интерфейса «PASS» и зелёная отметка означают лишь, что раскрытая строка соответствует предоставленному хэш-обязательству. Для полноценной проверки ещё нужны сохранение обязательства до ставки и воспроизведение результата по протоколу провайдера. Даже подпись интерфейса «ЧЕСТНО» не расширяет этот вывод.
Хэш-обязательство проверяется через SHA-256 при обоих вариантах HMAC. Выбор HMAC-SHA512 меняет получение HMAC, но не функцию проверки обязательства.
Полный инструмент Provably Fair также можно использовать как помощник для проверки SHA-256 хэш-обязательства буквального сида. Он использует HMAC-SHA512 с сообщением client:nonce и своё преобразование первых четырёх байтов, поэтому не является универсальным средством воспроизведения игровых результатов и не повторяет наш пример с блоком 0.
Что проверка не доказывает о безопасности и RTP
Совпадение хэша и воспроизведённого результата подтверждает конкретную вычислительную цепочку, но не гарантирует прибыль, выплаты или безопасность всей системы. RTP нужно рассматривать отдельно от способа получения байтов.
Возьмём условную модель: вероятность выигрыша 0.5, а выплата при выигрыше составляет 1.98 от ставки, включая возвращаемую ставку; при проигрыше выплата равна нулю. Тогда:
RTP = 0.5 × 1.98 = 99%
Преимущество казино = 1%
При обороте ставок 1000:
ожидаемая выплата = 990
ожидаемая чистая прибыль = −10
RTP — теоретический возврат игроку в заданной модели. Эти числа не описывают конкретную игру и не прогнозируют итог сессии. Преобразование байтов в результат и таблица выплат — разные части расчёта.
Проверка хэша сама по себе не устанавливает:
- что серверный сид выбирался без предварительного подбора;
- что его исходный источник достаточно непредсказуем;
- что провайдер не прерывал неудобные раунды;
- что все правила игры и выплаты реализованы правильно;
- что вывод средств будет выполнен;
- что система прошла независимую сертификацию безопасности.
Если ты независимо выбираешь клиентский сид после публикации серверного хэш-обязательства, это при соответствующем протоколе уменьшает конкретный риск предварительного подбора серверного сида под уже известный клиентский вход. Это не защита от всех атак.
Утечка активного серверного сида не обязательно нарушает совпадение хэша: раскрытая строка может по-прежнему соответствовать обязательству, хотя секретность уже потеряна. Произвольная смена сидов через фиксированное число ставок тоже не устраняет все риски. Важны источник секретов, порядок выбора данных, прекращение использования сида и реализация протокола.
Наконец, не доверяй «предсказателям», которые обещают будущие исходы по одному публичному хэшу. Наш воспроизводимый пример работает с известным учебным секретом, а не извлекает неизвестный серверный сид из хэша. Для реальной проверки используй историю и официальный протокол конкретной версии игры — без фразы восстановления кошелька и без обещаний выигрыша.
Часто задаваемые вопросы
Для описанного здесь протокола с HMAC — нет: без серверного сида ты не воспроизведёшь поток байтов. До раскрытия сохрани хэш-обязательство и данные раунда. Другие протоколы могут предусматривать иной порядок проверки; ориентируйся на документацию конкретной игры.
Это две разные проверки. Совпадение полного хэша подтверждает, что раскрытая строка соответствует сохранённому хэш-обязательству. Совпадение числа проверяет воспроизведение результата по правилам игры. Одно короткое число может получиться из множества разных исходных данных.
Воспроизведи все необходимые блоки и точную позицию в потоке байтов по протоколу игры. В описанном примере каждый блок HMAC-SHA256 содержит 32 байта — восемь четырёхбайтовых значений. Позиция в потоке байтов и счётчик блоков HMAC не взаимозаменяемы.
Нет. Она показывает только частоты в выбранных интервалах для конкретных входных данных. Такая выборка не доказывает непредсказуемость серверного сида, качество реализации, реальный RTP или будущие частоты.
Нет. Описанная проверка выполняется с игровыми сидами, хэшами и данными раунда без блокчейна. Фраза восстановления кошелька не является игровым сидом: не вводи её в такие инструменты и не передавай для проверки.








