From 77fd88c3c5003caf2117c4c6ef8baf779fb9b1b0 Mon Sep 17 00:00:00 2001 From: Daniel Kronovet Date: Wed, 3 Jun 2020 08:43:09 -0700 Subject: [PATCH] Add Arbitration reputation penalties --- contracts/colony/Colony.sol | 20 +++++++++++++ contracts/colony/ColonyAuthority.sol | 4 ++- contracts/colony/IColony.sol | 22 ++++++++++++++ docs/_Interface_IColony.md | 31 ++++++++++++++++++++ test/contracts-network/colony-permissions.js | 14 +++++++++ 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index bd08d08ee6..7327701abc 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -117,6 +117,26 @@ contract Colony is ColonyStorage, PatriciaTreeProofs { return token; } + function emitDomainReputationPenalty( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + address _user, + int256 _amount + ) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId) + { + require(_amount <= 0, "colony-penalty-cannot-be-positive"); + IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, domains[_domainId].skillId); + } + + function emitSkillReputationPenalty(uint256 _permissionDomainId, uint256 _skillId, address _user, int256 _amount) + public stoppable validGlobalSkill(_skillId) + { + require(_amount <= 0, "colony-penalty-cannot-be-positive"); + require(isAuthorized(msg.sender, _permissionDomainId, msg.sig), "ds-auth-unauthorized"); + IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, _skillId); + } + function initialiseColony(address _colonyNetworkAddress, address _token) public stoppable { require(colonyNetworkAddress == address(0x0), "colony-already-initialised-network"); require(token == address(0x0), "colony-already-initialised-token"); diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index c260dabd19..144225ed51 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -85,8 +85,10 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayoutModifier(uint256,uint256,uint256,uint256,int256)"); addRoleCapability(ARBITRATION_ROLE, "setExpenditureClaimDelay(uint256,uint256,uint256,uint256,uint256)"); - // Added in colony v5 + // Added in colony v5 (current version) addRoleCapability(ARBITRATION_ROLE, "transferStake(uint256,uint256,address,address,uint256,uint256,address)"); + addRoleCapability(ARBITRATION_ROLE, "emitDomainReputationPenalty(uint256,uint256,uint256,address,int256)"); + addRoleCapability(ARBITRATION_ROLE, "emitSkillReputationPenalty(uint256,uint256,address,int256)"); } function addRoleCapability(uint8 role, bytes memory sig) private { diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index b953ef0c41..897b26a09d 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -127,6 +127,28 @@ contract IColony is ColonyDataTypes, IRecovery { /// @return roles bytes32 representation of the roles function getUserRoles(address who, uint256 where) public view returns (bytes32 roles); + /// @notice Emit a negative domain reputation update. Available only to Arbitration role holders. + /// @param _permissionDomainId The domainId in which I hold the Arbitration role. + /// @param _childSkillIndex The index that the `_domainId` is relative to `_permissionDomainId`, + /// (only used if `_permissionDomainId` is different to `_domainId`) + /// @param _domainId The domain where the user will lose reputation. + /// @param _user The user who will lose reputation. + /// @param _amount The (negative) amount of reputation to lose. + function emitDomainReputationPenalty( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + address _user, + int256 _amount + ) public; + + /// @notice Emit a negative skill reputation update. Available only to Arbitration role holders. + /// @param _permissionDomainId The domainId in which I hold the Arbitration role. + /// @param _skillId The skill where the user will lose reputation. + /// @param _user The user who will lose reputation. + /// @param _amount The (negative) amount of reputation to lose. + function emitSkillReputationPenalty(uint256 _permissionDomainId, uint256 _skillId, address _user, int256 _amount) public; + /// @notice Called once when the colony is created to initialise certain storage slot values. /// @dev Sets the reward inverse to the uint max 2**256 - 1. /// @param _colonyNetworkAddress Address of the colony network diff --git a/docs/_Interface_IColony.md b/docs/_Interface_IColony.md index c3177e3e98..960d86334c 100644 --- a/docs/_Interface_IColony.md +++ b/docs/_Interface_IColony.md @@ -206,6 +206,37 @@ Deobligate the user some amount of tokens, releasing the stake. |_amount|uint256|Amount of internal token we are deobligating. +### `emitDomainReputationPenalty` + +Emit a negative domain reputation update. Available only to Arbitration role holders. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I hold the Arbitration role. +|_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId`, (only used if `_permissionDomainId` is different to `_domainId`) +|_domainId|uint256|The domain where the user will lose reputation. +|_user|address|The user who will lose reputation. +|_amount|int256|The (negative) amount of reputation to lose. + + +### `emitSkillReputationPenalty` + +Emit a negative skill reputation update. Available only to Arbitration role holders. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I hold the Arbitration role. +|_skillId|uint256|The skill where the user will lose reputation. +|_user|address|The user who will lose reputation. +|_amount|int256|The (negative) amount of reputation to lose. + + ### `executeTaskChange` Executes a task update transaction `_data` which is approved and signed by two of its roles (e.g. manager and worker) using the detached signatures for these users. diff --git a/test/contracts-network/colony-permissions.js b/test/contracts-network/colony-permissions.js index 0d689d18ad..c71f88147b 100644 --- a/test/contracts-network/colony-permissions.js +++ b/test/contracts-network/colony-permissions.js @@ -12,6 +12,7 @@ import { ADMINISTRATION_ROLE, INITIAL_FUNDING, SPECIFICATION_HASH, + GLOBAL_SKILL_ID, } from "../../helpers/constants"; import { fundColonyWithTokens, makeTask, setupRandomColony } from "../../helpers/test-data-generator"; @@ -278,6 +279,19 @@ contract("ColonyPermissions", (accounts) => { await colony.setAdministrationRole(1, 1, USER2, 3, true, { from: USER1 }); }); + it("should allow users with arbitration permission to emit negative reputation penalties", async () => { + await colony.setArbitrationRole(1, 0, USER1, 1, true); + + // Domain penalties + await colony.emitDomainReputationPenalty(1, 1, 3, USER2, -100, { from: USER1 }); + await checkErrorRevert(colony.emitDomainReputationPenalty(1, 1, 3, USER2, 100, { from: USER1 }), "colony-penalty-cannot-be-positive"); + + // Skill penalties + await colony.emitSkillReputationPenalty(1, GLOBAL_SKILL_ID, USER2, -100, { from: USER1 }); + await checkErrorRevert(colony.emitSkillReputationPenalty(1, GLOBAL_SKILL_ID, USER2, 100, { from: USER1 }), "colony-penalty-cannot-be-positive"); + await checkErrorRevert(colony.emitSkillReputationPenalty(2, GLOBAL_SKILL_ID, USER2, -100, { from: USER1 }), "ds-auth-unauthorized"); + }); + it("should allow permissions to propagate to subdomains", async () => { // Give User 2 funding permissions in domain 1 await colony.setFundingRole(1, UINT256_MAX, USER2, 1, true);