Randomness
Randomness is used for generating unique identifiers, ensuring fairness in games, cryptographic protocols, and much more. On ICP, all computations, including randomness, must be verifiable and reproducible across the network's nodes.
The network provides a verifiable random function (VRF) through the management canister that produces random values that are unpredictable yet verifiable, ensuring fairness and security while maintaining network consensus. It guarantees cryptographic security, making it suitable for use cases such as cryptographic key generation.
The VRF generates 256-bit random Blob
s in each execution round. A canister can request one of these random Blob
s via the management canister's raw_rand
method.
Motoko provides multiple options for incorporating randomness into your code, each suited for different scenarios. The right method for your application depends on its security, performance, and reproducibility requirements.
Method | Functionality | Security level | Example use cases | Key features |
---|---|---|---|---|
raw_rand function | Returns 32 bytes of cryptographic randomness from ICP’s VRF. | Strong cryptographic guarantees, ensures unpredictability. | Secure key generation, fairness-compliant applications, unpredictable randomness. | Directly retrieves randomness from the network’s consensus layer, 32-byte (256-bit) Blob s, asynchronous, returns fresh entropy each call. |
Random module | High-level wrapper for raw_rand , providing finite random pools. | Uses raw_rand , but requires careful handling to avoid entropy reuse. | Random number generation, shuffling, simulations. | Simplifies number generation, includes finite entropy pools, requires fresh raw_rand calls when exhausted. |
fuzz package | Pseudo-random generator that can be seeded with time, Blob s, or custom functions. | Security depends on the seed. | Fuzz testing, procedural generation, simulations, dynamic randomness. | Default seed is Time.now (low security), can be initialized with raw_rand for high security, supports custom generators. |
idempotency-keys package | Generates UUID v4 from a 16-byte random seed. | Security depends on the provided entropy. | Unique transaction IDs, idempotency, database keys. | Produces RFC4122-compliant UUIDs, requires secure entropy source, simple API UUID.generateV4(seed) . |
Before using the fuzz
or idempotency-keys
packages, ensure that Mops is installed and initialized in your Motoko project.
raw_rand
The raw_rand
function is a system API provided by the ICP management canister for requesting cryptographic randomness derived from the network’s verifiable random function. raw_rand
generates fresh entropy in every execution round, making it suitable for applications requiring high security such as key generation or applications where fairness is a legal requirement.
Since raw_rand
operates asynchronously, canisters must await its response before using the generated bytes. Each call returns a 32-byte (256-bit) random Blob
.
persistent actor {
let SubnetManager : actor {
raw_rand() : async Blob;
} = actor "aaaaa-aa";
public func randomBytes() : async Blob {
await SubnetManager.raw_rand();
};
}
Random
The Random
module provides an interface that wraps the raw_rand
function. Since raw_rand
returns raw bytes, the Random
module simplifies working with this entropy by offering structured methods for consuming randomness efficiently. The module includes Random.blob()
for fetching fresh 32-byte entropy and Random.Finite
, which provides a finite source of randomness that can be used until exhausted. When entropy runs out, a new random Blob
must be fetched asynchronously.
Below is an example demonstrating how to generate a random boolean using Random.Finite
.
import Random "mo:base/Random";
persistent actor {
public func randomBoolean() : async ?Bool {
let entropy = await Random.blob();
let finite = Random.Finite(entropy);
// Consumes 1 byte of entropy
finite.coin();
};
}
fuzz
The fuzz
package is a versatile random data generator, primarily designed for testing but also useful for creating random account IDs, unique values, and randomized inputs. It supports a wide range of data types, including numbers, text, arrays, Blob
s, and Principal
s.
The randomness source is customizable. You can initialize it with a time-based seed, a fixed seed for reproducibility, a random Blob
for stronger entropy, or a custom generator function. By default, fuzz
uses Time.now()
as its seed, enabling immediate access to pseudo-random values without external dependencies.
The following example demonstrates initialization with the default seed and generating a random Nat
.
import Fuzz "mo:fuzz";
let fuzz = Fuzz.Fuzz();
public shared query func randomNat() : async Nat {
fuzz.nat.random();
};
idempotency-keys
The idempotency-keys
package offers a method for generating universally unique identifiers (UUID) version 4 (v4), ideal for transaction tracking and request deduplication. It takes a 16-byte random seed and formats it according to the UUID v4 specification.
The uniqueness and security of the generated UUIDs depend on the entropy source used for the seed. For cryptographic-grade randomness, using raw_rand
is recommended, as it makes the package especially useful for idempotent API requests, database keys, and other scenarios that require globally unique identifiers.
The following example demonstrates generating a UUID v4 using idempotency-keys
.
import UUID "mo:idempotency-keys/UUID";
import Random "mo:base/Random";
persistent actor {
public func generateUUID() : async Text {
let seed = await Random.blob();
UUID.v4(seed);
};
}