Solidity 入门教程之 Ballot(投票) 案例学习

在 remix 中有三个例子这是第3个Ballot案例,看这篇之前,请先确定看过之前的三篇。

这个投票案例,主要实现几个功能。

  • 投票
  • 委托投票给另一个地址
  • 设置是否有投票的权利
  • 计算投票获胜的选项

源码如下

pragma solidity >=0.7.0 <0.8.0;
 /** 
 @title Ballot
 @dev Implements voting process along with vote delegation
 */
 contract Ballot {

   struct Voter {
     uint weight; // 授权权重
     bool voted;  // 如果是true,表示已经投票
     address delegate; // 委托帮投
     uint vote;   // 已投票提案的索引
   }

   struct Proposal {
     bytes32 name;   // 提案的名称
     uint voteCount; // 累计票数
   }

 address public chairperson;
 mapping(address => Voter) public voters;
 Proposal[] public proposals;


 /** 
 @dev 创建一个投票选项 'proposalNames'.
 @param proposalNames names of proposals
 */
 constructor(bytes32[] memory proposalNames) {
   chairperson = msg.sender;
   voters[chairperson].weight = 1;
   for (uint i = 0; i < proposalNames.length; i++) {
     proposals.push(Proposal({
         name: proposalNames[i],
         voteCount: 0
     }));
  }
 }

 /** 
 @dev 给投票的权利(发选票)
 @param voter address of voter
 */
 function giveRightToVote(address voter) public {
   require(
     msg.sender == chairperson,
     "Only chairperson can give right to vote."
   );
   require(
     !voters[voter].voted,
     "The voter already voted."
   );
   require(voters[voter].weight == 0); 
   voters[voter].weight = 1;
 }

 /**
 @dev 把票委托给别人
 @param to address to which vote is delegated
 */
 function delegate(address to) public {
   Voter storage sender = voters[msg.sender];
   require(!sender.voted, "You already voted.");
   require(to != msg.sender, "Self-delegation is disallowed.");
   while (voters[to].delegate != address(0)) {
     to = voters[to].delegate;
    // 不允许循环委托 
    require(to != msg.sender, "Found loop in delegation.");
   }
   sender.voted = true;
   sender.delegate = to;
   Voter storage delegate_ = voters[to];
   if (delegate_.voted) {
     // 如果被委托者已经投票了,则直接添加到票数中
     proposals[delegate_.vote].voteCount += sender.weight;
   } else {
     // 如果被委托者还没有投票,则添加被委托者的权重
     delegate_.weight += sender.weight;
   }
 }

 /**
 @dev 投票,包括被委托的票
 @param proposal index of proposal in the proposals array
 */
 function vote(uint proposal) public {
   Voter storage sender = voters[msg.sender];
   require(sender.weight != 0, "Has no right to vote");
   require(!sender.voted, "Already voted.");
   sender.voted = true;
   sender.vote = proposal;
   // 投票的索引超出可投票选项的范围,则会自动抛出并还原所有修改
   proposals[proposal].voteCount += sender.weight;
 }

 /** 
 @dev 计算获胜的提案
 @return winningProposal_ index of winning proposal in the proposals array
 */
 function winningProposal() public view
     returns (uint winningProposal_)
 {
   uint winningVoteCount = 0;
   for (uint p = 0; p < proposals.length; p++) {     
     if (proposals[p].voteCount > winningVoteCount) {
         winningVoteCount = proposals[p].voteCount;
         winningProposal_ = p;
     }
 }
 }

 /** 
 @dev 获取投票数最多的提案
 @return winnerName_ the name of the winner
 */
 function winnerName() public view
     returns (bytes32 winnerName_)
  {
    winnerName_ = proposals[winningProposal()].name;
  }
 }  

下面开始分析新的知识点

struct 结构体,复杂的数据类型,有多个属性。类似于 golang 的结构体。

mapping 映射,一种数据类型,类似 python 的字典,php 的关联数组,javascript的对象。

Proposal[] 定义数组的方式,solidity 是一种静态类型语言,数组定义的方式跟其他语言一样。往数组追加数据的方法是 push

memorystorage 是存储变量的两种方式,storage 指的是永久存储在区块链中的数据,memory 是临时变量,当调用完成时即被移除。这两个相当于我们电脑的硬盘和内存。

address(0) 表示 address 的初始值,一般用于判断 address 是否已赋值。

总结

这个案例新的知识点有,struct 结构体、mapping 映射、数组、memorystorageaddress 的初始值 address(0)

Tips

部署的时候,需要 bytes32[]数据,输入以下数组测试

["0xd4967590eb024589dfb6b9e48a576eb49ebc19d764b0d1d67dc21975e7258e97",
 "0x0000000000000000000000000000000000000000000000000000000000000001",
 "0x0000000000000000000000000000000000000000000000000000000000000002",
 "0x0000000000000000000000000000000000000000000000000000000000000003",
 "0x065e0be95fb43db528a20ba65c0e575e33cd4a9e1ca089dba4efff24596e8553"]
Leave a Reply

Your email address will not be published. Required fields are marked *