Solidity实现投票功能

使用solidity实现基本的投票并且去操作功能,像是投票,发票或是委托票等。

创建contract
https://ithelp.ithome.com.tw/upload/images/20220426/20108931aO5ZWnPDoI.png

Solidity by Example — Solidity 0.8.10 documentation (soliditylang.org)

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/// @title Voting with delegation.
contract Voting {
    
    // 选民
    struct Voter {
        uint weight; // weight is accumulated by delegation
        bool voted;  // if true, that person already voted
        address delegate; // 被委托人 person delegated to
        uint vote;   // 投票提案的索引 index of the voted proposal
    }

    // This is a type for a single proposal.
    struct Proposal {
        string name;   // short name (up to 32 bytes)
        uint voteCount; // number of accumulated votes
    }

    address public chairperson;

    // This declares a state variable that
    // stores a `Voter` struct for each possible address.
    mapping(address => Voter) public voters;

    // A dynamically-sized array of `Proposal` structs.
    Proposal[] public proposals;

    // 为`proposalNames` 中的每个提案,创建一个新投票表决 Create a new ballot to choose one of `proposalNames`.
    constructor(string[] memory proposalNames) {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;

        // 对每一提案名称,创建一个新的Proposal并添加到array
        for (uint i = 0; i < proposalNames.length; i++) {
            // `Proposal({...})` creates a temporary
            // Proposal object and `proposals.push(...)`
            // appends it to the end of `proposals`.
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
    }

    // 授权voter对这个表决进行投票,只有chairperson能调用此函数
    function giveRightToVote(address voter) external {
        // If the first argument of `require` evaluates
        // to `false`, execution terminates and all
        // changes to the state and to Ether balances
        // are reverted.
        // This used to consume all gas in old EVM versions, but
        // not anymore.
        // It is often a good idea to use `require` to check if
        // functions are called correctly.
        // As a second argument, you can also provide an
        // explanation about what went wrong.
        require(
            msg.sender == chairperson,
            "Only chairperson can give right to vote."
        );
        require(
            !voters[voter].voted,
            "The voter already voted."
        );
        require(voters[voter].weight == 0);
        voters[voter].weight = 1;
    }

    /// 将您的投票委托给选民 voter `to`.
    function delegate(address to) external {
        // assigns reference
        Voter storage sender = voters[msg.sender];
        require(!sender.voted, "You already voted.");

        require(to != msg.sender, "Self-delegation is disallowed.");

        // Forward the delegation as long as
        // `to` also delegated.
        // In general, such loops are very dangerous,
        // because if they run too long, they might
        // need more gas than is available in a block.
        // In this case, the delegation will not be executed,
        // but in other situations, such loops might
        // cause a contract to get "stuck" completely.
        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.");
        }

        // Since `sender` is a reference, this modifies `voters[msg.sender].voted`
        sender.voted = true;
        sender.delegate = to;
        Voter storage delegate_ = voters[to];
        if (delegate_.voted) {
            // If the delegate already voted,
            // directly add to the number of votes
            proposals[delegate_.vote].voteCount += sender.weight;
        } else {
            // If the delegate did not vote yet,
            // add to her weight.
            delegate_.weight += sender.weight;
        }
    }

    /// 把你的票(包括委托给你的票),投给提案`proposals[proposal].name`.
    function vote(uint proposal) external {
        Voter storage sender = voters[msg.sender];
        require(sender.weight != 0, "Has no right to vote");
        require(!sender.voted, "Already voted.");
        sender.voted = true;
        sender.vote = proposal;

        // If `proposal` is out of the range of the array,
        // this will throw automatically and revert all
        // changes.
        proposals[proposal].voteCount += sender.weight;
    }

    /// @dev Computes the winning proposal taking all
    /// previous votes into account.
    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;
            }
        }
    }

    // Calls winningProposal() function to get the index
    // of the winner contained in the proposals array and then
    // returns the name of the winner
    function winnerName() external view
            returns (string memory winnerName_)
    {
        winnerName_ = proposals[winningProposal()].name;
    }
}

测试

