Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
9028e01
First commit, to be squashed
area Mar 24, 2023
7941173
Bridge skills on creation to home chain
area Apr 9, 2023
77c683b
Add functionality to bridge reputation state
area Apr 10, 2023
288c1f6
Add some more bridging tests
area Apr 13, 2023
9ce19c1
Low hanging fruit from first (p)review
area Apr 20, 2023
f414a79
Test reputation decay on troubled bridging
area May 2, 2023
ca2ee5d
Additional multichain tests
area May 5, 2023
bd06c0e
Straighten out bridged skill trees so they match
area May 10, 2023
f1c66bb
First changes following second review
area Jun 9, 2023
5c46784
First changes following second review
area Jun 9, 2023
964168e
Update relevant Network struct definitions
kronosapiens Jun 12, 2023
ce3e83c
Add helper functions, misc refactoring
kronosapiens Jun 12, 2023
6a0a36d
Introduce ColonyNetworkSkills
kronosapiens Jun 13, 2023
673b395
Make bridging function names consistent
kronosapiens Jun 13, 2023
f62b082
Minor test edits
kronosapiens Jun 13, 2023
846ebb9
Fix chainIds, revert cross-chain setup
area Jun 18, 2023
ba4eb0b
Some tweaks from review, add events
area Jun 21, 2023
9a96c8b
Resurrect skipped tests as appropriate
area Jun 22, 2023
c01cf6f
Minor close to final tweaks
area Jun 22, 2023
9b1ba3a
Non-functional tweaks
area Jun 26, 2023
ef4b1de
Some contract tidying, extra tests for coverage
area Jun 26, 2023
8d0aebb
Slither updates
area Jun 26, 2023
6127b4b
Change how bridged transactions are tracked in tests
area Jun 30, 2023
77ffe12
Add guards for unsupported large chainIds
area Aug 7, 2023
77164cf
Add missing awaits to tests
area Aug 7, 2023
78c4f55
Try splitting up reputation tests
area Aug 8, 2023
fcf3ae3
Meaningless tweaks and correctly error-out in tests
area Aug 8, 2023
daad9b3
First changes following second review
area Jun 9, 2023
bafa705
Some contract tidying, extra tests for coverage
area Jun 26, 2023
dd91275
Per-domain reputation scaling
area May 18, 2023
9fb0402
Per-token reputation
area May 18, 2023
c43ea80
Payout scalar changes
area May 22, 2023
c3ac1eb
Colony-specific decay rates
area May 23, 2023
be35908
Reputation on payout, not finalization
area Jun 8, 2023
70b6dbd
First review changes
area Jun 8, 2023
7023a61
Factor out reputation scaling function
area Jun 9, 2023
d3f18ec
Store complement of skill, allow mining reward scaling
area Jun 10, 2023
633ae97
Reviewer refactor
kronosapiens Jun 14, 2023
e403511
Update permissions, kill nested terneries with fire
area Jun 18, 2023
30ee518
Store skill scaling on colony
area Jun 18, 2023
abef9cf
Make skill reputation scaling calculation more efficient
area Jun 20, 2023
beb8281
Cleanup post-rebase
area Jul 18, 2023
44cb1a2
Cross-chain variable decay
area Jun 28, 2023
22f263d
Easy changes following review
area Jul 17, 2023
22809d1
Slightly harder changes following review
area Jul 19, 2023
a96f3e3
Make skill bridging on colony creation more reliable in tests
area Aug 1, 2023
942568f
Changes post-review
area Aug 8, 2023
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
19 changes: 15 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,18 @@ jobs:
sudo apt-get install lsof
- run:
name: "Running reputation system unit tests"
command: npm run test:reputation
command: npm run test:reputation:1
environment:
NODE_OPTIONS: --max-old-space-size=6144
- run:
name: "Reset chains"
command: |
sudo apt-get update
sudo apt-get install lsof
npm run stop:blockchain:client && rm -rf ganache-chain-db*
- run:
name: "Running reputation system unit tests"
command: npm run test:reputation:2
environment:
NODE_OPTIONS: --max-old-space-size=6144
- run:
Expand All @@ -85,7 +96,7 @@ jobs:
- <<: *step_setup_global_packages
- run:
name: "Download parity"
command: wget https://releases.parity.io/ethereum/v2.3.8/x86_64-unknown-linux-gnu/parity
command: wget https://releases.parity.io/ethereum/v2.7.2/x86_64-unknown-linux-gnu/parity
- run:
name: "Setup parity"
command: |
Expand Down Expand Up @@ -308,7 +319,7 @@ jobs:
command: |
sudo apt-get update
sudo apt-get install lsof
npm run stop:blockchain:client
npm run stop:blockchain:client && rm -rf ganache-chain-db*
- run:
name: "Running coverage tests for foreign-side of bridge"
command: npm run test:contracts:bridging:2:coverage
Expand All @@ -326,7 +337,7 @@ jobs:
- <<: *step_restore_cache
- run:
name: "Install packages"
command: |
command: |
sudo npm install -g npm@8.5.5
npm ci
- attach_workspace:
Expand Down
1 change: 0 additions & 1 deletion .solcover.chainid.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ config.providerOptions.network_id = parseInt(process.env.CHAIN_ID, 10);
config.providerOptions._chainId = parseInt(process.env.CHAIN_ID, 10);
config.providerOptions._chainIdRpc = parseInt(process.env.CHAIN_ID, 10);
config.istanbulFolder = `./coverage-chainid-${process.env.CHAIN_ID}`

