import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数...

Chainlink 如何解决以太坊“随机数问题”.

随机数和区块链一直很难达到“一致”(译者注:区块链要求确定性,而随机数正相反)。到目前为止,区块链上还没有可验证的随机函数。

原因是:交易被旷工出块后,需要网络上的多个节点来确认才算真实有效。就要求每个节点验证时都必须得出相同的结果。如果函数是随机的(每次运行的结果不一样),则每个节点将得出不同的结果,从而导致交易得不到确认。

有一些解决(变通)方法可以生成一些 伪随机生成,但到目前为止,已有的方法都不算是真正的随机,或存在操控的可能。

登链社区之前也有一篇译文:区块链上生成随机数 大家可以读一读。

关于 chainlink

Chainlink 网络可以为任何区块链上的复杂智能合约提供可靠的防篡改输入和输出。 —来自 chain.link 官网的介绍

区块链和智能合约针对一组不可变的规则执行计算是个很棒的平台。问题是规则只能应用于系统内部的数据。而如果要从系统外部获取可验证的数据则非常困难。

Chainlink 想要通过提供去中心化的预言机来解决这个问题,使区块链能够通过 Chainlink 访问生态系统之外的数据。预言机(Oracles)实质上是区块链和外部世界之间的桥梁。

真正的随机

在最近的一篇文章中,Chainlink 宣布发布了其新的可验证随机函数(VRF)。开发者现在可以使用该功能将其集成到多个测试网上的 DApp 中,从而使智能合约能够获得可在链上验证的随机数。

可验证随机函数是怎么实现的?

如果你想在 JavaScript 中生成一个随机数,代码非常简单:

Math.random();

每执行一次,生成一个随机数。然而这不是 VRF 的工作方式。与 JavaScript 不同,VRF 是在一些交易实现的。

以下是 VRF 事件发生的顺序:

  1. 你的智能合约通过交易向 VRF 请求一个随机数。
  2. VRF 会生成该随机数字并进行验证。
  3. VRF 准备响应 1 的请求。
  4. VRF 通过另一笔交易将随机数字发送回你的智能合约。

为了使第 4 步成功,你的合约需要实现一个确定的函数,以便 VRF 调用以返回结果。如何在项目中实现呢?

如何实现随机性

让我们创建一个名为 RandomGenerator 的新合约,在合约里我们将调用 VRF 并接收结果。

第 1 步: 创建消费者合约

我们将引入 Chainlink 提供的 VRFConsumerBase 的合约,这是一个抽象合约,它定义了一个获取和消耗 VRF 的最少实现(后面也会列出 VRFConsumerBase 的代码),我们定义“ RandomGenerator.sol”文件开头像这样:

pragma solidity ^0.6.2;

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

contract RandomGenerator is VRFConsumerBase {

    constructor(address _vrfCoordinator, address _link) VRFConsumerBase(_vrfCoordinator, _link) public {
    }
}

VRFConsumerBase 的合约的源码如下:

pragma solidity 0.6.2;

import "./vendor/SafeMath.sol";
import "./interfaces/LinkTokenInterface.sol";
import "./VRFRequestIDBase.sol";

abstract contract VRFConsumerBase is VRFRequestIDBase {

  using SafeMath for uint256;

  function fulfillRandomness(bytes32 requestId, uint256 randomness)
    external virtual;


  function requestRandomness(bytes32 _keyHash, uint256 _fee, uint256 _seed)
    public returns (bytes32 requestId)
  {
    LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, _seed));
    // This is the seed actually passed to the VRF in VRFCoordinator
    uint256 vRFSeed  = makeVRFInputSeed(_keyHash, _seed, address(this), nonces[_keyHash]);
    // nonces[_keyHash] must stay in sync with
    // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above
    // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest)
    nonces[_keyHash] = nonces[_keyHash].add(1); 
    return makeRequestId(_keyHash, vRFSeed);
  }

  LinkTokenInterface internal LINK;
  address internal vrfCoordinator;

  // Nonces for each VRF key from which randomness has been requested.
  //
  // Must stay in sync with VRFCoordinator[_keyHash][this]
  mapping(bytes32 /* keyHash */ => uint256 /* nonce */) public nonces;
  constructor(address _vrfCoordinator, address _link) public {
    vrfCoordinator = _vrfCoordinator;
    LINK = LinkTokenInterface(_link);
  }
}

VRFConsumerBase 仍在后期测试中,因此还没有产品软件包对外提供。这就是为什么使用 GitHub 的 HTTP URL 进行导入的原因。

VRFConsumerBase 抽象合约有两个参数,分别代表协调器(coordinator)和 LINK ERC20 代币合约的地址。这些在每个网络上合约地址是固定的(稍后会详细介绍)。

第 2 步: 重载函数

VRFConsumerBase 中有两个对 VRF 流程至关重要的函数。

第一个是 requestRandomness ,这个函数已经实现了,我们不需要重载。这个函数是用来对 VRF 进行初始请求调用

另一个是 fulfillRandomness, 这是 VRF 在生成数字后,用来回调的函数。我们需要重载它,以便在获取随机数后执行相应的操作。

在我们合约的实现里,仅仅是把随机数存储在一个名为 randomNumber 的状态变量中,以便我们可以在结束时查询它。代码像这样:

pragma solidity ^0.6.2;

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

contract RandomGenerator is VRFConsumerBase {
    
    bytes32 public reqId;
    uint256 public randomNumber;

    constructor(address _vrfCoordinator, address _link) VRFConsumerBase(_vrfCoordinator, _link) public {
    }
    
    function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
        reqId = requestId;
        randomNumber = randomness;
    }
}

