如何在solidity中生成随机数?

Gar*_*ays 28 string blockchain ethereum solidity smartcontracts

我有几个 keccak,如果我能找到一种便宜的方法来获取所创建的 uint 的一部分,那么它们可以减少到一个。

pragma solidity ^0.4.19;

contract test {
  function test() {

  }

function sup() returns (uint test) {
    uint _test = uint(keccak256("wow"));
    return _test;
  }
}
Run Code Online (Sandbox Code Playgroud)

这会返回一个甜蜜的随机数:13483274892375982735325

现在的计划是,我不用用不同的“种子”调用 keccak 5 次,而是可以将该数字分开并得到类似:1348、3274、8923 等的内容,然后将其用作我的随机数,例如:1348 % 10

但坚固性并不能仅仅做到这一点。有什么便宜又好用的东西吗?

小智 22

Solidity 合约是确定性的。任何弄清楚你的合约如何产生随机性的人都可以预测其结果并使用此信息来利用你的应用程序。

一种选择是在链外产生随机性(无法预测)并在智能合约中使用它。Chainlink VRF是一种易于实施的解决方案,用于在智能合约中使用随机数据。以下是请求和接收随机数据的示例片段:

requestRandomness(keyHash, fee, seed);
Run Code Online (Sandbox Code Playgroud)

您的合约请求在回调函数中得到满足:

function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
  // Do something with randomness
}
Run Code Online (Sandbox Code Playgroud)

实现随机数的完整合约的示例如下:

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

}
Run Code Online (Sandbox Code Playgroud)

  • 请记住,以这种方式生成随机数需要付费(在 LINK 中支付)。 (4认同)
  • 在以太坊上每个请求将花费 10 LINK。今天 1 个链接 = 22.5 美元。如果您的项目是一个拥有 1000 个 NFT 的 NFT 项目,并且您想确保在每个铸币厂中都使用了“无法预测的随机数”。你可以算一下。 (3认同)

Yil*_*maz 19

您无法创建真正的随机数,但可以创建伪随机数。区块链是一个确定性系统,因此我们必须确保每个节点必须给出相同的随机数。确定性非常重要,因为无论智能合约代码在哪里执行,它在任何时候、任何地方都会产生相同的结果,这一点至关重要。

在确定性系统中获得真正的数字是不可能的,因为所使用的全局变量是可预测的或者可以以某种方式被操纵。

例如,时间戳漏洞就很常见。通常,块的时间戳是通过访问的block.timestamp,但该时间戳可以由矿工操纵,从而影响某些依赖时间戳的函数的结果。时间戳被用作彩票游戏中的随机性来源来选择下一个获胜者。因此,矿工可能会修改时间戳,从而增加成为下一个获胜者的机会。

为了获得真正的随机数,我们必须看看区块链之外。我们需要使用oracle服务来获取真实的随机数。如果您的智能合约中没有真正的随机数,您的智能合约可能会被黑客攻击。你可以阅读2个案例:

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

由于 Solidity 发展得非常快,其他答案已经过时了。这个答案有一天会过时,但现在你可以实现一个伪数生成器,如下所示:

  // I realized if you call the random() in for loop, you get same result. So I had to add another changing variable
  // /sf/ask/5148850661/#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
        
    }
Run Code Online (Sandbox Code Playgroud)

这将返回非常非常大的数字。但我们使用模运算符。

random() % players.length
Run Code Online (Sandbox Code Playgroud)

这将返回 0 到players.length 之间的数字。我们为此编写一个函数:

function pickWinner() public {
        uint index=random()%players.length;
    }
Run Code Online (Sandbox Code Playgroud)

使用 Chainlink 获取随机数

部署此合约后,您必须将 Link 代币发送到此合约并按getRandomNumber。等待大约一分钟,然后单击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 {}
}
Run Code Online (Sandbox Code Playgroud)

我回答了这个问题:Getting a random number with Chainlink VRF

  • 如果这个函数被快速连续调用几次,那么由于 block.difficult 和 block.timestamp 在中间调用中没有改变而返回相同的数字的可能性有多大? (6认同)
  • 您可以添加对状态变量之一的引用,该状态变量将在每次调用之间发生变化。最简单的例子是“uint randomCallCount” (2认同)

Gar*_*ays 0

这是我最好的尝试。基于另一个问题,我找不到了。如果我这样做我会链接它。

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;
}
Run Code Online (Sandbox Code Playgroud)