module.exports = config
19 changes: 18 additions & 1 deletion .solcover.crosschain.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
const config = require("./.solcover.js")
const log = console.log;
const { execSync } = require("child_process");
const ethers = require("ethers");

const { FORKED_XDAI_CHAINID } = require("./helpers/constants");

const existingCompileComplete = config.onCompileComplete;

config.istanbulFolder = `./coverage-cross-chain-${process.env.TRUFFLE_HOME ? "home" : "foreign"}`
let chainId;
// We configure the truffle coverage chain to have the same chainid as one of the
// nodes we've started up, but on a different port
// TODO: Actually query nodes, don't hard-code here, or work out how to get environment
// variables in package.json to work here as I want.
if (JSON.parse(process.env.TRUFFLE_FOREIGN)){
chainId = FORKED_XDAI_CHAINID + 1;
} else {
chainId = FORKED_XDAI_CHAINID;
}

config.providerOptions.network_id = chainId;
config.providerOptions._chainId = chainId;
config.providerOptions._chainIdRpc = chainId;

config.istanbulFolder = `./coverage-cross-chain-${JSON.parse(process.env.TRUFFLE_FOREIGN) ? "foreign" : "home"}`

function provisionSafeContracts(){
let output;
Expand Down
9 changes: 6 additions & 3 deletions .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ const log = console.log;
// Copies pre-built token artifacts to .coverage_artifacts/contracts
function provisionTokenContracts(config){
let output;
const provisionColonyToken = `BUILD_DIR="build-coverage" bash ./scripts/provision-token-contracts.sh`;
const provisionColonyToken = `BUILD_DIR="build-coverage" npm run provision:token:contracts`;

log('Provisioning ColonyToken contracts...')
output = execSync(provisionColonyToken);
log(output.toString())

const provisionSafeContracts = `BUILD_DIR="build-coverage" bash ./scripts/provision-safe-contracts.sh`;
const provisionSafeContracts = `BUILD_DIR="build-coverage" npm run provision:safe:contracts`;

log('Provisioning Safe contracts...')
output = execSync(provisionSafeContracts);
Expand All @@ -33,6 +33,9 @@ module.exports = {
account_keys_path: "./ganache-accounts.json",
vmErrorsOnRPCResponse: false,
total_accounts: 18,
_chainId: 265669100,
_chainIdRpc: 265669100,
network_id: 265669100,
accounts: [
{secretKey:"0x0355596cdb5e5242ad082c4fe3f8bbe48c9dba843fe1f99dd8272f487e70efae","balance":"100000000000000000000"},
{secretKey:"0xe9aebe8791ad1ebd33211687e9c53f13fe8cca53b271a6529c7d7ba05eda5ce2","balance":"100000000000000000000"},
Expand All @@ -56,5 +59,5 @@ module.exports = {
},
onCompileComplete: provisionTokenContracts,
istanbulFolder: "./coverage-contracts",
modifierWhitelist: ["always"],
modifierWhitelist: ["always", "onlyMiningChain", "onlyNotMiningChain"],
}
79 changes: 73 additions & 6 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
{
require(_amount > 0, "colony-reward-must-be-positive");
require(domainExists(_domainId), "colony-domain-does-not-exist");
IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, domains[_domainId].skillId);
emitReputation(_user, _amount, domains[_domainId].skillId);

emit ArbitraryReputationUpdate(msgSender(), _user, domains[_domainId].skillId, _amount);
}
Expand All @@ -57,7 +57,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
public stoppable auth validGlobalOrLocalSkill(_skillId)
{
require(_amount > 0, "colony-reward-must-be-positive");
IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, _skillId);
emitReputation(_user, _amount, _skillId);

emit ArbitraryReputationUpdate(msgSender(), _user, _skillId, _amount);
}
Expand All @@ -71,7 +71,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId)
{
require(_amount <= 0, "colony-penalty-cannot-be-positive");
IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, domains[_domainId].skillId);
emitReputation(_user, _amount, domains[_domainId].skillId);

