-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9fe2f2b
commit 0f57fca
Showing
3 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
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
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"); | ||
} | ||
} |
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
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; | ||
} | ||
|
||
} |
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
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; | ||
} | ||
} | ||
|
||
} |