Demo environment — sample data only. This demo resets in 28 minutes.
logo
Provably Fair

Implementation of Provable Fairness at clou.bet

At clou.bet, we’ve adopted an innovative method for implementing provable fairness on our online gaming platform. Understanding that traditional approaches can be complex and potentially daunting for new players, we’ve refined our process to make it more accessible and transparent. 

Traditional Server Seed Generation

Typically, online casinos produce an unhashed server seed represented as a 64-character hexadecimal string. While this approach is secure, it results in a seed that appears as a complex series of characters, making it challenging for players to decipher and verify.

Example of a Traditional Server Seed

g9IiwibWFjIjoiNDljMGEwYzgwOGM0MjYxMjUzOWQxNzE3OGFjZTI4YmE2NmVmNDkwZjg4ZjY2MDZjNDk3MDA0MDc0MmU2NjVjMSIsInRhZyI6IiJ9

clou.bet's pioneering method for generating server seeds.

To improve both accessibility and clarity, we've implemented a 12-word mnemonic seed phrase system for our server seed. This method simplifies the server seed's appearance, making it more intuitive and familiar—especially for users accustomed to cryptocurrency wallet formats.

Example of clou.bet's Server Seed

fringe father crew abandon lift jar blast awesome knife labor tape common 

Generating the 12-Word Mnemonic Phrase

Our server seed generation process starts by constructing a 12-word mnemonic phrase. Initially, we encode entropy in increments of 32 bits, then derive a checksum from the SHA256 hash of this entropy. The combined entropy and checksum bits are subsequently used to form the mnemonic phrase.

Entropy and Checksum Calculation

CS = ENT / 32
MS = (ENT + CS) / 11

|  ENT  | CS | ENT+CS |  MS  |
+-------+----+--------+------+
|  128  |  4 |   132  |  12  |
|  160  |  5 |   165  |  15  |
|  192  |  6 |   198  |  18  |
|  224  |  7 |   231  |  21  |
|  256  |  8 |   264  |  24  |
                          

JS Fairness Model for Float and Integer Generation

const byteGenerator = async function* (

    cursor = 0,

    clientSeed: string | null = null,

    serverSeed: string | null = null,

    nonce: number | null = null

  ) {

    // Setup cursor variables

    let currentRound = Math.floor(cursor / 32);

    let currentRoundCursor = cursor;

    currentRoundCursor -= currentRound * 32;



    // Generate outputs until cursor requirement is fulfilled

    while (true) {

      // HMAC function used to output provided inputs into bytes

      const hmac = createHmac(

        'SHA-256',

        clientSeed + ':' + nonce + ':' + currentRound,

        serverSeed

      );

      const buffer = new Uint8Array(str2ab(hmac));

      // Update cursor for next iteration of loop

      while (currentRoundCursor < 32) {

        // The yield keyword is used in a Generator function, similar to JavaScript's function* syntax

        yield buffer[currentRoundCursor];

        currentRoundCursor++;

      }

      currentRoundCursor = 0;

      currentRound++;

    }

  };

  const createHmac = (algorithm: string, text: string, key: string | null) => {


    // eslint-disable-next-line new-cap

    const shaObj = new jsSHA(algorithm, 'TEXT');

    shaObj.setHMACKey(key, 'TEXT');

    shaObj.update(text);

    return shaObj.getHMAC('BYTES');

  };

const generateIntegers = async ({

    cursor = 0,

    count = 1,

    target = 100,

    clientSeed,

    serverSeed,

    nonce = 0,

    floats = [],

  }: IntegerProps) => {

    // Step 1: Generate a series of floats

    floats =

      floats.length > 0

        ? floats

        : await generateFloats({

          cursor,

          count,

          clientSeed,

          serverSeed,

          nonce,

        });

    // Step 2: Positions

    const positions = Array.from({ length: target }, (_, i) => i + 1);



    // Step 3: Get hits

    const hits = floats.map((float, index) => {

      return positions.splice(Math.floor(float * (target - index)), 1)[0];

    });

    return hits;

  };

