Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/multichain reputation rebased hardhat 2 #1238

Merged
merged 65 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
5f99255
Make upgrade tests work with next version
area Feb 9, 2024
581e5fa
Make upgrade tests work with next version
area Feb 9, 2024
7280bb6
First commit, to be squashed
area Mar 24, 2023
eec4ec4
Bridge skills on creation to home chain
area Apr 9, 2023
e04c364
Add functionality to bridge reputation state
area Apr 10, 2023
b3369d4
Add some more bridging tests
area Apr 13, 2023
164b5d2
Low hanging fruit from first (p)review
area Apr 20, 2023
fc2b943
Test reputation decay on troubled bridging
area May 2, 2023
69804b0
Additional multichain tests
area May 5, 2023
b90ef5c
Straighten out bridged skill trees so they match
area May 10, 2023
64fb07c
First changes following second review
area Jun 9, 2023
aa89f46
First changes following second review
area Jun 9, 2023
070ef94
Update relevant Network struct definitions
kronosapiens Jun 12, 2023
98eafd4
Add helper functions, misc refactoring
kronosapiens Jun 12, 2023
9ae3382
Introduce ColonyNetworkSkills
kronosapiens Jun 13, 2023
b9402e3
Make bridging function names consistent
kronosapiens Jun 13, 2023
6623c79
Minor test edits
kronosapiens Jun 13, 2023
eff163c
Fix chainIds, revert cross-chain setup
area Jun 18, 2023
2099acb
Some tweaks from review, add events
area Jun 21, 2023
669c3a1
Resurrect skipped tests as appropriate
area Jun 22, 2023
6801949
Minor close to final tweaks
area Jun 22, 2023
4ed1684
Non-functional tweaks
area Jun 26, 2023
7c8c897
Some contract tidying, extra tests for coverage
area Jun 26, 2023
fddc5aa
Slither updates
area Jun 26, 2023
98f687c
Change how bridged transactions are tracked in tests
area Jun 30, 2023
5399f02
Add guards for unsupported large chainIds
area Aug 7, 2023
82bff34
Add missing awaits to tests
area Aug 7, 2023
0a1fa3d
Meaningless tweaks and correctly error-out in tests
area Aug 8, 2023
07da17f
Continue making cross-chain tests more robust
area Aug 9, 2023
dee1ae0
Fix flubbed rebase
area Aug 22, 2023
b31fd3c
Add example tests that need to pass
area Sep 1, 2023
25b0886
Add and get all bridging permission tests passing
area Sep 6, 2023
a0dc262
Pickup post-rebase
area Feb 8, 2024
59bfcfc
WIP: Wormhole restructuring
area Feb 16, 2024
b83167b
Make chainIds configurable
area Feb 28, 2024
a6b0b6c
Low hanging fruit from review
area Feb 28, 2024
2c0909c
More updates post-review
area Feb 28, 2024
41f2e19
Remove mintTokensForColonyNetwork
area Mar 1, 2024
ed472cb
Update version-specific extension tests
area Mar 2, 2024
465b55a
Adjust multi-chain logic around mining skill id
area Mar 1, 2024
ad9bc22
Add extra output to upgrade scripts
area Mar 6, 2024
52466e5
First pass after new review
area Mar 18, 2024
12ee067
Finish easy bits from first review plus rebase
area Mar 19, 2024
6639068
Try to factorise calls to bridge
area Mar 20, 2024
afb4b6c
Merge branch 'maint/hardhat-2' into feat/multichain-reputation-rebase…
area Mar 22, 2024
ce8a459
Merge branch 'maint/hardhat-2' into feat/multichain-reputation-rebase…
area Mar 25, 2024
8f31c4f
Further pickups from review
area Mar 29, 2024
f574f27
First attempt to add tests with mining on a non-xdai chain
area Mar 31, 2024
1de0919
Move skillId to a string in miner db
area Mar 31, 2024
15e2f63
More fixes for mining on non-gnosis chain
area Apr 2, 2024
d94674f
MINING_CHAIN_ID needs to be set for cross-chaintests
area Apr 3, 2024
4db80e1
Fix setup for test:reputation:2:anotherChain
area Apr 4, 2024
6839f17
Temporary workaround for upgrade tests
area Apr 4, 2024
48edf00
Merge branch 'feat/multichain-reputation-rebased' into feat/multichai…
area Apr 4, 2024
0fc2bea
Temporary workaround for upgrade tests
area Apr 4, 2024
d79fe09
Coerce tests after merge
area Apr 4, 2024
3015493
Fix spotty client-core-functionality tests
area Mar 25, 2024
97428fc
Log error in setupEtherRouter to help in future
area Mar 26, 2024
1caf6e5
Can't run smoke tests with hardhat yet
area Apr 5, 2024
4036572
Merge branch 'maint/hardhat-2' into feat/multichain-reputation-rebase…
area Apr 5, 2024
15c78f3
Changes post review
area Apr 6, 2024
d81142a
Changes post review II
area Apr 7, 2024
92f496c
Try to pin down spotty miner test
area Apr 8, 2024
1969840
Changes post review III
area Apr 8, 2024
0d9436c
Disable incorrect-equality on slither
area Apr 9, 2024
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
37 changes: 37 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,41 @@ jobs:
paths:
- coverage-cross-chain-foreign
- coverage-cross-chain-home
reputation-test-non-gnosis:
<<: *job_common
steps:
- checkout
- <<: *step_restore_cache
- setup_remote_docker:
version: docker23
- <<: *step_pull_solc_docker
- <<: *step_setup_global_packages
- run:
name: "Install lsof"
command: |
sudo apt-get update
sudo apt-get install lsof
- run:
name: "Running reputation system unit tests"
command: npm run test:reputation:1:anotherChain
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:anotherChain
environment:
NODE_OPTIONS: --max-old-space-size=6144
# - run:
# name: "Running storage consistency smoke tests"
# command: npm run test:contracts:smoke


