Skip to content

Commit

Permalink
Token and reputation voting WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Jun 13, 2019
1 parent 9fe2f2b commit 0f57fca
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 0 deletions.
57 changes: 57 additions & 0 deletions contracts/extensions/VotingBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
This file is part of The Colony Network.
The Colony Network is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The Colony Network is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with The Colony Network. If not, see <http://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.8;
pragma experimental ABIEncoderV2;

import "../../lib/dappsys/math.sol";
import "../IColony.sol";
import "../IColonyNetwork.sol";


contract VotingBase is DSMath {

uint64 constant REVEAL_PERIOD = 2 days;

enum PollState { Open, Reveal, Closed }

IColony colony;
IColonyNetwork colonyNetwork;

constructor(address _colony) public {
colony = IColony(_colony);
colonyNetwork = IColonyNetwork(colony.getColonyNetwork());
}

function getVoteSecret(bytes32 _salt, uint64 _vote) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_salt, _vote));
}

function getPollState(uint64 _pollCloseTime) public view returns (PollState) {
if (_pollCloseTime < uint64(now)) {
return PollState.Open;
} else if (add64(_pollCloseTime, REVEAL_PERIOD) < uint64(now)) {
return PollState.Reveal;
} else {
return PollState.Closed;
}
}

function add64(uint64 x, uint64 y) internal pure returns (uint64 z) {
require((z = x + y) >= x, "voting-base-add64-overflow");
}
}
119 changes: 119 additions & 0 deletions contracts/extensions/VotingReputation.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
This file is part of The Colony Network.
The Colony Network is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The Colony Network is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with The Colony Network. If not, see <http://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.8;
pragma experimental ABIEncoderV2;

import "../PatriciaTree/PatriciaTreeProofs.sol";
import "./VotingBase.sol";


contract VotingReputation is VotingBase, PatriciaTreeProofs {

struct Poll {
uint64 numOutcomes;
uint64 pollCloses;
bytes32 rootHash;
uint256 skillId;
mapping (uint64 => uint256) voteCounts;
}

// The UserVote type here is just the bytes32 voteSecret

uint256 pollCount;
mapping (uint256 => Poll) polls;
mapping (address => mapping (uint256 => bytes32)) userVotes;

function createPoll(uint64 _numOutcomes, uint64 _duration, uint256 _skillId) public {
pollCount += 1;
polls[pollCount] = Poll({
numOutcomes: _numOutcomes,
pollCloses: add64(uint64(now), _duration),
rootHash: colonyNetwork.getReputationRootHash(),
skillId: _skillId
});
}

function submitVote(uint256 _pollId, bytes32 _voteSecret) public {
uint64 pollCloses = polls[_pollId].pollCloses;
require(getPollState(pollCloses) == PollState.Open, "colony-rep-voting-poll-not-open");

userVotes[msg.sender][_pollId] = _voteSecret;
}

function revealVote(
uint256 _pollId,
bytes32 _salt,
uint64 _vote,
bytes memory _key,
bytes memory _value,
uint256 _branchMask,
bytes32[] memory _siblings
)
public
{
uint64 pollCloses = polls[_pollId].pollCloses;
require(getPollState(pollCloses) != PollState.Open, "colony-rep-voting-poll-still-open");

bytes32 voteSecret = getVoteSecret(_salt, _vote);
require(userVotes[msg.sender][_pollId] == voteSecret, "colony-rep-voting-secret-no-match");

// Validate proof and get reputation value
uint256 userReputation = checkReputation(_pollId, _key, _value, _branchMask, _siblings);

// Remove the secret
delete userVotes[msg.sender][_pollId];

// Increment the vote if poll in reveal, otherwise skip
// NOTE: since there's no locking, we could just `require` PollState.Reveal
if (getPollState(pollCloses) == PollState.Reveal) {
polls[_pollId].voteCounts[_vote] += userReputation;
}
}

function checkReputation(
uint256 _pollId,
bytes memory _key,
bytes memory _value,
uint256 _branchMask,
bytes32[] memory _siblings
)
internal view returns (uint256)
{
bytes32 impliedRoot = getImpliedRootHashKey(_key, _value, _branchMask, _siblings);
require(polls[_pollId].rootHash == impliedRoot, "colony-rep-voting-invalid-root-hash");

uint256 reputationValue;
address keyColonyAddress;
uint256 keySkill;
address keyUserAddress;

assembly {
reputationValue := mload(add(_value, 32))
keyColonyAddress := mload(add(_key, 20))
keySkill := mload(add(_key, 52))
keyUserAddress := mload(add(_key, 72))
}

require(keyColonyAddress == address(colony), "colony-rep-voting-invalid-colony-address");
require(keySkill == polls[_pollId].skillId, "colony-rep-voting-invalid-skill-id");
require(keyUserAddress == msg.sender, "colony-rep-voting-invalid-user-address");

return reputationValue;
}

}
115 changes: 115 additions & 0 deletions contracts/extensions/VotingToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
This file is part of The Colony Network.
The Colony Network is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The Colony Network is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with The Colony Network. If not, see <http://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.8;
pragma experimental ABIEncoderV2;

