手把手教你如何使用Chainlink闹钟在链上发起投票

以太坊上许多智能合约都需要通过外部计时器触发链上执行,因此dApp在链上发起投票时就会遇到问题,因为投票通常都有一个时间窗口。由于Solidity中没有计时器,因此智能合约的投票机制需要接入外部工具来启动或关闭投票窗口。

# 手把手教你如何使用Chainlink闹钟在链上发起投票 ![](https://img.learnblockchain.cn/2020/11/11_/360396202.png) 以太坊上许多智能合约都需要通过外部计时器触发链上执行,因此dApp在链上发起投票时就会遇到问题,因为投票通常都有一个时间窗口。由于Solidity中没有计时器,因此智能合约的投票机制需要接入外部工具来启动或关闭投票窗口。 其实在传统系统中,计时功能并不难实现,系统可以设置NTP时间与操作系统时间同步,也可以用实物钟表或计时器计时,甚至还可以用sleep表达式在一段时间内暂停代码运行。Solidity智能合约中的事件由链下交易触发,也就是说以太坊等区块链需要接入链下闹钟触发事件或调用函数。而好消息是,开发者可以将Chainlink节点作为闹钟,可靠地触发智能合约执行。 本文将教大家如何在dApp中轻松实现投票时间控制。现在随着越来越多dApp开始走DAO路线,将更多权力交到用户手中,这种投票机制也变得愈发重要。具体步骤如下: - 将Chainlink功能包导入智能合约并继承 - 格式化并提交Chainlink sleep(“until”)请求 - 将合约所有者设置成唯一有权限发起投票的人 - 使用简单的KYC确认每个地址只投一次票 - 使用Getters函数查看投票状态 本示例中使用的代码可以在[GitHub](https://github.com/gmondok/ChainlinkVoting)中查看,你还可以查看方便部署的[Remix](https://remix.ethereum.org/#https://remix.ethereum.org/)。 当然,这只是一个最基本的示例,你可以使用Chainlink“until”请求在任何时间点触发任何事件。这个示例只是一个初始框架,你可以在上面开发其他需要时间控制功能的智能合约。 ## 合约定义和构造函数: ```javascript pragma solidity ^0.6.6; import "github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol"; contract ChainlinkTimedVote is ChainlinkClient { uint private oraclePayment; address private oracle; bytes32 private jobId; uint private yesCount; uint private noCount; bool private votingLive; mapping(address => bool) public voters; //only the contract owner should be able to start voting address payable owner; modifier onlyOwner { require(msg.sender == owner); _; } constructor() public { setPublicChainlinkToken(); owner = msg.sender; oraclePayment = 0.1 * 10 ** 18; // 0.1 LINK //Kovan alarm oracle oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e; jobId = "a7ab70d561d34eb49e9b1612fd2e044b"; //initialize votes yesCount = 0; noCount = 0; votingLive = false; } ``` 导入ChainlinkClient并使用“is”关键词继承,就可以轻松向Chainlink节点请求数据。然后,我们需要定义几个全局变量,其中包括计时器预言机的地址,地址可以在[Chainlinks (测试网)](https://docs.chain.link/docs/available-oracles#https://docs.chain.link/docs/available-oracles)页面上查看。 另外,我们还需要一个job spec ID和一些追踪投票的变量,并且将投票者地址mapping为一个布尔值,以确认地址是否投过票。最后,我们要定义一个修改器,规定只有合约所有者才有权限发送消息调用函数。然后在构造函数中将合约所有者地址以及其他变量一同初始化。注:mapping不需要初始化,布尔类型默认值是false。 ## 向Chainlink发送请求,启动投票: ```javascript function startVote(uint voteMinutes) public onlyOwner { Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector); req.addUint("until", now + voteMinutes * 1 minutes); //Start voting window then submit request to sleep for $voteMinutes votingLive = true; sendChainlinkRequestTo(oracle, req, oraclePayment); } //Callback for startVote request function fulfill(bytes32 _requestId) public recordChainlinkFulfillment(_requestId) { //$voteMinutes minutes have elapsed, stop voting votingLive = false; } ``` 我们合约的第一个函数startVote就需要向Chainlink发起请求。在这里我们用到了onlyOwner修改器,确保只有合约所有者才有权限发起投票。 我们在[《怎样将智能合约和API连接在一起》](https://blog.chain.link/apis-smart-contracts-and-how-to-connect-them/)一文中提到,ChainlinkClient合约导出一个Chainlink.Request的数据结构,这个数据结构可以根据具体需求变成不同的格式。示例中,我们在数据结构中添加的不是“get”请求,而是“until”。我们在构造函数中定义的Chainlink节点地址可以识别这个until字符串,节点收到请求后会在规定时间内暂停任务流水线(task pipeline)(注:规定时间指“voteMinutes”分钟时间)。 在提交请求前,我们需要将votingLive设置成true,即成功开启投票,投票函数会查看变量状态以允许或拒绝投票。然后我们就可以提交请求了,这时Chainlink节点会暂停任务流水线。这样,我们就能在until请求设定的时间结束后调用fulfill回调函数。当调用fulfill函数时,设定的投票时间已经结束,因此就可以将votingLive设置回false,将投票关闭。 综上所述,我们要先创建一个“until”请求,请求中包含规定时间,并将其发送至某一节点,节点在规定时间内暂停后再调用fulfill函数。Chainlink计时器/闹钟实现起来就是这么简单!我们使用简单的votingLive布尔逻辑,就可以在向Chainlink闹钟提交请求之前启动投票,并在暂停了一段时间后回调函数被调用时关闭投票。在这个例子中我们仅仅使用了一个布尔标识符,但是你可以在其基础上开发出其他的延时功能。 ## 进行投票并查看投票状态: ```javascript //Increments appropriate vote counter if voting is live function vote(bool voteCast) public { require(!voters[msg.sender], "Already voted!"); //if voting is live and address hasn't voted yet, count vote if(voteCast) {yesCount++;} if(!voteCast) {noCount++;} //address has voted, mark them as such voters[msg.sender] = true; } //Outputs current vote counts function getVotes() public view returns (uint yesVotes, uint noVotes) { return(yesCount, noCount); } //Lets user know if their vote has been counted function haveYouVoted() public view returns (bool) { return voters[msg.sender]; } ``` 这里就需要用到真正能实现投票的逻辑了。投票函数接受一个true/false的参数来检验用户是否已经投过票,如果验证通过,则计算投票,并将他们mapping中地址对应的值更新为true,表示他们已经投过票了。我们使用require表达式而不是if表达式来验证地址。这样一来,已经投过票的交易就会被拒绝。最后,由于以太坊交易不会返回结果,我们添加了一些简单的view函数来查看目前投票数量,并确认投票是否被统计。 希望本文能够让大家了解Chainlink在主网上的其中一个实用功能,并为你们的智能合约实现更大的价值。如果你想要了解Chainlink的其他功能,请关注Chainlink发布的可验证随机数生成工具[Chainlink VRF](https://blog.chain.link/verifiable-random-functions-vrf-random-number-generation-rng-feature/)、接入[Chainlink喂价机制](https://blog.chain.link/build-defi-yield-farming-application-with-chainlink/)开发流动性挖矿dApp或了解Chainlink预言机的最新研究成果[公允排序服务](https://blog.chain.link/chainlink-fair-sequencing-services-enabling-a-provably-fair-defi-ecosystem/)。 如果你是一名开发者,并希望快速将智能合约连接至链下数据和系统,请查看[我们的开发者文档](https://docs.chain.link/)并加入我们在[ Discord](https://discordapp.com/invite/aSK4zew)上的技术讨论群。如果你希望透过电话具体讨论集成细节,请点击[点击此处](https://chainlink.typeform.com/to/gEwrPO)联系我们。 [Website](https://chain.link/) | [Twitter](https://twitter.com/chainlink) |[ Reddit](https://www.reddit.com/r/Chainlink/) | [YouTube](https://www.youtube.com/channel/UCnjkrlqaWEBSnKZQ71gdyFA) | [Telegram](https://t.me/chainlinkofficial) | [Events](https://blog.chain.link/tag/events/) | [GitHub](https://github.com/smartcontractkit/chainlink) | [Price Feeds](https://feeds.chain.link/) | [DeFi](https://defi.chain.link/) [原文链接](https://blog.chain.link/blockchain-voting-using-a-chainlink-alarm-clock-oracle/)

