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.
Please note that the random numbers generated by these functions are pseudo-random. While they rely on variables like block time and block height, which add some unpredictability, they are not truly random and could be vulnerable to predictability or manipulation in specific contexts. If your application requires true randomness for security-critical purposes, consider using an external oracle to provide random data.
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 pseudo-random number generation in Rell, you can use the following 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 help generate pseudo-random numbers for your blockchain applications. However, keep in mind the limitations of pseudo-randomness and consider using more secure methods if true randomness is required. Happy coding with Rell!