Skip to main content

Module 0x2::random

This module provides functionality for generating secure randomness.

use 0x1::bcs;
use 0x1::vector;
use 0x2::address;
use 0x2::hmac;
use 0x2::object;
use 0x2::transfer;
use 0x2::tx_context;
use 0x2::versioned;

Resource Random

Singleton shared object which stores the global randomness state. The actual state is stored in a versioned inner field.

struct Random has key
Fields

Struct RandomInner

struct RandomInner has store
Fields
version: u64
epoch: u64
randomness_round: u64
random_bytes: vector<u8>

Struct RandomGenerator

Unique randomness generator, derived from the global randomness.

struct RandomGenerator has drop
Fields
seed: vector<u8>
counter: u16
buffer: vector<u8>

Constants

const ENotSystemAddress: u64 = 0;

const EWrongInnerVersion: u64 = 1;

const CURRENT_VERSION: u64 = 1;

const EInvalidLength: u64 = 4;

const EInvalidRandomnessUpdate: u64 = 2;

const EInvalidRange: u64 = 3;

const RAND_OUTPUT_LEN: u16 = 32;

const U16_MAX: u64 = 65535;

Function create

Create and share the Random object. This function is called exactly once, when the Random object is first created. Can only be called by genesis or change_epoch transactions.

fun create(ctx: &mut tx_context::TxContext)
Implementation
fun create(ctx: &mut TxContext) {
    assert!(ctx.sender() == @0x0, ENotSystemAddress);

    let version = CURRENT_VERSION;

    let inner = RandomInner {
        version,
        epoch: ctx.epoch(),
        randomness_round: 0,
        random_bytes: vector[],
    };

    let self = Random {
        id: object::randomness_state(),
        inner: versioned::create(version, inner, ctx),
    };
    transfer::share_object(self);
}

Function load_inner_mut

fun load_inner_mut(self: &mut random::Random): &mut random::RandomInner
Implementation
fun load_inner_mut(self: &mut Random): &mut RandomInner {
    let version = versioned::version(&self.inner);

    // Replace this with a lazy update function when we add a new version of the inner object.
    assert!(version == CURRENT_VERSION, EWrongInnerVersion);
    let inner: &mut RandomInner = versioned::load_value_mut(&mut self.inner);
    assert!(inner.version == version, EWrongInnerVersion);
    inner
}

Function load_inner

fun load_inner(self: &random::Random): &random::RandomInner
Implementation
fun load_inner(self: &Random): &RandomInner {
    let version = versioned::version(&self.inner);

    // Replace this with a lazy update function when we add a new version of the inner object.
    assert!(version == CURRENT_VERSION, EWrongInnerVersion);
    let inner: &RandomInner = versioned::load_value(&self.inner);
    assert!(inner.version == version, EWrongInnerVersion);
    inner
}

Function update_randomness_state

Record new randomness. Called when executing the RandomnessStateUpdate system transaction.

fun update_randomness_state(self: &mut random::Random, new_round: u64, new_bytes: vector<u8>, ctx: &tx_context::TxContext)
Implementation
fun update_randomness_state(
    self: &mut Random,
    new_round: u64,
    new_bytes: vector<u8>,
    ctx: &TxContext,
) {
    // Validator will make a special system call with sender set as 0x0.
    assert!(ctx.sender() == @0x0, ENotSystemAddress);

    // Randomness should only be incremented.
    let epoch = ctx.epoch();
    let inner = self.load_inner_mut();
    if (inner.randomness_round == 0 && inner.epoch == 0 && inner.random_bytes.is_empty()) {
        // First update should be for round zero.
        assert!(new_round == 0, EInvalidRandomnessUpdate);
    } else {
        // Subsequent updates should either increase epoch or increment randomness_round.
        // Note that epoch may increase by more than 1 if an epoch is completed without
        // randomness ever being generated in that epoch.
        assert!(
            (epoch > inner.epoch && new_round == 0) ||
                    (new_round == inner.randomness_round + 1),
            EInvalidRandomnessUpdate,
        );
    };

    inner.epoch = ctx.epoch();
    inner.randomness_round = new_round;
    inner.random_bytes = new_bytes;
}