emit ArbitraryReputationUpdate(msgSender(), _user, domains[_domainId].skillId, _amount);
}
Expand All @@ -80,7 +80,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
public stoppable auth validGlobalOrLocalSkill(_skillId)
{
require(_amount <= 0, "colony-penalty-cannot-be-positive");
IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_user, _amount, _skillId);
emitReputation(_user, _amount, _skillId);

emit ArbitraryReputationUpdate(msgSender(), _user, _skillId, _amount);
}
Expand Down Expand Up @@ -114,7 +114,9 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
// After doing all the local storage changes, then do all the external calls
for (uint256 i = 0; i < _users.length; i++) {
require(ERC20Extended(token).transfer(_users[i], uint256(_amounts[i])), "colony-bootstrap-token-transfer-failed");
IColonyNetwork(colonyNetworkAddress).appendReputationUpdateLog(_users[i], _amounts[i], domains[1].skillId);
uint256 scaleFactor = tokenReputationScalings[token]; // NB This is a WAD
int256 tokenScaledReputationAmount = scaleReputation(_amounts[i], scaleFactor);
emitReputation(_users[i], tokenScaledReputationAmount, domains[1].skillId);
}

emit ColonyBootstrapped(msgSender(), _users, _amounts);
Expand Down Expand Up @@ -173,6 +175,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
function addGlobalSkill() public
stoppable
auth
onlyMiningChain
returns (uint256)
{
return IColonyNetwork(colonyNetworkAddress).addSkill(0); // ignore-swc-107
Expand Down Expand Up @@ -214,6 +217,38 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
IColonyNetwork(colonyNetworkAddress).addColonyVersion(_version, _resolver);
}

function setBridgeData(
address _bridgeAddress,
uint256 _chainId,
uint256 _gas,
bytes memory _updateLogBefore,
bytes memory _updateLogAfter,
bytes memory _skillCreationBefore,
bytes memory _skillCreationAfter,
bytes memory _setReputationRootHashBefore,
bytes memory _setReputationRootHashAfter,
bytes memory _setColonyDecayRateBefore,
bytes memory _setColonyDecayRateAfter
)
external
stoppable
auth
{
IColonyNetwork(colonyNetworkAddress).setBridgeData(
_bridgeAddress,
_chainId,
_gas,
_updateLogBefore,
_updateLogAfter,
_skillCreationBefore,
_skillCreationAfter,
_setReputationRootHashBefore,
_setReputationRootHashAfter,
_setColonyDecayRateBefore,
_setColonyDecayRateAfter
);
}

function addExtensionToNetwork(bytes32 _extensionId, address _resolver)
public stoppable auth
{
Expand Down Expand Up @@ -315,14 +350,38 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
ColonyAuthority colonyAuthority = ColonyAuthority(address(authority));
bytes4 sig;

sig = bytes4(keccak256("makeArbitraryTransactions(address[],bytes[],bool)"));
sig = bytes4(keccak256("setBridgeData(address,uint256,uint256,bytes,bytes,bytes,bytes,bytes,bytes,bytes,bytes)"));
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("setDomainReputationScaling(uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true);

// Set the default token weighting for the native token
tokenReputationScalings[token] = WAD;

sig = bytes4(keccak256("setReputationDecayRate(uint256,uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true);

sig = bytes4(keccak256("setReputationMiningCycleRewardReputationScaling(uint256)"));
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true);
}

function setTokenReputationScaling(address _token, uint256 _scaling) public stoppable {
tokenReputationScalings[_token] = _scaling;
}

function getTokenReputationScaling(address _token) public view returns (uint256) {
return tokenReputationScalings[_token];
}

function setReputationDecayRate(uint256 _numerator, uint256 _denominator) stoppable auth public {
IColonyNetwork(colonyNetworkAddress).setColonyReputationDecayRate(_numerator, _denominator);
}

function getMetatransactionNonce(address _user) override public view returns (uint256 nonce){
Expand Down Expand Up @@ -400,4 +459,12 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
return tokenApprovalTotals[_token];
}

function setReputationMiningCycleRewardReputationScaling(uint256 _factor) public stoppable auth {
require(_factor <= WAD, "colony-invalid-reputation-scale-factor");
require(IColonyNetwork(colonyNetworkAddress).getMetaColony() == address(this), "colony-only-on-metacolony");
uint256 reputationMiningSkillId = IColonyNetwork(colonyNetworkAddress).getReputationMiningSkillId();
skillReputationScalingComplements[reputationMiningSkillId] = WAD - _factor;

emit MiningReputationScalingSet(_factor);
}
}
7 changes: 7 additions & 0 deletions contracts/colony/ColonyAuthority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ contract ColonyAuthority is CommonAuthority {

// Added in colony v10 (ginger-lwss)
addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,address,uint256)");

// Added in colony vxxx
addRoleCapability(ROOT_ROLE, "setBridgeData(address,uint256,uint256,bytes,bytes,bytes,bytes,bytes,bytes,bytes,bytes)");
addRoleCapability(ROOT_ROLE, "setDomainReputationScaling(uint256,uint256)");
addRoleCapability(ROOT_ROLE, "setReputationDecayRate(uint256,uint256)");
addRoleCapability(ROOT_ROLE, "setReputationMiningCycleRewardReputationScaling(uint256)");

}

