智能合约实现简单投票系统
合约不难,在remix在线IDE中默认就会有这个投票合约,但是这里的是自己写的与其可能有所不同。整个合约较难的部分在于代理投票防止循环代理,这也是这个合约的重点吧。(相关代码有详细的解释)
ps:老版本的remix中的ballot有许多问题与错误,建议看新版本的。
// 从所有的提案中找出最大投票数的提案,作为最终的方案。
pragma solidity >=0.4.22;
contract Myballot{
//投票人
struct Voter{
bool voted; //是否投票
uint weight; //权重
uint8 vote_id; //投给哪个提案id,就是数组下标
address delegate; //投票权委托人
}
//提案
struct Proposal{
string name; //提案名字
string content; //提案内容
uint voteCount; //被投票数
bool enable; //启动
}
//主席
address public chairperson;
//可变长度数组,放置所有提案,下标与提案id对应,不用mapping的原因是方便遍历。
Proposal[] public proposals;
//当前的提案数
uint public nowNumProposal;
//Uint8的最大值
uint public UINT8_MAX_VALUE = (2**8)-1;
//投票人映射
mapping(address=>Voter) public voters;
constructor(uint _chairpersonWight) public{
//设置主席的初始参数
chairperson = msg.sender;
voters[chairperson].weight = _chairpersonWight;
//定义提案数组长度
// +1 是因为默认值0的数组位置不给用,从1开始
proposals.length = UINT8_MAX_VALUE + 1;
}
//提案申请
function applyProposal(string _proposalName, string _proposalContent) public{
//检查提案数量是否已满
require(nowNumProposal < UINT8_MAX_VALUE, "提案已满");
//提案数加1
++nowNumProposal;
//录入提案信息
proposals[nowNumProposal].name = _proposalName;
proposals[nowNumProposal].content = _proposalContent;
proposals[nowNumProposal].enable = true;
}
//仅主席可调用
modifier Onlychairperson{
require(msg.sender == chairperson, "仅主席可操作");
_;
}
//检查某人是否还有投票权
modifier haveVoted(address someone){
require(voters[someone].voted == false, "投票权已使用");
_;
}
//授权投票人
function authorizedVote(address _voter, uint _weight) public Onlychairperson haveVoted(_voter){
require(_weight >= 0 && _weight <= 100, "投票权重范围超出");
require(voters[_voter].weight == 0, "只允许赋权一次");
voters[_voter].weight = _weight;
}
//代理投票
function delegateVoting(address _to) public haveVoted(msg.sender){
//重点!!!
//代理会出现多重代理,要使用while找到目的地
require(_to != msg.sender, "不能将代理权给自己"); //设置后所有人都不可能自循环
//循环的目的:找到多重代理的最终者
//继续迭代条件(while条件):
//①代理者的下一个代理人存在 voters[_to].delegate != address(0)
//②不能代理回之前的代理人(造成环)若成环那么环中的人一定会调回到自己,后面的判断就生效 voters[_to].delegate != msg.sender
//③不能自循环(上面已保证)
while(voters[_to].delegate != address(0) && voters[_to].delegate != msg.sender)
_to = voters[_to].delegate; //迭代,将代理人的代理变为下一个代理_to
require(voters[_to].delegate != msg.sender, "不可循环代理");
//表示代理
voters[msg.sender].delegate = _to;
if(voters[_to].voted){
//如果代理人已经投过票了,那么就把权重累加到已投的提案
proposals[voters[_to].vote_id].voteCount += voters[msg.sender].weight;
voters[msg.sender].vote_id = voters[_to].vote_id;
}else{
//没投那么就把权重交给代理人
voters[_to].weight += voters[msg.sender].weight;
}
voters[msg.sender].voted = true;
}
//投票函数
function vote(uint8 _proposalID) public haveVoted(msg.sender){
require(_proposalID >= 0 && _proposalID <= UINT8_MAX_VALUE, "投票提案ID范围超出");
require(proposals[_proposalID].enable, "提案未开启");
proposals[_proposalID].voteCount += voters[msg.sender].weight;
voters[msg.sender].vote_id = _proposalID;
voters[msg.sender].voted = true;
}
//当前获胜提案
function winningProposal() public view returns(string name, uint maxProposalID, uint maxVoteCount){
require(nowNumProposal > 0, "当前无提案");
for(uint i = 1; i <= nowNumProposal; i++){
if(proposals[i].voteCount > maxVoteCount){
maxVoteCount = proposals[i].voteCount;
maxProposalID = i;
name = proposals[i].name;
}
}
require(maxVoteCount > 0, "当前无提案被投票");
}
}
基本的功能测试没有问题,但是还有许多问题尚待解决,例如先代理投票给没投过票的人,主席再授权权重就会报错,因为weight不是0了。还有就是代理投票给没投过票的人vote—id是没法写的,因为不知道代理人什么时候投、投给谁。
这些细节没有继续深究,感兴趣的可以继续完善。
博主原创文章,未经许可禁止转载
更多文章请访问博主个人博客:https://myblog.gumptlu.work/