check-coverage:
<<: *job_common
steps:
Expand Down Expand Up @@ -448,6 +483,8 @@ workflows:
context: dockerhub-credentials
- coverage-test-bridging:
context: dockerhub-credentials
- reputation-test-non-gnosis:
context: dockerhub-credentials
- check-coverage:
context: dockerhub-credentials
requires:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
path = lib/safe-contracts
url = https://github.com/safe-global/safe-contracts
ignore = dirty
[submodule "lib/wormhole"]
path = lib/wormhole
url = https://github.com/wormhole-foundation/wormhole.git
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
21 changes: 20 additions & 1 deletion .solcover.crosschain.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
const config = require("./.solcover.js")
const ethers = require("ethers");

config.istanbulFolder = `./coverage-cross-chain-${process.env.HARDHAT_FOREIGN ? "foreign" : "home"}`
const { FORKED_XDAI_CHAINID } = require("./helpers/constants");

config.istanbulFolder = `./coverage-cross-chain-${process.env.HARDHAT_FOREIGN === "true" ? "foreign" : "home"}`
console.log(`Coverage folder: ${config.istanbulFolder}`)

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.HARDHAT_FOREIGN)){
chainId = FORKED_XDAI_CHAINID + 1;
} else {
chainId = FORKED_XDAI_CHAINID;
}

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

module.exports = config
5 changes: 4 additions & 1 deletion .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,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 @@ -50,5 +53,5 @@ module.exports = {
},
onCompileComplete: provisionTokenContracts,
istanbulFolder: "./coverage-contracts",
modifierWhitelist: ["always"],
modifierWhitelist: ["always", "onlyMiningChain", "onlyNotMiningChain"],
}
52 changes: 52 additions & 0 deletions contracts/bridging/IColonyBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
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.25;
pragma experimental "ABIEncoderV2";

interface IColonyBridge {
/// @notice Function that checks whether a chain with the supplied evmChainId is supported
/// @param _evmChainId The chain id to check
/// @return bool Whether the chain is supported
function supportedEvmChainId(uint256 _evmChainId) external view returns (bool);

/// @notice Function to set the colony network address that the bridge will interact with
/// @param _colonyNetwork The address of the colony network
function setColonyNetworkAddress(address _colonyNetwork) external;

/// @notice Function to get the colony network address that the bridge is interacting with
/// @return address The address of the colony network
function getColonyNetworkAddress() external view returns (address);

/// @notice Function to set the address of the instance of this contract on other chains, that
/// this contract will expect to receive messages from
/// @param _evmChainId The chain id to set the address for
/// @param _colonyBridge The address of the colony bridge contract on the other chain
function setColonyBridgeAddress(uint256 _evmChainId, address _colonyBridge) external;

/// @notice Function to get the address of the instance of this contract on other chains
/// @param evmChainId The chain id to get the address for
function getColonyBridgeAddress(uint256 evmChainId) external view returns (address);

/// @notice Function to send a message to the colony bridge on another chain
/// @param evmChainId The chain id to send the message to
/// @param payload The message payload
/// @return bool Whether the message was sent successfully (to the best of the contract's knowledge,
/// in terms of the underlying bridge implementation)
function sendMessage(uint256 evmChainId, bytes memory payload) external returns (bool);
}
135 changes: 135 additions & 0 deletions contracts/bridging/WormholeBridgeForColony.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
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.25;

