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.05m。N × 0.18m超過 RP1 width 即拒絕。 match-start、matchId與MatchResultEvent必須攜帶/綁定完整 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;代價是所有正式入口、續接與結果驗證必須同步升級。