import "../ITokenLocking.sol";
import "./VotingBase.sol";


contract VotingToken is VotingBase {

struct Poll {
uint64 numOutcomes;
uint64 pollCloses;
mapping (uint64 => uint256) voteCounts;
}

struct UserVote {
uint256 pollId;
bytes32 voteSecret;
uint64 prevPollCloses;
uint64 nextPollCloses;
}

uint256 pollCount;
mapping (uint256 => Poll) polls;
mapping (address => mapping (uint64 => UserVote)) userVotes;

function createPoll(uint64 _numOutcomes, uint64 _duration) public {
pollCount += 1;
polls[pollCount] = Poll({
numOutcomes: _numOutcomes,
pollCloses: add64(uint64(now), _duration)
});
}

// TODO: Implement inner linked list
function submitVote(uint256 _pollId, bytes32 _voteSecret, uint64 _prevPollCloses, uint256 _prevPollId) public {
uint64 pollCloses = polls[_pollId].pollCloses;
require(getPollState(pollCloses) == PollState.Open, "colony-token-voting-poll-not-open");

UserVote storage prev = userVotes[msg.sender][_prevPollCloses];
UserVote storage next = userVotes[msg.sender][prev.nextPollCloses];

// Check we are inserting at the correct location
require(pollCloses > _prevPollCloses, "colony-token-voting-insert-too-soon");
require(pollCloses < prev.nextPollCloses || prev.nextPollCloses == 0, "colony-token-voting-insert-too-late");

userVotes[msg.sender][pollCloses] = UserVote({
pollId: _pollId,
voteSecret: _voteSecret,
prevPollCloses: _prevPollCloses,
nextPollCloses: prev.nextPollCloses
});

prev.nextPollCloses = pollCloses;
next.prevPollCloses = pollCloses;
}

function revealVote(uint256 _pollId, bytes32 _salt, uint64 _vote) public {
uint64 pollCloses = polls[_pollId].pollCloses;
require(getPollState(pollCloses) != PollState.Open, "colony-token-voting-poll-still-open");

UserVote storage curr = userVotes[msg.sender][pollCloses];
UserVote storage prev = userVotes[msg.sender][curr.prevPollCloses];
UserVote storage next = userVotes[msg.sender][curr.nextPollCloses];

bytes32 voteSecret = getVoteSecret(_salt, _vote);
require(curr.voteSecret == voteSecret, "colony-token-voting-secret-no-match");

// Remove the secret
prev.nextPollCloses = curr.nextPollCloses;
next.prevPollCloses = curr.prevPollCloses;
delete userVotes[msg.sender][pollCloses];

// Increment the vote if poll in reveal
if (getPollState(pollCloses) == PollState.Reveal) {
address token = colony.getToken();
address tokenLocking = colonyNetwork.getTokenLocking();
uint256 userBalance = ITokenLocking(tokenLocking).getUserLock(token, msg.sender).balance;
polls[_pollId].voteCounts[_vote] += userBalance;
}
}

function isAddressLocked(address _address) public view returns (bool) {
uint64 nextPollCloses = userVotes[_address][0].nextPollCloses;
if (nextPollCloses == 0) {
// The list is empty, no unrevealed votes for this address
return false;
} else if (now < nextPollCloses) {
// The poll is still open for voting and tokens transfer
return false;
} else {
// The poll is closed for voting and is in the reveal period, during which all votes' tokens are locked until reveal
// Note: even after the poll is resolved, tokens remain locked until reveal
return true;
}
}

}

0 comments on commit 0f57fca

Please sign in to comment.