How to generate a random number in solidity?
Asked Answered
V

11

28

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?

Vintner answered 18/2, 2018 at 6:3 Comment(1)
check here #52467748Clipclop
U
23

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);
    }

}
Undertrump answered 28/5, 2020 at 20:47 Comment(3)
Keep in mind that generating a random number this way has a fee (paid in LINK).Club
You will cost 10 LINK per request on Ethereum. 1 LINK = $22.5 today. If your project is an NFT project with 1000 NFTs and you want to make sure you used the 'can not be predicted random number' in every mint. You can do the math.Oxysalt
Although solidity contracts are deterministic, there is still a way to generate a Pseudo-random number. The answer should cover that first and then show options such as chainlink.Gelatinous
E
19

You cannot create truly random numbers but you can pseudo-random numbers. Blockchain is a deterministic system so we have to make sure that each node must give the same random number. Determinism, is very important because it is vital that regardless of where the smart contract code executes, it produces the same result every time and everywhere.

Getting truly numbers in a deterministic system is impossible because global variables are being used is predictable or can be somehow get manipulated.

For example, Timestamp vulnerability is quite common. Usually, the timestamp of a block is accessed via block.timestamp but this timestamp can be manipulated by miners, leading to influencing the outcome of some function that relies on timestamps. The timestamp is used as a source of randomness in lottery games to select the next winner. Thus, it might be possible for a miner to modify the timestamp in such a way that its chances of becoming the next winner increase.

In order to get the true random number we have to look at outside the blockchain. We need to use oracle services to get the true random number. If you do not have your true random number in your smart contract, your smart contract can get hacked. You can read about 2 cases:

https://www.reddit.com/r/ethereum/comments/74d3dc/smartbillions_lottery_contract_just_got_hacked/

https://hrishiolickel.medium.com/why-smart-contracts-fail-undiscovered-bugs-and-what-we-can-do-about-them-119aa2843007

since solidity evolving very fast, other answers are outdated. This answer will be outdated one day but as of now you can implement a pseudo number generator like this:

  // I realized if you call the random() in for loop, you get same result. So I had to add another changing variable
  // https://mcmap.net/q/503711/-how-to-generate-random-words-in-solidity-based-on-a-string-of-letters/73557284#73557284
  uint counter =1;
  function random() private view returns (uint) {
        counter++;
        // sha3 and now have been deprecated
        return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, players,counter)));
        // convert hash to integer
        // players is an array of entrants
        
    }

this will return very very big number. But we use modulo operator.

random() % players.length

this will return a number between 0 and players.length. we write a function for this:

function pickWinner() public {
        uint index=random()%players.length;
    }

Get random Number with Chainlink

After you deploy this contract, you have to send Link tokens to this contract and press on getRandomNumber. Wait about a minute and then click on result

 import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFConsumerBase.sol";

contract Test is VRFConsumerBase {
    bytes32 public keyHash;
    uint256 public fee;
    uint256 public ticketPrice;
    uint256 public result;

    // ---------------- GOERLI ADDRESSESS----------------
    // link address 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
    // key hash 0x0476f9a745b61ea5c0ab224d3a6e4c99f0b02fce4da01143a4f70aa80ae76e8a
    //ChainlinkVRFCoordinator 0x2bce784e69d2Ff36c71edcB9F88358dB0DfB55b4
    constructor(
        address _ChainlinkVRFCoordinator,
        address _ChainlinkLINKToken,
        bytes32 _ChainlinkKeyHash,
        uint256 _ticketPrice
    ) VRFConsumerBase(_ChainlinkVRFCoordinator, _ChainlinkLINKToken) {
        keyHash = _ChainlinkKeyHash;
        fee = 0.1 * 10 ** 18;
        ticketPrice = _ticketPrice;
    }

    function getRandomNumber() public payable returns (bytes32 requestId) {
        require(
            LINK.balanceOf(address(this)) >= fee,
            "YOU HAVE TO SEND LINK TOKEN TO THIS CONTRACT"
        );
        return requestRandomness(keyHash, fee);
    }

    // this is callback, it will be called by the vrf coordinator
    function fulfillRandomness(
        bytes32 requestId,
        uint256 randomness
    ) internal override {
        result = randomness;
    }

    receive() external payable {}
}

I answered this question: Getting a random number with Chainlink VRF

Endearment answered 30/4, 2021 at 11:16 Comment(3)
how likely is it that if this function were called several times in rapid succession that the same number would be returned because block.difficult and block.timestamp haven't changed in the intervening calls?Abiogenetic
@Abiogenetic as I mentioned "You cannot create truly random number but you can pseudo random number". u can change the paramsEndearment
You could add reference to one of your state variables that will change between each call. Simplest example is uint randomCallCountKorn
C
4

