6 \2 R* ^" ^2 r9 O
- pragma solidity >=0.7.0 <0.9.0;! l* n" M u8 @0 T3 J3 X' p
-
- //投票实验) g" _ ~0 O4 d" U( I* Y
- contract Ballot{$ V; _: K$ w! b
- struct Voter{3 ?& G7 w' u f/ M
- uint weight;//投票(单票)权重3 H. V7 F1 b) ^" W: U# G* K( B
- bool voted;//是否投过票,true为投过票,bool类型默认值为false
- address delegate;//想要委托投票的节点地址,address默认值为0x06 Y6 C! y/ Y5 B- e g5 X
- uint vote;//想要投票的节点的索引值(被投票节点信息用一维数组proposals存储)( p) W" x5 W5 Z" {9 b
- }, |3 b! \, F' o% D
- - W/ G+ D) n% E; |5 T& z4 L; |
- struct Proposal{//被投票节点的相关参数 {8 f4 D0 I+ C
- bytes32 name;//被投票节点的姓名标识6 Y' t- S1 |% r. J4 d+ |6 }( J+ [
- uint voteCount;//累积被投票数
- }( M B/ K, N% o" [+ {7 D
-
- address public chairperson;//投票管理员地址
- mapping(address => Voter) public voters;//地址对投票节点的信息的映射
- Proposal[] public proposals;//一维数组存储被投票节点信息
-
- //构造方法、构造函数5 a# j# u* ~; `8 M# t% ~/ J* C2 B
- //solidity和其他语言不一样,构造函数仅在部署合约时调用一次,后续调用合约不调用其构造函数
- //且一个合约只能有一个构造函数,不能进行构造函数重载. z3 t) Z% ?, @7 \) ?
- constructor(bytes32[] proposalNames) public{" m% z8 p, v3 K, j7 e8 r. b
- chairperson = msg.sender;//将第一次调用该合约的节点设置为管理员9 u+ e) a8 L2 u( A
- voters[chairperson].weight = 1;//将管理员投票权置为1
- ! }4 V7 m; `. K! n. w
- for(uint i=0; i<proposalNames.length; i++){
- //将所有被投票人姓名初始化进一维数组proposals,并将其对应票数初始化为0票8 R! M/ t' E6 \
- //.push(),括号中内容需要强调数据类型,eg:arr.push(uint(6));$ X( p: Z3 Y( Q3 N
- proposals.push(Proposal({
- name:proposalNames[i],
- voteCount:0
- }));
- }# I/ D3 C+ r* ] v/ Y) N
- }
-
- //由管理员授权可投票节点
- function giveRightToVote(address voter) public{) D5 U) g. Y: j; \) |
- //require中判断条件为false时,输出字符串"xxx...",异常会被抛出,程序执行会被挂起,
- //未消耗的gas会被退回,合约状态会回退到初始状态$ F' i1 h/ S. X% W9 Z* w
- require(% K- U! C8 i5 O; Z B4 ?8 ]8 S: ^! F7 `
- msg.sender == chairperson,"Only chairperson can give right to vote.". X, w* k' w& D7 E8 w
- );//执行此function的节点一定为管理员节点
- require(
- !voters[voter].voted,"The voter already voted."7 V( h7 V" a) o9 O; v; E
- );//若voter没投过票& b \9 w1 S Z" L1 Q: R6 W4 `4 X, w
- require(voters[voter].weight == 0);6 ?- Y8 g& T4 a$ K7 I
- //调用合约的人是管理员、待授权节点还没投过票、带授权节点投票权重为0时,进行授权 G) R5 }0 m# N, k1 y3 w
- voters[voter].weight = 1;//上述三个require()均成立时,授权票数
- }
- : C" T7 n/ G( p% A' g
- //投票授权
- function delegate(address to) public{
- Voter storage sender = voters[msg.sender];+ a! D8 r s- U
- require(!sender.voted, "You already voted.");% t6 @0 I) N3 y4 U* b# q
- require(to != msg.sender,"Self-delegation is disallowed.");
- //sender满足的条件:要有投票权限、没有投过票、被授权节点不是自己$ i1 r1 W7 m3 u
-
- //判断代理节点地址是否为空:address(0)或者address(0x0)1 p- o- x. e8 _# e# r/ w# B$ |
- while(voters[to].delegate != address(0)){% f$ x4 ]: h# t7 F9 m, f
- to = voters[to].delegate;//找到最终的代理节点
- require(to != msg.sender,"Found loop in delegation.");//若代理节点最终是自己则回退到初始状态
- }" s' }6 Q2 |" e, u5 L! ]
- ; w! x3 N; d: w
- sender.voted = true;//票权代理出去,状态改为已投票! o5 _& x2 A4 T; N6 D
- sender.delegate = to;//票权代理地址: a6 t4 x$ `' T) i! @
- Voter storage delegate_ = voters[to];//取出代理节点状态
-
- //若代理节点已投过票,将新代理的票权投出去,反之则将代理节点票权加和2 P3 Y2 K3 I& y
- if(delegate_.voted){
- proposals[delegate_.vote].voteCount += sender.weight;! S, H0 p, f$ O8 K. W4 w# @
- }else{
- delegate_.weight += sender.weight;
- }. o) ^2 J) U6 g4 `
- }; |# { `( ^* |# z9 f2 `1 g
- & b2 B8 x" Z8 J' P: z
- function vote(uint proposal) public{6 ^- N- U) p; Q& K% O1 ]
- Voter storage sender = voters[msg.sender];//通过地址获取对应投票信息) i3 G8 i" Y1 n
- require(!sender.voted,"Already voted.");//若sender未投过票
- sender.voted = true;//更改投票状态为已投过票
- sender.vote = proposal;//保存已投票节点
- proposals[proposal].voteCount += sender.weight;//票权加和
- }
-
- //返回票数最多的节点在一维数组proposals中的索引
- function winningProposal() public view returns(uint winningProposal_){7 I7 K- W2 X9 c; w! F! T) G
- uint winningVoteCount = 0;
- for(uint p=0;p<proposals.length;p++){
- if(proposals[p].voteCount > winningVoteCount){
- winningVoteCount = proposals[p].voteCount;* C3 w8 ?9 g! v- T5 [5 ~* w
- winningProposal_ = p;
- }
- }& \& M2 j2 u+ N, ]: N
- }
- //输出票数最多的节点name$ y* Z; N9 u' q9 ?
- function winnerName() public view returns(bytes32 winnerName_){6 m' I/ s5 A" c- n
- winnerName_ = proposals[winningProposal()].name;
- }
- }