From 9b4f9e3b4c56fe3d92861c239c64094f4209fc35 Mon Sep 17 00:00:00 2001 From: Daniel Kronovet Date: Wed, 9 Sep 2020 09:41:15 -0700 Subject: [PATCH] Update VotingReputation to use extension manager --- contracts/colony/ColonyRoles.sol | 10 ++- contracts/extensions/ColonyExtension.sol | 4 +- contracts/extensions/VotingReputation.sol | 38 ++++++-- .../extensions/VotingReputationFactory.sol | 48 ----------- test/extensions/voting-rep.js | 86 ++++++++----------- 5 files changed, 75 insertions(+), 111 deletions(-) delete mode 100644 contracts/extensions/VotingReputationFactory.sol diff --git a/contracts/colony/ColonyRoles.sol b/contracts/colony/ColonyRoles.sol index 2fb05c8b22..c295ee09ff 100644 --- a/contracts/colony/ColonyRoles.sol +++ b/contracts/colony/ColonyRoles.sol @@ -138,7 +138,11 @@ contract ColonyRoles is ColonyStorage { ); } - function getUserRoles(address who, uint256 where) public view returns (bytes32) { - return ColonyAuthority(address(authority)).getUserRoles(who, where); + function getUserRoles(address _user, uint256 _domain) public view returns (bytes32) { + return ColonyAuthority(address(authority)).getUserRoles(_user, _domain); } -} \ No newline at end of file + + function getCapabilityRoles(bytes4 _sig) public view returns (bytes32) { + return ColonyAuthority(address(authority)).getCapabilityRoles(address(this), _sig); + } +} diff --git a/contracts/extensions/ColonyExtension.sol b/contracts/extensions/ColonyExtension.sol index 448356b173..6ad5c3e04d 100644 --- a/contracts/extensions/ColonyExtension.sol +++ b/contracts/extensions/ColonyExtension.sol @@ -18,8 +18,8 @@ pragma solidity 0.5.8; pragma experimental ABIEncoderV2; -import "../common/EtherRouter.sol"; -import "../colony/IColony.sol"; +import "./../common/EtherRouter.sol"; +import "./../colony/IColony.sol"; contract ColonyExtension is DSAuth { diff --git a/contracts/extensions/VotingReputation.sol b/contracts/extensions/VotingReputation.sol index 2e84bcdb61..897dd1cf6f 100644 --- a/contracts/extensions/VotingReputation.sol +++ b/contracts/extensions/VotingReputation.sol @@ -27,12 +27,13 @@ import "./../common/ERC20Extended.sol"; import "./../patriciaTree/PatriciaTreeProofs.sol"; import "./../tokenLocking/ITokenLocking.sol"; +import "./ColonyExtension.sol"; -contract VotingReputation is DSMath, PatriciaTreeProofs { + +contract VotingReputation is ColonyExtension, DSMath, PatriciaTreeProofs { // Events event ExtensionInitialised(); - event ExtensionDeprecated(); event MotionCreated(uint256 indexed motionId, address creator, uint256 indexed domainId); event MotionStaked(uint256 indexed motionId, address indexed staker, uint256 indexed vote, uint256 amount); event MotionVoteSubmitted(uint256 indexed motionId, address indexed voter); @@ -93,7 +94,17 @@ contract VotingReputation is DSMath, PatriciaTreeProofs { uint256 revealPeriod; // Length of time for revealing votes uint256 escalationPeriod; // Length of time for escalating after a vote - constructor(address _colony) public { + /// @notice Return the version number + /// @return The version number + function version() public pure returns (uint256) { + return 1; + } + + /// @notice Install the extension + /// @param _colony Base colony for the installation + function install(address _colony) public { + require(address(colony) == address(0x0), "extension-already-installed"); + colony = IColony(_colony); colonyNetwork = IColonyNetwork(colony.getColonyNetwork()); tokenLocking = ITokenLocking(colonyNetwork.getTokenLocking()); @@ -121,7 +132,11 @@ contract VotingReputation is DSMath, PatriciaTreeProofs { ) public { - require(colony.hasUserRole(msg.sender, 1, ColonyDataTypes.ColonyRole.Root), "voting-rep-user-not-root"); + require( + colony.hasUserRole(msg.sender, 1, ColonyDataTypes.ColonyRole.Root), + "voting-rep-user-not-root" + ); + require(state == ExtensionState.Deployed, "voting-rep-already-initialised"); require(_totalStakeFraction <= WAD / 2, "voting-rep-greater-than-half-wad"); @@ -151,13 +166,17 @@ contract VotingReputation is DSMath, PatriciaTreeProofs { emit ExtensionInitialised(); } - /// @notice Deprecate the extension, prevening new motions from being created - function deprecate() public { - require(colony.hasUserRole(msg.sender, 1, ColonyDataTypes.ColonyRole.Root), "voting-rep-user-not-root"); + /// @notice Called when upgrading the extension + function finishUpgrade() public auth {} - state = ExtensionState.Deprecated; + /// @notice Called when deprecating (or undeprecating) the extension + function deprecate(bool _deprecated) public auth { + deprecated = _deprecated; + } - emit ExtensionDeprecated(); + /// @notice Called when uninstalling the extension + function uninstall() public auth { + selfdestruct(address(uint160(address(colony)))); } // Data structures @@ -803,6 +822,7 @@ contract VotingReputation is DSMath, PatriciaTreeProofs { bytes32[] memory _siblings ) internal + undeprecated { require(state == ExtensionState.Active, "voting-rep-not-active"); require(_altTarget != address(colony), "voting-rep-alt-target-cannot-be-base-colony"); diff --git a/contracts/extensions/VotingReputationFactory.sol b/contracts/extensions/VotingReputationFactory.sol deleted file mode 100644 index 211deeb24a..0000000000 --- a/contracts/extensions/VotingReputationFactory.sol +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of The Colony Network. - - The Colony Network is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - The Colony Network is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with The Colony Network. If not, see . -*/ - -pragma solidity 0.5.8; -pragma experimental ABIEncoderV2; - -import "./../colony/ColonyDataTypes.sol"; -import "./../colony/IColony.sol"; -import "./ExtensionFactory.sol"; -import "./VotingReputation.sol"; - - -contract VotingReputationFactory is ExtensionFactory, ColonyDataTypes { // ignore-swc-123 - mapping (address => VotingReputation) public deployedExtensions; - - modifier isRoot(address _colony) { - require(IColony(_colony).hasUserRole(msg.sender, 1, ColonyRole.Root), "colony-extension-user-not-root"); // ignore-swc-123 - _; - } - - function deployExtension(address _colony) external isRoot(_colony) { - require(deployedExtensions[_colony] == VotingReputation(0x00), "colony-extension-already-deployed"); - VotingReputation newExtensionAddress = new VotingReputation(_colony); - deployedExtensions[_colony] = newExtensionAddress; - - emit ExtensionDeployed("VotingReputation", _colony, address(newExtensionAddress)); - } - - function removeExtension(address _colony) external isRoot(_colony) { - deployedExtensions[_colony] = VotingReputation(0x00); - - emit ExtensionRemoved("VotingReputation", _colony); - } -} diff --git a/test/extensions/voting-rep.js b/test/extensions/voting-rep.js index 9d2069d26d..ea6e95e1b7 100644 --- a/test/extensions/voting-rep.js +++ b/test/extensions/voting-rep.js @@ -25,17 +25,21 @@ import { giveUserCLNYTokensAndStake, } from "../../helpers/test-data-generator"; +import { setupEtherRouter } from "../../helpers/upgradable-contracts"; + import PatriciaTree from "../../packages/reputation-miner/patricia"; const { expect } = chai; chai.use(bnChai(web3.utils.BN)); -const TokenLocking = artifacts.require("TokenLocking"); const IReputationMiningCycle = artifacts.require("IReputationMiningCycle"); +const TokenLocking = artifacts.require("TokenLocking"); const VotingReputation = artifacts.require("VotingReputation"); -const VotingReputationFactory = artifacts.require("VotingReputationFactory"); +const Resolver = artifacts.require("Resolver"); + +const VOTING_REPUTATION = soliditySha3("VotingReputation"); -contract("Voting Reputation", (accounts) => { +contract.only("Voting Reputation", (accounts) => { let colony; let token; let domain1; @@ -46,7 +50,6 @@ contract("Voting Reputation", (accounts) => { let tokenLocking; let voting; - let votingFactory; let reputationTree; @@ -113,7 +116,10 @@ contract("Voting Reputation", (accounts) => { const tokenLockingAddress = await colonyNetwork.getTokenLocking(); tokenLocking = await TokenLocking.at(tokenLockingAddress); - votingFactory = await VotingReputationFactory.new(); + const votingImplementation = await VotingReputation.new(); + const resolver = await Resolver.new(); + await setupEtherRouter("VotingReputation", { VotingReputation: votingImplementation.address }, resolver); + await metaColony.addExtension(VOTING_REPUTATION, resolver.address); }); beforeEach(async () => { @@ -126,8 +132,8 @@ contract("Voting Reputation", (accounts) => { domain2 = await colony.getDomain(2); domain3 = await colony.getDomain(3); - await votingFactory.deployExtension(colony.address); - const votingAddress = await votingFactory.deployedExtensions(colony.address); + await colony.installExtension(VOTING_REPUTATION, 1); + const votingAddress = await colonyNetwork.getExtensionInstallation(VOTING_REPUTATION, colony.address); voting = await VotingReputation.at(votingAddress); await voting.initialise( @@ -232,50 +238,38 @@ contract("Voting Reputation", (accounts) => { return soliditySha3(`0x${action.slice(preamble, preamble + 64 * 4)}${"0".repeat(64)}${action.slice(preamble + 64 * 5, action.length)}`); } - describe("deploying the extension", async () => { - it("can install the extension factory once if root and uninstall", async () => { - ({ colony } = await setupRandomColony(colonyNetwork)); - await checkErrorRevert(votingFactory.deployExtension(colony.address, { from: USER1 }), "colony-extension-user-not-root"); - await votingFactory.deployExtension(colony.address, { from: USER0 }); - await checkErrorRevert(votingFactory.deployExtension(colony.address, { from: USER0 }), "colony-extension-already-deployed"); - await votingFactory.removeExtension(colony.address, { from: USER0 }); - }); + describe("managing the extension", async () => { + it("can install the extension manually", async () => { + voting = await VotingReputation.new(); + await voting.install(colony.address); + + await checkErrorRevert(voting.install(colony.address), "extension-already-installed"); - it("can query for initisalisation values", async () => { - const totalStakeFraction = await voting.getTotalStakeFraction(); - const voterRewardFraction = await voting.getVoterRewardFraction(); - const userMinStakeFraction = await voting.getUserMinStakeFraction(); - const maxVoteFraction = await voting.getMaxVoteFraction(); + await voting.finishUpgrade(); + await voting.deprecate(true); + await voting.uninstall(); + }); - const stakePeriod = await voting.getStakePeriod(); - const submitPeriod = await voting.getSubmitPeriod(); - const revealPeriod = await voting.getRevealPeriod(); - const escalationPeriod = await voting.getEscalationPeriod(); + it("can install the extension with the extension manager", async () => { + ({ colony } = await setupRandomColony(colonyNetwork)); + await colony.installExtension(VOTING_REPUTATION, 1, { from: USER0 }); - expect(totalStakeFraction).to.eq.BN(TOTAL_STAKE_FRACTION); - expect(voterRewardFraction).to.eq.BN(VOTER_REWARD_FRACTION); - expect(userMinStakeFraction).to.eq.BN(USER_MIN_STAKE_FRACTION); - expect(maxVoteFraction).to.eq.BN(MAX_VOTE_FRACTION); + await checkErrorRevert(colony.installExtension(VOTING_REPUTATION, 1, { from: USER0 }), "colony-network-extension-already-installed"); + await checkErrorRevert(colony.uninstallExtension(VOTING_REPUTATION, { from: USER1 }), "ds-auth-unauthorized"); - expect(stakePeriod).to.eq.BN(STAKE_PERIOD); - expect(submitPeriod).to.eq.BN(SUBMIT_PERIOD); - expect(revealPeriod).to.eq.BN(REVEAL_PERIOD); - expect(escalationPeriod).to.eq.BN(ESCALATION_PERIOD); + await colony.uninstallExtension(VOTING_REPUTATION, { from: USER0 }); }); it("can deprecate the extension if root", async () => { - const action = await encodeTxData(colony, "makeTask", [1, UINT256_MAX, FAKE, 1, 0, 0]); - await voting.createRootMotion(ADDRESS_ZERO, action, domain1Key, domain1Value, domain1Mask, domain1Siblings); + await checkErrorRevert(colony.deprecateExtension(VOTING_REPUTATION, true, { from: USER2 }), "ds-auth-unauthorized"); - // Must be root - await checkErrorRevert(voting.deprecate({ from: USER2 }), "voting-rep-user-not-root"); - - await voting.deprecate(); + await colony.deprecateExtension(VOTING_REPUTATION, true); // Cant make new motions! + const action = await encodeTxData(colony, "makeTask", [1, UINT256_MAX, FAKE, 1, 0, 0]); await checkErrorRevert( voting.createRootMotion(ADDRESS_ZERO, action, domain1Key, domain1Value, domain1Mask, domain1Siblings), - "voting-rep-not-active" + "colony-extension-deprecated" ); }); @@ -285,10 +279,8 @@ contract("Voting Reputation", (accounts) => { }); it("cannot initialise with invalid values", async () => { - await votingFactory.removeExtension(colony.address, { from: USER0 }); - await votingFactory.deployExtension(colony.address); - const votingAddress = await votingFactory.deployedExtensions(colony.address); - voting = await VotingReputation.at(votingAddress); + voting = await VotingReputation.new(); + await voting.install(colony.address); await checkErrorRevert(voting.initialise(HALF.addn(1), HALF, WAD, WAD, YEAR, YEAR, YEAR, YEAR), "voting-rep-greater-than-half-wad"); await checkErrorRevert(voting.initialise(HALF, HALF.addn(1), WAD, WAD, YEAR, YEAR, YEAR, YEAR), "voting-rep-greater-than-half-wad"); @@ -1692,12 +1684,8 @@ contract("Voting Reputation", (accounts) => { }); it("can skip the staking phase if no new stake is required", async () => { - // Deploy a new extension with no voter compensation - await votingFactory.removeExtension(colony.address, { from: USER0 }); - await votingFactory.deployExtension(colony.address, { from: USER0 }); - const votingAddress = await votingFactory.deployedExtensions(colony.address); - voting = await VotingReputation.at(votingAddress); - + voting = await VotingReputation.new(); + await voting.install(colony.address); await colony.setArbitrationRole(1, UINT256_MAX, voting.address, 1, true); await voting.initialise(