跳轉到

D-20260814-20|多人可驗算起跑格

背景與驅動力

房主廣播的 members[] 載入順序原本同時決定物理 handle 與起跑 slot;matchId 又只綁排序後 roster,因此房主可以重排名單取得有利位置而不被其他玩家或帳本發現。若只改用 startedAt 或房主單方亂數洗牌,房主仍可離線試算後挑選結果。

考慮過的選項

  • 把房主 roster order 納入 matchId:只是讓偏袒可見,沒有移除房主決定權,否決。
  • startedAt、matchId 或房主單方 RNG 洗牌:可由房主 grinding,否決。
  • 全 participant 先 commitment、全鎖定後 reveal,再合成可驗算 seed:任何一名誠實 participant 都能阻止單方預選結果,採納。

決定

  • 開賽前鎖定 versioned context:canonical roster、逐回合 track PhysicsManifest digest、match rules、round count 與單調 series identity。每位 participant 以 32-byte CSPRNG nonce 簽 commitment;全 roster commitment 鎖定前 reveal 無效。之後簽 reveal,任一缺席、替換、簽章錯誤、commitment 不符或 context 改變都 fail closed。
  • gridSeed 只由 locked context 與依 PeerId 排序的 nonce 合成,不含 startedAt、房主名單順序或本機時鐘。同一 context 重試沿用本機 nonce/commitment/reveal;roster 或規則變更才建立新 context。
  • 每位 PeerId 的 base key 為 domain-separated hash(gridSeed, PeerId);排序後第 roundIndex 回合做 cyclic rotation。round index 依 proof 的 round count 由 0 連續前進,不接受跳號。
  • 所有車依 canonical PeerId order 建立物理拓撲,另以明確 slot mapping 放置。格位同縱向、沿 RP1 surface-frame right 軸左右對稱:(slot-(N-1)/2) × 0.18m;沿 up 抬 START_GRID_LIFT_M=0.05mN × 0.18m 超過 RP1 width 即拒絕。
  • match-startmatchIdMatchResultEvent 必須攜帶/綁定完整 bounded proof。Race runtime、resume、live receive 與 timeless fold 都重算 proof、seed、permutation 與 roster;anchor 透過已綁 proof 的 matchId 與 roundIndex 繼承相同 domain。
  • UI 顯示 commitment/reveal/cancelled 階段、timeout/mismatch/context changed 原因與未完成 PeerId。兩階段各 15 秒,不替逾時者產生 nonce。
  • 專案處於 pre-launch;只存在此 current shape,不提供缺 proof、舊 matchId 或舊 result 的 dual-read/fallback/migration。

後果與影響

房主仍可拒絕揭露而阻止一場比賽,但不能無痕重抽或選擇自己位置。最多 8 人時只增加兩輪有界賽前訊息與 bounded proof,不進 fixed-step、rollback 或 spectator frame;代價是所有正式入口、續接與結果驗證必須同步升級。