Tuzak
Bir hash otuz iki bayttır. Bir oyun 0 ile 1 arasında bir sayıya ihtiyaç duyar. Elde etmenin açık yolu, baytlardan bazılarını bir tamsayı olarak okumak ve en büyük olası değere bölmektir ve bizimki de dahil olmak üzere her provably fair açıklaması bunu bu şekilde açıklar. Açık yolun, iki farklı program bunu yaptığında ortaya çıkan bir sorunu vardır.
Hem sunucu hem de tarayıcı sıradan sayıları IEEE 754 double'ı olarak depolar: 64 bit, bunlardan 53'ü kesinlik taşır. Yedi bayt hash 56 bittir, sekiz bayt 64'tür ve ikisi de uygun değildir. Dolayısıyla dönüşüm bitleri kaybetmelidir ve soru yuvarlama olup olmadığı değil kaç kez olduğudur. Baytları birer birer biriktiren, çalışan toplamı 256 ile çarpan ve sonraki baytı ekleyen bir döngü, toplam 2⁵³'i geçtikten sonra her adımda yuvarlar. Kotlin'in 64-bitlik bir tamsayının double'a dönüştürülmesi tam olarak bir kez yuvarlar. Aynı değerin iki yuvarlaması her zaman tek bir yuvarlamayla aynı double'a inmez.
Çoğu zaman kimse fark etmez, çünkü fark on altı anlamlı rakamı olan bir sayının son bitindedir. Sonra bir oyun sayıyı 37 ile, 52 ile veya 1.000.000 ile çarpar ve aşağı doğru bir tamsayıya yuvarlar ve son bit, 36.9999999999999'un 36 mı yoksa 37 mi olacağına karar veren bitin tam olarak bitidir. Açık yolla inşa edilen bir doğrulayıcı, mükemmel dürüst rundların küçük bir kısmını reddetse de, bir reddetmeyi gören bir oyuncu, dürüst bir yuvarlama hatasını dürüst olmayan bir sunucudan ayırt etmenin hiçbir yolu olmazdı.
Motor nasıl etrafından döner
Çözüm, tarayıcının sunucu ile tamamen aynı sayıda yuvarlanmasını sağlamaktır, bu da birdir. Motor ilk dört baytı bir tamsayı olarak okur, bu bir double'a tam olarak sığar ve sonraki üç veya dört baytı başka bir tamsayı olarak okur, bu da tam olarak sığar. Her biri ikinin gücüne bölünür, bu double'lar için kesin bir işlemdir. Sonra ikisi eklenir. Bu tek toplama, kesinliğin kaybolduğu tek yerdir ve Kotlin'in tek dönüştürmesinin kaybettiği şekilde kaybeder.
ENGINE-VERIFIED_shared/rng.ts u56: “first 7 bytes → uniform [0,1), matching the server’s Long→Double rounding bit-for-bit: hi/2^32 and lo/2^56 are both exact dyadic doubles, so the one IEEE addition rounds the 56-bit value exactly once — the same single rounding Kotlin’s toDouble() performs. (A naive 56-bit accumulate in a double would round repeatedly past 2^53 and can differ at floor edges.)” u64 uses the same construction over eight bytes, “matching the server’s ULong→Double conversion in LimboService.outcome100”, and is the function every seed-pair original imports.
| FONKSİYON | OKUNAN BAYTLAR | İNŞAAT | EŞLEŞİRME |
|---|---|---|---|
| u56 | ilk 7 | hi ÷ 2³² + lo ÷ 2⁵⁶, bir toplama | sunucunun Long → Double dönüştürmesi |
| u64 | ilk 8 | hi ÷ 2³² + lo ÷ 2⁶⁴, bir toplama | sunucunun ULong → Double dönüştürmesi (Limbo) |
| h52 / bust100 | ilk 6.5 (13 hex basamak) | tam BigInt tamsayı aritmetiği, hiç double yok | sunucunun CrashDerivation.crashPoint100 |
Her seed-pair orijinal (bingo, blackjack, hold'em, koban, omikuji, sic bo, video poker, hanabi, fukubukuro, roulette) rng.ts'den u64 içe aktarır; generator ve verifier aynı içe aktarımdır.
Crash argümanını sonuca götürür. Onun crash noktası bustabit formülünü takip eder, floor((100 × 2⁵² − h) ÷ (2⁵² − h)) burada h hash'in öncü 52 bitidir ve bu bölme tam double'ın bir ile yanlış olabileceği sınırın üzerinde yer alır. Yani engine double kullanmaz. Sunucunun yaptığı gibi formülü tarayıcıda isteğe bağlı kesinlikli tamsayılarla hesaplar ve kaynak kodu yorumu bunu bir satırda açıklar: bir float yaklaşımı floor sınırlarında tamsayı bölünmesiyle anlaşmazlığa düşer ve verifier'ı iyi turları reddetmeye zorlar.
ENGINE-VERIFIED_shared/rng.ts bust100: h = h52(digest), the first 13 hex characters as a BigInt; p = (100·2⁵² − h) ÷ (2⁵² − h) in BigInt division, clamped to [100, 1,000,000]; the comment reads “Exact BigInt math to match the server (NewWhiteBack CrashDerivation.crashPoint100) bit-for-bit — a float approximation would disagree with integer division at floor boundaries and make the verifier reject good rounds.” The BigInt values are built with BigInt() calls rather than literals so the file compiles under the project’s es5 target.
, =
, = Exact BigInt math to match the server (NewWhiteBack CrashDerivation.crashPoint100) bit-for-bit — a float approximation would disagree with integer division at floor boundaries and make the verifier reject good rounds.
ENGINE-VERIFIED_shared/rng.ts header: “Crypto primitives shared by the provably-fair GENERATOR (demoLocal.ts, the per-game derive modules) and the VERIFIER (fairness.tsx). Extracted from fairness.tsx so a game’s derivation can be imported by both sides without a circular import.” hmacSha256Utf8 is described as consuming key and message “exactly as RandomUtils.generateHash consumes them on the server … one implementation, so the demo can never drift from what the verifier accepts.”
, =
, = BigInt değerleri, dosya projenin es5 hedefi altında derlenebilsin diye BigInt() çağrılarıyla oluşturulur.
}1 implementation, two jobs
- “Provably fair” is a claim about arithmetic, and arithmetic has edges. The commitment scheme is the headline; the bit-for-bit derivation is what makes the headline enforceable.
- A mismatch should be rare enough to be alarming. On this engine an honest round cannot fail verification because of rounding, so a failure is information rather than an artefact.
- You can read the function. The construction is a few lines, and the verification walkthrough shows where each one is used on a live round.
This article describes the client derivation module and quotes its comments about the server it mirrors; the server itself is not in the public repository, and the house service is the paying authority. It is an engineering note, not a certification.
, =
, =
, =
Bir bit gerçekten önemli mi?
Sayı 37 veya 52 veya bir milyon ile çarpıldığında ve aşağıya yuvarlandığında, son bit hangi tamsayının sonuç olacağını belirleyebilir. Bu farklı bir cep, kart veya öğedir.
Motor bunu nasıl önler?
Sayıyı tam temsil edilebilen iki parçadan oluşturur, ilk dört byte 2³² üzerinden ve sonraki byte 2⁵⁶ veya 2⁶⁴ üzerinden, ve bunları bir kez ekler, sunucunun tamsayı-double dönüşümünün tek yuvarlaması ile eşleştirir. Crash tam sayı aritmetiği kullanır ve hiç double kullanmaz.
Verifier oyundan ayrı bir program mı?
Hayır. Hash ve number fonksiyonları, hem demo engine hem de fairness panelinin içe aktardığı bir paylaşılan modülde yaşar, bu nedenle generator verifierden sapamaz.
Burada başarısız bir doğrulama ne anlama gelir?
Girdiler sunucunun kullandığından farklıdır, çünkü yuvarlama bu engine üzerinde yanlış bir başarısızlığa neden olamaz. Bu verifierin vermek için var olduğu sinyaldir.
KAYNAKLAR & REFERANSLAR
- Betkyo engine kaynağı: _shared/rng.ts (u56, u64, h52, bust100, hmacSha256Utf8 ve bunların yorumları)
- IEEE 754-2019 — Kayan Nokta Aritmetiği Standardı (binary64: 53 bit anlamlılık hassasiyeti)
- Bustabit provably fair crash-point formülü, bust100 yapısı yansıtır
BU MAKALEDEKİ OYUNLAR
Limbo — kurallar & ücretsiz oyna →Crash — kurallar & ücretsiz oyna →Roulette — kurallar & ücretsiz oyna →