diff --git a/contracts/deploy/01-outbox/01-arb-to-gnosis-outbox.ts b/contracts/deploy/01-outbox/01-arb-to-gnosis-outbox.ts index ef7aa3b4..2b584579 100644 --- a/contracts/deploy/01-outbox/01-arb-to-gnosis-outbox.ts +++ b/contracts/deploy/01-outbox/01-arb-to-gnosis-outbox.ts @@ -36,7 +36,7 @@ const paramsByChainId = { sequencerLimit: 86400, // 24 hours }, HARDHAT: { - deposit: parseEther("10"), // + 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) @@ -56,13 +56,15 @@ const deployOutbox: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const { providers } = ethers; // fallback to hardhat node signers on local network - const [namedAccounts, defaultSigner] = await Promise.all([ + const [namedAccounts, signers, rawChainId] = await Promise.all([ getNamedAccounts(), - hre.ethers.getSigners().then((signers) => signers[0]), + hre.ethers.getSigners(), + getChainId(), ]); - const deployer = namedAccounts.deployer ?? defaultSigner.address; - const chainId = Number(await 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 = { diff --git a/contracts/deploy/02-inbox/02-arb-to-gnosis-inbox.ts b/contracts/deploy/02-inbox/02-arb-to-gnosis-inbox.ts index 323da2ff..55a44bfe 100644 --- a/contracts/deploy/02-inbox/02-arb-to-gnosis-inbox.ts +++ b/contracts/deploy/02-inbox/02-arb-to-gnosis-inbox.ts @@ -18,22 +18,24 @@ const paramsByChainId = { }, HARDHAT: { epochPeriod: 600, // 10 minutes - routerAddress: ethers.constants.AddressZero, }, }; const deployInbox: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { - const { ethers, deployments, getNamedAccounts, getChainId } = hre; + const { ethers, deployments, getNamedAccounts, getChainId, config } = hre; const { deploy } = deployments; // fallback to hardhat node signers on local network - const [namedAccounts, defaultSigner] = await Promise.all([ + const [namedAccounts, signers, rawChainId] = await Promise.all([ getNamedAccounts(), - hre.ethers.getSigners().then((signers) => signers[0]), + hre.ethers.getSigners(), + getChainId(), ]); - const deployer = namedAccounts.deployer ?? defaultSigner.address; - const chainId = Number(await 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]]; diff --git a/contracts/deploy/03-routers/03-arb-to-gnosis-router.ts b/contracts/deploy/03-routers/03-arb-to-gnosis-router.ts index 444ea74b..0572747b 100644 --- a/contracts/deploy/03-routers/03-arb-to-gnosis-router.ts +++ b/contracts/deploy/03-routers/03-arb-to-gnosis-router.ts @@ -27,16 +27,18 @@ 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 [namedAccounts, defaultSigner] = await Promise.all([ + const [namedAccounts, signers, rawChainId] = await Promise.all([ getNamedAccounts(), - hre.ethers.getSigners().then((signers) => signers[0]), + hre.ethers.getSigners(), + getChainId(), ]); - const deployer = namedAccounts.deployer ?? defaultSigner.address; - console.log("deployer: %s", deployer); + 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]]; diff --git a/contracts/test/integration/ArbToGnosis.ts b/contracts/test/integration/ArbToGnosis.ts index ca91525e..e6bf984c 100644 --- a/contracts/test/integration/ArbToGnosis.ts +++ b/contracts/test/integration/ArbToGnosis.ts @@ -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); }); @@ -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);