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.
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 specifiedhigh
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
andto
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 additionalseed
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. Theblock_dto
struct andget_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!