const generateFloats = async ({

    cursor = 0,

    count = 1,

    range = [0, 1],

    clientSeed,

    serverSeed,

    nonce = 0,

    intOnly = false,

    checkScaledResult = true,

  }: Props) => {

    // Random number generator function

    const rng = byteGenerator(cursor, clientSeed, serverSeed, nonce);

    // Declare bytes as empty array

    const bytes = [];



    // Populate bytes array with sets of 4 from RNG output

    while (bytes.length < count * 4) {

      const item = await rng.next();

      bytes.push(item?.value);

    }

    // Return bytes as floats

    const floats: number[] = [];

    const byteChunks = chunkArray(bytes, 4);

    for (const bytesChunk of byteChunks) {

      let result = 0;

      for (let i = 0; i < bytesChunk.length; i++) {

        const divider = Math.pow(256, i + 1);

        const partialResult = bytesChunk[i] / divider;

        result += partialResult;

      }

      if (checkScaledResult) {

        // Scale the result to the desired range

        const scaledResult = result * (range[1] - range[0]) + range[0];

        let validResult = intOnly ? Math.floor(scaledResult) : scaledResult;

        while (floats.includes(validResult)) {

          validResult++;

          if (validResult > range[1]) {

            validResult = range[0];

          }

        }

        floats.push(validResult);

      } else {

        floats.push(result);

      }

    }



    return floats;

  };
                                

Generate Binary Seed from Mnemonic Function

Within our system, we convert both the client and server binary seeds into floats. This operation is essential for producing the binary seeds that confirm the fairness of our algorithms. By ensuring the mnemonic seed is accurately transformed into a binary format, we uphold the integrity and transparency of our processes.

Additionally, this function allows us to verify that the disclosed mnemonic seed aligns with the binary server seed, clearly demonstrating that our systems and algorithms operate fairly. Generating a binary seed from the mnemonic provides a reliable method to ensure the overall integrity and fairness of our procedures.

import CryptoJS from 'crypto-js';

export const generateBinarySeed = (mnemonic: string): string => {
  const salt = 'mnemonic';
  const iterationCount = 2048;
  const derivedKeyLength = 64;

  const seed = CryptoJS.PBKDF2(mnemonic, salt, {
    iterations: iterationCount,
    keySize: derivedKeyLength / 4, // Key size is specified in words, so divide by 4
    hasher: CryptoJS.algo.SHA512,
  });

  const binarySeed = CryptoJS.enc.Hex.parse(seed.toString());
  const hexStr = binarySeed.toString(CryptoJS.enc.Hex);
  const bin2hex = (s: string) => {
    let i;
    let l;
    let o = '';
    let n;
    s += '';
    for (i = 0, l = s.length; i < l; i++) {
      n = s.charCodeAt(i).toString(16);
      o += n.length < 2 ? '0' + n : n;
    }
    return o;
  };
  const binStr = bin2hex(hexStr);
  const shortBinStr = binStr.slice(0, 64);
  return shortBinStr;
};
                                    

Converting the Mnemonic Phrase to a Binary Seed

To derive the binary seed from the mnemonic phrase, we employ the PBKDF2 algorithm using HMAC-SHA512. In this process, the mnemonic phrase acts as the password, while the salt is the literal string 'mnemonic.'

Final Conversion to a 64-Character Hex String

We then refine the 128-character string, converting it into the conventional 64-character hexadecimal format typically used for generating results. 

Rotating Your Seed Pair

Whenever a player updates their client seed, our system concurrently rotates the server seed. This rotation of the seed pair guarantees that both seeds remain synchronized and are refreshed for every game, thereby reinforcing the integrity of our random number generation process.

Nonce

The nonce, which increases with every bet, operates in unison with the client and server seeds to produce a unique outcome for each game. This process guarantees both the fairness and the unpredictability of every wager.

Conclusion

This innovative method for generating server seeds not only simplifies the process of result verification but also makes it more accessible to users. By preserving high entropy and robust cryptographic security—similar to the techniques used in cryptocurrency wallets—it ensures the integrity of our games while reinforcing player confidence in the fairness and transparency of our gaming platform.