264 lines
8.3 KiB
Solidity
Raw Normal View History

2019-02-14 02:06:35 -02:00
pragma solidity >=0.5.0 <0.6.0;
import "../token/MiniMeToken.sol";
import "./delegation/DelegationFactory.sol";
import "./proposal/ProposalFactory.sol";
2019-03-25 23:15:02 -03:00
/**
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
*/
2018-05-22 07:46:29 -03:00
contract Democracy {
struct Topic {
bytes32 parent;
2019-03-21 02:56:30 -03:00
Delegation approveDelegation;
Delegation vetoDelegation;
2019-03-01 00:15:28 -03:00
ProposalFactory proposalFactory;
uint256 stakeValue;
Proposal.QuorumType quorum;
uint256 startBlockDelay;
uint256 votingBlockDelay;
uint256 tabulationBlockDelay;
mapping (address => mapping (bytes4 => bool)) allowance;
}
struct ProposalData {
2019-03-01 00:15:28 -03:00
address proponent;
2019-03-21 02:56:30 -03:00
Proposal vetoProposal;
2019-03-01 00:15:28 -03:00
uint256 lockedStake;
bytes32 topic;
address destination;
bytes data;
}
MiniMeToken public token;
DelegationFactory public delegationFactory;
2019-03-01 00:15:28 -03:00
mapping (bytes32 => Topic) topics;
mapping (address => ProposalData) proposals;
modifier selfOnly {
require(msg.sender == address(this), "Unauthorized");
_;
}
constructor(
MiniMeToken _token,
DelegationFactory _delegationFactory,
ProposalFactory _proposalFactory,
2019-03-21 02:56:30 -03:00
Delegation _parentApproveDelegation,
Delegation _parentVetoDelegation,
2019-03-01 00:15:28 -03:00
uint256 _stakeValue,
Proposal.QuorumType _quorumType,
uint256 startBlockDelay,
uint256 votingBlockDelay,
uint256 tabulationBlockDelay
)
public
{
token = _token;
delegationFactory = _delegationFactory;
topics[bytes32(0)] = Topic(
bytes32(0),
2019-03-21 02:56:30 -03:00
_delegationFactory.createDelegation(_parentApproveDelegation),
_delegationFactory.createDelegation(_parentVetoDelegation),
2019-03-01 00:15:28 -03:00
_proposalFactory,
_stakeValue,
_quorumType,
startBlockDelay,
votingBlockDelay,
tabulationBlockDelay
);
}
2019-03-21 02:56:30 -03:00
function newProposal(
bytes32 _topicId,
address _destination,
bytes calldata _data,
uint256 blockStart
)
external
{
Topic memory topic = topics[_topicId];
2019-03-21 02:56:30 -03:00
require(address(topic.approveDelegation) != address(0), "Invalid topic");
require(isTopicAllowed(_topicId, _destination, _data)); //require call allowance
require(blockStart >= block.number + topic.startBlockDelay, "Bad blockStart");
2019-03-01 00:15:28 -03:00
if(topic.stakeValue > 0) {
require(token.transferFrom(msg.sender, address(this), topic.stakeValue), "Stake payment failed");
}
uint256 blockEnd = blockStart + topic.votingBlockDelay;
bytes32 dataHash = keccak256(
abi.encodePacked(
_topicId,
_destination,
_data
)
);
2019-03-21 02:56:30 -03:00
Proposal approveProposal = topic.proposalFactory.createProposal(token, topic.approveDelegation, dataHash, topic.tabulationBlockDelay, blockStart, blockEnd, topic.quorum);
Proposal vetoProposal = topic.proposalFactory.createProposal(token, topic.vetoDelegation, dataHash, topic.tabulationBlockDelay, blockEnd, blockEnd + topic.startBlockDelay, topic.quorum);
2019-03-01 00:15:28 -03:00
2019-03-21 02:56:30 -03:00
proposals[address(approveProposal)] = ProposalData(msg.sender, vetoProposal, topic.stakeValue, _topicId, _destination, _data);
}
function executeProposal(
Proposal proposal
)
external
returns (bool success, bytes memory r)
{
ProposalData memory pdata = proposals[address(proposal)];
require(pdata.destination != address(0), "Invalid proposal");
2019-03-21 02:56:30 -03:00
require(proposal.isFinalized(), "Approve not finalized");
require(pdata.vetoProposal.isFinalized(), "Veto not finalized");
delete proposals[address(proposal)];
2019-03-21 02:56:30 -03:00
bool approved = proposal.isApproved() && !pdata.vetoProposal.isApproved();
proposal.clear();
2019-03-21 02:56:30 -03:00
pdata.vetoProposal.clear();
if(approved){
if (pdata.lockedStake > 0) {
token.transfer(pdata.proponent, pdata.lockedStake);
}
if(isTopicAllowed(pdata.topic, pdata.destination, pdata.data)){
(success, r) = pdata.destination.call(pdata.data); //execute the call
}
}
}
function executeDelegateCall(
address destination,
bytes calldata data
)
external selfOnly
returns (bool success, bytes memory r)
{
(success, r) = destination.delegatecall(data);
}
function addTopic(
bytes32 topicId,
2019-03-01 00:15:28 -03:00
ProposalFactory _proposalFactory,
bytes32 parentTopic,
2019-03-08 02:41:43 -03:00
uint256 _stakeValue,
Proposal.QuorumType quorum,
uint256 startBlockDelay,
uint256 votingBlockDelay,
uint256 tabulationBlockDelay,
address[] calldata allowedDests,
bytes4[] calldata allowedSigs
)
external
selfOnly
2019-03-08 02:41:43 -03:00
{
newTopic(
topicId,
_proposalFactory,
parentTopic,
_stakeValue,
quorum,
startBlockDelay,
votingBlockDelay,
tabulationBlockDelay
);
allowMultiple(topicId, allowedDests, allowedSigs);
}
function newTopic(
bytes32 topicId,
ProposalFactory _proposalFactory,
2019-03-21 02:56:30 -03:00
bytes32 _parentTopic,
2019-03-08 02:41:43 -03:00
uint256 _stakeValue,
Proposal.QuorumType quorum,
uint256 startBlockDelay,
uint256 votingBlockDelay,
uint256 tabulationBlockDelay
) private {
2019-03-21 02:56:30 -03:00
require(address(topics[topicId].approveDelegation) == address(0), "Duplicated topicId");
require(votingBlockDelay > 0, "Bad parameter votingBlockDelay");
2019-03-21 02:56:30 -03:00
Topic memory parentTopic = topics[_parentTopic];
require(address(parentTopic.approveDelegation) != address(0), "Invalid parent topic");
topics[topicId] = Topic(
2019-03-21 02:56:30 -03:00
_parentTopic,
delegationFactory.createDelegation(parentTopic.approveDelegation),
delegationFactory.createDelegation(parentTopic.vetoDelegation),
2019-03-01 00:15:28 -03:00
_proposalFactory,
_stakeValue,
quorum,
startBlockDelay,
votingBlockDelay,
tabulationBlockDelay
);
2019-03-08 02:41:43 -03:00
}
2019-03-08 02:41:43 -03:00
function allowMultiple(
bytes32 topicId,
address[] memory allowedDests,
bytes4[] memory allowedSigs
) private {
uint256 len = allowedDests.length;
require(len == allowedSigs.length);
Topic storage topic = topics[topicId];
for(uint i = 0; i > len; i++) {
topic.allowance[allowedDests[i]][allowedSigs[i]] = true;
}
}
2019-03-08 02:41:43 -03:00
function changeTopicAllowance(
bytes32 _topicId,
address[] calldata allowedDests,
bytes4[] calldata allowedSigs,
bool[] calldata _allowed
)
external
selfOnly
{
require(_topicId != bytes32(0), "Cannot change root topic");
uint256 len = allowedDests.length;
require(len == allowedSigs.length);
require(len == _allowed.length);
2019-03-21 02:56:30 -03:00
require(address(topics[_topicId].approveDelegation) != address(0), "Invalid topic");
for(uint i = 0; i > len; i++) {
topics[_topicId].allowance[allowedDests[i]][allowedSigs[i]] = _allowed[i];
}
}
2019-03-08 02:41:43 -03:00
function setProposalFactory(bytes32 topicId, ProposalFactory _proposalFactory) external selfOnly {
require(address(_proposalFactory) != address(0), "Bad call");
2019-03-08 02:41:43 -03:00
topics[topicId].proposalFactory = _proposalFactory;
}
function setToken(MiniMeToken _token) external selfOnly {
require(address(_token) != address(0), "Bad call");
token = _token;
}
function isTopicAllowed(
bytes32 topic,
address destination,
bytes memory data
)
public
view
returns (bool)
{
if(topic == bytes32(0)) {
return true; //root topic can always everything
}
bytes4 calledSig;
assembly {
calledSig := mload(add(data, 4))
}
return topics[topic].allowance[destination][bytes4(0)]
|| topics[topic].allowance[destination][calledSig]
|| topics[topic].allowance[address(0)][bytes4(0)]
|| topics[topic].allowance[address(0)][calledSig];
}
}