手把手教你如何使用Chainlink闹钟在链上发起投票

手把手教你如何使用Chainlink闹钟在链上发起投票插图

以太坊上许多智能合约都需要通过外部计时器触发链上执行,因此dApp在链上发起投票时就会遇到问题,因为投票通常都有一个时间窗口。由于Solidity中没有计时器,因此智能合约的投票机制需要接入外部工具来启动或关闭投票窗口。

其实在传统系统中,计时功能并不难实现,系统可以设置NTP时间与操作系统时间同步,也可以用实物钟表或计时器计时,甚至还可以用sleep表达式在一段时间内暂停代码运行。Solidity智能合约中的事件由链下交易触发,也就是说以太坊等区块链需要接入链下闹钟触发事件或调用函数。而好消息是,开发者可以将Chainlink节点作为闹钟,可靠地触发智能合约执行。

本文将教大家如何在dApp中轻松实现投票时间控制。现在随着越来越多dApp开始走DAO路线,将更多权力交到用户手中,这种投票机制也变得愈发重要。具体步骤如下:

  • 将Chainlink功能包导入智能合约并继承

  • 格式化并提交Chainlink sleep(“until”)请求

  • 将合约所有者设置成唯一有权限发起投票的人

  • 使用简单的KYC确认每个地址只投一次票

  • 使用Getters函数查看投票状态