Function new_generator

Create a generator. Can be used to derive up to MAX_U16 * 32 random bytes.

public fun new_generator(r: &random::Random, ctx: &mut tx_context::TxContext): random::RandomGenerator
Implementation
public fun new_generator(r: &Random, ctx: &mut TxContext): RandomGenerator {
    let inner = load_inner(r);
    let seed = hmac_sha3_256(
        &inner.random_bytes,
        &ctx.fresh_object_address().to_bytes(),
    );
    RandomGenerator { seed, counter: 0, buffer: vector[] }
}

Function derive_next_block

fun derive_next_block(g: &mut random::RandomGenerator): vector<u8>
Implementation
fun derive_next_block(g: &mut RandomGenerator): vector<u8> {
    g.counter = g.counter + 1;
    hmac_sha3_256(&g.seed, &bcs::to_bytes(&g.counter))
}

Function fill_buffer

fun fill_buffer(g: &mut random::RandomGenerator)
Implementation
fun fill_buffer(g: &mut RandomGenerator) {
    let next_block = derive_next_block(g);
    vector::append(&mut g.buffer, next_block);
}

Function generate_bytes

Generate n random bytes.

public fun generate_bytes(g: &mut random::RandomGenerator, num_of_bytes: u16): vector<u8>
Implementation
public fun generate_bytes(g: &mut RandomGenerator, num_of_bytes: u16): vector<u8> {
    let mut result = vector[];
    // Append RAND_OUTPUT_LEN size buffers directly without going through the generator's buffer.
    let mut num_of_blocks = num_of_bytes / RAND_OUTPUT_LEN;
    while (num_of_blocks > 0) {
        vector::append(&mut result, derive_next_block(g));
        num_of_blocks = num_of_blocks - 1;
    };
    // Fill the generator's buffer if needed.
    let num_of_bytes = num_of_bytes as u64;
    if (vector::length(&g.buffer) < (num_of_bytes - vector::length(&result))) {
        fill_buffer(g);
    };
    // Take remaining bytes from the generator's buffer.
    while (vector::length(&result) < num_of_bytes) {
        vector::push_back(&mut result, vector::pop_back(&mut g.buffer));
    };
    result
}

Function u256_from_bytes

fun u256_from_bytes(g: &mut random::RandomGenerator, num_of_bytes: u8): u256
Implementation
fun u256_from_bytes(g: &mut RandomGenerator, num_of_bytes: u8): u256 {
    if (vector::length(&g.buffer) < num_of_bytes as u64) {
        fill_buffer(g);
    };
    let mut result: u256 = 0;
    let mut i = 0;
    while (i < num_of_bytes) {
        let byte = vector::pop_back(&mut g.buffer);
        result = (result << 8) + (byte as u256);
        i = i + 1;
    };
    result
}

Function generate_u256

Generate a u256.

public fun generate_u256(g: &mut random::RandomGenerator): u256
Implementation
public fun generate_u256(g: &mut RandomGenerator): u256 {
    u256_from_bytes(g, 32)
}

Function generate_u128

Generate a u128.

public fun generate_u128(g: &mut random::RandomGenerator): u128
Implementation
public fun generate_u128(g: &mut RandomGenerator): u128 {
    u256_from_bytes(g, 16) as u128
}

Function generate_u64

Generate a u64.

public fun generate_u64(g: &mut random::RandomGenerator): u64
Implementation
public fun generate_u64(g: &mut RandomGenerator): u64 {
    u256_from_bytes(g, 8) as u64
}

Function generate_u32

Generate a u32.

public fun generate_u32(g: &mut random::RandomGenerator): u32
Implementation
public fun generate_u32(g: &mut RandomGenerator): u32 {
    u256_from_bytes(g, 4) as u32
}

Function generate_u16

