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;