Solidity笔记三

2020/08/23 Solidity 共 3878 字,约 12 分钟

简单的投票合约Ballot

需求叙述

  • 这个合约展示了如何进行委托投票,同时计票又是自动和完全透明的
  • 为每个(投票)表决创建一份合约,然后作为合约的创造者-主席,将给予每个独立地址以投票权
  • 地址后面的人可以自己选择投票,或委托他们信任的人投票
  • 投票时间结束时,winingProposal()将返回获得最多投票的提案

Ballot智能合约源码

pragma solidity >=0.4.22 <0.6.0;
contract Ballot {
    struct Voter {
        uint weight;
        bool voted;
        uint8 vote;
        address delegate;
    }
    struct Proposal {
        uint voteCount;
    }
    address chairperson;
    mapping(address => Voter) voters;
    Proposal[] proposals;

    /// Create a new ballot with $(_numProposals) different proposals.
    constructor(uint8 _numProposals) public {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;
        proposals.length = _numProposals;
    }

    /// Give $(toVoter) the right to vote on this ballot.
    /// May only be called by $(chairperson).
    function giveRightToVote(address toVoter) public {
        if (msg.sender != chairperson || voters[toVoter].voted) return;
        voters[toVoter].weight = 1;
    }

    /// Delegate your vote to the voter $(to).
    function delegate(address to) public {
        Voter storage sender = voters[msg.sender]; // assigns reference
        if (sender.voted) return;
        while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
            to = voters[to].delegate;
        if (to == msg.sender) return;
        sender.voted = true;
        sender.delegate = to;
        Voter storage delegateTo = voters[to];
        if (delegateTo.voted)
            proposals[delegateTo.vote].voteCount += sender.weight;
        else
            delegateTo.weight += sender.weight;
    }

    /// Give a single vote to proposal $(toProposal).
    function vote(uint8 toProposal) public {
        Voter storage sender = voters[msg.sender];
        if (sender.voted || toProposal >= proposals.length) return;
        sender.voted = true;
        sender.vote = toProposal;
        proposals[toProposal].voteCount += sender.weight;
    }

    function winningProposal() public view returns (uint8 _winningProposal) {
        uint256 winningVoteCount = 0;
        for (uint8 prop = 0; prop < proposals.length; prop++)
            if (proposals[prop].voteCount > winningVoteCount) {
                winningVoteCount = proposals[prop].voteCount;
                _winningProposal = prop;
            }
    }
}

Ballot智能合约源码解读

  • 首先定义了两个结构体,Voter和Proposal:

     struct Voter {
          uint weight;
          bool voted;
          uint8 vote;
          address delegate;
      }
      struct Proposal {
          uint voteCount;
      }
    

    Voter是投票者结构体,包括:weight(投票权重,可以简单理解为票数),voted(指代是否投过票),vote(指代要投给哪个提案),delegate(代理的地址,可以让其他人代理投票)。 Proposal是提案结构体,voteCount指代提案的总投票数。

  • 之后定义了chairperson变量,一个地址到投票者的映射voters,即为每个投票者存储他们的状态,定义了一个Proposal结构类型的动态数组。
  • 在构造函数中,传入的参数是提案的数量,初始化主席,以及主席的投票权重,初始化提案的个数。
    constructor(uint8 _numProposals) public {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;
        proposals.length = _numProposals;
    }
  • 将投票权分发给投票者,这个函数只能主席进行调用。
    function giveRightToVote(address toVoter) public {
        if (msg.sender != chairperson || voters[toVoter].voted) return;
        voters[toVoter].weight = 1;
    }

首先判断调用者是否是主席,赋予投票权利的必须是主席,并且投票者必须是没有投过票,投票者才有票数(权重)

  • 之后定义代理函数,代理函数的功能是让其他投票者代理投票,代理函数的代码如下:
    function delegate(address to) public {
        Voter storage sender = voters[msg.sender]; // assigns reference
        if (sender.voted) return;
        while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
            to = voters[to].delegate;
        if (to == msg.sender) return;
        sender.voted = true;
        sender.delegate = to;
        Voter storage delegateTo = voters[to];
        if (delegateTo.voted)
            proposals[delegateTo.vote].voteCount += sender.weight;
        else
            delegateTo.weight += sender.weight;
    }

在这个函数中,传入的参数是代理投票者的地址,首先获得需要代理的投票者的状态,将相关信息存储到sender变量中,随后,要保证需要代理的投票者没有投过票。之后的while循环实现的是嵌套代理功能。因为代理投票者也有可能将代理权交由其他投票者进行代理,因此需要将代理的地址设为最内层代理投票者的地址,并且需要保证,代理的地址不为0地址且代理者不为需要代理者本身,之后再检查一下代理的投票者是不是投票者本身,如果是则返回,将需要代理的投票者的投票状态设为已投票,代理设为最后得到的代理地址,最后一句代理投票者的状态更新一下相关信息,如果代理投票者已经投过票了,将代理投票者投给的那个提案的票数相应地增加,增加的票数就是需要代理的投票者的票数(weight),如果代理投票者还没有投票,将代理投票者的票数增加相应的票数。

  • 之后定义投票函数,如下:
    function vote(uint8 toProposal) public {
        Voter storage sender = voters[msg.sender];
        if (sender.voted || toProposal >= proposals.length) return;
        sender.voted = true;
        sender.vote = toProposal;
        proposals[toProposal].voteCount += sender.weight;
    }

投票函数首先需要保证调用者没有投过票并且投给的那个提案有效,之后,改变调用函数的投票者的状态,更改提案的状态,即在投票给的那个提案上增加相应的票数。

  • 最后是判断哪个提案胜出的函数:
    function winningProposal() public view returns (uint8 _winningProposal) {
        uint256 winningVoteCount = 0;
        for (uint8 prop = 0; prop < proposals.length; prop++)
            if (proposals[prop].voteCount > winningVoteCount) {
                winningVoteCount = proposals[prop].voteCount;
                _winningProposal = prop;
            }
    }

这个函数的功能就是不断地遍历提案,选出提案里票数最多地提案,返回这个提案的提案号。

文档信息

Search

    Table of Contents