4.1 介紹
下面我們要介紹一個(gè)使用Solidity 編寫的投票合約。該合約實(shí)現(xiàn)了一下功能。
- 選民登記
- 候選人提議登記
- 投票
- 委托第三方投票
- 票數(shù)統(tǒng)計(jì),決選出候選人
該投票項(xiàng)目是一個(gè)公開透明的,不可作弊的工程。
4.2
//聲明使用的Solidity 版本號(hào)
pragma solidity >=0.4.22 <0.6.0;
/// 投票程序
contract Ballot {
//使用結(jié)構(gòu)體(STRUCT),聲明一個(gè)選票對(duì)象。
struct Voter {
uint weight; // 選票的權(quán)重,1表示1票
bool voted; // 該選票是否已經(jīng)投出
address delegate; // 該選票委托給第三方地址(如果空就是沒有)
uint vote; // 該選票投給哪個(gè)提議
}
//使用結(jié)構(gòu)體(STRUCT),聲明一個(gè)【提議】
struct Proposal {
bytes32 name; // 32 位的提議名稱
uint voteCount; // 總票數(shù)
}
//address 關(guān)鍵字代表一個(gè)以太坊地址
// 聲明該項(xiàng)目的主持人。
address public chairperson;
//mapping 關(guān)鍵字代表一個(gè) 集合
// 聲明以太坊地址(選民)和選票之間的映射
mapping(address => Voter) public voters;
// 【提議數(shù)組】 本投票程序的候選提議
Proposal[] public proposals;
// 構(gòu)造函數(shù) 項(xiàng)目部署的時(shí)候 進(jìn)行初始化設(shè)計(jì),只運(yùn)行一次
constructor(bytes32[] memory proposalNames) public {
// 項(xiàng)目部署者就是該項(xiàng)目的主持人。msg是以太坊關(guān)鍵字代表當(dāng)前交易信息。
chairperson = msg.sender;
// 主持人也有投票的權(quán)力,將其
voters[chairperson].weight = 1;
// 講傳入的提議 初始化。
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` 函數(shù) 創(chuàng)建一個(gè)臨時(shí)的提議對(duì)象
// `proposals.push(...)` 函數(shù)講該提議放入proposals 末尾
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
// 選民登記
function giveRightToVote(address voter) public {
// require 關(guān)鍵字表示攔截,如果返回的結(jié)果是false 則執(zhí)行終止,所有對(duì)狀態(tài)和以太余額的更改將被還原
require(
msg.sender == chairperson,
"只有主持人,能進(jìn)行選民登記."
);
require(
!voters[voter].voted,
"該選民存在,并且已經(jīng)投票過"
);
// 該選民必須是從來沒有登記過
require(voters[voter].weight == 0);
//登記選民,并且給予1票的權(quán)力
voters[voter].weight = 1;
}
// 投票
function vote(uint proposal) public {
//獲取當(dāng)前的自己的選票 storage 表示直接操作存儲(chǔ)
Voter storage sender = voters[msg.sender];
//weight 不能等于0
require(sender.weight != 0, "Has no right to vote");
//voted 必須還沒有投票過
require(!sender.voted, "Already voted.");
// 將選票設(shè)置為已投
sender.voted = true;
// 設(shè)置要投給哪個(gè)提議
sender.vote = proposal;
// 更新 該提議的票數(shù)
proposals[proposal].voteCount += sender.weight;
}
///選民可以將它們的選票委托給第三方機(jī)構(gòu)來投票
function delegate(address to) public {
//獲取當(dāng)前的自己的選票 storage 表示直接操作存儲(chǔ)
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.");
}
// 將本選票設(shè)置為已投
sender.voted = true;
//將該票委托為 委托人
sender.delegate = to;
Voter storage delegate_ = voters[to];
//如果委托人已經(jīng)投過票了
if (delegate_.voted) {
//直接將自己的票數(shù),記錄在委托人支持的提議上
proposals[delegate_.vote].voteCount += sender.weight;
} else {
//如果委托人還沒投票 ,將委托人手上的票數(shù)+1
delegate_.weight += sender.weight;
}
}
/// 獲取票數(shù)最多的提議 編號(hào)
/// view 表示調(diào)用該函數(shù)不需要觸發(fā)交易。
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;
}
}
}
// 獲取獲勝協(xié)議的 名稱
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
4.3 測(cè)試
部署時(shí)候的輸入?yún)?shù):
["0xca35b7d915458ef540ade6068dfe2f44e8fa733c","0xca35b7d915458ef540ade6068dfe2f44e8fa733c"]