import { IWormhole } from "../../lib/wormhole/ethereum/contracts/interfaces/IWormhole.sol";
import { IColonyNetwork } from "../colonyNetwork/IColonyNetwork.sol";
import { IColonyBridge } from "./IColonyBridge.sol";
import { CallWithGuards } from "../common/CallWithGuards.sol";
import { DSAuth } from "../../lib/dappsys/auth.sol";

contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards {
address colonyNetwork;
IWormhole public wormhole;

// ChainId => colonyBridge
mapping(uint256 => address) colonyBridges;

// Maps evm chain id to wormhole chain id
mapping(uint256 => uint16) public evmChainIdToWormholeChainId;

modifier onlyColonyNetwork() {
require(msg.sender == colonyNetwork, "wormhole-bridge-only-colony-network");
_;
}

function setChainIdMapping(
uint256[] calldata evmChainIds,
uint16[] calldata wormholeChainIds
) public auth {
require(
evmChainIds.length == wormholeChainIds.length,
"colony-bridge-chainid-mapping-length-mismatch"
);
for (uint256 i = 0; i < evmChainIds.length; i++) {
evmChainIdToWormholeChainId[evmChainIds[i]] = wormholeChainIds[i];
}
}

function supportedEvmChainId(uint256 _evmChainId) public view returns (bool) {
return evmChainIdToWormholeChainId[_evmChainId] != 0;
}

function setWormholeAddress(address _wormhole) public auth {
wormhole = IWormhole(_wormhole);
}

function setColonyNetworkAddress(address _colonyNetwork) public auth {
colonyNetwork = _colonyNetwork;
}

function getColonyNetworkAddress() public view returns (address) {
return colonyNetwork;
}

function setColonyBridgeAddress(uint256 _evmChainId, address _bridgeAddress) public auth {
require(_evmChainId <= type(uint128).max, "colony-bridge-chainid-too-large");
uint16 requestedWormholeChainId = evmChainIdToWormholeChainId[_evmChainId];
colonyBridges[requestedWormholeChainId] = _bridgeAddress;
}

function getColonyBridgeAddress(uint256 evmChainId) public view returns (address) {
uint16 requestedWormholeChainId = evmChainIdToWormholeChainId[evmChainId];
return colonyBridges[requestedWormholeChainId];
}

function wormholeAddressToEVMAddress(
bytes32 _wormholeFormatAddress
) public pure returns (address) {
return address(uint160(uint256(_wormholeFormatAddress)));
}

function receiveMessage(bytes memory _vaa) public {
// VAAs are the primitives used on wormhole (Verified Action Approvals)
// See https://docs.wormhole.com/wormhole/explore-wormhole/vaa for more details
// Note that the documentation sometimes also calls them VMs (as does IWormhole)
// I believe VM stands for 'Verified Message'
(IWormhole.VM memory wormholeMessage, bool valid, string memory reason) = wormhole
.parseAndVerifyVM(_vaa);

// Check the vaa was valid
require(valid, reason);

// Check came from a known colony bridge
require(
wormholeAddressToEVMAddress(wormholeMessage.emitterAddress) ==
colonyBridges[wormholeMessage.emitterChainId],
"colony-bridge-bridged-tx-only-from-colony-bridge"
);

// We ignore sequence numbers - bridging out of order is okay, because we have our own way of handling that

// Make the call requested to the colony network
(bool success, bytes memory returndata) = callWithGuards(
colonyNetwork,
wormholeMessage.payload
);

// Note that this is not a require because returndata might not be a string, and if we try
// to decode it we'll get a revert.
if (!success) {
revert(abi.decode(returndata, (string)));
}
}

function sendMessage(
uint256 _evmChainId,
bytes memory _payload
) public onlyColonyNetwork returns (bool) {
require(supportedEvmChainId(_evmChainId), "colony-bridge-not-known-chain");
// This returns a sequence, but we don't care about it
// The first sequence ID is, I believe 0, so all return values are potentially valid
// slither-disable-next-line unused-return
try wormhole.publishMessage(0, _payload, 0) {
return true;
} catch {
return false;
}
}
}
39 changes: 22 additions & 17 deletions contracts/colony/Colony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,6 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
emit TokensMinted(msgSender(), _guy, _wad);
}

