Kanıtlanabilir Adil
Bir turu doğrula
= 1,000000 KC
Sonuç
- Çarpan
- —
- Kazanma şansı
- —
- Ödeme
- —
- —
Hesaplama
- SHA-256(sunucu tohumu baytları)—
- SHA-256(sunucu tohumunun hex metni)—
- Mesaj—
- HMAC-SHA256(sunucu tohumu, mesaj)—
- 0–3. baytlar: — = ——
- Ödeme—
Kuralın tamamı
Sunucu tohumu: 64 onaltılık karakter olarak gösterilen 32 rastgele bayt. Yayımlanan hash, bu 32 baytın SHA-256 değeridir; onaltılık metnin değil.
Mesaj: istemci tohumu, iki nokta, ardından başında sıfır olmadan onluk tabanda nonce; UTF-8 olarak kodlanır — örneğin Xq3vL9mTz2RkPw7a:1.
Mesaj üzerinde, 32 sunucu tohumu baytıyla anahtarlanmış HMAC-SHA256. Sonuç 32 bayttır.
Bu 32 baytı sekiz adet 4 baytlık big-endian sayı olarak oku. 4.294.960.000 altındaki ilk sayıyı kullan; bu, 2³² altındaki en büyük 10.000 katıdır. Bu değere eşit ya da üstündeki bir sayı atlanır ve sonraki dört bayt okunur. Böylece 10.000 atışın her biri tam olarak eşit olasılıkta kalır.
Sekizinin de atlanması durumunda — yaklaşık 6,9 × 10⁻⁴⁷ olasılık — sunucu bahsi reddeder ve hiçbir şey kaydetmez.
Zar = bu sayının 10.000’e bölümünden kalan, 100’e bölünür: 0,00, 0,01, … 99,99’dan biri.
Hedef: 0,01’in katı — üzerinde için 0,01 ile 99,98, altında için 0,02 ile 99,99 arası. Yeni bir bahsin kazanma şansı en fazla %98,00’dir: üzerinde için 1,99’dan, altında için 98,00’e kadar.
Üzerinde, yalnızca zar hedeften büyükse; altında, yalnızca küçükse kazanır. Hedefe eşit bir zar kaybeder.
Yüzde olarak kazanma şansı: üzerinde için 99,99 − hedef, altında için hedefin kendisi.
Çarpan: 95 ÷ kazanma şansı — %5 kasa avantajı — altı ondalığa aşağı yuvarlanır. Sunucudaki gibi standart çift duyarlıklı aritmetikle hesaplanır: 19.996 olası hedef ve yön çiftinin 2 tanesinde bu, tam aritmetiğe göre milyonda bir düşük yuvarlanır; bu yüzden 98,71 üzerinde 74,218749× öder.
Ödeme: bahis tutarı × çarpan, tam bir taban birime aşağı yuvarlanır — KC’nin milyonda biri. Kayıp 0 öder. Hiçbir tutar başka bir para birimine çevrilmez.
Referans uygulama
// KOCase dice v1 — reference verifier. WebCrypto only: Bun, Deno, Node 20+ or a browser.export async function verifyDiceV1( serverSeedHex: string, // the revealed server seed: 64 lowercase hex characters hashScheme: 'bytes' | 'hex', // what the seed's published hash was taken over clientSeed: string, nonce: bigint, // the first bet on a seed is nonce 1 target: number, // a multiple of 0.01 direction: 'over' | 'under', wager: bigint, // in base units: 10^-6 KC) { if (!/^[0-9a-f]{64}$/.test(serverSeedHex)) throw new Error('the server seed must be 64 lowercase hex characters'); if (hashScheme !== 'bytes' && hashScheme !== 'hex') throw new Error('the hash scheme is bytes or hex'); const { subtle } = globalThis.crypto; const hex = (b: ArrayBuffer) => [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, '0')).join(''); const seed = new Uint8Array(serverSeedHex.match(/../g)!.map((h) => parseInt(h, 16))); const committed = hashScheme === 'hex' ? new TextEncoder().encode(serverSeedHex) : seed; // 'hex': the 64-character text; 'bytes': the 32 bytes it spells const serverSeedHash = hex(await subtle.digest('SHA-256', committed)); const key = await subtle.importKey('raw', seed, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']); const message = new TextEncoder().encode(`${clientSeed}:${nonce}`); const mac = new DataView(await subtle.sign('HMAC', key, message)); let roll: number | undefined; for (let offset = 0; offset < 32 && roll === undefined; offset += 4) { const value = mac.getUint32(offset); // big-endian if (value < 4_294_960_000) roll = (value % 10_000) / 100; // floor(2^32 / 10000) * 10000 } if (roll === undefined) throw new Error('all eight windows rejected: the server refuses this bet'); const chance = direction === 'over' ? 99.99 - target : target; // percent, in doubles as the server does const scaled = BigInt(Math.floor((95 / chance) * 1_000_000)); // multiplier, floored to 6 decimals const win = direction === 'over' ? roll > target : roll < target; // a roll equal to the target loses const payout = win ? (wager * scaled) / 1_000_000n : 0n; // bigint division floors const multiplier = `${scaled / 1_000_000n}.${(scaled % 1_000_000n).toString().padStart(6, '0')}`; return { serverSeedHash, roll, win, multiplier, payout };}Bunun kanıtladıkları ve henüz kanıtlamadıkları
Açıklanan bir tohumun yayımlanan hash’iyle eşleştiğini ve tohum, istemci tohumu ve nonce’un gösterilen zarı, sonucu ve ödemeyi tam olarak ürettiğini kanıtlar.
Hash, bahisten önce gördüğün hash olmalı. İlk kez sonradan okunan bir hash o bahis hakkında hiçbir şey taahhüt etmez.
İlk istemci tohumun tarayıcında çekilir ve kaydolduğunda taahhüt edilen bir sunucu tohumunu etkinleştirir. Bundan önce açılan hesaplarda ilk tohumun istemci tohumunu sunucu seçti ve tohumu yenileyene kadar o kalır; o tohumda bir kontrol yalnızca atışların gösterilen değerlerden çıktığını gösterir, sunucunun iki tohumu birlikte seçemediğini göstermez.
Her seferinde tek bir turu kontrol eder. Açıklanmış bir tohumun tüm tur listesi oyundaki Kanıtlanabilir Adil bölümündedir; orada listelenen nonce’lar, tohumun bahis sayısına göre boşluk ve tekrar için kontrol edilir.