Skip to content

Commit

Permalink
Introduce a multiExtension approach
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Jun 24, 2021
1 parent fffe3ab commit b709c03
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 99 deletions.
17 changes: 9 additions & 8 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -343,27 +343,28 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain {
}

function installExtension(bytes32 _extensionId, uint256 _version)
public stoppable auth
public stoppable auth returns (address)
{
IColonyNetwork(colonyNetworkAddress).installExtension(_extensionId, _version);
address extension = IColonyNetwork(colonyNetworkAddress).installExtension(_extensionId, _version);
return extension;
}

function upgradeExtension(bytes32 _extensionId, uint256 _newVersion)
function upgradeExtension(address payable _extension, uint256 _newVersion)
public stoppable auth
{
IColonyNetwork(colonyNetworkAddress).upgradeExtension(_extensionId, _newVersion);
IColonyNetwork(colonyNetworkAddress).upgradeExtension(_extension, _newVersion);
}

function deprecateExtension(bytes32 _extensionId, bool _deprecated)
function deprecateExtension(address payable _extension, bool _deprecated)
public stoppable auth
{
IColonyNetwork(colonyNetworkAddress).deprecateExtension(_extensionId, _deprecated);
IColonyNetwork(colonyNetworkAddress).deprecateExtension(_extension, _deprecated);
}

function uninstallExtension(bytes32 _extensionId)
function uninstallExtension(address payable _extension)
public stoppable auth
{
IColonyNetwork(colonyNetworkAddress).uninstallExtension(_extensionId);
IColonyNetwork(colonyNetworkAddress).uninstallExtension(_extension);
}

function addDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _parentDomainId) public
Expand Down
3 changes: 3 additions & 0 deletions contracts/colony/ColonyAuthority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ contract ColonyAuthority is CommonAuthority {
addRoleCapability(ROOT_ROLE, "unlockToken()");

// Added in colony v6 (d-lwss)
addRoleCapability(ROOT_ROLE, "upgradeExtension(address,uint256)");
addRoleCapability(ROOT_ROLE, "deprecateExtension(address,bool)");
addRoleCapability(ROOT_ROLE, "uninstallExtension(address)");
addRoleCapability(FUNDING_ROLE, "moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)");
}

Expand Down
5 changes: 3 additions & 2 deletions contracts/colony/ColonyStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,12 @@ contract ColonyStorage is CommonStorage, ColonyDataTypes, ColonyNetworkDataTypes
// Ensure msg.sender is a contract
require(isContract(msg.sender), "colony-sender-must-be-contract");

