base structure of upgradable republic
This commit is contained in:
commit
c07850d068
|
@ -0,0 +1,311 @@
|
||||||
|
pragma solidity ^0.4.11;
|
||||||
|
|
||||||
|
import "../token/MiniMeToken.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title DelegationProxy
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* @dev Creates a delegation proxy layer for MiniMeToken.
|
||||||
|
*/
|
||||||
|
contract DelegationProxy {
|
||||||
|
event Delegate(address who, address to);
|
||||||
|
//default delegation proxy, being used when user didn't set any delegation at this level.
|
||||||
|
address public parentProxy;
|
||||||
|
//snapshots of changes, allow delegation changes be done at any time without compromising vote results.
|
||||||
|
mapping (address => Delegation[]) public delegations;
|
||||||
|
//storage of indexes of the addresses to `delegations[to].from`
|
||||||
|
mapping (address => uint256) toIndexes;
|
||||||
|
|
||||||
|
struct Delegation {
|
||||||
|
uint128 fromBlock; //when this was updated
|
||||||
|
address to; //who recieved this delegaton
|
||||||
|
address[] from; //list of addresses that delegated to this address
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Calls Constructor
|
||||||
|
*/
|
||||||
|
function DelegationProxy(address _parentProxy) public {
|
||||||
|
parentProxy = _parentProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Changes the delegation of `msg.sender` to `_to`. if _to 0x00: delegate to self.
|
||||||
|
* In case of having a parent proxy, if never defined, fall back to parent proxy.
|
||||||
|
* If once defined and want to delegate to parent proxy, set `_to` as parent address.
|
||||||
|
* @param _to To what address the caller address will delegate to.
|
||||||
|
*/
|
||||||
|
function delegate(address _to) external {
|
||||||
|
_updateDelegate(msg.sender, _to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads `_who` configured delegation in this level,
|
||||||
|
* or from parent level if `_who` never defined/defined to parent address.
|
||||||
|
* @param _who What address to lookup.
|
||||||
|
* @return The address `_who` choosen delegate to.
|
||||||
|
*/
|
||||||
|
function delegatedTo(address _who) public constant returns (address) {
|
||||||
|
return delegatedToAt(_who, block.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads the final delegate of `_who` at block number `_block`.
|
||||||
|
* @param _who Address to lookup.
|
||||||
|
* @return Final delegate address.
|
||||||
|
*/
|
||||||
|
function delegationOf(address _who) public constant returns(address) {
|
||||||
|
return delegationOfAt(_who, block.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads the sum of votes a `_who' have at block number `_block`.
|
||||||
|
* @param _who From what address.
|
||||||
|
* @param _token Address of source MiniMeToken.
|
||||||
|
* @return Amount of influence of `who` have.
|
||||||
|
*/
|
||||||
|
function influenceOf(address _who, MiniMeToken _token) public constant returns(uint256 _total) {
|
||||||
|
return influenceOfAt(_who, _token, block.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads amount delegated influence `_who` received from other addresses.
|
||||||
|
* @param _who What address to lookup.
|
||||||
|
* @param _token Source MiniMeToken.
|
||||||
|
* @return Sum of delegated influence received by `_who` in block `_block` from other addresses.
|
||||||
|
*/
|
||||||
|
function delegatedInfluenceFrom(address _who, address _token) public constant returns(uint256 _total) {
|
||||||
|
return delegatedInfluenceFromAt(_who, _token, block.number, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads amount delegated influence `_who` received from other addresses at block number `_block`.
|
||||||
|
* @param _who What address to lookup.
|
||||||
|
* @param _token Source MiniMeToken.
|
||||||
|
* @param _block Position in history to lookup.
|
||||||
|
* @return Sum of delegated influence received by `_who` in block `_block` from other addresses
|
||||||
|
*/
|
||||||
|
function delegatedInfluenceFromAt(
|
||||||
|
address _who,
|
||||||
|
address _token,
|
||||||
|
uint _block
|
||||||
|
)
|
||||||
|
public
|
||||||
|
constant
|
||||||
|
returns(uint256 _total)
|
||||||
|
{
|
||||||
|
return delegatedInfluenceFromAt(_who, _token, _block, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads `_who` configured delegation at block number `_block` in this level,
|
||||||
|
* or from parent level if `_who` never defined/defined to parent address.
|
||||||
|
* @param _who What address to lookup.
|
||||||
|
* @param _block Block number of what height in history.
|
||||||
|
* @return The address `_who` choosen delegate to.
|
||||||
|
*/
|
||||||
|
function delegatedToAt(address _who, uint _block) public constant returns (address addr) {
|
||||||
|
Delegation[] storage checkpoints = delegations[_who];
|
||||||
|
|
||||||
|
//In case there is no registry
|
||||||
|
if (checkpoints.length == 0) {
|
||||||
|
if (parentProxy != 0x0) {
|
||||||
|
return DelegationProxy(parentProxy).delegatedToAt(_who, _block);
|
||||||
|
} else {
|
||||||
|
return 0x0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Delegation memory d = _getMemoryAt(checkpoints, _block);
|
||||||
|
// Case user set delegate to parentProxy address
|
||||||
|
if (d.to == parentProxy && parentProxy != 0x0) {
|
||||||
|
return DelegationProxy(parentProxy).delegatedToAt(_who, _block);
|
||||||
|
}
|
||||||
|
return d.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads the final delegate of `_who` at block number `_block`.
|
||||||
|
* @param _who Address to lookup.
|
||||||
|
* @param _block From what block.
|
||||||
|
* @return Final delegate address.
|
||||||
|
*/
|
||||||
|
function delegationOfAt(address _who, uint _block) public constant returns(address) {
|
||||||
|
address delegate = delegatedToAt(_who, _block);
|
||||||
|
if (delegate != 0x0) { //_who is delegating?
|
||||||
|
return delegationOfAt(delegate, _block); //load the delegation of _who delegation
|
||||||
|
} else {
|
||||||
|
return _who; //reached the endpoint of delegation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Reads amount delegated influence received from other addresses.
|
||||||
|
* @param _who What address to lookup.
|
||||||
|
* @param _token Source MiniMeToken.
|
||||||
|
* @param _block Position in history to lookup.
|
||||||
|
* @param _childProxy The child DelegationProxy requesting the call to parent.
|
||||||
|
* @return Sum of delegated influence received by `_who` in block `_block` from other addresses.
|
||||||
|
*/
|
||||||
|
function delegatedInfluenceFromAt(
|
||||||
|
address _who,
|
||||||
|
address _token,
|
||||||
|
uint _block,
|
||||||
|
address _childProxy
|
||||||
|
)
|
||||||
|
public
|
||||||
|
constant
|
||||||
|
returns(uint256 _total)
|
||||||
|
{
|
||||||
|
Delegation[] storage checkpoints = delegations[_who];
|
||||||
|
//In case there is no registry
|
||||||
|
if (checkpoints.length == 0) {
|
||||||
|
if (parentProxy != 0x0) {
|
||||||
|
return DelegationProxy(parentProxy).delegatedInfluenceFromAt(
|
||||||
|
_from,
|
||||||
|
_token,
|
||||||
|
_block,
|
||||||
|
_childProxy
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Delegation memory d = _getMemoryAt(checkpoints, _block);
|
||||||
|
// Case user set delegate to parentProxy
|
||||||
|
if (d.to == parentProxy && parentProxy != 0x0) {
|
||||||
|
return DelegationProxy(parentProxy).delegatedInfluenceFromAt(
|
||||||
|
_from,
|
||||||
|
_token,
|
||||||
|
_block,
|
||||||
|
_childProxy
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint _len = d.from.length;
|
||||||
|
for (uint256 i = 0; _len > i; i++) {
|
||||||
|
address _from = d.from[i];
|
||||||
|
_total += MiniMeToken(_token).balanceOfAt(_from, _block); // source of _who votes
|
||||||
|
_total += DelegationProxy(_childProxy).delegatedInfluenceFromAt(
|
||||||
|
_from,
|
||||||
|
_token,
|
||||||
|
_block,
|
||||||
|
_childProxy
|
||||||
|
); //sum the from delegation votes
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Reads the sum of votes a `_who' have at block number `_block`.
|
||||||
|
* @param _who From what address.
|
||||||
|
* @param _token Address of source MiniMeToken.
|
||||||
|
* @param _block From what block
|
||||||
|
* @return Amount of influence of `who` have.
|
||||||
|
*/
|
||||||
|
function influenceOfAt(address _who, MiniMeToken _token, uint _block) public constant returns(uint256 _total) {
|
||||||
|
if (delegationOfAt(_who, _block) == _who) { //is endpoint of delegation?
|
||||||
|
_total = MiniMeToken(_token).balanceOfAt(_who, _block); // source of _who votes
|
||||||
|
_total += delegatedInfluenceFromAt(_who, _token, _block, address(this)); //votes delegated to `_who`
|
||||||
|
} else {
|
||||||
|
_total = 0; //no influence because were delegated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Changes the delegation of `_from` to `_to`. if _to 0x00: delegate to self.
|
||||||
|
* In case of having a parent proxy, if never defined, fall back to parent proxy.
|
||||||
|
* If once defined and want to delegate to parent proxy, set `_to` as parent address.
|
||||||
|
* @param _from Address delegating.
|
||||||
|
* @param _to Address delegated.
|
||||||
|
*/
|
||||||
|
function _updateDelegate(address _from, address _to) internal {
|
||||||
|
require(delegationOfAt(_to, block.number) != msg.sender); //block impossible circular delegation
|
||||||
|
Delegate(_from, _to);
|
||||||
|
Delegation memory _newFrom; //allocate memory
|
||||||
|
Delegation[] storage fromHistory = delegations[_from];
|
||||||
|
if (fromHistory.length > 0) { //have old config?
|
||||||
|
_newFrom = fromHistory[fromHistory.length - 1]; //load to memory
|
||||||
|
_newFrom.from = fromHistory[fromHistory.length - 1].from;
|
||||||
|
if (toIndexes[_from] > 0) { //was delegating? remove old link
|
||||||
|
_removeDelegated(_newFrom.to, toIndexes[_from]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Add the new delegation
|
||||||
|
_newFrom.fromBlock = uint128(block.number);
|
||||||
|
_newFrom.to = _to; //delegate address
|
||||||
|
|
||||||
|
if (_to != 0x0 && _to != parentProxy) { //_to is an address?
|
||||||
|
_addDelegated(_from, _to);
|
||||||
|
} else {
|
||||||
|
toIndexes[_from] = 0; //zero index
|
||||||
|
}
|
||||||
|
fromHistory.push(_newFrom); //register `from` delegation update;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev `_getDelegationAt` retrieves the delegation at a given block number.
|
||||||
|
* @param checkpoints The memory being queried.
|
||||||
|
* @param _block The block number to retrieve the value at.
|
||||||
|
* @return The delegation being queried.
|
||||||
|
*/
|
||||||
|
function _getMemoryAt(Delegation[] storage checkpoints, uint _block) internal constant returns (Delegation d) {
|
||||||
|
// Case last checkpoint is the one;
|
||||||
|
if (_block >= checkpoints[checkpoints.length-1].fromBlock) {
|
||||||
|
d = checkpoints[checkpoints.length-1];
|
||||||
|
} else {
|
||||||
|
// Lookup in array;
|
||||||
|
uint min = 0;
|
||||||
|
uint max = checkpoints.length-1;
|
||||||
|
while (max > min) {
|
||||||
|
uint mid = (max + min + 1) / 2;
|
||||||
|
if (checkpoints[mid].fromBlock <= _block) {
|
||||||
|
min = mid;
|
||||||
|
} else {
|
||||||
|
max = mid-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d = checkpoints[min];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Removes delegation at `_toIndex` from `_to` address.
|
||||||
|
* @param _to Delegate address to remove delegation.
|
||||||
|
* @param _toIndex Index of delegation being removed.
|
||||||
|
*/
|
||||||
|
function _removeDelegated(address _to, uint _toIndex) private {
|
||||||
|
Delegation[] storage oldTo = delegations[_to]; //load delegation storage
|
||||||
|
uint _oldToLen = oldTo.length;
|
||||||
|
assert(_oldToLen > 0); //if code tried to remove from nothing this is absolutely bad
|
||||||
|
Delegation memory _newOldTo = oldTo[_oldToLen - 1];//copy to memory last result
|
||||||
|
address replacer = _newOldTo.from[_newOldTo.from.length - 1];
|
||||||
|
_newOldTo.from[_toIndex - 1] = replacer; //replace delegated address at `_toIndex`
|
||||||
|
oldTo.push(_newOldTo); //save the value at memory
|
||||||
|
oldTo[_oldToLen].from.length--; //remove duplicated `to`
|
||||||
|
toIndexes[replacer] = _toIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Add delegation of `_from` in delegate `_to`.
|
||||||
|
* @param _from Delegator address delegating their influence.
|
||||||
|
* @param _to Delegate address receiving the influence;
|
||||||
|
* @return _toIndex The index of `_from` in `_to` delegate.
|
||||||
|
*/
|
||||||
|
function _addDelegated(address _from, address _to) private {
|
||||||
|
Delegation memory _newTo; // allocates space in memory
|
||||||
|
Delegation[] storage toHistory = delegations[_to]; //load delegation storage
|
||||||
|
uint toHistLen = toHistory.length;
|
||||||
|
if (toHistLen > 0) { //have data, should copy 'from' array.
|
||||||
|
_newTo = toHistory[toHistLen - 1]; //copy to memory last one
|
||||||
|
} else {
|
||||||
|
_newTo.to = parentProxy; // configure delegate of `_to` because it was never defined
|
||||||
|
}
|
||||||
|
_newTo.fromBlock = uint128(block.number); //define the new block number
|
||||||
|
toHistory.push(_newTo); //register `to` delegated from
|
||||||
|
toHistory[toHistLen].from.push(_from); //add the delegated from in to list
|
||||||
|
toIndexes[_from] = toHistory[toHistLen].from.length; //link to index
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
pragma solidity ^0.4.11;
|
||||||
|
|
||||||
|
import "./DelegationProxyModel.sol";
|
||||||
|
import "../deploy/RecoverableSystem.sol";
|
||||||
|
import "../deploy/KillableModel.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title DelegationProxyFactory
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* @dev Upgradable delegation proxy factory
|
||||||
|
*/
|
||||||
|
contract DelegationProxyFactory {
|
||||||
|
|
||||||
|
address public systemModel;
|
||||||
|
address public recover;
|
||||||
|
address public watchdog;
|
||||||
|
|
||||||
|
function DelegationProxyFactory(address _recover, address _watchdog) public {
|
||||||
|
watchdog = _watchdog;
|
||||||
|
recover = _recover;
|
||||||
|
systemModel = new DelegationProxyModel(watchdog);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(address _parent) external returns (DelegationProxy) {
|
||||||
|
DelegationProxyModel instance = DelegationProxyModel(address(new RecoverableSystem(systemModel, recover)));
|
||||||
|
instance.initialize(_parent);
|
||||||
|
return DelegationProxy(address(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
pragma solidity ^0.4.11;
|
||||||
|
|
||||||
|
import "../deploy/KillableModel.sol";
|
||||||
|
import "./DelegationProxy.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title DelegationProxyModel
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* @dev Creates a delegation proxy killable model for cheap redeploy and upgradability.
|
||||||
|
*/
|
||||||
|
contract DelegationProxyModel is KillableModel, DelegationProxy {
|
||||||
|
bool private ready = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Constructor of the model - only knows about watchdog that can trigger upgrade
|
||||||
|
*/
|
||||||
|
function DelegationProxyModel(address _watchdog) KillableModel(_watchdog) DelegationProxy(0x0) public {
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Creates a new DelegationProxy with `_parentProxy` as default delegation.
|
||||||
|
*/
|
||||||
|
function initialize(address _parentProxy) public {
|
||||||
|
require(!ready);
|
||||||
|
ready = true;
|
||||||
|
parentProxy = _parentProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
pragma solidity ^0.4.10;
|
||||||
|
|
||||||
|
import "./TrustNetwork.sol";
|
||||||
|
import "./DelegationProxy.sol";
|
||||||
|
import "../token/MiniMeToken.sol";
|
||||||
|
import "../common/Controlled.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title ProposalManager
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* Store the proposals, votes and results for other smartcontracts
|
||||||
|
*/
|
||||||
|
contract ProposalManager is Controlled {
|
||||||
|
|
||||||
|
TrustNetwork public trustNet;
|
||||||
|
MiniMeToken public SNT;
|
||||||
|
address public stakeBank;
|
||||||
|
|
||||||
|
Proposal[] proposals;
|
||||||
|
|
||||||
|
struct Proposal {
|
||||||
|
address topic;
|
||||||
|
|
||||||
|
address destination;
|
||||||
|
uint value;
|
||||||
|
bytes data;
|
||||||
|
uint stake;
|
||||||
|
|
||||||
|
uint blockStart;
|
||||||
|
uint voteBlockEnd;
|
||||||
|
uint vetoBlockEnd;
|
||||||
|
|
||||||
|
VoteTicket[] votes;
|
||||||
|
mapping(address => uint) voteIndex;
|
||||||
|
|
||||||
|
uint tabulationPosition;
|
||||||
|
bool tabulated;
|
||||||
|
mapping(uint8 => uint) results;
|
||||||
|
|
||||||
|
bool approved;
|
||||||
|
bool executed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct VoteTicket {
|
||||||
|
address voter;
|
||||||
|
Vote vote;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Vote {
|
||||||
|
Reject,
|
||||||
|
Approve,
|
||||||
|
Veto
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProposalManager(MiniMeToken _SNT, TrustNetwork _trustNet, address _stakeBank) public {
|
||||||
|
trustNet = _trustNet;
|
||||||
|
SNT = _SNT;
|
||||||
|
stakeBank = _stakeBank;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addProposal(address topic, address destination, uint value, bytes data, uint stake) returns (uint) {
|
||||||
|
require(stake > 1000);
|
||||||
|
require(SNT.transferFrom(msg.sender, stakeBank, stake));
|
||||||
|
uint pos = proposals.length++;
|
||||||
|
Proposal storage p = proposals[pos];
|
||||||
|
|
||||||
|
p.topic = topic;
|
||||||
|
p.destination = destination;
|
||||||
|
p.value = value;
|
||||||
|
p.data = data;
|
||||||
|
p.stake = stake;
|
||||||
|
|
||||||
|
p.blockStart = block.number + 1000; //will be replaced by configurations
|
||||||
|
p.voteBlockEnd = p.blockStart + 10000; //dummy value
|
||||||
|
p.vetoBlockEnd = p.voteBlockEnd + 5000; //dummy value
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProposal(uint id) public constant returns (address topic, address destination, uint value, uint stake, bool approved, bool executed) {
|
||||||
|
Proposal memory p = proposals[id];
|
||||||
|
return (p.topic, p.destination, p.value, p.stake, p.approved, p.executed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProposalData(uint id) public constant returns(bytes){
|
||||||
|
return proposals[id].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setExecuted(uint id) public onlyController {
|
||||||
|
proposals[id].executed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function vote(uint _proposal, Vote _vote) {
|
||||||
|
Proposal storage proposal = proposals[_proposal];
|
||||||
|
require(block.number >= proposal.blockStart);
|
||||||
|
if (_vote == Vote.Veto) {
|
||||||
|
require(block.number <= proposal.vetoBlockEnd);
|
||||||
|
} else {
|
||||||
|
require(block.number <= proposal.voteBlockEnd);
|
||||||
|
}
|
||||||
|
uint votePos = proposal.voteIndex[msg.sender];
|
||||||
|
if (votePos == 0) {
|
||||||
|
votePos = proposal.votes.length;
|
||||||
|
} else {
|
||||||
|
votePos = votePos - 1;
|
||||||
|
}
|
||||||
|
VoteTicket storage ticket = proposal.votes[votePos];
|
||||||
|
assert (ticket.voter == 0x0 || ticket.voter == msg.sender);
|
||||||
|
ticket.voter = msg.sender;
|
||||||
|
ticket.vote = _vote;
|
||||||
|
proposal.voteIndex[msg.sender] = votePos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tabulate(uint _proposal, uint loopLimit) {
|
||||||
|
Proposal storage proposal = proposals[_proposal];
|
||||||
|
require(block.number > proposal.vetoBlockEnd);
|
||||||
|
require(!proposal.tabulated);
|
||||||
|
|
||||||
|
uint totalVoted = proposal.votes.length;
|
||||||
|
if (loopLimit == 0) {
|
||||||
|
loopLimit = totalVoted;
|
||||||
|
}
|
||||||
|
require (loopLimit <= totalVoted);
|
||||||
|
require (loopLimit > proposal.tabulationPosition);
|
||||||
|
|
||||||
|
DelegationProxy voteDelegation;
|
||||||
|
DelegationProxy vetoDelegation;
|
||||||
|
(voteDelegation, vetoDelegation) = trustNet.getTopic(proposal.topic);
|
||||||
|
|
||||||
|
for (uint i = proposal.tabulationPosition; i < loopLimit; i++) {
|
||||||
|
VoteTicket memory _vote = proposal.votes[i];
|
||||||
|
if (_vote.vote == Vote.Reject || _vote.vote == Vote.Approve) {
|
||||||
|
proposal.results[uint8(_vote.vote)] += voteDelegation.influenceOfAt(_vote.voter, SNT, proposal.voteBlockEnd);
|
||||||
|
} else {
|
||||||
|
proposal.results[uint8(_vote.vote)] += vetoDelegation.influenceOfAt(_vote.voter, SNT, proposal.vetoBlockEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proposal.tabulationPosition = i;
|
||||||
|
if (proposal.tabulationPosition == totalVoted) {
|
||||||
|
proposal.tabulated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
pragma solidity ^0.4.10;
|
||||||
|
|
||||||
|
import "../common/Controlled.sol";
|
||||||
|
import "./DelegationProxyFactory.sol";
|
||||||
|
import "./DelegationProxy.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title TrustNetwork
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* Defines two contolled DelegationProxy chains: vote and veto chains.
|
||||||
|
* New layers need to be defined under a unique topic address topic, and all fall back to root topic (topic 0x0)
|
||||||
|
*/
|
||||||
|
contract TrustNetwork is Controlled {
|
||||||
|
mapping (address => Topic) topics;
|
||||||
|
DelegationProxyFactory delegationFactory;
|
||||||
|
|
||||||
|
struct Topic {
|
||||||
|
DelegationProxy voteProxy;
|
||||||
|
DelegationProxy vetoProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TrustNetwork(address _delegationFactory) public {
|
||||||
|
delegationFactory = DelegationProxyFactory(_delegationFactory);
|
||||||
|
topics[0x0] = newTopic(0x0, 0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTopic(address topicId, address parentTopic) onlyController {
|
||||||
|
|
||||||
|
Topic memory parent = topics[parentTopic];
|
||||||
|
address vote = address(parent.voteProxy);
|
||||||
|
address veto = address(parent.vetoProxy);
|
||||||
|
require(vote != 0x0);
|
||||||
|
require(veto != 0x0);
|
||||||
|
|
||||||
|
Topic storage topic = topics[topicId];
|
||||||
|
require(address(topic.voteProxy) == 0x0);
|
||||||
|
require(address(topic.vetoProxy) == 0x0);
|
||||||
|
|
||||||
|
|
||||||
|
topics[topicId] = newTopic(vote, veto);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTopic(address _topicId) public constant returns (DelegationProxy vote, DelegationProxy veto) {
|
||||||
|
Topic memory topic = topics[_topicId];
|
||||||
|
vote = topic.voteProxy;
|
||||||
|
veto = topic.vetoProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newTopic(address _vote, address _veto) internal returns (Topic topic) {
|
||||||
|
topic = Topic ({
|
||||||
|
voteProxy: delegationFactory.create(_vote),
|
||||||
|
vetoProxy: delegationFactory.create(_veto)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
pragma solidity ^0.4.10;
|
||||||
|
|
||||||
|
import "./TrustNetwork.sol";
|
||||||
|
import "../deploy/KillableModel.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title TrustNetworkModel
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* Model for TrustNetwork
|
||||||
|
*/
|
||||||
|
contract TrustNetworkModel is KillableModel, TrustNetwork {
|
||||||
|
|
||||||
|
|
||||||
|
function TrustNetworkModel(address _watchdog) KillableModel(_watchdog) TrustNetwork(0x0) public {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(address _delegationFactory) public {
|
||||||
|
require(topics[0x0].voteProxy == address(0x0));
|
||||||
|
delegationFactory = DelegationProxyFactory(_delegationFactory);
|
||||||
|
topics[0x0] = newTopic(0x0, 0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,312 @@
|
||||||
|
const DelegationProxy = artifacts.require("DelegationProxy.sol")
|
||||||
|
const MiniMeTokenFactory = artifacts.require("MiniMeTokenFactory.sol")
|
||||||
|
const MiniMeToken = artifacts.require("MiniMeToken.sol")
|
||||||
|
const TestUtils = require("./TestUtils.js")
|
||||||
|
|
||||||
|
// TODO: This a very minimal set of tests purely for understanding the contract. I think they can be used though.
|
||||||
|
contract("DelegationProxy", accounts => {
|
||||||
|
|
||||||
|
//Initialize global/common contracts for all tests
|
||||||
|
let delegationProxy = []
|
||||||
|
const delegateTo = [
|
||||||
|
[
|
||||||
|
accounts[1],
|
||||||
|
accounts[2],
|
||||||
|
0x0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x0,
|
||||||
|
accounts[2],
|
||||||
|
0x0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
const delegationOf = [
|
||||||
|
[
|
||||||
|
accounts[2],
|
||||||
|
accounts[2],
|
||||||
|
accounts[2]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
accounts[0],
|
||||||
|
accounts[2],
|
||||||
|
accounts[2]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
const delegatedInfluence = [
|
||||||
|
[0, 1, 2],
|
||||||
|
[0, 0, 1]
|
||||||
|
]
|
||||||
|
|
||||||
|
const influence = [
|
||||||
|
[0, 0, 3],
|
||||||
|
[1, 0, 2]
|
||||||
|
]
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
delegationProxy[0] = await DelegationProxy.new(0)
|
||||||
|
delegationProxy[1] = await DelegationProxy.new(delegationProxy[0].address)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("delegate(address _to)", () => {
|
||||||
|
|
||||||
|
it("creates Delegate Log event", async () => {
|
||||||
|
const i = 0
|
||||||
|
const j = 0
|
||||||
|
delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]})
|
||||||
|
const delegateArgs = await TestUtils.listenForEvent(delegationProxy[i].Delegate())
|
||||||
|
assert.equal(delegateArgs.who, accounts[j], "["+i+","+j+"] Delegate Log shows delegating from isn't sender")
|
||||||
|
assert.equal(delegateArgs.to, delegateTo[i][j], "["+i+","+j+"]Delegate Log shows delegating to isn't passed address")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it("updates delegations mapping with new delegate", async () => {
|
||||||
|
const i = 0
|
||||||
|
const j = 0
|
||||||
|
await delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]})
|
||||||
|
const delegations = await delegationProxy[i].delegations.call(accounts[j], 0)
|
||||||
|
assert.equal(delegations[0].c, web3.eth.blockNumber, "["+i+","+j+"] Delegations block number is incorrect")
|
||||||
|
assert.equal(delegations[1], delegateTo[i][j], "["+i+","+j+"] Delegations to account is incorrect")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("stores delegation checkpoints correctly", async () => {
|
||||||
|
const delegateTo2 = [
|
||||||
|
[
|
||||||
|
0x0,
|
||||||
|
accounts[0],
|
||||||
|
accounts[0]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
accounts[2],
|
||||||
|
0x0,
|
||||||
|
accounts[1]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
const delegationOf2 = [
|
||||||
|
[
|
||||||
|
accounts[0],
|
||||||
|
accounts[0],
|
||||||
|
accounts[0]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
accounts[1],
|
||||||
|
accounts[1],
|
||||||
|
accounts[1]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
const delegateTo3 = [
|
||||||
|
[
|
||||||
|
0x0,
|
||||||
|
0x0,
|
||||||
|
0x0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0x0,
|
||||||
|
0x0,
|
||||||
|
0x0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
const delegationOf3 = [
|
||||||
|
[
|
||||||
|
accounts[0],
|
||||||
|
accounts[1],
|
||||||
|
accounts[2]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
accounts[0],
|
||||||
|
accounts[1],
|
||||||
|
accounts[2]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
for (var i = 0; i < delegateTo.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blockn1 = web3.eth.blockNumber
|
||||||
|
|
||||||
|
for (var i = 0; i < delegateTo2.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo2[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo2[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blockn2 = web3.eth.blockNumber
|
||||||
|
|
||||||
|
for (var i = 0; i < delegateTo3.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo3[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo3[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blockn3 = web3.eth.blockNumber
|
||||||
|
|
||||||
|
for (var i = 0; i < delegateTo.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegatedToAt(accounts[j], blockn1),
|
||||||
|
delegateTo[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegatedToAt("+accounts[j]+", +"+blockn1+") is incorrect")
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegatedToAt(accounts[j], blockn2),
|
||||||
|
delegateTo2[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegatedToAt("+accounts[j]+", +"+blockn2+") is incorrect")
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegatedToAt(accounts[j], blockn3),
|
||||||
|
delegateTo3[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegatedToAt("+accounts[j]+", +"+blockn3+") is incorrect")
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegationOfAt(accounts[j], blockn1),
|
||||||
|
delegationOf[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegationOfAt("+accounts[j]+", +"+blockn1+") is incorrect")
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegationOfAt(accounts[j], blockn2),
|
||||||
|
delegationOf2[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegationOfAt("+accounts[j]+", +"+blockn2+") is incorrect")
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegationOfAt(accounts[j], blockn3),
|
||||||
|
delegationOf3[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegationOfAt("+accounts[j]+", +"+blockn3+") is incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it("delegates back to parentProxy", async () => {
|
||||||
|
for (var i = 0; i < delegateTo.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blockn1 = web3.eth.blockNumber
|
||||||
|
|
||||||
|
for (var j = 0; j < delegateTo[1].length; j++) {
|
||||||
|
await delegationProxy[1].delegate(delegationProxy[0].address, {from: accounts[j]});
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockn2 = web3.eth.blockNumber
|
||||||
|
|
||||||
|
for (var j = 0; j < delegateTo[1].length; j++) {
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[1].delegatedToAt(accounts[j], blockn1),
|
||||||
|
delegateTo[1][j],
|
||||||
|
"["+j+"] +"+delegationProxy[1].address+".delegatedToAt("+accounts[j]+", +"+blockn1+") is incorrect")
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[1].delegatedToAt(accounts[j], blockn2),
|
||||||
|
delegateTo[0][j],
|
||||||
|
"["+j+"] +"+delegationProxy[1].address+".delegatedToAt("+accounts[j]+", +"+blockn2+") is incorrect")
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[1].delegationOfAt(accounts[j], blockn1),
|
||||||
|
delegationOf[1][j],
|
||||||
|
"["+j+"] +"+delegationProxy[1].address+".delegationOfAt("+accounts[j]+", +"+blockn1+") is incorrect")
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[1].delegationOfAt(accounts[j], blockn2),
|
||||||
|
delegationOf[0][j],
|
||||||
|
"["+j+"] +"+delegationProxy[1].address+".delegationOfAt("+accounts[j]+", +"+blockn2+") is incorrect")
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("delegatedToAt(address _who, uint _block)", () => {
|
||||||
|
|
||||||
|
it("returns correctly delegated to address", async () => {
|
||||||
|
let delegatedTo
|
||||||
|
for (var i = 0; i < delegateTo.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]});
|
||||||
|
delegatedTo = await delegationProxy[i].delegatedToAt(accounts[j], web3.eth.blockNumber)
|
||||||
|
assert.equal(
|
||||||
|
delegatedTo,
|
||||||
|
delegateTo[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegatedToAt("+accounts[j]+", +"+web3.eth.blockNumber+") is incorrect")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("delegationOfAt(address _who, uint _block)", () => {
|
||||||
|
|
||||||
|
it("returns correctly delegation endpoints of address", async () => {
|
||||||
|
let result = [[],[]]
|
||||||
|
for (var i = 0; i < delegateTo.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
for (j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
assert.equal(
|
||||||
|
await delegationProxy[i].delegationOfAt(accounts[j], web3.eth.blockNumber),
|
||||||
|
delegationOf[i][j],
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegationOfAt("+accounts[j]+", +"+web3.eth.blockNumber+") is incorrect"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("delegatedInfluenceFromAt(address _who, address _token, uint _block)", () => {
|
||||||
|
|
||||||
|
let miniMeTokenFactory
|
||||||
|
let miniMeToken
|
||||||
|
const tokensBalance = 1000
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
miniMeTokenFactory = await MiniMeTokenFactory.new();
|
||||||
|
miniMeToken = await MiniMeToken.new(miniMeTokenFactory.address, 0, 0, "TestToken", 18, "TTN", true)
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
miniMeToken.generateTokens(accounts[i], tokensBalance)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns expected amount of influence delegated from", async () => {
|
||||||
|
|
||||||
|
for (var i = 0; i < delegationProxy.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
await delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
for (var j = 0; j < delegatedInfluence[i].length; j++) {
|
||||||
|
assert.equal(
|
||||||
|
delegatedInfluence[i][j] * tokensBalance,
|
||||||
|
(await delegationProxy[i].delegatedInfluenceFromAt(accounts[j], miniMeToken.address, web3.eth.blockNumber)).toNumber(),
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".delegatedInfluenceFrom("+accounts[j]+", +"+web3.eth.blockNumber+") is not as expected"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("influenceOfAt(address _who, address _token, uint _block)", () => {
|
||||||
|
let miniMeTokenFactory
|
||||||
|
let miniMeToken
|
||||||
|
const tokensBalance = 1000
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
miniMeTokenFactory = await MiniMeTokenFactory.new();
|
||||||
|
miniMeToken = await MiniMeToken.new(miniMeTokenFactory.address, 0, 0, "TestToken", 18, "TTN", true)
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
miniMeToken.generateTokens(accounts[i], tokensBalance)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns expected influence", async () => {
|
||||||
|
|
||||||
|
for (var i = 0; i < influence.length; i++) {
|
||||||
|
for (var j = 0; j < delegateTo[i].length; j++) {
|
||||||
|
delegationProxy[i].delegate(delegateTo[i][j], {from: accounts[j]});
|
||||||
|
}
|
||||||
|
for (var j = 0; j < influence[i].length; j++) {
|
||||||
|
assert.equal(
|
||||||
|
influence[i][j] * tokensBalance,
|
||||||
|
(await delegationProxy[i].influenceOfAt(accounts[j], miniMeToken.address, web3.eth.blockNumber)).toNumber(),
|
||||||
|
"["+i+","+j+"] +"+delegationProxy[i].address+".influenceOfAt("+accounts[j]+", +"+web3.eth.blockNumber+") is not as expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
Loading…
Reference in New Issue