Онлайн-
Апгрейдов0

Аудит Provably Fair: как скептику проверить честность апгрейда

Обновлено: · 12 мин чтения

Если вы читаете эту статью, значит, слову «честно» вы не верите по умолчанию, и это здравая позиция. Здесь нет рассказа о том, как «всё устроено красиво». Вместо этого мы поставим себя на место проверяющего: определим, какие виды обмана вообще возможны в игре на случай, какой шаг аудита каждый из них выдаёт, и пройдём весь путь от сбора данных до собственного кода. Напомним сразу: апгрейд на DOTAGRADE — азартная механика, доступная только с 18 лет, выигрыш не гарантирован, а сам Provably Fair шансов не повышает. Сайт не связан с Valve и Steam.

Что значит «поймать нечестность»

Проверка честности — это не поиск доказательства удачи. Мы ищем противоречия: место, где опубликованные данные, формула и записанный исход перестают сходиться. Если после аудита противоречий нет, подмена результата на стороне сайта исключена для этой конкретной игры. Если есть — у вас на руках готовый материал для обращения в поддержку.

Для аудита нужны три вещи: данные игры, формула и сравнение с исходом. Формула общая для всех и полностью известна:

  • публичный хеш: HMAC-SHA256, где ключ — секретная соль, а сообщение — серверный сид;
  • ролл: HMAC-SHA512, где ключ — клиентский сид, дефис и нонс, а сообщение — серверный сид; из полученной hex-строки берутся первые 15 символов, число делится с остатком на maxRoll, к остатку прибавляется единица;
  • победа: ролл лежит внутри окна выигрыша.

Значение maxRoll мы здесь не называем намеренно: берите его со страницы проверки, на правом краю шкалы диапазона.

Четыре способа обмана и чем каждый ловится

Модель угроз проста: у сайта есть мотив и техническая возможность изменить что-то после вашей ставки. Перечислим, что именно и где это всплывёт.

Гипотетическая подменаЧто делает нечестный операторКакой шаг аудита её выдаёт
Подмена серверного сидаПосле ставки подставляет другой сид, чтобы получить нужный роллХеш раскрытой пары не совпадёт с публичным хешем, который вы видели заранее
Подгонка роллаЗаписывает в историю число, не равное формулеВаш пересчёт даст другое число, чем поле roll
Неверное окноДвигает границы from…to или сужает их относительно заявленного шансаШирина окна не сойдётся с произведением шанса на maxRoll, а ролл окажется не там, где указан исход
Пропуск или перекрутка nonceВыбрасывает «неудобные» номера или повторяет ихНонсы внутри пары идут подряд; дыра или повтор видны в серии проверенных игр

Обратите внимание на логику первой строки. Публичный хеш виден до игры, а серверный сид — после смены пары. Нечестному оператору пришлось бы подобрать другой сид с тем же HMAC-SHA256 от той же соли, а это вычислительно нереально. Поэтому именно сравнение хеша — фундамент всей проверки, а остальные шаги страхуют формулу и исход.

Важно. Аудит подтверждает расчёт, а не вашу выгоду. Даже безупречно честный ролл чаще всего будет проигрышным при низком шансе, а долгосрочный ожидаемый результат в такой игре отрицательный. Ставьте только то, что не жалко потерять.

Гигиена клиентского сида

Клиентский сид — ваш вклад в расчёт, и обращаться с ним стоит осознанно.

  • Придумайте свою строку. Допустимая длина — от 4 до 64 символов. Сид действует во всех играх, поэтому после сохранения он влияет на каждый следующий апгрейд.
  • Не используйте личное. Сид потом появляется в JSON и в истории клиентских сидов, а вы можете захотеть показать данные другому человеку. Не вставляйте туда пароли, имена, почту.
  • Не меняйте его перед каждой игрой. Каждая смена закрывает пару и раскрывает прошлый серверный сид. Это полезно для аудита, но слишком частые смены лишают вас серии: проверять придётся россыпь коротких пар.
  • Фиксируйте момент смены. Записывайте, какой сид был до и после. История клиентских сидов на странице хранит это, но копия у вас под рукой надёжнее.
  • Не считайте «красивый» сид счастливым. Строка влияет на ролл непредсказуемо, и подобрать её под выигрыш нельзя: серверный сид вы до игры не знаете.

Чек-лист аудита по шагам

