Developers Home»how to guides»Generating On-chain Randomness

Generating On-chain Randomness

Goal

Implement a randomness function inside a pallet.

Use Cases

  • NFT applications
  • Casino gaming type applications

Overview

Randomnessis useful in computer programs for everything from gaming applications to selecting block authors. True randomness is hard to come by in deterministic computers. This is particularly true in the context of a blockchain when all the nodes in the network must agree on the state of the chain. FRAME provides runtime engineers with a source of randomness, using the Randomness trait. This guide would step you through making use of FRAME's Randomness trait by using it's random method and a nonce as a subject. For additional entropy to the randomness value, the last step shows how to assign the RandomCollectiveFlip pallet to the configuration trait of a pallet exposing some "random" type.

Steps

1. Import Randomness

Inside the pallet you want to use, import the Randomness trait from frame_support,:

use frame_support::traits::Randomness;

Now, include it in your pallet's configuration trait:

#[pallet::config]
pub trait frame_system::Config {
    type MyRandomness: Randomness<H256>;
}

Notice that the Randomness trait specifies a generic return of type Output. Use sp_core::H256 in your pallet to satisfy that trait requirement.

A warning on using this trait

As stated in this trait's documentation at best, this trait can give you randomness which was hard to predict a long time ago but that has become easy to predict recently. Bear this in mind depending on your use case for it.

2. Create a nonce

Use a nonce to serve as a subject for the frame_support::traits::Randomness::random(subject: &[u8]) method.

There's two steps to including a nonce in your pallet:

  1. Create a Nonce storage item. This could be of type u32 or u64 (no need for it to be larger).
  2. Create a private nonce function. This will be used to increment the nonce each time it's used.

The increment_nonce() private function could be implemented in such a way that it returns the nonce as well as updates it. Using this approach it would look like this:

    fn get_and_increment_nonce() -> Vec<u8> {
        let nonce = Nonce::get();
        Nonce::put(nonce.wrapping_add(1));
        nonce.encode()
    }

Information

Learn more about wrapping_add and encode() in the Rust documentation.

3. Use Randomness in a dispatchable

Using the nonce, you can call the random() method that Randomness exposes. The code snippet below is a made up example that assumes relevant events and storage items have been implemented:

        #[pallet::weight(100)]
        pub fn create_unique(
            origin: OriginFor<T>)
            -> DispatchResultWithPostInfo {

                // Account calling this dispatchable.
                let sender = ensure_signed(origin)?;

                // Random value.
                let nonce = Self::get_and_increment_nonce();
                let randomValue = T::MyRandomness::random(&nonce);

                // Write the random value to storage.
                <MyStorageItem<T>>::put(randomValue);

                Self::deposit_event(Event::UniqueCreated(randomValue));
            }

4. Updating your pallet's runtime implementation

Having added a type to your pallet's configuration trait Config opens up the opportunity to further enhance the randomness derived by the Randomness trait, by using the Randomness Collective Flip pallet.

Using this pallet alongside the Randomness trait will significantly improve the entropy being processed by random().

In runtime/src/lib.rs, assuming pallet_random_collective_flip is instantiated in construct_runtime as RandomCollectiveFlip, specify your exposed type like this:

impl my_pallet::Config for Runtime{
    type Event;
    type MyRandomness = RandomCollectiveFlip;
}

Examples

Other

Last edit: on

Was This Guide Helpful?
Help us improve