diff --git a/.circleci/config.yml b/.circleci/config.yml index c147ae4c5e..470aa28cd5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,19 @@ step_setup_slither: &step_setup_slither pip install solc-select solc-select install 0.8.25 solc-select use 0.8.25 - +step_install_nvm: &step_install_nvm + run: + name: "Install nvm" + command: | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + echo 'export NVM_DIR=$HOME/.nvm' >> $BASH_ENV + echo 'source $NVM_DIR/nvm.sh' >> $BASH_ENV +step_install_lsof: &step_install_lsof + run: + name: "Install lsof" + command: | + sudo apt-get update + sudo apt-get install lsof jobs: unit-test-reputation: <<: *job_common @@ -63,11 +75,7 @@ jobs: version: docker23 - <<: *step_pull_solc_docker - <<: *step_setup_global_packages - - run: - name: "Install lsof" - command: | - sudo apt-get update - sudo apt-get install lsof + - <<: *step_install_lsof - run: name: "Running reputation system unit tests" command: npm run test:reputation:1 @@ -76,8 +84,6 @@ jobs: - 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" @@ -129,6 +135,7 @@ jobs: - run: name: "Upload function selectors to 4byte.directory" command: npx hardhat upload-selectors + - <<: *step_install_nvm - run: name: "Checking contract versions" command: pnpm run check:versioning @@ -161,11 +168,7 @@ jobs: version: docker23 - <<: *step_pull_solc_docker - <<: *step_setup_global_packages - - run: - name: "Install lsof" - command: | - sudo apt-get update - sudo apt-get install lsof + - <<: *step_install_lsof - run: name: "Running network contracts unit tests" command: pnpm run test:contracts @@ -188,11 +191,7 @@ jobs: version: docker23 - <<: *step_pull_solc_docker - <<: *step_setup_global_packages - - run: - name: "Install lsof" - command: | - sudo apt-get update - sudo apt-get install lsof + - <<: *step_install_lsof - run: name: "Running extension contracts unit tests" command: pnpm run test:contracts:extensions @@ -243,11 +242,7 @@ jobs: sed -i "s/000000000000000000000000000000deadbeef16/$(parity account new --chain ./parity-genesis.json --keys-path ./keys --password ./parityPassword)/g" ./parity-genesis.json sed -i "s/000000000000000000000000000000deadbeef17/$(parity account new --chain ./parity-genesis.json --keys-path ./keys --password ./parityPassword)/g" ./parity-genesis.json sed -i "s/000000000000000000000000000000deadbeef18/$(parity account new --chain ./parity-genesis.json --keys-path ./keys --password ./parityPassword)/g" ./parity-genesis.json - - run: - name: "Install lsof" - command: | - sudo apt-get update - sudo apt-get install lsof + - <<: *step_install_lsof - run: name: "Running ganache upgrade tests" command: pnpm run test:contracts:upgrade:ganache @@ -260,11 +255,10 @@ jobs: - run: name: "Running gas cost tests" command: pnpm run test:contracts:gasCosts && npx codechecks + - <<: *step_install_lsof - run: name: "Reset chains" command: | - sudo apt-get update - sudo apt-get install lsof pnpm run stop:blockchain:client && rm -rf ganache-chain-db* - run: name: "Running patricia tree tests" @@ -402,11 +396,10 @@ jobs: command: pnpm run test:contracts:bridging:1:coverage environment: NODE_OPTIONS: --max-old-space-size=6144 + - <<: *step_install_lsof - run: name: "Reset chains" command: | - sudo apt-get update - sudo apt-get install lsof pnpm run stop:blockchain:client && rm -rf ganache-chain-db* - run: name: "Running coverage tests for foreign-side of bridge" @@ -427,11 +420,7 @@ jobs: version: docker23 - <<: *step_pull_solc_docker - <<: *step_setup_global_packages - - run: - name: "Install lsof" - command: | - sudo apt-get update - sudo apt-get install lsof + - <<: *step_install_lsof - run: name: "Running reputation system unit tests" command: pnpm run test:reputation:1:anotherChain @@ -440,8 +429,6 @@ jobs: - run: name: "Reset chains" command: | - sudo apt-get update - sudo apt-get install lsof pnpm run stop:blockchain:client && rm -rf ganache-chain-db* - run: name: "Running reputation system unit tests" diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index 7996f35bab..bcad30ac35 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -34,7 +34,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP // This function, exactly as defined, is used in build scripts. Take care when updating. // Version number should be upped with every change in Colony or its dependency contracts or libraries. // prettier-ignore - function version() public pure returns (uint256 colonyVersion) { return 15; } + function version() public pure returns (uint256 colonyVersion) { return 16; } function getColonyNetwork() public view returns (address) { return colonyNetworkAddress; diff --git a/helpers/constants.js b/helpers/constants.js index 20dbeede38..521c87cfc1 100644 --- a/helpers/constants.js +++ b/helpers/constants.js @@ -13,7 +13,7 @@ const INT256_MIN = new BN(2).pow(new BN(255)).mul(new BN(-1)); const INT128_MAX = new BN(2).pow(new BN(127)).sub(new BN(1)); const INT128_MIN = new BN(2).pow(new BN(127)).mul(new BN(-1)); -const CURR_VERSION = 15; +const CURR_VERSION = 16; const RECOVERY_ROLE = 0; const ROOT_ROLE = 1; diff --git a/helpers/test-helper.js b/helpers/test-helper.js index b21c7a5f48..72bcd0bea6 100644 --- a/helpers/test-helper.js +++ b/helpers/test-helper.js @@ -20,9 +20,11 @@ const { XDAI_CHAINID, FORKED_XDAI_CHAINID, CREATEX_ADDRESS, + CURR_VERSION, } = require("./constants"); const IColony = artifacts.require("IColony"); +const IColonyNetwork = artifacts.require("IColonyNetwork"); const IMetaColony = artifacts.require("IMetaColony"); const ITokenLocking = artifacts.require("ITokenLocking"); const Token = artifacts.require("Token"); @@ -1272,13 +1274,33 @@ exports.getMultichainSkillId = function getMultichainSkillId(chainId, skillId) { return ethers.BigNumber.from(chainId).mul(ethers.BigNumber.from(2).pow(128)).add(ethers.BigNumber.from(skillId)); }; -exports.upgradeColonyTo = async function (colony, _version) { - const version = new BN(_version); - let currentVersion = await colony.version(); - while (currentVersion.ltn(version)) { - await colony.upgrade(currentVersion.addn(1)); - currentVersion = await colony.version(); - } +exports.upgradeColonyOnceThenToLatest = async function (colony) { + // Assume that we need to do one 'proper' upgrade, and then we just + // set the version to the desired version. The idea here is that we are testing either + // * Support in the latest version for states that are only archievable historically in version X + // * A bug in version X that was fixed. + // In either case, installing version X, doing what we need in the test, and then upgrading to X+1 + // and then the latest version should be sufficient. The 'proper' upgrade allows for a fix to be applied + // in `finishUpgrade`, and then upgrading to the latest version will let us check that whatever the state + // is, we can handle it 'correctly', whatever that means. + // This means that our tests don't get longer and longer to run from deploying and upgrading through every + // version in between. + const currentVersion = await colony.version(); + await colony.upgrade(currentVersion.addn(1)); + + const networkAddress = await colony.getColonyNetwork(); + const colonyNetwork = await IColonyNetwork.at(networkAddress); + + const editableColony = await exports.getColonyEditable(colony, colonyNetwork); + const existingSlot = await exports.web3GetStorageAt(colony.address, 2); + + // Doing it this way preserves the items that share this storage slot with the address, + // which are recoverymode related. + const newestResolver = await colonyNetwork.getColonyVersionResolver(CURR_VERSION); + + const newSlotValue = existingSlot.slice(0, 26) + newestResolver.slice(2); + + await editableColony.setStorageSlot(2, newSlotValue); }; exports.isMainnet = async function isMainnet() { diff --git a/scripts/versioningCheck.sh b/scripts/versioningCheck.sh index e4ff7d09a9..b0c5558638 100644 --- a/scripts/versioningCheck.sh +++ b/scripts/versioningCheck.sh @@ -6,15 +6,34 @@ CURRENT_BRANCH=`git branch --show-current` git checkout $LATEST_RELEASE + +if (command -v nvm &> /dev/null) +then + NODE_MANAGER="nvm" +elif (command -v n &> /dev/null) +then + NODE_MANAGER="n" +elif (command -v fnm &> /dev/null) +then + NODE_MANAGER="fnm" +else + echo "No node manager found" + exit 1 +fi + # Compile release -npm ci --force && npx truffle compile -rm -rf build-$LATEST_RELEASE || true -mv build build-$LATEST_RELEASE +$NODE_MANAGER install +$NODE_MANAGER use +npm ci --force && npx hardhat compile +rm -rf artifacts-$LATEST_RELEASE || true +mv artifacts artifacts-$LATEST_RELEASE # Compile current commit git checkout $CURRENT_BRANCH +$NODE_MANAGER install +$NODE_MANAGER use find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + -npm ci && npx hardhat compile +pnpm install --frozen-lockfile && npx hardhat compile version_from_commit() { COMMIT=$1; @@ -64,7 +83,7 @@ compare_bytecodes_check_extension_version() { CONTRACT_NAME=`basename $1 .sol` FILE_WITH_VERSION=$2 - LAST_RELEASE_FILE="build-$LATEST_RELEASE/contracts/$CONTRACT_NAME.json" + LAST_RELEASE_FILE="artifacts-$LATEST_RELEASE/contracts/extensions/$CONTRACT_NAME.sol/$CONTRACT_NAME.json" THIS_COMMIT_FILE="artifacts/contracts/extensions/$CONTRACT_NAME.sol/$CONTRACT_NAME.json" if [ ! -f "$LAST_RELEASE_FILE" ]; then @@ -72,7 +91,7 @@ compare_bytecodes_check_extension_version() { return fi - LAST_RELEASE_BYTECODE=$(relevant_bytecode ./build-$LATEST_RELEASE/contracts/$CONTRACT_NAME.json) + LAST_RELEASE_BYTECODE=$(relevant_bytecode ./artifacts-$LATEST_RELEASE/contracts/extensions/$CONTRACT_NAME.sol/$CONTRACT_NAME.json) NEW_BYTECODE=$(relevant_bytecode ./artifacts/contracts/extensions/$CONTRACT_NAME.sol/$CONTRACT_NAME.json) # If the bytecode is different, check the version in the appropriate file @@ -107,7 +126,7 @@ extension_check_and_dependencies() { for file in contracts/colony/* do CONTRACT_NAME=`basename $file .sol` - LAST_RELEASE_BYTECODE=$(relevant_bytecode ./build-$LATEST_RELEASE/contracts/$CONTRACT_NAME.json) + LAST_RELEASE_BYTECODE=$(relevant_bytecode ./artifacts-$LATEST_RELEASE/contracts/colony/$CONTRACT_NAME.sol/$CONTRACT_NAME.json) NEW_BYTECODE=$(relevant_bytecode ./artifacts/contracts/colony/$CONTRACT_NAME.sol/$CONTRACT_NAME.json) # If the bytecode is different, check the version in colony diff --git a/test/contracts-network/colony-expenditure.js b/test/contracts-network/colony-expenditure.js index fe1342c85e..4d053b032f 100644 --- a/test/contracts-network/colony-expenditure.js +++ b/test/contracts-network/colony-expenditure.js @@ -5,18 +5,16 @@ const { BN } = require("bn.js"); const { ethers } = require("ethers"); const { soliditySha3 } = require("web3-utils"); +const { UINT256_MAX, INT128_MAX, WAD, SECONDS_PER_DAY, MAX_PAYOUT, IPFS_HASH, ADDRESS_ZERO, HASHZERO } = require("../../helpers/constants"); const { - UINT256_MAX, - INT128_MAX, - WAD, - SECONDS_PER_DAY, - MAX_PAYOUT, - IPFS_HASH, - ADDRESS_ZERO, - HASHZERO, - CURR_VERSION, -} = require("../../helpers/constants"); -const { checkErrorRevert, expectEvent, getTokenArgs, forwardTime, getBlockTime, bn2bytes32, upgradeColonyTo } = require("../../helpers/test-helper"); + checkErrorRevert, + expectEvent, + getTokenArgs, + forwardTime, + getBlockTime, + bn2bytes32, + upgradeColonyOnceThenToLatest, +} = require("../../helpers/test-helper"); const { fundColonyWithTokens, setupRandomColony } = require("../../helpers/test-data-generator"); const { setupEtherRouter } = require("../../helpers/upgradable-contracts"); const { @@ -349,7 +347,7 @@ contract("Colony Expenditure", (accounts) => { // Upgrade to current version await colonyNetworkAsEtherRouter.setResolver(latestResolver); - await upgradeColonyTo(metaColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(metaColony); await checkErrorRevert(colony.setExpenditureSkill(expenditureId, SLOT0, globalSkillId, { from: ADMIN }), "colony-not-valid-local-skill"); await checkErrorRevert(colony.setExpenditureSkill(expenditureId, SLOT0, globalSkillId2, { from: ADMIN }), "colony-not-valid-local-skill"); diff --git a/test/contracts-network/colony.js b/test/contracts-network/colony.js index 0953246b13..199b31e648 100755 --- a/test/contracts-network/colony.js +++ b/test/contracts-network/colony.js @@ -4,7 +4,7 @@ const chai = require("chai"); const bnChai = require("bn-chai"); const { ethers } = require("ethers"); -const { IPFS_HASH, UINT256_MAX, WAD, ADDRESS_ZERO, SPECIFICATION_HASH, HASHZERO, CURR_VERSION } = require("../../helpers/constants"); +const { IPFS_HASH, UINT256_MAX, WAD, ADDRESS_ZERO, SPECIFICATION_HASH, HASHZERO } = require("../../helpers/constants"); const { getTokenArgs, web3GetBalance, @@ -12,7 +12,7 @@ const { expectNoEvent, expectAllEvents, expectEvent, - upgradeColonyTo, + upgradeColonyOnceThenToLatest, } = require("../../helpers/test-helper"); const { setupRandomColony, @@ -476,7 +476,7 @@ contract("Colony", (accounts) => { it("should be able to query for a task", async () => { await oldColony.makeTask(1, UINT256_MAX, SPECIFICATION_HASH, 1, localSkillId, 0, { from: USER0 }); - await upgradeColonyTo(oldColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(oldColony); const taskId = await colony.getTaskCount(); const task = await colony.getTask(taskId); @@ -497,7 +497,7 @@ contract("Colony", (accounts) => { it("should be able to query for a payment", async () => { await oldColony.addPayment(1, UINT256_MAX, USER1, token.address, WAD, 1, localSkillId, { from: USER0 }); - await upgradeColonyTo(oldColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(oldColony); const paymentId = await colony.getPaymentCount(); const payment = await colony.getPayment(paymentId); @@ -517,7 +517,7 @@ contract("Colony", (accounts) => { // Move funds into task funding pot await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, UINT256_MAX, 1, fundingPotId, WAD, token.address); - await upgradeColonyTo(oldColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(oldColony); // Move funds back await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, UINT256_MAX, fundingPotId, 1, WAD, token.address); }); @@ -533,7 +533,7 @@ contract("Colony", (accounts) => { // Move funds into task funding pot await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, UINT256_MAX, 1, fundingPotId, WAD, token.address); - await upgradeColonyTo(oldColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(oldColony); // Move funds back await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, UINT256_MAX, fundingPotId, 1, WAD, token.address); }); diff --git a/test/contracts-network/meta-colony.js b/test/contracts-network/meta-colony.js index 46e57379c5..4fd257a282 100644 --- a/test/contracts-network/meta-colony.js +++ b/test/contracts-network/meta-colony.js @@ -5,13 +5,13 @@ const bnChai = require("bn-chai"); const ethers = require("ethers"); const { soliditySha3 } = require("web3-utils"); -const { UINT256_MAX, WAD, ADDRESS_ZERO, HASHZERO, CURR_VERSION } = require("../../helpers/constants"); +const { UINT256_MAX, WAD, ADDRESS_ZERO, HASHZERO } = require("../../helpers/constants"); const { checkErrorRevert, removeSubdomainLimit, restoreSubdomainLimit, bn2bytes32, - upgradeColonyTo, + upgradeColonyOnceThenToLatest, getChainId, } = require("../../helpers/test-helper"); const { setupColonyNetwork, setupMetaColonyWithLockedCLNYToken, setupRandomColony } = require("../../helpers/test-data-generator"); @@ -448,7 +448,7 @@ contract("Meta Colony", (accounts) => { // Upgrade to current version await colonyNetworkAsEtherRouter.setResolver(latestResolver); - await upgradeColonyTo(metaColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(metaColony); }); describe("when getting a skill", () => { diff --git a/test/extensions/one-tx-payment.js b/test/extensions/one-tx-payment.js index 79ed45e5a3..f69909acb2 100644 --- a/test/extensions/one-tx-payment.js +++ b/test/extensions/one-tx-payment.js @@ -14,10 +14,9 @@ const { ADMINISTRATION_ROLE, ADDRESS_ZERO, SECONDS_PER_DAY, - CURR_VERSION, } = require("../../helpers/constants"); -const { checkErrorRevert, web3GetCode, rolesToBytes32, expectEvent, upgradeColonyTo } = require("../../helpers/test-helper"); +const { checkErrorRevert, web3GetCode, rolesToBytes32, expectEvent, upgradeColonyOnceThenToLatest } = require("../../helpers/test-helper"); const { setupRandomColony, fundColonyWithTokens, getMetaTransactionParameters, setupColony } = require("../../helpers/test-data-generator"); const { expect } = chai; @@ -285,7 +284,7 @@ contract("One transaction payments", (accounts) => { // Upgrade to current version await colonyNetworkAsEtherRouter.setResolver(latestResolver); - await upgradeColonyTo(metaColony, CURR_VERSION); + await upgradeColonyOnceThenToLatest(metaColony); await checkErrorRevert( oneTxPayment.makePaymentFundedFromDomain(1, UINT256_MAX, 1, UINT256_MAX, [USER1], [token.address], [10], 1, globalSkillId),