The cryptography, in depth

Battlerocketship, under the hood

The tour keeps it simple. Here is the real machinery, written to be readable by anyone, with deeper asides for those who want them. The same ideas power every game in the arcade.

Hashing

beginner

A hash function takes any data, a word, a file, a whole game board, and returns a short fixed-size fingerprint. These games use keccak256, which always returns 32 bytes (64 hex characters).

h = keccak256(data)
Same input always gives the same 32-byte output.

Three properties matter:

  • Deterministic: the same input always gives the same hash.
  • One-way: you cannot run it backward to recover the input.
  • Avalanche: change one bit and the whole output changes.
keccak256(input) =
0x3c61050a35421655441c721d163a8e8568cc126d41746bc5371d9c3e36f4ba4d
change one character β†’ rocket at cell 13
0xb92f0de5e6204b300beef827386f056eecc45dd2bd3e57dcb1976d90dc8d9504

Same input, same output, every time (deterministic). Change a single character and the whole result scrambles (the avalanche effect), and there is no way to run it backward. That is what makes a hash a tamper-evident fingerprint.

deeperIt is also collision-resistant: finding two different inputs with the same hash would take roughly 2128 work, which is infeasible. That is precisely what lets a hash stand in for the data as an unforgeable commitment.
in this gameBattlerocketship hashes every one of the 100 cells on your board, ship or water, into a leaf:leaf = keccak256( keccak256( index β€– isShip β€– salt ) )

Merkle trees

beginner

Hashing one thing gives one fingerprint. A Merkle tree fingerprints a whole collection into a single root, while still letting you prove any one item cheaply. You hash each item into a leaf, pair the leaves and hash each pair, then repeat until a single root remains.

parent = keccak256( min(a, b) β€– max(a, b) )
Pairs are sorted before hashing, so a verifier doesn't need to know left from right.

Click any cell to see its proof rebuild the root:

root
0x9224a
H(A,B)
0xa26ef
H(C,D)
0x9a8b0

To prove cell A is in the committed tree, you only reveal it plus two sibling hashes (not the whole board):

H(0x9d753, 0xcd334) = 0xa26ef
H(0xa26ef, 0x9a8b0) = 0x9224a
βœ“ equals the committed root 0x9224a

A tree of n cells needs only logβ‚‚(n) sibling hashes per proof (100 cells β†’ 7).

The magic is the proof: to convince someone a single cell is part of the committed root, you reveal only that cell plus a handful of sibling hashes, never the whole board.

deeperA tree of n leaves needs only ⌈logβ‚‚(n)βŒ‰ hashes per proof, so a 100-cell board proves any cell with 7 hashes. Verification just re-hashes up the path and checks it equals the published root.
in this gameYour 100 cell-hashes combine into one fleet root, the single value you commit on-chain. When your opponent fires at a cell, you answer with that cell plus its 7-hash proof, and they verify it against your committed root.

Commit and reveal

beginner

A commitment lets you lock in a secret now and reveal it later, with two guarantees:

  • Binding: you can't change what you committed. Any change to the data changes the root.
  • Hiding: the commitment (just a hash) reveals nothing about the data.

You publish the root up front. Later, when challenged, you reveal a specific piece plus its Merkle proof. The other side checks the proof against the root you already locked, so you can neither lie about it nor have changed it.

deeperEach cell is salted before hashing. Without a random salt, a ship-or-water cell is one of only two values, which an opponent could simply hash both ways and match. The salt makes each leaf unguessable.
in this gameYou publish only the fleet root before the game. That root is binding (you can't move a rocket afterward) and hiding (it reveals nothing about where your ships are). That is the whole reason no referee is needed.

Signatures

deeper

A digital signature proves a specific message came from a specific wallet, and nobody can forge it without that wallet's private key. The contract recovers the signer from the signature and checks it matches.

verify( message, signature ) β†’ signer address
EIP-712 signs a typed, human-readable message, e.g. Result(gameId, winner).

This is how a game can be settled with a single cheap transaction: the loser signs a short β€œI concede” message and the winner submits it. No private key ever leaves your wallet.

in this gameThe cheap way to finish a game: the loser signs a one-line Result(gameId, winner) concession and the winner submits it. A winner can also self-settle by submitting the 17 ship-cell proofs they collected during play, with no cooperation needed.

Soulbound tokens

beginner

Most NFTs can be bought and sold. A soulbound token (the ERC-5192 standard) is one that is permanently non-transferable: once minted to your address, it stays there forever.

That is exactly what you want for a trophy. With no resale market it carries no monetary value, so it is a pure proof of achievement, an on-chain record that you earned it, rather than something bought.

in this gameWin a burn-for-badge match and the contract burns both entries (the MOON leaves circulation, won by nobody) and mints you a soulbound trophy. Nobody wins money, the trophy is the prize.

The honest part

The contract is open-source and covered by tests. We're upfront about the trust model:

  • You can never lose your entry to a cheater. If someone stalls or commits a bad board, the game times out and both entries refund.
  • A win is proven by the 17 collected ship-cell proofs. Full on-chain enforcement that a board is a legal fleet, and fraud proofs for mid-game stalling, are planned v2 work.
  • The relay that carries moves is untrusted: it only forwards messages, which are verified against on-chain commitments and signatures, so it can't cheat.

Curious enough to read the source? The Solidity contract and tests live in the open-source repo. Same primitives, no hidden parts.