@arbitrum/nitro-contracts 1.0.0-beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. package/.prettierrc +5 -0
  2. package/.solhint.json +18 -0
  3. package/deploy/BridgeStubCreator.js +10 -0
  4. package/deploy/HashProofHelper.js +13 -0
  5. package/deploy/InboxStubCreator.js +17 -0
  6. package/deploy/OneStepProofEntryCreator.js +19 -0
  7. package/deploy/OneStepProver0Creator.js +14 -0
  8. package/deploy/OneStepProverHostIoCreator.js +14 -0
  9. package/deploy/OneStepProverMathCreator.js +14 -0
  10. package/deploy/OneStepProverMemoryCreator.js +14 -0
  11. package/deploy/SequencerInboxStubCreator.js +13 -0
  12. package/deploy/ValueArrayTesterCreator.js +13 -0
  13. package/hardhat.config.ts +47 -0
  14. package/hardhat.prod-config.js +18 -0
  15. package/package.json +49 -0
  16. package/scripts/build.bash +5 -0
  17. package/src/bridge/Bridge.sol +168 -0
  18. package/src/bridge/IBridge.sol +68 -0
  19. package/src/bridge/IInbox.sol +80 -0
  20. package/src/bridge/IMessageProvider.sol +11 -0
  21. package/src/bridge/IOutbox.sol +52 -0
  22. package/src/bridge/ISequencerInbox.sol +85 -0
  23. package/src/bridge/Inbox.sol +414 -0
  24. package/src/bridge/Messages.sol +38 -0
  25. package/src/bridge/Outbox.sol +188 -0
  26. package/src/bridge/SequencerInbox.sol +274 -0
  27. package/src/challenge/ChallengeLib.sol +135 -0
  28. package/src/challenge/ChallengeManager.sol +367 -0
  29. package/src/challenge/IChallengeManager.sol +75 -0
  30. package/src/challenge/IChallengeResultReceiver.sol +13 -0
  31. package/src/libraries/AddressAliasHelper.sol +29 -0
  32. package/src/libraries/AdminFallbackProxy.sol +153 -0
  33. package/src/libraries/ArbitrumProxy.sol +20 -0
  34. package/src/libraries/Constants.sol +10 -0
  35. package/src/libraries/CryptographyPrimitives.sol +323 -0
  36. package/src/libraries/DelegateCallAware.sol +44 -0
  37. package/src/libraries/Error.sol +38 -0
  38. package/src/libraries/IGasRefunder.sol +35 -0
  39. package/src/libraries/MerkleLib.sol +46 -0
  40. package/src/libraries/MessageTypes.sol +14 -0
  41. package/src/libraries/SecondaryLogicUUPSUpgradeable.sol +58 -0
  42. package/src/libraries/UUPSNotUpgradeable.sol +56 -0
  43. package/src/mocks/BridgeStub.sol +115 -0
  44. package/src/mocks/Counter.sol +13 -0
  45. package/src/mocks/ExecutionManager.sol +41 -0
  46. package/src/mocks/InboxStub.sol +131 -0
  47. package/src/mocks/MockResultReceiver.sol +59 -0
  48. package/src/mocks/SequencerInboxStub.sol +42 -0
  49. package/src/mocks/SimpleProxy.sol +19 -0
  50. package/src/node-interface/NodeInterface.sol +50 -0
  51. package/src/osp/HashProofHelper.sol +154 -0
  52. package/src/osp/IOneStepProofEntry.sol +20 -0
  53. package/src/osp/IOneStepProver.sol +27 -0
  54. package/src/osp/OneStepProofEntry.sol +129 -0
  55. package/src/osp/OneStepProver0.sol +566 -0
  56. package/src/osp/OneStepProverHostIo.sol +357 -0
  57. package/src/osp/OneStepProverMath.sol +514 -0
  58. package/src/osp/OneStepProverMemory.sol +313 -0
  59. package/src/precompiles/ArbAddressTable.sol +60 -0
  60. package/src/precompiles/ArbAggregator.sol +62 -0
  61. package/src/precompiles/ArbBLS.sol +53 -0
  62. package/src/precompiles/ArbDebug.sol +39 -0
  63. package/src/precompiles/ArbFunctionTable.sol +29 -0
  64. package/src/precompiles/ArbGasInfo.sol +121 -0
  65. package/src/precompiles/ArbInfo.sol +15 -0
  66. package/src/precompiles/ArbOwner.sol +65 -0
  67. package/src/precompiles/ArbOwnerPublic.sol +18 -0
  68. package/src/precompiles/ArbRetryableTx.sol +89 -0
  69. package/src/precompiles/ArbStatistics.sol +29 -0
  70. package/src/precompiles/ArbSys.sol +134 -0
  71. package/src/precompiles/ArbosActs.sol +41 -0
  72. package/src/precompiles/ArbosTest.sol +14 -0
  73. package/src/rollup/BridgeCreator.sol +120 -0
  74. package/src/rollup/IRollupCore.sol +152 -0
  75. package/src/rollup/IRollupLogic.sol +183 -0
  76. package/src/rollup/Node.sol +99 -0
  77. package/src/rollup/RollupAdminLogic.sol +322 -0
  78. package/src/rollup/RollupCore.sol +627 -0
  79. package/src/rollup/RollupCreator.sol +133 -0
  80. package/src/rollup/RollupEventBridge.sol +46 -0
  81. package/src/rollup/RollupLib.sol +135 -0
  82. package/src/rollup/RollupUserLogic.sol +712 -0
  83. package/src/rollup/ValidatorUtils.sol +243 -0
  84. package/src/rollup/ValidatorWallet.sol +76 -0
  85. package/src/rollup/ValidatorWalletCreator.sol +43 -0
  86. package/src/state/Deserialize.sol +321 -0
  87. package/src/state/GlobalState.sol +44 -0
  88. package/src/state/Instructions.sol +159 -0
  89. package/src/state/Machine.sol +65 -0
  90. package/src/state/MerkleProof.sol +99 -0
  91. package/src/state/Module.sol +33 -0
  92. package/src/state/ModuleMemory.sol +42 -0
  93. package/src/state/PcArray.sol +45 -0
  94. package/src/state/PcStack.sol +32 -0
  95. package/src/state/StackFrame.sol +63 -0
  96. package/src/state/Value.sol +65 -0
  97. package/src/state/ValueArray.sol +47 -0
  98. package/src/state/ValueStack.sol +39 -0
  99. package/src/test-helpers/CryptographyPrimitivesTester.sol +27 -0
  100. package/src/test-helpers/MessageTester.sol +34 -0
  101. package/src/test-helpers/ValueArrayTester.sol +34 -0
  102. package/test/contract/arbRollup.spec.ts +869 -0
  103. package/test/contract/common/challengeLib.ts +43 -0
  104. package/test/contract/common/globalStateLib.ts +17 -0
  105. package/test/contract/common/rolluplib.ts +259 -0
  106. package/test/contract/cryptographyPrimitives.spec.ts +82 -0
  107. package/test/contract/sequencerInboxForceInclude.spec.ts +516 -0
  108. package/test/contract/utils.ts +40 -0
  109. package/test/prover/hash-proofs.ts +75 -0
  110. package/test/prover/one-step-proof.ts +93 -0
  111. package/test/prover/proofs/.gitkeep +0 -0
  112. package/test/prover/value-arrays.ts +11 -0
  113. package/tsconfig.json +13 -0
