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; }; }