我们在 fulfillRandomness 函数上添加了 override 修饰符以实现重载,在实现中,使用 reqIdrandomNumber 来保存接收变量的值。

第 3 步: 生成随机数

正如在前面 第 1 步提到的,函数调用需要传递一些地址和其他值作为参数。在部署智能合约并调用构造函数时,它需要 VRF 协调器(coordinator)合约地址和网络上 LINK 代币合约地址。在 Ropsten 测试网上,合约地址如下:

  • VRF coordinator: 0xf720CF1B963e0e7bE9F58fd471EFa67e7bF00cfb
  • LINK 代币: 0x20fE562d797A42Dcb3399062AE9546cd06f63280

当调用 requestRandomness 函数时,我们需要传递几个参数:生成随机数的 key hash,生成随机数的费用 fee(使用 LINK 代币)和生成随机性的种子 seed(最后一个由我们提供)。requestRandomness 函数签名如下:

function requestRandomness(bytes32 _keyHash, uint256 _fee, uint256 _seed) public returns (bytes32 requestId)

在 Ropsten 网络上,参数值如下:

  • Key hash 值: 0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205
  • 费用 Fee (1 LINK): 1000000000000000000
  • 种子 Seed: [我们想要的任意值]

因此我们的调用代码如下:

// 设置ropsten key hash
bytes32 keyHash = "0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205";// // 设置 ropsten LINK 费用
fee = 1000000000000000000;
// 设置种子
seed = 123456789;
// 请求随机数
bytes32 reqId = rand.requestRandomness(keyHash, fee, seed);

当结果返回时,随机值将存储并且可以通过以下方法获取:

rand.randomNumber;

自己尝试一下

现在我们将逐步实践如何使用 Remix IDE 和 Metamask 插件从 VRF 获取随机数。在继续之前,请确保已在浏览器上安装了 Metamask 插件。

  • 打开 Remix IDE
  • 如果还没用过 Remix,需要向下图一样选择 Solidity 语言。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第1张图片
  • 创建一个文件: RandomGenerator ,把第 2 步中的代码复制过来。
  • 使用左侧菜单,单击 Solidity 图标,然后选择 0.6.2 编译器版本,如下图所示。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第2张图片

然后单击下面的按钮,并在下拉列表中选择“Injected web3”,如下图所示。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第3张图片
  • 这时 Metamask 会提示一个连接请求,我们点击接受请求。
  • 确保 MetaMask 连接的是 Ropsten 网络,如下图所示:

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第4张图片
  • 确保 Metamask 帐户中有一些 Ropsten 网络的 以太币,如果没有,可以从这里获取。
  • 回到 Remix,在同一选项卡上,应该看到橙色的“ Deploy”按钮,单击“ Deploy”按钮进行部署,注意要接受 Metamask 弹出的合约部署请求。
  • 在部署后,我们需要确保合约中存有一些 LINK 代币,以便它可以为请求随机数支付费用。打开 Ropsten LINK 的“水龙头”,粘贴 Metamask 地址,就可以在 Metamask 中收到 100 LINK。
  • Metamask 不知道 LINK 代币在 Ropsten 网络上的地址,因此我们需要添加它。在“ Metamask”中,在帐户名称左侧,单击“菜单”符号,然后单击底部的 “Add Token”。
  • 在 “Custom Token(自定义代币)”下, 添加地址: 0x20fE562d797A42Dcb3399062AE9546cd06f63280. 剩下的信息将自动填充,提交之后可以看到账号下有 100 个 LINK,下图是 70 个 LINK 的账号截图:

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第5张图片
  • 回到 Remix, 复制部署合约地址,如下图:

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第6张图片
  • 现在我们将向合约发送一些 LINK。回到 Metamask,然后单击 100 LINK 旁边的 3 个点。粘贴合约地址并发送 10 LINK。确认交易后,再继续下一步。
  • 在 Remix 中,我们现在可以请求随机数了。在同一选项卡中,向下滚动会发现更多代表合约公有(public)函数的橙色按钮,如下图所示。单击 requestRandomness 右侧的箭头以展开参数。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第7张图片
  • 按顺序复制这 3 个数:0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205, 1000000000000000000, 123456789(或者其他你想要的值)作为参数提交交易。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第8张图片

交易可能需要一些时间才能确定,因此需要关注一下终端输出在 Etherscan 中的交易信息。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第9张图片

交易完成后,我们需要等待 VRF 生成随机数并将其发送回我们的合约。几分钟后,单击我们在 Remix 中发送交易的橙色按钮下方的蓝色“ randomNumber”按钮,检查合约是否收到了随机数,如下图所示。

import是引进外部函数吗_[译]如何使用VRF(可验证随机函数)在以太坊上生成随机数..._第10张图片

如果一切顺利,应该有一个像我这样的随机数,它是 30207470459964961279215818016791723193587102244018403859363363849439350753829.

现在就大功告成了。

结论

使用 Chainlink 可以在智能合约中可以使用可验证的随机数。在文章中阐述了该机制的工作原理,以及演示了如何将代码集成到智能合约中获取随机数

作者:Alex Roan

原文:https://medium.com/coinmonks/how-to-generate-random-numbers-on-ethereum-using-vrf-8250839dd9e2

欢迎大家在知乎,微博,GitHub 关注我

[登链社区](https://learnblockchain.cn/)赞助翻译。

你可能感兴趣的:(import是引进外部函数吗)