@@ -0,0 +1,869 @@
1
+ /*
2
+ * Copyright 2019-2020, Offchain Labs, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /* eslint-env node, mocha */
18
+ import { ethers, run } from "hardhat";
19
+ import { Signer } from "@ethersproject/abstract-signer";
20
+ import { BigNumberish, BigNumber } from "@ethersproject/bignumber";
21
+ import { BytesLike, hexConcat, zeroPad } from "@ethersproject/bytes";
22
+ import { ContractTransaction } from "@ethersproject/contracts";
23
+ import { assert, expect } from "chai";
24
+ import {
25
+ BridgeCreator__factory,
26
+ ChallengeManager,
27
+ ChallengeManager__factory,
28
+ OneStepProofEntry__factory,
29
+ OneStepProver0__factory,
30
+ OneStepProverHostIo__factory,
31
+ OneStepProverMath__factory,
32
+ OneStepProverMemory__factory,
33
+ RollupAdminLogic,
34
+ RollupAdminLogic__factory,
35
+ RollupCreator__factory,
36
+ RollupUserLogic,
37
+ RollupUserLogic__factory,
38
+ SequencerInbox,
39
+ SequencerInbox__factory,
40
+ } from "../../build/types";
41
+ import { initializeAccounts } from "./utils";
42
+
43
+ import { Node, RollupContract, forceCreateNode, assertionEquals } from "./common/rolluplib";
44
+ import { RollupLib } from "../../build/types/RollupUserLogic";
45
+ type AssertionStruct = RollupLib.AssertionStruct;
46
+ type ExecutionStateStruct = RollupLib.ExecutionStateStruct;
47
+ import { keccak256 } from "ethers/lib/utils";
48
+ import { ConfigStruct, RollupCreatedEvent } from "../../build/types/RollupCreator";
49
+ import { constants } from "ethers";
50
+ import { blockStateHash, MachineStatus } from "./common/challengeLib";
51
+ import * as globalStateLib from "./common/globalStateLib";
52
+ import { RollupChallengeStartedEvent } from "../../build/types/IRollupCore";
53
+
54
+ const zerobytes32 = ethers.constants.HashZero;
55
+ const stakeRequirement = 10;
56
+ const stakeToken = ethers.constants.AddressZero;
57
+ const confirmationPeriodBlocks = 100;
58
+ const minimumAssertionPeriod = 75;
59
+ const ZERO_ADDR = ethers.constants.AddressZero;
60
+ const extraChallengeTimeBlocks = 20;
61
+ const wasmModuleRoot = "0x9900000000000000000000000000000000000000000000000000000000000010";
62
+
63
+ // let rollup: RollupContract
64
+ let rollup: RollupContract;
65
+ let rollupUser: RollupUserLogic;
66
+ let rollupAdmin: RollupAdminLogic;
67
+ let accounts: Signer[];
68
+ let validators: Signer[];
69
+ let sequencerInbox: SequencerInbox;
70
+ let admin: Signer;
71
+ let sequencer: Signer;
72
+ let challengeManager: ChallengeManager;
73
+
74
+ async function getDefaultConfig(
75
+ _confirmPeriodBlocks = confirmationPeriodBlocks
76
+ ): Promise<ConfigStruct> {
77
+ return {
78
+ baseStake: stakeRequirement,
79
+ chainId: stakeToken,
80
+ confirmPeriodBlocks: _confirmPeriodBlocks,
81
+ extraChallengeTimeBlocks: extraChallengeTimeBlocks,
82
+ owner: await accounts[0].getAddress(),
83
+ sequencerInboxMaxTimeVariation: {
84
+ delayBlocks: (60 * 60 * 24) / 15,
85
+ futureBlocks: 12,
86
+ delaySeconds: 60 * 60 * 24,
87
+ futureSeconds: 60 * 60,
88
+ },
89
+ stakeToken: stakeToken,
90
+ wasmModuleRoot: wasmModuleRoot,
91
+ loserStakeEscrow: ZERO_ADDR,
92
+ };
93
+ }
94
+
95
+ const setup = async () => {
96
+ const accounts = await initializeAccounts();
97
+ admin = accounts[0];
98
+
99
+ const user = accounts[1];
100
+
101
+ const val1 = accounts[2];
102
+ const val2 = accounts[3];
103
+ const val3 = accounts[4];
104
+ const val4 = accounts[5];
105
+ sequencer = accounts[6];
106
+
107
+ const oneStep0Fac = (await ethers.getContractFactory(
108
+ "OneStepProver0"
109
+ )) as OneStepProver0__factory;
110
+ const oneStep0 = await oneStep0Fac.deploy();
111
+ const oneStepMemoryFac = (await ethers.getContractFactory(
112
+ "OneStepProverMemory"
113
+ )) as OneStepProverMemory__factory;
114
+ const oneStepMemory = await oneStepMemoryFac.deploy();
115
+ const oneStepMathFac = (await ethers.getContractFactory(
116
+ "OneStepProverMath"
117
+ )) as OneStepProverMath__factory;
118
+ const oneStepMath = await oneStepMathFac.deploy();
119
+ const oneStepHostIoFac = (await ethers.getContractFactory(
120
+ "OneStepProverHostIo"
121
+ )) as OneStepProverHostIo__factory;
122
+ const oneStepHostIo = await oneStepHostIoFac.deploy();
123
+
124
+ const oneStepProofEntryFac = (await ethers.getContractFactory(
125
+ "OneStepProofEntry"
126
+ )) as OneStepProofEntry__factory;
127
+ const oneStepProofEntry = await oneStepProofEntryFac.deploy(
128
+ oneStep0.address,
129
+ oneStepMemory.address,
130
+ oneStepMath.address,
131
+ oneStepHostIo.address
132
+ );
133
+
134
+ const challengeManagerTemplateFac = (await ethers.getContractFactory(
135
+ "ChallengeManager"
136
+ )) as ChallengeManager__factory;
137
+ const challengeManagerTemplate = await challengeManagerTemplateFac.deploy();
138
+
139
+ const rollupAdminLogicFac = (await ethers.getContractFactory(
140
+ "RollupAdminLogic"
141
+ )) as RollupAdminLogic__factory;
142
+ const rollupAdminLogicTemplate = await rollupAdminLogicFac.deploy();
143
+
144
+ const rollupUserLogicFac = (await ethers.getContractFactory(
145
+ "RollupUserLogic"
146
+ )) as RollupUserLogic__factory;
147
+ const rollupUserLogicTemplate = await rollupUserLogicFac.deploy();
148
+
149
+ const bridgeCreatorFac = (await ethers.getContractFactory(
150
+ "BridgeCreator"
151
+ )) as BridgeCreator__factory;
152
+ const bridgeCreator = await bridgeCreatorFac.deploy();
153
+
154
+ const rollupCreatorFac = (await ethers.getContractFactory(
155
+ "RollupCreator"
156
+ )) as RollupCreator__factory;
157
+ const rollupCreator = await rollupCreatorFac.deploy();
158
+
159
+ await rollupCreator.setTemplates(
160
+ bridgeCreator.address,
161
+ oneStepProofEntry.address,
162
+ challengeManagerTemplate.address,
163
+ rollupAdminLogicTemplate.address,
164
+ rollupUserLogicTemplate.address
165
+ );
166
+
167
+ const nonce = await rollupCreator.signer.provider!.getTransactionCount(rollupCreator.address);
168
+ const expectedRollupAddress = ethers.utils.getContractAddress({
169
+ from: rollupCreator.address,
170
+ nonce: nonce + 2,
171
+ });
172
+
173
+ const response = await rollupCreator.createRollup(
174
+ await getDefaultConfig(),
175
+ expectedRollupAddress
176
+ );
177
+ const rec = await response.wait();
178
+
179
+ const rollupCreatedEvent = rollupCreator.interface.parseLog(rec.logs[rec.logs.length - 1])
180
+ .args as RollupCreatedEvent["args"];
181
+
182
+ const rollupAdmin = rollupAdminLogicFac
183
+ .attach(expectedRollupAddress)
184
+ .connect(rollupCreator.signer);
185
+ const rollupUser = rollupUserLogicFac.attach(expectedRollupAddress).connect(user);
186
+
187
+ await rollupAdmin.setValidator(
188
+ [await val1.getAddress(), await val2.getAddress(), await val3.getAddress()],
189
+ [true, true, true]
190
+ );
191
+
192
+ await rollupAdmin.setIsBatchPoster(await sequencer.getAddress(), true);
193
+
194
+ sequencerInbox = ((await ethers.getContractFactory(
195
+ "SequencerInbox"
196
+ )) as SequencerInbox__factory).attach(rollupCreatedEvent.sequencerInbox);
197
+
198
+ challengeManager = ((await ethers.getContractFactory(
199
+ "ChallengeManager"
200
+ )) as ChallengeManager__factory).attach(await rollupUser.challengeManager());
201
+
202
+ return {
203
+ admin,
204
+ user,
205
+
206
+ rollupAdmin,
207
+ rollupUser,
208
+
209
+ validators: [val1, val2, val3, val4],
210
+
211
+ rollupAdminLogicTemplate,
212
+ rollupUserLogicTemplate,
213
+ blockChallengeFactory: challengeManagerTemplateFac,
214
+ rollupEventBridge: await rollupAdmin.rollupEventBridge(),
215
+ outbox: await rollupAdmin.outbox(),
216
+ sequencerInbox: rollupCreatedEvent.sequencerInbox,
217
+ delayedBridge: rollupCreatedEvent.delayedBridge,
218
+ };
219
+ };
220
+
221
+ async function tryAdvanceChain(blocks: number): Promise<void> {
222
+ try {
223
+ for (let i = 0; i < blocks; i++) {
224
+ await ethers.provider.send("evm_mine", []);
225
+ }
226
+ } catch (e) {
227
+ // EVM mine failed. Try advancing the chain by sending txes if the node
228
+ // is in dev mode and mints blocks when txes are sent
229
+ for (let i = 0; i < blocks; i++) {
230
+ const tx = await accounts[0].sendTransaction({
231
+ value: 0,
232
+ to: await accounts[0].getAddress(),
233
+ });
234
+ await tx.wait();
235
+ }
236
+ }
237
+ }
238
+
239
+ async function advancePastAssertion(blockProposed: number): Promise<void> {
240
+ await tryAdvanceChain(blockProposed + confirmationPeriodBlocks);
241
+ }
242
+
243
+ function newRandomExecutionState() {
244
+ const blockHash = keccak256(ethers.utils.randomBytes(32));
245
+ const sendRoot = keccak256(ethers.utils.randomBytes(32));
246
+ const machineStatus = 1;
247
+
248
+ return newExecutionState(blockHash, sendRoot, 1, 0, machineStatus);
249
+ }
250
+
251
+ function newExecutionState(
252
+ blockHash: string,
253
+ sendRoot: string,
254
+ inboxPosition: BigNumberish,
255
+ positionInMessage: BigNumberish,
256
+ machineStatus: BigNumberish
257
+ ): ExecutionStateStruct {
258
+ return {
259
+ globalState: {
260
+ bytes32Vals: [blockHash, sendRoot],
261
+ u64Vals: [inboxPosition, positionInMessage],
262
+ },
263
+ machineStatus,
264
+ };
265
+ }
266
+
267
+ function newRandomAssertion(prevExecutionState: ExecutionStateStruct): AssertionStruct {
268
+ return {
269
+ beforeState: prevExecutionState,
270
+ afterState: newRandomExecutionState(),
271
+ numBlocks: 10,
272
+ };
273
+ }
274
+
275
+ async function makeSimpleNode(
276
+ rollup: RollupContract,
277
+ sequencerInbox: SequencerInbox,
278
+ parentNode: {
279
+ assertion: { afterState: ExecutionStateStruct };
280
+ nodeNum: number;
281
+ nodeHash: BytesLike;
282
+ inboxMaxCount: BigNumber;
283
+ },
284
+ siblingNode?: Node,
285
+ prevNode?: Node,
286
+ stakeToAdd?: BigNumber
287
+ ): Promise<{ tx: ContractTransaction; node: Node }> {
288
+ const staker = await rollup.rollup.getStaker(await rollup.rollup.signer.getAddress());
289
+
290
+ const assertion = newRandomAssertion(parentNode.assertion.afterState);
291
+ const { tx, node, expectedNewNodeHash } = await rollup.stakeOnNewNode(
292
+ sequencerInbox,
293
+ parentNode,
294
+ assertion,
295
+ siblingNode,
296
+ stakeToAdd
297
+ );
298
+
299
+ expect(assertionEquals(assertion, node.assertion), "unexpected assertion").to.be.true;
300
+ assert.equal(node.nodeNum, (prevNode || siblingNode || parentNode).nodeNum + 1);
301
+ assert.equal(node.nodeHash, expectedNewNodeHash);
302
+
303
+ if (stakeToAdd) {
304
+ const stakerAfter = await rollup.rollup.getStaker(await rollup.rollup.signer.getAddress());
305
+ expect(stakerAfter.latestStakedNode.toNumber()).to.eq(node.nodeNum);
306
+ expect(stakerAfter.amountStaked.toString()).to.eq(
307
+ staker.amountStaked.add(stakeToAdd).toString()
308
+ );
309
+ }
310
+ return { tx, node };
311
+ }
312
+
313
+ const makeSends = (count: number, batchStart = 0) => {
314
+ return [...Array(count)].map((_, i) =>
315
+ hexConcat([[0], zeroPad([i + batchStart], 32), zeroPad([0], 32), zeroPad([1], 32)])
316
+ );
317
+ };
318
+
319
+ let prevNode: Node;
320
+ const prevNodes: Node[] = [];
321
+
322
+ function updatePrevNode(node: Node) {
323
+ prevNode = node;
324
+ prevNodes.push(node);
325
+ }
326
+
327
+ describe("ArbRollup", () => {
328
+ it("should deploy contracts", async function () {
329
+ accounts = await initializeAccounts();
330
+
331
+ await run("deploy", { tags: "test" });
332
+ });
333
+
334
+ it("should initialize", async function () {
335
+ const {
336
+ rollupAdmin: rollupAdminContract,
337
+ rollupUser: rollupUserContract,
338
+ user: userI,
339
+ admin: adminI,
340
+ validators: validatorsI,
341
+ } = await setup();
342
+ rollupAdmin = rollupAdminContract;
343
+ rollupUser = rollupUserContract;
344
+ admin = adminI;
345
+ validators = validatorsI;
346
+ rollup = new RollupContract(rollupUser.connect(validators[0]));
347
+ });
348
+
349
+ it("should only initialize once", async function () {
350
+ await expect(
351
+ rollupAdmin.initialize(await getDefaultConfig(), {
352
+ challengeManager: constants.AddressZero,
353
+ delayedBridge: constants.AddressZero,
354
+ outbox: constants.AddressZero,
355
+ rollupAdminLogic: constants.AddressZero,
356
+ rollupEventBridge: constants.AddressZero,
357
+ rollupUserLogic: constants.AddressZero,
358
+ sequencerInbox: constants.AddressZero,
359
+ })
360
+ ).to.be.revertedWith("Initializable: contract is already initialized");
361
+ });
362
+
363
+ it("should place stake on new node", async function () {
364
+ await tryAdvanceChain(minimumAssertionPeriod);
365
+
366
+ const initNode: {
367
+ assertion: { afterState: ExecutionStateStruct };
368
+ nodeNum: number;
369
+ nodeHash: BytesLike;
370
+ inboxMaxCount: BigNumber;
371
+ } = {
372
+ assertion: {
373
+ afterState: {
374
+ globalState: {
375
+ bytes32Vals: [zerobytes32, zerobytes32],
376
+ u64Vals: [0, 0],
377
+ },
378
+ machineStatus: MachineStatus.FINISHED,
379
+ },
380
+ },
381
+ inboxMaxCount: BigNumber.from(1),
382
+ nodeHash: zerobytes32,
383
+ nodeNum: 0,
384
+ };
385
+
386
+ const stake = await rollup.currentRequiredStake();
387
+ const { node } = await makeSimpleNode(
388
+ rollup,
389
+ sequencerInbox,
390
+ initNode,
391
+ undefined,
392
+ undefined,
393
+ stake
394
+ );
395
+ updatePrevNode(node);
396
+ });
397
+
398
+ it("should let a new staker place on existing node", async function () {
399
+ const stake = await rollup.currentRequiredStake();
400
+ await rollupUser
401
+ .connect(validators[2])
402
+ .newStakeOnExistingNode(1, prevNode.nodeHash, { value: stake });
403
+ });
404
+
405
+ it("should move stake to a new node", async function () {
406
+ await tryAdvanceChain(minimumAssertionPeriod);
407
+ const { node } = await makeSimpleNode(rollup, sequencerInbox, prevNode);
408
+ updatePrevNode(node);
409
+ });
410
+
411
+ it("should let the second staker place on the new node", async function () {
412
+ await rollup.connect(validators[2]).stakeOnExistingNode(2, prevNode.nodeHash);
413
+ });
414
+
415
+ it("should confirm node", async function () {
416
+ await tryAdvanceChain(confirmationPeriodBlocks * 2);
417
+
418
+ await rollup.confirmNextNode(prevNodes[0]);
419
+ });
420
+
421
+ it("should confirm next node", async function () {
422
+ await tryAdvanceChain(minimumAssertionPeriod);
423
+ await rollup.confirmNextNode(prevNodes[1]);
424
+ });
425
+
426
+ let challengedNode: Node;
427
+ let validNode: Node;
428
+ it("should let the first staker make another node", async function () {
429
+ await tryAdvanceChain(minimumAssertionPeriod);
430
+ const { node } = await makeSimpleNode(rollup, sequencerInbox, prevNode);
431
+ challengedNode = node;
432
+ validNode = node;
433
+ });
434
+
435
+ let challengerNode: Node;
436
+ it("should let the second staker make a conflicting node", async function () {
437
+ await tryAdvanceChain(minimumAssertionPeriod);
438
+ const { node } = await makeSimpleNode(
439
+ rollup.connect(validators[2]),
440
+ sequencerInbox,
441
+ prevNode,
442
+ validNode
443
+ );
444
+ challengerNode = node;
445
+ });
446
+
447
+ it("should fail to confirm first staker node", async function () {
448
+ await advancePastAssertion(challengedNode.proposedBlock);
449
+ await expect(rollup.confirmNextNode(validNode)).to.be.revertedWith("NOT_ALL_STAKED");
450
+ });
451
+
452
+ let challengeIndex: number;
453
+ it("should initiate a challenge", async function () {
454
+ const tx = rollup.createChallenge(
455
+ await validators[0].getAddress(),
456
+ await validators[2].getAddress(),
457
+ challengedNode,
458
+ challengerNode
459
+ );
460
+ const receipt = await (await tx).wait();
461
+ const ev = rollup.rollup.interface.parseLog(receipt.logs![receipt.logs!.length - 1]);
462
+ expect(ev.name).to.equal("RollupChallengeStarted");
463
+
464
+ const parsedEv = ev.args as RollupChallengeStartedEvent["args"];
465
+ challengeIndex = parsedEv.challengeIndex.toNumber();
466
+ });
467
+
468
+ it("should make a new node", async function () {
469
+ const { node } = await makeSimpleNode(
470
+ rollup,
471
+ sequencerInbox,
472
+ validNode,
473
+ undefined,
474
+ challengerNode
475
+ );
476
+ challengedNode = node;
477
+ });
478
+
479
+ it("new staker should make a conflicting node", async function () {
480
+ const stake = await rollup.currentRequiredStake();
481
+ await rollup.rollup
482
+ .connect(validators[1])
483
+ .newStakeOnExistingNode(3, validNode.nodeHash, { value: stake.add(50) });
484
+
485
+ const { node } = await makeSimpleNode(
486
+ rollup.connect(validators[1]),
487
+ sequencerInbox,
488
+ validNode,
489
+ challengedNode
490
+ );
491
+ challengerNode = node;
492
+ });
493
+
494
+ it("asserter should win via timeout", async function () {
495
+ await advancePastAssertion(challengedNode.proposedBlock);
496
+ await challengeManager.connect(validators[0]).timeout(challengeIndex);
497
+ });
498
+
499
+ it("confirm first staker node", async function () {
500
+ await rollup.confirmNextNode(validNode);
501
+ });
502
+
503
+ it("should reject out of order second node", async function () {
504
+ await rollup.rejectNextNode(stakeToken);
505
+ });
506
+
507
+ it("should initiate another challenge", async function () {
508
+ const tx = rollup.createChallenge(
509
+ await validators[0].getAddress(),
510
+ await validators[1].getAddress(),
511
+ challengedNode,
512
+ challengerNode
513
+ );
514
+ const receipt = await (await tx).wait();
515
+ const ev = rollup.rollup.interface.parseLog(receipt.logs![receipt.logs!.length - 1]);
516
+ expect(ev.name).to.equal("RollupChallengeStarted");
517
+ const parsedEv = ev.args as RollupChallengeStartedEvent["args"];
518
+ challengeIndex = parsedEv.challengeIndex.toNumber();
519
+
520
+ await expect(
521
+ rollup.rollup.completeChallenge(
522
+ challengeIndex,
523
+ await sequencer.getAddress(),
524
+ await validators[3].getAddress()
525
+ )
526
+ ).to.be.revertedWith("WRONG_SENDER");
527
+ });
528
+
529
+ it("challenger should reply in challenge", async function () {
530
+ const seg0 = blockStateHash(
531
+ BigNumber.from(challengerNode.assertion.beforeState.machineStatus),
532
+ globalStateLib.hash(challengerNode.assertion.beforeState.globalState)
533
+ );
534
+
535
+ const seg1 = blockStateHash(
536
+ BigNumber.from(challengedNode.assertion.afterState.machineStatus),
537
+ globalStateLib.hash(challengedNode.assertion.afterState.globalState)
538
+ );
539
+ await challengeManager.connect(validators[1]).bisectExecution(
540
+ challengeIndex,
541
+ {
542
+ challengePosition: BigNumber.from(0),
543
+ oldSegments: [seg0, seg1],
544
+ oldSegmentsLength: BigNumber.from(challengedNode.assertion.numBlocks),
545
+ oldSegmentsStart: 0,
546
+ },
547
+ [
548
+ seg0,
549
+ zerobytes32,
550
+ zerobytes32,
551
+ zerobytes32,
552
+ zerobytes32,
553
+ zerobytes32,
554
+ zerobytes32,
555
+ zerobytes32,
556
+ zerobytes32,
557
+ zerobytes32,
558
+ zerobytes32,
559
+ ]
560
+ );
561
+ });
562
+
563
+ it("challenger should win via timeout", async function () {
564
+ await advancePastAssertion(challengedNode.proposedBlock);
565
+ await challengeManager.timeout(challengeIndex);
566
+ });
567
+
568
+ it("should reject out of order second node", async function () {
569
+ await rollup.rejectNextNode(await validators[1].getAddress());
570
+ });
571
+
572
+ it("confirm next node", async function () {
573
+ await tryAdvanceChain(confirmationPeriodBlocks);
574
+ await rollup.confirmNextNode(challengerNode);
575
+ });
576
+
577
+ it("should add and remove stakes correctly", async function () {
578
+ /*
579
+ RollupUser functions that alter stake and their respective Core logic
580
+
581
+ user: newStake
582
+ core: createNewStake
583
+
584
+ user: addToDeposit
585
+ core: increaseStakeBy
586
+
587
+ user: reduceDeposit
588
+ core: reduceStakeTo
589
+
590
+ user: returnOldDeposit
591
+ core: withdrawStaker
592
+
593
+ user: withdrawStakerFunds
594
+ core: withdrawFunds
595
+ */
596
+
597
+ const initialStake = await rollup.rollup.amountStaked(await validators[1].getAddress());
598
+
599
+ await rollup.connect(validators[1]).reduceDeposit(initialStake);
600
+
601
+ await expect(
602
+ rollup.connect(validators[1]).reduceDeposit(initialStake.add(1))
603
+ ).to.be.revertedWith("TOO_LITTLE_STAKE");
604
+
605
+ await rollup
606
+ .connect(validators[1])
607
+ .addToDeposit(await validators[1].getAddress(), { value: 5 });
608
+
609
+ await rollup.connect(validators[1]).reduceDeposit(5);
610
+
611
+ const prevBalance = await validators[1].getBalance();
612
+ const prevWithdrawablefunds = await rollup.rollup.withdrawableFunds(
613
+ await validators[1].getAddress()
614
+ );
615
+
616
+ const tx = await rollup.rollup
617
+ .connect(validators[1])
618
+ .withdrawStakerFunds(await validators[1].getAddress());
619
+ const receipt = await tx.wait();
620
+ const gasPaid = receipt.gasUsed.mul(receipt.effectiveGasPrice);
621
+
622
+ const postBalance = await validators[1].getBalance();
623
+ const postWithdrawablefunds = await rollup.rollup.withdrawableFunds(
624
+ await validators[1].getAddress()
625
+ );
626
+
627
+ expect(postWithdrawablefunds).to.equal(0);
628
+ expect(postBalance.add(gasPaid)).to.equal(prevBalance.add(prevWithdrawablefunds));
629
+
630
+ // this gets deposit and removes staker
631
+ await rollup.rollup.connect(validators[1]).returnOldDeposit(await validators[1].getAddress());
632
+ // all stake is now removed
633
+ });
634
+
635
+ it("should allow removing zombies", async function () {
636
+ const zombieCount = (await rollup.rollup.connect(validators[2]).zombieCount()).toNumber();
637
+ for (let i = 0; i < zombieCount; i++) {
638
+ await rollup.rollup.connect(validators[2]).removeZombie(0, 1024);
639
+ }
640
+ });
641
+
642
+ it("should pause the contracts then resume", async function () {
643
+ const prevIsPaused = await rollup.rollup.paused();
644
+ expect(prevIsPaused).to.equal(false);
645
+
646
+ await rollupAdmin.pause();
647
+
648
+ const postIsPaused = await rollup.rollup.paused();
649
+ expect(postIsPaused).to.equal(true);
650
+
651
+ await expect(
652
+ rollup.connect(validators[1]).addToDeposit(await validators[1].getAddress(), { value: 5 })
653
+ ).to.be.revertedWith("Pausable: paused");
654
+
655
+ await rollupAdmin.resume();
656
+ });
657
+
658
+ it("should allow admin to alter rollup while paused", async function () {
659
+ const prevLatestConfirmed = await rollup.rollup.latestConfirmed();
660
+ expect(prevLatestConfirmed.toNumber()).to.equal(6);
661
+ // prevNode is prevLatestConfirmed
662
+ prevNode = challengerNode;
663
+
664
+ const stake = await rollup.currentRequiredStake();
665
+
666
+ const { node: node1 } = await makeSimpleNode(
667
+ rollup,
668
+ sequencerInbox,
669
+ prevNode,
670
+ undefined,
671
+ undefined,
672
+ stake
673
+ );
674
+ const node1Num = await rollup.rollup.latestNodeCreated();
675
+ expect(node1Num.toNumber(), "node1num").to.eq(node1.nodeNum);
676
+
677
+ await tryAdvanceChain(minimumAssertionPeriod);
678
+
679
+ const { node: node2 } = await makeSimpleNode(
680
+ rollup.connect(validators[2]),
681
+ sequencerInbox,
682
+ prevNode,
683
+ node1,
684
+ undefined,
685
+ stake
686
+ );
687
+ const node2Num = await rollup.rollup.latestNodeCreated();
688
+ expect(node2Num.toNumber(), "node2num").to.eq(node2.nodeNum);
689
+
690
+ const tx = await rollup.createChallenge(
691
+ await validators[0].getAddress(),
692
+ await validators[2].getAddress(),
693
+ node1,
694
+ node2
695
+ );
696
+ const receipt = await tx.wait();
697
+ const ev = rollup.rollup.interface.parseLog(receipt.logs![receipt.logs!.length - 1]);
698
+ expect(ev.name).to.equal("RollupChallengeStarted");
699
+ const parsedEv = ev.args as RollupChallengeStartedEvent["args"];
700
+ challengeIndex = parsedEv.challengeIndex.toNumber();
701
+
702
+ expect(await challengeManager.currentResponder(challengeIndex), "turn challenger").to.eq(
703
+ await validators[2].getAddress()
704
+ );
705
+
706
+ await expect(
707
+ rollupAdmin.forceResolveChallenge(
708
+ [await validators[0].getAddress()],
709
+ [await validators[2].getAddress()]
710
+ ),
711
+ "force resolve"
712
+ ).to.be.revertedWith("Pausable: not paused");
713
+
714
+ await expect(
715
+ rollup.createChallenge(
716
+ await validators[0].getAddress(),
717
+ await validators[2].getAddress(),
718
+ node1,
719
+ node2
720
+ ),
721
+ "create challenge"
722
+ ).to.be.revertedWith("IN_CHAL");
723
+
724
+ await rollupAdmin.pause();
725
+
726
+ await rollupAdmin.forceResolveChallenge(
727
+ [await validators[0].getAddress()],
728
+ [await validators[2].getAddress()]
729
+ );
730
+
731
+ // challenge should have been destroyed
732
+ expect(await challengeManager.currentResponder(challengeIndex), "turn reset").to.equal(
733
+ constants.AddressZero
734
+ );
735
+
736
+ const challengeA = await rollupAdmin.currentChallenge(await validators[0].getAddress());
737
+ const challengeB = await rollupAdmin.currentChallenge(await validators[2].getAddress());
738
+
739
+ expect(challengeA).to.equal(ZERO_ADDR);
740
+ expect(challengeB).to.equal(ZERO_ADDR);
741
+
742
+ await rollupAdmin.forceRefundStaker([
743
+ await validators[0].getAddress(),
744
+ await validators[2].getAddress(),
745
+ ]);
746
+
747
+ const adminAssertion = newRandomAssertion(prevNode.assertion.afterState);
748
+ const { node: forceCreatedNode1 } = await forceCreateNode(
749
+ rollupAdmin,
750
+ sequencerInbox,
751
+ prevNode,
752
+ adminAssertion,
753
+ node2
754
+ );
755
+ expect(assertionEquals(forceCreatedNode1.assertion, adminAssertion), "assertion error").to.be
756
+ .true;
757
+
758
+ const adminNodeNum = await rollup.rollup.latestNodeCreated();
759
+ const midLatestConfirmed = await rollup.rollup.latestConfirmed();
760
+ expect(midLatestConfirmed.toNumber()).to.equal(6);
761
+
762
+ expect(adminNodeNum.toNumber()).to.equal(node2Num.toNumber() + 1);
763
+
764
+ const adminAssertion2 = newRandomAssertion(prevNode.assertion.afterState);
765
+ const { node: forceCreatedNode2 } = await forceCreateNode(
766
+ rollupAdmin,
767
+ sequencerInbox,
768
+ prevNode,
769
+ adminAssertion2,
770
+ forceCreatedNode1
771
+ );
772
+
773
+ const postLatestCreated = await rollup.rollup.latestNodeCreated();
774
+
775
+ await rollupAdmin.forceConfirmNode(
776
+ adminNodeNum,
777
+ adminAssertion.afterState.globalState.bytes32Vals[0],
778
+ adminAssertion.afterState.globalState.bytes32Vals[1]
779
+ );
780
+
781
+ const postLatestConfirmed = await rollup.rollup.latestConfirmed();
782
+ expect(postLatestCreated).to.equal(adminNodeNum.add(1));
783
+ expect(postLatestConfirmed).to.equal(adminNodeNum);
784
+
785
+ await rollupAdmin.resume();
786
+
787
+ // should create node after resuming
788
+
789
+ prevNode = forceCreatedNode1;
790
+
791
+ await tryAdvanceChain(minimumAssertionPeriod);
792
+
793
+ await expect(
794
+ makeSimpleNode(
795
+ rollup.connect(validators[2]),
796
+ sequencerInbox,
797
+ prevNode,
798
+ undefined,
799
+ forceCreatedNode2,
800
+ stake
801
+ )
802
+ ).to.be.revertedWith("STAKER_IS_ZOMBIE");
803
+
804
+ await expect(
805
+ makeSimpleNode(rollup.connect(validators[2]), sequencerInbox, prevNode)
806
+ ).to.be.revertedWith("NOT_STAKED");
807
+
808
+ await rollup.rollup.connect(validators[2]).removeOldZombies(0);
809
+
810
+ await makeSimpleNode(
811
+ rollup.connect(validators[2]),
812
+ sequencerInbox,
813
+ prevNode,
814
+ undefined,
815
+ forceCreatedNode2,
816
+ stake
817
+ );
818
+ });
819
+
820
+ it("should initialize a fresh rollup", async function () {
821
+ const {
822
+ rollupAdmin: rollupAdminContract,
823
+ rollupUser: rollupUserContract,
824
+ user: userI,
825
+ admin: adminI,
826
+ validators: validatorsI,
827
+ } = await setup();
828
+ rollupAdmin = rollupAdminContract;
829
+ rollupUser = rollupUserContract;
830
+ admin = adminI;
831
+ validators = validatorsI;
832
+ rollup = new RollupContract(rollupUser.connect(validators[0]));
833
+ });
834
+
835
+ it("should stake on initial node again", async function () {
836
+ await tryAdvanceChain(minimumAssertionPeriod);
837
+
838
+ const initNode: {
839
+ assertion: { afterState: ExecutionStateStruct };
840
+ nodeNum: number;
841
+ nodeHash: BytesLike;
842
+ inboxMaxCount: BigNumber;
843
+ } = {
844
+ assertion: {
845
+ afterState: {
846
+ globalState: {
847
+ bytes32Vals: [zerobytes32, zerobytes32],
848
+ u64Vals: [0, 0],
849
+ },
850
+ machineStatus: MachineStatus.FINISHED,
851
+ },
852
+ },
853
+ inboxMaxCount: BigNumber.from(1),
854
+ nodeHash: zerobytes32,
855
+ nodeNum: 0,
856
+ };
857
+
858
+ const stake = await rollup.currentRequiredStake();
859
+ const { node } = await makeSimpleNode(
860
+ rollup,
861
+ sequencerInbox,
862
+ initNode,
863
+ undefined,
864
+ undefined,
865
+ stake
866
+ );
867
+ updatePrevNode(node);
868
+ });
869
+ });