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

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 (59) hide show
  1. package/package.json +6 -2
  2. package/src/bridge/Bridge.sol +138 -32
  3. package/src/bridge/IBridge.sol +34 -14
  4. package/src/bridge/IDelayedMessageProvider.sol +15 -0
  5. package/src/bridge/IInbox.sol +8 -19
  6. package/src/bridge/IOutbox.sol +43 -23
  7. package/src/bridge/IOwnable.sol +10 -0
  8. package/src/bridge/ISequencerInbox.sol +30 -32
  9. package/src/bridge/Inbox.sol +133 -35
  10. package/src/bridge/Outbox.sol +145 -33
  11. package/src/bridge/SequencerInbox.sol +179 -60
  12. package/src/challenge/ChallengeLib.sol +0 -2
  13. package/src/challenge/ChallengeManager.sol +4 -8
  14. package/src/challenge/IChallengeManager.sol +1 -1
  15. package/src/libraries/Error.sol +113 -0
  16. package/src/libraries/IGasRefunder.sol +15 -14
  17. package/src/libraries/MerkleLib.sol +11 -2
  18. package/src/libraries/MessageTypes.sol +1 -0
  19. package/src/mocks/BridgeStub.sol +69 -21
  20. package/src/mocks/InboxStub.sol +2 -0
  21. package/src/mocks/SequencerInboxStub.sol +10 -8
  22. package/src/mocks/Simple.sol +8 -0
  23. package/src/node-interface/NodeInterface.sol +62 -4
  24. package/src/osp/IOneStepProver.sol +1 -2
  25. package/src/osp/OneStepProver0.sol +1 -87
  26. package/src/osp/OneStepProverHostIo.sol +5 -6
  27. package/src/osp/OneStepProverMath.sol +37 -27
  28. package/src/osp/OneStepProverMemory.sol +3 -4
  29. package/src/precompiles/ArbAggregator.sol +23 -33
  30. package/src/precompiles/ArbBLS.sol +1 -43
  31. package/src/precompiles/ArbGasInfo.sol +10 -19
  32. package/src/precompiles/ArbOwner.sol +21 -15
  33. package/src/precompiles/ArbRetryableTx.sol +10 -1
  34. package/src/precompiles/ArbSys.sol +4 -4
  35. package/src/precompiles/ArbosActs.sol +9 -2
  36. package/src/rollup/BridgeCreator.sol +23 -28
  37. package/src/rollup/IRollupCore.sol +3 -3
  38. package/src/rollup/{IRollupEventBridge.sol → IRollupEventInbox.sol} +2 -2
  39. package/src/rollup/IRollupLogic.sol +21 -18
  40. package/src/rollup/RollupAdminLogic.sol +72 -34
  41. package/src/rollup/RollupCore.sol +20 -9
  42. package/src/rollup/RollupCreator.sol +21 -11
  43. package/src/rollup/{RollupEventBridge.sol → RollupEventInbox.sol} +10 -10
  44. package/src/rollup/RollupLib.sol +21 -5
  45. package/src/rollup/RollupUserLogic.sol +10 -18
  46. package/src/rollup/ValidatorWallet.sol +125 -8
  47. package/src/rollup/ValidatorWalletCreator.sol +11 -6
  48. package/src/state/Deserialize.sol +3 -22
  49. package/src/state/GlobalState.sol +7 -0
  50. package/src/state/Instructions.sol +2 -10
  51. package/src/state/Machine.sol +0 -4
  52. package/src/state/ModuleMemory.sol +2 -1
  53. package/src/state/Value.sol +2 -3
  54. package/src/test-helpers/BridgeTester.sol +233 -0
  55. package/src/test-helpers/InterfaceCompatibilityTester.sol +11 -0
  56. package/src/test-helpers/OutboxWithoutOptTester.sol +214 -0
  57. package/src/test-helpers/RollupMock.sol +21 -0
  58. package/src/bridge/IMessageProvider.sol +0 -11
  59. package/src/state/PcStack.sol +0 -32