启动节点模拟

使用Remix

Compile
6836d7755c7f47e9d9968d03d963102f.png

Deploy合约
60dcbda1e6fb9b626bf32a42971da503.png

Output:
01a42accd383115e71bb7f2a0cc666ca.png

测试环节

测试Proposals
c41a256adb6ddb4d5e74e7f4eb0d41ad.png
ec2ace9a949252c68b2b6828906e45eb.png
结果
7cf338dc0710f7c6d010bab3fd485e1b.png

chairperson
e7b48c98bf1819e87e938545b0a6b3be.png
f3ddc2c074769fe9d3691d4f3d323f7c.png

chairperson(0xac55502aF00E1F405f115C17F45D43c36C47F7C3)给其他人票:
b5747b3d057a8069cf752a86eedbfee6.png

91248b3a2723be21181060f45997dc92.png
dfa40f2058113e31c7db89516cbe4b10.png
其他也是:
0xF197FFB0422d7026809210826325bfa719105251
0x7CB7374abE10dDd7e16b3dfe5E68623DAb1b13F3
0x9AD3eB9367f6B123a843AcF946e4D360c445FB9d

测试非Chairperson给其他人票:
28a7f09e7a7942092216d701d825690a
出现ERROR:
6b2de3ffc6f39cc46ee968bc366dd34d
Output
bf38ca023c06dd9a531fd6627694ed6b

测试Voter:
ffbd08e166bbddb3e22d921110c33de6.png
f25eef82869bd061a8a2d70e0e7635e3.png
5c7d3c18e0bd0498e8b4f0178b6d4426.png

接着切换帐户:
0xF197FFB0422d7026809210826325bfa719105251
将自己的票委托给他人(0x7CB7374abE10dDd7e16b3dfe5E68623DAb1b13F3):
7782111f4b8d5767b184026a0b427738.png
00f86906c6a8040498549947acd013bc.png

使用看Voter函数来观察:
587cf0d82be3660e1facb6a451814760.png

测试投票:
0xF197FFB0422d7026809210826325bfa719105251
5df311b3ed3db7bd7221dca18795fd1a.png
2ffce6578af5e74817d11de5550720cd.png
(我们把票给别人了,无法投,并且先看一下propsal)
9260e0e053c89ee645efeca41703bfba.png
能看到没有投成功
834c890042afc1541f94fc1f328d9add.png

切换成其他人开始投票:
0x7CB7374abE10dDd7e16b3dfe5E68623DAb1b13F3
0x9AD3eB9367f6B123a843AcF946e4D360c445FB9d
执行vote
76a1125299a3e9dfa09f0873a9438c8c.png
5ff749732431754ff317ebb3d246e551.png
接着看proposal(Alice, Bob):
02953c4bf5ce86b50cea475500955786.png

呼叫WINNERNAME和WINNINGPROPOSAL
2297887d4239991f95b92da765d5e315.png
bee24a7df5dea92ab6e26c8bcb0c0ca7.png

合约内的东西都测试完毕


Reference:

Solidity by Example


欢迎大家来我的Blog看:

1.Blog: 文章连结


<<:  如何清除 iMac/Macbook 上所有资料?--〖2022亲测有用〗

>>:  利用大数据分析预测MLB胜负(中)

【PHP Telegram Bot】Day11 - Webhook 与 Web Hosting

网站服务器 网路上有很多的免费服务器(Free Hosting) 几乎每个都有支援 PHP 我就拿 ...

【Day 03】第一个小程序

今天,就让我们写一个小程序,向 C 语言这个世界打招呼吧! #include<stdio.h&...

Day 1 介绍测试框架 RSpec

该文章同步发布於:我的部落格 RSpec 是什麽? 是一款在 2005 年释出的开放原始码的测试函...

【Day17】Uart_TX 状态机的实现

Uart 是什麽? UART(Universal Asynchronous Receiver/Tra...

Day23 - Shioaji X Backtesting - RSI低买高卖策略

好啦!之前介绍过了如何用Backtesting套件来实做均线的策略, 前面也介绍过了如何安装Ta-l...