// Ensure msg.sender is an extension
// Ensure msg.sender is an extension, must check old & new formats
// slither-disable-next-line unused-return
try ColonyExtension(msg.sender).identifier() returns (bytes32 extensionId) {
require(
IColonyNetwork(colonyNetworkAddress).getExtensionInstallation(extensionId, address(this)) == msg.sender,
IColonyNetwork(colonyNetworkAddress).getExtensionInstallation(extensionId, address(this)) == msg.sender ||
IColonyNetwork(colonyNetworkAddress).getExtensionMultiInstallation(msg.sender) == address(this),
"colony-must-be-extension"
);
} catch {
Expand Down
15 changes: 8 additions & 7 deletions contracts/colony/IColony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -247,23 +247,24 @@ interface IColony is ColonyDataTypes, IRecovery {
/// @notice Install an extension to the colony. Secured function to authorised members.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
/// @param version The new extension version to install
function installExtension(bytes32 extensionId, uint256 version) external;
/// @return extension The address of the extension installation
function installExtension(bytes32 extensionId, uint256 version) external returns (address extension);

/// @notice Upgrade an extension in a colony. Secured function to authorised members.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
/// @param extension The address of the extension installation
/// @param newVersion The version to upgrade to (must be one larger than the current version)
function upgradeExtension(bytes32 extensionId, uint256 newVersion) external;
function upgradeExtension(address payable extension, uint256 newVersion) external;

/// @notice Set the deprecation of an extension in a colony. Secured function to authorised members.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
/// @param extension The address of the extension installation
/// @param deprecated Whether to deprecate the extension or not
function deprecateExtension(bytes32 extensionId, bool deprecated) external;
function deprecateExtension(address payable extension, bool deprecated) external;

/// @notice Uninstall an extension from a colony. Secured function to authorised members.
/// @dev This is a permanent action -- re-installing the extension will deploy a new contract
/// @dev It is recommended to deprecate an extension before uninstalling to allow active objects to be resolved
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
function uninstallExtension(bytes32 extensionId) external;
/// @param extension The address of the extension installation
function uninstallExtension(address payable extension) external;

/// @notice Add a colony domain, and its respective local skill under skill with id `_parentSkillId`.
/// New funding pot is created and associated with the domain here.
Expand Down
15 changes: 8 additions & 7 deletions contracts/colonyNetwork/ColonyNetworkDataTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,26 +116,27 @@ interface ColonyNetworkDataTypes {

/// @notice Event logged when an extension is installed in a colony
/// @param extensionId The identifier for the extension
/// @param extension Address of the extension installation
/// @param colony The address of the colony
/// @param version The version of the extension
event ExtensionInstalled(bytes32 indexed extensionId, address indexed colony, uint256 version);
event ExtensionInstalled(bytes32 indexed extensionId, address indexed extension, address indexed colony, uint256 version);

/// @notice Event logged when an extension is upgraded in a colony
/// @param extensionId The identifier for the extension
/// @param extension Address of the extension installation
/// @param colony The address of the colony
/// @param version The new version of the extension
event ExtensionUpgraded(bytes32 indexed extensionId, address indexed colony, uint256 version);
event ExtensionUpgraded(address indexed extension, address indexed colony, uint256 version);

/// @notice Event logged when an extension is (un)deprecated in a colony
/// @param extensionId The identifier for the extension
/// @param extension Address of the extension installation
/// @param colony The address of the colony
/// @param deprecated Whether the extension is deprecated or not
event ExtensionDeprecated(bytes32 indexed extensionId, address indexed colony, bool deprecated);
event ExtensionDeprecated(address indexed extension, address indexed colony, bool deprecated);

/// @notice Event logged when an extension is uninstalled from a colony
/// @param extensionId The identifier for the extension
/// @param extension Address of the extension installation
/// @param colony The address of the colony
event ExtensionUninstalled(bytes32 indexed extensionId, address indexed colony);
event ExtensionUninstalled(address indexed extension, address indexed colony);

struct Skill {
// total number of parent skills
Expand Down
54 changes: 33 additions & 21 deletions contracts/colonyNetwork/ColonyNetworkExtensions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,59 +50,63 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage {
public
stoppable
calledByColony
returns (address)
{
require(resolvers[_extensionId][_version] != address(0x0), "colony-network-extension-bad-version");
require(installations[_extensionId][msg.sender] == address(0x0), "colony-network-extension-already-installed");

EtherRouter extension = new EtherRouter();
installations[_extensionId][msg.sender] = address(extension);
multiInstallations[address(extension)] = msg.sender;

extension.setResolver(resolvers[_extensionId][_version]);
ColonyExtension(address(extension)).install(msg.sender);

emit ExtensionInstalled(_extensionId, msg.sender, _version);
emit ExtensionInstalled(_extensionId, address(extension), msg.sender, _version);

return address(extension);
}

function upgradeExtension(bytes32 _extensionId, uint256 _newVersion)
function upgradeExtension(address payable _extension, uint256 _newVersion)
public
stoppable
calledByColony
{
require(installations[_extensionId][msg.sender] != address(0x0), "colony-network-extension-not-installed");
require(multiInstallations[_extension] == msg.sender, "colony-network-extension-not-installed");

bytes32 extensionId = ColonyExtension(_extension).identifier();

address payable extension = installations[_extensionId][msg.sender];
require(_newVersion == ColonyExtension(extension).version() + 1, "colony-network-extension-bad-increment");
require(resolvers[_extensionId][_newVersion] != address(0x0), "colony-network-extension-bad-version");
require(_newVersion == ColonyExtension(_extension).version() + 1, "colony-network-extension-bad-increment");
require(resolvers[extensionId][_newVersion] != address(0x0), "colony-network-extension-bad-version");

EtherRouter(extension).setResolver(resolvers[_extensionId][_newVersion]);
ColonyExtension(extension).finishUpgrade();
assert(ColonyExtension(extension).version() == _newVersion);
EtherRouter(_extension).setResolver(resolvers[extensionId][_newVersion]);
ColonyExtension(_extension).finishUpgrade();

emit ExtensionUpgraded(_extensionId, msg.sender, _newVersion);
assert(ColonyExtension(_extension).version() == _newVersion);

emit ExtensionUpgraded(_extension, msg.sender, _newVersion);
}

function deprecateExtension(bytes32 _extensionId, bool _deprecated)
function deprecateExtension(address payable _extension, bool _deprecated)
public
stoppable
calledByColony
{
ColonyExtension(installations[_extensionId][msg.sender]).deprecate(_deprecated);
ColonyExtension(_extension).deprecate(_deprecated);

emit ExtensionDeprecated(_extensionId, msg.sender, _deprecated);
emit ExtensionDeprecated(_extension, msg.sender, _deprecated);
}

function uninstallExtension(bytes32 _extensionId)
function uninstallExtension(address payable _extension)
public
stoppable
calledByColony
{
require(installations[_extensionId][msg.sender] != address(0x0), "colony-network-extension-not-installed");
require(multiInstallations[_extension] == msg.sender, "colony-network-extension-not-installed");

delete multiInstallations[_extension];

ColonyExtension extension = ColonyExtension(installations[_extensionId][msg.sender]);
installations[_extensionId][msg.sender] = address(0x0);
extension.uninstall();
ColonyExtension(_extension).uninstall();

emit ExtensionUninstalled(_extensionId, msg.sender);
emit ExtensionUninstalled(_extension, msg.sender);
}

// Public view functions
Expand All @@ -115,6 +119,14 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage {
return resolvers[_extensionId][_version];
}

function getExtensionMultiInstallation(address _extension)
public
view
returns (address)
{
return multiInstallations[_extension];
}

function getExtensionInstallation(bytes32 _extensionId, address _colony)
public
view
Expand Down
7 changes: 5 additions & 2 deletions contracts/colonyNetwork/ColonyNetworkStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,16 @@ contract ColonyNetworkStorage is CommonStorage, ColonyNetworkDataTypes, DSMath {
uint256 DEPRECATED_lastMetaColonyStipendIssued; // Storage slot 37

// [_extensionId][version] => resolver
mapping(bytes32 => mapping(uint256 => address)) resolvers; // Storage slot 38
mapping (bytes32 => mapping(uint256 => address)) resolvers; // Storage slot 38
// [_extensionId][colony] => address
mapping(bytes32 => mapping(address => address payable)) installations; // Storage slot 39
mapping (bytes32 => mapping(address => address payable)) installations; // Storage slot 39

// Used for whitelisting payout tokens
mapping (address => bool) payoutWhitelist; // Storage slot 40

// [_extension] => colony
mapping (address => address payable) multiInstallations; // Storage slot 41

modifier calledByColony() {
require(_isColony[msg.sender], "colony-caller-must-be-colony");
_;
Expand Down
20 changes: 13 additions & 7 deletions contracts/colonyNetwork/IColonyNetwork.sol
Original file line number Diff line number Diff line change
Expand Up @@ -316,21 +316,22 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery {
/// @notice Install an extension in a colony. Can only be called by a Colony.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
/// @param version Version of the extension to install
function installExtension(bytes32 extensionId, uint256 version) external;
/// @return extension The address of the extension installation
function installExtension(bytes32 extensionId, uint256 version) external returns (address extension);

/// @notice Upgrade an extension in a colony. Can only be called by a Colony.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
/// @param extension Address of the extension installation
/// @param newVersion Version of the extension to upgrade to (must be one greater than current)
function upgradeExtension(bytes32 extensionId, uint256 newVersion) external;
function upgradeExtension(address payable extension, uint256 newVersion) external;

/// @notice Set the deprecation of an extension in a colony. Can only be called by a Colony.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
/// @param extension Address of the extension installation
/// @param deprecated Whether to deprecate the extension or not
function deprecateExtension(bytes32 extensionId, bool deprecated) external;
function deprecateExtension(address payable extension, bool deprecated) external;

/// @notice Uninstall an extension in a colony. Can only be called by a Colony.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
function uninstallExtension(bytes32 extensionId) external;
/// @param extension Address of the extension installation
function uninstallExtension(address payable extension) external;

/// @notice Get an extension's resolver.
/// @param extensionId keccak256 hash of the extension name, used as an indentifier
Expand All @@ -344,6 +345,11 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery {
/// @return installation The address of the installed extension
function getExtensionInstallation(bytes32 extensionId, address colony) external view returns (address installation);

/// @notice Get an extension's installed colony.
/// @param extension Address of the extension installation
/// @return colony Address of the colony the extension is installed in
function getExtensionMultiInstallation(address extension) external view returns (address colony);

/// @notice Return 1 / the fee to pay to the network. e.g. if the fee is 1% (or 0.01), return 100.
/// @return _feeInverse The inverse of the network fee
function getFeeInverse() external view returns (uint256 _feeInverse);
Expand Down
11 changes: 8 additions & 3 deletions docs/_Interface_IColony.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Set the deprecation of an extension in a colony. Secured function to authorised

|Name|Type|Description|
|---|---|---|
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|extension|address|The address of the extension installation
|deprecated|bool|Whether to deprecate the extension or not


Expand Down Expand Up @@ -1038,6 +1038,11 @@ Install an extension to the colony. Secured function to authorised members.
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|version|uint256|The new extension version to install

**Return Parameters**

|Name|Type|Description|
|---|---|---|
|extension|address|The address of the extension installation

### `lockToken`

Expand Down Expand Up @@ -1753,7 +1758,7 @@ Uninstall an extension from a colony. Secured function to authorised members.

|Name|Type|Description|
|---|---|---|
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|extension|address|The address of the extension installation


### `unlockToken`
Expand Down Expand Up @@ -1823,7 +1828,7 @@ Upgrade an extension in a colony. Secured function to authorised members.

|Name|Type|Description|
|---|---|---|
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|extension|address|The address of the extension installation
|newVersion|uint256|The version to upgrade to (must be one larger than the current version)


Expand Down
28 changes: 25 additions & 3 deletions docs/_Interface_IColonyNetwork.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Set the deprecation of an extension in a colony. Can only be called by a Colony.

|Name|Type|Description|
|---|---|---|
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|extension|address|Address of the extension installation
|deprecated|bool|Whether to deprecate the extension or not


Expand Down Expand Up @@ -352,6 +352,23 @@ Get an extension's installation.
|---|---|---|
|installation|address|The address of the installed extension

### `getExtensionMultiInstallation`

Get an extension's installed colony.


**Parameters**

|Name|Type|Description|
|---|---|---|
|extension|address|Address of the extension installation

**Return Parameters**

|Name|Type|Description|
|---|---|---|
|colony|address|Address of the colony the extension is installed in

### `getExtensionResolver`

Get an extension's resolver.
Expand Down Expand Up @@ -663,6 +680,11 @@ Install an extension in a colony. Can only be called by a Colony.
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|version|uint256|Version of the extension to install

**Return Parameters**

|Name|Type|Description|
|---|---|---|
|extension|address|The address of the extension installation

### `isColony`

Expand Down Expand Up @@ -934,7 +956,7 @@ Uninstall an extension in a colony. Can only be called by a Colony.

|Name|Type|Description|
|---|---|---|
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|extension|address|Address of the extension installation


### `unstakeForMining`
Expand Down Expand Up @@ -982,5 +1004,5 @@ Upgrade an extension in a colony. Can only be called by a Colony.

|Name|Type|Description|
|---|---|---|
|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier
|extension|address|Address of the extension installation
|newVersion|uint256|Version of the extension to upgrade to (must be one greater than current)
4 changes: 4 additions & 0 deletions helpers/test-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -1004,3 +1004,7 @@ export function bn2bytes32(x, size = 64) {
export function rolesToBytes32(roles) {
return `0x${new BN(roles.map((role) => new BN(1).shln(role)).reduce((a, b) => a.or(b), new BN(0))).toString(16, 64)}`;
}

export function getExtensionAddressFromTx(installExtensionTx) {
return `0x${installExtensionTx.receipt.rawLogs[1].topics[2].slice(26)}`;
}
Loading

0 comments on commit b709c03

Please sign in to comment.