How to generate a random number in solidity?

Garry Plays picture Garry Plays · Feb 18, 2018 · Viewed 7.9k times · Source

I have a couple of keccaks, which could be reduced to one, if I would find a cheap way to get parts of the created uint.

pragma solidity ^0.4.19;

contract test {
  function test() {

  }

function sup() returns (uint test) {
    uint _test = uint(keccak256("wow"));
    return _test;
  }
}

This returns me a sweet random number: 13483274892375982735325

Now the plan is that instead of calling keccak 5 times with different "seeds", I could take that number apart and get something like: 1348, 3274, 8923 etc. which I then use for my random number e.g.: 1348 % 10

But solidity can't just do that. Is there anything cheap that could work?

Answer

user13637383 picture user13637383 · May 28, 2020

Solidity contracts are deterministic. Anyone who figures out how your contract produces randomness can anticipate its results and use this information to exploit your application.

One option is to produce randomness off-chain (where it cannot be predicted) and use it in your smart contract. Chainlink VRF is an easy-to-implement solution for using random data in smart contracts. Here's an example snippet to request & receive random data:

requestRandomness(keyHash, fee, seed);

Your contract's request is fulfilled in a callback function:

function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
  // Do something with randomness
}

An example of a full contract that implements a random number would be:

pragma solidity 0.6.2;

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

contract Verifiable6SidedDiceRoll is VRFConsumerBase {
    using SafeMath for uint;

    bytes32 internal keyHash;
    uint256 internal fee;

    event RequestRandomness(
        bytes32 indexed requestId,
        bytes32 keyHash,
        uint256 seed
    );

    event RequestRandomnessFulfilled(
        bytes32 indexed requestId,
        uint256 randomness
    );

    /**
     * @notice Constructor inherits VRFConsumerBase
     * @dev Ropsten deployment params:
     * @dev   _vrfCoordinator: 0xf720CF1B963e0e7bE9F58fd471EFa67e7bF00cfb
     * @dev   _link:           0x20fE562d797A42Dcb3399062AE9546cd06f63280
     */
    constructor(address _vrfCoordinator, address _link)
        VRFConsumerBase(_vrfCoordinator, _link) public
    {
        vrfCoordinator = _vrfCoordinator;
        LINK = LinkTokenInterface(_link);
        keyHash = 0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205; // hard-coded for Ropsten
        fee = 10 ** 18; // 1 LINK hard-coded for Ropsten
    }

    /** 
     * @notice Requests randomness from a user-provided seed
     * @dev The user-provided seed is hashed with the current blockhash as an additional precaution.
     * @dev   1. In case of block re-orgs, the revealed answers will not be re-used again.
     * @dev   2. In case of predictable user-provided seeds, the seed is mixed with the less predictable blockhash.
     * @dev This is only an example implementation and not necessarily suitable for mainnet.
     * @dev You must review your implementation details with extreme care.
     */
    function rollDice(uint256 userProvidedSeed) public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
        uint256 seed = uint256(keccak256(abi.encode(userProvidedSeed, blockhash(block.number)))); // Hash user seed and blockhash
        bytes32 _requestId = requestRandomness(keyHash, fee, seed);
        emit RequestRandomness(_requestId, keyHash, seed);
        return _requestId;
    }

    function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
        uint256 d6Result = randomness.mod(6).add(1);
        emit RequestRandomnessFulfilled(requestId, randomness);
    }

}