🎲Provably Fair

Our platform uses a provably fair system to generate random numbers for bets, ensuring transparency and fairness. We leverage the EOS blockchain, a public, immutable ledger, to source unpredictable block hashes that cannot be manipulated by our platform or users. This allows anyone to independently verify the randomness of the generated numbers.

How it works

  1. Fetching the current block

    1. When a bet is placed we query the EOS blockchain to retrieve the current block number using the get_info API endpoint from eos.greymass.com

    2. We add 3 blocks to the current block number to determine a target block, this delay ensures that the block hash is not known at the time the bet is placed, making it impossible to predict or manipulate the outcome.

  2. Waiting for the target block

    1. We wait for the target block to be created on the EOS blockchain (a few seconds)

    2. Once the target block is available we retrieve its block hash using the get_block api endpoint

  3. Generate the random number

    1. The block hash (64 char hexadecimal string) is used as the source of randomness

    2. We take the first 8 characters of the hash and convert them to a 32 bit integer

    3. The integer is scaled to fit within the desired range (2 for coinflip, 6 for diceroll, 1000 for Guess the number)

    4. If the generated number exceeds the range then we reshash the value using sha256 and repeat the process until a valid number is produced.

  4. Displaying results

    1. Approx. 5 seconds after the bet is played we display the following verifiable data:

      1. Initial Block Number: The block number at the time the bet was placed

      2. Target Block Number: The block number used to generate the random number (initialBlock + 3)

      3. Block hash: The hash of the target block

      4. Random number: The final number generated scaled to the range 0 to max

  1. Verifying the Randomness

    1. Users can independently verify the random number by:

      1. Visiting the public EOS blockchain explorer (Bloks.io) and entering the target block number to retrieve the corresponding block hash

      2. Using the provided python code (below) to replicate the random number generation process

      3. Comparing the generated random number to the one displayed by the platform

    2. A verify randomness button in the UI

Python Code for Verification

Below is Python code that demonstrates how the random number is generated from the block hash

import requests
import hashlib
import time

async def get_current_block():
    try:
        response = requests.get("https://eos.greymass.com/v1/chain/get_info")
        response.raise_for_status()  # Raise an error for bad status codes
        data = response.json()
        return data.get("head_block_num")
    except Exception as e:
        print(f"Error fetching current block: {e}")
        return None

async def get_block_hash(block_num):
    try:
        response = requests.post(
            "https://eos.greymass.com/v1/chain/get_block",
            headers={"Content-Type": "application/json"},
            json={"block_num_or_id": block_num}
        )
        response.raise_for_status()
        data = response.json()
        return data.get("id")
    except Exception as e:
        print(f"Error fetching block hash: {e}")
        return None

def hash_to_number_with_max(hash_str, max_value):
    # Take first 8 characters of the hash and convert to integer
    seed = hash_str[:8]
    seed_int = int(seed, 16)
    
    # Calculate scaling factor to ensure fair distribution
    max_int = 2**32 - 1
    scale_factor = max_int // max_value
    
    random_number = seed_int
    while random_number >= scale_factor * max_value:
        # Rehash if number is out of range
        hash_obj = hashlib.sha256(str(random_number).encode()).hexdigest()
        random_number = int(hash_obj[:8], 16)
    
    return random_number // scale_factor

async def get_provable_random_number(max_value):
    # Get current block number
    current_block = await get_current_block()
    if not current_block:
        raise Exception("Unable to fetch current block")
    
    # Calculate target block (current + 3)
    target_block = current_block + 3
    block_hash = None
    
    # Wait for target block to be created
    while not block_hash:
        block_hash = await get_block_hash(target_block)
        if not block_hash:
            time.sleep(1)  # Wait 1 second before retrying
    
    # Generate random number from block hash
    random_number = hash_to_number_with_max(block_hash, max_value)
    
    return {
        "randomNumber": random_number,
        "initialBlock": current_block,
        "blockNum": target_block,
        "blockHash": block_hash
    }

# Example usage
import asyncio
max_value = 100  # Example max value for the random number
result = asyncio.run(get_provable_random_number(max_value))
print(result)

Notes

Dependencies: Install the requests library using pip install requests

Verification: use the blockNum and blockHash from the result to check the block hash on bloks.io and run the hash_to_number_with_max function with the same max_value to confirm the randomNumber

Edge cases: If the api is unavailable or the target block is not yet created, the code retries every second until successful

Last updated