Discover Cloudbet.com
certified checkmark stamp next to cloudbet logo

How Cloudbet’s Provably Fair Mines Game Works: A Technical Breakdown

Provably fair gambling solves a problem that’s been inherent to online casinos since their inception: how can a player trust that a game outcome wasn’t manipulated after the fact? The answer, it turns out, is the same tool cryptographers reach for whenever two parties need to agree on something without trusting each other — a commit-reveal scheme.

What follows is a full walkthrough of the pipeline Cloudbet uses for its Mines game — from seed generation to mine placement — and how any player with a little coding knowledge can independently verify every result they’ve ever played.

This article is the first in a planned series of technical breakdowns on the provably fair mechanisms behind Cloudbet Originals. For hands-on verification of any round (including Mines), check out Cloudbet’s Provably Fair Calculator.

Committing to the outcome before play begins

Before each round, the server generates a random 32-byte server seed and immediately publishes its SHA3-256 hash — the commitment. This hash is a one-way fingerprint: it proves the seed existed at that moment without revealing what it is.

Once the game ends, Cloudbet discloses the raw server seed. Anyone can hash it and confirm it matches the pre-game commitment, which proves the seed — and therefore the mine layout — was fixed before the first tile was picked. There’s no window in which the casino could swap it for a more favourable value.

Mixing in player entropy

The server seed alone would leave players entirely dependent on the casino’s randomness. To prevent that, two additional inputs are folded in: a client seed (set by the player) and a nonce (an integer that increments with each bet).

All three are concatenated and hashed with SHA3-256 to produce the round signature:

roundSignature = SHA3-256(`${serverSeed}:${clientSeed}:${nonce}`)

The client seed ensures the casino can’t predict the outcome at commitment time, since the player’s input is unknown when the server seed is generated. The nonce ensures each round produces a distinct signature even when both seeds remain unchanged between bets.

Generating randomness with SHAKE256

The round signature is fed into SHAKE256, an extendable-output function (XOF) from the SHA-3 family. Unlike a standard hash, a XOF can produce an arbitrarily long pseudo-random byte stream from a fixed input — useful here because placing multiple mines requires multiple independent random values.

The implementation pulls 4 bytes at a time from the stream. When the initial output is exhausted, it appends "next" to the state and squeezes again, though in practice a 25-tile grid with any realistic mine count stays well within the first 256 bits.

Placing mines without bias: rejection sampling

Converting a random 32-bit integer into a grid position using a simple modulo operation introduces a subtle but real problem: unless 2^32 is evenly divisible by the number of remaining positions P, some positions will be marginally more likely than others.

Cloudbet eliminates this with rejection sampling:

javascript
const maxAcceptable = Math.floor(0x100000000 / P) * P;
let rand;
do { rand = rng.next().value; } while (rand >= maxAcceptable);
const index = rand % P;

Any value outside the largest multiple of P that fits in 32 bits is discarded, and the next value from the SHAKE256 stream is drawn instead. What remains is a uniform distribution over exactly P positions.

After each mine is placed, that position is removed from the pool and P decrements by 1 — a partial Fisher-Yates shuffle over the flat 25-element grid array.

Verifying a round

Post-game verification requires four values, all published by Cloudbet after the round:

  • serverSeed
  • clientSeed
  • nonce
  • The declared minePositions

The verification steps are:

  1. Hash serverSeed with SHA3-256 and confirm it matches the pre-game commitment.
  2. Derive roundSignature = SHA3-256(serverSeed:clientSeed:nonce).
  3. Initialise the SHAKE256 stream with roundSignature and run the rejection-sampling loop to independently reproduce minePositions.

A match confirms the grid was not altered after the round began.

Sample implementation (Node.js)

bash
npm install js-sha3
javascript
const crypto = require('crypto');
const { sha3_256, shake256 } = require('js-sha3');

function generateServerSeed() {
  const seed = crypto.randomBytes(32).toString('hex');
  return { serverSeed: seed, commitment: sha3_256(seed) };
}

function createRoundSignature(serverSeed, clientSeed, nonce) {
  return sha3_256(`${serverSeed}:${clientSeed}:${nonce}`);
}

function* createShake256Stream(signature) {
  const byteStream = shake256.create(32);
  byteStream.update(signature);
  while (true) {
    const buf = Buffer.from(byteStream.digest({ buffer: true }));
    for (let i = 0; i < buf.length; i += 4) {
      if (i + 4 <= buf.length) yield buf.readUInt32BE(i);
    }
    byteStream.update('next');
  }
}

function pickUniquePositions(count, totalTiles, rng) {
  const available = Array.from({ length: totalTiles }, (_, i) => i);
  const result = [];
  for (let i = 0; i < count; i++) {
    const P = available.length;
    const maxAcceptable = Math.floor(0x100000000 / P) * P;
    let rand;
    do { rand = rng.next().value; } while (rand >= maxAcceptable);
    const index = rand % P;
    result.push(available[index]);
    available.splice(index, 1);
  }
  return result;
}

const { serverSeed, commitment } = generateServerSeed();
const clientSeed = 'player-provided-seed';
const nonce = 0;
const sig = createRoundSignature(serverSeed, clientSeed, nonce);
const rng = createShake256Stream(sig);
const mines = pickUniquePositions(5, 25, rng);

console.log('Commitment:', commitment);
console.log('Server seed (post-game):', serverSeed);
console.log('Mine positions:', mines);

A few things worth noting

Why SHA3 over SHA2? For a simple commitment, either would work. SHA3’s sponge construction does eliminate length-extension attacks, and using it throughout the pipeline — both for the commitment and as the basis for SHAKE256 — keeps the cryptographic surface consistent.

Why SHAKE256 over a seeded PRNG? Determinism and auditability. SHAKE256 is a standardised primitive with a well-defined spec, which means independent implementations in any language will produce identical output from the same input. A custom PRNG introduces implementation-defined behaviour that makes third-party verification harder.

Can a player game the system by choosing a favourable client seed? In principle, yes — a player could trial different client seeds to find one that produces a favourable grid. This is considered acceptable within the design: the player’s right to choose their seed is a feature, not a vulnerability. The casino’s exposure is symmetric, and critically, the same mechanism that lets a player shop for a good seed also lets them prove the casino never manipulated theirs.

The Mines game is a clean entry point into provably fair design precisely because its RNG pipeline is relatively self-contained: a single commit-reveal cycle, a straightforward byte stream, and a well-understood sampling technique. As the series progresses, we’ll see how other Cloudbet Originals adapt and extend these same foundations — sometimes with additional entropy sources, different output mappings, or multi-stage verification — while preserving the same core guarantee: that no outcome can be decided after the player has committed to their action.

Share this post


You may also like

See our casino and sportsbook in action
Cloudbet Academy
Get smarter
Get rewarded

Complete free Academy lessons and earn exclusive promo codes.