function addRoleCapability(uint8 role, bytes memory sig) private {
Expand Down
6 changes: 6 additions & 0 deletions contracts/colony/ColonyDataTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ interface ColonyDataTypes {

event ArbitraryTransaction(address target, bytes data, bool success);

event DomainReputationScalingSet(uint256 domainId, uint256 factor);

/// @notice Event logged when the reputation scaling factor for miners is set
/// @param factor The factor (from 0 to WAD) governing how reputation awards for miners is scaled
event MiningReputationScalingSet(uint256 factor);

struct RewardPayoutCycle {
// Reputation root hash at the time of reward payout creation
bytes32 reputationState;
Expand Down
12 changes: 12 additions & 0 deletions contracts/colony/ColonyDomains.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ contract ColonyDomains is ColonyStorage {
// Set initial colony reward inverse amount to the max indicating a zero rewards to start with
rewardInverse = 2**256 - 1;

// Set the token weighting for the native token to 1
tokenReputationScalings[token] = WAD;

emit ColonyInitialised(msgSender(), _colonyNetworkAddress, _token);
}

Expand Down Expand Up @@ -124,6 +127,15 @@ contract ColonyDomains is ColonyStorage {
return domainCount;
}

function setDomainReputationScaling(uint256 _domainId, uint256 _factor) public stoppable auth {
require(domainExists(_domainId), "colony-domain-does-not-exist");
require(_factor <= WAD, "colony-network-invalid-reputation-scale-factor");

skillReputationScalingComplements[domains[_domainId].skillId] = WAD - _factor;

emit DomainReputationScalingSet(_domainId, _factor);
}

// Internal

function initialiseDomain(uint256 _skillId) internal {
Expand Down
10 changes: 5 additions & 5 deletions contracts/colony/ColonyExpenditure.sol
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ contract ColonyExpenditure is ColonyStorage {
expenditureOnlyOwner(_id)
{
require(_slots.length == _skillIds.length, "colony-expenditure-bad-slots");
IColonyNetwork colonyNetworkContract = IColonyNetwork(colonyNetworkAddress);

for (uint256 i; i < _slots.length; i++) {
require(isValidGlobalOrLocalSkill(_skillIds[i]), "colony-not-valid-global-or-local-skill");
Expand Down Expand Up @@ -214,6 +213,7 @@ contract ColonyExpenditure is ColonyStorage {
require(_slots.length == _payoutModifiers.length, "colony-expenditure-bad-slots");

for (uint256 i; i < _slots.length; i++) {
require(_payoutModifiers[i] <= 0, "colony-expenditure-bad-payout-modifier");
expenditureSlots[_id][_slots[i]].payoutModifier = _payoutModifiers[i];

emit ExpenditurePayoutModifierSet(msgSender(), _id, _slots[i], _payoutModifiers[i]);
Expand Down Expand Up @@ -316,14 +316,14 @@ contract ColonyExpenditure is ColonyStorage {
// Validate payout modifier
if (offset == 2) {
require(
int256(uint256(_value)) <= MAX_PAYOUT_MODIFIER &&
(int256(uint256(_value)) <= 0 || IColony(address(this)).hasUserRole(msgSender(), 1, ColonyRole.Root)) &&
int256(uint256(_value)) >= MIN_PAYOUT_MODIFIER,
"colony-expenditure-bad-payout-modifier"
);
}

} else {
require(false, "colony-expenditure-bad-slot");
revert("colony-expenditure-bad-slot");
}

executeStateChange(keccak256(abi.encode(_id, _storageSlot)), _mask, _keys, _value);
Expand Down Expand Up @@ -361,11 +361,11 @@ contract ColonyExpenditure is ColonyStorage {
internal
{
for (uint256 i; i < _tokens.length; i++) {
(bool success, bytes memory returndata) = address(this).delegatecall(
(bool success, bytes memory returndata) = address(this).delegatecall( // solhint-disable-line avoid-low-level-calls
abi.encodeWithSignature("setExpenditurePayouts(uint256,uint256[],address,uint256[])", _id, _slots[i], _tokens[i], _values[i])
);
if (!success) {
if (returndata.length == 0) revert();
if (returndata.length == 0) revert("colony-expenditure-null-return");
assembly {
revert(add(32, returndata), mload(returndata))
}
Expand Down
Loading