@arbitrum/nitro-contracts 1.0.0-beta.5 → 1.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/package.json +2 -1
  2. package/src/bridge/Bridge.sol +127 -32
  3. package/src/bridge/IBridge.sol +40 -6
  4. package/src/bridge/{IMessageProvider.sol → IDelayedMessageProvider.sol} +2 -3
  5. package/src/bridge/IInbox.sol +23 -2
  6. package/src/bridge/IOwnable.sol +9 -0
  7. package/src/bridge/ISequencerInbox.sol +36 -9
  8. package/src/bridge/Inbox.sol +131 -45
  9. package/src/bridge/Outbox.sol +134 -31
  10. package/src/bridge/SequencerInbox.sol +159 -60
  11. package/src/challenge/ChallengeLib.sol +0 -2
  12. package/src/challenge/ChallengeManager.sol +4 -8
  13. package/src/challenge/IChallengeManager.sol +1 -1
  14. package/src/libraries/Error.sol +6 -0
  15. package/src/libraries/IGasRefunder.sol +12 -13
  16. package/src/libraries/MerkleLib.sol +11 -2
  17. package/src/libraries/MessageTypes.sol +1 -0
  18. package/src/mocks/BridgeStub.sol +67 -21
  19. package/src/mocks/InboxStub.sol +3 -9
  20. package/src/mocks/SequencerInboxStub.sol +10 -8
  21. package/src/mocks/Simple.sol +8 -0
  22. package/src/node-interface/NodeInterface.sol +32 -5
  23. package/src/osp/IOneStepProver.sol +1 -2
  24. package/src/osp/OneStepProver0.sol +1 -87
  25. package/src/osp/OneStepProverHostIo.sol +5 -6
  26. package/src/osp/OneStepProverMath.sol +37 -27
  27. package/src/osp/OneStepProverMemory.sol +3 -4
  28. package/src/precompiles/ArbAggregator.sol +23 -33
  29. package/src/precompiles/ArbBLS.sol +1 -43
  30. package/src/precompiles/ArbGasInfo.sol +1 -19
  31. package/src/precompiles/ArbOwner.sol +12 -15
  32. package/src/precompiles/ArbRetryableTx.sol +10 -1
  33. package/src/precompiles/ArbosActs.sol +9 -2
  34. package/src/rollup/BridgeCreator.sol +23 -28
  35. package/src/rollup/IRollupCore.sol +3 -3
  36. package/src/rollup/{IRollupEventBridge.sol → IRollupEventInbox.sol} +2 -2
  37. package/src/rollup/IRollupLogic.sol +21 -18
  38. package/src/rollup/RollupAdminLogic.sol +30 -34
  39. package/src/rollup/RollupCore.sol +15 -7
  40. package/src/rollup/RollupCreator.sol +21 -11
  41. package/src/rollup/{RollupEventBridge.sol → RollupEventInbox.sol} +10 -10
  42. package/src/rollup/RollupLib.sol +20 -5
  43. package/src/rollup/RollupUserLogic.sol +9 -18
  44. package/src/rollup/ValidatorWallet.sol +124 -8
  45. package/src/rollup/ValidatorWalletCreator.sol +11 -6
  46. package/src/state/Deserialize.sol +3 -22
  47. package/src/state/Instructions.sol +2 -10
  48. package/src/state/Machine.sol +0 -4
  49. package/src/state/ModuleMemory.sol +2 -1
  50. package/src/state/Value.sol +2 -3
  51. package/src/test-helpers/BridgeTester.sol +223 -0
  52. package/src/test-helpers/OutboxWithoutOptTester.sol +188 -0
  53. package/src/test-helpers/RollupMock.sol +21 -0
  54. package/src/state/PcStack.sol +0 -32
@@ -5,9 +5,12 @@
5
5
  pragma solidity ^0.8.0;
6
6
 
7
7
  import "./IBridge.sol";
8
+ import "./IInbox.sol";
8
9
  import "./ISequencerInbox.sol";
10
+ import "../rollup/IRollupLogic.sol";
9
11
  import "./Messages.sol";
10
12
 
13
+ import {L1MessageType_batchPostingReport} from "../libraries/MessageTypes.sol";
11
14
  import {GasRefundEnabled, IGasRefunder} from "../libraries/IGasRefunder.sol";
