Прозрачность игры
Как это работает
Протокол отделяет публичное обязательство от раскрытия семени. Это предотвращает выбор результата после участия.
1. Обязательство
Хэш публикуется перед результатом. Он работает как запечатанный цифровой конверт.
2. Рисуем
Результат получается на основе данных, уже зафиксированных детерминированной функцией.
3. Откровение
После получения результата начальное число и необходимые параметры становятся доступными для проверки.
4. Воспроизведение
Браузер повторяет хеши и отрисовку. Тест считается пройденным только тогда, когда все соответствует записи.
Тест на рулетку
Как рассчитывается результат
Рулетка сочетает в себе цепочку семян с каталогом и весами, сохраняющимися на момент вращения.
- Товары упорядочены по ключам. Каждый ключ и вес попадают в канонический каталог, используемый при расчете.
- Обязательство по вращению , это sha-256 домена протокола, начального числа сервера и канонического каталога.
- Цепное обязательство связывает первоначальный хэш, каталог и размер; позиция доказывает, что начальное число принадлежит опубликованной цепочке.
- Хэш результата становится числом по модулю суммы весов. Этот билет определяет выбранный элемент.
Код подтверждения
Вставьте его в консоль браузера или сохраните как файл .mjs и запустите с помощью Node. Код повторяет хэши и отрисовывает тестовые данные и показывает, соответствует ли каждая часть.
Проверьте встречу выше, чтобы заполнить код подтверждающими данными.
const proof = {
"serverSeed": "SERVER_SEED",
"commitmentHash": "COMMITMENT_HASH",
"chainCommitment": "CHAIN_COMMITMENT",
"headHash": "HEAD_HASH",
"index": 0,
"size": 1,
"items": [
{
"key": "ITEM_KEY",
"weight": 1
}
]
}
async function sha256(text) {
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('')
}
async function verifyReward(proof) {
const items = [...proof.items]
.sort((left, right) => (left.key < right.key ? -1 : left.key > right.key ? 1 : 0))
.map(({ key, weight }) => ({ key, weight }))
const commitment = await sha256(
JSON.stringify(['tgrtslj:reward:commitment:v1', proof.serverSeed, items]),
)
const chain = await sha256(
JSON.stringify(['tgrtslj:reward:commitment-chain:v1', proof.headHash, items, proof.size]),
)
let link = proof.serverSeed
for (let step = 0; step <= proof.index; step += 1) link = await sha256(link)
const resultHash = await sha256(
JSON.stringify(['tgrtslj:reward:weighted-draw:v1', proof.serverSeed, items]),
)
const totalWeight = items.reduce((sum, item) => sum + item.weight, 0)
const ticket = Number(BigInt('0x' + resultHash) % BigInt(totalWeight))
let upperBound = 0
const selected = items.find((item) => ticket < (upperBound += item.weight))
return {
commitmentMatches: commitment === proof.commitmentHash,
chainMatches: chain === proof.chainCommitment,
positionMatches: proof.index < proof.size && link === proof.headHash,
resultHash,
ticket,
item: selected.key,
}
}
console.log(await verifyReward(proof))
Тест скретч-карты
Как рассчитывается результат
Доказательство становится доступным только после откровения, когда семя может быть безопасно раскрыто.
- Каталог и вес фиксируются при выдаче скретч-карты, даже если позже конфигурация изменится.
- Escrow использует тот же криптографический домен и взвешенный розыгрыш, что и рулетка.
- Начальная позиция сверяется с исходным хэшем и ранее опубликованным обязательством по цепочке.
- Предмет, хэш результата и обнаруженное начальное число должны точно воспроизводить зарегистрированный приз.
Код подтверждения
Вставьте его в консоль браузера или сохраните как файл .mjs и запустите с помощью Node. Код повторяет хэши и отрисовывает тестовые данные и показывает, соответствует ли каждая часть.
Проверьте встречу выше, чтобы заполнить код подтверждающими данными.
const proof = {
"serverSeed": "SERVER_SEED",
"commitmentHash": "COMMITMENT_HASH",
"chainCommitment": "CHAIN_COMMITMENT",
"headHash": "HEAD_HASH",
"index": 0,
"size": 1,
"items": [
{
"key": "ITEM_KEY",
"weight": 1
}
]
}
async function sha256(text) {
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('')
}
async function verifyReward(proof) {
const items = [...proof.items]
.sort((left, right) => (left.key < right.key ? -1 : left.key > right.key ? 1 : 0))
.map(({ key, weight }) => ({ key, weight }))
const commitment = await sha256(
JSON.stringify(['tgrtslj:reward:commitment:v1', proof.serverSeed, items]),
)
const chain = await sha256(
JSON.stringify(['tgrtslj:reward:commitment-chain:v1', proof.headHash, items, proof.size]),
)
let link = proof.serverSeed
for (let step = 0; step <= proof.index; step += 1) link = await sha256(link)
const resultHash = await sha256(
JSON.stringify(['tgrtslj:reward:weighted-draw:v1', proof.serverSeed, items]),
)
const totalWeight = items.reduce((sum, item) => sum + item.weight, 0)
const ticket = Number(BigInt('0x' + resultHash) % BigInt(totalWeight))
let upperBound = 0
const selected = items.find((item) => ticket < (upperBound += item.weight))
return {
commitmentMatches: commitment === proof.commitmentHash,
chainMatches: chain === proof.chainCommitment,
positionMatches: proof.index < proof.size && link === proof.headHash,
resultHash,
ticket,
item: selected.key,
}
}
console.log(await verifyReward(proof))

