4.1 介绍
下面我们要介绍一个使用Solidity 编写的投票合约。该合约实现了一下功能。
- 选民登记
- 候选人提议登记
- 投票
- 委托第三方投票
- 票数统计,决选出候选人
该投票项目是一个公开透明的,不可作弊的工程。
4.2
//声明使用的Solidity 版本号
pragma solidity >=0.4.22 <0.6.0;
/// 投票程序
contract Ballot {
//使用结构体(STRUCT),声明一个选票对象。
struct Voter {
uint weight; // 选票的权重,1表示1票
bool voted; // 该选票是否已经投出
address delegate; // 该选票委托给第三方地址(如果空就是没有)
uint vote; // 该选票投给哪个提议
}
//使用结构体(STRUCT),声明一个【提议】
struct Proposal {
bytes32 name; // 32 位的提议名称
uint voteCount; // 总票数
}
//address 关键字代表一个以太坊地址
// 声明该项目的主持人。
address public chairperson;
//mapping 关键字代表一个 集合
// 声明以太坊地址(选民)和选票之间的映射
mapping(address => Voter) public voters;
// 【提议数组】 本投票程序的候选提议
Proposal[] public proposals;
// 构造函数 项目部署的时候 进行初始化设计,只运行一次
constructor(bytes32[] memory proposalNames) public {
// 项目部署者就是该项目的主持人。msg是以太坊关键字代表当前交易信息。
chairperson = msg.sender;
// 主持人也有投票的权力,将其
voters[chairperson].weight = 1;
// 讲传入的提议 初始化。
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` 函数 创建一个临时的提议对象
// `proposals.push(...)` 函数讲该提议放入proposals 末尾
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
// 选民登记
function giveRightToVote(address voter) public {
// require 关键字表示拦截,如果返回的结果是false 则执行终止,所有对状态和以太余额的更改将被还原
require(
msg.sender == chairperson,
"只有主持人,能进行选民登记."
);
require(
!voters[voter].voted,
"该选民存在,并且已经投票过"
);
// 该选民必须是从来没有登记过
require(voters[voter].weight == 0);
//登记选民,并且给予1票的权力
voters[voter].weight = 1;
}
// 投票
function vote(uint proposal) public {
//获取当前的自己的选票 storage 表示直接操作存储
Voter storage sender = voters[msg.sender];
//weight 不能等于0
require(sender.weight != 0, "Has no right to vote");
//voted 必须还没有投票过
require(!sender.voted, "Already voted.");
// 将选票设置为已投
sender.voted = true;
// 设置要投给哪个提议
sender.vote = proposal;
// 更新 该提议的票数
proposals[proposal].voteCount += sender.weight;
}
///选民可以将它们的选票委托给第三方机构来投票
function delegate(address to) public {
//获取当前的自己的选票 storage 表示直接操作存储
Voter storage sender = voters[msg.sender];
//voted 必须还没有投票过
require(!sender.voted, "You already voted.");
// 委托人(to) 不能是自己。
require(to != msg.sender, "Self-delegation is disallowed.");
// 这段话比较绕,就是你委托给A,A还有他的委托人B 那你的委托人就是B。知道没有委托人为止。 address(0) 表示0地址 。
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
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 {
//如果委托人还没投票 ,将委托人手上的票数+1
delegate_.weight += sender.weight;
}
}
/// 获取票数最多的提议 编号
/// view 表示调用该函数不需要触发交易。
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;
}
}
}
// 获取获胜协议的 名称
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
4.3 测试
部署时候的输入参数:
["0xca35b7d915458ef540ade6068dfe2f44e8fa733c","0xca35b7d915458ef540ade6068dfe2f44e8fa733c"]