Blockchain: Implementing an On-Chain Random Number Generator

randomrandomness

I've been doing some research on gasleft() (used to be msg.gas but is now deprecated) and was unable to find any specifics on how or where this value is defined. I was able to find another detailed ethereum stack exchange post talking about gasleft decrementing after every step in a function call.

If gasleft is only calculated and available DURING an EVM instruction execution, does that mean that it's non-deterministic and can be used for random number generation? If not, how would one even be able to calculate the cost of an instruction to try and exploit this in the scenario that it's used to generate random numbers? Anyone can change the gasLimit when sending a transaction and this would lead to non-deterministic in my understanding.

src: How is msg.gas calculated?

//gasLimit => 1000000

Random Instructions Costing GAS
gasleft(); => 870000
Random Instructions Costing GAS
gasleft(); => 820000
uint256 randomNumber = random();
Do Something With randomNumber

function random() internal returns (uint256) {
    return uint256(keccak256(abi.encodePacked(gasleft())));
}

To add on to this, I wrote a simple script that would mean the gas cost changes based on the number of people that decided to "mint" during a wave.

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;

contract TimebasedRNG {
    mapping (address => uint256) addressToTimestamp;
    address[] currentWaveMinters;
    uint256 currentWaveTime;
    uint256[] public randoms;

    function mint() external {
        require(addressToTimestamp[msg.sender] == 0, "already minted within a minute");
        if (currentWaveTime == 0) {
            currentWaveTime = block.timestamp;
            delete randoms;
        }
        currentWaveMinters.push(msg.sender);
        addressToTimestamp[msg.sender] = block.timestamp;

        if (hasBeenOneMinute()) {
            uint256 seed = 0;
            uint256 random;
            for (uint i = 0; i < currentWaveMinters.length; i++) {
                seed = uint256(keccak256(abi.encodePacked(
                    seed,
                    currentWaveMinters[i],
                    addressToTimestamp[currentWaveMinters[i]],
                    currentWaveTime,
                    gasleft()
                )));
            }

            for (uint i = 0; i < currentWaveMinters.length; i++) {
                random = uint256(keccak256(abi.encodePacked(
                    seed,
                    currentWaveMinters[i],
                    addressToTimestamp[currentWaveMinters[i]],
                    currentWaveTime
                )));
                randoms.push(random);
                delete addressToTimestamp[currentWaveMinters[i]];
            }
            delete currentWaveMinters;
            delete currentWaveTime;
        }
    }

    function hasBeenOneMinute() public view returns (bool) {
        return (block.timestamp - currentWaveTime) > 20 seconds;
    }
}

Could this be good enough of a pseudo-random number generator that it would be too difficult to exploit?

Best Answer

does that mean that it's non-deterministic

Everything is deterministic on a blockchain. Otherwise they would not function. Thus, your idea does not make sense.

Related Topic