Spor

Oyunlar

Genel

Özel Oyunlar - Kanıtlanabilir Adil

Özel Oyunlarda Nasıl Kullanılır

YEET Original oyunlarındaki her bahis bir round seed ile başlar. Round seed, client seed'inizin ve bahsin nonce değerinin, YEET tarafından seçilen bir server seed ile anahtarlanmış HMAC-SHA256 özetidir. Oyun, o bahis için ihtiyaç duyduğu her rastgele değeri bu round seed'den çeker. Server seed, siz oynamadan önce size özetlenmiş (hashed) şekilde gösterilir ve seed çiftinizi değiştirdiğinizde açıklanır; böylece geçmişteki herhangi bir bahsin round seed'ini yeniden hesaplayabilir ve sonucun bahis yaptıktan sonra değiştirilmediğini kontrol edebilirsiniz.

Diğer doğrulayıcılar eşleşmeyebilir

Diğer kumarhaneler için oluşturulmuş doğrulama siteleri rastgele sayıları farklı şekilde üretebilir, bu nedenle bir YEET bahsini orada kontrol etmek farklı bir sonuç verebilir. YEET bahislerini, oyunun Adillik (Fairness) penceresindeki Doğrulama sekmesinden veya bu sayfadaki kod ile doğrulayın.

Rastgele Sayı Üretimi

Her bahis iki adımda oluşturulur. İlk olarak, seed çiftiniz ve bahsin nonce değeri tek bir round seed verir. İkinci olarak, round seed rastgele baytlardan oluşan bir akış üretir ve oyun, bu akıştan tarafsız tam sayılar okur. Aynı girdiler her zaman aynı sayıları verir, sonucun doğrulanabilir olmasını sağlayan da budur.

1. Girdiler

  • Sunucumuz tarafından oluşturulan rastgele 64 karakterli onaltılık dize
  • Herhangi bir zamanda değiştirebileceğiniz bir dize
  • Oynanan her oyunla artan bir sayaç
  • İmleç (Cursor) — round seed mesajının son kısmı. Her zaman 0'dır.

2. Round seed

Round seed, clientSeed:nonce:0 metni üzerinde server seed (32 bayt olarak) ile anahtarlanmış bir HMAC-SHA256'dır. Sonuç 32 bayttır ve 64 onaltılık karakter olarak yazılır. Bir bahsin tam olarak bir tane round seed'i vardır.

async function deriveGameSeed(serverSeed, clientSeed, nonce) {
  const seedBytes = hexToBytes(serverSeed);
  if (seedBytes.length !== 32) {
    throw new Error(`Server seed must be 32 bytes (64 hex characters), got ${seedBytes.length}.`);
  }

  const key = await crypto.subtle.importKey(
    'raw', toArrayBuffer(seedBytes), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'],
  );
  const message = new TextEncoder().encode(`${clientSeed}:${nonce}:0`);
  return bytesToHex(new Uint8Array(await crypto.subtle.sign('HMAC', key, message)));
}

3. Round seed'den entropi

Round seed, ikinci bir HMAC-SHA256'nın anahtarıdır. Her çağrı 8 baytlık bir sayacı (küçük endian, 0'dan başlar) imzalar ve 32 baytlık bir blok verir; sayaç blok başına bir artar. Okumalar, baytları mevcut bloktan sırayla alır. Bir okuma, blokta kalan baytlardan fazlasına ihtiyaç duyduğunda, bloğun geri kalanı atlanır ve bir sonraki blok oluşturulur.

class GameEntropy {
  constructor(seedBytes) {
    if (seedBytes.length !== 32) throw new Error('Seed must be exactly 32 bytes (64 hex characters).');
    this.buffer = new Uint8Array(32);
    this.offset = 32;
    this.cursor = 0;
    this.key = crypto.subtle.importKey(
      'raw', toArrayBuffer(seedBytes), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'],
    );
  }

  async next(count) {
    if (count < 1 || count > 32) throw new RangeError('count must be between 1 and 32');

    if (this.offset + count > 32) {
      const message = new ArrayBuffer(8);
      const view = new DataView(message);
      view.setUint32(0, this.cursor >>> 0, true);
      view.setUint32(4, Math.floor(this.cursor / 0x100000000) >>> 0, true);

      const hash = await crypto.subtle.sign('HMAC', await this.key, message);
      this.buffer.set(new Uint8Array(hash));
      this.cursor++;
      this.offset = 0;
    }

    const slice = this.buffer.slice(this.offset, this.offset + count);
    this.offset += count;
    return slice;
  }
}

4. Tarafsız sayılar

Sonuçlar tam sayı olarak çekilir. readUint32, 4 baytı işaretsiz 32 bitlik bir tam sayı (küçük-endian) olarak okur. unbiasedUint(entropy, n), 0'dan n − 1'e kadar her biri aynı şansa sahip bir sayı döndürür. n, ikinin bir kuvveti olduğunda kalanı doğrudan alır. Aksi takdirde, 32 bite sığan en büyük n katının altındaki değerleri kabul eder ve değer bunun üzerinde veya eşitse tekrar okur, böylece hiçbir sonuç kayırılmaz.

async function readUint32(entropy) {
  const bytes = await entropy.next(4);
  return new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0, true);
}

async function unbiasedUint(entropy, range) {
  if (range <= 0) throw new Error('Range must be positive');
  if (range === 1) return 0;

  if ((range & (range - 1)) === 0) {
    return (await readUint32(entropy)) % range;
  }

  const maxValue = 0xffffffff;
  const threshold = maxValue - (maxValue % range) - 1;

  for (;;) {
    const raw = await readUint32(entropy);
    if (raw <= threshold) return raw % range;
  }
}

5. Yardımcılar

function hexToBytes(hex) {
  if (typeof hex !== 'string') throw new TypeError('Hex value must be a string.');
  const clean = hex.trim().replace(/^0x/i, '');
  if (clean.length % 2 !== 0) throw new Error('Hex value must have an even length.');
  if (!/^[0-9a-fA-F]*$/.test(clean)) throw new Error('Hex value contains non-hex characters.');

  const bytes = new Uint8Array(clean.length / 2);
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = parseInt(clean.substring(i * 2, i * 2 + 2), 16);
  }
  return bytes;
}

function bytesToHex(bytes) {
  return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
}

function toArrayBuffer(bytes) {
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
}

6. Bahis doğrulama

  • Bahsinizi geçmişinizden açın ve istemci tohumunu (client seed), nonce değerini ve hash'lenmiş sunucu tohumunu kopyalayın.
  • Sunucu tohumunu ortaya çıkarmak için tohum çiftinizi döndürün ve SHA-256 hash'inin bahisten önce gördüğünüz hash ile eşleştiğini kontrol edin.
  • Yuvarlak tohumu (round seed) elde etmek için sunucu tohumu, istemci tohumu ve nonce ile deriveGameSeed fonksiyonunu çalıştırın.
  • Yuvarlak tohumundan bir GameEntropy oluşturun, oyunun dönüşümünü uygulayın (Dönüşümlere bakın) ve sonucu bahsinizle karşılaştırın.