Created
October 10, 2017 15:29
-
-
Save DustinWehr/98c9310afab793588aa8e031f1d79aec to your computer and use it in GitHub Desktop.
ballot.v.py fixed but still simple
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Voting with delegation. | |
# Information about voters | |
voters: public({ | |
# weight is accumulated by delegation | |
weight: num, | |
# if true, that person already voted or delegated | |
voted: bool, | |
# person delegated to | |
delegate: address, | |
# index of the voted proposal. not meaningful unless `voted` is True. | |
vote: num | |
}[address]) | |
# This is a type for a list of proposals. | |
proposals: public({ | |
# short name (up to 32 bytes) | |
name: bytes32, | |
# number of accumulated votes | |
vote_count: num | |
}[num]) | |
voter_count: public(num) | |
chairperson: public(address) | |
@constant | |
def delegated(addr: address) -> bool: | |
return self.voters[addr].delegate != 0x0000000000000000000000000000000000000000 | |
@constant | |
def directly_voted(addr: address) -> bool: | |
return self.voters[addr].voted and self.voters[addr].delegate == 0x0000000000000000000000000000000000000000 | |
# Setup global variables | |
def __init__(_proposalNames: bytes32[5]): | |
self.chairperson = msg.sender | |
self.voter_count = 0 | |
for i in range(5): | |
self.proposals[i] = { | |
name: _proposalNames[i], | |
vote_count: 0 | |
} | |
# Give `voter` the right to vote on this ballot. | |
# May only be called by `chairperson`. | |
def give_right_to_vote(voter: address): | |
# Throws if sender is not chairperson | |
assert msg.sender == self.chairperson | |
# Throws if voter has already voted | |
assert not self.voters[voter].voted | |
# Throws if voter's voting weight isn't 0 | |
assert self.voters[voter].weight == 0 | |
self.voters[voter].weight = 1 | |
self.voter_count += 1 | |
# Used by `delegate`. Can be called by anyone. | |
def forward_weight(delegate_with_weight_to_forward: address): | |
assert self.delegated(delegate_with_weight_to_forward) | |
# Throw if there is nothing to do: | |
assert self.voters[delegate_with_weight_to_forward].weight > 0 | |
target = self.voters[delegate_with_weight_to_forward].delegate | |
for i in range(10): | |
if self.delegated(target): | |
target = self.voters[target].delegate | |
else: | |
# This effectively detects cycles of length <= 10. | |
# BUT cycles aren't actually problematic for correctness; | |
# they just result in spoiled votes. | |
# So, in the production version, this should instead be | |
# the responsibility of the contract's client, and this | |
# check should be removed. | |
assert target != delegate_with_weight_to_forward | |
# weight will be moved to someone who directly voted or | |
# hasn't voted | |
break | |
weight_to_forward = self.voters[delegate_with_weight_to_forward].weight | |
self.voters[delegate_with_weight_to_forward].weight = 0 | |
self.voters[target].weight += weight_to_forward | |
if self.directly_voted(target): | |
self.proposals[self.voters[target].vote].vote_count += weight_to_forward | |
self.voters[target].weight = 0 | |
# To reiterate: if target is also delegate, this function will need | |
# to be called again. | |
# Delegate your vote to the voter `to`. | |
def delegate(to: address): | |
# Throws if sender has already voted | |
assert not self.voters[msg.sender].voted | |
# Throws if sender tries to delegate their vote to themselves or to | |
# the default address valye of 0x0000000000000000000000000000000000000000 | |
assert to != msg.sender and to != 0x0000000000000000000000000000000000000000 | |
self.voters[msg.sender].delegate = to | |
self.voters[msg.sender].voted = True | |
# this call will throw iff this delegation would cause a loop | |
# of length <= 10 | |
self.forward_weight(msg.sender) | |
# Give your vote (including votes delegated to you) | |
# to proposal `proposals[proposal].name`. | |
def vote(proposal: num): | |
assert not self.voters[msg.sender].voted | |
self.voters[msg.sender].voted = True | |
self.voters[msg.sender].vote = proposal | |
# If `proposal` is out of the range of the array, | |
# this will throw automatically and revert all | |
# changes. | |
self.proposals[proposal].vote_count += self.voters[msg.sender].weight | |
# Computes the winning proposal taking all | |
# previous votes into account. | |
@constant | |
def winning_proposal() -> num: | |
winning_vote_count = 0 | |
for i in range(5): | |
if self.proposals[i].vote_count > winning_vote_count: | |
winning_vote_count = self.proposals[i].vote_count | |
winning_proposal = i | |
return winning_proposal | |
# Calls winning_proposal() function to get the index | |
# of the winner contained in the proposals array and then | |
# returns the name of the winner | |
@constant | |
def winner_name() -> bytes32: | |
return self.proposals[self.winning_proposal()].name |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment