What is Provably Fair?

Rocketpot usesProvably Fairalgorithms. You can verify the outcome of the game with the source code provided below.

The Provably Fair system is a mathematical method that is used to ensure that no one, neither players nor the operator of the site, can know the result of the game before it starts and noone can tamper with the random numbers of the game.

How it works?

We have generated a chain of 8,000,000 SHA256 hashes. Each element is the hash of the lowercase, hexadecimal string representation of the previous hash.

The hash of the chain's last element is 5de24be2ba88f21070aca0b909a23ba8977a60e047e750dc6bd637aa3b4defc8

Every game maps to a hash in the chain: The 8,000,000th element of the chain is the hash of game #1 and the first element in the chain is the hash of game #8,000,000.

How to verify it?

To verify that a hash belongs to a game #n, simply hash it n times and compare the result with the terminating hash.

To calculate a game's result from its hash:

    const CryptoJS = require("crypto-js");

    function generateGameResultFromSeed(roundSeed, salt) {

      // Number of most significant bits to use
      const nBitsToUse = 52;

      // Step 1. HMAC_SHA256(message=seed, key=salt)
      const hmac = CryptoJS.HmacSHA256(roundSeed, salt);
      const roundHash = hmac.toString(CryptoJS.enc.Hex);

      // Step 2. r = 52 most significant bits
      const roundRandSource = roundHash.slice(0, nBitsToUse / 4);
      const r = parseInt(roundRandSource, 16);

      // Step 3. Calculate crash = r / 2^52, uniformly distributed in [0; 1)
      const twoPower52 = Math.pow(2, nBitsToUse);
      let crash = r / twoPower52;

      // Step 4. Calculate crash normalized in 100 - 100B range
      crash = Math.floor(97 / crash);
      crash = Math.min(crash, 100000000000);
      crash = Math.max(crash, 100);

      // Step 5. Take next 52 bits as seed for player selection in Jackpot round
      const jpPlayerRandSource = roundHash.slice(nBitsToUse / 4, 2 * nBitsToUse / 4);
      const jpPlayerRandom = parseInt(jpPlayerRandSource, 16);

      // Step 6. Take next 52 bits as seed for picking Jackpot value on the wheel
      const jpValueRandSource = roundHash.slice(2 * nBitsToUse / 4, 3 * nBitsToUse / 4);
      const jpValueRandom = parseInt(jpValueRandSource, 16);

      return {roundHash, jpPlayerRandom, jpValueRandom, crash, roundSeed};
    }
Before being used to calculate the corresponding result, each game hash is salted with the lowercase, hexadecimal string representation of the hash of a bitcoin block.

The number of the block to use was published on the Bitcointalk forum (URL https://bitcointalk.org/index.php?topic=5178349) before it was mined, proving that we didn't deliberately pick a chain that is unfavorable for players.

This block is #591433 with hash 00000000000000000014ccc7303b1be1fd8c6b2cb5e961d57fbdc57f661db418

It can be verified that we posted the number of the block to use before it was mined at the following snapshots of the bitcointalk seeding event post: https://archive.is/Mwq4s , http://loyce.club/archive/posts/5224/52246534.html and https://archive.is/r8zCU (thanks LoyceV for scraping and archiving the post!)

Jackpot player selection and wheel positions
The jackpot wheel results can be verified with the following code: https://jsfiddle.net/7uackeqo/

Preparation steps:

  • 1. Divide round hash by 3 parts (52 bits each).
  • 2. Generate integers from each part.
  • 3. The 1st part is for the multiplier (crash point).
  • 4. The 2nd part is for selecting the jackpot player.
  • 5. The 3rd part is for determining the jackpot wheel spin result.

Comment:The process of generating an integer from the hash is similar to any crash game, we just generate 3 integers instead of 1.


Player selection steps:

  • 1. Order the player list by user names (cast to lowercase, we don’t allow duplicate names that differ in case only).
  • 2. Add up all players’ bets as total bet value.
  • 3. Check if there is a player whose bet is bigger than 90% of total bet value.
          a. If there is no such player, continue to step #4.
          b. Otherwise - normalize bets by limiting this player’s chance to 90%.
  • 4. Assign each player to the range of values based on their bet amount, username (after ordering) and total bet (after normalization, if such occured).
  • 5. Use value from preparation step #4 as a seed for mersenne twister PRNG sequence.
  • 6. Take next unbiased value from the Mersenne Twister PRNG sequence that is lower than total bet (value from step #2).
  • 7. Pick a player that is in the range of selected value.

Comment:An intuitive way to explain the player selection process is that each player gets 1 ticket per each satoshi of the bet. And then one ticket is picked based on the value from the hash chain. Given the player list (that consists of player names and their bets) and round hash, anyone can verify the player selection result. Once all the bets are committed, there is no way for the operator to manipulate the player selection.


Wheel spin steps:

  • 1. Use value from preparation step #5 as a seed for mersenne twister PRNG sequence.
  • 2. Take next unbiased value from the Mersenne Twister PRNG sequence that is lower than the number of sectors on the jackpot wheel.
  • 3. If selected value is a respin sector, take next unbiased value from the Mersenne Twister PRNG sequence and repeat until non respin sector is picked.

Comment:Given the wheel config (sector indexes and their values) and round hash, anyone can verify the wheel spin result and there is no way to manipulate the spin result after seed event occured.

This text has been edited 24th of September 2019.