Generate a u16.

public fun generate_u16(g: &mut random::RandomGenerator): u16
Implementation
public fun generate_u16(g: &mut RandomGenerator): u16 {
    u256_from_bytes(g, 2) as u16
}

Function generate_u8

Generate a u8.

public fun generate_u8(g: &mut random::RandomGenerator): u8
Implementation
public fun generate_u8(g: &mut RandomGenerator): u8 {
    u256_from_bytes(g, 1) as u8
}

Function generate_bool

Generate a boolean.

public fun generate_bool(g: &mut random::RandomGenerator): bool
Implementation
public fun generate_bool(g: &mut RandomGenerator): bool {
    (u256_from_bytes(g, 1) & 1) == 1
}

Function u128_in_range

fun u128_in_range(g: &mut random::RandomGenerator, min: u128, max: u128, num_of_bytes: u8): u128
Implementation
fun u128_in_range(g: &mut RandomGenerator, min: u128, max: u128, num_of_bytes: u8): u128 {
    assert!(min <= max, EInvalidRange);
    if (min == max) {
        return min
    };
    // Pick a random number in [0, max - min] by generating a random number that is larger than max-min, and taking
    // the modulo of the random number by the range size. Then add the min to the result to get a number in
    // [min, max].
    let range_size = (max - min) as u256 + 1;
    let rand = u256_from_bytes(g, num_of_bytes);
    min + (rand % range_size as u128)
}

Function generate_u128_in_range

Generate a random u128 in [min, max] (with a bias of 2^{-64}).

public fun generate_u128_in_range(g: &mut random::RandomGenerator, min: u128, max: u128): u128
Implementation
public fun generate_u128_in_range(g: &mut RandomGenerator, min: u128, max: u128): u128 {
    u128_in_range(g, min, max, 24)
}

Function generate_u64_in_range

public fun generate_u64_in_range(g: &mut random::RandomGenerator, min: u64, max: u64): u64
Implementation
public fun generate_u64_in_range(g: &mut RandomGenerator, min: u64, max: u64): u64 {
    u128_in_range(g, min as u128, max as u128, 16) as u64
}

Function generate_u32_in_range

Generate a random u32 in [min, max] (with a bias of 2^{-64}).

public fun generate_u32_in_range(g: &mut random::RandomGenerator, min: u32, max: u32): u32
Implementation
public fun generate_u32_in_range(g: &mut RandomGenerator, min: u32, max: u32): u32 {
    u128_in_range(g, min as u128, max as u128, 12) as u32
}

Function generate_u16_in_range

Generate a random u16 in [min, max] (with a bias of 2^{-64}).

public fun generate_u16_in_range(g: &mut random::RandomGenerator, min: u16, max: u16): u16
Implementation
public fun generate_u16_in_range(g: &mut RandomGenerator, min: u16, max: u16): u16 {
    u128_in_range(g, min as u128, max as u128, 10) as u16
}

Function generate_u8_in_range

Generate a random u8 in [min, max] (with a bias of 2^{-64}).

public fun generate_u8_in_range(g: &mut random::RandomGenerator, min: u8, max: u8): u8
Implementation
public fun generate_u8_in_range(g: &mut RandomGenerator, min: u8, max: u8): u8 {
    u128_in_range(g, min as u128, max as u128, 9) as u8
}

Function shuffle

Shuffle a vector using the random generator (Fisher–Yates/Knuth shuffle).

public fun shuffle<T>(g: &mut random::RandomGenerator, v: &mut vector<T>)
Implementation
public fun shuffle<T>(g: &mut RandomGenerator, v: &mut vector<T>) {
    let n = vector::length(v);
    if (n == 0) {
        return
    };
    assert!(n <= U16_MAX, EInvalidLength);
    let n = n as u16;
    let mut i: u16 = 0;
    let end = n - 1;
    while (i < end) {
        let j = generate_u16_in_range(g, i, end);
        vector::swap(v, i as u64, j as u64);
        i = i + 1;
    };
}