To generate a pseudo-random number you could do something like

function random() private view returns (uint) {
    return uint(keccak256(block.difficulty, now));
} 

If you need a random number in a specific range you can e.g. use modulo. For instance to get a random number between 0 and 999 (both incl.) you can do it as follows:

function random() private view returns (uint) {
    uint randomHash = uint(keccak256(block.difficulty, now));
    return randomHash % 1000;
} 

If you have e.g. another field of type array available, you could pass its length to the keccak256 function as an additional argument.

(All code was compiled with v0.4.17).

Cookout answered 23/2, 2018 at 13:5 Comment(3)
Can anyone tell if this safe?Marvismarwin
@RenanCoelho This method generates a pseudo-random number using values like block.difficulty and now, which can be influenced by miners. Therefore, it's not safe for production environments where true randomness is required, as it could be predicted or manipulated by malicious actors.Cookout
Thanks for the info! Maybe the safest way to do it is using Chainlink VRF. chain.link/vrfMarvismarwin
L
3
// Generate a random number between 1 and 100:
uint randNonce = 0;
uint random = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % 100;
randNonce++;
uint random2 = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % 100;
Lodhia answered 16/2, 2022 at 15:23 Comment(0)
K
2

In order to prevent a manipulation you need a more than pseudorandom number.

Have a look at the randao smart contract. It provides actual random numbers that an attacker can not easily manipulate.

Klinger answered 10/3, 2018 at 2:3 Comment(0)
G
1

Generating a random number in solidity is pretty complicated. Because blockchain is a deterministic system.

For generating a random number in solidity you have to generator this number from outside the blocks.

For this you have to learn about the VRFConsumerBase. This is a package which help us to generator the fully random number without disturbing our blockchain blocks. For more help you can checkout from this link: https://docs.chain.link/docs/get-a-random-number/

Here is the simple example generating a random number in solidity.

pragma solidity ^0.6.6;
import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";

contract Lottery is VRFConsumerBase, Ownable {
    address payable[] public players;
    uint256 public usdEntryFee;
    address payable recentWinner;
    AggregatorV3Interface internal ethUSDPriceFeed;
    enum LOTTERY_STATE {
        OPEN,
        CLOSED,
        CALCULATING_WINNER
    }
    LOTTERY_STATE public lottery_state;
    uint256 public fee;
    bytes32 public keyHash;
    uint256 public randomness;

    constructor(
        address _priceFeedAddress,
        address _vrfCoordinator,
        address _link,
        uint256 _fee,
        bytes32 _keyHash
    ) public VRFConsumerBase(_vrfCoordinator, _link) {
        usdEntryFee = 50 * (10**18);
        ethUSDPriceFeed = AggregatorV3Interface(_priceFeedAddress);
        lottery_state = LOTTERY_STATE.CLOSED;
        fee = _fee;
        keyHash = _keyHash;
    }

    function enter() public payable {
        //$50 minimum
        require(lottery_state == LOTTERY_STATE.OPEN, "LOTTERY IS CLOSED!");
        require(msg.value >= getEntranceFee(), "Not Enough ETH!");
        players.push(msg.sender);
    }

    function getEntranceFee() public view returns (uint256) {
        (, int256 price, , , ) = ethUSDPriceFeed.latestRoundData();
        uint256 adjustedPrice = uint256(price) * 10**10;
        uint256 costToEnter = (usdEntryFee * 10**18) / adjustedPrice;
        return costToEnter;
    }

    function startLottery() public onlyOwner {
        require(
            lottery_state == LOTTERY_STATE.CLOSED,
            "Can't start a new lottery yet!"
        );
        lottery_state = LOTTERY_STATE.OPEN;
    }

    function endLottery() public onlyOwner {
        
        lottery_state = LOTTERY_STATE.CALCULATING_WINNER;
        bytes32 requestId = requestRandomness(keyHash, fee);
    }

    function fulfillRandomness(bytes32 _requestId, uint256 _randomness)
        internal
        override
    {
        require(
            lottery_state == LOTTERY_STATE.CALCULATING_WINNER,
            "You aren't there yet!"
        );
        require(_randomness > 0, "random-not-found");
        uint256 indexOfWinner = _randomness % players.length;
        recentWinner = players[indexOfWinner];
        recentWinner.transfer(address(this).balance);
        players = new address payable[](0);
        lottery_state = LOTTERY_STATE.CLOSED;
        randomness = _randomness;
    }
}

Just let me know if you have any other question.

Greece answered 23/6, 2022 at 4:27 Comment(0)
S
1

First, let's see how a random number is generated on your computer. It needs a source of randomness, like - the temperature of CPU, how many times you pressed the letter "B", the speed of your fan at a time t etc.

