mirror of
https://github.com/status-im/topic-democracy.git
synced 2025-02-24 08:08:11 +00:00
abstract proposal, supports contract voting
This commit is contained in:
parent
729f9d0e59
commit
371fa233b6
43
contracts/common/MerkleProof.sol
Normal file
43
contracts/common/MerkleProof.sol
Normal file
@ -0,0 +1,43 @@
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
|
||||
/**
|
||||
* @title MerkleProof
|
||||
* @dev Merkle proof verification based on
|
||||
* https://github.com/ameensol/merkle-tree-solidity/blob/master/src/MerkleProof.sol
|
||||
*/
|
||||
library MerkleProof {
|
||||
/**
|
||||
* @dev Verifies a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves
|
||||
* and each pair of pre-images are sorted.
|
||||
* @param _proof Merkle proof containing sibling hashes on the branch from the leaf to the root of the Merkle tree
|
||||
* @param _root Merkle root
|
||||
* @param _leaf Leaf of Merkle tree
|
||||
*/
|
||||
function verifyProof(
|
||||
bytes32[] memory _proof,
|
||||
bytes32 _root,
|
||||
bytes32 _leaf
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bool)
|
||||
{
|
||||
bytes32 computedHash = _leaf;
|
||||
|
||||
for (uint256 i = 0; i < _proof.length; i++) {
|
||||
bytes32 proofElement = _proof[i];
|
||||
|
||||
if (computedHash < proofElement) {
|
||||
// Hash(current computed hash + current element of the proof)
|
||||
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
|
||||
} else {
|
||||
// Hash(current element of the proof + current computed hash)
|
||||
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the computed hash (root) is equal to the provided root
|
||||
return computedHash == _root;
|
||||
}
|
||||
}
|
153
contracts/democracy/Proposal.sol
Normal file
153
contracts/democracy/Proposal.sol
Normal file
@ -0,0 +1,153 @@
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "../common/Controlled.sol";
|
||||
import "../common/MessageSigned.sol";
|
||||
import "../common/MerkleProof.sol";
|
||||
import "../token/MiniMeToken.sol";
|
||||
import "./Delegation.sol";
|
||||
import "./TrustNetworkInterface.sol";
|
||||
|
||||
/**
|
||||
* @title Proposal
|
||||
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||
* Store votes and tabulate results for Democracy
|
||||
*/
|
||||
contract Proposal is Controlled, MessageSigned {
|
||||
MiniMeToken public token;
|
||||
Delegation public delegation;
|
||||
uint256 public tabulationBlockDelay;
|
||||
|
||||
bytes32 topic;
|
||||
bytes32 txHash;
|
||||
uint blockStart;
|
||||
uint voteBlockEnd;
|
||||
|
||||
//votes storage
|
||||
bytes32[] signatures;
|
||||
mapping(address => Vote) voteMap;
|
||||
|
||||
//tabulation process
|
||||
uint256 lastTabulationBlock;
|
||||
mapping(address => address) tabulated;
|
||||
mapping(uint8 => uint256) results;
|
||||
|
||||
Vote result;
|
||||
|
||||
|
||||
enum Vote {
|
||||
Null,
|
||||
Reject,
|
||||
Approve
|
||||
}
|
||||
|
||||
constructor(
|
||||
MiniMeToken _token,
|
||||
Delegation _delegation,
|
||||
bytes32 _topic,
|
||||
bytes32 _txHash,
|
||||
uint256 _tabulationBlockDelay,
|
||||
uint256 _blockStart,
|
||||
uint256 _blockEndDelay
|
||||
)
|
||||
public
|
||||
|
||||
{
|
||||
delegation = _delegation;
|
||||
token = _token;
|
||||
tabulationBlockDelay = _tabulationBlockDelay;
|
||||
topic = _topic;
|
||||
txHash = _txHash;
|
||||
blockStart = _blockStart;
|
||||
voteBlockEnd = blockStart + _blockEndDelay;
|
||||
}
|
||||
|
||||
function vote(bytes32 _signatures)
|
||||
external
|
||||
{
|
||||
require(block.number >= blockStart, "Voting not started");
|
||||
require(block.number <= voteBlockEnd, "Voting ended");
|
||||
signatures.push(_signatures);
|
||||
}
|
||||
|
||||
function vote(Vote _vote)
|
||||
external
|
||||
{
|
||||
require(block.number >= blockStart, "Voting not started");
|
||||
require(block.number <= voteBlockEnd, "Voting ended");
|
||||
voteMap[msg.sender] = _vote;
|
||||
|
||||
}
|
||||
|
||||
function tabulate(Vote _vote, uint256 _position, bytes32[] calldata _proof, bytes calldata _signature)
|
||||
external
|
||||
{
|
||||
require(MerkleProof.verifyProof(_proof, signatures[_position], keccak256(_signature)), "Invalid proof");
|
||||
address _voter = recoverAddress(keccak256(abi.encodePacked(address(this), _vote)), _signature);
|
||||
voteMap[_voter] = _vote;
|
||||
_tabulate(_voter);
|
||||
}
|
||||
|
||||
function tabulate(address _voter)
|
||||
external
|
||||
{
|
||||
_tabulate(_voter);
|
||||
}
|
||||
|
||||
function _tabulate(address _voter)
|
||||
internal
|
||||
{
|
||||
require(block.number > voteBlockEnd, "Voting running");
|
||||
address _claimer = _voter;
|
||||
Vote _vote = voteMap[_claimer];
|
||||
if(_vote == Vote.Null) { //not voted, can be claimed by delegate
|
||||
_claimer = delegation.delegationOfAt(_voter, voteBlockEnd);
|
||||
_vote = voteMap[_claimer]; //loads delegate vote.
|
||||
_claimer = _voter; // try finding first delegate from chain which voted
|
||||
while(_vote == Vote.Null) {
|
||||
_claimer = delegation.delegatedToAt(_claimer, voteBlockEnd);
|
||||
_vote = voteMap[_claimer]; //loads delegate vote.
|
||||
}
|
||||
}
|
||||
|
||||
require(_vote != Vote.Null, "Cannot be Vote.Null");
|
||||
uint256 voterBalance = token.balanceOfAt(_voter, voteBlockEnd);
|
||||
address currentClaimer = tabulated[_voter];
|
||||
if(currentClaimer != address(0))
|
||||
{
|
||||
require(currentClaimer != _voter, "Voter already tabulated");
|
||||
require(currentClaimer != _claimer, "Claimer already tabulated");
|
||||
Vote oldVote = voteMap[currentClaimer];
|
||||
require(oldVote != _vote, "Doesn't changes tabulation");
|
||||
results[uint8(oldVote)] -= voterBalance;
|
||||
}
|
||||
tabulated[_voter] = _claimer;
|
||||
results[uint8(_vote)] += voterBalance;
|
||||
lastTabulationBlock = block.number;
|
||||
}
|
||||
|
||||
function finalize()
|
||||
external
|
||||
{
|
||||
require(block.number > voteBlockEnd, "Voting running");
|
||||
require(lastTabulationBlock + tabulationBlockDelay > block.number, "Tabulation running");
|
||||
require(result == Vote.Null, "Already finalized");
|
||||
uint256 totalTokens = token.totalSupplyAt(voteBlockEnd);
|
||||
uint256 approvals = results[uint8(Vote.Approve)];
|
||||
uint256 rejects = results[uint8(Vote.Reject)];
|
||||
uint256 approvalQuorum = (totalTokens / 2);
|
||||
if(approvals-rejects >= approvalQuorum) {
|
||||
result = Vote.Approve;
|
||||
} else {
|
||||
result = Vote.Reject;
|
||||
}
|
||||
}
|
||||
|
||||
function clear()
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
require(result != Vote.Null, "Not finalized");
|
||||
selfdestruct(controller);
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,7 @@ contract ProposalManager is Controlled, MessageSigned {
|
||||
bytes32[] signatures;
|
||||
mapping(address => Vote) voteMap;
|
||||
|
||||
mapping(address => bool) tabulated;
|
||||
mapping(address => address) tabulated;
|
||||
mapping(uint8 => uint256) results;
|
||||
Vote result;
|
||||
uint256 lastTabulationTimestamp;
|
||||
@ -104,7 +104,7 @@ contract ProposalManager is Controlled, MessageSigned {
|
||||
}
|
||||
require(proposal.signatures[_position] == merkleHash, "Invalid proof");
|
||||
address _voter = recoverAddress(keccak256(abi.encodePacked(address(this),_proposalId,_vote)), _signature);
|
||||
require(!proposal.tabulated[_voter], "Address already tabulated");
|
||||
require(proposal.tabulated[_voter] == address(0), "Address already tabulated");
|
||||
|
||||
proposal.results[uint8(_vote)] += token.balanceOfAt(_voter, proposal.voteBlockEnd);
|
||||
|
||||
@ -117,12 +117,14 @@ contract ProposalManager is Controlled, MessageSigned {
|
||||
{
|
||||
Proposal storage proposal = proposals[_proposalId];
|
||||
require(block.number > proposal.voteBlockEnd);
|
||||
require(!proposal.tabulated[_delegator]);
|
||||
proposal.tabulated[_delegator] = true;
|
||||
require(proposal.tabulated[_delegator] == address(0), "Already tabulated");
|
||||
Vote _vote = proposal.voteMap[_delegator];
|
||||
if(_vote == Vote.Null) {
|
||||
if(_vote == Vote.Null) { //not voted, can be claimed by delegate
|
||||
address delegate = trustNet.getVoteDelegation(proposal.topic).delegationOfAt(_delegator, proposal.voteBlockEnd);
|
||||
proposal.tabulated[_delegator] = delegate;
|
||||
_vote = proposal.voteMap[delegate];
|
||||
} else { //voted by themselves
|
||||
proposal.tabulated[_delegator] = _delegator;
|
||||
}
|
||||
|
||||
if (_vote == Vote.Reject || _vote == Vote.Approve) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user