Skip to content

Commit 43b46f5

Browse files
authored
Merge pull request #553 from JoinColony/feature/domain-permissions
Domain-level permissions
2 parents b99f6f9 + 0fd02d2 commit 43b46f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1360
-544
lines changed

.circleci/config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ jobs:
7373
- run:
7474
name: "Checking contract recovery modifiers"
7575
command: yarn run check:recoverymods
76+
- run:
77+
name: "Checking contract authDomain modifiers"
78+
command: yarn run check:auth
7679
- run:
7780
name: "Running unit tests"
7881
command: yarn run test:contracts

contracts/Colony.sol

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,56 @@ contract Colony is ColonyStorage, PatriciaTreeProofs {
2828
// Version number should be upped with every change in Colony or its dependency contracts or libraries.
2929
function version() public pure returns (uint256 colonyVersion) { return 1; }
3030

31-
function setFounderRole(address _user) public stoppable auth {
32-
// To allow only one address to have founder role at a time, we have to remove current founder from their role
31+
function setRootRole(address _user, bool _setTo) public stoppable auth {
32+
ColonyAuthority(address(authority)).setUserRole(_user, uint8(ColonyRole.Root), _setTo);
33+
34+
emit ColonyRootRoleSet(_user, _setTo);
35+
}
36+
37+
function setArchitectureRole(
38+
uint256 _permissionDomainId,
39+
uint256 _childSkillIndex,
40+
address _user,
41+
uint256 _domainId,
42+
bool _setTo
43+
) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId)
44+
{
45+
// Because this permission has some restrictions on domains of action, we transparently implement it as two roles
3346
ColonyAuthority colonyAuthority = ColonyAuthority(address(authority));
34-
colonyAuthority.setUserRole(msg.sender, uint8(ColonyRole.Founder), false);
35-
colonyAuthority.setUserRole(_user, uint8(ColonyRole.Founder), true);
47+
colonyAuthority.setUserRole(_user, _domainId, uint8(ColonyRole.Architecture), _setTo);
48+
colonyAuthority.setUserRole(_user, _domainId, uint8(ColonyRole.ArchitectureSubdomain), _setTo);
3649

37-
emit ColonyFounderRoleSet(msg.sender, _user);
50+
emit ColonyArchitectureRoleSet(_user, _setTo);
3851
}
3952

40-
function setAdminRole(address _user) public stoppable auth {
41-
ColonyAuthority(address(authority)).setUserRole(_user, uint8(ColonyRole.Admin), true);
53+
function setFundingRole(
54+
uint256 _permissionDomainId,
55+
uint256 _childSkillIndex,
56+
address _user,
57+
uint256 _domainId,
58+
bool _setTo
59+
) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId)
60+
{
61+
ColonyAuthority(address(authority)).setUserRole(_user, _domainId, uint8(ColonyRole.Funding), _setTo);
4262

43-
emit ColonyAdminRoleSet(_user);
63+
emit ColonyFundingRoleSet(_user, _setTo);
4464
}
4565

46-
// Can only be called by the founder role.
47-
function removeAdminRole(address _user) public stoppable auth {
48-
ColonyAuthority(address(authority)).setUserRole(_user, uint8(ColonyRole.Admin), false);
66+
function setAdministrationRole(
67+
uint256 _permissionDomainId,
68+
uint256 _childSkillIndex,
69+
address _user,
70+
uint256 _domainId,
71+
bool _setTo
72+
) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId)
73+
{
74+
ColonyAuthority(address(authority)).setUserRole(_user, _domainId, uint8(ColonyRole.Administration), _setTo);
4975

50-
emit ColonyAdminRoleRemoved(_user);
76+
emit ColonyAdministrationRoleSet(_user, _setTo);
5177
}
5278

53-
function hasUserRole(address _user, ColonyRole _role) public view returns (bool) {
54-
return ColonyAuthority(address(authority)).hasUserRole(_user, uint8(_role));
79+
function hasUserRole(address _user, uint256 _domainId, ColonyRole _role) public view returns (bool) {
80+
return ColonyAuthority(address(authority)).hasUserRole(_user, _domainId, uint8(_role));
5581
}
5682

5783
function getColonyNetwork() public view returns (address) {
@@ -81,7 +107,7 @@ contract Colony is ColonyStorage, PatriciaTreeProofs {
81107
setFunctionReviewers(bytes4(keccak256("removeTaskWorkerRole(uint256)")), TaskRole.Manager, TaskRole.Worker);
82108
setFunctionReviewers(bytes4(keccak256("cancelTask(uint256)")), TaskRole.Manager, TaskRole.Worker);
83109

84-
setRoleAssignmentFunction(bytes4(keccak256("setTaskManagerRole(uint256,address)")));
110+
setRoleAssignmentFunction(bytes4(keccak256("setTaskManagerRole(uint256,address,uint256,uint256)")));
85111
setRoleAssignmentFunction(bytes4(keccak256("setTaskEvaluatorRole(uint256,address)")));
86112
setRoleAssignmentFunction(bytes4(keccak256("setTaskWorkerRole(uint256,address)")));
87113

@@ -165,10 +191,9 @@ contract Colony is ColonyStorage, PatriciaTreeProofs {
165191
return colonyNetwork.addColonyVersion(_version, _resolver);
166192
}
167193

168-
function addDomain(uint256 _parentDomainId) public
194+
function addDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _parentDomainId) public
169195
stoppable
170-
auth
171-
domainExists(_parentDomainId)
196+
authDomain(_permissionDomainId, _childSkillIndex, _parentDomainId)
172197
{
173198
// Note: Remove when we want to allow more domain hierarchy levels
174199
require(_parentDomainId == 1, "colony-parent-domain-not-root");

contracts/ColonyAuthority.sol

Lines changed: 52 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,85 +17,64 @@
1717

1818
pragma solidity >=0.5.3;
1919

20-
import "./CommonAuthority.sol";
2120
import "./ColonyDataTypes.sol";
21+
import "./CommonAuthority.sol";
2222

2323

2424
contract ColonyAuthority is CommonAuthority {
25-
uint8 founderRole = uint8(ColonyDataTypes.ColonyRole.Founder);
26-
uint8 adminRole = uint8(ColonyDataTypes.ColonyRole.Admin);
27-
28-
constructor(address colony) public CommonAuthority(colony) {
29-
// Bootstrap colony
30-
setFounderRoleCapability(colony, "bootstrapColony(address[],int256[])");
31-
// Mint tokens
32-
setFounderRoleCapability(colony, "mintTokens(uint256)");
33-
// Add global skill
34-
setFounderRoleCapability(colony, "addGlobalSkill(uint256)");
35-
// Transfer founder role
36-
setFounderRoleCapability(colony, "setFounderRole(address)");
37-
// Remove admin role
38-
setFounderRoleCapability(colony, "removeAdminRole(address)");
39-
// Set recovery role
40-
setFounderRoleCapability(colony, "setRecoveryRole(address)");
41-
// Remove recovery role
42-
setFounderRoleCapability(colony, "removeRecoveryRole(address)");
43-
// Upgrade colony
44-
setFounderRoleCapability(colony, "upgrade(uint256)");
45-
// Claim colony ENS label
46-
setFounderRoleCapability(colony, "registerColonyLabel(string,string)");
47-
// Set Network fee inverse
48-
setFounderRoleCapability(colony, "setNetworkFeeInverse(uint256)");
49-
// Set Reward fee inverse
50-
setFounderRoleCapability(colony, "setRewardInverse(uint256)");
51-
// Add colony version to the network
52-
setFounderRoleCapability(colony, "addNetworkColonyVersion(uint256,address)");
53-
54-
// Allocate funds
55-
setAdminRoleCapability(colony, "moveFundsBetweenPots(uint256,uint256,uint256,address)");
56-
setFounderRoleCapability(colony, "moveFundsBetweenPots(uint256,uint256,uint256,address)");
57-
// Add domain
58-
setAdminRoleCapability(colony, "addDomain(uint256)");
59-
setFounderRoleCapability(colony, "addDomain(uint256)");
60-
61-
// Add task
62-
setAdminRoleCapability(colony, "makeTask(bytes32,uint256,uint256,uint256)");
63-
setFounderRoleCapability(colony, "makeTask(bytes32,uint256,uint256,uint256)");
64-
65-
// Add payment
66-
setAdminRoleCapability(colony, "addPayment(address,address,uint256,uint256,uint256)");
67-
setFounderRoleCapability(colony, "addPayment(address,address,uint256,uint256,uint256)");
68-
// Update payments
69-
setAdminRoleCapability(colony, "finalizePayment(uint256)");
70-
setFounderRoleCapability(colony, "finalizePayment(uint256)");
71-
72-
setAdminRoleCapability(colony, "setPaymentRecipient(uint256,address)");
73-
setFounderRoleCapability(colony, "setPaymentRecipient(uint256,address)");
74-
75-
setAdminRoleCapability(colony, "setPaymentDomain(uint256,uint256)");
76-
setFounderRoleCapability(colony, "setPaymentDomain(uint256,uint256)");
77-
78-
setAdminRoleCapability(colony, "setPaymentSkill(uint256,uint256)");
79-
setFounderRoleCapability(colony, "setPaymentSkill(uint256,uint256)");
80-
81-
setAdminRoleCapability(colony, "setPaymentPayout(uint256,address,uint256)");
82-
setFounderRoleCapability(colony, "setPaymentPayout(uint256,address,uint256)");
83-
84-
// Start next reward payout
85-
setAdminRoleCapability(colony, "startNextRewardPayout(address,bytes,bytes,uint256,bytes32[])");
86-
setFounderRoleCapability(colony, "startNextRewardPayout(address,bytes,bytes,uint256,bytes32[])");
87-
// Set admin
88-
setAdminRoleCapability(colony, "setAdminRole(address)");
89-
setFounderRoleCapability(colony, "setAdminRole(address)");
90-
}
91-
92-
function setFounderRoleCapability(address colony, bytes memory sig) private {
93-
bytes4 functionSig = bytes4(keccak256(sig));
94-
setRoleCapability(founderRole, colony, functionSig, true);
25+
uint8 constant FUNDING_ROLE = uint8(ColonyDataTypes.ColonyRole.Funding);
26+
uint8 constant ADMINISTRATION_ROLE = uint8(ColonyDataTypes.ColonyRole.Administration);
27+
uint8 constant ARBITRATION_ROLE = uint8(ColonyDataTypes.ColonyRole.Arbitration);
28+
uint8 constant ARCHITECTURE_ROLE = uint8(ColonyDataTypes.ColonyRole.Architecture);
29+
uint8 constant ARCHITECTURE_SUBDOMAIN_ROLE = uint8(ColonyDataTypes.ColonyRole.ArchitectureSubdomain);
30+
uint8 constant ROOT_ROLE = uint8(ColonyDataTypes.ColonyRole.Root);
31+
32+
address colony;
33+
34+
constructor(address _colony) public CommonAuthority(_colony) {
35+
colony = _colony;
36+
37+
// Add permissions for the Administration role
38+
addRoleCapability(ADMINISTRATION_ROLE, "makeTask(uint256,uint256,bytes32,uint256,uint256,uint256)");
39+
addRoleCapability(ADMINISTRATION_ROLE, "addPayment(uint256,uint256,address,address,uint256,uint256,uint256)");
40+
addRoleCapability(ADMINISTRATION_ROLE, "setPaymentRecipient(uint256,uint256,uint256,address)");
41+
addRoleCapability(ADMINISTRATION_ROLE, "setPaymentDomain(uint256,uint256,uint256,uint256)");
42+
addRoleCapability(ADMINISTRATION_ROLE, "setPaymentSkill(uint256,uint256,uint256,uint256)");
43+
addRoleCapability(ADMINISTRATION_ROLE, "setPaymentPayout(uint256,uint256,uint256,address,uint256)");
44+
addRoleCapability(ADMINISTRATION_ROLE, "finalizePayment(uint256,uint256,uint256)");
45+
46+
// Add permissions for the Funding role
47+
addRoleCapability(FUNDING_ROLE, "moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,address)");
48+
49+
// Add permissions for the Architecture role
50+
addRoleCapability(ARCHITECTURE_ROLE, "addDomain(uint256,uint256,uint256)");
51+
addRoleCapability(ARCHITECTURE_SUBDOMAIN_ROLE, "setArchitectureRole(uint256,uint256,address,uint256,bool)");
52+
addRoleCapability(ARCHITECTURE_SUBDOMAIN_ROLE, "setFundingRole(uint256,uint256,address,uint256,bool)");
53+
addRoleCapability(ARCHITECTURE_SUBDOMAIN_ROLE, "setAdministrationRole(uint256,uint256,address,uint256,bool)");
54+
55+
// Add permissions for the Root role
56+
addRoleCapability(ROOT_ROLE, "setRootRole(address,bool)");
57+
addRoleCapability(ROOT_ROLE, "setArchitectureRole(uint256,uint256,address,uint256,bool)");
58+
addRoleCapability(ROOT_ROLE, "setFundingRole(uint256,uint256,address,uint256,bool)");
59+
addRoleCapability(ROOT_ROLE, "setAdministrationRole(uint256,uint256,address,uint256,bool)");
60+
// Managing recovery roles
61+
addRoleCapability(ROOT_ROLE, "setRecoveryRole(address)");
62+
addRoleCapability(ROOT_ROLE, "removeRecoveryRole(address)");
63+
// Colony functions
64+
addRoleCapability(ROOT_ROLE, "startNextRewardPayout(address,bytes,bytes,uint256,bytes32[])");
65+
addRoleCapability(ROOT_ROLE, "bootstrapColony(address[],int256[])");
66+
addRoleCapability(ROOT_ROLE, "registerColonyLabel(string,string)");
67+
addRoleCapability(ROOT_ROLE, "setRewardInverse(uint256)");
68+
addRoleCapability(ROOT_ROLE, "mintTokens(uint256)");
69+
addRoleCapability(ROOT_ROLE, "upgrade(uint256)");
70+
// Meta Colony functions
71+
addRoleCapability(ROOT_ROLE, "addNetworkColonyVersion(uint256,address)");
72+
addRoleCapability(ROOT_ROLE, "setNetworkFeeInverse(uint256)");
73+
addRoleCapability(ROOT_ROLE, "addGlobalSkill(uint256)");
9574
}
9675

97-
function setAdminRoleCapability(address colony, bytes memory sig) private {
76+
function addRoleCapability(uint8 role, bytes memory sig) private {
9877
bytes4 functionSig = bytes4(keccak256(sig));
99-
setRoleCapability(adminRole, colony, functionSig, true);
78+
setRoleCapability(role, colony, functionSig, true);
10079
}
10180
}

contracts/ColonyDataTypes.sol

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,21 @@ contract ColonyDataTypes {
3636
/// @param newVersion The new colony version upgraded to
3737
event ColonyUpgraded(uint256 oldVersion, uint256 newVersion);
3838

39-
/// @notice Event logged when the colony founder role is changed
40-
/// @param oldFounder The current founder delegating the role away
41-
/// @param newFounder User who is new new colony founder
42-
event ColonyFounderRoleSet(address oldFounder, address newFounder);
39+
/// @notice Event logged when a new user is assigned the colony funding role
40+
/// @param user The newly added colony funding user address
41+
event ColonyFundingRoleSet(address user, bool setTo);
4342

44-
/// @notice Event logged when a new user is assigned the colony admin role
45-
/// @param user The newly added colony admin user address
46-
event ColonyAdminRoleSet(address user);
43+
/// @notice Event logged when a new user is assigned the colony administration role
44+
/// @param user The newly added colony administration user address
45+
event ColonyAdministrationRoleSet(address user, bool setTo);
4746

48-
/// @notice Event logged when an existing colony admin is removed the colony admin role
49-
/// @param user The removed colony admin user address
50-
event ColonyAdminRoleRemoved(address user);
47+
/// @notice Event logged when a new user is assigned the colony architecture role
48+
/// @param user The newly added colony architecture user address
49+
event ColonyArchitectureRoleSet(address user, bool setTo);
50+
51+
/// @notice Event logged when a new user is assigned the colony root role
52+
/// @param user The newly added colony root user address
53+
event ColonyRootRoleSet(address user, bool setTo);
5154

5255
/// @notice Event logged when colony funds, either tokens or ether, has been moved between funding pots
5356
/// @param fromPot The source funding pot
@@ -204,7 +207,7 @@ contract ColonyDataTypes {
204207

205208
enum TaskStatus { Active, Cancelled, Finalized }
206209

207-
enum ColonyRole { Founder, Admin }
210+
enum ColonyRole { Recovery, Root, Arbitration, Architecture, ArchitectureSubdomain, Funding, Administration }
208211

209212
struct Role {
210213
// Address of the user for the given role

contracts/ColonyFunding.sol

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs {
106106
processPayout(payment.fundingPotId, _token, fundingPot.payouts[_token], payment.recipient);
107107
}
108108

109-
function setPaymentPayout(uint256 _id, address _token, uint256 _amount) public
110-
auth
109+
function setPaymentPayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, address _token, uint256 _amount) public
111110
stoppable
111+
authDomain(_permissionDomainId, _childSkillIndex, payments[_id].domainId)
112112
validPayoutAmount(_amount)
113113
paymentNotFinalized(_id)
114114
{
@@ -141,35 +141,43 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs {
141141
return (fundingPot.associatedType, fundingPot.associatedTypeId, fundingPot.payoutsWeCannotMake);
142142
}
143143

144-
function moveFundsBetweenPots(uint256 _fromPot, uint256 _toPot, uint256 _amount, address _token) public
144+
function moveFundsBetweenPots(
145+
uint256 _permissionDomainId,
146+
uint256 _fromChildSkillIndex,
147+
uint256 _toChildSkillIndex,
148+
uint256 _fromPot,
149+
uint256 _toPot,
150+
uint256 _amount,
151+
address _token
152+
)
153+
public
145154
stoppable
146-
auth
155+
authDomain(_permissionDomainId, _fromChildSkillIndex, getDomainFromFundingPot(_fromPot))
156+
authDomain(_permissionDomainId, _toChildSkillIndex, getDomainFromFundingPot(_toPot))
147157
validFundingTransfer(_fromPot, _toPot)
148158
{
149-
uint fromPotPreviousAmount = fundingPots[_fromPot].balance[_token];
150-
uint toPotPreviousAmount = fundingPots[_toPot].balance[_token];
151-
152-
fundingPots[_fromPot].balance[_token] = sub(fromPotPreviousAmount, _amount);
153-
fundingPots[_toPot].balance[_token] = add(toPotPreviousAmount, _amount);
154-
155-
// If this pot is associated with a Task, prevent money being taken from the pot
156-
// if the remaining balance is less than the amount needed for payouts,
157-
// unless the task was cancelled.
158-
if (fundingPots[_fromPot].associatedType == FundingPotAssociatedType.Task) {
159-
uint fromPotPreviousPayoutAmount = fundingPots[_fromPot].payouts[_token];
160-
uint surplus = (fromPotPreviousAmount > fromPotPreviousPayoutAmount) ? sub(fromPotPreviousAmount, fromPotPreviousPayoutAmount) : 0;
161-
162-
Task storage task = tasks[fundingPots[_fromPot].associatedTypeId];
163-
require(task.status == TaskStatus.Cancelled || surplus >= _amount, "colony-funding-task-bad-state");
159+
FundingPot storage fromPot = fundingPots[_fromPot];
160+
FundingPot storage toPot = fundingPots[_toPot];
161+
162+
fromPot.balance[_token] = sub(fromPot.balance[_token], _amount);
163+
toPot.balance[_token] = add(toPot.balance[_token], _amount);
164+
165+
// If this pot is associated with a Task, prevent money being taken from the pot if the
166+
// remaining balance is less than the amount needed for payouts, unless the task was cancelled.
167+
if (fromPot.associatedType == FundingPotAssociatedType.Task) {
168+
require(
169+
tasks[fromPot.associatedTypeId].status == TaskStatus.Cancelled || fromPot.balance[_token] >= fromPot.payouts[_token],
170+
"colony-funding-task-bad-state"
171+
);
164172
}
165173

166-
if (fundingPots[_fromPot].associatedType == FundingPotAssociatedType.Task ||
167-
fundingPots[_fromPot].associatedType == FundingPotAssociatedType.Payment) {
174+
if (fromPot.associatedType == FundingPotAssociatedType.Task || fromPot.associatedType == FundingPotAssociatedType.Payment) {
175+
uint256 fromPotPreviousAmount = add(fromPot.balance[_token], _amount);
168176
updatePayoutsWeCannotMakeAfterPotChange(_fromPot, _token, fromPotPreviousAmount);
169177
}
170178

171-
if (fundingPots[_toPot].associatedType == FundingPotAssociatedType.Task ||
172-
fundingPots[_toPot].associatedType == FundingPotAssociatedType.Payment) {
179+
if (toPot.associatedType == FundingPotAssociatedType.Task || toPot.associatedType == FundingPotAssociatedType.Payment) {
180+
uint256 toPotPreviousAmount = sub(toPot.balance[_token], _amount);
173181
updatePayoutsWeCannotMakeAfterPotChange(_toPot, _token, toPotPreviousAmount);
174182
}
175183

@@ -203,7 +211,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs {
203211
}
204212

205213
function startNextRewardPayout(address _token, bytes memory key, bytes memory value, uint256 branchMask, bytes32[] memory siblings)
206-
public auth stoppable
214+
public stoppable auth
207215
{
208216
ITokenLocking tokenLocking = ITokenLocking(IColonyNetwork(colonyNetworkAddress).getTokenLocking());
209217
uint256 totalLockCount = tokenLocking.lockToken(token);
@@ -414,7 +422,7 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs {
414422
uint currentTotalAmount = fundingPot.payouts[_token];
415423
uint currentTaskRolePayout = task.payouts[uint8(_role)][_token];
416424
task.payouts[uint8(_role)][_token] = _amount;
417-
425+
418426
fundingPot.payouts[_token] = add(sub(currentTotalAmount, currentTaskRolePayout), _amount);
419427

420428
updatePayoutsWeCannotMakeAfterBudgetChange(task.fundingPotId, _token, currentTotalAmount);
@@ -458,4 +466,21 @@ contract ColonyFunding is ColonyStorage, PatriciaTreeProofs {
458466
fee = _payout/feeInverse + 1;
459467
}
460468
}
469+
470+
function getDomainFromFundingPot(uint256 _fundingPotId) private view returns (uint256 domainId) {
471+
require(_fundingPotId <= fundingPotCount, "colony-funding-nonexistent-pot");
472+
FundingPot storage fundingPot = fundingPots[_fundingPotId];
473+
474+
if (fundingPot.associatedType == FundingPotAssociatedType.Domain) {
475+
domainId = fundingPot.associatedTypeId;
476+
} else if (fundingPot.associatedType == FundingPotAssociatedType.Task) {
477+
domainId = tasks[fundingPot.associatedTypeId].domainId;
478+
} else if (fundingPot.associatedType == FundingPotAssociatedType.Payment) {
479+
domainId = payments[fundingPot.associatedTypeId].domainId;
480+
} else {
481+
// If rewards pot, return root domain.
482+
assert(_fundingPotId == 0);
483+
domainId = 1;
484+
}
485+
}
461486
}

0 commit comments

Comments
 (0)