diff --git a/src/Ballot.sol b/src/Ballot.sol index 637b4d982d21b4b2c01bec74188d5c6d11e93937..14cb9c0f0404daefc1dd6362685afe3f688fe423 100644 --- a/src/Ballot.sol +++ b/src/Ballot.sol @@ -1,139 +1,138 @@ -pragma solidity ^0.4.11; +// Basado en https://soliditycookbook.com/voting +// vim:syntax:filetype=javascript:ai:sm +// vim:expandtab:backspace=indent,eol,start:softtabstop=4 +pragma solidity ^0.4.25; /// @title Voting with 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 { - 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 + struct Voter { + address voter; + int vote; // index of the voted proposal or -1 if not voted } - // This is a type for a single proposal. - struct Proposal { - bytes32 name; // short name (up to 32 bytes) - uint voteCount; // number of accumulated votes + struct Proposal { + bytes32 name; // short name (up to 32 bytes) + uint voteCount; // number of accumulated votes + } + struct Votingrules { + string title; + address chairman; + uint voteAtOrAfter; + uint voteAtOrBefore; + // Which percentage of the registered voters must + // vote in order for the vote to be considered valid. + // e.g. 0.0 + float percentOfRegisteredVotersReqToBeValid; + // Which percentage of the cast votes are required + // for the motion/title to pass? + // MAJORITY VOTE: + // specify 50.00000000001 + // TWO-THIRDS: + // specify 67.77777777777 + float percentOfVotesCastToWin; + // Counting registered voters who do not vote as blank + // votes, has the effect that it is more difficult + // to acquire the desired votes. + bool countNonvotesAsBlanks; } - 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; + Votingrules public rules; + mapping( address => int ) public voterMap; + Voter[] public voterList; + uint public numvoters; + Proposal[] public proposalList; + uint public numproposals; /// Create a new ballot to choose one of `proposalNames`. - function Ballot(bytes32[] proposalNames) { - chairperson = msg.sender; - voters[chairperson].weight = 1; + constructor( + string ballotTitle, + uint voteAtOrAfter, + uint voteAtOrBefore, + float percentOfRegisteredVotersReqToBeValid, + float percentOfVotesCastToWin, + bool countNonvotesAsBlanks, + bytes32[] proposalNames + ) { + require( voteAtOrBefore > now ); + // chairman can not automatically vote. Chairman must + // giveRightToVote to himself if he wants to vote. + rules.chairman = msg.sender; + rules.title = ballotTitle; + rules.voteAtOrAfter = voteAtOrAfter; + rules.voteAtOrBefore = voteAtOrBefore; + rules.percentOfRegisteredVotersReqToBeValid = percentOfRegisteredVotersReqToBeValid, + rules.percentOfVotesCastToWin = percentOfVotesCastToWin, + rules.countNonvotesAsBlanks = countNonvotesAsBlanks, // 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++) { - // `Proposal({...})` creates a temporary - // Proposal object and `proposals.push(...)` - // appends it to the end of `proposals`. - proposals.push(Proposal({ - name: proposalNames[i], - voteCount: 0 - })); + numproposals = proposalNames.length; + int i = 0; + while ( i < numproposals ) + { + Proposal newprop; + newprop.name = proposalNames[i]; + newprop.voteCount = 0; + proposalList.push( newprop ); + i++; } } // Give `voter` the right to vote on this ballot. - // May only be called by `chairperson`. - function giveRightToVote(address voter) { - // If the argument of `require` evaluates to `false`, - // it terminates and reverts all changes to - // the state and to Ether balances. It is often - // a good idea to use this if functions are - // called incorrectly. But watch out, this - // will currently also consume all provided gas - // (this is planned to change in the future). - require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0)); - voters[voter].weight = 1; - } - - /// Delegate your vote to the voter `to`. - function delegate(address to) { - // assigns reference - Voter storage sender = voters[msg.sender]; - require(!sender.voted); - - // Self-delegation is not allowed. - require(to != msg.sender); - - // 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); - } - - // 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; - } - } - - /// Give your vote (including votes delegated to you) - /// to proposal `proposals[proposal].name`. - function vote(uint proposal) { - Voter storage sender = voters[msg.sender]; - require(!sender.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; + function giveRightToVote( address voter ) + { + // May only be called by chairman. + require( msg.sender == chairman ); + require( voteAtOrBefore <= now ); + uint idx = voterMap[voter]; + // Can't add voters more than once. + require( idx == 0 ); + // Not even the voter listed in [0]. + if ( voterList.length > 0 ) + require( voterList[0].voter != voter ); + // If the voter's address doesn't match, it is because + // he doesn't exist (and then we always have idx=0). + // So we push him onto the voterList. + Voter newvoter; + newvoter.voter = voter; + newvoter.vote = -1; + idx = voterList.push( newvoter ) - 1; + voterMap[voter] = idx; + numvoters++; } - /// @dev Computes the winning proposal taking all - /// previous votes into account. - function winningProposal() constant - returns (uint winningProposal) + function getVoterIdx( address voter ) + readonly + returns int { - uint winningVoteCount = 0; - for (uint p = 0; p < proposals.length; p++) { - if (proposals[p].voteCount > winningVoteCount) { - winningVoteCount = proposals[p].voteCount; - winningProposal = p; - } - } + int idx = voterMap[voter]; + if ( idx > 0 ) + return idx; + if ( voterList[0].voter == voter ) + return 0; + return -1; } - // 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() constant - returns (bytes32 winnerName) + /// Give your vote to proposal `proposals[proposal].name`. + function vote( uint proposal ) { - winnerName = proposals[winningProposal()].name; + require( proposal < numproposals ); + require( rules.voteAtOrAfter >= now ); + require( rules.voteAtOrBefore <= now ); + int idx = getVoterIdx( msg.sender ); + require( idx > -1 ); + int formervote = voterList[idx].vote; + require( formervote != int(proposal) ); + if ( formervote > -1 ) + { + // He changed his vote - this is normal for politicians, too. + proposals[ uint(formervote) ].voteCount--; + } + proposals[ proposal ].voteCount++; + voterList[ uint(idx) ].vote = uint(proposal); } }