🔐 公正性驗證
這個頁面是純前端的獨立驗證器。它向伺服器請求公開的承諾哈希、分布哈希、以及(池子抽完後才公開的)server_seed,然後完全在你的瀏覽器內重跑相同的洗牌演算法,比對每一張籤號對應的獎品。
伺服器無法在這個過程中作弊 — 因為承諾哈希在池子開賣前就已公開,事後任何更動都會被你的瀏覽器算出不同的雜湊抓到。
⚡ 即時驗證單張籤號(v55bk 新功能)
不用等池子抽完,輸入卡池 ID + 籤號即可立即用 Merkle proof 驗證該籤的獎品對應沒被竄改。
📜 全表驗證(池抽完後可用)
池子全部抽完後 server_seed 公開,這個驗證會在瀏覽器重跑 Fisher-Yates 洗牌比對每張籤號的對應。
演算法 v2 — 完整步驟(任何人可獨立復現)
- 池子開賣前,伺服器產生 32-byte
server_seed,公布commitment = SHA256(server_seed)。 - 伺服器同時公布 canonical 分布陣列
[{prizeId, count}, ...]與distributionHash = SHA256(JSON.stringify(distribution))。 - 建立陣列
expanded[total]:對每筆 distribution,把該 prizeId pushcount次。 - 用 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]
- 所有籤都抽完後,伺服器在
/api/pools/:id/fairness回傳server_seed。 - 你的瀏覽器:
- 確認
SHA256(server_seed) === commitment - 確認
SHA256(JSON.stringify(distribution)) === distributionHash - 用同樣演算法重跑 Fisher-Yates → 算出 (ticket_no → prizeId) 對應
- 把你抽到的籤號丟下方查詢框 → 對比你實際拿到的獎品
- 確認