Skip to main content

How to generate random numbers using Rell

In blockchain applications like games, lotteries, and cryptographic protocols, it is essential to generate random numbers that are both unpredictable and reproducible. In Chromia, you can achieve this using custom Rell functions. These functions utilize various blockchain context elements, such as block height, timestamp, and block IDs, ensuring the generation of unpredictable random numbers while maintaining reproducibility with identical inputs.

caution

We will utilize op_context in the following functions. However, note that op_context is only available within operations. For more details, refer to the documentation.

To achieve this, we can use the following Rell functions:

  • get_random_number(high): Generates a random number between 0 (inclusive) and the specified high value (inclusive). It leverages elements such as block time, block height, and operation index for randomness.

    function get_random_number(high: integer = 100): integer {
    if (high == 0) return 0; // avoid division by zero
    return (op_context.last_block_time - op_context.block_height - op_context.op_index) % high + 1;
    }
  • get_random_number_in_range(from, to): Generates a random number within a specified range (from and to inclusive).

    function get_random_number_in_range(from: integer, to: integer): integer {
    require(from <= to, "To must be higher than from");
    return from + get_random_number(to - from);
    }
  • get_random_number_with_seed(high, seed): Generates a random number with an additional seed parameter. If multiple random numbers are needed within the same operation, provide a different seed each time. For instance, use the iterator index as the seed in a loop. This function combines a random hexadecimal number with block time, block height, and row ID for randomness. The block_dto struct and get_last_block_data function are used to retrieve the latest block data.

    function get_random_number_with_seed(high: integer, seed: integer = 0) {
    if (high == 0) return 0;
    val key = seed % 32;
    val last_block_data = get_last_block_data();
    val random_value = integer.from_hex(last_block_data.block_rid.to_hex()[key]) + (
    last_block_data.block_timestamp - last_block_data.block_height - last_block_data.rowid
    );
    return random_value % high + 1;
    }

    struct block_dto {
    timestamp;
    block_rid: byte_array;
    block_height: integer;
    rowid: integer;
    }

    function get_last_block_data(): (
    rowid: integer,
    block_height: integer,
    block_timestamp: integer,
    block_rid: byte_array
    ) {
    // Ensure the chain has at least 2 blocks written, as the last block may not have block_rid yet
    val blocks = block @* { } (
    block_dto(
    timestamp = $.timestamp,
    block_rid = $.block_rid,
    block_height = $.block_height,
    rowid = $.rowid.to_integer()
    )
    ) limit 2;

    return when (blocks.size()) {
    1 -> (
    rowid = blocks[0].rowid,
    block_height = blocks[0].block_height,
    block_timestamp = blocks[0].timestamp,
    block_rid = blocks[0].block_rid
    );
    2 -> (
    rowid = blocks[1].rowid,
    block_height = blocks[1].block_height,
    block_timestamp = blocks[1].timestamp,
    block_rid = blocks[1].block_rid
    );
    else -> (
    rowid = 88,
    block_height = 32253,
    block_timestamp = 55554444333,
    block_rid = chain_context.blockchain_rid
    );
    };
    }

Using these functions will make your blockchain apps stronger and fairer, ensuring secure and efficient random number generation. Happy coding with Rell!