Created
April 3, 2019 04:14
-
-
Save jordan-furr/da488bcdc40d5ae8617a762c8d1cb10a to your computer and use it in GitHub Desktop.
Tokenized Voting
This file contains 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
pragma solidity ^0.4.23; | |
contract Owned { | |
address public owner; | |
constructor () public { | |
owner = msg.sender; | |
} | |
modifier onlyOwner() { | |
assert(msg.sender == owner); | |
_; | |
} | |
function transferOwnership(address newOwner) external onlyOwner { | |
if (newOwner != address(0)) { | |
owner = newOwner; | |
} | |
} | |
} | |
//TODO: hashing definitely should be in here somewhere this is not secure in the slightest | |
contract ElectionPlatform is Owned { | |
Election[] elections; // store elections | |
Voter[] public voters; // hold voters | |
mapping (address => uint) voterID; | |
//hold info about election | |
struct Election { | |
string name; | |
Candidate[] candidates; // hold candidates | |
mapping (address => uint) candidateID; | |
mapping (address => uint) participants; // who has already participated | |
uint startDate; // time of election start | |
bool hasBeenStarted; // used so election can only be started once | |
uint endDate; // time election ends | |
bool finished; // has winner been computed | |
Candidate winner; // hold winner of election | |
} | |
// hold info about candidates | |
struct Candidate { | |
string name; | |
string party; | |
mapping (address => uint) supporters; | |
uint votes; | |
} | |
// hold info about registered voters | |
struct Voter { | |
string name; | |
string party; | |
} | |
modifier onlyVoters { | |
require(voterID[msg.sender] != 0); | |
_; | |
} | |
constructor() Owned() public { | |
//add 0 index empty voter | |
addVoter(0, "", ""); | |
// setup for demo | |
// create election with 2 candidates | |
addElection("US Presidential 2020"); | |
// add two candidates | |
addCandidate(0, 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c, "Donald Trump", "Republican"); | |
addCandidate(0, 0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db, "Kanye West", "Democrat"); | |
// add admin as a voter | |
addVoter(msg.sender, "admin", "Independent"); | |
} | |
// create a candidate | |
function addCandidate(uint targetElection, address targetCandidate, string name, string party) onlyOwner public { | |
//pull election | |
Election storage e = elections[targetElection]; | |
//check if election has already started | |
require(!e.hasBeenStarted, "election has already started"); | |
//check if candidate already exists | |
uint id = e.candidateID[targetCandidate]; | |
//if doesn't exist create | |
if(id == 0) { | |
e.candidateID[targetCandidate] = e.candidates.length; | |
id = e.candidates.length++; | |
} | |
// create or update struct info | |
Candidate storage c = e.candidates[id]; | |
c.name = name; | |
c.party = party; | |
} | |
//create a voter | |
function addVoter(address targetVoter, string name, string party) onlyOwner public { | |
//check if candidate already exists | |
uint id = voterID[targetVoter]; | |
//if doesn't exist create | |
if(id == 0) { | |
voterID[targetVoter] = voters.length; | |
id = voters.length++; | |
} | |
// create or update struct info | |
Voter storage v = voters[id]; | |
v.name = name; | |
v.party = party; | |
} | |
function addElection(string name) onlyOwner public { | |
uint id = elections.length++; | |
Election storage e = elections[id]; | |
e.name = name; | |
// add 0 index empty candidate | |
addCandidate(id, 0, "", ""); | |
} | |
//TODO: for front end, need getters for voter and candidate arrays | |
//allow owner to start election, gives a duration and sets the boolean | |
function startElection(uint targetElection, uint durationInMinutes) onlyOwner public { | |
//pull election | |
Election storage e = elections[targetElection]; | |
//check if has been started | |
require(!e.hasBeenStarted, "election has already been started"); | |
e.hasBeenStarted = true; | |
e.startDate = now; | |
e.endDate = e.startDate + durationInMinutes * 1 minutes; | |
} | |
//give the remaining time in minutes | |
function checkRemainingTime(uint targetElection) public view returns (uint) { | |
//pull election | |
Election storage e = elections[targetElection]; | |
require(e.hasBeenStarted, "election has not been started"); | |
if(now < e.endDate) { | |
return ((e.endDate - now) / 60) + 1; // plus 1 to round up | |
} else { | |
return 0; | |
} | |
} | |
// function to vote for a candidate | |
function vote(uint targetElection, address candidate) onlyVoters public { | |
//pull election | |
Election storage e = elections[targetElection]; | |
// make sure election has been started | |
require(e.hasBeenStarted, "election has not been started"); | |
// make sure election is still running | |
require(now < e.endDate, "time is up"); | |
//make sure candidate is valid | |
require(e.candidateID[candidate] != 0, "invalid candidate"); | |
// get that candidate | |
uint id = e.candidateID[candidate]; | |
Candidate storage c = e.candidates[id]; | |
//make sure voter is registered | |
require(voterID[msg.sender] != 0); | |
//check if voter has voted before, if so undo vote for that candidate | |
if (e.participants[msg.sender] == 1) { | |
uint i = 0; | |
Candidate storage oldC; | |
while(true) { | |
oldC = e.candidates[i]; | |
if(oldC.supporters[msg.sender] == 1){ | |
break; | |
} | |
i++; | |
} | |
oldC.supporters[msg.sender] = 0; | |
oldC.votes--; | |
} | |
//vote for candidate | |
c.supporters[msg.sender] = 1; | |
c.votes++; | |
e.participants[msg.sender] = 1; | |
} | |
// finish the election, first time computes winner after that just returns | |
function endElection(uint targetElection) public { | |
//get election | |
Election storage e = elections[targetElection]; | |
// make sure election has concluded | |
require(now > e.endDate && e.hasBeenStarted, "election hasn't finished"); | |
// make sure hasn't already been computed | |
require (!e.finished, "result already concluded"); | |
// compute and set winner | |
Candidate storage c; | |
e.winner = e.candidates[1]; | |
uint winning_votes = e.winner.votes; // will be used to check for ties | |
uint winner_index = 1; | |
for(uint i = 1; i < e.candidates.length; i++) { | |
c = e.candidates[i]; | |
if(c.votes > e.winner.votes) { | |
e.winner = c; | |
winning_votes = e.winner.votes; | |
winner_index = i; | |
} | |
} | |
// check for tie, create and return special tie candidate if so | |
for(i = 1; i < e.candidates.length; i++) { | |
c = e.candidates[i]; | |
if(c.votes == e.winner.votes && winner_index != i) { | |
e.hasBeenStarted = false; // allow use of function | |
addCandidate(targetElection, 0, "TIE", "TIE"); // indirectly makes only the owner able to end election | |
e.hasBeenStarted = true; | |
e.winner = e.candidates[e.candidates.length - 1]; | |
break; | |
} | |
} | |
e.finished = true; | |
} | |
// view result | |
function viewResults(uint targetElection) public view returns (string) { | |
//get election | |
Election storage e = elections[targetElection]; | |
// make sure result has been computed | |
require(e.finished, "not yet computed"); | |
//return winners name | |
return e.winner.name; | |
} | |
//view a candidate name | |
function viewCandidateName(uint targetElection, uint candidate) public view returns (string) { | |
Election storage e = elections[targetElection]; | |
Candidate storage c = e.candidates[candidate]; | |
return c.name; | |
} | |
//view a candidate party | |
function viewCandidateParty(uint targetElection, uint candidate) public view returns (string) { | |
Election storage e = elections[targetElection]; | |
Candidate storage c = e.candidates[candidate]; | |
return c.party; | |
} | |
//view a election name | |
function viewElectionName(uint targetElection) public view returns (string) { | |
Election storage e = elections[targetElection]; | |
return e.name; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment