@ -1,10 +1,13 @@
pragma solidity ^ 0 . 4 . 0 ;
/ / SPDX - License - Identifier : GPL - 3 . 0
/ / / @ title Voting with delegation .
pragma solidity >= 0 . 7 . 0 < 0 . 9 . 0 ;
/**
* @ title Ballot
* @ dev Implements voting process along with vote delegation
* /
contract Ballot {
contract Ballot {
/ / This declares a new complex type which will
/ / be used for variables later .
/ / It will represent a single voter .
struct Voter {
struct Voter {
uint weight ; / / weight is accumulated by delegation
uint weight ; / / weight is accumulated by delegation
bool voted ; / / if true , that person already voted
bool voted ; / / if true , that person already voted
@ -12,33 +15,31 @@ contract Ballot {
uint vote ; / / index of the voted proposal
uint vote ; / / index of the voted proposal
}
}
/ / This is a type for a single proposal .
struct Proposal {
struct Proposal {
/ / If you can limit the length to a certain number of bytes ,
/ / always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name ; / / short name ( up to 32 bytes )
bytes32 name ; / / short name ( up to 32 bytes )
uint voteCount ; / / number of accumulated votes
uint voteCount ; / / number of accumulated votes
}
}
address public chairperson ;
address public chairperson ;
/ / This declares a state variable that
/ / stores a \ ` Voter \ ` struct for each possible address .
mapping ( address => Voter ) public voters ;
mapping ( address => Voter ) public voters ;
/ / A dynamically - sized array of \ ` Proposal \ ` structs .
Proposal [ ] public proposals ;
Proposal [ ] public proposals ;
/ / / Create a new ballot to choose one of \ ` proposalNames \ ` .
/**
function Ballot ( bytes32 [ ] proposalNames ) {
* @ dev Create a new ballot to choose one of ' proposalNames ' .
* @ param proposalNames names of proposals
* /
constructor ( bytes32 [ ] memory proposalNames ) {
chairperson = msg . sender ;
chairperson = msg . sender ;
voters [ chairperson ] . weight = 1 ;
voters [ chairperson ] . weight = 1 ;
/ / For each of the provided proposal names ,
/ / create a new proposal object and add it
/ / to the end of the array .
for ( uint i = 0 ; i < proposalNames . length ; i ++ ) {
for ( uint i = 0 ; i < proposalNames . length ; i ++ ) {
/ / \ ` Proposal ( { . . . } ) \ ` creates a temporary
/ / ' Proposal({...}) ' creates a temporary
/ / Proposal object and \ ` proposals . push ( . . . ) \ `
/ / Proposal object and ' proposals.push(...) '
/ / appends it to the end of \ ` proposals \ ` .
/ / appends it to the end of ' proposals ' .
proposals . push ( Proposal ( {
proposals . push ( Proposal ( {
name : proposalNames [ i ] ,
name : proposalNames [ i ] ,
voteCount : 0
voteCount : 0
@ -46,98 +47,92 @@ contract Ballot {
}
}
}
}
/ / Give \ ` voter \ ` the right to vote on this ballot .
/**
/ / May only be called by \ ` chairperson \ ` .
* @ dev Give ' voter ' the right to vote on this ballot . May only be called by ' chairperson ' .
function giveRightToVote ( address voter ) {
* @ param voter address of voter
if ( msg . sender != chairperson || voters [ voter ] . voted ) {
* /
/ / \ ` throw \ ` terminates and reverts all changes to
function giveRightToVote ( address voter ) public {
/ / the state and to Ether balances . It is often
require (
/ / a good idea to use this if functions are
msg . sender == chairperson ,
/ / called incorrectly . But watch out , this
" Only chairperson can give right to vote. "
/ / will also consume all provided gas .
) ;
throw ;
require (
}
! voters [ voter ] . voted ,
" The voter already voted. "
) ;
require ( voters [ voter ] . weight == 0 ) ;
voters [ voter ] . weight = 1 ;
voters [ voter ] . weight = 1 ;
}
}
/ / / Delegate your vote to the voter \ ` to \ ` .
/**
function delegate ( address to ) {
* @ dev Delegate your vote to the voter ' to ' .
/ / assigns reference
* @ param to address to which vote is delegated
Voter sender = voters [ msg . sender ] ;
* /
if ( sender . voted )
function delegate ( address to ) public {
throw ;
Voter storage sender = voters [ msg . sender ] ;
require ( ! sender . voted , " You already voted. " ) ;
/ / Forward the delegation as long as
require ( to != msg . sender , " Self-delegation is disallowed. " ) ;
/ / \ ` to \ ` also delegated .
/ / In general , such loops are very dangerous ,
while ( voters [ to ] . delegate != address ( 0 ) ) {
/ / 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 ) &&
voters [ to ] . delegate != msg . sender
) {
to = voters [ to ] . delegate ;
to = voters [ to ] . delegate ;
}
/ / We found a loop in the delegation , not allowed .
/ / We found a loop in the delegation , not allowed .
if ( to == msg . sender ) {
require ( to != msg . sender , " Found loop in delegation. " ) ;
throw ;
}
}
/ / Since \ ` sender \ ` is a reference , this
/ / modifies \ ` voters [ msg . sender ] . voted \ `
sender . voted = true ;
sender . voted = true ;
sender . delegate = to ;
sender . delegate = to ;
Voter delegate = voters [ to ] ;
Voter storage delegate_ = voters [ to ] ;
if ( delegate . voted ) {
if ( delegate_ . voted ) {
/ / If the delegate already voted ,
/ / If the delegate already voted ,
/ / directly add to the number of votes
/ / directly add to the number of votes
proposals [ delegate . vote ] . voteCount += sender . weight ;
proposals [ delegate_ . vote ] . voteCount += sender . weight ;
} else {
} else {
/ / If the delegate did not vote yet ,
/ / If the delegate did not vote yet ,
/ / add to her weight .
/ / add to her weight .
delegate . weight += sender . weight ;
delegate_ . weight += sender . weight ;
}
}
}
}
/ / / Give your vote ( including votes delegated to you )
/**
/ / / to proposal \ ` proposals [ proposal ] . name \ ` .
* @ dev Give your vote ( including votes delegated to you ) to proposal ' proposals[proposal].name ' .
function vote ( uint proposal ) {
* @ param proposal index of proposal in the proposals array
Voter sender = voters [ msg . sender ] ;
* /
if ( sender . voted )
function vote ( uint proposal ) public {
throw ;
Voter storage sender = voters [ msg . sender ] ;
require ( sender . weight != 0 , " Has no right to vote " ) ;
require ( ! sender . voted , " Already voted. " ) ;
sender . voted = true ;
sender . voted = true ;
sender . vote = proposal ;
sender . vote = proposal ;
/ / If \ ` proposal \ ` is out of the range of the array ,
/ / If ' proposal ' is out of the range of the array ,
/ / this will throw automatically and revert all
/ / this will throw automatically and revert all
/ / changes .
/ / changes .
proposals [ proposal ] . voteCount += sender . weight ;
proposals [ proposal ] . voteCount += sender . weight ;
}
}
/ / / @ dev Computes the winning proposal taking all
/**
/ / / previous votes into account .
* @ dev Computes the winning proposal taking all previous votes into account .
function winningProposal ( ) constant
* @ return winningProposal_ index of winning proposal in the proposals array
returns ( uint winningProposal )
* /
function winningProposal ( ) public view
returns ( uint winningProposal_ )
{
{
uint winningVoteCount = 0 ;
uint winningVoteCount = 0 ;
for ( uint p = 0 ; p < proposals . length ; p ++ ) {
for ( uint p = 0 ; p < proposals . length ; p ++ ) {
if ( proposals [ p ] . voteCount > winningVoteCount ) {
if ( proposals [ p ] . voteCount > winningVoteCount ) {
winningVoteCount = proposals [ p ] . voteCount ;
winningVoteCount = proposals [ p ] . voteCount ;
winningProposal = p ;
winningProposal_ = p ;
}
}
}
}
}
}
/ / Calls winningProposal ( ) function to get the index
/**
/ / of the winner contained in the proposals array and then
* @ dev Calls winningProposal ( ) function to get the index of the winner contained in the proposals array and then
/ / returns the name of the winner
* @ return winnerName_ the name of the winner
function winnerName ( ) constant
* /
returns ( bytes32 winnerName )
function winnerName ( ) public view
returns ( bytes32 winnerName_ )
{
{
winnerName = proposals [ winningProposal ( ) ] . name ;
winnerName_ = proposals [ winningProposal ( ) ] . name ;
}
}
}
}