This document outlines the security measures preventing cheating in the Snake game leaderboard.
The original contract allowed direct calls to submitScore(), enabling players to:
- Submit fake scores without playing.
- Use bots.
- Manipulate scores via direct contract calls.
We implemented a cryptographic signature system where scores must be signed by the game server before submission.
The game server holds a private key. When a game finishes, the server signs the score along with the player's address and timestamp. The smart contract verifies this signature before accepting the score.
- Unique Signatures: Each signature is valid only once.
- Expirations: Signatures expire after 5 minutes.
The API validates game data (duration, moves, score consistency) before signing to ensure the score was legitimately achieved.
- Player completes a game.
- Client sends move data to the API.
- API validates the data.
- If valid, API returns a signed score.
- Client submits the signed score to the smart contract.
contracts/Leaderboard.sol: Signature verification logic.app/api/sign-score/route.ts: Score signing endpoint.lib/secure-score.ts: Signing utilities.
-
Generate a private key:
node -e "console.log('0x' + require('crypto').randomBytes(32).toString('hex'))" -
Set
GAME_SERVER_PRIVATE_KEYin.env.local. -
Deploy the contract:
npx hardhat run scripts/deploy-secure-leaderboard.ts --network monad-testnet
- Server: Keep the private key secure. Implement rate limiting and logging.
- Client: Handle signature failures gracefully.
Attempting to submit fake scores, replay signatures, or bypass the server should fail with the current implementation.
- Server Dependence: Score submission requires the game server to be online.