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 tohigh, 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 betweenfromandto, 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 extraseedargument. 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 theblock_dtostruct and theget_last_block_datafunction.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.