5 c, N# k8 N" o+ V3 s
- pragma solidity >=0.7.0 <0.9.0;# J2 h! \, [ j( R8 K6 L3 P
- 8 k7 O5 q% C) v3 t( ~' D
- //投票实验. W" X5 j4 d# y
- contract Ballot{
- struct Voter{% k& S% x( O& R& U
- uint weight;//投票(单票)权重
- bool voted;//是否投过票,true为投过票,bool类型默认值为false
- address delegate;//想要委托投票的节点地址,address默认值为0x04 ?- F3 V) v% M6 V/ @9 h" Y2 D
- uint vote;//想要投票的节点的索引值(被投票节点信息用一维数组proposals存储)
- }
-
- struct Proposal{//被投票节点的相关参数+ L; w( _; M0 H: k- I0 i
- bytes32 name;//被投票节点的姓名标识
- uint voteCount;//累积被投票数
- }$ B4 U! w, E* V1 Y; K ]
- 6 g" f9 U; ^2 k* h- C
- address public chairperson;//投票管理员地址7 H1 a- G0 i1 {+ m$ m5 s( G
- mapping(address => Voter) public voters;//地址对投票节点的信息的映射
- Proposal[] public proposals;//一维数组存储被投票节点信息
-
- //构造方法、构造函数: C A: X) z$ z# f
- //solidity和其他语言不一样,构造函数仅在部署合约时调用一次,后续调用合约不调用其构造函数
- //且一个合约只能有一个构造函数,不能进行构造函数重载2 ?3 y" D8 [$ d3 e
- constructor(bytes32[] proposalNames) public{
- chairperson = msg.sender;//将第一次调用该合约的节点设置为管理员
- voters[chairperson].weight = 1;//将管理员投票权置为1
-
- for(uint i=0; i<proposalNames.length; i++){/ Z3 q, P2 r. Z0 m" v. X i( D
- //将所有被投票人姓名初始化进一维数组proposals,并将其对应票数初始化为0票
- //.push(),括号中内容需要强调数据类型,eg:arr.push(uint(6));3 g' n, X6 K) }1 m
- proposals.push(Proposal({, b! u, k, C- O/ B+ W; W# j
- name:proposalNames[i],- D4 R9 L6 B& [ r( G( L& K+ v6 b
- voteCount:0
- }));- X$ H" H4 G) ]; x" G$ n
- }
- }
- ' h& D5 f( y! X% B5 _& _
- //由管理员授权可投票节点
- function giveRightToVote(address voter) public{: \! ^5 C3 x1 c" z
- //require中判断条件为false时,输出字符串"xxx...",异常会被抛出,程序执行会被挂起," ]) F) K; B, k* F2 W9 m. {
- //未消耗的gas会被退回,合约状态会回退到初始状态
- require(
- msg.sender == chairperson,"Only chairperson can give right to vote."
- );//执行此function的节点一定为管理员节点8 @5 Y- h# k5 E& T: D% A( M1 k- [
- require(
- !voters[voter].voted,"The voter already voted."
- );//若voter没投过票! a8 x) S2 K4 Q1 ]# J
- require(voters[voter].weight == 0);
- //调用合约的人是管理员、待授权节点还没投过票、带授权节点投票权重为0时,进行授权
- voters[voter].weight = 1;//上述三个require()均成立时,授权票数/ K& z7 k6 a0 ^# J e
- }6 @7 e8 M" Q- @: j
- ( i" s, l- y& |! e L* ]
- //投票授权+ e& O: Y: F, C8 Q* s1 ^8 [) B
- function delegate(address to) public{( ?, D8 b* N% X7 j
- Voter storage sender = voters[msg.sender];
- require(!sender.voted, "You already voted.");3 P' U6 m# w( v" k; o
- require(to != msg.sender,"Self-delegation is disallowed.");
- //sender满足的条件:要有投票权限、没有投过票、被授权节点不是自己
- + F% s8 \4 |+ z0 _* \
- //判断代理节点地址是否为空:address(0)或者address(0x0)
- while(voters[to].delegate != address(0)){
- to = voters[to].delegate;//找到最终的代理节点
- require(to != msg.sender,"Found loop in delegation.");//若代理节点最终是自己则回退到初始状态
- }
- @4 v8 H0 w' p2 p3 p2 S
- sender.voted = true;//票权代理出去,状态改为已投票
- sender.delegate = to;//票权代理地址 R( X: s" A! E! `* m9 [, z+ a& E
- Voter storage delegate_ = voters[to];//取出代理节点状态
-
- //若代理节点已投过票,将新代理的票权投出去,反之则将代理节点票权加和 y/ y3 y' w* b4 {" f) W) u
- if(delegate_.voted){, g% Z% W0 q. Z; v$ v
- proposals[delegate_.vote].voteCount += sender.weight;
- }else{8 F' k: R6 b# T7 A% H
- delegate_.weight += sender.weight;
- }' t& D& d# I7 N8 ~; o8 O* R. b, ~
- }9 K4 \- O% V! a
-
- function vote(uint proposal) public{
- Voter storage sender = voters[msg.sender];//通过地址获取对应投票信息
- require(!sender.voted,"Already voted.");//若sender未投过票
- sender.voted = true;//更改投票状态为已投过票
- sender.vote = proposal;//保存已投票节点
- proposals[proposal].voteCount += sender.weight;//票权加和
- }8 {; w' h/ J7 C/ [* w+ P! [7 `9 j
-
- //返回票数最多的节点在一维数组proposals中的索引4 V; s$ w( T! C
- function winningProposal() public view returns(uint winningProposal_){! {0 p8 U( b. _3 B
- uint winningVoteCount = 0; [6 t% f5 s- |% |
- for(uint p=0;p<proposals.length;p++){* k* L9 B, y& B2 z8 q- _- X
- if(proposals[p].voteCount > winningVoteCount){
- winningVoteCount = proposals[p].voteCount;" H5 X+ e* z" }& A$ {
- winningProposal_ = p;
- }/ a3 s( L& j% c" m
- }
- }% L( A& P2 x9 u; {! j
- //输出票数最多的节点name
- function winnerName() public view returns(bytes32 winnerName_){
- winnerName_ = proposals[winningProposal()].name;
- }
- }