Transparansi permainan
Bagaimana cara kerjanya
Protokol memisahkan komitmen publik dari pengungkapan benih. Ini mencegah hasil dipilih setelah partisipasi.
1. Komitmen
Sebuah hash dipublikasikan sebelum hasilnya. Itu berfungsi seperti amplop digital yang disegel.
2. Undian
Hasilnya berasal dari data yang sudah dikompromikan oleh fungsi deterministik.
3. Wahyu
Setelah hasilnya, benih dan parameter yang diperlukan disediakan untuk pemeriksaan.
4. Reproduksi
Peramban mengulangi hash dan undian. Bukti hanya lolos ketika semuanya sesuai dengan catatan.
Ujian Roulette
Bagaimana hasil dihitung
Roleta menggabungkan serangkaian biji dengan katalog dan bobot yang dipertahankan pada saat putaran.
- Item disusun menurut kunci. Setiap kunci dan berat dimasukkan ke dalam katalog kanonik yang digunakan untuk perhitungan.
- Komitmen dari giro adalah sha-256 dari domain protokol, dari benih server, dan dari katalog kanonik.
- Komitmen rantai mengikat hash awal, katalog, dan ukuran; posisi membuktikan bahwa benih tersebut milik rantai yang diterbitkan.
- Hash dari hasil menjadi angka modulo jumlah bobot. Tiket ini menentukan item yang dipilih.
Kode verifikasi
Tempel di konsol browser atau simpan sebagai file .mjs dan jalankan dengan Node. Kode ini membuat ulang hash dan undian dengan data ujian dan menunjukkan apakah setiap bagian cocok.
Periksa janji di atas untuk mengisi kode dengan data ujian.
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))
Tes Gosok
Bagaimana hasil dihitung
Bukti hanya tersedia setelah pengungkapan, ketika benih sudah dapat dipamerkan dengan aman.
- Katalog dan bobot dibekukan saat scratch card diberikan, bahkan jika pengaturan berubah setelahnya.
- Komitmen menggunakan domain kriptografi yang sama dan undian berbobot yang sama dari Roulette.
- Posisi seed diperiksa terhadap hash awal dan komitmen rantai yang diterbitkan sebelumnya.
- Item, hash hasil, dan seed yang diungkapkan harus mereproduksi hadiah yang tercatat dengan tepat.
Kode verifikasi
Tempel di konsol browser atau simpan sebagai file .mjs dan jalankan dengan Node. Kode ini membuat ulang hash dan undian dengan data ujian dan menunjukkan apakah setiap bagian cocok.
Periksa janji di atas untuk mengisi kode dengan data ujian.
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))

