Skip to content

Commit 10860dc

Browse files
committed
WIP: Wormhole restructuring
1 parent b8ba1e7 commit 10860dc

27 files changed

+1127
-649
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
path = lib/safe-contracts
1111
url = https://github.com/safe-global/safe-contracts
1212
ignore = dirty
13+
[submodule "lib/wormhole"]
14+
path = lib/wormhole
15+
url = https://github.com/wormhole-foundation/wormhole.git
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
/*
3+
This file is part of The Colony Network.
4+
5+
The Colony Network is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
The Colony Network is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with The Colony Network. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
pragma solidity 0.8.23;
20+
pragma experimental "ABIEncoderV2";
21+
22+
interface IColonyBridge {
23+
function supportedEvmChainId(uint256 _evmChainId) external pure returns (bool);
24+
25+
function setColonyNetworkAddress(address _colonyNetwork) external;
26+
27+
function getColonyNetworkAddress() external view returns (address);
28+
29+
function setColonyBridgeAddress(uint256 evmChainId, address _colonyNetwork) external;
30+
31+
function getColonyBridgeAddress(uint256 evmChainId) external view returns (address);
32+
33+
function sendMessage(uint256 evmChainId, bytes memory payload) external returns (bool);
34+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
/*
3+
This file is part of The Colony Network.
4+
5+
The Colony Network is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
The Colony Network is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with The Colony Network. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
pragma solidity 0.8.23;
20+
21+
import { IWormhole } from "../../lib/wormhole/ethereum/contracts/interfaces/IWormhole.sol";
22+
import { IColonyNetwork } from "../colonyNetwork/IColonyNetwork.sol";
23+
import { IColonyBridge } from "./IColonyBridge.sol";
24+
import { DSAuth } from "../../lib/dappsys/auth.sol";
25+
26+
contract WormholeBridgeForColony is DSAuth, IColonyBridge {
27+
address colonyNetwork;
28+
// ChainId => colonyBridge
29+
mapping(uint256 => address) colonyBridges;
30+
31+
IWormhole public wormhole;
32+
33+
function evmChainIdToWormholeChainId(uint256 _evmChainId) public pure returns (uint16) {
34+
if (_evmChainId == 1) {
35+
return 2;
36+
} else if (_evmChainId == 137 || _evmChainId == 80001) {
37+
return 5; // Polygon
38+
} else if (_evmChainId == 10 || _evmChainId == 420) {
39+
return 24; // Optimism
40+
} else if (_evmChainId == 100) {
41+
return 25; // xDai
42+
} else {
43+
return uint16(_evmChainId % 265669);
44+
}
45+
}
46+
47+
function supportedEvmChainId(uint256 _evmChainId) public pure returns (bool) {
48+
return
49+
_evmChainId == 1 ||
50+
_evmChainId == 137 ||
51+
_evmChainId == 80001 ||
52+
_evmChainId == 10 ||
53+
_evmChainId == 420 ||
54+
_evmChainId == 100 ||
55+
_evmChainId == 265669100 ||
56+
_evmChainId == 265669101;
57+
}
58+
59+
function setWormholeAddress(address _wormhole) public auth {
60+
wormhole = IWormhole(_wormhole);
61+
}
62+
63+
function setColonyNetworkAddress(address _colonyNetwork) public auth {
64+
colonyNetwork = _colonyNetwork;
65+
}
66+
67+
function getColonyNetworkAddress() public view returns (address) {
68+
return colonyNetwork;
69+
}
70+
71+
function setColonyBridgeAddress(uint256 evmChainId, address _colonyNetwork) public auth {
72+
require(evmChainId <= type(uint128).max, "colony-bridge-chainid-too-large");
73+
uint16 requestedWormholeChainId = evmChainIdToWormholeChainId(evmChainId);
74+
colonyBridges[requestedWormholeChainId] = _colonyNetwork;
75+
}
76+
77+
function getColonyBridgeAddress(uint256 evmChainId) public view returns (address) {
78+
uint16 requestedWormholeChainId = evmChainIdToWormholeChainId(evmChainId);
79+
return colonyBridges[requestedWormholeChainId];
80+
}
81+
82+
function wormholeFormatAddressToEthereumAddress(
83+
bytes32 _wormholeFormatAddress
84+
) public pure returns (address) {
85+
return address(uint160(uint256(_wormholeFormatAddress)));
86+
}
87+
88+
function receiveMessage(bytes memory _vaa) public {
89+
(IWormhole.VM memory wormholeMessage, bool valid, string memory reason) = wormhole
90+
.parseAndVerifyVM(_vaa);
91+
92+
// Check the vaa was valid
93+
require(valid, reason);
94+
95+
// Check came from a known colony bridge
96+
require(
97+
wormholeFormatAddressToEthereumAddress(wormholeMessage.emitterAddress) ==
98+
colonyBridges[wormholeMessage.emitterChainId],
99+
"colony-bridge-bridged-tx-only-from-colony-bridge"
100+
);
101+
102+
// We ignore sequence numbers - bridging out of order is okay, because we have our own way of handling that
103+
104+
// Do the thing
105+
106+
(bool success, bytes memory returndata) = address(colonyNetwork).call(wormholeMessage.payload);
107+
if (!success) {
108+
// Stolen shamelessly from
109+
// https://ethereum.stackexchange.com/questions/83528/how-can-i-get-the-revert-reason-of-a-call-in-solidity-so-that-i-can-use-it-in-th
110+
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
111+
if (returndata.length >= 68) {
112+
assembly {
113+
// Slice the sighash.
114+
returndata := add(returndata, 0x04)
115+
}
116+
require(false, abi.decode(returndata, (string))); // All that remains is the revert string
117+
}
118+
require(false, "require-execute-call-reverted-with-no-error");
119+
}
120+
121+
require(success, "wormhole-bridge-receive-message-failed");
122+
}
123+
124+
function sendMessage(
125+
uint256 evmChainId,
126+
bytes memory payload
127+
) public onlyColonyNetwork returns (bool) {
128+
if (!supportedEvmChainId(evmChainId)) {
129+
revert("colony-bridge-not-known-chain");
130+
}
131+
try wormhole.publishMessage(0, payload, 0) {
132+
return true;
133+
} catch {
134+
return false;
135+
}
136+
}
137+
138+
modifier onlyColonyNetwork() {
139+
require(msg.sender == colonyNetwork, "wormhole-bridge-only-colony-network");
140+
_;
141+
}
142+
}

contracts/colony/Colony.sol

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -210,32 +210,36 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
210210
IColonyNetwork(colonyNetworkAddress).addColonyVersion(_version, _resolver);
211211
}
212212

213-
function setBridgeData(
214-
address _bridgeAddress,
215-
uint256 _chainId,
216-
uint256 _gas,
217-
bytes4 _msgSenderSig,
218-
address _correspondingNetwork,
219-
bytes memory _updateLogBefore,
220-
bytes memory _updateLogAfter,
221-
bytes memory _skillCreationBefore,
222-
bytes memory _skillCreationAfter,
223-
bytes memory _setReputationRootHashBefore,
224-
bytes memory _setReputationRootHashAfter
225-
) external stoppable auth {
226-
IColonyNetwork(colonyNetworkAddress).setBridgeData(
227-
_bridgeAddress,
228-
_chainId,
229-
_gas,
230-
_msgSenderSig,
231-
_correspondingNetwork,
232-
_updateLogBefore,
233-
_updateLogAfter,
234-
_skillCreationBefore,
235-
_skillCreationAfter,
236-
_setReputationRootHashBefore,
237-
_setReputationRootHashAfter
238-
);
213+
// function setBridgeData(
214+
// address _bridgeAddress,
215+
// uint256 _chainId,
216+
// uint256 _gas,
217+
// bytes4 _msgSenderSig,
218+
// address _correspondingNetwork,
219+
// bytes memory _updateLogBefore,
220+
// bytes memory _updateLogAfter,
221+
// bytes memory _skillCreationBefore,
222+
// bytes memory _skillCreationAfter,
223+
// bytes memory _setReputationRootHashBefore,
224+
// bytes memory _setReputationRootHashAfter
225+
// ) external stoppable auth {
226+
// IColonyNetwork(colonyNetworkAddress).setBridgeData(
227+
// _bridgeAddress,
228+
// _chainId,
229+
// _gas,
230+
// _msgSenderSig,
231+
// _correspondingNetwork,
232+
// _updateLogBefore,
233+
// _updateLogAfter,
234+
// _skillCreationBefore,
235+
// _skillCreationAfter,
236+
// _setReputationRootHashBefore,
237+
// _setReputationRootHashAfter
238+
// );
239+
// }
240+
241+
function setColonyBridgeAddress(address _bridgeAddress) public stoppable auth {
242+
IColonyNetwork(colonyNetworkAddress).setColonyBridgeAddress(_bridgeAddress);
239243
}
240244

241245
function addExtensionToNetwork(bytes32 _extensionId, address _resolver) public stoppable auth {
@@ -340,11 +344,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP
340344
ColonyAuthority colonyAuthority = ColonyAuthority(address(authority));
341345
bytes4 sig;
342346

343-
sig = bytes4(
344-
keccak256(
345-
"setBridgeData(address,uint256,uint256,bytes4,address,bytes,bytes,bytes,bytes,bytes,bytes)"
346-
)
347-
);
347+
sig = bytes4(keccak256("setColonyBridgeAddress(address)"));
348348
colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true);
349349
}
350350

contracts/colony/ColonyAuthority.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ contract ColonyAuthority is CommonAuthority {
130130
addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,address,uint256)");
131131

132132
// Added in colony vxxx
133-
addRoleCapability(ROOT_ROLE, "setBridgeData(address,uint256,uint256,bytes4,address,bytes,bytes,bytes,bytes,bytes,bytes)");
133+
addRoleCapability(ROOT_ROLE, "setColonyBridgeAddress(address)");
134134
}
135135

136136
function addRoleCapability(uint8 role, bytes memory sig) private {

contracts/colony/IMetaColony.sol

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,33 @@ interface IMetaColony is IColony {
5555
/// @param _resolver The deployed resolver containing the extension contract logic
5656
function addExtensionToNetwork(bytes32 _extensionId, address _resolver) external;
5757

58-
/// @notice Called to set the details about bridge _bridgeAddress
58+
// /// @notice Called to set the details about bridge _bridgeAddress
59+
// /// @param _bridgeAddress The address of the bridge
60+
// /// @param _chainId The chainId of the corresponding network
61+
// /// @param _gas How much gas to use for a bridged transaction
62+
// /// @param _msgSenderFunctionSig The function signature of the function to call on the bridge to get the msgSender
63+
// /// @param _correspondingNetwork The address of the corresponding colony network contract on the other network
64+
// /// @param _updateLogBefore The tx data before the dynamic part of the tx to bridge to the update log
65+
// /// @param _updateLogAfter The tx data after the dynamic part of the tx to bridge to the update log
66+
// /// @param _skillCreationBefore The tx data before the dynamic part of the tx to brdige skill creation
67+
// /// @param _skillCreationAfter The tx data after the dynamic part of the tx to brdige skill creation
68+
// /// @param _setReputationRootHashBefore The tx data before the dynamic part of the tx to bridge a new reputation root hash
69+
// /// @param _setReputationRootHashAfter The tx data after the dynamic part of the tx to bridge a new reputation root hash
70+
// function setBridgeData(
71+
// address _bridgeAddress,
72+
// uint256 _chainId,
73+
// uint256 _gas,
74+
// bytes4 _msgSenderFunctionSig,
75+
// address _correspondingNetwork,
76+
// bytes memory _updateLogBefore,
77+
// bytes memory _updateLogAfter,
78+
// bytes memory _skillCreationBefore,
79+
// bytes memory _skillCreationAfter,
80+
// bytes memory _setReputationRootHashBefore,
81+
// bytes memory _setReputationRootHashAfter
82+
// ) external;
83+
84+
// @notice Called to set the address of the colony bridge contract
5985
/// @param _bridgeAddress The address of the bridge
60-
/// @param _chainId The chainId of the corresponding network
61-
/// @param _gas How much gas to use for a bridged transaction
62-
/// @param _msgSenderFunctionSig The function signature of the function to call on the bridge to get the msgSender
63-
/// @param _correspondingNetwork The address of the corresponding colony network contract on the other network
64-
/// @param _updateLogBefore The tx data before the dynamic part of the tx to bridge to the update log
65-
/// @param _updateLogAfter The tx data after the dynamic part of the tx to bridge to the update log
66-
/// @param _skillCreationBefore The tx data before the dynamic part of the tx to brdige skill creation
67-
/// @param _skillCreationAfter The tx data after the dynamic part of the tx to brdige skill creation
68-
/// @param _setReputationRootHashBefore The tx data before the dynamic part of the tx to bridge a new reputation root hash
69-
/// @param _setReputationRootHashAfter The tx data after the dynamic part of the tx to bridge a new reputation root hash
70-
function setBridgeData(
71-
address _bridgeAddress,
72-
uint256 _chainId,
73-
uint256 _gas,
74-
bytes4 _msgSenderFunctionSig,
75-
address _correspondingNetwork,
76-
bytes memory _updateLogBefore,
77-
bytes memory _updateLogAfter,
78-
bytes memory _skillCreationBefore,
79-
bytes memory _skillCreationAfter,
80-
bytes memory _setReputationRootHashBefore,
81-
bytes memory _setReputationRootHashAfter
82-
) external;
86+
function setColonyBridgeAddress(address _bridgeAddress) external;
8387
}

contracts/colonyNetwork/ColonyNetworkDataTypes.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ interface ColonyNetworkDataTypes {
149149

150150
/// @notice Event logged when the colony network has data about a bridge contract set.
151151
/// @param bridgeAddress The address of the bridge contract that will be interacted with
152-
event BridgeDataSet(address bridgeAddress);
152+
event BridgeSet(address bridgeAddress);
153153

154154
/// @notice Event logged when bridging of a skill creation did not succeed.
155155
/// @param skillId The skillId that failed to bridge

contracts/colonyNetwork/ColonyNetworkMining.sol

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { IReputationMiningCycle } from "./../reputationMiningCycle/IReputationMi
2626
import { ITokenLocking } from "./../tokenLocking/ITokenLocking.sol";
2727
import { ColonyNetworkStorage } from "./ColonyNetworkStorage.sol";
2828
import { IMetaColony } from "./../colony/IMetaColony.sol";
29+
import { IColonyBridge } from "./../bridging/IColonyBridge.sol";
2930

3031
contract ColonyNetworkMining is ColonyNetworkStorage {
3132
// TODO: Can we handle a dispute regarding the very first hash that should be set?
@@ -98,37 +99,32 @@ contract ColonyNetworkMining is ColonyNetworkStorage {
9899
bytes32 newHash,
99100
uint256 newNLeaves,
100101
uint256 _nonce
101-
) public onlyNotMiningChain checkBridgedSender stoppable {
102+
) public onlyNotMiningChain onlyColonyBridge stoppable {
102103
require(
103-
_nonce >= bridgeCurrentRootHashNonces[bridgeData[msgSender()].chainId],
104+
_nonce >= bridgeCurrentRootHashNonces[block.chainid],
104105
"colony-mining-bridge-invalid-nonce"
105106
);
106-
bridgeCurrentRootHashNonces[bridgeData[msgSender()].chainId] = _nonce;
107+
bridgeCurrentRootHashNonces[block.chainid] = _nonce;
107108
reputationRootHash = newHash;
108109
reputationRootHashNLeaves = newNLeaves;
109110

110111
emit ReputationRootHashSet(newHash, newNLeaves, newAddressArray(), 0);
111112
}
112113

113-
function bridgeCurrentRootHash(address _bridgeAddress) public onlyMiningChain stoppable {
114-
uint256 chainId = bridgeData[_bridgeAddress].chainId;
115-
require(chainId != 0, "colony-network-not-known-bridge");
116-
117-
bridgeCurrentRootHashNonces[chainId] += 1;
118-
119-
bytes memory payload = abi.encodePacked(
120-
bridgeData[_bridgeAddress].setReputationRootHashBefore,
121-
abi.encodeWithSignature(
122-
"setReputationRootHashFromBridge(bytes32,uint256,uint256)",
123-
reputationRootHash,
124-
reputationRootHashNLeaves,
125-
bridgeCurrentRootHashNonces[chainId]
126-
),
127-
bridgeData[_bridgeAddress].setReputationRootHashAfter
114+
function bridgeCurrentRootHash(uint256 _chainId) public onlyMiningChain stoppable {
115+
require(colonyBridgeAddress != address(0x0), "colony-network-bridge-not-set");
116+
117+
bridgeCurrentRootHashNonces[_chainId] += 1;
118+
119+
bytes memory payload = abi.encodeWithSignature(
120+
"setReputationRootHashFromBridge(bytes32,uint256,uint256)",
121+
reputationRootHash,
122+
reputationRootHashNLeaves,
123+
bridgeCurrentRootHashNonces[_chainId]
128124
);
129125

130126
// slither-disable-next-line unchecked-lowlevel
131-
(bool success, ) = _bridgeAddress.call(payload);
127+
bool success = IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, payload);
132128
// We require success so estimation calls can tell us if bridging is going to work
133129
require(success, "colony-mining-bridge-call-failed");
134130
}

0 commit comments

Comments
 (0)