commit c07850d068635b61c95b2821846b4e62f07dedfa Author: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Tue Nov 28 01:33:25 2017 -0200 base structure of upgradable republic diff --git a/contracts/democracy/DelegationProxy.sol b/contracts/democracy/DelegationProxy.sol new file mode 100644 index 0000000..15dfba7 --- /dev/null +++ b/contracts/democracy/DelegationProxy.sol @@ -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 + } + +} \ No newline at end of file diff --git a/contracts/democracy/DelegationProxyFactory.sol b/contracts/democracy/DelegationProxyFactory.sol new file mode 100644 index 0000000..1a1ee09 --- /dev/null +++ b/contracts/democracy/DelegationProxyFactory.sol @@ -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)); + } + +} \ No newline at end of file diff --git a/contracts/democracy/DelegationProxyModel.sol b/contracts/democracy/DelegationProxyModel.sol new file mode 100644 index 0000000..4d7bb2f --- /dev/null +++ b/contracts/democracy/DelegationProxyModel.sol @@ -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; + } + +} \ No newline at end of file diff --git a/contracts/democracy/ProposalManager.sol b/contracts/democracy/ProposalManager.sol new file mode 100644 index 0000000..f4d4a7a --- /dev/null +++ b/contracts/democracy/ProposalManager.sol @@ -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; + } + } + +} \ No newline at end of file diff --git a/contracts/democracy/TrustNetwork.sol b/contracts/democracy/TrustNetwork.sol new file mode 100644 index 0000000..3fccf1e --- /dev/null +++ b/contracts/democracy/TrustNetwork.sol @@ -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) + }); + } + + + + +} \ No newline at end of file diff --git a/contracts/democracy/TrustNetworkModel.sol b/contracts/democracy/TrustNetworkModel.sol new file mode 100644 index 0000000..d02c127 --- /dev/null +++ b/contracts/democracy/TrustNetworkModel.sol @@ -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); + } + + + +} \ No newline at end of file diff --git a/test/DelegationProxyTest.js b/test/DelegationProxyTest.js new file mode 100644 index 0000000..93a9067 --- /dev/null +++ b/test/DelegationProxyTest.js @@ -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") + } + } + }) + }) + +}) \ No newline at end of file