Onchain randomness
Overview
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.
- Motoko
- Rust
- TypeScript
- Python
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 { call, IDL, update } from 'azle';
export default class {
@update([], IDL.Vec(IDL.Nat8))
async randomBytes(): Promise<Uint8Array> {
const bytes: Uint8Array = await call('aaaaa-aa', 'raw_rand', {
returnIdlType: IDL.Vec(IDL.Nat8)
});
return bytes;
}
}
from kybra import (
Async,
blob,
CallResult,
match,
update,
Variant,
)
from kybra.canisters.management import (
management_canister,
)
class RawRandResult(Variant, total=False):
Ok: blob
Err: str
@update
def get_raw_rand() -> Async[RawRandResult]:
raw_rand_call_result: CallResult[blob] = yield management_canister.raw_rand()
return match(
raw_rand_call_result,
{
"Ok": lambda randomness: {"Ok": randomness},
"Err": lambda err: {"Err": err},
},
)
Kybra canisters must be deployed from a Python virtual environment. Learn more in the Kybra docs.