Provably Fair

Результат нельзя подкрутить

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

Формула

hash   = HMAC_SHA256(server_seed, client_seed + ":" + nonce)
roll   = parseInt(hash.slice(0, 8), 16) / 2**32   // [0, 1)

// кейс: выбор по кумулятивным весам
target = roll * сумма_весов
предмет = первый, у которого накопленный вес >= target

// апгрейдер: сравнение с порогом
p      = (стоимость_входа / стоимость_цели) * (1 - комиссия)
успех  = roll < p

Первые 8 символов хеша дают целое число до 2³², деление на 2³² даёт дробь в диапазоне от нуля до единицы. Дальше это число либо сопоставляется с таблицей дропа, либо сравнивается с порогом успеха. Никакой дополнительной логики между расчётом и выдачей нет.

Как это работает

01

Сервер готовит сид заранее

До начала игры сервер генерирует секретную строку — server seed — и публикует её SHA-256 хеш. Сам сид остаётся скрытым, но хеш нельзя подобрать обратно.

02

Ты задаёшь свой сид

Client seed можно поменять в любой момент. Он участвует в расчёте наравне с серверным, поэтому предсказать результат в одиночку не может ни одна сторона.

03

Каждая игра нумеруется

Nonce — порядковый номер ставки. Одна и та же пара сидов даёт разные результаты для разных номеров.

04

Сид раскрывается

После смены сида сервер публикует исходный server seed. Ты сверяешь его хеш с тем, что был показан заранее — и пересчитываешь любой свой дроп.

Проверить свой дроп

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

const enc = new TextEncoder();
const key = await crypto.subtle.importKey(
  "raw", enc.encode(SERVER_SEED),
  { name: "HMAC", hash: "SHA-256" }, false, ["sign"]
);
const sig = await crypto.subtle.sign(
  "HMAC", key, enc.encode(CLIENT_SEED + ":" + NONCE)
);
const hex = [...new Uint8Array(sig)]
  .map(b => b.toString(16).padStart(2, "0")).join("");

const roll = parseInt(hex.slice(0, 8), 16) / 2 ** 32;
console.log(roll);

Пока идёт разработка: сейчас сайт работает в демо-режиме, результат считается в браузере, а не на сервере. Схема выше — то, как это устроено в боевой версии. До запуска реальных ставок раздел будет полностью рабочим, с публикацией сидов и историей проверок.

RTP всех кейсов

RTP — доля от цены кейса, которая в среднем возвращается игроку предметами. Значения посчитаны прямо из таблиц дропа, а не вписаны вручную: изменится таблица — изменится и число здесь.