🔐 公正性驗證

這個頁面是純前端的獨立驗證器。它向伺服器請求公開的承諾哈希、分布哈希、以及(池子抽完後才公開的)server_seed,然後完全在你的瀏覽器內重跑相同的洗牌演算法,比對每一張籤號對應的獎品。

伺服器無法在這個過程中作弊 — 因為承諾哈希在池子開賣前就已公開,事後任何更動都會被你的瀏覽器算出不同的雜湊抓到。

⚡ 即時驗證單張籤號(v55bk 新功能)

不用等池子抽完,輸入卡池 ID + 籤號即可立即用 Merkle proof 驗證該籤的獎品對應沒被竄改。


📜 全表驗證(池抽完後可用)

池子全部抽完後 server_seed 公開,這個驗證會在瀏覽器重跑 Fisher-Yates 洗牌比對每張籤號的對應。

演算法 v2 — 完整步驟(任何人可獨立復現)
  1. 池子開賣前,伺服器產生 32-byte server_seed,公布 commitment = SHA256(server_seed)
  2. 伺服器同時公布 canonical 分布陣列 [{prizeId, count}, ...]distributionHash = SHA256(JSON.stringify(distribution))
  3. 建立陣列 expanded[total]:對每筆 distribution,把該 prizeId push count 次。
  4. 用 HMAC-SHA256 stream PRNG 跑 Fisher-Yates 洗牌:
label = "shuffle:" + poolId
counter = 0
buffer = []
nextU32():
  if buffer 不夠 4 bytes: buffer = HMAC-SHA256(server_seed, label + ":" + counter); counter++
  取 4 bytes 組成 uint32
nextInt(max):  // 排除偏差
  limit = floor(2^32 / max) * max
  loop:
    v = nextU32()
    if v < limit: return v % max

for i from total-1 downto 1:
  j = nextInt(i + 1)
  swap expanded[i], expanded[j]

ticket_no = k + 1  →  expanded[k]
  1. 所有籤都抽完後,伺服器在 /api/pools/:id/fairness 回傳 server_seed
  2. 你的瀏覽器:
    1. 確認 SHA256(server_seed) === commitment
    2. 確認 SHA256(JSON.stringify(distribution)) === distributionHash
    3. 用同樣演算法重跑 Fisher-Yates → 算出 (ticket_no → prizeId) 對應
    4. 把你抽到的籤號丟下方查詢框 → 對比你實際拿到的獎品