Skip to content

Commit

Permalink
test(arbToGno): added more unit test and concurrency in deployment sc…
Browse files Browse the repository at this point in the history
…ripts
  • Loading branch information
madhurMongia committed Sep 25, 2024
1 parent 2c3c514 commit 46247de
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 22 deletions.
15 changes: 11 additions & 4 deletions contracts/deploy/01-outbox/01-arb-to-gnosis-outbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const paramsByChainId = {
sequencerLimit: 86400, // 24 hours
},
HARDHAT: {
deposit: parseEther("10"), // 120 xDAI budget for timeout
deposit: parseEther("10"),
// Average happy path wait time is 22.5 mins, assume no censorship
epochPeriod: 600, // 10 min
minChallengePeriod: 600, // 10 min (assume no sequencer backdating)
Expand All @@ -56,8 +56,15 @@ const deployOutbox: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { providers } = ethers;

// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
const chainId = Number(await getChainId());
const [namedAccounts, signers, rawChainId] = await Promise.all([
getNamedAccounts(),
hre.ethers.getSigners(),
getChainId(),
]);

const deployer = namedAccounts.deployer ?? signers[0].address;
const chainId = Number(rawChainId);

console.log("deploying to chainId %s with deployer %s", chainId, deployer);

const routerNetworks = {
Expand Down Expand Up @@ -89,7 +96,7 @@ const deployOutbox: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
console.log("calculated future router for nonce %d: %s", nonce + 10, routerAddress);

const senderGatewayAddress = getContractAddress(deployer, nonce + 6); // with the current order of transaction ,nonce for sender gateway would be 14.
console.log("calculated future SenderGatewayToGnosis address for nonce %d: %s", nonce, senderGatewayAddress);
console.log("calculated future SenderGatewayToGnosis address for nonce %d: %s", nonce + 6, senderGatewayAddress);

const ambMock = await deploy("MockAMB", {
from: deployer,
Expand Down
13 changes: 10 additions & 3 deletions contracts/deploy/02-inbox/02-arb-to-gnosis-inbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const paramsByChainId = {
},
HARDHAT: {
epochPeriod: 600, // 10 minutes
routerAddress: ethers.constants.AddressZero,
},
};

Expand All @@ -27,8 +26,16 @@ const deployInbox: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { deploy } = deployments;

// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
const chainId = Number(await getChainId());
const [namedAccounts, signers, rawChainId] = await Promise.all([
getNamedAccounts(),
hre.ethers.getSigners(),
getChainId(),
]);

const deployer = namedAccounts.deployer ?? signers[0].address;
const chainId = Number(rawChainId);

console.log("deploying to chainId %s with deployer %s", chainId, deployer);

const { epochPeriod } = paramsByChainId[SenderChains[chainId]];

Expand Down
22 changes: 15 additions & 7 deletions contracts/deploy/03-routers/03-arb-to-gnosis-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,28 @@ const paramsByChainId = {
const deployRouter: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { deployments, getNamedAccounts, getChainId } = hre;
const { deploy } = deployments;
const chainId = Number(await getChainId());

// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
console.log("deployer: %s", deployer);
const [namedAccounts, signers, rawChainId] = await Promise.all([
getNamedAccounts(),
hre.ethers.getSigners(),
getChainId(),
]);

const deployer = namedAccounts.deployer ?? signers[0].address;
const chainId = Number(rawChainId);

console.log("deploying to chainId %s with deployer %s", chainId, deployer);

const { arbitrumBridge, amb } = paramsByChainId[RouterChains[chainId]];

// ----------------------------------------------------------------------------------------------
const hardhatDeployer = async () => {
const veaOutbox = await deployments.get("VeaOutboxArbToGnosis");
const veaInbox = await deployments.get("VeaInboxArbToGnosis");
const amb = await deployments.get("MockAMB");
//const ArbSysMock = await deployments.get('arbSysMock');
const [veaOutbox, veaInbox, amb] = await Promise.all([
deployments.get("VeaOutboxArbToGnosis"),
deployments.get("VeaInboxArbToGnosis"),
deployments.get("MockAMB"),
]);

const sequencerInbox = await deploy("SequencerInboxMock", {
from: deployer,
Expand Down
201 changes: 193 additions & 8 deletions contracts/test/integration/ArbToGnosis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,12 @@ describe("Arbitrum to Gnosis Bridge Tests", async () => {
await network.provider.send("evm_mine");

// Ensure bridger and challenger have enough WETH
await weth.transfer(bridger.address, TEN_ETH.mul(2));
await weth.transfer(challenger.address, TEN_ETH.mul(2));
await weth.transfer(bridger.address, TEN_ETH.mul(10));
await weth.transfer(challenger.address, TEN_ETH.mul(10));

// Approve WETH spending for both
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH.mul(2));
await weth.connect(challenger).approve(veaOutbox.address, TEN_ETH.mul(2));
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH.mul(10));
await weth.connect(challenger).approve(veaOutbox.address, TEN_ETH.mul(10));
await amb.setMaxGasPerTx(100000);
});

Expand Down Expand Up @@ -447,10 +447,102 @@ describe("Arbitrum to Gnosis Bridge Tests", async () => {
const verifiedEvent = verifiedEvents[0];
expect(verifiedEvent.args._epoch).to.equal(epoch, "Verified event epoch mismatch");

const expectedClaim = {
stateRoot: batchMerkleRoot,
claimer: bridger.address,
timestampClaimed: claimBlock.timestamp,
timestampVerification: 0,
blocknumberVerification: 0,
honest: 1,
challenger: challenger.address,
};

const expectedClaimHash = await veaOutbox.hashClaim(expectedClaim);
const storedClaimHash = await veaOutbox.claimHashes(epoch);

expect(storedClaimHash).to.equal(expectedClaimHash, "Stored claim hash does not match expected");
expect(await veaOutbox.stateRoot()).to.equal(batchMerkleRoot, "VeaOutbox stateRoot should be updated");
expect(await veaOutbox.latestVerifiedEpoch()).to.equal(epoch, "VeaOutbox latestVerifiedEpoch should be updated");
});

it("should not update latestEpoch and stateRoot when resolving older dispute", async () => {
const { claimBlock } = await setupClaimAndChallenge(epoch, batchMerkleRoot, 0);

// Create and verify newer epochs
const newEpoch1 = epoch + 1;
const newMerkleRoot1 = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("newer1"));

// Advance time to the next epoch
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
await network.provider.send("evm_mine");

const newClaimTxOne = await veaOutbox.connect(bridger).claim(newEpoch1, newMerkleRoot1);
const newClaimTxOneBlock = await ethers.provider.getBlock(newClaimTxOne.blockNumber!);

const sequencerDelayLimit = await veaOutbox.sequencerDelayLimit();
const maxL2StateSyncDelay = sequencerDelayLimit.add(EPOCH_PERIOD);
await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
await network.provider.send("evm_mine");

const newVerifyTxOne = await veaOutbox.startVerification(
newEpoch1,
createClaim(newMerkleRoot1, bridger.address, newClaimTxOneBlock.timestamp)
);
const newVerifyTxOneBlock = await ethers.provider.getBlock(newVerifyTxOne.blockNumber!);

await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD]);
await network.provider.send("evm_mine");

await veaOutbox.connect(bridger).verifySnapshot(newEpoch1, {
...createClaim(newMerkleRoot1, bridger.address, newClaimTxOneBlock.timestamp),
blocknumberVerification: newVerifyTxOne.blockNumber!,
timestampVerification: newVerifyTxOneBlock.timestamp,
});

// Advance time to the next epoch
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
await network.provider.send("evm_mine");

const newEpoch2 = (await veaOutbox.epochNow()).toNumber() - 1;
const newMerkleRoot2 = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("newer2"));
const newClaimTxTwo = await veaOutbox.connect(bridger).claim(newEpoch2, newMerkleRoot2);

const newClaimTxTwoBlock = await ethers.provider.getBlock(newClaimTxTwo.blockNumber!);

await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
await network.provider.send("evm_mine");

const newVerifyTxTwo = await veaOutbox.startVerification(
newEpoch2,
createClaim(newMerkleRoot2, bridger.address, newClaimTxTwoBlock.timestamp)
);
const newVerifyTxTwoBlock = await ethers.provider.getBlock(newVerifyTxTwo.blockNumber!);

await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD]);
await network.provider.send("evm_mine");

await veaOutbox.connect(bridger).verifySnapshot(newEpoch2, {
...createClaim(newMerkleRoot2, bridger.address, newClaimTxTwoBlock.timestamp),
timestampVerification: newVerifyTxTwoBlock.timestamp!,
blocknumberVerification: newVerifyTxTwo.blockNumber!,
});

// Resolve the dispute for the old epoch
await simulateDisputeResolution(epoch, {
stateRoot: batchMerkleRoot,
claimer: bridger.address,
timestampClaimed: claimBlock.timestamp,
timestampVerification: 0,
blocknumberVerification: 0,
honest: 0,
challenger: challenger.address,
});

// Check that latestEpoch and stateRoot weren't updated to the old epoch's data
expect(await veaOutbox.latestVerifiedEpoch()).to.equal(newEpoch2, "Latest verified epoch should not change");
expect(await veaOutbox.stateRoot()).to.equal(newMerkleRoot2, "State root should not change");
});

