Random Number Generation (RNG) in Solidity

Random Number Generation (RNG) in Solidity

Random number generation (RNG) in solidity must be done by sending a seed to an off-chain resource like an oracle, which must then return the generated random number and verifiable proof back to the smart contract. Random numbers cannot be generated natively in Solidity due to the determinism of blockchains.

With Chainlink VRF now live on Ethereum mainnet, developers can easily generate random numbers in Solidity in a safe, secure, and verifiable way. In this technical article, we’ll show you how to generate a random number in Solidity using Chainlink VRF.Examples of generating safe random numbers in your smart contracts can be found in the Chainlink Documentation. Here is a Remix example of blockchain random number generation on the Kovan testnet for those looking to test it out now. Just remember to follow the request and receive methodology and fund your smart contract with LINK.

Chainlink VRF (Verifiable Random Function) is a provably-fair and verifiable source of randomness designed for smart contracts. Solidity developers can use it as a tamper-proof random number generator to build safe and reliable smart contracts for Ethereum applications that rely on unpredictable outcomes.

The first step in generating a random number in Solidity using Chainlink VRF is to determine a seed. It is extremely important to choose a seed that is difficult to influence or predict. If someone can influence or predict the seed, they could in theory try to collude with the oracle performing the request for randomness to give themselves a favorable result. Because of this, it’s advised to not use values derived from the state of the blockchain such as the block height or block timestamp.

This seed is then sent in a request to a Chainlink oracle. The oracle then generates a pseudo random number with the given seed, and returns the result back to the smart contract, along with cryptographic proof that the random number was generated using the seed. This cryptographic proof is created via public-key cryptography, a widely accepted feature of blockchain technology. It’s important that the result can be verified, because actors such as miners or oracles can try to influence the result of a random number for their own benefit.

This is a high-level overview of how Chainlink VRF works. More details of the underlying technical implementation can be found in our introduction to Chainlink VRF. However, as a developer, you don’t need to worry about anything except for obtaining a seed and then creating a request to a Chainlink oracle.

Creating the Consumer Contract

To obtain a random number in your Solidity smart contract, we should first inherit from the Chainlink VRFConsumerBase contract. Our contract should also contain variables for the random number result, the public key hash used to generate randomness, and the given fee to the oracle for fulfilling the request.

import "https://raw.githubusercontent.com/smartcontractkit/chainlink/master/evm-contracts/src/v0.6/VRFConsumerBase.sol";

contract RandomNumberConsumer is VRFConsumerBase {
    
    bytes32 internal keyHash;
    uint256 internal fee;
    uint256 public randomResult;
    
}

Next, in the constructor we should initialize the Chainlink VRF coordinator. We do this by calling the VRFConsumerBase function, passing in the address of the VRF coordinator and the address of the Chainlink token for the given environment. We also need to set the keyHash variable, which is the public key against which randomness is generated. Environment specific values for these can be obtained in the Contract Addresses section of Chainlink VRF documentation. Finally, we need to set the LINK token payment amount. For the Kovan test environment, it's 0.1 LINK.

   constructor() 
        VRFConsumerBase(
            0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9, // VRF Coordinator
            0xa36085F69e2889c224210F603D836748e7dC0088  // LINK Token
        ) public
    {
        keyHash = 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4;
        fee = 0.1 * 10 ** 18; // 0.1 LINK
    }

Next, we override the two functions ‘getRandomNumber’ and ‘fulfillRandomness’ in our contract.  The ‘getRandomNumber’ function should take the seed as an input parameter, and should simply call the VRFConsumerBase ‘requestRandomness’ function, passing in the keyHash, the fee amount, and the given seed.

  /** 
     * Requests randomness from a user-provided seed
     */
    function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
        return requestRandomness(keyHash, fee, userProvidedSeed);
    }

When executed, this function sends the request to the given VRF Coordinator contract, which then builds up a final seed and sends it on to the Chainlink oracle for that VRF Coordinator. The final seed is built up with a hash of the following values:

  • The user supply seed
  • The public key hash of the Chainlink oracle fulfilling the request
  • The users nonce at the time of the request
  • The address of the contract that made the request
  • The current block number

The reason these extra values are used is to protect against the scenario of a contract getting the same result using the same seed more than once. The nonce helps protect against a contract doing multiple requests within the same block, so in theory a contract can do multiple requests within the same block using the same seed for each request, and they will still get back unique verifiable random numbers for each request.

The ‘fulfillRandomness’ function should simply accept the random number response as an unsigned integer, along with the ID of the request, and then store the given random number in the contract. This function is called by the VRFCoordinator contract when it receives and verifies a random number. More information on both these functions can be found in the Chainlink VRF documentation.

 /**
     * Callback function used by VRF Coordinator
     */
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResult = randomness;
    }

We now have a complete and working example of random number generation (RNG) in Solidity, and can now deploy and test the contract.

Testing the Random Number Generation Consumer Contract

The complete contract above can be easily opened, compiled, and deployed in Remix on the Kovan network. Once deployed, be sure to fund the contract with some LINK.

Once the contract has been funded with at least 0.1 LINK, we can call the ‘getRandomNumber’ function, passing in a number as a seed. This will send the request along with the seed to the VRF coordinator running on a Chainlink oracle.

Random Number Generation (RNG) in SolidityRandom Number Solidity 

Once this transaction has been processed, we then wait a few seconds for the Chainlink oracle to fulfill the request for a random number, and then call the ‘fulfillRandomness’ function we created earlier to return the random number back to our consumer contract.

We can then call the ‘randomResult’ getter function to see the result of the verifiable random number that was generated by the Chainlink oracle with the given seed. We now have a verifiable random number that can be used throughout our consumer contract and any other applications that use it.

Random Number Generation (RNG) in SolidityRandom Result Solidity (Remix)

Verifying Randomness

Now that we have a random number returned to our contract, you may wonder how can we be sure that it was generated with the given seed and public key hash of the Chainlink oracle that performed the request. The answer when using Chainlink VRF is that you don’t need to. The verification happens automatically as part of the VRFCoordinator contract fulfilling the request.

If the validation fails, then the random number is not returned to the consuming contract, and the transaction is reverted. So blockchain developers that use Chainlink VRF can be confident that their random numbers obtained via Chainlink VRF are verifiably random. The underlying technical details of the verification can be found in our technical walkthrough of Chainlink VRF.

Summary

Chainlink VRF allows Solidity developers to quickly and easily generate random numbers in their smart contracts in a safe, secure and proven verifiable manner.

If you are a smart contract developer and want to take advantage of the Chainlink VRF feature, visit the Chainlink developer documentation and join the technical discussion on Discord. You can learn more about Chainlink by visiting the Chainlink website and following Chainlink on Twitter and Reddit.

Publication date: 
11/23/2020 - 20:44
Author: 
Disclaimer

The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.