12
15
  import "../libraries/DelegateCallAware.sol";
13
16
  import {MAX_DATA_SIZE} from "../libraries/Constants.sol";
@@ -20,10 +23,9 @@ import {MAX_DATA_SIZE} from "../libraries/Constants.sol";
20
23
  * sequencer within a time limit they can be force included into the rollup inbox by anyone.
21
24
  */
22
25
  contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox {
23
- bytes32[] public override inboxAccs;
24
26
  uint256 public totalDelayedMessagesRead;
25
27
 
26
- IBridge public delayedBridge;
28
+ IBridge public bridge;
27
29
 
28
30
  /// @dev The size of the batch header
29
31
  uint256 public constant HEADER_LENGTH = 40;
@@ -31,19 +33,29 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
31
33
  /// the sequencer inbox has authenticated the data. Currently not used.
32
34
  bytes1 public constant DATA_AUTHENTICATED_FLAG = 0x40;
33
35
 
34
- address public rollup;
36
+ IOwnable public rollup;
35
37
  mapping(address => bool) public isBatchPoster;
36
38
  ISequencerInbox.MaxTimeVariation public maxTimeVariation;
37
39
 
40
+ struct DasKeySetInfo {
41
+ bool isValidKeyset;
42
+ uint64 creationBlock;
43
+ }
44
+ mapping(bytes32 => DasKeySetInfo) public dasKeySetInfo;
45
+
46
+ modifier onlyRollupOwner() {
47
+ if (msg.sender != rollup.owner()) revert NotOwner(msg.sender, address(rollup));
48
+ _;
49
+ }
50
+
38
51
  function initialize(
39
- IBridge delayedBridge_,
40
- address rollup_,
52
+ IBridge bridge_,
41
53
  ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_
42
54
  ) external onlyDelegated {
43
- if (delayedBridge != IBridge(address(0))) revert AlreadyInit();
44
- if (delayedBridge_ == IBridge(address(0))) revert HadZeroInit();
45
- delayedBridge = delayedBridge_;
46
- rollup = rollup_;
55
+ if (bridge != IBridge(address(0))) revert AlreadyInit();
56
+ if (bridge_ == IBridge(address(0))) revert HadZeroInit();
57
+ bridge = bridge_;
58
+ rollup = bridge_.rollup();
47
59
  maxTimeVariation = maxTimeVariation_;
48
60
  }
49
61
 
@@ -98,22 +110,24 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
98
110
  // Verify that message hash represents the last message sequence of delayed message to be included
99
111
  bytes32 prevDelayedAcc = 0;
100
112
  if (_totalDelayedMessagesRead > 1) {
101
- prevDelayedAcc = delayedBridge.inboxAccs(_totalDelayedMessagesRead - 2);
113
+ prevDelayedAcc = bridge.delayedInboxAccs(_totalDelayedMessagesRead - 2);
102
114
  }
103
115
  if (
104
- delayedBridge.inboxAccs(_totalDelayedMessagesRead - 1) !=
116
+ bridge.delayedInboxAccs(_totalDelayedMessagesRead - 1) !=
105
117
  Messages.accumulateInboxMessage(prevDelayedAcc, messageHash)
106
118
  ) revert IncorrectMessagePreimage();
107
119
 
108
120
  (bytes32 dataHash, TimeBounds memory timeBounds) = formEmptyDataHash(
109
121
  _totalDelayedMessagesRead
110
122
  );
111
- (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl(
112
- dataHash,
113
- _totalDelayedMessagesRead
114
- );
123
+ (
124
+ uint256 seqMessageIndex,
125
+ bytes32 beforeAcc,
126
+ bytes32 delayedAcc,
127
+ bytes32 afterAcc
128
+ ) = addSequencerL2BatchImpl(dataHash, _totalDelayedMessagesRead, 0);
115
129
  emit SequencerBatchDelivered(
116
- inboxAccs.length - 1,
130
+ seqMessageIndex,
117
131
  beforeAcc,
118
132
  afterAcc,
119
133
  delayedAcc,
@@ -128,21 +142,24 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
128
142
  bytes calldata data,
129
143
  uint256 afterDelayedMessagesRead,
130
144
  IGasRefunder gasRefunder
131
- ) external refundsGasWithCalldata(gasRefunder, payable(msg.sender)) {
145
+ ) external refundsGas(gasRefunder) {
132
146
  // solhint-disable-next-line avoid-tx-origin
133
147
  if (msg.sender != tx.origin) revert NotOrigin();
134
148
  if (!isBatchPoster[msg.sender]) revert NotBatchPoster();
135
- if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber();
136
149
  (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash(
137
150
  data,
138
151
  afterDelayedMessagesRead
139
152
  );
140
- (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl(
141
- dataHash,
142
- afterDelayedMessagesRead
143
- );
153
+ (
154
+ uint256 seqMessageIndex,
155
+ bytes32 beforeAcc,
156
+ bytes32 delayedAcc,
157
+ bytes32 afterAcc
158
+ ) = addSequencerL2BatchImpl(dataHash, afterDelayedMessagesRead, data.length);
159
+ if (seqMessageIndex != sequenceNumber)
160
+ revert BadSequencerNumber(seqMessageIndex, sequenceNumber);
144
161
  emit SequencerBatchDelivered(
145
- inboxAccs.length - 1,
162
+ sequenceNumber,
146
163
  beforeAcc,
147
164
  afterAcc,
148
165
  delayedAcc,
@@ -157,18 +174,23 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
157
174
  bytes calldata data,
158
175
  uint256 afterDelayedMessagesRead,
159
176
  IGasRefunder gasRefunder
160
- ) external override refundsGasNoCalldata(gasRefunder, payable(msg.sender)) {
161
- if (!isBatchPoster[msg.sender] && msg.sender != rollup) revert NotBatchPoster();
162
- if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber();
177
+ ) external override refundsGas(gasRefunder) {
178
+ if (!isBatchPoster[msg.sender] && msg.sender != address(rollup)) revert NotBatchPoster();
163
179
 
164
180
  (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash(
165
181
  data,
166
182
  afterDelayedMessagesRead
167
183
  );
168
- (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl(
169
- dataHash,
170
- afterDelayedMessagesRead
171
- );
184
+ // we set the calldata length posted to 0 here since the caller isn't the origin
185
+ // of the tx, so they might have not paid tx input cost for the calldata
186
+ (
187
+ uint256 seqMessageIndex,
188
+ bytes32 beforeAcc,
189
+ bytes32 delayedAcc,
190
+ bytes32 afterAcc
191
+ ) = addSequencerL2BatchImpl(dataHash, afterDelayedMessagesRead, 0);
192
+ if (seqMessageIndex != sequenceNumber)
193
+ revert BadSequencerNumber(seqMessageIndex, sequenceNumber);
172
194
  emit SequencerBatchDelivered(
173
195
  sequenceNumber,
174
196
  beforeAcc,
@@ -181,6 +203,22 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
181
203
  emit SequencerBatchData(sequenceNumber, data);
182
204
  }
183
205
 
206
+ modifier validateBatchData(bytes calldata data) {
207
+ uint256 fullDataLen = HEADER_LENGTH + data.length;
208
+ if (fullDataLen > MAX_DATA_SIZE) revert DataTooLarge(fullDataLen, MAX_DATA_SIZE);
209
+ if (data.length > 0 && (data[0] & DATA_AUTHENTICATED_FLAG) == DATA_AUTHENTICATED_FLAG) {
210
+ revert DataNotAuthenticated();
211
+ }
212
+ // the first byte is used to identify the type of batch data
213
+ // das batches expect to have the type byte set, followed by the keyset (so they should have at least 33 bytes)
214
+ if (data.length >= 33 && data[0] & 0x80 != 0) {
215
+ // we skip the first byte, then read the next 32 bytes for the keyset
216
+ bytes32 dasKeysetHash = bytes32(data[1:33]);
217
+ if (!dasKeySetInfo[dasKeysetHash].isValidKeyset) revert NoSuchKeyset(dasKeysetHash);
218
+ }
219
+ _;
220
+ }
221
+
184
222
  function packHeader(uint256 afterDelayedMessagesRead)
185
223
  internal
186
224
  view
@@ -202,25 +240,12 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
202
240
  function formDataHash(bytes calldata data, uint256 afterDelayedMessagesRead)
203
241
  internal
204
242
  view
243
+ validateBatchData(data)
205
244
  returns (bytes32, TimeBounds memory)
206
245
  {
207
- uint256 fullDataLen = HEADER_LENGTH + data.length;
208
- if (fullDataLen < HEADER_LENGTH) revert DataLengthOverflow();
209
- if (fullDataLen > MAX_DATA_SIZE) revert DataTooLarge(fullDataLen, MAX_DATA_SIZE);
210
- bytes memory fullData = new bytes(fullDataLen);
211
246
  (bytes memory header, TimeBounds memory timeBounds) = packHeader(afterDelayedMessagesRead);
212
-
213
- for (uint256 i = 0; i < HEADER_LENGTH; i++) {
214
- fullData[i] = header[i];
215
- }
216
- if (data.length > 0 && (data[0] & DATA_AUTHENTICATED_FLAG) == DATA_AUTHENTICATED_FLAG) {
217
- revert DataNotAuthenticated();
218
- }
219
- // copy data into fullData at offset of HEADER_LENGTH (the extra 32 offset is because solidity puts the array len first)
220
- assembly {
221
- calldatacopy(add(fullData, add(HEADER_LENGTH, 32)), data.offset, data.length)
222
- }
223
- return (keccak256(fullData), timeBounds);
247
+ bytes32 dataHash = keccak256(bytes.concat(header, data));
248
+ return (dataHash, timeBounds);
224
249
  }
225
250
 
226
251
  function formEmptyDataHash(uint256 afterDelayedMessagesRead)
@@ -232,43 +257,117 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
232
257
  return (keccak256(header), timeBounds);
233
258
  }
234
259
 
235
- function addSequencerL2BatchImpl(bytes32 dataHash, uint256 afterDelayedMessagesRead)
260
+ function addSequencerL2BatchImpl(
261
+ bytes32 dataHash,
262
+ uint256 afterDelayedMessagesRead,
263
+ uint256 calldataLengthPosted
264
+ )
236
265
  internal
237
266
  returns (
267
+ uint256 seqMessageIndex,
238
268
  bytes32 beforeAcc,
239
269
  bytes32 delayedAcc,
240
270
  bytes32 acc
241
271
  )
242
272
  {
243
273
  if (afterDelayedMessagesRead < totalDelayedMessagesRead) revert DelayedBackwards();
244
- if (afterDelayedMessagesRead > delayedBridge.messageCount()) revert DelayedTooFar();
274
+ if (afterDelayedMessagesRead > bridge.delayedMessageCount()) revert DelayedTooFar();
245
275
 
246
- if (inboxAccs.length > 0) {
247
- beforeAcc = inboxAccs[inboxAccs.length - 1];
248
- }
249
- if (afterDelayedMessagesRead > 0) {
250
- delayedAcc = delayedBridge.inboxAccs(afterDelayedMessagesRead - 1);
251
- }
276
+ (seqMessageIndex, beforeAcc, delayedAcc, acc) = bridge.enqueueSequencerMessage(
277
+ dataHash,
278
+ afterDelayedMessagesRead
279
+ );
252
280
 
253
- acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc));
254
- inboxAccs.push(acc);
255
281
  totalDelayedMessagesRead = afterDelayedMessagesRead;
282
+
283
+ if (calldataLengthPosted > 0) {
284
+ // this msg isn't included in the current sequencer batch, but instead added to
285
+ // the delayed messages queue that is yet to be included
286
+ address batchPoster = msg.sender;
287
+ bytes memory spendingReportMsg = abi.encodePacked(
288
+ block.timestamp,
289
+ batchPoster,
290
+ dataHash,
291
+ seqMessageIndex,
292
+ block.basefee
293
+ );
294
+ uint256 msgNum = bridge.submitBatchSpendingReport(
295
+ batchPoster,
296
+ keccak256(spendingReportMsg)
297
+ );
298
+ // this is the same event used by Inbox.sol after including a message to the delayed message accumulator
299
+ emit InboxMessageDelivered(msgNum, spendingReportMsg);
300
+ }
301
+ }
302
+
303
+ function inboxAccs(uint256 index) external view override returns (bytes32) {
304
+ return bridge.sequencerInboxAccs(index);
256
305
  }
257
306
 
258
307
  function batchCount() external view override returns (uint256) {
259
- return inboxAccs.length;
308
+ return bridge.sequencerMessageCount();
260
309
  }
261
310
 
311
+ /**
312
+ * @notice Set max delay for sequencer inbox
313
+ * @param maxTimeVariation_ the maximum time variation parameters
314
+ */
262
315
  function setMaxTimeVariation(ISequencerInbox.MaxTimeVariation memory maxTimeVariation_)
263
316
  external
264
317
  override
318
+ onlyRollupOwner
265
319
  {
266
- if (msg.sender != rollup) revert NotRollup(msg.sender, rollup);
267
320
  maxTimeVariation = maxTimeVariation_;
321
+ emit OwnerFunctionCalled(0);
268
322
  }
269
323
 
270
- function setIsBatchPoster(address addr, bool isBatchPoster_) external override {
271
- if (msg.sender != rollup) revert NotRollup(msg.sender, rollup);
324
+ /**
325
+ * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
326
+ * @param addr the address
327
+ * @param isBatchPoster_ if the specified address should be authorized as a batch poster
328
+ */
329
+ function setIsBatchPoster(address addr, bool isBatchPoster_) external override onlyRollupOwner {
272
330
  isBatchPoster[addr] = isBatchPoster_;
331
+ emit OwnerFunctionCalled(1);
332
+ }
333
+
334
+ /**
335
+ * @notice Makes Data Availability Service keyset valid
336
+ * @param keysetBytes bytes of the serialized keyset
337
+ */
338
+ function setValidKeyset(bytes calldata keysetBytes) external override onlyRollupOwner {
339
+ bytes32 ksHash = keccak256(keysetBytes);
340
+ if (dasKeySetInfo[ksHash].isValidKeyset) revert AlreadyValidDASKeyset(ksHash);
341
+ dasKeySetInfo[ksHash] = DasKeySetInfo({
342
+ isValidKeyset: true,
343
+ creationBlock: uint64(block.number)
344
+ });
345
+ emit SetValidKeyset(ksHash, keysetBytes);
346
+ emit OwnerFunctionCalled(2);
347
+ }
348
+
349
+ /**
350
+ * @notice Invalidates a Data Availability Service keyset
351
+ * @param ksHash hash of the keyset
352
+ */
353
+ function invalidateKeysetHash(bytes32 ksHash) external override onlyRollupOwner {
354
+ if (!dasKeySetInfo[ksHash].isValidKeyset) revert NoSuchKeyset(ksHash);
355
+ // we don't delete the block creation value since its used to fetch the SetValidKeyset
356
+ // event efficiently. The event provides the hash preimage of the key.
357
+ // this is still needed when syncing the chain after a keyset is invalidated.
358
+ dasKeySetInfo[ksHash].isValidKeyset = false;
359
+ emit InvalidateKeyset(ksHash);
360
+ emit OwnerFunctionCalled(3);
361
+ }
362
+
363
+ function isValidKeysetHash(bytes32 ksHash) external view returns (bool) {
364
+ return dasKeySetInfo[ksHash].isValidKeyset;
365
+ }
366
+
367
+ /// @notice the creation block is intended to still be available after a keyset is deleted
368
+ function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256) {
369
+ DasKeySetInfo memory ksInfo = dasKeySetInfo[ksHash];
370
+ if (ksInfo.creationBlock == 0) revert NoSuchKeyset(ksHash);
371
+ return uint256(ksInfo.creationBlock);
273
372
  }
274
373
  }
@@ -61,14 +61,12 @@ library ChallengeLib {
61
61
  ValueArray memory valuesArray = ValueArray({inner: startingValues});
62
62
  ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0});
63
63
  ValueStack memory internalStack;
64
- PcStack memory blocks;
65
64
  StackFrameWindow memory frameStack;
66
65
 
67
66
  Machine memory mach = Machine({
68
67
  status: MachineStatus.RUNNING,
69
68
  valueStack: values,
70
69
  internalStack: internalStack,
71
- blockStack: blocks,
72
70
  frameStack: frameStack,
73
71
  globalStateHash: globalStateHash,
74
72
  moduleIdx: 0,
@@ -33,7 +33,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
33
33
  IChallengeResultReceiver public resultReceiver;
34
34
 
35
35
  ISequencerInbox public sequencerInbox;
36
- IBridge public delayedBridge;
36
+ IBridge public bridge;
37
37
  IOneStepProofEntry public osp;
38
38
 
39
39
  function challengeInfo(uint64 challengeIndex)
@@ -99,14 +99,14 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
99
99
  function initialize(
100
100
  IChallengeResultReceiver resultReceiver_,
101
101
  ISequencerInbox sequencerInbox_,
102
- IBridge delayedBridge_,
102
+ IBridge bridge_,
103
103
  IOneStepProofEntry osp_
104
104
  ) external override onlyDelegated {
105
105
  require(address(resultReceiver) == address(0), "ALREADY_INIT");
106
106
  require(address(resultReceiver_) != address(0), "NO_RESULT_RECEIVER");
107
107
  resultReceiver = resultReceiver_;
108
108
  sequencerInbox = sequencerInbox_;
109
- delayedBridge = delayedBridge_;
109
+ bridge = bridge_;
110
110
  osp = osp_;
111
111
  }
112
112
 
@@ -254,11 +254,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager {
254
254
  }
255
255
 
256
256
  bytes32 afterHash = osp.proveOneStep(
257
- ExecutionContext({
258
- maxInboxMessagesRead: challenge.maxInboxMessages,
259
- sequencerInbox: sequencerInbox,
260
- delayedBridge: delayedBridge
261
- }),
257
+ ExecutionContext({maxInboxMessagesRead: challenge.maxInboxMessages, bridge: bridge}),
262
258
  challengeStart,
263
259
  selection.oldSegments[selection.challengePosition],
264
260
  proof
@@ -43,7 +43,7 @@ interface IChallengeManager {
43
43
  function initialize(
44
44
  IChallengeResultReceiver resultReceiver_,
45
45
  ISequencerInbox sequencerInbox_,
46
- IBridge delayedBridge_,
46
+ IBridge bridge_,
47
47
  IOneStepProofEntry osp_
48
48
  ) external;
49
49
 
@@ -36,3 +36,9 @@ error NotContract(address addr);
36
36
  /// @param actualLength The length of the merkle proof provided
37
37
  /// @param maxProofLength The max length a merkle proof can have
38
38
  error MerkleProofTooLong(uint256 actualLength, uint256 maxProofLength);
39
+
40
+ /// @dev Thrown when an un-authorized address tries to access an admin function
41
+ /// @param sender The un-authorized sender
42
+ /// @param rollup The rollup, which would be authorized
43
+ /// @param owner The rollup's owner, which would be authorized
44
+ error NotRollupOrOwner(address sender, address rollup, address owner);
@@ -13,23 +13,22 @@ interface IGasRefunder {
13
13
  }
14
14
 
15
15
  abstract contract GasRefundEnabled {
16
- modifier refundsGasWithCalldata(IGasRefunder gasRefunder, address payable spender) {
16
+ /// @dev this refunds the sender for execution costs of the tx
17
+ /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging
18
+ /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded
19
+ modifier refundsGas(IGasRefunder gasRefunder) {
17
20
  uint256 startGasLeft = gasleft();
18
21
  _;
19
22
  if (address(gasRefunder) != address(0)) {
20
- uint256 calldataSize;
21
- assembly {
22
- calldataSize := calldatasize()
23
+ uint256 calldataSize = 0;
24
+ // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call
25
+ // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input
26
+ if (msg.sender == tx.origin) {
27
+ assembly {
28
+ calldataSize := calldatasize()
29
+ }
23
30
  }
24
- gasRefunder.onGasSpent(spender, startGasLeft - gasleft(), calldataSize);
25
- }
26
- }
27
-
28
- modifier refundsGasNoCalldata(IGasRefunder gasRefunder, address payable spender) {
29
- uint256 startGasLeft = gasleft();
30
- _;
31
- if (address(gasRefunder) != address(0)) {
32
- gasRefunder.onGasSpent(spender, startGasLeft - gasleft(), 0);
31
+ gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize);
33
32
  }
34
33
  }
35
34
  }
@@ -34,10 +34,19 @@ library MerkleLib {
34
34
  if (proofItems > 256) revert MerkleProofTooLong(proofItems, 256);
35
35
  bytes32 h = item;
36
36
  for (uint256 i = 0; i < proofItems; i++) {
37
+ bytes32 node = nodes[i];
37
38
  if (route % 2 == 0) {
38
- h = keccak256(abi.encodePacked(h, nodes[i]));
39
+ assembly {
40
+ mstore(0x00, h)
41
+ mstore(0x20, node)
42
+ h := keccak256(0x00, 0x40)
43
+ }
39
44
  } else {
40
- h = keccak256(abi.encodePacked(nodes[i], h));
45
+ assembly {
46
+ mstore(0x00, node)
47
+ mstore(0x20, h)
48
+ h := keccak256(0x00, 0x40)
49
+ }
41
50
  }
42
51
  route /= 2;
43
52
  }
@@ -8,6 +8,7 @@ uint8 constant L2_MSG = 3;
8
8
  uint8 constant L1MessageType_L2FundedByL1 = 7;
9
9
  uint8 constant L1MessageType_submitRetryableTx = 9;
10
10
  uint8 constant L1MessageType_ethDeposit = 12;
11
+ uint8 constant L1MessageType_batchPostingReport = 13;
11
12
  uint8 constant L2MessageType_unsignedEOATx = 0;
12
13
  uint8 constant L2MessageType_unsignedContractTx = 1;
13
14
 
@@ -14,19 +14,28 @@ contract BridgeStub is IBridge {
14
14
  bool allowed;
15
15
  }
16
16
 
17
- mapping(address => InOutInfo) private allowedInboxesMap;
17
+ mapping(address => InOutInfo) private allowedDelayedInboxesMap;
18
18
  //mapping(address => InOutInfo) private allowedOutboxesMap;
19
19
 
20
- address[] public allowedInboxList;
20
+ address[] public allowedDelayedInboxList;
21
21
  address[] public allowedOutboxList;
22
22
 
23
23
  address public override activeOutbox;
24
24
 
25
25
  // Accumulator for delayed inbox; tail represents hash of the current state; each element represents the inclusion of a new message.
26
- bytes32[] public override inboxAccs;
26
+ bytes32[] public override delayedInboxAccs;
27
27
 
28
- function allowedInboxes(address inbox) external view override returns (bool) {
29
- return allowedInboxesMap[inbox].allowed;
28
+ bytes32[] public override sequencerInboxAccs;
29
+
30
+ address public sequencerInbox;
31
+
32
+ function setSequencerInbox(address _sequencerInbox) external override {
33
+ sequencerInbox = _sequencerInbox;
34
+ emit SequencerInboxUpdated(_sequencerInbox);
35
+ }
36
+
37
+ function allowedDelayedInboxes(address inbox) external view override returns (bool) {
38
+ return allowedDelayedInboxesMap[inbox].allowed;
30
39
  }
31
40
 
32
41
  function allowedOutboxes(address) external pure override returns (bool) {
@@ -38,9 +47,9 @@ contract BridgeStub is IBridge {
38
47
  address sender,
39
48
  bytes32 messageDataHash
40
49
  ) external payable override returns (uint256) {
41
- require(allowedInboxesMap[msg.sender].allowed, "NOT_FROM_INBOX");
50
+ require(allowedDelayedInboxesMap[msg.sender].allowed, "NOT_FROM_INBOX");
42
51
  return
43
- addMessageToAccumulator(
52
+ addMessageToDelayedAccumulator(
44
53
  kind,
45
54
  sender,
46
55
  block.number,
@@ -50,7 +59,34 @@ contract BridgeStub is IBridge {
50
59
  );
51
60
  }
52
61
 
53
- function addMessageToAccumulator(
62
+ function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead)
63
+ external
64
+ returns (
65
+ uint256 seqMessageIndex,
66
+ bytes32 beforeAcc,
67
+ bytes32 delayedAcc,
68
+ bytes32 acc
69
+ )
70
+ {
71
+ seqMessageIndex = sequencerInboxAccs.length;
72
+ if (sequencerInboxAccs.length > 0) {
73
+ beforeAcc = sequencerInboxAccs[sequencerInboxAccs.length - 1];
74
+ }
75
+ if (afterDelayedMessagesRead > 0) {
76
+ delayedAcc = delayedInboxAccs[afterDelayedMessagesRead - 1];
77
+ }
78
+ acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc));
79
+ sequencerInboxAccs.push(acc);
80
+ }
81
+
82
+ function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
83
+ external
84
+ returns (uint256)
85
+ {
86
+ // TODO: implement stub
87
+ }
88
+
89
+ function addMessageToDelayedAccumulator(
54
90
  uint8,
55
91
  address,
56
92
  uint256,
@@ -58,7 +94,7 @@ contract BridgeStub is IBridge {
58
94
  uint256,
59
95
  bytes32 messageDataHash
60
96
  ) internal returns (uint256) {
61
- uint256 count = inboxAccs.length;
97
+ uint256 count = delayedInboxAccs.length;
62
98
  bytes32 messageHash = Messages.messageHash(
63
99
  0,
64
100
  address(uint160(0)),
@@ -70,9 +106,9 @@ contract BridgeStub is IBridge {
70
106
  );
71
107
  bytes32 prevAcc = 0;
72
108
  if (count > 0) {
73
- prevAcc = inboxAccs[count - 1];
109
+ prevAcc = delayedInboxAccs[count - 1];
74
110
  }
75
- inboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash));
111
+ delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash));
76
112
  return count;
77
113
  }
78
114
 
@@ -84,21 +120,23 @@ contract BridgeStub is IBridge {
84
120
  revert("NOT_IMPLEMENTED");
85
121
  }
86
122
 
87
- function setInbox(address inbox, bool enabled) external override {
88
- InOutInfo storage info = allowedInboxesMap[inbox];
123
+ function setDelayedInbox(address inbox, bool enabled) external override {
124
+ InOutInfo storage info = allowedDelayedInboxesMap[inbox];
89
125
  bool alreadyEnabled = info.allowed;
90
126
  emit InboxToggle(inbox, enabled);
91
127
  if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
92
128
  return;
93
129
  }
94
130
  if (enabled) {
95
- allowedInboxesMap[inbox] = InOutInfo(allowedInboxList.length, true);
96
- allowedInboxList.push(inbox);
131
+ allowedDelayedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true);
132
+ allowedDelayedInboxList.push(inbox);
97
133
  } else {
98
- allowedInboxList[info.index] = allowedInboxList[allowedInboxList.length - 1];
99
- allowedInboxesMap[allowedInboxList[info.index]].index = info.index;
100
- allowedInboxList.pop();
101
- delete allowedInboxesMap[inbox];
134
+ allowedDelayedInboxList[info.index] = allowedDelayedInboxList[
135
+ allowedDelayedInboxList.length - 1
136
+ ];
137
+ allowedDelayedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index;
138
+ allowedDelayedInboxList.pop();
139
+ delete allowedDelayedInboxesMap[inbox];
102
140
  }
103
141
  }
104
142
 
@@ -109,7 +147,15 @@ contract BridgeStub is IBridge {
109
147
  revert("NOT_IMPLEMENTED");
110
148
  }
111
149
 
112
- function messageCount() external view override returns (uint256) {
113
- return inboxAccs.length;
150
+ function delayedMessageCount() external view override returns (uint256) {
151
+ return delayedInboxAccs.length;
152
+ }
153
+
154
+ function sequencerMessageCount() external view override returns (uint256) {
155
+ return sequencerInboxAccs.length;
156
+ }
157
+
158
+ function rollup() external pure override returns (IOwnable) {
159
+ revert("NOT_IMPLEMENTED");
114
160
  }
115
161
  }
@@ -28,16 +28,10 @@ contract InboxStub is IInbox {
28
28
  }
29
29
 
30
30
  /**
31
- * @notice Send a generic L2 message to the chain
32
- * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input
33
- * @param messageData Data of the message being sent
31
+ * @dev DEPRECATED in favor of sendL2Message
34
32
  */
35
33
  function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256) {
36
- // solhint-disable-next-line avoid-tx-origin
37
- require(msg.sender == tx.origin, "origin only");
38
- uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(messageData));
39
- emit InboxMessageDeliveredFromOrigin(msgNum);
40
- return msgNum;
34
+ return sendL2Message(messageData);
41
35
  }
42
36
 
43
37
  /**
@@ -45,7 +39,7 @@ contract InboxStub is IInbox {
45
39
  * @dev This method can be used to send any type of message that doesn't require L1 validation
46
40
  * @param messageData Data of the message being sent
47
41
  */
48
- function sendL2Message(bytes calldata messageData) external override returns (uint256) {
42
+ function sendL2Message(bytes calldata messageData) public override returns (uint256) {
49
43
  uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(messageData));
50
44
  emit InboxMessageDelivered(msgNum, messageData);
51
45
  return msgNum;