जाल
एक हैश बत्तीस बाइट्स है। एक गेम को 0 और 1 के बीच एक संख्या चाहिए। स्पष्ट तरीका यह है कि कुछ बाइट्स को एक पूर्णांक के रूप में पढ़ें और सबसे बड़े संभावित मान से विभाजित करें, और हर provably fair व्याख्याता, हमारे सहित, इसे इसी तरह वर्णित करता है। स्पष्ट तरीके की एक समस्या है जो केवल तब दिखाई देती है जब दो अलग-अलग प्रोग्राम इसे करते हैं।
सर्वर और ब्राउज़र दोनों साधारण संख्याओं को IEEE 754 डबल्स के रूप में स्टोर करते हैं: 64 बिट्स, जिनमें से 53 परिशुद्धता रखते हैं। हैश की सात बाइट्स 56 बिट्स हैं, आठ बाइट्स 64 हैं, और कोई भी फिट नहीं होता। इसलिए रूपांतरण को बिट्स खोने चाहिए, और सवाल यह नहीं है कि क्या यह राउंड करता है बल्कि कितनी बार। एक लूप जो एक बार में बाइट्स को जमा करता है, चलती कुल को 256 से गुणा करता है और अगली बाइट को जोड़ता है, हर चरण पर राउंड करता है एक बार कुल 2⁵³ को पास करता है। Kotlin की 64-बिट पूर्णांक को डबल में रूपांतरण बिल्कुल एक बार राउंड करता है। एक ही मान की दो राउंडिंग हमेशा एक ही डबल पर नहीं उतरती जैसे एक।
अधिकांश समय कोई नहीं देखता, क्योंकि अंतर सोलह महत्वपूर्ण अंकों वाली संख्या के अंतिम बिट में है। फिर एक गेम संख्या को 37, या 52, या 1,000,000 से गुणा करता है, और एक पूर्णांक तक राउंड डाउन करता है, और अंतिम बिट वह बिट है जो तय करता है कि 36.9999999999999 36 हो जाता है या 37। एक स्पष्ट तरीके से बनाया गया वेरिफायर पूरी तरह से ईमानदार राउंड के एक छोटे अंश को अस्वीकार करेगा, और एक खिलाड़ी जो अस्वीकार देखता है उसके पास ईमानदार राउंडिंग त्रुटि को बेईमान सर्वर से अलग बताने का कोई तरीका नहीं होगा।
इंजन इसके चारों ओर कैसे कदम रखता है
फिक्स सर्वर की तरह ब्राउज़र को बिल्कुल उतनी बार राउंड करना है, जो एक बार है। इंजन पहली चार बाइट्स को एक पूर्णांक के रूप में पढ़ता है, जो एक डबल में बिल्कुल फिट करता है, और अगली तीन या चार बाइट्स को एक और पूर्णांक के रूप में पढ़ता है, जो भी बिल्कुल फिट करता है। प्रत्येक को दो की शक्ति से विभाजित किया जाता है, एक ऑपरेशन जो डबल्स के लिए सटीक है। फिर दोनों को जोड़ा जाता है। वह एकल जोड़ वह एकमात्र स्थान है जहां परिशुद्धता खो जाती है, और यह इसे Kotlin के एकल रूपांतरण की तरह एक ही तरीके से खोता है।
ENGINE-VERIFIED_shared/rng.ts u56: "पहली 7 बाइट्स → uniform [0,1), सर्वर की Long→Double राउंडिंग बिट-दर-बिट मेल खाता है: hi/2^32 और lo/2^56 दोनों सटीक dyadic डबल्स हैं, इसलिए एकल IEEE जोड़ 56-बिट मान को बिल्कुल एक बार राउंड करता है — Kotlin के toDouble() द्वारा किया जाने वाला एक ही एकल राउंडिंग। (एक naive 56-बिट 2^53 के पास एक डबल में जमा होता है और फर्श किनारों पर भिन्न हो सकता है।)" u64 आठ बाइट्स पर एक ही निर्माण का उपयोग करता है, "सर्वर के ULong→Double रूपांतरण से मेल खाता है LimboService.outcome100 में", और वह फंक्शन है जो हर seed-pair original आयात करता है।
| फंक्शन | बाइट्स पढ़े | निर्माण | मेल खाता है |
|---|---|---|---|
| u56 | पहली 7 | hi ÷ 2³² + lo ÷ 2⁵⁶, एक जोड़ | सर्वर की Long → Double रूपांतरण |
| u64 | पहली 8 | hi ÷ 2³² + lo ÷ 2⁶⁴, एक जोड़ | सर्वर की ULong → Double रूपांतरण (Limbo) |
| h52 / bust100 | first 6.5 (13 hex digits) | exact BigInt integer arithmetic, no double at all | the server's CrashDerivation.crashPoint100 |
Every seed-pair original (bingo, blackjack, hold'em, koban, omikuji, sic bo, video poker, hanabi, fukubukuro, roulette) imports u64 from rng.ts; the generator and the verifier are the same import।
Crash takes the argument to its conclusion। Its crash point follows the bustabit formula, floor((100 × 2⁵² − h) ÷ (2⁵² − h)) where h is the leading 52 bits of the hash, and that division sits on exactly the kind of boundary where a double can be off by one। So the engine does not use a double। It computes the formula with arbitrary-precision integers, in the browser, the way the server does, and the comment in the source says why in one line: a float approximation would disagree with integer division at floor boundaries and make the verifier reject good rounds।
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.
अप्रतिम BigInt गणित सर्वर से मेल खाने के लिए (NewWhiteBack CrashDerivation.crashPoint100) बिट-दर-बिट — एक float अनुमान integer division पर floor boundaries पर असहमत होगा और verifier को अच्छे rounds को reject करने के लिए बनाएगा।
The BigInt values are built with BigInt() calls rather than literals so the file compiles under the project's es5 target।</1>
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.”
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।
- “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.
क्या लेना है इससे
<0>
Provably fair
एक दावा है अंकगणित के बारे में, और अंकगणित के किनारे हैं।</0> The commitment scheme is the headline; the bit-for-bit derivation is what makes the headline enforceable।
क्या एक बिट वास्तव में मायने रखता है?
जब संख्या को 37 या 52 या एक मिलियन से गुणा किया जाता है और नीचे की ओर पूर्णांकित किया जाता है, तो अंतिम बिट यह तय कर सकता है कि कौन सा पूर्णांक परिणाम देता है। यह एक अलग पॉकेट, कार्ड या आइटम है।
इंजन इससे कैसे बचता है?
यह संख्या को दो बिल्कुल प्रतिनिधित्वयोग्य टुकड़ों से बनाता है, पहले चार बाइट्स को 2³² पर और अगली बाइट्स को 2⁵⁶ या 2⁶⁴ पर, और उन्हें एक बार जोड़ता है, जो सर्वर के इंटीजर-टू-डबल रूपांतरण के एकल पूर्णांकन से मेल खाता है। Crash सटीक पूर्णांक अंकगणित का उपयोग करता है और बिल्कुल भी डबल्स नहीं।
क्या सत्यापनकर्ता गेम से एक अलग प्रोग्राम है?
नहीं। हैश और नंबर फंक्शन्स एक साझा मॉड्यूल में रहते हैं जिसे डेमो इंजन और निष्पक्षता पैनल दोनों आयात करते हैं, इसलिए जनरेटर सत्यापनकर्ता से अलग नहीं हो सकता।
यहाँ एक विफल सत्यापन का क्या मतलब है?
कि इनपुट उससे भिन्न हैं जो सर्वर ने उपयोग किया था, क्योंकि पूर्णांकन इस इंजन पर एक गलत विफलता का कारण नहीं बन सकता। यह वह संकेत है जो सत्यापनकर्ता देने के लिए मौजूद है।
स्रोत और संदर्भ
- Betkyo इंजन स्रोत: _shared/rng.ts (u56, u64, h52, bust100, hmacSha256Utf8 और उनकी टिप्पणियां)
- IEEE 754-2019 — फ्लोटिंग-पॉइंट अंकगणित के लिए मानक (binary64: महत्व परिशुद्धि के 53 बिट्स)
- Bustabit provably fair crash-point सूत्र, जो निर्माण bust100 को प्रतिबिंबित करता है
इस लेख में गेम्स
Limbo — नियम और मुफ्त खेल →Crash — नियम और मुफ्त खेल →Roulette — नियम और मुफ्त खेल →