it("should allow bridger to withdraw deposit plus reward", async () => {
const { claimBlock } = await setupClaimAndChallenge(epoch, batchMerkleRoot, 0);

Expand Down Expand Up @@ -558,12 +650,12 @@ describe("Arbitrum to Gnosis Bridge Tests", async () => {
await network.provider.send("evm_mine");

// Ensure bridger and challenger have enough WETH
await weth.transfer(bridger.address, TEN_ETH.mul(2));
await weth.transfer(challenger.address, TEN_ETH.mul(2));
await weth.transfer(bridger.address, TEN_ETH.mul(10));
await weth.transfer(challenger.address, TEN_ETH.mul(10));

// Approve WETH spending for both
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH.mul(2));
await weth.connect(challenger).approve(veaOutbox.address, TEN_ETH.mul(2));
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH.mul(10));
await weth.connect(challenger).approve(veaOutbox.address, TEN_ETH.mul(10));
});

it("should allow challenger to submit a challenge to a dishonest claim", async () => {
Expand Down Expand Up @@ -613,6 +705,21 @@ describe("Arbitrum to Gnosis Bridge Tests", async () => {
challenger: challenger.address,
});

const expectedClaim = {
stateRoot: dishonestMerkleRoot,
claimer: bridger.address,
timestampClaimed: claimBlock.timestamp,
timestampVerification: 0,
blocknumberVerification: 0,
honest: 2,
challenger: challenger.address,
};

const expectedClaimHash = await veaOutbox.hashClaim(expectedClaim);
const storedClaimHash = await veaOutbox.claimHashes(epoch);

expect(storedClaimHash).to.equal(expectedClaimHash, "Stored claim hash does not match expected");

expect(await veaOutbox.stateRoot()).to.equal(honestMerkleRoot, "State root should be updated to honest root");
});