Ниже порядок действий, который можно выполнять от первого пункта до последнего. Если на каком-то шаге появляется расхождение, остановитесь и сохраните данные.

  1. До игры. Откройте страницу Provably Fair, вкладку «Настройки сидов», и скопируйте публичный хеш пары. Запишите текущий клиентский сид и убедитесь, что он ваш.
  2. Сыграйте один или несколько апгрейдов. Запишите ID каждой игры: он есть в профиле, а кнопка «Проверить» доступна у каждого апгрейда в профиле.
  3. Смените клиентский сид. Раскрыть серверный сид можно только после первого апгрейда на этой паре, и только после смены.
  4. Раскройте сид кнопкой «Раскрыть серверный сид» или найдите его в истории серверных сидов.
  5. Сверьте хеш. Вычислите HMAC-SHA256 от раскрытого сида с раскрытой солью и сравните с хешем из пункта 1.
  6. Для каждой игры откройте вкладку «Проверка апгрейда», введите ID и заберите JSON.
  7. Пересчитайте ролл собственным кодом и сравните с полем roll.
  8. Проверьте окно: ширину, положение и попадание ролла.
  9. Проверьте нонсы: внутри пары они должны идти без дыр.
  10. Сопоставьте исход («Выигрыш» или «Проигрыш») с результатом пункта 8.

Весь процесс занимает несколько минут, если автоматизировать пункты 5–8 функцией из разделов ниже.

Какие данные забрать: Copy JSON и поля

На вкладке «Проверка апгрейда» введите ID и дождитесь данных. Сразу после игры расчёт может занять несколько секунд, и страница об этом сообщит. Далее нажмите «Скопировать JSON» (в англоязычном виде — Copy JSON). В буфер попадает объект с шестью полями:

ПолеЧто этоДля чего нужно в аудите
server_seedСерверный сидСообщение в обеих формулах HMAC
secret_saltСекретная сольКлюч для проверки публичного хеша
public_hashПубличный хешЭталон, с которым сравнивается ваш расчёт
client_seedВаш сидЧасть ключа для ролла
nonceНомер апгрейда в пареВторая часть ключа для ролла
rollВыпавшее числоТо, что вы воспроизводите

Границы окна from…to в JSON не входят: их читайте на странице проверки рядом со шкалой. Туда же смотрите за maxRoll. Если серверный сид ещё не раскрыт, нужных полей не будет, и сначала придётся сменить пару.

Проверка в браузере через Web Crypto

Страница сама пересчитывает результат через Web Crypto, но вы можете сделать то же самое независимо: вставьте код в консоль разработчика вкладки с другим сайтом или в локальный файл. Работает на crypto.subtle, которому нужно безопасное соединение (HTTPS или localhost). Число из 15 hex-символов занимает 60 бит, поэтому обычный Number не годится: он теряет точность после 53 бит, и нужен BigInt.

const enc = new TextEncoder();