@@ -13,7 +13,8 @@ import "./ValidatorWallet.sol";
13
13
  contract ValidatorWalletCreator is Ownable {
14
14
  event WalletCreated(
15
15
  address indexed walletAddress,
16
- address indexed userAddress,
16
+ address indexed executorAddress,
17
+ address indexed ownerAddress,
17
18
  address adminProxy
18
19
  );
19
20
  event TemplateUpdated();
@@ -29,15 +30,19 @@ contract ValidatorWalletCreator is Ownable {
29
30
  emit TemplateUpdated();
30
31
  }
31
32
 
32
- function createWallet() external returns (address) {
33
+ function createWallet(address[] calldata initialExecutorAllowedDests)
34
+ external
35
+ returns (address)
36
+ {
37
+ address _executor = msg.sender;
38
+ address _owner = msg.sender;
33
39
  ProxyAdmin admin = new ProxyAdmin();
34
40
  address proxy = address(
35
41
  new TransparentUpgradeableProxy(address(template), address(admin), "")
36
42
  );
37
- admin.transferOwnership(msg.sender);
38
- ValidatorWallet(proxy).initialize();
39
- ValidatorWallet(proxy).transferOwnership(msg.sender);
40
- emit WalletCreated(proxy, msg.sender, address(admin));
43
+ admin.transferOwnership(_owner);
44
+ ValidatorWallet(payable(proxy)).initialize(_executor, _owner, initialExecutorAllowedDests);
45
+ emit WalletCreated(proxy, _executor, _owner, address(admin));
41
46
  return proxy;
42
47
  }
43
48
  }
@@ -6,7 +6,6 @@ pragma solidity ^0.8.0;
6
6
 
7
7
  import "./Value.sol";
8
8
  import "./ValueStack.sol";
9
- import "./PcStack.sol";
10
9
  import "./Machine.sol";
11
10
  import "./Instructions.sol";
12
11
  import "./StackFrame.sol";
@@ -120,23 +119,6 @@ library Deserialize {
120
119
  stack = ValueStack({proved: ValueArray(proved), remainingHash: remainingHash});
121
120
  }
122
121
 
123
- function pcStack(bytes calldata proof, uint256 startOffset)
124
- internal
125
- pure
126
- returns (PcStack memory stack, uint256 offset)
127
- {
128
- offset = startOffset;
129
- bytes32 remainingHash;
130
- (remainingHash, offset) = b32(proof, offset);
131
- uint256 provedLength;
132
- (provedLength, offset) = u256(proof, offset);
133
- uint32[] memory proved = new uint32[](provedLength);
134
- for (uint256 i = 0; i < proved.length; i++) {
135
- (proved[i], offset) = u32(proof, offset);
136
- }
137
- stack = PcStack({proved: PcArray(proved), remainingHash: remainingHash});
138
- }
139
-
140
122
  function instruction(bytes calldata proof, uint256 startOffset)
141
123
  internal
142
124
  pure
@@ -199,10 +181,12 @@ library Deserialize {
199
181
  {
200
182
  offset = startOffset;
201
183
  uint64 size;
184
+ uint64 maxSize;
202
185
  bytes32 root;
203
186
  (size, offset) = u64(proof, offset);
187
+ (maxSize, offset) = u64(proof, offset);
204
188
  (root, offset) = b32(proof, offset);
205
- mem = ModuleMemory({size: size, merkleRoot: root});
189
+ mem = ModuleMemory({size: size, maxSize: maxSize, merkleRoot: root});
206
190
  }
207
191
 
208
192
  function module(bytes calldata proof, uint256 startOffset)
@@ -274,7 +258,6 @@ library Deserialize {
274
258
  }
275
259
  ValueStack memory values;
276
260
  ValueStack memory internalStack;
277
- PcStack memory blocks;
278
261
  bytes32 globalStateHash;
279
262
  uint32 moduleIdx;
280
263
  uint32 functionIdx;
@@ -283,7 +266,6 @@ library Deserialize {
283
266
  bytes32 modulesRoot;
284
267
  (values, offset) = valueStack(proof, offset);
285
268
  (internalStack, offset) = valueStack(proof, offset);
286
- (blocks, offset) = pcStack(proof, offset);
287
269
  (frameStack, offset) = stackFrameWindow(proof, offset);
288
270
  (globalStateHash, offset) = b32(proof, offset);
289
271
  (moduleIdx, offset) = u32(proof, offset);
@@ -294,7 +276,6 @@ library Deserialize {
294
276
  status: status,
295
277
  valueStack: values,
296
278
  internalStack: internalStack,
297
- blockStack: blocks,
298
279
  frameStack: frameStack,
299
280
  globalStateHash: globalStateHash,
300
281
  moduleIdx: moduleIdx,
@@ -41,4 +41,11 @@ library GlobalStateLib {
41
41
  function getPositionInMessage(GlobalState memory state) internal pure returns (uint64) {
42
42
  return state.u64Vals[1];
43
43
  }
44
+
45
+ function isEmpty(GlobalState calldata state) internal pure returns (bool) {
46
+ return (state.bytes32Vals[0] == bytes32(0) &&
47
+ state.bytes32Vals[1] == bytes32(0) &&
48
+ state.u64Vals[0] == 0 &&
49
+ state.u64Vals[1] == 0);
50
+ }
44
51
  }
@@ -12,9 +12,6 @@ struct Instruction {
12
12
  library Instructions {
13
13
  uint16 internal constant UNREACHABLE = 0x00;
14
14
  uint16 internal constant NOP = 0x01;
15
- uint16 internal constant BLOCK = 0x02;
16
- uint16 internal constant BRANCH = 0x0C;
17
- uint16 internal constant BRANCH_IF = 0x0D;
18
15
  uint16 internal constant RETURN = 0x0F;
19
16
  uint16 internal constant CALL = 0x10;
20
17
  uint16 internal constant CALL_INDIRECT = 0x11;
@@ -129,14 +126,11 @@ library Instructions {
129
126
  uint16 internal constant I64_EXTEND_16S = 0xC3;
130
127
  uint16 internal constant I64_EXTEND_32S = 0xC4;
131
128
 
132
- uint16 internal constant END_BLOCK = 0x8000;
133
- uint16 internal constant END_BLOCK_IF = 0x8001;
134
129
  uint16 internal constant INIT_FRAME = 0x8002;
135
- uint16 internal constant ARBITRARY_JUMP_IF = 0x8003;
136
- uint16 internal constant PUSH_STACK_BOUNDARY = 0x8004;
130
+ uint16 internal constant ARBITRARY_JUMP = 0x8003;
131
+ uint16 internal constant ARBITRARY_JUMP_IF = 0x8004;
137
132
  uint16 internal constant MOVE_FROM_STACK_TO_INTERNAL = 0x8005;
138
133
  uint16 internal constant MOVE_FROM_INTERNAL_TO_STACK = 0x8006;
139
- uint16 internal constant IS_STACK_BOUNDARY = 0x8007;
140
134
  uint16 internal constant DUP = 0x8008;
141
135
  uint16 internal constant CROSS_MODULE_CALL = 0x8009;
142
136
  uint16 internal constant CALLER_MODULE_INTERNAL_CALL = 0x800A;
@@ -150,8 +144,6 @@ library Instructions {
150
144
  uint16 internal constant READ_INBOX_MESSAGE = 0x8021;
151
145
  uint16 internal constant HALT_AND_SET_FINISHED = 0x8022;
152
146
 
153
- uint16 internal constant ARBITRARY_JUMP = 0x8023;
154
-
155
147
  uint256 internal constant INBOX_INDEX_SEQUENCER = 0;
156
148
  uint256 internal constant INBOX_INDEX_DELAYED = 1;
157
149
 
@@ -5,7 +5,6 @@
5
5
  pragma solidity ^0.8.0;
6
6
 
7
7
  import "./ValueStack.sol";
8
- import "./PcStack.sol";
9
8
  import "./Instructions.sol";
10
9
  import "./StackFrame.sol";
11
10
 
@@ -20,7 +19,6 @@ struct Machine {
20
19
  MachineStatus status;
21
20
  ValueStack valueStack;
22
21
  ValueStack internalStack;
23
- PcStack blockStack;
24
22
  StackFrameWindow frameStack;
25
23
  bytes32 globalStateHash;
26
24
  uint32 moduleIdx;
@@ -30,7 +28,6 @@ struct Machine {
30
28
  }
31
29
 
32
30
  library MachineLib {
33
- using PcStackLib for PcStack;
34
31
  using StackFrameLib for StackFrameWindow;
35
32
  using ValueStackLib for ValueStack;
36
33
 
@@ -43,7 +40,6 @@ library MachineLib {
43
40
  "Machine running:",
44
41
  mach.valueStack.hash(),
45
42
  mach.internalStack.hash(),
46
- mach.blockStack.hash(),
47
43
  mach.frameStack.hash(),
48
44
  mach.globalStateHash,
49
45
  mach.moduleIdx,
@@ -9,6 +9,7 @@ import "./Deserialize.sol";
9
9
 
10
10
  struct ModuleMemory {
11
11
  uint64 size;
12
+ uint64 maxSize;
12
13
  bytes32 merkleRoot;
13
14
  }
14
15
 
@@ -16,7 +17,7 @@ library ModuleMemoryLib {
16
17
  using MerkleProofLib for MerkleProof;
17
18
 
18
19
  function hash(ModuleMemory memory mem) internal pure returns (bytes32) {
19
- return keccak256(abi.encodePacked("Memory:", mem.size, mem.merkleRoot));
20
+ return keccak256(abi.encodePacked("Memory:", mem.size, mem.maxSize, mem.merkleRoot));
20
21
  }
21
22
 
22
23
  function proveLeaf(
@@ -11,8 +11,7 @@ enum ValueType {
11
11
  F64,
12
12
  REF_NULL,
13
13
  FUNC_REF,
14
- INTERNAL_REF,
15
- STACK_BOUNDARY
14
+ INTERNAL_REF
16
15
  }
17
16
 
18
17
  struct Value {
@@ -26,7 +25,7 @@ library ValueLib {
26
25
  }
27
26
 
28
27
  function maxValueType() internal pure returns (ValueType) {
29
- return ValueType.STACK_BOUNDARY;
28
+ return ValueType.INTERNAL_REF;
30
29
  }
31
30
 
32
31
  function assumeI32(Value memory val) internal pure returns (uint32) {
@@ -0,0 +1,233 @@
1
+ // Copyright 2021-2022, Offchain Labs, Inc.
2
+ // For license information, see https://github.com/nitro/blob/master/LICENSE
3
+ // SPDX-License-Identifier: BUSL-1.1
4
+
5
+ pragma solidity ^0.8.4;
6
+
7
+ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
8
+ import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
9
+
10
+ import {
11
+ NotContract,
12
+ NotRollupOrOwner,
13
+ NotDelayedInbox,
14
+ NotSequencerInbox,
15
+ NotOutbox,
16
+ InvalidOutboxSet
17
+ } from "../libraries/Error.sol";
18
+ import "../bridge/IBridge.sol";
19
+ import "../bridge/Messages.sol";
20
+ import "../libraries/DelegateCallAware.sol";
21
+
22
+ /**
23
+ * @title Staging ground for incoming and outgoing messages
24
+ * @notice Holds the inbox accumulator for delayed messages, and is the ETH escrow
25
+ * for value sent with these messages.
26
+ * Since the escrow is held here, this contract also contains a list of allowed
27
+ * outboxes that can make calls from here and withdraw this escrow.
28
+ */
29
+ contract BridgeTester is Initializable, DelegateCallAware, IBridge {
30
+ using AddressUpgradeable for address;
31
+
32
+ struct InOutInfo {
33
+ uint256 index;
34
+ bool allowed;
35
+ }
36
+
37
+ mapping(address => InOutInfo) private allowedInboxesMap;
38
+ mapping(address => InOutInfo) private allowedOutboxesMap;
39
+
40
+ address[] public allowedDelayedInboxList;
41
+ address[] public allowedOutboxList;
42
+
43
+ address private _activeOutbox;
44
+
45
+ IOwnable public rollup;
46
+ address public sequencerInbox;
47
+
48
+ modifier onlyRollupOrOwner() {
49
+ if (msg.sender != address(rollup)) {
50
+ address rollupOwner = rollup.owner();
51
+ if (msg.sender != rollupOwner) {
52
+ revert NotRollupOrOwner(msg.sender, address(rollup), rollupOwner);
53
+ }
54
+ }
55
+ _;
56
+ }
57
+
58
+ function setSequencerInbox(address _sequencerInbox) external override onlyRollupOrOwner {
59
+ sequencerInbox = _sequencerInbox;
60
+ emit SequencerInboxUpdated(_sequencerInbox);
61
+ }
62
+
63
+ /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
64
+ bytes32[] public override delayedInboxAccs;
65
+
66
+ bytes32[] public override sequencerInboxAccs;
67
+
68
+ address private constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max);
69
+
70
+ function initialize(IOwnable rollup_) external initializer {
71
+ _activeOutbox = EMPTY_ACTIVEOUTBOX;
72
+ rollup = rollup_;
73
+ }
74
+
75
+ function activeOutbox() public view returns (address) {
76
+ if (_activeOutbox == EMPTY_ACTIVEOUTBOX) return address(uint160(0));
77
+ return _activeOutbox;
78
+ }
79
+
80
+ function allowedDelayedInboxes(address inbox) external view override returns (bool) {
81
+ return allowedInboxesMap[inbox].allowed;
82
+ }
83
+
84
+ function allowedOutboxes(address outbox) external view override returns (bool) {
85
+ return allowedOutboxesMap[outbox].allowed;
86
+ }
87
+
88
+ function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead)
89
+ external
90
+ returns (
91
+ uint256 seqMessageIndex,
92
+ bytes32 beforeAcc,
93
+ bytes32 delayedAcc,
94
+ bytes32 acc
95
+ )
96
+ {
97
+ // TODO: implement stub logic
98
+ }
99
+
100
+ function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
101
+ external
102
+ returns (uint256)
103
+ {
104
+ // TODO: implement stub
105
+ }
106
+
107
+ /**
108
+ * @dev Enqueue a message in the delayed inbox accumulator.
109
+ * These messages are later sequenced in the SequencerInbox, either by the sequencer as
110
+ * part of a normal batch, or by force inclusion.
111
+ */
112
+ function enqueueDelayedMessage(
113
+ uint8 kind,
114
+ address sender,
115
+ bytes32 messageDataHash
116
+ ) external payable override returns (uint256) {
117
+ if (!allowedInboxesMap[msg.sender].allowed) revert NotDelayedInbox(msg.sender);
118
+ return
119
+ addMessageToDelayedAccumulator(
120
+ kind,
121
+ sender,
122
+ uint64(block.number),
123
+ uint64(block.timestamp), // solhint-disable-line not-rely-on-time
124
+ block.basefee,
125
+ messageDataHash
126
+ );
127
+ }
128
+
129
+ function addMessageToDelayedAccumulator(
130
+ uint8 kind,
131
+ address sender,
132
+ uint64 blockNumber,
133
+ uint64 blockTimestamp,
134
+ uint256 baseFeeL1,
135
+ bytes32 messageDataHash
136
+ ) internal returns (uint256) {
137
+ uint256 count = delayedInboxAccs.length;
138
+ bytes32 messageHash = Messages.messageHash(
139
+ kind,
140
+ sender,
141
+ blockNumber,
142
+ blockTimestamp,
143
+ count,
144
+ baseFeeL1,
145
+ messageDataHash
146
+ );
147
+ bytes32 prevAcc = 0;
148
+ if (count > 0) {
149
+ prevAcc = delayedInboxAccs[count - 1];
150
+ }
151
+ delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash));
152
+ emit MessageDelivered(
153
+ count,
154
+ prevAcc,
155
+ msg.sender,
156
+ kind,
157
+ sender,
158
+ messageDataHash,
159
+ baseFeeL1,
160
+ blockTimestamp
161
+ );
162
+ return count;
163
+ }
164
+
165
+ function executeCall(
166
+ address to,
167
+ uint256 value,
168
+ bytes calldata data
169
+ ) external override returns (bool success, bytes memory returnData) {
170
+ if (!allowedOutboxesMap[msg.sender].allowed) revert NotOutbox(msg.sender);
171
+ if (data.length > 0 && !to.isContract()) revert NotContract(to);
172
+ address prevOutbox = _activeOutbox;
173
+ _activeOutbox = msg.sender;
174
+ // We set and reset active outbox around external call so activeOutbox remains valid during call
175
+
176
+ // We use a low level call here since we want to bubble up whether it succeeded or failed to the caller
177
+ // rather than reverting on failure as well as allow contract and non-contract calls
178
+ // solhint-disable-next-line avoid-low-level-calls
179
+ (success, returnData) = to.call{value: value}(data);
180
+ _activeOutbox = prevOutbox;
181
+ emit BridgeCallTriggered(msg.sender, to, value, data);
182
+ }
183
+
184
+ function setDelayedInbox(address inbox, bool enabled) external override onlyRollupOrOwner {
185
+ InOutInfo storage info = allowedInboxesMap[inbox];
186
+ bool alreadyEnabled = info.allowed;
187
+ emit InboxToggle(inbox, enabled);
188
+ if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
189
+ return;
190
+ }
191
+ if (enabled) {
192
+ allowedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true);
193
+ allowedDelayedInboxList.push(inbox);
194
+ } else {
195
+ allowedDelayedInboxList[info.index] = allowedDelayedInboxList[
196
+ allowedDelayedInboxList.length - 1
197
+ ];
198
+ allowedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index;
199
+ allowedDelayedInboxList.pop();
200
+ delete allowedInboxesMap[inbox];
201
+ }
202
+ }
203
+
204
+ function setOutbox(address outbox, bool enabled) external override onlyRollupOrOwner {
205
+ InOutInfo storage info = allowedOutboxesMap[outbox];
206
+ bool alreadyEnabled = info.allowed;
207
+ emit OutboxToggle(outbox, enabled);
208
+ if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
209
+ return;
210
+ }
211
+ if (enabled) {
212
+ allowedOutboxesMap[outbox] = InOutInfo(allowedOutboxList.length, true);
213
+ allowedOutboxList.push(outbox);
214
+ } else {
215
+ allowedOutboxList[info.index] = allowedOutboxList[allowedOutboxList.length - 1];
216
+ allowedOutboxesMap[allowedOutboxList[info.index]].index = info.index;
217
+ allowedOutboxList.pop();
218
+ delete allowedOutboxesMap[outbox];
219
+ }
220
+ }
221
+
222
+ function delayedMessageCount() external view override returns (uint256) {
223
+ return delayedInboxAccs.length;
224
+ }
225
+
226
+ function sequencerMessageCount() external view override returns (uint256) {
227
+ return sequencerInboxAccs.length;
228
+ }
229
+
230
+ receive() external payable {}
231
+
232
+ function acceptFundsFromOldBridge() external payable {}
233
+ }
@@ -0,0 +1,11 @@
1
+ // Copyright 2021-2022, Offchain Labs, Inc.
2
+ // For license information, see https://github.com/nitro/blob/master/LICENSE
3
+ // SPDX-License-Identifier: BUSL-1.1
4
+
5
+ // solhint-disable-next-line compiler-version
6
+ pragma solidity >=0.6.9 <0.9.0;
7
+
8
+ import "../bridge/IBridge.sol";
9
+ import "../bridge/IOutbox.sol";
10
+ import "../bridge/IInbox.sol";
11
+ import "../bridge/ISequencerInbox.sol";
@@ -0,0 +1,214 @@
1
+ // Copyright 2021-2022, Offchain Labs, Inc.
2
+ // For license information, see https://github.com/nitro/blob/master/LICENSE
3
+ // SPDX-License-Identifier: BUSL-1.1
4
+
5
+ pragma solidity ^0.8.4;
6
+
7
+ import {
8
+ AlreadyInit,
9
+ NotRollup,
10
+ ProofTooLong,
11
+ PathNotMinimal,
12
+ UnknownRoot,
13
+ AlreadySpent,
14
+ BridgeCallFailed
15
+ } from "../libraries/Error.sol";
16
+ import "../bridge/IBridge.sol";
17
+ import "../bridge/IOutbox.sol";
18
+ import "../libraries/MerkleLib.sol";
19
+ import "../libraries/DelegateCallAware.sol";
20
+
21
+ contract OutboxWithoutOptTester is DelegateCallAware, IOutbox {
22
+ address public rollup; // the rollup contract
23
+ IBridge public bridge; // the bridge contract
24
+
25
+ mapping(uint256 => bool) public spent; // maps leaf number => if spent
26
+ mapping(bytes32 => bytes32) public roots; // maps root hashes => L2 block hash
27
+
28
+ struct L2ToL1Context {
29
+ uint128 l2Block;
30
+ uint128 l1Block;
31
+ uint128 timestamp;
32
+ bytes32 outputId;
33
+ address sender;
34
+ }
35
+ // Note, these variables are set and then wiped during a single transaction.
36
+ // Therefore their values don't need to be maintained, and their slots will
37
+ // be empty outside of transactions
38
+ L2ToL1Context internal context;
39
+ uint128 public constant OUTBOX_VERSION = 2;
40
+
41
+ function initialize(IBridge _bridge) external {
42
+ if (address(bridge) != address(0)) revert AlreadyInit();
43
+ bridge = _bridge;
44
+ rollup = address(_bridge.rollup());
45
+ }
46
+
47
+ function updateSendRoot(bytes32 root, bytes32 l2BlockHash) external override {
48
+ //if (msg.sender != rollup) revert NotRollup(msg.sender, rollup); //test only!!!
49
+ roots[root] = l2BlockHash;
50
+ emit SendRootUpdated(root, l2BlockHash);
51
+ }
52
+
53
+ /// @notice When l2ToL1Sender returns a nonzero address, the message was originated by an L2 account
54
+ /// When the return value is zero, that means this is a system message
55
+ /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies
56
+ function l2ToL1Sender() external view override returns (address) {
57
+ return context.sender;
58
+ }
59
+
60
+ function l2ToL1Block() external view override returns (uint256) {
61
+ return uint256(context.l2Block);
62
+ }
63
+
64
+ function l2ToL1EthBlock() external view override returns (uint256) {
65
+ return uint256(context.l1Block);
66
+ }
67
+
68
+ function l2ToL1Timestamp() external view override returns (uint256) {
69
+ return uint256(context.timestamp);
70
+ }
71
+
72
+ // @deprecated batch number is now always 0
73
+ function l2ToL1BatchNum() external pure override returns (uint256) {
74
+ return 0;
75
+ }
76
+
77
+ function l2ToL1OutputId() external view override returns (bytes32) {
78
+ return context.outputId;
79
+ }
80
+
81
+ /**
82
+ * @notice Executes a messages in an Outbox entry.
83
+ * @dev Reverts if dispute period hasn't expired, since the outbox entry
84
+ * is only created once the rollup confirms the respective assertion.
85
+ * @param proof Merkle proof of message inclusion in send root
86
+ * @param index Merkle path to message
87
+ * @param l2Sender sender if original message (i.e., caller of ArbSys.sendTxToL1)
88
+ * @param to destination address for L1 contract call
89
+ * @param l2Block l2 block number at which sendTxToL1 call was made
90
+ * @param l1Block l1 block number at which sendTxToL1 call was made
91
+ * @param l2Timestamp l2 Timestamp at which sendTxToL1 call was made
92
+ * @param value wei in L1 message
93
+ * @param data abi-encoded L1 message data
94
+ */
95
+ function executeTransaction(
96
+ bytes32[] calldata proof,
97
+ uint256 index,
98
+ address l2Sender,
99
+ address to,
100
+ uint256 l2Block,
101
+ uint256 l1Block,
102
+ uint256 l2Timestamp,
103
+ uint256 value,
104
+ bytes calldata data
105
+ ) external virtual override {
106
+ bytes32 outputId;
107
+ {
108
+ bytes32 userTx = calculateItemHash(
109
+ l2Sender,
110
+ to,
111
+ l2Block,
112
+ l1Block,
113
+ l2Timestamp,
114
+ value,
115
+ data
116
+ );
117
+
118
+ outputId = recordOutputAsSpent(proof, index, userTx);
119
+ emit OutBoxTransactionExecuted(to, l2Sender, 0, index);
120
+ }
121
+
122
+ // we temporarily store the previous values so the outbox can naturally
123
+ // unwind itself when there are nested calls to `executeTransaction`
124
+ L2ToL1Context memory prevContext = context;
125
+
126
+ context = L2ToL1Context({
127
+ sender: l2Sender,
128
+ l2Block: uint128(l2Block),
129
+ l1Block: uint128(l1Block),
130
+ timestamp: uint128(l2Timestamp),
131
+ outputId: outputId
132
+ });
133
+
134
+ // set and reset vars around execution so they remain valid during call
135
+ executeBridgeCall(to, value, data);
136
+
137
+ context = prevContext;
138
+ }
139
+
140
+ function executeTransactionSimulation(
141
+ uint256,
142
+ address,
143
+ address,
144
+ uint256,
145
+ uint256,
146
+ uint256,
147
+ uint256,
148
+ bytes calldata
149
+ ) external pure override {
150
+ revert("Not implemented");
151
+ }
152
+
153
+ function isSpent(uint256) external pure override returns (bool) {
154
+ revert("Not implemented");
155
+ }
156
+
157
+ function recordOutputAsSpent(
158
+ bytes32[] memory proof,
159
+ uint256 index,
160
+ bytes32 item
161
+ ) internal returns (bytes32) {
162
+ if (proof.length >= 256) revert ProofTooLong(proof.length);
163
+ if (index >= 2**proof.length) revert PathNotMinimal(index, 2**proof.length);
164
+
165
+ // Hash the leaf an extra time to prove it's a leaf
166
+ bytes32 calcRoot = calculateMerkleRoot(proof, index, item);
167
+ if (roots[calcRoot] == bytes32(0)) revert UnknownRoot(calcRoot);
168
+
169
+ if (spent[index]) revert AlreadySpent(index);
170
+ spent[index] = true;
171
+
172
+ return bytes32(index);
173
+ }
174
+
175
+ function executeBridgeCall(
176
+ address to,
177
+ uint256 value,
178
+ bytes memory data
179
+ ) internal {
180
+ (bool success, bytes memory returndata) = bridge.executeCall(to, value, data);
181
+ if (!success) {
182
+ if (returndata.length > 0) {
183
+ // solhint-disable-next-line no-inline-assembly
184
+ assembly {
185
+ let returndata_size := mload(returndata)
186
+ revert(add(32, returndata), returndata_size)
187
+ }
188
+ } else {
189
+ revert BridgeCallFailed();
190
+ }
191
+ }
192
+ }
193
+
194
+ function calculateItemHash(
195
+ address l2Sender,
196
+ address to,
197
+ uint256 l2Block,
198
+ uint256 l1Block,
199
+ uint256 l2Timestamp,
200
+ uint256 value,
201
+ bytes calldata data
202
+ ) public pure override returns (bytes32) {
203
+ return
204
+ keccak256(abi.encodePacked(l2Sender, to, l2Block, l1Block, l2Timestamp, value, data));
205
+ }
206
+
207
+ function calculateMerkleRoot(
208
+ bytes32[] memory proof,
209
+ uint256 path,
210
+ bytes32 item
211
+ ) public pure override returns (bytes32) {
212
+ return MerkleLib.calculateRoot(proof, path, keccak256(abi.encodePacked(item)));
213
+ }
214
+ }