function mintTokensForColonyNetwork(uint _wad) public stoppable {
// Only the colony Network can call this function
require(msgSender() == colonyNetworkAddress, "colony-access-denied-only-network-allowed");
// Function only valid on the Meta Colony
require(
address(this) == IColonyNetwork(colonyNetworkAddress).getMetaColony(),
"colony-access-denied-only-meta-colony-allowed"
);
// Not callable on Xdai
require(!isXdai(), "colony-network-forbidden-on-xdai");

ERC20Extended(token).mint(_wad);
assert(ERC20Extended(token).transfer(colonyNetworkAddress, _wad));

emit TokensMinted(msgSender(), colonyNetworkAddress, _wad);
}

function registerColonyLabel(
string memory colonyName,
string memory orbitdb
Expand Down Expand Up @@ -210,6 +193,22 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
IColonyNetwork(colonyNetworkAddress).addColonyVersion(_version, _resolver);
}

function setColonyBridgeAddress(address _bridgeAddress) public stoppable auth {
IColonyNetwork(colonyNetworkAddress).setColonyBridgeAddress(_bridgeAddress);
}

function initialiseReputationMining(
uint256 miningChainId,
bytes32 newHash,
uint256 newNLeaves
) public stoppable auth {
IColonyNetwork(colonyNetworkAddress).initialiseReputationMining(
miningChainId,
newHash,
newNLeaves
);
}

function addExtensionToNetwork(bytes32 _extensionId, address _resolver) public stoppable auth {
IColonyNetwork(colonyNetworkAddress).addExtensionToNetwork(_extensionId, _resolver);
}
Expand Down Expand Up @@ -312,6 +311,12 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP

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

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

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

function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) {
Expand Down
2 changes: 2 additions & 0 deletions contracts/colony/ColonyAuthority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ contract ColonyAuthority is CommonAuthority {
// Added in colony v15 (hazel-lwss-2)
addRoleCapability(ARBITRATION_ROLE, "cancelExpenditureViaArbitration(uint256,uint256,uint256)");
addRoleCapability(ARBITRATION_ROLE, "finalizeExpenditureViaArbitration(uint256,uint256,uint256)");
addRoleCapability(ROOT_ROLE, "setColonyBridgeAddress(address)");
addRoleCapability(ROOT_ROLE, "initialiseReputationMining(uint256,bytes32,uint256)");
}

function addRoleCapability(uint8 role, bytes memory sig) private {
Expand Down
22 changes: 17 additions & 5 deletions contracts/colony/IMetaColony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ pragma experimental "ABIEncoderV2";
import { IColony } from "./IColony.sol";

interface IMetaColony is IColony {
/// @notice Mints CLNY in the Meta Colony and transfers them to the colony network.
/// Only allowed to be called on the Meta Colony by the colony network.
/// @param _wad Amount to mint and transfer to the colony network
function mintTokensForColonyNetwork(uint256 _wad) external;

/// @notice Set the Colony Network fee inverse amount.
/// @dev Calls `IColonyNetwork.setFeeInverse`.
/// @param _feeInverse Nonzero amount for the fee inverse
Expand Down Expand Up @@ -54,4 +49,21 @@ interface IMetaColony is IColony {
/// @param _extensionId keccak256 hash of the extension name, used as an indentifier
/// @param _resolver The deployed resolver containing the extension contract logic
function addExtensionToNetwork(bytes32 _extensionId, address _resolver) external;

// @notice Called to set the address of the colony bridge contract
/// @param _bridgeAddress The address of the bridge
function setColonyBridgeAddress(address _bridgeAddress) external;

/// @notice Creates initial inactive reputation mining cycle.
/// @dev Only callable from metacolony
/// @param miningChainId The chainId of the chain the mining cycle is being created on
/// Can either be this chain or another chain, and the function will behave differently depending
/// on which is the case.
/// @param newHash The root hash of the reputation state tree
/// @param newNLeaves The number of leaves in the state tree
function initialiseReputationMining(
uint256 miningChainId,
bytes32 newHash,
uint256 newNLeaves
) external;
}
Loading