diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index e1c0fbfafb..b674da187e 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -50,7 +50,7 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { require(domainExists(_domainId), "colony-domain-does-not-exist"); IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, domains[_domainId].skillId); - emit ArbitraryReputationUpdate(msg.sender, _user, domains[_domainId].skillId, _amount); + emit ArbitraryReputationUpdate(msgSender(), _user, domains[_domainId].skillId, _amount); } function emitSkillReputationReward(uint256 _skillId, address _user, int256 _amount) @@ -59,7 +59,7 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { require(_amount > 0, "colony-reward-must-be-positive"); IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, _skillId); - emit ArbitraryReputationUpdate(msg.sender, _user, _skillId, _amount); + emit ArbitraryReputationUpdate(msgSender(), _user, _skillId, _amount); } function emitDomainReputationPenalty( @@ -73,7 +73,7 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { require(_amount <= 0, "colony-penalty-cannot-be-positive"); IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, domains[_domainId].skillId); - emit ArbitraryReputationUpdate(msg.sender, _user, domains[_domainId].skillId, _amount); + emit ArbitraryReputationUpdate(msgSender(), _user, domains[_domainId].skillId, _amount); } function emitSkillReputationPenalty(uint256 _skillId, address _user, int256 _amount) @@ -82,7 +82,7 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { require(_amount <= 0, "colony-penalty-cannot-be-positive"); IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, _skillId); - emit ArbitraryReputationUpdate(msg.sender, _user, _skillId, _amount); + emit ArbitraryReputationUpdate(msgSender(), _user, _skillId, _amount); } function initialiseColony(address _colonyNetworkAddress, address _token) public stoppable { @@ -274,6 +274,7 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { function addDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _parentDomainId) public stoppable + domainNotDeprecated(_parentDomainId) authDomain(_permissionDomainId, _childSkillIndex, _parentDomainId) { addDomain(_permissionDomainId, _childSkillIndex, _parentDomainId, ""); @@ -310,8 +311,20 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { } } - function getDomain(uint256 _id) public view returns (Domain memory domain) { - domain = domains[_id]; + function deprecateDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bool _deprecated) public + stoppable + authDomain(_permissionDomainId, _childSkillIndex, _domainId) + { + if (domains[_domainId].deprecated != _deprecated) { + domains[_domainId].deprecated = _deprecated; + + emit DomainDeprecated(msgSender(), _domainId, _deprecated); + } + + } + + function getDomain(uint256 _domainId) public view returns (Domain memory domain) { + domain = domains[_domainId]; } function getDomainCount() public view returns (uint256) { @@ -366,19 +379,13 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { emit ColonyUpgraded(msgSender(), currentVersion, _newVersion); } - // v7 to v8 + // v8 to v9 function finishUpgrade() public always { ColonyAuthority colonyAuthority = ColonyAuthority(address(authority)); bytes4 sig; - sig = bytes4(keccak256("makeArbitraryTransactions(address[],bytes[],bool)")); - colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); - - sig = bytes4(keccak256("setDefaultGlobalClaimDelay(uint256)")); - colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); - - sig = bytes4(keccak256("setExpenditureMetadata(uint256,uint256,uint256,string)")); - colonyAuthority.setRoleCapability(uint8(ColonyRole.Arbitration), address(this), sig, true); + sig = bytes4(keccak256("deprecateDomain(uint256,uint256,uint256,bool)")); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Architecture), address(this), sig, true); } function getMetatransactionNonce(address _user) override public view returns (uint256 nonce){ @@ -465,7 +472,8 @@ contract Colony is BasicMetaTransaction, ColonyStorage, PatriciaTreeProofs { // Create a new domain with the given skill and new funding pot domains[domainCount] = Domain({ skillId: _skillId, - fundingPotId: fundingPotCount + fundingPotId: fundingPotCount, + deprecated: false }); emit DomainAdded(msgSender(), domainCount); diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index b89f53aeaa..f9a703dca1 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -118,6 +118,9 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ROOT_ROLE, "makeArbitraryTransactions(address[],bytes[],bool)"); addRoleCapability(ROOT_ROLE, "setDefaultGlobalClaimDelay(uint256)"); addRoleCapability(ARBITRATION_ROLE, "setExpenditureMetadata(uint256,uint256,uint256,string)"); + + // Added in colony v9 (f-lwss) + addRoleCapability(ARCHITECTURE_ROLE, "deprecateDomain(uint256,uint256,uint256,bool)"); } function addRoleCapability(uint8 role, bytes memory sig) private { diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index 0c020feafc..bead4535f1 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -252,6 +252,12 @@ interface ColonyDataTypes { /// @param metadata IPFS hash of the metadata event DomainMetadata(address agent, uint256 indexed domainId, string metadata); + /// @notice Event logged when domain metadata is updated + /// @param agent The address that is responsible for triggering this event + /// @param domainId Id of the domain + /// @param deprecated Whether or not the domain is deprecated + event DomainDeprecated(address agent, uint256 indexed domainId, bool deprecated); + /// @notice Event logged when Colony metadata is updated /// @param agent The address that is responsible for triggering this event /// @param metadata IPFS hash of the metadata @@ -410,5 +416,6 @@ interface ColonyDataTypes { struct Domain { uint256 skillId; uint256 fundingPotId; + bool deprecated; } } diff --git a/contracts/colony/ColonyExpenditure.sol b/contracts/colony/ColonyExpenditure.sol index 3bbba7f024..e817981669 100644 --- a/contracts/colony/ColonyExpenditure.sol +++ b/contracts/colony/ColonyExpenditure.sol @@ -40,6 +40,7 @@ contract ColonyExpenditure is ColonyStorage { function makeExpenditure(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId) public stoppable + domainNotDeprecated(_domainId) authDomain(_permissionDomainId, _childSkillIndex, _domainId) returns (uint256) { diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index d16f0f774f..8230abbfd5 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -218,6 +218,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123 ) public stoppable + domainNotDeprecated(getDomainFromFundingPot(_toPot)) authDomain(_permissionDomainId, _childSkillIndex, _domainId) validFundingTransfer(_fromPot, _toPot) { @@ -238,6 +239,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs { // ignore-swc-123 ) public stoppable + domainNotDeprecated(getDomainFromFundingPot(_toPot)) authDomain(_permissionDomainId, _fromChildSkillIndex, getDomainFromFundingPot(_fromPot)) authDomain(_permissionDomainId, _toChildSkillIndex, getDomainFromFundingPot(_toPot)) validFundingTransfer(_fromPot, _toPot) diff --git a/contracts/colony/ColonyStorage.sol b/contracts/colony/ColonyStorage.sol index e38726c5e0..1a39a55b4b 100755 --- a/contracts/colony/ColonyStorage.sol +++ b/contracts/colony/ColonyStorage.sol @@ -115,6 +115,11 @@ contract ColonyStorage is ColonyDataTypes, ColonyNetworkDataTypes, DSMath, Commo // Modifiers + modifier domainNotDeprecated(uint256 _id) { + require(!domains[_id].deprecated, "colony-domain-deprecated"); + _; + } + modifier validPayoutAmount(uint256 _amount) { require(_amount <= MAX_PAYOUT, "colony-payout-too-large"); _; diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 0a5d43af99..10245ec208 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -308,6 +308,13 @@ interface IColony is ColonyDataTypes, IRecovery, IBasicMetaTransaction { /// @param _metadata Metadata relating to the domain. Expected to be the IPFS hash of a JSON blob, but not enforced by the contracts. function editDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, string memory _metadata) external; + /// @notice Deprecate a domain, preventing certain actions from happening there + /// @param _permissionDomainId The domainId in which I have the permission to take this action + /// @param _childSkillIndex The index that the `_domainId` is relative to `_permissionDomainId` + /// @param _domainId Id of the domain being deprecated + /// @param _deprecated Whether or not the domain is deprecated + function deprecateDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bool _deprecated) external; + /// @notice Get a domain by id. /// @param _id Id of the domain which details to get /// @return domain The domain diff --git a/contracts/testHelpers/NoLimitSubdomains.sol b/contracts/testHelpers/NoLimitSubdomains.sol index 153e38967b..4637b49141 100644 --- a/contracts/testHelpers/NoLimitSubdomains.sol +++ b/contracts/testHelpers/NoLimitSubdomains.sol @@ -46,7 +46,8 @@ contract NoLimitSubdomains is ColonyStorage { // Create a new domain with the given skill and new funding pot domains[domainCount] = Domain({ skillId: _skillId, - fundingPotId: fundingPotCount + fundingPotId: fundingPotCount, + deprecated: false }); emit DomainAdded(msg.sender, domainCount); diff --git a/docs/_Interface_IColony.md b/docs/_Interface_IColony.md index 0f6d187444..28cc6b2cf2 100644 --- a/docs/_Interface_IColony.md +++ b/docs/_Interface_IColony.md @@ -248,6 +248,21 @@ Deobligate the user some amount of tokens, releasing the stake. |_amount|uint256|Amount of internal token we are deobligating. +### `deprecateDomain` + +Deprecate a domain, preventing certain actions from happening there + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId` +|_domainId|uint256|Id of the domain being deprecated +|_deprecated|bool|Whether or not the domain is deprecated + + ### `deprecateExtension` Set the deprecation of an extension in a colony. Secured function to authorised members. diff --git a/test-smoke/colony-storage-consistent.js b/test-smoke/colony-storage-consistent.js index 8a83d7a29e..f16e3b66fc 100644 --- a/test-smoke/colony-storage-consistent.js +++ b/test-smoke/colony-storage-consistent.js @@ -154,11 +154,11 @@ contract("Contract Storage", (accounts) => { console.log("miningCycleStateHash:", miningCycleAccount.stateRoot.toString("hex")); console.log("tokenLockingStateHash:", tokenLockingAccount.stateRoot.toString("hex")); - expect(colonyNetworkAccount.stateRoot.toString("hex")).to.equal("cbe7c27231f4c94f1fdff92a685599c6ada69af16a0f32ab3a72e85334a4a2ca"); - expect(colonyAccount.stateRoot.toString("hex")).to.equal("b198bd282eac14f7dc4d71817c4184462c6db7ae11a8f7662edecd4afbba6234"); - expect(metaColonyAccount.stateRoot.toString("hex")).to.equal("c91229b9b01734f45e65feea0561ed90bee1365c953ceb187e87e80e9a96ef86"); - expect(miningCycleAccount.stateRoot.toString("hex")).to.equal("f1ae4c855f083837446bc31b3355ca0291daa363dd9afb655de51829a46fc635"); - expect(tokenLockingAccount.stateRoot.toString("hex")).to.equal("860aa632a7a9a21119b0e27c30d0c3f4da5916eb5263c7870d4dda3eb7162e32"); + expect(colonyNetworkAccount.stateRoot.toString("hex")).to.equal("099e984edc5c6d9194d5b26a5ec058f2ba341cf591781fe94f65900f9a72fa7a"); + expect(colonyAccount.stateRoot.toString("hex")).to.equal("ba1042d654baa721eb012da81409c1087a9447b8a36b6d844233826e5055fbfe"); + expect(metaColonyAccount.stateRoot.toString("hex")).to.equal("352feafb44e40c6097234df1a675c5cd618fd81c0f88e8c6478c838e788bd77a"); + expect(miningCycleAccount.stateRoot.toString("hex")).to.equal("474e00d9b002118dee0c336eaf0902b7719bb7d4a877e2d26f6a2452a5a89a6e"); + expect(tokenLockingAccount.stateRoot.toString("hex")).to.equal("5f5b79a400ce5a1a62416a7c8343245aacfa7f11ac97641c4ddefe7cce1927a2"); }); }); }); diff --git a/test/contracts-network/colony.js b/test/contracts-network/colony.js index 62eef25780..017a66521b 100755 --- a/test/contracts-network/colony.js +++ b/test/contracts-network/colony.js @@ -200,6 +200,27 @@ contract("Colony", (accounts) => { }); }); + describe("when deprecating domains", () => { + it("should log the DomainDeprecated event", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + await expectEvent(colony.deprecateDomain(1, 0, 2, true), "DomainDeprecated", [USER0, 2, true]); + }); + + it("should not log the DomainDeprecated event if the state did not change", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + await expectNoEvent(colony.deprecateDomain(1, 0, 2, false), "DomainDeprecated"); + }); + + it("should not be able to perform prohibited actions in the domain", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + await colony.deprecateDomain(1, 0, 2, true); + + await checkErrorRevert(colony.addDomain(1, 0, 2), "colony-domain-deprecated"); + await checkErrorRevert(colony.makeExpenditure(1, 0, 2), "colony-domain-deprecated"); + await checkErrorRevert(colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, 0, 1, 2, 100, token.address), "colony-domain-deprecated"); + }); + }); + describe("when bootstrapping the colony", () => { const INITIAL_REPUTATIONS = [WAD.muln(5), WAD.muln(4), WAD.muln(3), WAD.muln(2)]; const INITIAL_ADDRESSES = accounts.slice(0, 4);