每一條 hash chain 的承諾與來源宣告。這一頁不需要登入 —— 承諾要能被還沒註冊的人檢查才有意義。
我們先隨機產生一個 seed,反覆做 sha256 兩百萬次,得到一條鏈。★ 最後算出來的那個值叫 head_hash,在任何人下注之前就公布出去 —— 它是結果,不是起點。從 head_hash 推不出任何 seed(那需要找到 sha256 的原像),正因為推不回去,公布它才是安全的。
遊戲使用的順序跟產生的順序相反:第 1 局用倒數第二環、第 2 局用倒數第三環,以此類推。要竄改任何一局,都得找到 sha256 的原像。
每一局開始前,我們公布那一局的 seed_hash(承諾)。它其實就是上一局結束時揭露的 seed;第 1 局的 seed_hash 就是 head_hash。
那一局結束後,我們揭露 server_seed。你算一次 sha256 就能確認它扣得住承諾。
爆點不是從鏈直接取出來的,而是由 HMAC-SHA256(key = server_seed, msg = salt) 算出。salt 取自我們事先宣告、當時還不存在的公開亂數(drand 的某一輪)。
以下計算請在你自己的機器上做。請伺服器驗證伺服器自己算的東西,那不是驗證。sha256 是決定性的:同樣輸入,任何語言、任何機器、任何時間,永遠得到同一個值。
// 這一局的 seed,雜湊一次要等於這一局開局前公布的 seed_hash sha256(server_seed) === seed_hash
// 手上只有任一局的 seed,也能自己驗到鏈頭 —— 不需要任何歷史資料 let x = server_seed for (let i = 0; i < chain_index; i++) x = sha256(x) x === head_hash // 兩百萬環也只要約 0.4 秒
// 爆點不是挑的,是從 seed 算出來的
h = HMAC_SHA256(key = server_seed, msg = salt) 的前 52 bits
crash_centi = min(max_multiplier * 100,
floor(100 * rtp * 2^52 / (2^52 - h)))★ (a) 與 (b) 只證明「seed 沒被換過」。一定要加上 (c),才證明「爆點是算出來的,不是挑的」。少了 (c),我們仍然可以在鏈不變的前提下,挑一條對自己有利的鏈 —— 那正是 salt 要擋的攻擊。
開發用固定 salt,禁止用於正式環境
h = first 52 bits of HMAC_SHA256(key=server_seed, msg=salt); crash_centi = min(max_multiplier*100, floor(100*rtp*2^52 / (2^52 - h))); chain: sha256(seed[k]) == seed_hash[k], walking back to head_hash開發用固定 salt,禁止用於正式環境
limit = floor(2^52/37)*37; for i=0,1,2,…: x = first 52 bits of HMAC_SHA256(key=server_seed, msg=salt+":"+i); if x < limit then pocket = x % 37; chain: sha256(seed[k]) == seed_hash[k], walking back to head_hash開發用固定 salt,禁止用於正式環境
cards: per column c=0..4 take need[c] of 15 by partial Fisher-Yates with UintN over HMAC_SHA256(key=server_seed, msg=client_seed+":"+nonce+":"+counter); balls: limit = floor(2^52/n)*n; for i=0,1,2,…: x = first 52 bits of HMAC_SHA256(key=round_seed, msg=salt+":"+i); if x < limit then r = x % n (Fisher-Yates over 1..75, n = 75-j); chain: sha256(seed[k]) == seed_hash[k], walking back to head_hash開發用固定 salt,禁止用於正式環境
stream: block_i = HMAC_SHA256(key=round_seed, msg=salt+"|"+client_seeds+":"+chain_index+":"+i), client_seeds = participants' client seeds joined by "," in bet order; hands: for each participant in bet order roll 4 dice via UintN(6)+1 (rejection sampling), reroll the whole hand while it has no point (discards kept in draws); highest hand takes pot×(1−commission); ties split, remainder to winners[UintN(k)]; chain: sha256(seed[k]) == seed_hash[k], walking back to head_hash伺服器時間 2026-10-04T07:44:28.192Z