Skip to content

Commit

Permalink
Redesign VotingHybrid to have single lifecycle WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Mar 15, 2021
1 parent 7034255 commit 421968b
Show file tree
Hide file tree
Showing 7 changed files with 1,637 additions and 361 deletions.
122 changes: 95 additions & 27 deletions contracts/extensions/VotingBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ pragma solidity 0.7.3;
pragma experimental ABIEncoderV2;

import "./../colonyNetwork/IColonyNetwork.sol";
import "./../patriciaTree/PatriciaTreeProofs.sol";
import "./../tokenLocking/ITokenLocking.sol";
import "./ColonyExtension.sol";


abstract contract VotingBase is ColonyExtension {
abstract contract VotingBase is ColonyExtension, PatriciaTreeProofs {

// Events
event MotionCreated(uint256 indexed motionId, address creator, uint256 indexed domainId);
Expand Down Expand Up @@ -172,17 +173,22 @@ abstract contract VotingBase is ColonyExtension {
// Data structures
enum MotionState { Null, Staking, Submit, Reveal, Closed, Finalizable, Finalized, Failed }

struct Influence {
uint256 reputation;
uint256 token;
}

struct Motion {
uint64[3] events; // For recording motion lifecycle timestamps (STAKE, SUBMIT, REVEAL)
bytes32 rootHash;
uint256 domainId;
uint256 skillId;
uint256 maxVotes;
uint256 totalVotes;
uint256 paidVoterComp;
uint256[2] pastVoterComp; // [nay, yay]
uint256[2] stakes; // [nay, yay]
uint256[2] votes; // [nay, yay]
Influence[2] votes; // [nay, yay]
Influence totalVotes;
Influence maxVotes;
bool escalated;
bool finalized;
address altTarget;
Expand All @@ -200,9 +206,9 @@ abstract contract VotingBase is ColonyExtension {

// Virtual functions

function getInfluence(uint256 _motionId, address _user) public view virtual returns (uint256);
function getInfluence(uint256 _motionId, address _user) public view virtual returns (Influence memory);

function getTotalInfluence(uint256 _motionId) public view virtual returns (uint256);
function getTotalInfluence(uint256 _motionId) public view virtual returns (Influence memory);

function postReveal(uint256 _motionId, address _user) internal virtual;

Expand All @@ -219,8 +225,9 @@ abstract contract VotingBase is ColonyExtension {
require(getMotionState(_motionId) == MotionState.Reveal, "voting-base-motion-not-reveal");
require(_vote <= 1, "voting-base-bad-vote");

uint256 influence = getInfluence(_motionId, msg.sender);
motion.votes[_vote] = add(motion.votes[_vote], influence);
Influence memory influence = getInfluence(_motionId, msg.sender);
motion.votes[_vote].reputation = add(motion.votes[_vote].reputation, influence.reputation);
motion.votes[_vote].token = add(motion.votes[_vote].token, influence.token);

bytes32 voteSecret = voteSecrets[_motionId][msg.sender];
require(voteSecret == getVoteSecret(_salt, _vote), "voting-base-secret-no-match");
Expand All @@ -232,7 +239,10 @@ abstract contract VotingBase is ColonyExtension {
emit MotionVoteRevealed(_motionId, msg.sender, _vote);

// See if reputation revealed matches reputation submitted
if (add(motion.votes[NAY], motion.votes[YAY]) == motion.totalVotes) {
if (
add(motion.votes[NAY].reputation, motion.votes[YAY].reputation) == motion.totalVotes.reputation &&
add(motion.votes[NAY].token, motion.votes[YAY].token) == motion.totalVotes.token
) {
motion.events[REVEAL_END] = uint64(block.timestamp);

emit MotionEventSet(_motionId, REVEAL_END);
Expand All @@ -249,14 +259,20 @@ abstract contract VotingBase is ColonyExtension {

assert(
motion.stakes[YAY] == getRequiredStake(_motionId) ||
add(motion.votes[NAY], motion.votes[YAY]) > 0
add(
add(motion.votes[NAY].reputation, motion.votes[YAY].reputation),
add(motion.votes[NAY].token, motion.votes[YAY].token)
) > 0
);

motion.finalized = true;

bool canExecute = (
motion.stakes[NAY] < motion.stakes[YAY] ||
motion.votes[NAY] < motion.votes[YAY]
(
motion.votes[NAY].reputation <= motion.votes[YAY].reputation &&
motion.votes[NAY].token <= motion.votes[YAY].token
)
);

if (getSig(motion.action) == CHANGE_FUNCTION && motion.altTarget == address(0x0)) {
Expand All @@ -271,8 +287,10 @@ abstract contract VotingBase is ColonyExtension {
}

bytes32 actionHash = hashExpenditureAction(motion.action);
uint256 votePower = (add(motion.votes[NAY], motion.votes[YAY]) > 0) ?
motion.votes[YAY] : motion.stakes[YAY];
uint256 votePower = (add(
add(motion.votes[NAY].reputation, motion.votes[YAY].reputation),
add(motion.votes[NAY].token, motion.votes[YAY].token)
) > 0) ? add(motion.votes[YAY].reputation, motion.votes[YAY].token) : motion.stakes[YAY];

if (expenditurePastVotes[actionHash] < votePower) {
expenditurePastVotes[actionHash] = votePower;
Expand Down Expand Up @@ -449,8 +467,8 @@ abstract contract VotingBase is ColonyExtension {
// If not, did the YAY side stake?
} else if (motion.stakes[YAY] == requiredStake) {
return MotionState.Finalizable;
// If not, was there a prior vote we can fall back on?
} else if (add(motion.votes[NAY], motion.votes[YAY]) > 0) {
// If not, was there a prior (reputation) vote we can fall back on?
} else if (add(motion.votes[NAY].reputation, motion.votes[YAY].reputation) > 0) {
return MotionState.Finalizable;
// Otherwise, the motion failed
} else {
Expand Down Expand Up @@ -478,12 +496,17 @@ abstract contract VotingBase is ColonyExtension {

/// @notice Get the voter reward
/// @param _motionId The id of the motion
/// @param _voterInfluence The influence the voter has in the domain
/// @param _influence The influence the voter has in the domain
/// @return The voter reward
function getVoterReward(uint256 _motionId, uint256 _voterInfluence) public view returns (uint256) {
function getVoterReward(uint256 _motionId, Influence memory _influence) public view returns (uint256) {
Motion storage motion = motions[_motionId];
uint256 totalInfluence = getTotalInfluence(_motionId);
uint256 fractionUserInfluence = wdiv(_voterInfluence, totalInfluence);
Influence memory totalInfluence = getTotalInfluence(_motionId);

uint256 fractionUserInfluence = wdiv(
add(_influence.reputation, _influence.token),
add(totalInfluence.reputation, totalInfluence.token)
);

uint256 totalStake = add(motion.stakes[YAY], motion.stakes[NAY]);
return wmul(wmul(fractionUserInfluence, totalStake), voterRewardFraction);
}
Expand All @@ -508,11 +531,14 @@ abstract contract VotingBase is ColonyExtension {
uint256 repPenalty;

// Went to a vote, use vote to determine reward or penalty
if (add(motion.votes[NAY], motion.votes[YAY]) > 0) {
if (add(
add(motion.votes[NAY].reputation, motion.votes[YAY].reputation),
add(motion.votes[NAY].token, motion.votes[YAY].token)
) > 0) {

uint256 loserStake = sub(requiredStake, motion.paidVoterComp);
uint256 totalVotes = add(motion.votes[NAY], motion.votes[YAY]);
uint256 winFraction = wdiv(motion.votes[_vote], totalVotes);
uint256 totalVotes = add(add(motion.votes[NAY].reputation, motion.votes[YAY].reputation), add(motion.votes[NAY].token, motion.votes[YAY].token));
uint256 winFraction = wdiv(add(motion.votes[_vote].reputation, motion.votes[_vote].token), totalVotes);
uint256 winShare = wmul(winFraction, 2 * WAD); // On a scale of 0-2 WAD

if (winShare > WAD || (winShare == WAD && _vote == NAY)) {
Expand Down Expand Up @@ -612,9 +638,10 @@ abstract contract VotingBase is ColonyExtension {
require(amount > 0, "voting-base-bad-amount");

uint256 stakerTotalAmount = add(stakes[_motionId][msg.sender][_vote], amount);
Influence memory influence = getInfluence(_motionId, msg.sender);

require(
stakerTotalAmount <= getInfluence(_motionId, msg.sender),
stakerTotalAmount <= max(influence.reputation, influence.token),
"voting-base-insufficient-influence"
);
require(
Expand Down Expand Up @@ -682,18 +709,25 @@ abstract contract VotingBase is ColonyExtension {
require(getMotionState(_motionId) == MotionState.Submit, "voting-base-motion-not-open");
require(_voteSecret != bytes32(0), "voting-base-invalid-secret");

uint256 influence = getInfluence(_motionId, msg.sender);
Influence memory influence = getInfluence(_motionId, msg.sender);

// Count reputation if first submission
if (voteSecrets[_motionId][msg.sender] == bytes32(0)) {
motion.totalVotes = add(motion.totalVotes, influence);
motion.totalVotes.reputation = add(motion.totalVotes.reputation, influence.reputation);
motion.totalVotes.token = add(motion.totalVotes.token, influence.token);
}

voteSecrets[_motionId][msg.sender] = _voteSecret;

emit MotionVoteSubmitted(_motionId, msg.sender);

if (motion.totalVotes >= wmul(motion.maxVotes, maxVoteFraction)) {
if ((
motion.totalVotes.reputation == 0 ||
motion.totalVotes.reputation >= wmul(motion.maxVotes.reputation, maxVoteFraction)
) && (
motion.totalVotes.token == 0 ||
motion.totalVotes.token >= wmul(motion.maxVotes.token, maxVoteFraction)
)) {
motion.events[SUBMIT_END] = uint64(block.timestamp);
motion.events[REVEAL_END] = uint64(block.timestamp + revealPeriod);

Expand All @@ -706,7 +740,8 @@ abstract contract VotingBase is ColonyExtension {
}

function getRequiredStake(uint256 _motionId) public view returns (uint256) {
return wmul(motions[_motionId].maxVotes, totalStakeFraction);
Motion storage motion = motions[_motionId];
return wmul(add(motion.maxVotes.reputation, motion.maxVotes.token), totalStakeFraction);
}

function flip(uint256 _vote) internal pure returns (uint256) {
Expand Down Expand Up @@ -858,4 +893,37 @@ abstract contract VotingBase is ColonyExtension {

}
}

function getReputationFromProof(
uint256 _motionId,
address _who,
bytes memory _key,
bytes memory _value,
uint256 _branchMask,
bytes32[] memory _siblings
)
internal view returns (uint256)
{
bytes32 impliedRoot = getImpliedRootHashKey(_key, _value, _branchMask, _siblings);
require(motions[_motionId].rootHash == impliedRoot, "voting-base-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), "voting-base-invalid-colony-address");
require(keySkill == motions[_motionId].skillId, "voting-base-invalid-skill-id");
require(keyUserAddress == _who, "voting-base-invalid-user-address");

return reputationValue;
}

}
Loading

0 comments on commit 421968b

Please sign in to comment.