Skip to main content

On-chain randomness

The Internet Computer provides a secure and verifiable way to generate random numbers directly within canisters. This functionality is exposed through the raw_rand method offered by the management canister and the Motoko Random module.

The method takes no input and returns 32 pseudo-random bytes to the caller.

How it works

Behind the scenes, ICP utilizes a Verifiable Random Function (VRF) to generate randomness.

During each round, the VRF is evaluated with the current input being the number of the round. This process produces a fresh set of random bytes.

These bytes become the seed for a pseudorandom number generator (PRNG) called random tape. The random tape is built using chain-key cryptography and is used to create unique random values for each canister that requested randomness in the previous round.

raw_rand calls only use the random tape from the next round. This ensures that no one can exploit knowledge of the current round's randomness to predict future outputs. To learn more about the execution layer of ICP, refer to the execution layer documentation.

To learn more about the technical details of randomness on ICP, watch the Community Conversations video on randomness.

How to use

Developers can directly access randomness through the raw_rand method in the management canister. See the examples below.

actor {
let SubnetManager : actor {
raw_rand() : async Blob;
} = actor "aaaaa-aa";

public func random_bytes() : async Blob {
let bytes = await SubnetManager.raw_rand();
return bytes;
};
};

In addition to the raw_rand method, Motoko offers a Random module for generating random numbers.

let (randomBytes,): (Vec<u8>,) = ic_cdk::api::call(Principal.management_canister(), "raw_rand", ()).await?;
import { blob, Canister, ic, update } from 'azle';
import { managementCanister } from 'azle/canisters/management';

export default Canister({
randomBytes: update([], blob, async () => {
return await ic.call(managementCanister.raw_rand);
})
});
from kybra import Async, blob, CallResult, match, update, Variant
from kybra.canisters.management import management_canister


class RandomBytesResult(Variant, total=False):
Ok: blob
Err: str


@update
def random_bytes() -> Async[RandomBytesResult]:
call_result: CallResult[blob] = yield management_canister.raw_rand()

return match(
call_result, {"Ok": lambda ok: {"Ok": ok}, "Err": lambda err: {"Err": err}}
)