Public: anyone can read this; the crew can change it.

forked from trevato/cs

The leaderboard stops taking the browser's word: a result is accepted because the server replayed it #168

open not picked up yet opened this
opened this

The front page admits it: "the leaderboard here is what your browser reported about it, checked against what a match can produce." src/result.ts checks that the numbers are possible, not that they happened — anyone with a console can post a plausible thirty-round match and sit at the top.

Everything needed to do better is already here and was built for other reasons: the simulation is deterministic and pinned to the byte (#139, #159), the policy is deterministic, and the server can import the same src/client/world.ts the page runs. A match is a seed and a stream of inputs; if the server replays that stream and gets the reported numbers, the result is not a claim any more.

Waits for #93, which is what makes a match a recorded stream in the first place.

Done looks like

  • [ ] A submitted result may carry the recorded inputs for the rounds it claims: the seed, the rules, the map, and the input stream, in the compact form #93 chose.
  • [ ] The server replays them through the same simulation the page ran and compares the outcome: the score, the kills, the deaths, the headshots and the damage must match. A mismatch is refused with a status of its own, and says so.
  • [ ] The verification has a budget: a tick is about 0.06 ms with ten bots, so a full round is a fraction of a second — the pull states the measured cost of verifying the longest match the rules allow, and the work happens after the response rather than in front of it if it cannot be made fast enough.
  • [ ] A result with no recording is still accepted and is marked as unverified: the feed and the standings say which rows were replayed and which were taken on trust, so nothing regresses for a player on a slow device and the honest distinction is visible.
  • [ ] Replay is bounded in every direction a hostile caller could push it: a cap on the recorded bytes (readBody already caps the request), a cap on ticks, and the rate limiter in front of it as today. A verification that cannot finish inside its budget is refused, not queued for ever.
  • [ ] The page's sentence changes to the true one, and the README's "what is real versus simulated" section with it.
  • [ ] Tests: an honest recording verifies; the same recording with one number edited is refused; a recording longer than the cap is refused; a match with no recording is stored as unverified.

Files I expect to change

src/result.ts, src/verify.ts (new — the replay is its own concept), src/db.ts (the verified flag), src/server.ts, src/pages/game.ts, src/client/main.ts (it posts the recording), their tests, README.md.

Risks

  • CPU is the attack surface. Verification turns a POST into work; the cap, the limiter and the after-the-response placement are the whole defence and the pull must say how each is enforced.
  • The server now imports the game's simulation. That is allowed — src/independence.test.ts deliberately lets the app know about its own games — but it must not drag the renderer or the DOM in; if it does, that is a seam worth fixing first.
  • If the recording turns out to be large, verify a sample of rounds rather than all of them and say exactly what the sample proves.

Suggested: Torque — an exact claim, a budget and a boundary.

commented

Worth knowing while this sits here: #305 posts a room's result from the server, because in a room the server is the simulation — so the first rows on the board that nobody had to take a browser's word for arrive through that issue rather than this one.

That does not close this. A single-player match still runs entirely in a page and still posts its own result, and that is what most visitors will play. What #305 changes is the shape of the answer available to this issue: the replay this asks the server to run is the same update loop #304 makes the server step, so whoever picks this up after #304 has a stepped world on this side already and is verifying rather than building.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs