Skip to main content

Module iota::random

This module provides functionality for generating secure randomness.

use iota::address; use iota::dynamic_field; use iota::hex; use iota::hmac; use iota::object; use iota::transfer; use iota::tx_context; use iota::versioned; use std::ascii; use std::bcs; use std::option; use std::string; use std::vector;

Module Functions

prv 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 iota::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); }

Structs

struct Random

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

public struct Random has key

Fields

pub new_generator

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

Using randomness can be error-prone if you don't observe the subtleties in its correct use, for example, randomness dependent code might be exploitable to attacks that carefully set the gas budget in a way that breaks security. For more information, see: https://docs.iota.org/developer/advanced/onchain-randomness

public fun new_generator(r: &iota::random::Random, ctx: &mut iota::tx_context::TxContext): iota::random::RandomGenerator

Implementation

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

prv load_inner

fun load_inner(self: &iota::random::Random): &iota::random::RandomInner

Implementation

fun load_inner(self: &Random): &RandomInner {     let version = self.inner.version();     // 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 = self.inner.load_value();     assert!(inner.version == version, EWrongInnerVersion);     inner }

prv load_inner_mut

fun load_inner_mut(self: &mut iota::random::Random): &mut iota::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 = self.inner.load_value_mut();     assert!(inner.version == version, EWrongInnerVersion);     inner }

prv update_randomness_state

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

fun update_randomness_state(self: &mut iota::random::Random, new_round: u64, new_bytes: vector<u8>, ctx: &iota::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; }

struct RandomInner

public 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.

public struct RandomGenerator has drop

Fields
seed: vector<u8>
counter: u16
buffer: vector<u8>

pub generate_bool

Generate a boolean.

public fun generate_bool(g: &mut iota::random::RandomGenerator): bool

Implementation

public fun generate_bool(g: &mut RandomGenerator): bool {     (uint_from_bytes!(g, 1) & 1) == 1 }

pub generate_bytes

Generate n random bytes.

public fun generate_bytes(g: &mut iota::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 num_of_blocks = num_of_bytes / RAND_OUTPUT_LEN;     num_of_blocks.do!(|| result.append(g.derive_next_block()));     // Fill the generator's buffer if needed.     let num_of_bytes = num_of_bytes as u64;     let remaining = num_of_bytes - result.length();     if (g.buffer.length() < remaining) {         let next_block = g.derive_next_block();         g.buffer.append(next_block);     };     // Take remaining bytes from the generator's buffer.     remaining.do!(|| result.push_back(g.buffer.pop_back()));     result }

pub generate_u128

Generate a u128.

public fun generate_u128(g: &mut iota::random::RandomGenerator): u128

Implementation

public fun generate_u128(g: &mut RandomGenerator): u128 {     uint_from_bytes!(g, 16) }

pub 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 iota::random::RandomGenerator, min: u128, max: u128): u128

Implementation

public fun generate_u128_in_range(g: &mut RandomGenerator, min: u128, max: u128): u128 {     uint_in_range!(g, min, max, 24) }

pub generate_u16

Generate a u16.

public fun generate_u16(g: &mut iota::random::RandomGenerator): u16

Implementation

public fun generate_u16(g: &mut RandomGenerator): u16 {     uint_from_bytes!(g, 2) }

pub 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 iota::random::RandomGenerator, min: u16, max: u16): u16

Implementation

public fun generate_u16_in_range(g: &mut RandomGenerator, min: u16, max: u16): u16 {     uint_in_range!(g, min, max, 10) }

pub generate_u256

Generate a u256.

public fun generate_u256(g: &mut iota::random::RandomGenerator): u256

Implementation

public fun generate_u256(g: &mut RandomGenerator): u256 {     uint_from_bytes!(g, 32) }

pub generate_u32

Generate a u32.

public fun generate_u32(g: &mut iota::random::RandomGenerator): u32

Implementation

public fun generate_u32(g: &mut RandomGenerator): u32 {     uint_from_bytes!(g, 4) }

pub 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 iota::random::RandomGenerator, min: u32, max: u32): u32

Implementation

public fun generate_u32_in_range(g: &mut RandomGenerator, min: u32, max: u32): u32 {     uint_in_range!(g, min, max, 12) }

pub generate_u64

Generate a u64.

public fun generate_u64(g: &mut iota::random::RandomGenerator): u64

Implementation

public fun generate_u64(g: &mut RandomGenerator): u64 {     uint_from_bytes!(g, 8) }

pub generate_u64_in_range

public fun generate_u64_in_range(g: &mut iota::random::RandomGenerator, min: u64, max: u64): u64

Implementation

public fun generate_u64_in_range(g: &mut RandomGenerator, min: u64, max: u64): u64 {     uint_in_range!(g, min, max, 16) }

pub generate_u8

Generate a u8.

public fun generate_u8(g: &mut iota::random::RandomGenerator): u8

Implementation

public fun generate_u8(g: &mut RandomGenerator): u8 {     uint_from_bytes!(g, 1) }

pub 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 iota::random::RandomGenerator, min: u8, max: u8): u8

Implementation

public fun generate_u8_in_range(g: &mut RandomGenerator, min: u8, max: u8): u8 {     uint_in_range!(g, min, max, 9) }

pub shuffle

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

public fun shuffle<T>(g: &mut iota::random::RandomGenerator, v: &mut vector<T>)

Implementation

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

prv derive_next_block

Get the next block of 32 random bytes.

fun derive_next_block(g: &mut iota::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)) }

prv uint_from_bytes (macro)

macro fun uint_from_bytes<$T: drop>($g: &mut iota::random::RandomGenerator, $num_of_bytes: u8): $T

Implementation

macro fun uint_from_bytes<$T: drop>($g: &mut RandomGenerator, $num_of_bytes: u8): $T {     let g = $g;     let num_of_bytes = $num_of_bytes;     if (g.buffer.length() < num_of_bytes as u64) {         let next_block = g.derive_next_block();         g.buffer.append(next_block);     };     // TODO: why regression test fails if we use $T instead of u256     let mut result: u256 = 0;     num_of_bytes.do!(|_| {         let byte = g.buffer.pop_back() as u256;         result = (result << 8) + byte;     });     result as $T }

prv uint_in_range (macro)

Helper macro to generate a random uint in [min, max] using a random number with num_of_bytes bytes. Assumes that the caller verified the inputs, and uses num_of_bytes to control the bias (e.g., 8 bytes larger than the actual type used by the caller function to limit the bias by 2^-64).

macro fun uint_in_range<$T: drop>($g: &mut iota::random::RandomGenerator, $min: $T, $max: $T, $num_of_bytes: u8): $T

Implementation

macro fun uint_in_range<$T: drop>(     $g: &mut RandomGenerator,     $min: $T,     $max: $T,     $num_of_bytes: u8, ): $T {     let min = $min;     let max = $max;     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 = uint_from_bytes!($g, $num_of_bytes);     min + (rand % range_size as $T) }

Constants

err ENotSystemAddress

const ENotSystemAddress: u64 = 0;

err EWrongInnerVersion

const EWrongInnerVersion: u64 = 1;

err EInvalidRandomnessUpdate

const EInvalidRandomnessUpdate: u64 = 2;

err EInvalidRange

const EInvalidRange: u64 = 3;

err EInvalidLength

const EInvalidLength: u64 = 4;

const CURRENT_VERSION

const CURRENT_VERSION: u64 = 1;

const RAND_OUTPUT_LEN

const RAND_OUTPUT_LEN: u16 = 32;

const U16_MAX

const U16_MAX: u64 = 65535;