Skip to content

Commit 0da4c27

Browse files
committed
test(arbToGno): honest claim-no challenge-bridger paid
1 parent 3d8a8c3 commit 0da4c27

File tree

1 file changed

+352
-0
lines changed

1 file changed

+352
-0
lines changed
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
import { expect } from "chai";
2+
import { deployments, ethers, network } from "hardhat";
3+
import { BigNumber } from "ethers";
4+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
5+
import { MerkleTree } from "../merkle/MerkleTree";
6+
const { mine } = require("@nomicfoundation/hardhat-network-helpers");
7+
8+
import {
9+
VeaOutboxArbToGnosis,
10+
ReceiverGatewayMock,
11+
VeaInboxArbToGnosis,
12+
SenderGatewayMock,
13+
RouterArbToGnosis,
14+
MockWETH,
15+
MockAMB,
16+
ArbSysMock,
17+
} from "../../typechain-types";
18+
19+
// Constants
20+
const TEN_ETH = BigNumber.from(10).pow(19);
21+
const EPOCH_PERIOD = 600;
22+
const CHALLENGE_PERIOD = 600;
23+
const SEQUENCER_DELAY = 300;
24+
25+
describe("Arbitrum to Gnosis Bridge Tests", async () => {
26+
// Test participants
27+
let bridger: SignerWithAddress;
28+
let sender: SignerWithAddress;
29+
let receiver: SignerWithAddress;
30+
let challenger: SignerWithAddress;
31+
32+
// Contracts
33+
let veaOutbox: VeaOutboxArbToGnosis;
34+
let receiverGateway: ReceiverGatewayMock;
35+
let veaInbox: VeaInboxArbToGnosis;
36+
let senderGateway: SenderGatewayMock;
37+
let router: RouterArbToGnosis;
38+
let amb: MockAMB;
39+
let weth: MockWETH;
40+
let arbsysMock: ArbSysMock;
41+
42+
// Helper function to create a claim object
43+
const createClaim = (stateRoot: string, claimer: string, timestamp: number) => ({
44+
stateRoot,
45+
claimer,
46+
timestampClaimed: timestamp,
47+
timestampVerification: 0,
48+
blocknumberVerification: 0,
49+
honest: 0,
50+
challenger: ethers.constants.AddressZero,
51+
});
52+
53+
// Helper function to simulate dispute resolution
54+
async function simulateDisputeResolution(epoch: number, claim: any) {
55+
await veaInbox.connect(bridger).sendSnapshot(epoch, 100000, claim, { gasLimit: 100000 });
56+
57+
await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD + SEQUENCER_DELAY]);
58+
await network.provider.send("evm_mine");
59+
60+
const events = await amb.queryFilter(amb.filters.MockedEvent());
61+
const lastEvent = events[events.length - 1];
62+
63+
await amb.executeMessageCall(
64+
veaOutbox.address,
65+
router.address,
66+
lastEvent.args._data,
67+
lastEvent.args.messageId,
68+
1000000
69+
);
70+
}
71+
72+
// Helper function to setup a claim and challenge
73+
async function setupClaimAndChallenge(epoch: any, merkleRoot: string, honest: number) {
74+
const claimTx = await veaOutbox.connect(bridger).claim(epoch, merkleRoot);
75+
const claimBlock = await ethers.provider.getBlock(claimTx.blockNumber!);
76+
77+
const challengeTx = await veaOutbox.connect(challenger).challenge(epoch, {
78+
stateRoot: merkleRoot,
79+
claimer: bridger.address,
80+
timestampClaimed: claimBlock.timestamp,
81+
timestampVerification: 0,
82+
blocknumberVerification: 0,
83+
honest,
84+
challenger: ethers.constants.AddressZero,
85+
});
86+
87+
return { claimBlock, merkleRoot, challengeTx };
88+
}
89+
90+
before("Initialize wallets", async () => {
91+
[challenger, bridger, sender, receiver] = await ethers.getSigners();
92+
});
93+
94+
beforeEach("Setup contracts and tokens", async () => {
95+
// Deploy contracts
96+
await deployments.fixture(["ArbToGnosisOutbox", "ArbToGnosisInbox", "ArbToGnosisRouter"], {
97+
fallbackToGlobal: true,
98+
keepExistingDeployments: false,
99+
});
100+
101+
// Get contract instances
102+
veaOutbox = (await ethers.getContract("VeaOutboxArbToGnosis")) as VeaOutboxArbToGnosis;
103+
receiverGateway = (await ethers.getContract("ArbToGnosisReceiverGateway")) as ReceiverGatewayMock;
104+
veaInbox = (await ethers.getContract("VeaInboxArbToGnosis")) as VeaInboxArbToGnosis;
105+
senderGateway = (await ethers.getContract("ArbToGnosisSenderGateway")) as SenderGatewayMock;
106+
router = (await ethers.getContract("RouterArbToGnosis")) as RouterArbToGnosis;
107+
amb = (await ethers.getContract("MockAMB")) as MockAMB;
108+
weth = (await ethers.getContract("MockWETH")) as MockWETH;
109+
arbsysMock = (await ethers.getContract("ArbSysMock")) as ArbSysMock;
110+
111+
// Setup initial token balances
112+
await weth.deposit({ value: TEN_ETH.mul(100) });
113+
await weth.transfer(bridger.address, TEN_ETH.mul(10));
114+
});
115+
116+
describe("Honest Claim - No Challenge - Bridger Paid", async () => {
117+
it("should send a message and save snapshot", async () => {
118+
const data = 1121;
119+
await senderGateway.connect(sender).sendMessage(data);
120+
await veaInbox.connect(bridger).saveSnapshot();
121+
122+
const BatchOutgoing = veaInbox.filters.SnapshotSaved();
123+
const batchOutGoingEvent = await veaInbox.queryFilter(BatchOutgoing);
124+
const epoch = Math.floor((await batchOutGoingEvent[0].getBlock()).timestamp / EPOCH_PERIOD);
125+
const batchMerkleRoot = await veaInbox.snapshots(epoch);
126+
127+
expect(await veaInbox.snapshots(epoch)).to.equal(batchMerkleRoot, "Snapshot not saved correctly");
128+
});
129+
130+
it("should allow bridger to claim", async () => {
131+
// Setup
132+
const data = 1121;
133+
await senderGateway.connect(sender).sendMessage(data);
134+
await veaInbox.connect(bridger).saveSnapshot();
135+
136+
const BatchOutgoing = veaInbox.filters.SnapshotSaved();
137+
const batchOutGoingEvent = await veaInbox.queryFilter(BatchOutgoing);
138+
const epoch = Math.floor((await batchOutGoingEvent[0].getBlock()).timestamp / EPOCH_PERIOD);
139+
const batchMerkleRoot = await veaInbox.snapshots(epoch);
140+
141+
// Advance time to next epoch
142+
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
143+
await network.provider.send("evm_mine");
144+
145+
// Approve WETH spending and claim
146+
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH.mul(2));
147+
const claimTx = await veaOutbox.connect(bridger).claim(epoch, batchMerkleRoot);
148+
149+
// Check claim event
150+
await expect(claimTx).to.emit(veaOutbox, "Claimed").withArgs(bridger.address, epoch, batchMerkleRoot);
151+
152+
// Ensure double claim is not possible
153+
await expect(veaOutbox.connect(bridger).claim(epoch, batchMerkleRoot)).to.be.revertedWith("Claim already made.");
154+
});
155+
156+
it("should start verification after maxL2StateSyncDelay", async () => {
157+
// Setup
158+
const data = 1121;
159+
await senderGateway.connect(sender).sendMessage(data);
160+
await veaInbox.connect(bridger).saveSnapshot();
161+
162+
const BatchOutgoing = veaInbox.filters.SnapshotSaved();
163+
const batchOutGoingEvent = await veaInbox.queryFilter(BatchOutgoing);
164+
const epoch = Math.floor((await batchOutGoingEvent[0].getBlock()).timestamp / EPOCH_PERIOD);
165+
const batchMerkleRoot = await veaInbox.snapshots(epoch);
166+
167+
// Advance time and make claim
168+
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
169+
await network.provider.send("evm_mine");
170+
171+
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH);
172+
const claimTx = await veaOutbox.connect(bridger).claim(epoch, batchMerkleRoot);
173+
const block = await ethers.provider.getBlock(claimTx.blockNumber!);
174+
175+
// Calculate and advance time for maxL2StateSyncDelay
176+
const sequencerDelayLimit = await veaOutbox.sequencerDelayLimit();
177+
const maxL2StateSyncDelay = sequencerDelayLimit.add(EPOCH_PERIOD);
178+
await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
179+
await network.provider.send("evm_mine");
180+
181+
// Start verification
182+
const startVerificationTx = await veaOutbox.startVerification(
183+
epoch,
184+
createClaim(batchMerkleRoot, bridger.address, block.timestamp)
185+
);
186+
187+
await expect(startVerificationTx).to.emit(veaOutbox, "VerificationStarted").withArgs(epoch);
188+
});
189+
190+
it("should verify snapshot after challenge period", async () => {
191+
// Setup
192+
const data = 1121;
193+
await senderGateway.connect(sender).sendMessage(data);
194+
await veaInbox.connect(bridger).saveSnapshot();
195+
196+
const BatchOutgoing = veaInbox.filters.SnapshotSaved();
197+
const batchOutGoingEvent = await veaInbox.queryFilter(BatchOutgoing);
198+
const epoch = Math.floor((await batchOutGoingEvent[0].getBlock()).timestamp / EPOCH_PERIOD);
199+
const batchMerkleRoot = await veaInbox.snapshots(epoch);
200+
201+
// Advance time, make claim, and start verification
202+
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
203+
await network.provider.send("evm_mine");
204+
205+
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH);
206+
const claimTx = await veaOutbox.connect(bridger).claim(epoch, batchMerkleRoot);
207+
const block = await ethers.provider.getBlock(claimTx.blockNumber!);
208+
209+
const sequencerDelayLimit = await veaOutbox.sequencerDelayLimit();
210+
const maxL2StateSyncDelay = sequencerDelayLimit.add(EPOCH_PERIOD);
211+
212+
await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
213+
await network.provider.send("evm_mine");
214+
215+
const startVerificationTx = await veaOutbox.startVerification(
216+
epoch,
217+
createClaim(batchMerkleRoot, bridger.address, block.timestamp)
218+
);
219+
const verificationBlock = await ethers.provider.getBlock(startVerificationTx.blockNumber!);
220+
221+
// Advance time for challenge period
222+
const safeAdvanceTime = CHALLENGE_PERIOD + EPOCH_PERIOD;
223+
await network.provider.send("evm_increaseTime", [safeAdvanceTime]);
224+
await network.provider.send("evm_mine");
225+
226+
// Verify snapshot
227+
await veaOutbox.connect(bridger).verifySnapshot(epoch, {
228+
...createClaim(batchMerkleRoot, bridger.address, block.timestamp),
229+
timestampVerification: verificationBlock.timestamp,
230+
blocknumberVerification: startVerificationTx.blockNumber!,
231+
});
232+
233+
expect(await veaOutbox.stateRoot()).to.equal(batchMerkleRoot, "State root not updated correctly");
234+
expect(await veaOutbox.latestVerifiedEpoch()).to.equal(epoch, "Latest verified epoch not updated");
235+
});
236+
237+
it("should relay message after verification", async () => {
238+
// Setup
239+
const data = 1121;
240+
const sendMessageTx = await senderGateway.connect(sender).sendMessage(data);
241+
await veaInbox.connect(bridger).saveSnapshot();
242+
243+
const MessageSent = veaInbox.filters.MessageSent();
244+
const MessageSentEvent = await veaInbox.queryFilter(MessageSent);
245+
const msg = MessageSentEvent[0].args._nodeData;
246+
const nonce = "0x" + msg.slice(2, 18);
247+
const to = "0x" + msg.slice(18, 58);
248+
const msgData = "0x" + msg.slice(58);
249+
250+
let nodes: string[] = [];
251+
nodes.push(MerkleTree.makeLeafNode(nonce, to, msgData));
252+
const mt = new MerkleTree(nodes);
253+
const proof = mt.getHexProof(nodes[0]);
254+
255+
const BatchOutgoing = veaInbox.filters.SnapshotSaved();
256+
const batchOutGoingEvent = await veaInbox.queryFilter(BatchOutgoing);
257+
const epoch = Math.floor((await batchOutGoingEvent[0].getBlock()).timestamp / EPOCH_PERIOD);
258+
const batchMerkleRoot = await veaInbox.snapshots(epoch);
259+
260+
// Advance time, make claim, and start verification
261+
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
262+
await network.provider.send("evm_mine");
263+
264+
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH);
265+
const claimTx = await veaOutbox.connect(bridger).claim(epoch, batchMerkleRoot);
266+
const block = await ethers.provider.getBlock(claimTx.blockNumber!);
267+
268+
const sequencerDelayLimit = await veaOutbox.sequencerDelayLimit();
269+
const maxL2StateSyncDelay = sequencerDelayLimit.add(EPOCH_PERIOD);
270+
271+
await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
272+
await network.provider.send("evm_mine");
273+
274+
const startVerificationTx = await veaOutbox.startVerification(
275+
epoch,
276+
createClaim(batchMerkleRoot, bridger.address, block.timestamp)
277+
);
278+
const verificationBlock = await ethers.provider.getBlock(startVerificationTx.blockNumber!);
279+
280+
await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD]);
281+
await mine(Math.ceil(CHALLENGE_PERIOD / 12));
282+
283+
await veaOutbox.connect(bridger).verifySnapshot(epoch, {
284+
...createClaim(batchMerkleRoot, bridger.address, block.timestamp),
285+
timestampVerification: verificationBlock.timestamp,
286+
blocknumberVerification: startVerificationTx.blockNumber!,
287+
});
288+
289+
// Relay message
290+
const relayTx = await veaOutbox.connect(receiver).sendMessage(proof, nonce, to, msgData);
291+
await expect(relayTx).to.emit(veaOutbox, "MessageRelayed").withArgs(0);
292+
293+
// Ensure message can't be relayed twice
294+
await expect(veaOutbox.connect(receiver).sendMessage(proof, nonce, to, msgData)).to.be.revertedWith(
295+
"Message already relayed"
296+
);
297+
});
298+
299+
it("should allow bridger to withdraw deposit after successful claim", async () => {
300+
// Setup
301+
const data = 1121;
302+
await senderGateway.connect(sender).sendMessage(data);
303+
await veaInbox.connect(bridger).saveSnapshot();
304+
305+
const BatchOutgoing = veaInbox.filters.SnapshotSaved();
306+
const batchOutGoingEvent = await veaInbox.queryFilter(BatchOutgoing);
307+
const epoch = Math.floor((await batchOutGoingEvent[0].getBlock()).timestamp / EPOCH_PERIOD);
308+
const batchMerkleRoot = await veaInbox.snapshots(epoch);
309+
310+
// Advance time, make claim, and start verification
311+
await network.provider.send("evm_increaseTime", [EPOCH_PERIOD]);
312+
await network.provider.send("evm_mine");
313+
314+
await weth.connect(bridger).approve(veaOutbox.address, TEN_ETH);
315+
const claimTx = await veaOutbox.connect(bridger).claim(epoch, batchMerkleRoot);
316+
const block = await ethers.provider.getBlock(claimTx.blockNumber!);
317+
318+
const sequencerDelayLimit = await veaOutbox.sequencerDelayLimit();
319+
const maxL2StateSyncDelay = sequencerDelayLimit.add(EPOCH_PERIOD);
320+
321+
await network.provider.send("evm_increaseTime", [maxL2StateSyncDelay.toNumber()]);
322+
await network.provider.send("evm_mine");
323+
324+
const startVerificationTx = await veaOutbox.startVerification(
325+
epoch,
326+
createClaim(batchMerkleRoot, bridger.address, block.timestamp)
327+
);
328+
const verificationBlock = await ethers.provider.getBlock(startVerificationTx.blockNumber!);
329+
330+
await network.provider.send("evm_increaseTime", [CHALLENGE_PERIOD]);
331+
await mine(Math.ceil(CHALLENGE_PERIOD / 12));
332+
333+
await veaOutbox.connect(bridger).verifySnapshot(epoch, {
334+
...createClaim(batchMerkleRoot, bridger.address, block.timestamp),
335+
timestampVerification: verificationBlock.timestamp,
336+
blocknumberVerification: startVerificationTx.blockNumber!,
337+
});
338+
339+
// Withdraw deposit
340+
const initialBalance = await weth.balanceOf(bridger.address);
341+
await veaOutbox.connect(bridger).withdrawClaimDeposit(epoch, {
342+
...createClaim(batchMerkleRoot, bridger.address, block.timestamp),
343+
timestampVerification: verificationBlock.timestamp,
344+
blocknumberVerification: startVerificationTx.blockNumber!,
345+
honest: 1,
346+
});
347+
const finalBalance = await weth.balanceOf(bridger.address);
348+
349+
expect(finalBalance.sub(initialBalance)).to.equal(TEN_ETH, "Incorrect withdrawal amount");
350+
});
351+
});
352+
});

0 commit comments

Comments
 (0)