本示例中使用的代码可以在GitHub中查看,你还可以查看方便部署的Remix。

当然,这只是一个最基本的示例,你可以使用Chainlink“until”请求在任何时间点触发任何事件。这个示例只是一个初始框架,你可以在上面开发其他需要时间控制功能的智能合约。

合约定义和构造函数:

pragma solidity ^0.6.6;

import "github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol";

contract ChainlinkTimedVote is ChainlinkClient
{
  uint private oraclePayment;
  address private oracle;
  bytes32 private jobId;
  uint private yesCount;
  uint private noCount;
  bool private votingLive;
  mapping(address => bool) public voters;

  //only the contract owner should be able to start voting
  address payable owner;
  modifier onlyOwner {
  require(msg.sender == owner);
  _;
  }

  constructor() public {
      setPublicChainlinkToken();
      owner = msg.sender;
      oraclePayment = 0.1 * 10 ** 18; // 0.1 LINK
      //Kovan alarm oracle
      oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e; 
      jobId = "a7ab70d561d34eb49e9b1612fd2e044b";
      //initialize votes
      yesCount = 0;
      noCount = 0;
      votingLive = false;
  }

导入ChainlinkClient并使用“is”关键词继承,就可以轻松向Chainlink节点请求数据。然后,我们需要定义几个全局变量,其中包括计时器预言机的地址,地址可以在Chainlinks (测试网)页面上查看。

另外,我们还需要一个job spec ID和一些追踪投票的变量,并且将投票者地址mapping为一个布尔值,以确认地址是否投过票。最后,我们要定义一个修改器,规定只有合约所有者才有权限发送消息调用函数。然后在构造函数中将合约所有者地址以及其他变量一同初始化。注:mapping不需要初始化,布尔类型默认值是false。

向Chainlink发送请求,启动投票:

function startVote(uint voteMinutes) public onlyOwner {
      Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
      req.addUint("until", now + voteMinutes * 1 minutes);
      //Start voting window then submit request to sleep for $voteMinutes
      votingLive = true;
      sendChainlinkRequestTo(oracle, req, oraclePayment);
  }

  //Callback for startVote request
  function fulfill(bytes32 _requestId) public recordChainlinkFulfillment(_requestId) {
      //$voteMinutes minutes have elapsed, stop voting
      votingLive = false;
  }

我们合约的第一个函数startVote就需要向Chainlink发起请求。在这里我们用到了onlyOwner修改器,确保只有合约所有者才有权限发起投票。

我们在《怎样将智能合约和API连接在一起》一文中提到,ChainlinkClient合约导出一个Chainlink.Request的数据结构,这个数据结构可以根据具体需求变成不同的格式。示例中,我们在数据结构中添加的不是“get”请求,而是“until”。我们在构造函数中定义的Chainlink节点地址可以识别这个until字符串,节点收到请求后会在规定时间内暂停任务流水线(task pipeline)(注:规定时间指“voteMinutes”分钟时间)。

在提交请求前,我们需要将votingLive设置成true,即成功开启投票,投票函数会查看变量状态以允许或拒绝投票。然后我们就可以提交请求了,这时Chainlink节点会暂停任务流水线。这样,我们就能在until请求设定的时间结束后调用fulfill回调函数。当调用fulfill函数时,设定的投票时间已经结束,因此就可以将votingLive设置回false,将投票关闭。

综上所述,我们要先创建一个“until”请求,请求中包含规定时间,并将其发送至某一节点,节点在规定时间内暂停后再调用fulfill函数。Chainlink计时器/闹钟实现起来就是这么简单!我们使用简单的votingLive布尔逻辑,就可以在向Chainlink闹钟提交请求之前启动投票,并在暂停了一段时间后回调函数被调用时关闭投票。在这个例子中我们仅仅使用了一个布尔标识符,但是你可以在其基础上开发出其他的延时功能。

进行投票并查看投票状态:

//Increments appropriate vote counter if voting is live
  function vote(bool voteCast) public {
    require(!voters[msg.sender], "Already voted!");
    //if voting is live and address hasn't voted yet, count vote  
      if(voteCast) {yesCount++;}
      if(!voteCast) {noCount++;}
      //address has voted, mark them as such
      voters[msg.sender] = true;
   }

   //Outputs current vote counts
  function getVotes() public view returns (uint yesVotes, uint noVotes) {
      return(yesCount, noCount);
  }

  //Lets user know if their vote has been counted
  function haveYouVoted() public view returns (bool) {
      return voters[msg.sender];
  }

这里就需要用到真正能实现投票的逻辑了。投票函数接受一个true/false的参数来检验用户是否已经投过票,如果验证通过,则计算投票,并将他们mapping中地址对应的值更新为true,表示他们已经投过票了。我们使用require表达式而不是if表达式来验证地址。这样一来,已经投过票的交易就会被拒绝。最后,由于以太坊交易不会返回结果,我们添加了一些简单的view函数来查看目前投票数量,并确认投票是否被统计。

希望本文能够让大家了解Chainlink在主网上的其中一个实用功能,并为你们的智能合约实现更大的价值。如果你想要了解Chainlink的其他功能,请关注Chainlink发布的可验证随机数生成工具Chainlink VRF、接入Chainlink喂价机制开发流动性挖矿dApp或了解Chainlink预言机的最新研究成果公允排序服务。

如果你是一名开发者,并希望快速将智能合约连接至链下数据和系统,请查看我们的开发者文档并加入我们在 Discord上的技术讨论群。如果你希望透过电话具体讨论集成细节,请点击点击此处联系我们。

Website | Twitter | Reddit | YouTube | Telegram | Events | GitHub | Price Feeds | DeFi

原文链接

区块链技术网。

  • 发表于 2020-11-09 14:17
  • 阅读 ( 764 )
  • 学分 ( 16 )
  • 分类:预言机

评论