Skip to content

Commit 5aed2f0

Browse files
committed
Additional multichain tests
1 parent 9fb4863 commit 5aed2f0

File tree

7 files changed

+120
-23
lines changed

7 files changed

+120
-23
lines changed

.solcover.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ module.exports = {
5959
},
6060
onCompileComplete: provisionTokenContracts,
6161
istanbulFolder: "./coverage-contracts",
62-
modifierWhitelist: ["always"],
62+
modifierWhitelist: ["always", "onlyMiningChain", "onlyNotMiningChain"],
6363
}

contracts/colonyNetwork/ColonyNetwork.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,9 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall
258258
require(bridge.chainId != 0, "colony-network-not-known-bridge");
259259

260260
// Require that specified skill is next
261+
// Note this also implicitly checks that the chainId prefix of the skill is correct
261262
require(networkSkillCounts[bridge.chainId] + 1 == _skillId, "colony-network-not-next-bridged-skill");
262263

263-
// TODO: Require skill from right bridge
264-
265264
uint256 parentSkillId = pendingSkillAdditions[bridge.chainId][_skillId];
266265
require(parentSkillId != 0, "colony-network-no-such-bridged-skill");
267266
if (parentSkillId > bridge.chainId << 128){

contracts/colonyNetwork/ColonyNetworkStorage.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ contract ColonyNetworkStorage is ColonyNetworkDataTypes, DSMath, CommonStorage,
152152

153153
modifier skillExists(uint256 skillId) {
154154
require(skillCount >= skillId, "colony-invalid-skill-id");
155-
155+
if (!isMiningChain()){
156+
require((skillId >> 128) == getChainId() , "colony-invalid-skill-id");
157+
}
156158
_;
157159
}
158160
}

contracts/common/MultiChain.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ contract MultiChain {
5757
uint256 constant MINING_CHAIN_ID = 100;
5858

5959
modifier onlyMiningChain() {
60-
require(isMiningChain(), "colony-reputation-mining-only-valid-on-mining-chain");
60+
require(isMiningChain(), "colony-only-valid-on-mining-chain");
6161
_;
6262
}
6363

6464
modifier onlyNotMiningChain() {
65-
require(!isMiningChain(), "colony-reputation-mining-only-valid-not-on-mining-chain");
65+
require(!isMiningChain(), "colony-only-valid-not-on-mining-chain");
6666
_;
6767
}
6868

test-chainid/chainid-dependent-behaviour.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,19 @@ contract("Contract Storage", (accounts) => {
226226
await expectEvent(tx, "Burn(address indexed,uint256)", [tokenAuction.address, receivedTotal]);
227227
}
228228
});
229+
230+
it("Global skills can only be created on the mining chain", async () => {
231+
if (chainId === XDAI || chainId === FORKED_XDAI) {
232+
await metaColony.addGlobalSkill();
233+
} else {
234+
await checkErrorRevert(metaColony.addGlobalSkill(), "colony-only-valid-on-mining-chain");
235+
}
236+
});
237+
238+
it("Reputation mining cannot be initialised on non-mining chain", async () => {
239+
if (chainId !== XDAI && chainId !== FORKED_XDAI) {
240+
await checkErrorRevert(colonyNetwork.initialiseReputationMining(), "colony-only-valid-on-mining-chain");
241+
}
242+
});
229243
});
230244
});

