01 — Whisp docs

Randomness and fairness

Nobody can know the winning square while bets can still change. Not even the operator.

SEED COMMITTEDfixed before betting closesSLOT HASHfirst slot ≥ reveal_endSEALED BETS~60 sOPEN~6.5 sRESOLVE(seed)STARTCOMMIT_ENDREVEAL_END
One round. The result binds a seed fixed before betting closes and a slot hash from after every bet is open.

Two inputs

  • A seed. The crank keeps a hash chain of seeds: each one is the sha256 of the next. The board stores the last revealed seed as the commitment, and every round, refunded or not, uses the next seed down the chain. No two rounds share a seed, and only the right one hashes back to the commitment.
  • A slot hash. The hash of the first slot Solana produced at or after reveal_end, from the SlotHashes sysvar. It does not exist until every bet is open. The first resolve, lock or refund to read it pins it on the board.

The formula

value          = sha256(seed ‖ slot_hash ‖ round_id_le)
winning_square = u64_le(value[0..8]) % 25
jackpot_roll   = u64_le(sha256(value ‖ "jackpot")[0..8]) % jackpot_odds   (1000 at launch; 0 = hit if anyone is on the square)

All little-endian; the board labels squares 01 to 25, the index plus one. Every resolve emits the seed, the slot, its hash and the value. The program checks them itself, so normal rounds need no checking.

Why it's fair

  • The seed is fixed before betting closes. The admin can re-anchor the chain only while betting is open.
  • The slot hash comes after the reveal window, so nobody, the crank included, knows the result while bets can still be sealed or opened.
  • Rounds run on a fixed clock and a resolve must land before the next round is due, so the crank cannot pick the deciding slot by resolving early or late. Only a refund, which is counted, pushes the next round back, to the first start after the refund.
  • A block producer could try to grind the slot hash, but without the seed it cannot aim.
  • The crank can withhold its seed and force a refund (Liveness). That only pays if the operator, or someone it colludes with, has a stake in the round, so operator keys never play. And every refund can be checked.

Checking a skipped round

If the crank has not resolved a round with opened bets about 16 s after betting closes, any open Whisp page whose ghost wallet holds a little SOL sends lock_round, which pins the deciding slot hash on the board. After the deadline it refunds any unresolved round, empty or not, and the refund pins the hash too. The board keeps the last 8 refunds that had a stake (an empty one is only counted) with that slot and hash and which squares had stakes. A refund leaves the commitment where it is, so the next resolve must reveal a seed that hashes back through every refunded round's seed, and the program fills those seeds in. The crank can also reveal them one at a time, oldest first, with reveal_refund. A seed comes out only once its round is over, and the admin cannot re-anchor the chain while any refund's seed is not out yet.

Live, read from the chain:

[ check ] reads that one account and prints a line per refund:

Round R · would have paid square S   (· jackpot)
Round R · square S · nobody on it    no stake on S, so nothing to pay
Round R · seed not out yet           the next resolve reveals it
Round R · not recorded               nobody pinned the hash in time

The square uses the formula above. Any RPC will do, or run npm run check in the repo's crank folder. If refunds cluster on rounds the house would have lost, you have your answer.

What it can't prove

  • Duels use the same winning square, so they share these limits: a skipped round passes its duels to the next round, and every skip is counted and checkable as above.
  • A refund shows "not recorded" only if the crank skipped it and no funded Whisp page was open from about 16 s to about 2 minutes after betting closed, when Solana drops the slot hash.
  • A validator that produces the deciding slot and knows the seed could grind that slot's hash. Only the operator knows the seed, and the fixed clock gives a validator it runs only its stake share of rounds, plus any it moves by forcing a refund, which is counted.
  • A refund lands the next round on the first start after it, so a late refund pushes it further out, to a deciding slot the refunder picks. Open Whisp pages refund right after the deadline, so a late refund means no funded page was open, and it shows: a refund sent long after its round's deadline.
  • All of this assumes the deployed program is the code in the repo: check the verified build and the upgrade authority (Contract addresses).

Explain this page withClaudeChatGPTGrok