智能合约实现简单投票系统

智能合约实现简单投票系统

合约不难,在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/

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxx_undefined

谢谢请博主吃糖

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值