@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,516 @@
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
+
19
+ import { ethers, network } from 'hardhat'
20
+ import { BigNumber } from '@ethersproject/bignumber'
21
+ import { Block, TransactionReceipt } from '@ethersproject/providers'
22
+ import { expect } from 'chai'
23
+ import {
24
+ Bridge,
25
+ Bridge__factory,
26
+ Inbox,
27
+ Inbox__factory,
28
+ MessageTester,
29
+ SequencerInbox,
30
+ SequencerInbox__factory,
31
+ TransparentUpgradeableProxy__factory,
32
+ } from '../../build/types'
33
+ import { initializeAccounts } from './utils'
34
+ import { Event } from '@ethersproject/contracts'
35
+ import { Interface } from '@ethersproject/abi'
36
+ import {
37
+ BridgeInterface,
38
+ MessageDeliveredEvent,
39
+ } from '../../build/types/Bridge'
40
+ import { Signer } from 'ethers'
41
+
42
+ const mineBlocks = async (count: number, timeDiffPerBlock = 14) => {
43
+ const block = (await network.provider.send('eth_getBlockByNumber', [
44
+ 'latest',
45
+ false,
46
+ ])) as Block
47
+ let timestamp = BigNumber.from(block.timestamp).toNumber()
48
+ for (let i = 0; i < count; i++) {
49
+ timestamp = timestamp + timeDiffPerBlock
50
+ await network.provider.send('evm_mine', [timestamp])
51
+ }
52
+ }
53
+
54
+ describe('SequencerInboxForceInclude', async () => {
55
+ const findMatchingLogs = <TInterface extends Interface, TEvent extends Event>(
56
+ receipt: TransactionReceipt,
57
+ iFace: TInterface,
58
+ eventTopicGen: (i: TInterface) => string,
59
+ ): TEvent['args'][] => {
60
+ const logs = receipt.logs.filter(
61
+ (log) => log.topics[0] === eventTopicGen(iFace),
62
+ )
63
+ return logs.map((l) => iFace.parseLog(l).args as TEvent['args'])
64
+ }
65
+
66
+ const getMessageDeliveredEvents = (receipt: TransactionReceipt) => {
67
+ const bridgeInterface = Bridge__factory.createInterface()
68
+ return findMatchingLogs<BridgeInterface, MessageDeliveredEvent>(
69
+ receipt,
70
+ bridgeInterface,
71
+ (i) => i.getEventTopic(i.getEvent('MessageDelivered')),
72
+ )
73
+ }
74
+
75
+ const sendDelayedTx = async (
76
+ sender: Signer,
77
+ inbox: Inbox,
78
+ bridge: Bridge,
79
+ messageTester: MessageTester,
80
+ l2Gas: number,
81
+ l2GasPrice: number,
82
+ nonce: number,
83
+ destAddr: string,
84
+ amount: BigNumber,
85
+ data: string,
86
+ ) => {
87
+ const countBefore = (await bridge.functions.messageCount())[0].toNumber()
88
+ const sendUnsignedTx = await inbox
89
+ .connect(sender)
90
+ .sendUnsignedTransaction(l2Gas, l2GasPrice, nonce, destAddr, amount, data)
91
+ const sendUnsignedTxReceipt = await sendUnsignedTx.wait()
92
+
93
+ const countAfter = (await bridge.functions.messageCount())[0].toNumber()
94
+ expect(countAfter, 'Unexpected inbox count').to.eq(countBefore + 1)
95
+
96
+ const senderAddr = await sender.getAddress()
97
+
98
+ const messageDeliveredEvent = getMessageDeliveredEvents(
99
+ sendUnsignedTxReceipt,
100
+ )[0]
101
+ const l1BlockNumber = sendUnsignedTxReceipt.blockNumber
102
+ const blockL1 = await sender.provider!.getBlock(l1BlockNumber)
103
+ const baseFeeL1 = blockL1.baseFeePerGas!.toNumber()
104
+ const l1BlockTimestamp = blockL1.timestamp
105
+ const delayedAcc = await bridge.inboxAccs(countBefore)
106
+
107
+ // need to hex pad the address
108
+ const messageDataHash = ethers.utils.solidityKeccak256(
109
+ ['uint8', 'uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes'],
110
+ [
111
+ 0,
112
+ l2Gas,
113
+ l2GasPrice,
114
+ nonce,
115
+ ethers.utils.hexZeroPad(destAddr, 32),
116
+ amount,
117
+ data,
118
+ ],
119
+ )
120
+ expect(
121
+ messageDeliveredEvent.messageDataHash,
122
+ 'Incorrect messageDataHash',
123
+ ).to.eq(messageDataHash)
124
+
125
+ const messageHash = (
126
+ await messageTester.functions.messageHash(
127
+ 3,
128
+ senderAddr,
129
+ l1BlockNumber,
130
+ l1BlockTimestamp,
131
+ countBefore,
132
+ baseFeeL1,
133
+ messageDataHash,
134
+ )
135
+ )[0]
136
+
137
+ const prevAccumulator = messageDeliveredEvent.beforeInboxAcc
138
+ expect(prevAccumulator, 'Incorrect prev accumulator').to.eq(
139
+ countBefore === 0
140
+ ? ethers.utils.hexZeroPad('0x', 32)
141
+ : await bridge.inboxAccs(countBefore - 1),
142
+ )
143
+
144
+ const nextAcc = (
145
+ await messageTester.functions.accumulateInboxMessage(
146
+ prevAccumulator,
147
+ messageHash,
148
+ )
149
+ )[0]
150
+
151
+ expect(delayedAcc, 'Incorrect delayed acc').to.eq(nextAcc)
152
+
153
+ return {
154
+ baseFeeL1: baseFeeL1,
155
+ deliveredMessageEvent: messageDeliveredEvent,
156
+ l1BlockNumber,
157
+ l1BlockTimestamp,
158
+ delayedAcc,
159
+ l2Gas,
160
+ l2GasPrice,
161
+ nonce,
162
+ destAddr,
163
+ amount,
164
+ data,
165
+ senderAddr,
166
+ inboxAccountLength: countAfter,
167
+ }
168
+ }
169
+
170
+ const forceIncludeMessages = async (
171
+ sequencerInbox: SequencerInbox,
172
+ newTotalDelayedMessagesRead: number,
173
+ kind: number,
174
+ l1blockNumber: number,
175
+ l1Timestamp: number,
176
+ l1BaseFee: number,
177
+ senderAddr: string,
178
+ messageDataHash: string,
179
+ expectedErrorType?: string,
180
+ ) => {
181
+ const inboxLengthBefore = (await sequencerInbox.batchCount()).toNumber()
182
+
183
+ const forceInclusionTx = sequencerInbox.forceInclusion(
184
+ newTotalDelayedMessagesRead,
185
+ kind,
186
+ [l1blockNumber, l1Timestamp],
187
+ l1BaseFee,
188
+ senderAddr,
189
+ messageDataHash,
190
+ )
191
+ if (expectedErrorType) {
192
+ await expect(forceInclusionTx).to.be.revertedWith(`reverted with custom error '${expectedErrorType}()'`)
193
+ } else {
194
+ await (await forceInclusionTx).wait()
195
+
196
+ const totalDelayedMessagsReadAfter = (
197
+ await sequencerInbox.totalDelayedMessagesRead()
198
+ ).toNumber()
199
+ expect(
200
+ totalDelayedMessagsReadAfter,
201
+ 'Incorrect totalDelayedMessagesRead after.',
202
+ ).to.eq(newTotalDelayedMessagesRead)
203
+ const inboxLengthAfter = (await sequencerInbox.batchCount()).toNumber()
204
+ expect(
205
+ inboxLengthAfter - inboxLengthBefore,
206
+ 'Inbox not incremented',
207
+ ).to.eq(1)
208
+ }
209
+ }
210
+
211
+ const setupSequencerInbox = async (maxDelayBlocks = 10, maxDelayTime = 0) => {
212
+ const accounts = await initializeAccounts()
213
+ const admin = accounts[0]
214
+ const adminAddr = await admin.getAddress()
215
+ const user = accounts[1]
216
+ const dummyRollup = accounts[2]
217
+
218
+ const sequencerInboxFac = (await ethers.getContractFactory(
219
+ 'SequencerInbox',
220
+ )) as SequencerInbox__factory
221
+ const seqInboxTemplate = await sequencerInboxFac.deploy()
222
+ const inboxFac = (await ethers.getContractFactory(
223
+ 'Inbox',
224
+ )) as Inbox__factory
225
+ const inboxTemplate = await inboxFac.deploy()
226
+ const bridgeFac = (await ethers.getContractFactory(
227
+ 'Bridge',
228
+ )) as Bridge__factory
229
+ const bridgeTemplate = await bridgeFac.deploy()
230
+ const transparentUpgradeableProxyFac = (await ethers.getContractFactory(
231
+ 'TransparentUpgradeableProxy',
232
+ )) as TransparentUpgradeableProxy__factory
233
+
234
+ const bridgeProxy = await transparentUpgradeableProxyFac.deploy(
235
+ bridgeTemplate.address,
236
+ adminAddr,
237
+ '0x',
238
+ )
239
+ const sequencerInboxProxy = await transparentUpgradeableProxyFac.deploy(
240
+ seqInboxTemplate.address,
241
+ adminAddr,
242
+ '0x',
243
+ )
244
+ const inboxProxy = await transparentUpgradeableProxyFac.deploy(
245
+ inboxTemplate.address,
246
+ adminAddr,
247
+ '0x',
248
+ )
249
+
250
+ const bridge = await bridgeFac.attach(bridgeProxy.address).connect(user)
251
+ const sequencerInbox = await sequencerInboxFac
252
+ .attach(sequencerInboxProxy.address)
253
+ .connect(user)
254
+ const inbox = await inboxFac.attach(inboxProxy.address).connect(user)
255
+
256
+ await bridge.initialize()
257
+
258
+ await sequencerInbox.initialize(
259
+ bridgeProxy.address,
260
+ await dummyRollup.getAddress(),
261
+ {
262
+ delayBlocks: maxDelayBlocks,
263
+ delaySeconds: maxDelayTime,
264
+ futureBlocks: 10,
265
+ futureSeconds: 3000,
266
+ },
267
+ )
268
+ await inbox.initialize(bridgeProxy.address)
269
+
270
+ await bridge.setInbox(inbox.address, true)
271
+
272
+ const messageTester = (await (
273
+ await ethers.getContractFactory('MessageTester')
274
+ ).deploy()) as MessageTester
275
+
276
+ return {
277
+ user,
278
+ bridge: bridge,
279
+ inbox: inbox,
280
+ sequencerInbox: sequencerInbox,
281
+ messageTester,
282
+ }
283
+ }
284
+
285
+ it('can force-include', async () => {
286
+ const {
287
+ user,
288
+ inbox,
289
+ bridge,
290
+ messageTester,
291
+ sequencerInbox,
292
+ } = await setupSequencerInbox()
293
+
294
+ const delayedTx = await sendDelayedTx(
295
+ user,
296
+ inbox,
297
+ bridge,
298
+ messageTester,
299
+ 1000000,
300
+ 21000000000,
301
+ 0,
302
+ await user.getAddress(),
303
+ BigNumber.from(10),
304
+ '0x1010',
305
+ )
306
+ const maxTimeVariation = await sequencerInbox.maxTimeVariation()
307
+
308
+ await mineBlocks(maxTimeVariation.delayBlocks.toNumber())
309
+
310
+ await forceIncludeMessages(
311
+ sequencerInbox,
312
+ delayedTx.inboxAccountLength,
313
+ delayedTx.deliveredMessageEvent.kind,
314
+ delayedTx.l1BlockNumber,
315
+ delayedTx.l1BlockTimestamp,
316
+ delayedTx.baseFeeL1,
317
+ delayedTx.senderAddr,
318
+ delayedTx.deliveredMessageEvent.messageDataHash,
319
+ )
320
+ })
321
+
322
+ it('can force-include one after another', async () => {
323
+ const {
324
+ user,
325
+ inbox,
326
+ bridge,
327
+ messageTester,
328
+ sequencerInbox,
329
+ } = await setupSequencerInbox()
330
+ const delayedTx = await sendDelayedTx(
331
+ user,
332
+ inbox,
333
+ bridge,
334
+ messageTester,
335
+ 1000000,
336
+ 21000000000,
337
+ 0,
338
+ await user.getAddress(),
339
+ BigNumber.from(10),
340
+ '0x1010',
341
+ )
342
+
343
+ const delayedTx2 = await sendDelayedTx(
344
+ user,
345
+ inbox,
346
+ bridge,
347
+ messageTester,
348
+ 1000000,
349
+ 21000000000,
350
+ 1,
351
+ await user.getAddress(),
352
+ BigNumber.from(10),
353
+ '0xdeadface',
354
+ )
355
+
356
+ const maxTimeVariation = await sequencerInbox.maxTimeVariation()
357
+ await mineBlocks(maxTimeVariation.delayBlocks.toNumber())
358
+
359
+ await forceIncludeMessages(
360
+ sequencerInbox,
361
+ delayedTx.inboxAccountLength,
362
+ delayedTx.deliveredMessageEvent.kind,
363
+ delayedTx.l1BlockNumber,
364
+ delayedTx.l1BlockTimestamp,
365
+ delayedTx.baseFeeL1,
366
+ delayedTx.senderAddr,
367
+ delayedTx.deliveredMessageEvent.messageDataHash,
368
+ )
369
+ await forceIncludeMessages(
370
+ sequencerInbox,
371
+ delayedTx2.inboxAccountLength,
372
+ delayedTx2.deliveredMessageEvent.kind,
373
+ delayedTx2.l1BlockNumber,
374
+ delayedTx2.l1BlockTimestamp,
375
+ delayedTx2.baseFeeL1,
376
+ delayedTx2.senderAddr,
377
+ delayedTx2.deliveredMessageEvent.messageDataHash,
378
+ )
379
+ })
380
+
381
+ it('can force-include three at once', async () => {
382
+ const {
383
+ user,
384
+ inbox,
385
+ bridge,
386
+ messageTester,
387
+ sequencerInbox,
388
+ } = await setupSequencerInbox()
389
+ await sendDelayedTx(
390
+ user,
391
+ inbox,
392
+ bridge,
393
+ messageTester,
394
+ 1000000,
395
+ 21000000000,
396
+ 0,
397
+ await user.getAddress(),
398
+ BigNumber.from(10),
399
+ '0x1010',
400
+ )
401
+ await sendDelayedTx(
402
+ user,
403
+ inbox,
404
+ bridge,
405
+ messageTester,
406
+ 1000000,
407
+ 21000000000,
408
+ 1,
409
+ await user.getAddress(),
410
+ BigNumber.from(10),
411
+ '0x101010',
412
+ )
413
+ const delayedTx3 = await sendDelayedTx(
414
+ user,
415
+ inbox,
416
+ bridge,
417
+ messageTester,
418
+ 1000000,
419
+ 21000000000,
420
+ 10,
421
+ await user.getAddress(),
422
+ BigNumber.from(10),
423
+ '0x10101010',
424
+ )
425
+
426
+ const maxTimeVariation = await sequencerInbox.maxTimeVariation()
427
+ await mineBlocks(maxTimeVariation.delayBlocks.toNumber())
428
+
429
+ await forceIncludeMessages(
430
+ sequencerInbox,
431
+ delayedTx3.inboxAccountLength,
432
+ delayedTx3.deliveredMessageEvent.kind,
433
+ delayedTx3.l1BlockNumber,
434
+ delayedTx3.l1BlockTimestamp,
435
+ delayedTx3.baseFeeL1,
436
+ delayedTx3.senderAddr,
437
+ delayedTx3.deliveredMessageEvent.messageDataHash,
438
+ )
439
+ })
440
+
441
+ it('cannot include before max block delay', async () => {
442
+ const {
443
+ user,
444
+ inbox,
445
+ bridge,
446
+ messageTester,
447
+ sequencerInbox,
448
+ } = await setupSequencerInbox(10, 100)
449
+ const delayedTx = await sendDelayedTx(
450
+ user,
451
+ inbox,
452
+ bridge,
453
+ messageTester,
454
+ 1000000,
455
+ 21000000000,
456
+ 0,
457
+ await user.getAddress(),
458
+ BigNumber.from(10),
459
+ '0x1010',
460
+ )
461
+
462
+ const maxTimeVariation = await sequencerInbox.maxTimeVariation()
463
+ await mineBlocks(maxTimeVariation.delayBlocks.toNumber() - 1, 5)
464
+
465
+ await forceIncludeMessages(
466
+ sequencerInbox,
467
+ delayedTx.inboxAccountLength,
468
+ delayedTx.deliveredMessageEvent.kind,
469
+ delayedTx.l1BlockNumber,
470
+ delayedTx.l1BlockTimestamp,
471
+ delayedTx.baseFeeL1,
472
+ delayedTx.senderAddr,
473
+ delayedTx.deliveredMessageEvent.messageDataHash,
474
+ 'ForceIncludeBlockTooSoon',
475
+ )
476
+ })
477
+
478
+ it('cannot include before max time delay', async () => {
479
+ const {
480
+ user,
481
+ inbox,
482
+ bridge,
483
+ messageTester,
484
+ sequencerInbox,
485
+ } = await setupSequencerInbox(10, 100)
486
+ const delayedTx = await sendDelayedTx(
487
+ user,
488
+ inbox,
489
+ bridge,
490
+ messageTester,
491
+ 1000000,
492
+ 21000000000,
493
+ 0,
494
+ await user.getAddress(),
495
+ BigNumber.from(10),
496
+ '0x1010',
497
+ )
498
+
499
+ const maxTimeVariation = await sequencerInbox.maxTimeVariation()
500
+ // mine a lot of blocks - but use a short time per block
501
+ // this should mean enough blocks have passed, but not enough time
502
+ await mineBlocks(maxTimeVariation.delayBlocks.toNumber() + 1, 5)
503
+
504
+ await forceIncludeMessages(
505
+ sequencerInbox,
506
+ delayedTx.inboxAccountLength,
507
+ delayedTx.deliveredMessageEvent.kind,
508
+ delayedTx.l1BlockNumber,
509
+ delayedTx.l1BlockTimestamp,
510
+ delayedTx.baseFeeL1,
511
+ delayedTx.senderAddr,
512
+ delayedTx.deliveredMessageEvent.messageDataHash,
513
+ 'ForceIncludeTimeTooSoon',
514
+ )
515
+ })
516
+ })
@@ -0,0 +1,40 @@
1
+ import { ethers } from 'hardhat'
2
+ import { Signer } from '@ethersproject/abstract-signer'
3
+
4
+ export async function initializeAccounts(): Promise<Signer[]> {
5
+ const [account0] = await ethers.getSigners()
6
+ const provider = account0.provider!
7
+
8
+ const accounts: Signer[] = [account0]
9
+ for (let i = 0; i < 9; i++) {
10
+ const account = ethers.Wallet.createRandom().connect(provider)
11
+ accounts.push(account)
12
+ const tx = await account0.sendTransaction({
13
+ value: ethers.utils.parseEther('10000.0'),
14
+ to: await account.getAddress(),
15
+ })
16
+ await tx.wait()
17
+ }
18
+ return accounts
19
+ }
20
+
21
+ export async function tryAdvanceChain(
22
+ account: Signer,
23
+ blocks: number
24
+ ): Promise<void> {
25
+ try {
26
+ for (let i = 0; i < blocks; i++) {
27
+ await ethers.provider.send('evm_mine', [])
28
+ }
29
+ } catch (e) {
30
+ // EVM mine failed. Try advancing the chain by sending txes if the node
31
+ // is in dev mode and mints blocks when txes are sent
32
+ for (let i = 0; i < blocks; i++) {
33
+ const tx = await account.sendTransaction({
34
+ value: 0,
35
+ to: await account.getAddress(),
36
+ })
37
+ await tx.wait()
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,75 @@
1
+ import { assert } from "chai";
2
+ import { ethers, run } from "hardhat";
3
+
4
+ describe("HashProofHelper", function () {
5
+ it("Should produce valid proofs from full preimages", async function () {
6
+ await run("deploy", { tags: "HashProofHelper" });
7
+
8
+ const hashProofHelper = await ethers.getContract("HashProofHelper");
9
+
10
+ for (let i = 0; i < 16; i += 1) {
11
+ const len = Math.floor(Math.random() * 256);
12
+ const data = Math.floor(Math.random() * 256);
13
+ const offset = Math.floor(Math.random() * 256);
14
+ const bytes = Array(len).fill(data);
15
+ const hash = ethers.utils.keccak256(bytes);
16
+
17
+ const proofTx = await hashProofHelper.proveWithFullPreimage(bytes, offset);
18
+ const receipt = await proofTx.wait();
19
+ const log = hashProofHelper.interface.parseLog(receipt.logs[0]);
20
+ const provenPart = await hashProofHelper.getPreimagePart(hash, offset);
21
+
22
+ let dataHex = data.toString(16);
23
+ dataHex = "00".slice(dataHex.length) + dataHex;
24
+ const partLen = Math.min(32, Math.max(0, len - offset));
25
+ const partString = "0x" + dataHex.repeat(partLen);
26
+ assert.equal(log.args["fullHash"], hash);
27
+ assert.equal(log.args["offset"], offset);
28
+ assert.equal(log.args["part"], partString);
29
+ assert.equal(provenPart, partString);
30
+ }
31
+ });
32
+
33
+ it("Should produce valid proofs from split preimages", async function () {
34
+ await run("deploy", { tags: "HashProofHelper" });
35
+
36
+ const hashProofHelper = await ethers.getContract("HashProofHelper");
37
+
38
+ for (let i = 0; i < 16; i += 1) {
39
+ const len = Math.floor(Math.random() * 1024);
40
+ const data = Math.floor(Math.random() * 256);
41
+ const offset = Math.floor(Math.random() * 256);
42
+ const bytes = Array(len).fill(data);
43
+ const hash = ethers.utils.keccak256(bytes);
44
+
45
+ let provenLen = 0;
46
+ let provenPart = null;
47
+ let log = null;
48
+ while (provenPart === null) {
49
+ let nextPartialLen = 136 * (1 + Math.floor(Math.random() * 2));
50
+ if (nextPartialLen > len - provenLen) {
51
+ nextPartialLen = len - provenLen;
52
+ }
53
+ const newProvenLen = provenLen + nextPartialLen;
54
+ const isFinal = newProvenLen == len;
55
+ const proofTx = await hashProofHelper.proveWithSplitPreimage(bytes.slice(provenLen, newProvenLen), offset, isFinal ? 1 : 0);
56
+ const receipt = await proofTx.wait();
57
+ if (receipt.logs.length > 0) {
58
+ log = hashProofHelper.interface.parseLog(receipt.logs[0]);
59
+ provenPart = await hashProofHelper.getPreimagePart(hash, offset);
60
+ }
61
+ provenLen = newProvenLen;
62
+ }
63
+
64
+ let dataHex = data.toString(16);
65
+ dataHex = "00".slice(dataHex.length) + dataHex;
66
+ const partLen = Math.min(32, Math.max(0, len - offset));
67
+ const partString = "0x" + dataHex.repeat(partLen);
68
+ assert.isNotNull(log);
69
+ assert.equal(log!.args["fullHash"], hash);
70
+ assert.equal(log!.args["offset"], offset);
71
+ assert.equal(log!.args["part"], partString);
72
+ assert.equal(provenPart, partString);
73
+ }
74
+ });
75
+ });