test/contracts-network/colony-network-recovery.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ contract("Colony Network Recovery", (accounts) => {
192192
await checkErrorRevert(colonyNetwork.setPayoutWhitelist(ADDRESS_ZERO, true), "colony-in-recovery-mode");
193193
await checkErrorRevert(colonyNetwork.claimMiningReward(ADDRESS_ZERO), "colony-in-recovery-mode");
194194
await checkErrorRevert(colonyNetwork.startTokenAuction(ADDRESS_ZERO), "colony-in-recovery-mode");
195+
await checkErrorRevert(colonyNetwork.bridgeSkillIfNotMiningChain(1), "colony-in-recovery-mode");
196+
await checkErrorRevert(colonyNetwork.appendReputationUpdateLogFromBridge(ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0), "colony-in-recovery-mode");
197+
await checkErrorRevert(colonyNetwork.bridgePendingReputationUpdate(ADDRESS_ZERO, 0), "colony-in-recovery-mode");
198+
await checkErrorRevert(colonyNetwork.addBridgedReputationUpdate(0, ADDRESS_ZERO), "colony-in-recovery-mode");
195199

196200
await colonyNetwork.approveExitRecovery();
197201
await colonyNetwork.exitRecoveryMode();

test/cross-chain/cross-chain.js

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,64 @@ contract("Cross-chain", (accounts) => {
268268
await bridgeMonitor.close();
269269
});
270270

271+
describe("administrating cross-network bridges", async () => {
272+
it("bridge data can be queried", async () => {
273+
const bridgeData = await homeColonyNetwork.getBridgeData(homeBridge.address);
274+
275+
expect(bridgeData.gas.toNumber()).to.equal(1000000);
276+
expect(ethers.BigNumber.from(bridgeData.chainId).toHexString()).to.equal(ethers.BigNumber.from(foreignChainId).toHexString());
277+
expect(bridgeData.setReputationRootHashBefore.toLowerCase()).to.equal(
278+
`0xdc8601b3000000000000000000000000${foreignColonyNetwork.address.slice(
279+
2
280+
// eslint-disable-next-line max-len
281+
)}000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000044`.toLowerCase()
282+
);
283+
expect(bridgeData.setReputationRootHashAfter).to.equal(`0x00000000000000000000000000000000000000000000000000000000`);
284+
});
285+
286+
it("mining bridge address is queryable", async () => {
287+
let bridgeAddress = await homeColonyNetwork.getMiningBridgeAddress();
288+
expect(bridgeAddress).to.equal(ADDRESS_ZERO);
289+
290+
bridgeAddress = await foreignColonyNetwork.getMiningBridgeAddress();
291+
expect(bridgeAddress).to.equal(foreignBridge.address);
292+
});
293+
294+
it("setBridgeData can only be called by the metacolony", async () => {
295+
const tx = await foreignColonyNetwork.setBridgeData(ADDRESS_ZERO, "0x00", "0x00", 0, 1, "0x00", "0x00", "0x00", "0x00", { gasLimit: 1000000 });
296+
await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-meta-colony");
297+
});
298+
299+
it("setBridgeData can only set the mining chain bridge on a not-mining chain", async () => {
300+
const tx = await foreignMetacolony.setBridgeData(ADDRESS_ZERO, "0x00", "0x00", 0, 1, "0x00", "0x00", "0x00", "0x00", { gasLimit: 1000000 });
301+
await checkErrorRevertEthers(tx.wait(), "colony-network-can-only-set-mining-chain-bridge");
302+
});
303+
304+
it("updating the bridge for a chain does not reset the bridged skill count", async () => {
305+
const countBefore = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId);
306+
const tx = await homeMetacolony.setBridgeData(
307+
homeBridge.address, // bridge address
308+
"0x", // log before
309+
"0x", // log after
310+
1000000, // gas
311+
foreignChainId, // chainid
312+
`0x`, // skill before
313+
"0x", // skill after
314+
`0xdc8601b3000000000000000000000000${foreignColonyNetwork.address.slice(
315+
2
316+
// eslint-disable-next-line max-len
317+
)}000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000044`,
318+
"0x00000000000000000000000000000000000000000000000000000000" // root hash after
319+
);
320+
await tx.wait();
321+
322+
const countAfter = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId);
323+
expect(countAfter).to.not.equal(0);
324+
console.log(countAfter);
325+
expect(countAfter.sub(countBefore).toNumber()).to.equal(0);
326+
});
327+
});
328+
271329
describe("when controlling a gnosis wallet on another chain", async () => {
272330
it("can send tokens out of the gnosis safe", async () => {
273331
// Create token contract on foreign chain
@@ -343,6 +401,22 @@ contract("Cross-chain", (accounts) => {
343401
expect(beforeCount.add(1).toHexString()).to.equal(afterCount.toHexString());
344402
});
345403

404+
it("addSkillFromBridge cannot be called by a non-bridge address", async () => {
405+
const tx = await homeColonyNetwork.addSkillFromBridge(0, 0, { gasLimit: 1000000 });
406+
await checkErrorRevertEthers(tx.wait(), "colony-network-not-known-bridge");
407+
});
408+
409+
it("addBridgedPendingSkill cannot be called referring to a bridge that doesn't exist", async () => {
410+
const tx = await homeColonyNetwork.addBridgedPendingSkill(ADDRESS_ZERO, 1, { gasLimit: 1000000 });
411+
await checkErrorRevertEthers(tx.wait(), "colony-network-not-known-bridge");
412+
});
413+
414+
it("addBridgedPendingSkill doesn't create skills that haven't been bridged", async () => {
415+
const homeSkillCount = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId);
416+
const tx = await homeColonyNetwork.addBridgedPendingSkill(homeBridge.address, homeSkillCount.add(1), { gasLimit: 1000000 });
417+
await checkErrorRevertEthers(tx.wait(), "colony-network-no-such-bridged-skill");
418+
});
419+
346420
it("if a skill is bridged out-of-order, it's added to the pending mapping", async () => {
347421
bridgeMonitor.skipCount = 1;
348422
// Create a skill on the foreign chain
@@ -452,23 +526,21 @@ contract("Cross-chain", (accounts) => {
452526
expect(homeSkillCount.toHexString()).to.equal(foreignSkillCount.toHexString());
453527
});
454528

455-
// it.only("if bridge isn't a contract, skill creation fails", async () => {
456-
// const bridgeData = await foreignColonyNetwork.getBridgeData(foreignBridge.address);
457-
// await foreignMetacolony.setBridgeData(
458-
// ADDRESS_ZERO,
459-
// bridgeData.updateLogBefore,
460-
// bridgeData.updateLogAfter,
461-
// bridgeData.gas,
462-
// bridgeData.chainId,
463-
// bridgeData.skillCreationBefore,
464-
// bridgeData.skillCreationAfter,
465-
// bridgeData.setReputationRootHashBefore,
466-
// bridgeData.setReputationRootHashAfter
467-
// );
468-
469-
// tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, ethers.BigNumber.from(2).pow(256).sub(1), 1);
470-
// await tx.wait();
471-
// });
529+
it("can't bridge a skill that doesn't exist", async () => {
530+
const skillCount = await foreignColonyNetwork.getSkillCount();
531+
const nonExistentSkillId = skillCount.add(10000000);
532+
const tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(nonExistentSkillId, { gasLimit: 1000000 });
533+
await checkErrorRevertEthers(tx.wait(), "colony-invalid-skill-id");
534+
});
535+
536+
it("if bridge is broken, bridging skill transaction fails", async () => {
537+
let tx = await foreignBridge.setBridgeEnabled(false);
538+
await tx.wait();
539+
const skillCount = await foreignColonyNetwork.getSkillCount();
540+
541+
tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(skillCount, { gasLimit: 1000000 });
542+
await checkErrorRevertEthers(tx.wait(), "colony-network-unable-to-bridge-skill-creation");
543+
});
472544
});
473545

474546
describe("while earning reputation on another chain", async () => {
@@ -701,6 +773,7 @@ contract("Cross-chain", (accounts) => {
701773
tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1339");
702774
await tx.wait();
703775
await p;
776+
704777
p = getPromiseForNextBridgedTransaction();
705778
tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1340");
706779
await tx.wait();
@@ -856,5 +929,10 @@ contract("Cross-chain", (accounts) => {
856929
expect(pending.user).to.equal(ADDRESS_ZERO);
857930
expect(pending.colony).to.equal(ADDRESS_ZERO);
858931
});
932+
933+
it("appendReputationUpdateLogFromBridge cannot be called by a non-bridge address", async () => {
934+
const tx = await homeColonyNetwork.appendReputationUpdateLogFromBridge(ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0, { gasLimit: 1000000 });
935+
await checkErrorRevertEthers(tx.wait(), "colony-network-not-known-bridge");
936+
});
859937
});
860938
});

0 commit comments

Comments
 (0)