Chromia
Start Building

01 / 01Guide

Generating random numbers in Rell

Games, lotteries, and cryptographic protocols built on a blockchain need random numbers that are hard to predict yet reproducible. On Chromia, you can produce them with custom Rell functions. These functions draw on blockchain context values such as block height, timestamp, and block IDs, so their output is difficult to predict but always identical for identical inputs.

The full code for this guide is available in the random number generation repository.

The functions below rely on op_context, which you can only access inside operations. See the op_context reference for details.

You can generate pseudo-random numbers in Rell with these functions:

  • get_random_number(high): Returns a number from 0 up to high, with both bounds inclusive. Block time, block height, and the operation index serve as sources of randomness.

    rell
    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): Returns a number between from and to, with both bounds inclusive.

    rell
    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): Takes an extra seed argument. When you need several random numbers in one operation, pass a different seed for each call, for example the loop iterator index. The function mixes a random hexadecimal number with block time, block height, and row ID. It reads the latest block data through the block_dto struct and the get_last_block_data function.

    rell
    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 @* { .timestamp > 0 } (      block_dto (          timestamp = $.timestamp,          block_rid = $.block_rid,          block_height = $.block_height,          rowid = $.rowid.to_integer()      ),      @omit @sort_desc $.timestamp  ) limit 1;  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      );      else -> (          rowid = 88,          block_height = 32253,          block_timestamp = 55554444333,          block_rid = chain_context.blockchain_rid      );  };}

These functions give your blockchain application pseudo-random numbers. Remember that pseudo-randomness has limits, and switch to a more secure source when you need true randomness.

Written for this site from Chromia’s public course. Code samples are the course’s own.