async function hmacHex(hash, key, message) {
  const k = await crypto.subtle.importKey(
    "raw", enc.encode(key), { name: "HMAC", hash }, false, ["sign"]
  );
  const sig = await crypto.subtle.sign("HMAC", k, enc.encode(message));
  return [...new Uint8Array(sig)]
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

async function audit(data, maxRoll, win) {
  const hashOk =
    (await hmacHex("SHA-256", data.secret_salt, data.server_seed)) === data.public_hash;

  const h = await hmacHex("SHA-512", `${data.client_seed}-${data.nonce}`, data.server_seed);
  const roll = Number(BigInt("0x" + h.slice(0, 15)) % BigInt(maxRoll)) + 1;

  const inWindow = win.from <= win.to
    ? roll >= win.from && roll <= win.to
    : roll >= win.from || roll <= win.to;

  return { hashOk, roll, rollMatches: String(roll) === String(data.roll), inWindow };
}

// Пример вызова (числа условные, подставьте свои):
// audit(JSON.parse(text), 100, { from: 41, to: 65 }).then(console.log);

Обратите внимание на порядок параметров hmacHex: сначала ключ, потом сообщение. В обоих вызовах сообщением служит серверный сид. Перепутать аргументы — самая частая причина ложного «несовпадения».

Те же вычисления на Python и PHP

Если не доверяете браузеру или хотите прогнать десятки игр подряд, подойдёт любой язык с HMAC. Ниже функции, которые возвращают сразу несколько результатов, а не один print.

import hmac, hashlib

def verify(data, max_roll, window):
    """window = (from_, to) со страницы проверки; max_roll берётся оттуда же."""
    msg = data["server_seed"].encode()
    calc = hmac.new(data["secret_salt"].encode(), msg, hashlib.sha256).hexdigest()
    key = f'{data["client_seed"]}-{data["nonce"]}'.encode()
    h = hmac.new(key, msg, hashlib.sha512).hexdigest()
    roll = int(h[:15], 16) % max_roll + 1
    lo, hi = window
    inside = (lo <= roll <= hi) if lo <= hi else (roll >= lo or roll <= hi)
    return {
        "hash_ok": calc == data["public_hash"],
        "roll": roll,
        "roll_ok": str(roll) == str(data["roll"]),
        "in_window": inside,
    }
<?php
function verify(array $d, int $maxRoll, int $from, int $to): array {
    $hashOk = hash_equals($d['public_hash'],
        hash_hmac('sha256', $d['server_seed'], $d['secret_salt']));
    $h    = hash_hmac('sha512', $d['server_seed'], $d['client_seed'] . '-' . $d['nonce']);
    $roll = (hexdec(substr($h, 0, 15)) % $maxRoll) + 1;
    $in   = $from <= $to ? ($roll >= $from && $roll <= $to)
                         : ($roll >= $from || $roll <= $to);
    return ['hash_ok' => $hashOk, 'roll' => $roll,
            'roll_ok' => (string)$roll === (string)$d['roll'], 'in_window' => $in];
}

В PHP порядок аргументов у hash_hmac иной, чем у Python: сначала алгоритм, затем сообщение, затем ключ. Для hexdec с 15 hex-символами нужна 64-разрядная сборка PHP. Интерфейс сайта ссылается на онлайн-интерпретатор PHP, и этот код можно запустить там.

Три значения true и совпавшее число в поле roll означают, что расчёт чист. Значение in_window само по себе ни хорошее, ни плохое: оно просто должно соответствовать исходу, который записан за игрой.

Окно выигрыша на условной шкале 1…100

Окно — самая необычная часть алгоритма, поэтому разберём его на учебных числах. Условный пример: maxRoll равен 100, шанс 25%. Ширина окна — round(0,25 × 100), то есть 25 позиций.

Обычное окно. Пусть оно идёт от 41 до 65. В диапазон входят позиции 41, 42, …, 65 — ровно 25 штук. Ролл 52 внутри, значит, апгрейд выигран. Ролл 70 снаружи, это проигрыш. Граничные значения проверьте отдельно: 41 и 65 внутри, 40 и 66 снаружи.

Окно через край. Теперь пусть from равен 90, а to равен 14. Раз from больше to, окно состоит из двух частей: 90…100 (11 позиций) и 1…14 (14 позиций). Сумма снова 25. Ролл 3 — выигрыш, ролл 95 — выигрыш, ролл 50 — проигрыш. Границы: 90 внутри, 89 снаружи, 14 внутри, 15 снаружи.

Типичная ошибка при самостоятельной проверке — применить условие from <= roll <= to без разбора случая from > to. Тогда любая игра с окном через край покажет «несоответствие» там, где его нет. В коде выше этот случай обработан отдельной веткой.

Ещё одна проверка, дешёвая и полезная: посчитайте ширину окна и сравните с шансом, показанным перед игрой. Если на шкале 1…100 вы видели 25%, а окно занимает 40 позиций, вопрос уместен. Помните, что при сдвиге окна кнопкой «Подвинуть окно выигрыша» меняется его положение, но не ширина.

Что показывает страница проверки: статусы и ошибки

Статусы страницы проверки удобно читать как диагноз.

  • «Пересчёт совпал с ответом сервера». Встроенный расчёт в вашем браузере дал то же число, что у сервера. Это сильный сигнал, но полный аудит всё равно включает хеш и окно.
  • «Пересчёт не совпал с ответом сервера». Не торопитесь с выводами: сначала убедитесь, что вы смотрите на правильный ID, а ваши скопированные данные целые. Если ваш независимый код тоже даёт другое число, чем поле roll, сохраните JSON и пишите в поддержку.
  • «Серверный сид ещё не раскрыт». Сверить ролл нельзя, пока пара не сменена. До этого проверяется только опубликованный хеш, то есть вы лишь убеждаетесь, что он зафиксирован.
  • «Раскрыть серверный сид» не работает. Так бывает, если на паре ещё не сыграно ни одного апгрейда: кнопка требует хотя бы одной игры. Также возможен сбой загрузки, тогда помогает «Повторить».
  • «Пересчёт недоступен: браузер не даёт доступ к Web Crypto». Это ограничение соединения, а не обвинение сайту. Откройте страницу по HTTPS или используйте код из разделов выше.
  • «Апгрейд не найден». Проверьте ID: лишний пробел или чужой номер дают именно такой ответ.
  • «Апгрейд ещё рассчитывается». Подождите несколько секунд и повторите.
  • Отменённые и компенсированные ставки. Сервер не возвращает для них нонс и ролл, поэтому сверять нечего: проверен только опубликованный хеш. Это штатная ситуация, а не признак подлога.

В выводе сервера вы также увидите строки «данные корректны» или «данные не сходятся». Вторая означает внутреннюю несогласованность данных, и это повод сохранить JSON и скриншот, а не повод паниковать.

Чего Provably Fair не доказывает

Скептик вправе знать пределы метода, и они довольно широкие.

  • Цены. Система не оценивает, справедливо ли оценены предметы в ставке и в цели. Цены сверяйте самостоятельно, учитывая, что они могут отличаться от Steam Market.
  • Сам шанс как параметр. Проверка подтверждает, что ролл вычислен честно и сопоставлен с окном. Является ли заявленный шанс выгодным для вас — вопрос не к криптографии. Подробнее о том, как шанс связан со стоимостью, написано в статье как работает апгрейд.
  • «Повезло» или «не повезло». Честный ролл не обязан радовать. Ни одна серия проверок не делает проигрыш менее вероятным.
  • Выдачу предметов. Отдельная история — вывод в Steam, где свои условия; о них читайте в материале про вывод скинов.
  • Вашу дисциплину. Система не ограничивает ставки за вас.

Ответы на частые вопросы по механике собраны в FAQ по апгрейду. Сама страница проверки открывается по адресу /ru/provably-fair после входа через Steam, а начать можно с главной.

Итог для тех, кто читает по диагонали

  • Подмена серверного сида ловится сравнением хеша, подгонка ролла — пересчётом, неверное окно — шириной и положением границ, пропуск нонса — просмотром серии.
  • Для аудита нужны JSON (шесть полей), maxRoll и границы окна со страницы проверки.
  • Окно с from > to состоит из двух частей, и в коде для него нужна отдельная ветка.
  • Provably Fair не оценивает цены и не меняет шанс; игра остаётся азартной, 18+.

Частые вопросы

Достаточно ли одной проверки, чтобы считать сайт честным?

Нет. Одна проверка подтверждает одну игру. Чтобы картина была убедительной, разумно проверить серию апгрейдов в закрытой паре сидов: хеш, ролл, окно и порядок нонсов. Даже серия лишь показывает отсутствие подмены в проверенных играх и ничего не говорит о будущих результатах или о вашей удаче.

Почему мой код выдаёт другой ролл, хотя данные я скопировал верно?

Чаще всего причина в мелочах. Проверьте, что ключ — это клиентский сид, дефис и нонс без пробелов, а сообщение — серверный сид; что взяты первые 15 hex-символов; что в JavaScript используется BigInt; что значение maxRoll взято с вашей страницы проверки. В PHP отдельно следите за порядком аргументов hash_hmac. Если после этого число всё равно отличается от поля roll, сохраните JSON и обратитесь в поддержку.

Что делать, если ставка отменена или компенсирована и ролла нет?

Для таких ставок сервер не возвращает нонс и ролл, поэтому пересчитывать нечего. Страница сообщает, что проверен только опубликованный хеш. Это нормальное поведение, а не ошибка: вы можете убедиться лишь в том, что хеш был зафиксирован заранее.

Повышает ли Provably Fair мои шансы на выигрыш?

Нет. Система только позволяет проверить, что результат вычислен по зафиксированным данным и не подменён. Шанс определяется соотношением стоимости ваших предметов и цели, а выигрыш никогда не гарантирован. Игра предназначена для совершеннолетних, и ставить стоит лишь то, что вы готовы потерять.

Попробуйте апгрейд на DOTAGRADE

Шанс показан до начала игры. Игра только 18+, ставьте лишь то, чем готовы рискнуть.

Читайте также

18+ Апгрейд — игра на случай, выигрыш не гарантирован. Играйте ответственно.