Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 0 additions & 130 deletions contracts/colonyNetwork/ColonyNetwork.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,121 +108,6 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall
return colonies[_id];
}

function addSkill(
uint _parentSkillId
) public stoppable skillExists(_parentSkillId) allowedToAddSkill returns (uint256) {
require(_parentSkillId > 0, "colony-network-invalid-parent-skill");

Skill storage parentSkill = skills[_parentSkillId];
require(!parentSkill.DEPRECATED_globalSkill, "colony-network-no-global-skills");

skillCount += 1;
Skill memory s;

s.nParents = parentSkill.nParents + 1;
skills[skillCount] = s;

uint parentSkillId = _parentSkillId;
bool notAtRoot = true;
uint powerOfTwo = 1;
uint treeWalkingCounter = 1;

// Walk through the tree parent skills up to the root
while (notAtRoot) {
// Add the new skill to each parent children
parentSkill.children.push(skillCount);
parentSkill.nChildren += 1;

// When we are at an integer power of two steps away from the newly added skill (leaf) node,
// add the current parent skill to the new skill's parents array
if (treeWalkingCounter == powerOfTwo) {
// slither-disable-next-line controlled-array-length
skills[skillCount].parents.push(parentSkillId);
powerOfTwo = powerOfTwo * 2;
}

// Check if we've reached the root of the tree yet (it has no parents)
// Otherwise get the next parent
if (parentSkill.nParents == 0) {
notAtRoot = false;
} else {
parentSkillId = parentSkill.parents[0];
parentSkill = skills[parentSkill.parents[0]];
}

treeWalkingCounter += 1;
}

emit SkillAdded(skillCount, _parentSkillId);
return skillCount;
}

function getParentSkillId(uint _skillId, uint _parentSkillIndex) public view returns (uint256) {
return ascendSkillTree(_skillId, _parentSkillIndex + 1);
}

function getChildSkillId(uint _skillId, uint _childSkillIndex) public view returns (uint256) {
if (_childSkillIndex == UINT256_MAX) {
return _skillId;
} else {
Skill storage skill = skills[_skillId];
require(
_childSkillIndex < skill.children.length,
"colony-network-out-of-range-child-skill-index"
);
return skill.children[_childSkillIndex];
}
}

function deprecateSkill(
uint256 _skillId,
bool _deprecated
) public stoppable allowedToAddSkill returns (bool) {
require(
skills[_skillId].nParents == 0,
"colony-network-deprecate-local-skills-temporarily-disabled"
);
bool changed = skills[_skillId].deprecated != _deprecated;
skills[_skillId].deprecated = _deprecated;
return changed;
}

/// @notice @deprecated
function deprecateSkill(uint256 _skillId) public stoppable {
deprecateSkill(_skillId, true);
}

function initialiseRootLocalSkill() public stoppable calledByColony returns (uint256) {
skillCount++;
return skillCount;
}

function appendReputationUpdateLog(
address _user,
int _amount,
uint _skillId
) public stoppable calledByColony skillExists(_skillId) {
if (_amount == 0 || _user == address(0x0)) {
// We short-circut amount=0 as it has no effect to save gas, and we ignore Address Zero because it will
// mess up the tracking of the total amount of reputation in a colony, as that's the key that it's
// stored under in the patricia/merkle tree. Colonies can still pay tokens out to it if they want,
// it just won't earn reputation.
return;
}

uint128 nParents = skills[_skillId].nParents;
// We only update child skill reputation if the update is negative, otherwise just set nChildren to 0 to save gas
uint128 nChildren = _amount < 0 ? skills[_skillId].nChildren : 0;
IReputationMiningCycle(inactiveReputationMiningCycle).appendReputationUpdateLog(
_user,
_amount,
_skillId,
msgSender(),
nParents,
nChildren
);
}

function checkNotAdditionalProtectedVariable(uint256 _slot) public view {
// solhint-disable-line no-empty-blocks
}
Expand Down Expand Up @@ -263,19 +148,4 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall
protectSlot(slot);
metatransactionNonces[_user] += 1;
}

function ascendSkillTree(uint _skillId, uint _parentSkillNumber) internal view returns (uint256) {
if (_parentSkillNumber == 0) {
return _skillId;
}

Skill storage skill = skills[_skillId];
for (uint256 i; i < skill.parents.length; i++) {
if (2 ** (i + 1) > _parentSkillNumber) {
uint _newSkillId = skill.parents[i];
uint _newParentSkillNumber = _parentSkillNumber - 2 ** i;
return ascendSkillTree(_newSkillId, _newParentSkillNumber);
}
}
}
}
161 changes: 161 additions & 0 deletions contracts/colonyNetwork/ColonyNetworkSkills.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
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 <http://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.23;
pragma experimental "ABIEncoderV2";

import "./../reputationMiningCycle/IReputationMiningCycle.sol";
import "./../common/Multicall.sol";
import "./ColonyNetworkStorage.sol";

contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall {
// Skills

function addSkill(
uint _parentSkillId
) public stoppable skillExists(_parentSkillId) allowedToAddSkill returns (uint256) {
require(_parentSkillId > 0, "colony-network-invalid-parent-skill");

Skill storage parentSkill = skills[_parentSkillId];
require(!parentSkill.DEPRECATED_globalSkill, "colony-network-no-global-skills");

skillCount += 1;
Skill memory s;

s.nParents = parentSkill.nParents + 1;
skills[skillCount] = s;

uint parentSkillId = _parentSkillId;
bool notAtRoot = true;
uint powerOfTwo = 1;
uint treeWalkingCounter = 1;

// Walk through the tree parent skills up to the root
while (notAtRoot) {
// Add the new skill to each parent children
parentSkill.children.push(skillCount);
parentSkill.nChildren += 1;

// When we are at an integer power of two steps away from the newly added skill (leaf) node,
// add the current parent skill to the new skill's parents array
if (treeWalkingCounter == powerOfTwo) {
// slither-disable-next-line controlled-array-length
skills[skillCount].parents.push(parentSkillId);
powerOfTwo = powerOfTwo * 2;
}

// Check if we've reached the root of the tree yet (it has no parents)
// Otherwise get the next parent
if (parentSkill.nParents == 0) {
notAtRoot = false;
} else {
parentSkillId = parentSkill.parents[0];
parentSkill = skills[parentSkill.parents[0]];
}

treeWalkingCounter += 1;
}

emit SkillAdded(skillCount, _parentSkillId);
return skillCount;
}

function deprecateSkill(
uint256 _skillId,
bool _deprecated
) public stoppable allowedToAddSkill returns (bool) {
require(
skills[_skillId].nParents == 0,
"colony-network-deprecate-local-skills-temporarily-disabled"
);
bool changed = skills[_skillId].deprecated != _deprecated;
skills[_skillId].deprecated = _deprecated;
return changed;
}

/// @notice @deprecated
function deprecateSkill(uint256 _skillId) public stoppable {
deprecateSkill(_skillId, true);
}

function initialiseRootLocalSkill() public stoppable calledByColony returns (uint256) {
skillCount++;
return skillCount;
}

function appendReputationUpdateLog(
address _user,
int _amount,
uint _skillId
) public stoppable calledByColony skillExists(_skillId) {
if (_amount == 0 || _user == address(0x0)) {
// We short-circut amount=0 as it has no effect to save gas, and we ignore Address Zero because it will
// mess up the tracking of the total amount of reputation in a colony, as that's the key that it's
// stored under in the patricia/merkle tree. Colonies can still pay tokens out to it if they want,
// it just won't earn reputation.
return;
}

uint128 nParents = skills[_skillId].nParents;
// We only update child skill reputation if the update is negative, otherwise just set nChildren to 0 to save gas
uint128 nChildren = _amount < 0 ? skills[_skillId].nChildren : 0;
IReputationMiningCycle(inactiveReputationMiningCycle).appendReputationUpdateLog(
_user,
_amount,
_skillId,
msgSender(),
nParents,
nChildren
);
}

// View

function getParentSkillId(uint _skillId, uint _parentSkillIndex) public view returns (uint256) {
return ascendSkillTree(_skillId, _parentSkillIndex + 1);
}

function getChildSkillId(uint _skillId, uint _childSkillIndex) public view returns (uint256) {
if (_childSkillIndex == UINT256_MAX) {
return _skillId;
} else {
Skill storage skill = skills[_skillId];
require(
_childSkillIndex < skill.children.length,
"colony-network-out-of-range-child-skill-index"
);
return skill.children[_childSkillIndex];
}
}

// Internal

function ascendSkillTree(uint _skillId, uint _parentSkillNumber) internal view returns (uint256) {
if (_parentSkillNumber == 0) {
return _skillId;
}

Skill storage skill = skills[_skillId];
for (uint256 i; i < skill.parents.length; i++) {
if (2 ** (i + 1) > _parentSkillNumber) {
uint _newSkillId = skill.parents[i];
uint _newParentSkillNumber = _parentSkillNumber - 2 ** i;
return ascendSkillTree(_newSkillId, _newParentSkillNumber);
}
}
}
}
2 changes: 2 additions & 0 deletions helpers/upgradable-contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ exports.setupUpgradableColonyNetwork = async function setupUpgradableColonyNetwo
colonyNetworkAuction,
colonyNetworkENS,
colonyNetworkExtensions,
colonyNetworkSkills,
contractRecovery,
) {
const deployedImplementations = {};
Expand All @@ -115,6 +116,7 @@ exports.setupUpgradableColonyNetwork = async function setupUpgradableColonyNetwo
deployedImplementations.ColonyNetworkMining = colonyNetworkMining.address;
deployedImplementations.ColonyNetworkAuction = colonyNetworkAuction.address;
deployedImplementations.ColonyNetworkENS = colonyNetworkENS.address;
deployedImplementations.ColonyNetworkSkills = colonyNetworkSkills.address;
deployedImplementations.ColonyNetworkExtensions = colonyNetworkExtensions.address;
deployedImplementations.ContractRecovery = contractRecovery.address;

Expand Down
2 changes: 2 additions & 0 deletions migrations/2_deploy_contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const ColonyNetworkMining = artifacts.require("./ColonyNetworkMining");
const ColonyNetworkAuction = artifacts.require("./ColonyNetworkAuction");
const ColonyNetworkENS = artifacts.require("./ColonyNetworkENS");
const ColonyNetworkExtensions = artifacts.require("./ColonyNetworkExtensions");
const ColonyNetworkSkills = artifacts.require("./ColonyNetworkSkills");
const ReputationMiningCycle = artifacts.require("./ReputationMiningCycle");
const ReputationMiningCycleRespond = artifacts.require("./ReputationMiningCycleRespond");
const ReputationMiningCycleBinarySearch = artifacts.require("./ReputationMiningCycleBinarySearch");
Expand All @@ -28,6 +29,7 @@ module.exports = async function (deployer, network) {
await deployer.deploy(ColonyNetworkAuction);
await deployer.deploy(ColonyNetworkENS);
await deployer.deploy(ColonyNetworkExtensions);
await deployer.deploy(ColonyNetworkSkills);
await deployer.deploy(ReputationMiningCycle);
await deployer.deploy(ReputationMiningCycleRespond);
await deployer.deploy(ReputationMiningCycleBinarySearch);
Expand Down
3 changes: 3 additions & 0 deletions migrations/3_setup_colony_network.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ColonyNetworkMining = artifacts.require("./ColonyNetworkMining");
const ColonyNetworkAuction = artifacts.require("./ColonyNetworkAuction");
const ColonyNetworkENS = artifacts.require("./ColonyNetworkENS");
const ColonyNetworkExtensions = artifacts.require("./ColonyNetworkExtensions");
const ColonyNetworkSkills = artifacts.require("./ColonyNetworkSkills");
const EtherRouter = artifacts.require("./EtherRouter");
const Resolver = artifacts.require("./Resolver");

Expand All @@ -22,6 +23,7 @@ module.exports = async function (deployer) {
const colonyNetworkAuction = await ColonyNetworkAuction.deployed();
const colonyNetworkENS = await ColonyNetworkENS.deployed();
const colonyNetworkExtensions = await ColonyNetworkExtensions.deployed();
const colonyNetworkSkills = await ColonyNetworkSkills.deployed();
const etherRouter = await EtherRouter.deployed();
const resolver = await Resolver.deployed();
const contractRecovery = await ContractRecovery.deployed();
Expand All @@ -35,6 +37,7 @@ module.exports = async function (deployer) {
colonyNetworkAuction,
colonyNetworkENS,
colonyNetworkExtensions,
colonyNetworkSkills,
contractRecovery,
);

Expand Down
10 changes: 5 additions & 5 deletions test-smoke/colony-storage-consistent.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ contract("Contract Storage", (accounts) => {
console.log("miningCycleStateHash:", miningCycleStateHash);
console.log("tokenLockingStateHash:", tokenLockingStateHash);

expect(colonyNetworkStateHash).to.equal("0xe2a19d28c1a68778bfe793623d1b9f71f43db3e98b46fef29f3ea1040968f26c");
expect(colonyStateHash).to.equal("0x58b09676f8fb26ec467b5bb8ea3392b6da0db191acc5ee2f400a0940ee79f4ce");
expect(metaColonyStateHash).to.equal("0xa09c107f9a66e313434ba2d6633e09c15fcb365db7678cf4dc4a19ca481a3954");
expect(miningCycleStateHash).to.equal("0xfd18a690f69132bd95d32bf3a91cb2b60d0da16993cd60087bf8ccc1fa75b680");
expect(tokenLockingStateHash).to.equal("0x0a66e763122dc805a1fcd36aa1f0cc40228ffa53ed050fec4ac78c70cad4d31a");
expect(colonyNetworkStateHash).to.equal("0x7df06499d65ae6b6164fc768c7cfc89e0c7a56d5483a21a9a95cafa8eaaee719");
expect(colonyStateHash).to.equal("0xcfcaeb63eba9378b73a4c62e5a4cb4674b4e301f73814776f16f717055a7c295");
expect(metaColonyStateHash).to.equal("0xba451b41b29bc477b8b53f057b9252c6daedeab4ee917b5ba00d46f6d2919bfc");
expect(miningCycleStateHash).to.equal("0xf453858c03397af01668d54e6031e5a5594cff94c8fdb618c44899cd24cb1856");
expect(tokenLockingStateHash).to.equal("0xb0e53da184faa87011a47b92e99d95f22afcc2feba5aa009be659242df09df63");
});
});
});