Expand Down Expand Up @@ -718,6 +825,84 @@ describe("Arbitrum to Gnosis Bridge Tests", async () => {
expect(await veaOutbox.stateRoot()).to.equal(honestMerkleRoot, "State root should be updated to honest root");
});

it("should not update latestEpoch and stateRoot when resolving older dispute", async () => {
const { claimBlock } = await setupClaimAndChallenge(epoch, dishonestMerkleRoot, 0);

// Create and verify newer epochs
const newEpoch1 = epoch + 1;
const newMerkleRoot1 = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("newer1"));

// Advance time to the next epoch
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
await network.provider.send("evm_mine");

const newClaimTxOne = await veaOutbox.connect(bridger).claim(newEpoch1, newMerkleRoot1);
const newClaimTxOneBlock = await ethers.provider.getBlock(newClaimTxOne.blockNumber!);

const sequencerDelayLimit = await veaOutbox.sequencerDelayLimit();
const maxL2StateSyncDelay = sequencerDelayLimit.add(EPOCH_PERIOD);
await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
await network.provider.send("evm_mine");

const newVerifyTxOne = await veaOutbox.startVerification(
newEpoch1,
createClaim(newMerkleRoot1, bridger.address, newClaimTxOneBlock.timestamp)
);
const newVerifyTxOneBlock = await ethers.provider.getBlock(newVerifyTxOne.blockNumber!);

await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD]);
await network.provider.send("evm_mine");

await veaOutbox.connect(bridger).verifySnapshot(newEpoch1, {
...createClaim(newMerkleRoot1, bridger.address, newClaimTxOneBlock.timestamp),
blocknumberVerification: newVerifyTxOne.blockNumber!,
timestampVerification: newVerifyTxOneBlock.timestamp,
});

// Advance time to the next epoch
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
await network.provider.send("evm_mine");

const newEpoch2 = (await veaOutbox.epochNow()).toNumber() - 1;
const newMerkleRoot2 = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("newer2"));
const newClaimTxTwo = await veaOutbox.connect(bridger).claim(newEpoch2, newMerkleRoot2);

const newClaimTxTwoBlock = await ethers.provider.getBlock(newClaimTxTwo.blockNumber!);

await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
await network.provider.send("evm_mine");

const newVerifyTxTwo = await veaOutbox.startVerification(
newEpoch2,
createClaim(newMerkleRoot2, bridger.address, newClaimTxTwoBlock.timestamp)
);
const newVerifyTxTwoBlock = await ethers.provider.getBlock(newVerifyTxTwo.blockNumber!);

await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD]);
await network.provider.send("evm_mine");

await veaOutbox.connect(bridger).verifySnapshot(newEpoch2, {
...createClaim(newMerkleRoot2, bridger.address, newClaimTxTwoBlock.timestamp),
timestampVerification: newVerifyTxTwoBlock.timestamp!,
blocknumberVerification: newVerifyTxTwo.blockNumber!,
});

// Resolve the dispute for the old epoch
await simulateDisputeResolution(epoch, {
stateRoot: dishonestMerkleRoot,
claimer: bridger.address,
timestampClaimed: claimBlock.timestamp,
timestampVerification: 0,
blocknumberVerification: 0,
honest: 0,
challenger: challenger.address,
});

// Check that latestEpoch and stateRoot weren't updated to the old epoch's data
expect(await veaOutbox.latestVerifiedEpoch()).to.equal(newEpoch2, "Latest verified epoch should not change");
expect(await veaOutbox.stateRoot()).to.equal(newMerkleRoot2, "State root should not change");
});

it("should not allow multiple withdrawals for the same challenge", async () => {
const { claimBlock } = await setupClaimAndChallenge(epoch, dishonestMerkleRoot, 0);

Expand Down

0 comments on commit 46247de

Please sign in to comment.