以太网 Solidity example - 投票

代码

pragma solidity ^0.4.11;

/**
    创建投票活动,传入选项名称
    授权某地址用户可以投票
    用户投票
    用户授权代理人
    查看投票结果

    需要一个投票选项(名称、票数)列表、一个投票人(地址=>{拥有的票数、代理人地址、是否已经投票、投给哪个选项})列表
*/
contract Ballot {
    // 投票人
    struct ballotVoter {
        uint delegateWeight; // 代理给的累计票数
        bool voteSpent;  // 是否已经投票
        address delegateTo; // 代理人地址
        uint voteIndex;   // 给谁投票,提案索引
    }
    // 提案
    struct Proposal {
        bytes32 proposalName;   // 提案名称
        uint voteCount; // 累计投票数
    }

    address public chairman; // 主席地址
    mapping(address => ballotVoter) public ballotVoters; // 投票人列表
    Proposal[] public proposalsOption; // 提案列表

    /// 构造函数
    // 创建人就是主席
    // 设置主席的票数=1
    // 遍历提案名称列表,构造提案,放入列表
    constructor(bytes32[] proposalNames) public {
        chairman = msg.sender;
        ballotVoters[chairman].delegateWeight = 1;
        // 对于每个提案名称,创建一个新的提案对象,放入列表
        for (uint i = 0; i < proposalNames.length; i++) {
            proposalsOption.push(Proposal({
                proposalName : proposalNames[i],
                voteCount : 0}));
        }
    }

    // 给某人分配投票权
    // 要求:1. 只有主席才能执行此动作;2. 此人没有投过票;3. 此人的票数为0
    // 加入投票人列表,并设置票数为1
    function giveVotingRights(address voter) public {
        require((msg.sender == chairman) && !ballotVoters[voter].voteSpent && (ballotVoters[voter].delegateWeight == 0));
        ballotVoters[voter].delegateWeight = 1;
    }
    /// 把投票权代理给某人
    // 要求:1. 调用者没有投过票;2. 调用者不是目标代理人
    function delegateTo(address to) public {

        ballotVoter storage sender = ballotVoters[msg.sender]; // 根据调用者地址取得对应的投票人对象
        require(!sender.voteSpent);
        require(to != msg.sender);

        // 当目标代理人的代理人地址不为空时时,目标代理人设置为代理人的代理人
        // 相当于一直往上找,一直找到没有代理人的
        while (ballotVoters[to].delegateTo != address(0)) {
            to = ballotVoters[to].delegateTo;
            require(to != msg.sender); // 调用发起人不能是代理人
        }

        sender.voteSpent = true; // 设置为已投票,不能再投了
        sender.delegateTo = to; // 设置代理人
        ballotVoter storage _delegateTo = ballotVoters[to]; // 取得代理人对应的投票人对象
        // 如果代理人已经投过票,增加代理人所投的那个提案的票数,票数为调用者的票数
        if (_delegateTo.voteSpent) {
            proposalsOption[_delegateTo.voteIndex].voteCount += sender.delegateWeight;
        }
        // 如果代理人还没投票,就把票数给代理人
        else {
            _delegateTo.delegateWeight += sender.delegateWeight;
        }
    }

    // 直接投票
    // 根据调用者地址取得投票人对象
    // 要求:还没有投过票
    // 设置投票人已经投过票、投的提案
    // 增加提案票数
    function voteIndex(uint proposal) public {
        ballotVoter storage sender = ballotVoters[msg.sender]; // 根据调用者地址,取得对应的投票人对象
        require(!sender.voteSpent); // 必须还没投过
        sender.voteSpent = true; // 设置已投票
        sender.voteIndex = proposal; // 设置投票提案的索引值

        proposalsOption[proposal].voteCount += sender.delegateWeight; // 增加目标提案的票数
    }

    // 遍历提案,找出票数最高的,返回胜出提案索引值
    function winnerProposal() public constant
    returns (uint _winnerProposal)
    {
        uint winnerVoteCount = 0;
        for (uint p = 0; p < proposalsOption.length; p++) {
            if (proposalsOption[p].voteCount > winnerVoteCount) {
                winnerVoteCount = proposalsOption[p].voteCount;
                _winnerProposal = p;
            }
        }

    }

    // 取得胜出提案名称
    function winner() public constant
    returns (bytes32 _winner)
    {
        _winner = proposalsOption[winnerProposal()].proposalName;
    }

}

测试

(1)发布

构造参数是 bytes32[] 填:

["0x6f7074696f6e4100000000000000000000000000000000000000000000000000","0x6f7074696f6e4200000000000000000000000000000000000000000000000000","0x6f7074696f6e4300000000000000000000000000000000000000000000000000","0x6f7074696f6e4400000000000000000000000000000000000000000000000000"]

(2)授权

从 Account 列表中复制出几个地址:

投票人地址:
0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db
0x583031d1113ad414f02576bd6afabfb302140225

分别填到 giveVotingRights 参数输入框中,并执行。

debugger中可以看到投票人数据,和提案数据:

(3)投票

把account切换到尾号为 160c 的账号,执行投票操作,voteIndex 参数中填写提案索引,例如 1

执行 winnerProposalwinner 查看效果:

debugger中也可以看到效果:

160cvoteIndex1,提案1voteCount1

(4)代理

把account切换到尾号为 2db 的账号,执行设置代理的操作,代理给 160c

debugger中查看:

2dbdelegateTo160c,提案1voteCount 变为2

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容