But in blockchain, there is nearly no source of randomness. Everything the contract sees, the public sees. And someone could game the system just by looking a the smart contract, aka seeing its source of randomness.

Given you take the above into consideration, you can use this simple random generator solidity program to generate random numbers between 0 to 100. Bare in mind that someone can still game the system. We're just making it hard for them.

contract Random {
uint256 private seed;

constructor() {
    seed = (block.timestamp + block.difficulty) % 100;
}

function getRandomNumber() public returns (uint256) {
    seed = (seed + block.timestamp + block.difficulty) % 100;
    return seed;
}

}

block.difficulty and block.timestamp are pretty random and we're taking advantage of that.

block.difficulty is a indicates how difficult a block is to mine for miners. So we can generally say the more transactions a block has the more difficult it is to mine.

block.timestamp is Unix time of block when it is mined.

So, we're seeding at the creation of the smart contract and even to make it hard when we call getRandomNumber we're changing the seed, by adding another source of randomness, i.e the previous seed. Hope this helps.

Shericesheridan answered 26/8, 2022 at 6:19 Comment(0)
V
0

Here my best attempt. Based on a different problem I can't find anymore. If I do I'll link it.

pragma solidity ^0.4.19;

contract test {

event randomNumbers(uint[8] numbers, uint[8] randomNumbers);

function testa() public returns (uint[8] bla) {

    //uint something = 12345678123456781234567812345678;
    uint something = uint(keccak256("rockandroll"));

    uint[8] memory array;
    uint[8] memory random;

    for(uint i=0; i<8; i++) {
        uint digit = something % 10000;
        // do something with digit
        array[i] = digit;
        something /= 10000;
        random[i] =digit % 10;
    }

    randomNumbers(array,random);

    return array;
}
Vintner answered 18/2, 2018 at 10:15 Comment(0)
M
0

You can't do that using only Solidity, otherwise it's unsafe. You need an oracle.

If you don't, the random number is more or less predictable.

For example, you could use the block timestamp and difficulty to create a random number but it's pseudo-random:

function createRandomNumber(uint256 max) external view returns (uint256)
{
    return
        uint256(
            keccak256(
                abi.encodePacked(
                    block.timestamp,
                    block.difficulty,
                    msg.sender
                )
            )
        ) % max;
}

So you need to use ChainLink's VRF V2 contract (verifiable random number):

contract SomeContract is VRFV2WrapperConsumerBase {

    // fee given to the oracle for fulfilling the request (LINK tokens)
    uint256 internal fee;

    uint32 callbackGasLimit = 100000;

    uint16 confirmations = 3;

    // Goerli testnet addresses
    address linkToken = 0x326C977E6efc84E512bB9C30f76E30c160eD06FB;
    address vrfWrapper = 0x708701a1DfF4f478de54383E49a627eD4852C816;

    constructor() VRFV2WrapperConsumerBase(linkToken, vrfWrapper) {
        fee = 0.25 * 10**18;
    }

    // Requests a random number from ChainLink
    function generateRandomNumber() public returns (uint256) {
        require(
            LINK.balanceOf(address(this)) > fee,
            "Not enough LINK"
        );

        // Request a random number from chainlink
        uint256 requestId = requestRandomness(
            callbackGasLimit,
            confirmations,
            1
        );

        return requestId;
    }

    // will be executed when chainlink is done generating the number
    function fulfillRandomWords(
        uint256 _requestId,
        uint256[] memory _randomWords
    ) internal override {
        uint256 number = _randomWords[0];

        // Do something with the random number
    }
}
Maxia answered 14/12, 2022 at 22:16 Comment(0)
D
0

You can generate randomness on chain by taking some data at a given time, mixing it and form a number.

unchecked {
  randomTokenId = uint256(
    keccak256(
      abi.encode(
        keccak256(
          abi.encodePacked(
            msg.sender,
            tx.origin,
            gasleft(),
            block.timestamp,
            block.number,
            blockhash(block.number),
            blockhash(block.number - 100)
          )
        )
      )
    )
  ) % 100;
}

Problem with this approach is all data is available at the time and user control the data.

But this Twitter thread helped with an interesting idea: https://twitter.com/_MouseDev/status/1623044322336579615

It uses a future commit and reveal. You mint a token, for example, on block 16723029. But the mint function will only generate your tokenId or random id when 16723029 + 5 (a future block). When after 5 blocks this functions is called, your randomness is revealed and you get your random tokenId or random id. Genius and simple.

If you are minting the last token, you can call it arbitrary call the function.

Dublin answered 28/2, 2023 at 0:16 Comment(0)
M
0

SKALE supports true random generation and it is free to use chain. They have a speical arrangement where 11/16 nodes have to sign to get a random value.

https://docs.skale.network/tools/skale-specific/random-number-generator

Maidamaidan answered 9/8, 2023 at 2:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.