@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
@@ -21,16 +21,26 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
21
21
  onlyProxy
22
22
  initializer
23
23
  {
24
- delayedBridge = connectedContracts.delayedBridge;
25
- sequencerBridge = connectedContracts.sequencerInbox;
24
+ rollupDeploymentBlock = block.number;
25
+ bridge = connectedContracts.bridge;
26
+ sequencerInbox = connectedContracts.sequencerInbox;
27
+ connectedContracts.bridge.setDelayedInbox(address(connectedContracts.inbox), true);
28
+ connectedContracts.bridge.setSequencerInbox(address(connectedContracts.sequencerInbox));
29
+
30
+ inbox = connectedContracts.inbox;
26
31
  outbox = connectedContracts.outbox;
27
- delayedBridge.setOutbox(address(connectedContracts.outbox), true);
28
- rollupEventBridge = connectedContracts.rollupEventBridge;
29
- delayedBridge.setInbox(address(connectedContracts.rollupEventBridge), true);
32
+ connectedContracts.bridge.setOutbox(address(connectedContracts.outbox), true);
33
+ rollupEventInbox = connectedContracts.rollupEventInbox;
34
+ connectedContracts.bridge.setDelayedInbox(
35
+ address(connectedContracts.rollupEventInbox),
36
+ true
37
+ );
30
38
 
31
- rollupEventBridge.rollupInitialized(config.chainId);
32
- sequencerBridge.addSequencerL2Batch(0, "", 1, IGasRefunder(address(0)));
39
+ connectedContracts.rollupEventInbox.rollupInitialized(config.chainId);
40
+ connectedContracts.sequencerInbox.addSequencerL2Batch(0, "", 1, IGasRefunder(address(0)));
33
41
 
42
+ validatorUtils = connectedContracts.validatorUtils;
43
+ validatorWalletCreator = connectedContracts.validatorWalletCreator;
34
44
  challengeManager = connectedContracts.challengeManager;
35
45
 
36
46
  Node memory node = createInitialNode();
@@ -53,8 +63,6 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
53
63
 
54
64
  stakeToken = config.stakeToken;
55
65
 
56
- sequencerBridge.setMaxTimeVariation(config.sequencerInboxMaxTimeVariation);
57
-
58
66
  emit RollupInitialized(config.wasmModuleRoot, config.chainId);
59
67
  }
60
68
 
@@ -86,7 +94,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
86
94
  */
87
95
  function setOutbox(IOutbox _outbox) external override {
88
96
  outbox = _outbox;
89
- delayedBridge.setOutbox(address(_outbox), true);
97
+ bridge.setOutbox(address(_outbox), true);
90
98
  emit OwnerFunctionCalled(0);
91
99
  }
92
100
 
@@ -96,7 +104,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
96
104
  */
97
105
  function removeOldOutbox(address _outbox) external override {
98
106
  require(_outbox != address(outbox), "CUR_OUTBOX");
99
- delayedBridge.setOutbox(_outbox, false);
107
+ bridge.setOutbox(_outbox, false);
100
108
  emit OwnerFunctionCalled(1);
101
109
  }
102
110
 
@@ -105,8 +113,8 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
105
113
  * @param _inbox Inbox contract to add or remove
106
114
  * @param _enabled New status of inbox
107
115
  */
108
- function setInbox(address _inbox, bool _enabled) external override {
109
- delayedBridge.setInbox(address(_inbox), _enabled);
116
+ function setDelayedInbox(address _inbox, bool _enabled) external override {
117
+ bridge.setDelayedInbox(address(_inbox), _enabled);
110
118
  emit OwnerFunctionCalled(2);
111
119
  }
112
120
 
@@ -224,27 +232,6 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
224
232
  emit OwnerFunctionCalled(13);
225
233
  }
226
234
 
227
- /**
228
- * @notice Set max delay for sequencer inbox
229
- * @param maxTimeVariation the maximum time variation parameters
230
- */
231
- function setSequencerInboxMaxTimeVariation(
232
- ISequencerInbox.MaxTimeVariation calldata maxTimeVariation
233
- ) external override {
234
- sequencerBridge.setMaxTimeVariation(maxTimeVariation);
235
- emit OwnerFunctionCalled(14);
236
- }
237
-
238
- /**
239
- * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
240
- * @param addr the address
241
- * @param isBatchPoster if the specified address should be authorized as a batch poster
242
- */
243
- function setIsBatchPoster(address addr, bool isBatchPoster) external override {
244
- ISequencerInbox(sequencerBridge).setIsBatchPoster(addr, isBatchPoster);
245
- emit OwnerFunctionCalled(19);
246
- }
247
-
248
235
  /**
249
236
  * @notice Upgrades the implementation of a beacon controlled by the rollup
250
237
  * @param beacon address of beacon to be upgraded
@@ -319,4 +306,55 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade
319
306
  wasmModuleRoot = newWasmModuleRoot;
320
307
  emit OwnerFunctionCalled(26);
321
308
  }
309
+
310
+ /**
311
+ * @notice set a new sequencer inbox contract
312
+ * @param _sequencerInbox new address of sequencer inbox
313
+ */
314
+ function setSequencerInbox(address _sequencerInbox) external override {
315
+ bridge.setSequencerInbox(_sequencerInbox);
316
+ emit OwnerFunctionCalled(27);
317
+ }
318
+
319
+ /**
320
+ * @notice sets the rollup's inbox reference. Does not update the bridge's view.
321
+ * @param newInbox new address of inbox
322
+ */
323
+ function setInbox(IInbox newInbox) external {
324
+ inbox = newInbox;
325
+ emit OwnerFunctionCalled(28);
326
+ }
327
+
328
+ function createNitroMigrationGenesis(RollupLib.Assertion calldata assertion)
329
+ external
330
+ whenPaused
331
+ {
332
+ bytes32 expectedSendRoot = bytes32(0);
333
+ uint64 expectedInboxCount = 1;
334
+
335
+ require(latestNodeCreated() == 0, "NON_GENESIS_NODES_EXIST");
336
+ require(GlobalStateLib.isEmpty(assertion.beforeState.globalState), "NOT_EMPTY_BEFORE");
337
+ require(
338
+ assertion.beforeState.machineStatus == MachineStatus.FINISHED,
339
+ "BEFORE_MACHINE_NOT_FINISHED"
340
+ );
341
+ // accessors such as state.getSendRoot not available for calldata structs, only memory
342
+ require(
343
+ assertion.afterState.globalState.bytes32Vals[1] == expectedSendRoot,
344
+ "NOT_ZERO_SENDROOT"
345
+ );
346
+ require(
347
+ assertion.afterState.globalState.u64Vals[0] == expectedInboxCount,
348
+ "INBOX_NOT_AT_ONE"
349
+ );
350
+ require(assertion.afterState.globalState.u64Vals[1] == 0, "POSITION_IN_MESSAGE_NOT_ZERO");
351
+ require(
352
+ assertion.afterState.machineStatus == MachineStatus.FINISHED,
353
+ "AFTER_MACHINE_NOT_FINISHED"
354
+ );
355
+ bytes32 genesisBlockHash = assertion.afterState.globalState.bytes32Vals[0];
356
+ createNewNode(assertion, 0, expectedInboxCount, bytes32(0));
357
+ confirmNode(1, genesisBlockHash, expectedSendRoot);
358
+ emit OwnerFunctionCalled(29);
359
+ }
322
360
  }
@@ -9,7 +9,7 @@ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
9
9
  import "./Node.sol";
10
10
  import "./IRollupCore.sol";
11
11
  import "./RollupLib.sol";
12
- import "./IRollupEventBridge.sol";
12
+ import "./IRollupEventInbox.sol";
13
13
  import "./IRollupCore.sol";
14
14
 
15
15
  import "../challenge/IChallengeManager.sol";
@@ -31,11 +31,17 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
31
31
  uint256 public baseStake;
32
32
  bytes32 public wasmModuleRoot;
33
33
 
34
- IBridge public delayedBridge;
35
- ISequencerInbox public sequencerBridge;
34
+ IInbox public inbox;
35
+ IBridge public bridge;
36
36
  IOutbox public outbox;
37
- IRollupEventBridge public rollupEventBridge;
37
+ ISequencerInbox public sequencerInbox;
38
+ IRollupEventInbox public rollupEventInbox;
38
39
  IChallengeManager public override challengeManager;
40
+
41
+ // misc useful contracts when interacting with the rollup
42
+ address public validatorUtils;
43
+ address public validatorWalletCreator;
44
+
39
45
  // when a staker loses a challenge, half of their funds get escrowed in this address
40
46
  address public loserStakeEscrow;
41
47
  address public stakeToken;
@@ -63,6 +69,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
63
69
 
64
70
  mapping(address => uint256) private _withdrawableFunds;
65
71
  uint256 public totalWithdrawableFunds;
72
+ uint256 public rollupDeploymentBlock;
66
73
 
67
74
  // The node number of the initial node
68
75
  uint64 internal constant GENESIS_NODE = 0;
@@ -529,7 +536,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
529
536
  {
530
537
  // validate data
531
538
  memoryFrame.prevNode = getNode(prevNodeNum);
532
- memoryFrame.currentInboxSize = sequencerBridge.batchCount();
539
+ memoryFrame.currentInboxSize = bridge.sequencerMessageCount();
533
540
 
534
541
  // Make sure the previous state is correct against the node being built on
535
542
  require(
@@ -545,7 +552,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
545
552
  if (afterInboxCount == prevInboxPosition) {
546
553
  require(
547
554
  assertion.afterState.globalState.getPositionInMessage() >=
548
- assertion.afterState.globalState.getPositionInMessage(),
555
+ assertion.beforeState.globalState.getPositionInMessage(),
549
556
  "INBOX_POS_IN_MSG_BACKWARDS"
550
557
  );
551
558
  }
@@ -560,7 +567,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
560
567
  require(afterInboxCount <= memoryFrame.currentInboxSize, "INBOX_PAST_END");
561
568
  // This gives replay protection against the state of the inbox
562
569
  if (afterInboxCount > 0) {
563
- memoryFrame.sequencerBatchAcc = sequencerBridge.inboxAccs(afterInboxCount - 1);
570
+ memoryFrame.sequencerBatchAcc = bridge.sequencerInboxAccs(afterInboxCount - 1);
564
571
  }
565
572
  }
566
573
 
@@ -582,9 +589,13 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
582
589
  memoryFrame.hasSibling,
583
590
  memoryFrame.lastHash,
584
591
  memoryFrame.executionHash,
585
- memoryFrame.sequencerBatchAcc
592
+ memoryFrame.sequencerBatchAcc,
593
+ wasmModuleRoot
594
+ );
595
+ require(
596
+ newNodeHash == expectedNodeHash || expectedNodeHash == bytes32(0),
597
+ "UNEXPECTED_NODE_HASH"
586
598
  );
587
- require(newNodeHash == expectedNodeHash, "UNEXPECTED_NODE_HASH");
588
599
 
589
600
  memoryFrame.node = NodeLib.createNode(
590
601
  RollupLib.stateHash(assertion.afterState, memoryFrame.currentInboxSize),
@@ -18,7 +18,7 @@ contract RollupCreator is Ownable {
18
18
  address inboxAddress,
19
19
  address adminProxy,
20
20
  address sequencerInbox,
21
- address delayedBridge
21
+ address bridge
22
22
  );
23
23
  event TemplatesUpdated();
24
24
 
@@ -28,6 +28,9 @@ contract RollupCreator is Ownable {
28
28
  IRollupAdmin public rollupAdminLogic;
29
29
  IRollupUser public rollupUserLogic;
30
30
 
31
+ address public validatorUtils;
32
+ address public validatorWalletCreator;
33
+
31
34
  constructor() Ownable() {}
32
35
 
33
36
  function setTemplates(
@@ -35,22 +38,26 @@ contract RollupCreator is Ownable {
35
38
  IOneStepProofEntry _osp,
36
39
  IChallengeManager _challengeManagerLogic,
37
40
  IRollupAdmin _rollupAdminLogic,
38
- IRollupUser _rollupUserLogic
41
+ IRollupUser _rollupUserLogic,
42
+ address _validatorUtils,
43
+ address _validatorWalletCreator
39
44
  ) external onlyOwner {
40
45
  bridgeCreator = _bridgeCreator;
41
46
  osp = _osp;
42
47
  challengeManagerTemplate = _challengeManagerLogic;
43
48
  rollupAdminLogic = _rollupAdminLogic;
44
49
  rollupUserLogic = _rollupUserLogic;
50
+ validatorUtils = _validatorUtils;
51
+ validatorWalletCreator = _validatorWalletCreator;
45
52
  emit TemplatesUpdated();
46
53
  }
47
54
 
48
55
  struct CreateRollupFrame {
49
56
  ProxyAdmin admin;
50
- IBridge delayedBridge;
57
+ IBridge bridge;
51
58
  ISequencerInbox sequencerInbox;
52
59
  IInbox inbox;
53
- IRollupEventBridge rollupEventBridge;
60
+ IRollupEventInbox rollupEventInbox;
54
61
  IOutbox outbox;
55
62
  ArbitrumProxy rollup;
56
63
  }
@@ -68,10 +75,10 @@ contract RollupCreator is Ownable {
68
75
  frame.admin = new ProxyAdmin();
69
76
 
70
77
  (
71
- frame.delayedBridge,
78
+ frame.bridge,
72
79
  frame.sequencerInbox,
73
80
  frame.inbox,
74
- frame.rollupEventBridge,
81
+ frame.rollupEventInbox,
75
82
  frame.outbox
76
83
  ) = bridgeCreator.createBridge(
77
84
  address(frame.admin),
@@ -93,20 +100,23 @@ contract RollupCreator is Ownable {
93
100
  challengeManager.initialize(
94
101
  IChallengeResultReceiver(expectedRollupAddr),
95
102
  frame.sequencerInbox,
96
- frame.delayedBridge,
103
+ frame.bridge,
97
104
  osp
98
105
  );
99
106
 
100
107
  frame.rollup = new ArbitrumProxy(
101
108
  config,
102
109
  ContractDependencies({
103
- delayedBridge: frame.delayedBridge,
110
+ bridge: frame.bridge,
104
111
  sequencerInbox: frame.sequencerInbox,
112
+ inbox: frame.inbox,
105
113
  outbox: frame.outbox,
106
- rollupEventBridge: frame.rollupEventBridge,
114
+ rollupEventInbox: frame.rollupEventInbox,
107
115
  challengeManager: challengeManager,
108
116
  rollupAdminLogic: rollupAdminLogic,
109
- rollupUserLogic: rollupUserLogic
117
+ rollupUserLogic: rollupUserLogic,
118
+ validatorUtils: validatorUtils,
119
+ validatorWalletCreator: validatorWalletCreator
110
120
  })
111
121
  );
112
122
  require(address(frame.rollup) == expectedRollupAddr, "WRONG_ROLLUP_ADDR");
@@ -116,7 +126,7 @@ contract RollupCreator is Ownable {
116
126
  address(frame.inbox),
117
127
  address(frame.admin),
118
128
  address(frame.sequencerInbox),
119
- address(frame.delayedBridge)
129
+ address(frame.bridge)
120
130
  );
121
131
  return address(frame.rollup);
122
132
  }
@@ -4,36 +4,36 @@
4
4
 
5
5
  pragma solidity ^0.8.0;
6
6
 
7
- import "./IRollupEventBridge.sol";
7
+ import "./IRollupEventInbox.sol";
8
8
  import "../bridge/IBridge.sol";
9
- import "../bridge/IMessageProvider.sol";
9
+ import "../bridge/IDelayedMessageProvider.sol";
10
10
  import "../libraries/DelegateCallAware.sol";
11
11
  import {INITIALIZATION_MSG_TYPE} from "../libraries/MessageTypes.sol";
12
12
 
13
13
  /**
14
14
  * @title The inbox for rollup protocol events
15
15
  */
16
- contract RollupEventBridge is IRollupEventBridge, IMessageProvider, DelegateCallAware {
16
+ contract RollupEventInbox is IRollupEventInbox, IDelayedMessageProvider, DelegateCallAware {
17
17
  uint8 internal constant CREATE_NODE_EVENT = 0;
18
18
  uint8 internal constant CONFIRM_NODE_EVENT = 1;
19
19
  uint8 internal constant REJECT_NODE_EVENT = 2;
20
20
  uint8 internal constant STAKE_CREATED_EVENT = 3;
21
21
 
22
- IBridge public bridge;
23
- address public rollup;
22
+ IBridge public override bridge;
23
+ address public override rollup;
24
24
 
25
25
  modifier onlyRollup() {
26
26
  require(msg.sender == rollup, "ONLY_ROLLUP");
27
27
  _;
28
28
  }
29
29
 
30
- function initialize(address _bridge, address _rollup) external onlyDelegated {
31
- require(rollup == address(0), "ALREADY_INIT");
32
- bridge = IBridge(_bridge);
33
- rollup = _rollup;
30
+ function initialize(IBridge _bridge) external override onlyDelegated {
31
+ require(address(bridge) == address(0), "ALREADY_INIT");
32
+ bridge = _bridge;
33
+ rollup = address(_bridge.rollup());
34
34
  }
35
35
 
36
- function rollupInitialized(uint256 chainId) external onlyRollup {
36
+ function rollupInitialized(uint256 chainId) external override onlyRollup {
37
37
  bytes memory initMsg = abi.encodePacked(chainId);
38
38
  uint256 num = bridge.enqueueDelayedMessage(
39
39
  INITIALIZATION_MSG_TYPE,
@@ -11,7 +11,8 @@ import "../bridge/ISequencerInbox.sol";
11
11
 
12
12
  import "../bridge/IBridge.sol";
13
13
  import "../bridge/IOutbox.sol";
14
- import "./IRollupEventBridge.sol";
14
+ import "../bridge/IInbox.sol";
15
+ import "./IRollupEventInbox.sol";
15
16
  import "./IRollupLogic.sol";
16
17
 
17
18
  struct Config {
@@ -23,17 +24,22 @@ struct Config {
23
24
  address owner;
24
25
  address loserStakeEscrow;
25
26
  uint256 chainId;
27
+ uint64 genesisBlockNum;
26
28
  ISequencerInbox.MaxTimeVariation sequencerInboxMaxTimeVariation;
27
29
  }
28
30
 
29
31
  struct ContractDependencies {
30
- IBridge delayedBridge;
32
+ IBridge bridge;
31
33
  ISequencerInbox sequencerInbox;
34
+ IInbox inbox;
32
35
  IOutbox outbox;
33
- IRollupEventBridge rollupEventBridge;
36
+ IRollupEventInbox rollupEventInbox;
34
37
  IChallengeManager challengeManager;
35
38
  IRollupAdmin rollupAdminLogic;
36
39
  IRollupUser rollupUserLogic;
40
+ // misc contracts that are useful when interacting with the rollup
41
+ address validatorUtils;
42
+ address validatorWalletCreator;
37
43
  }
38
44
 
39
45
  library RollupLib {
@@ -127,9 +133,19 @@ library RollupLib {
127
133
  bool hasSibling,
128
134
  bytes32 lastHash,
129
135
  bytes32 assertionExecHash,
130
- bytes32 inboxAcc
136
+ bytes32 inboxAcc,
137
+ bytes32 wasmModuleRoot
131
138
  ) internal pure returns (bytes32) {
132
139
  uint8 hasSiblingInt = hasSibling ? 1 : 0;
133
- return keccak256(abi.encodePacked(hasSiblingInt, lastHash, assertionExecHash, inboxAcc));
140
+ return
141
+ keccak256(
142
+ abi.encodePacked(
143
+ hasSiblingInt,
144
+ lastHash,
145
+ assertionExecHash,
146
+ inboxAcc,
147
+ wasmModuleRoot
148
+ )
149
+ );
134
150
  }
135
151
  }
@@ -487,6 +487,10 @@ abstract contract AbsRollupUserLogic is
487
487
  return currentRequiredStake(blockNumber, firstUnresolvedNodeNum, latestCreatedNode);
488
488
  }
489
489
 
490
+ function owner() external view returns (address) {
491
+ return _getAdmin();
492
+ }
493
+
490
494
  function currentRequiredStake() public view returns (uint256) {
491
495
  uint64 firstUnresolvedNodeNum = firstUnresolvedNode();
492
496
 
@@ -615,18 +619,13 @@ contract RollupUserLogic is AbsRollupUserLogic, IRollupUser {
615
619
 
616
620
  /**
617
621
  * @notice Withdraw uncommitted funds owned by sender from the rollup chain
618
- * @param destination Address to transfer the withdrawn funds to
619
622
  */
620
- function withdrawStakerFunds(address payable destination)
621
- external
622
- override
623
- onlyValidator
624
- whenNotPaused
625
- returns (uint256)
626
- {
623
+ function withdrawStakerFunds() external override onlyValidator whenNotPaused returns (uint256) {
627
624
  uint256 amount = withdrawFunds(msg.sender);
628
625
  // This is safe because it occurs after all checks and effects
629
- destination.transfer(amount);
626
+ // solhint-disable-next-line avoid-low-level-calls
627
+ (bool success, ) = msg.sender.call{value: amount}("");
628
+ require(success, "TRANSFER_FAILED");
630
629
  return amount;
631
630
  }
632
631
  }
@@ -692,18 +691,11 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 {
692
691
 
693
692
  /**
694
693
  * @notice Withdraw uncommitted funds owned by sender from the rollup chain
695
- * @param destination Address to transfer the withdrawn funds to
696
694
  */
697
- function withdrawStakerFunds(address payable destination)
698
- external
699
- override
700
- onlyValidator
701
- whenNotPaused
702
- returns (uint256)
703
- {
695
+ function withdrawStakerFunds() external override onlyValidator whenNotPaused returns (uint256) {
704
696
  uint256 amount = withdrawFunds(msg.sender);
705
697
  // This is safe because it occurs after all checks and effects
706
- require(IERC20Upgradeable(stakeToken).transfer(destination, amount), "TRANSFER_FAILED");
698
+ require(IERC20Upgradeable(stakeToken).transfer(msg.sender, amount), "TRANSFER_FAILED");
707
699
  return amount;
708
700
  }
709
701
 
@@ -6,24 +6,117 @@ pragma solidity ^0.8.0;
6
6
 
7
7
  import "../challenge/IChallengeManager.sol";
8
8
  import "../libraries/DelegateCallAware.sol";
9
+ import "../libraries/IGasRefunder.sol";
9
10
  import "@openzeppelin/contracts/utils/Address.sol";
10
11
  import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
11
12
 
12
- contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware {
13
+ /// @dev thrown when arrays provided don't have the expected length
14
+ error BadArrayLength(uint256 expected, uint256 actual);
15
+
16
+ /// @dev thrown when a function is called by an address that isn't the owner nor a executor
17
+ error NotExecutorOrOwner(address actual);
18
+
19
+ /// @dev thrown when the particular address can't be called by an executor
20
+ error OnlyOwnerDestination(address expected, address actual, address destination);
21
+
22
+ /// @dev thrown when eth withdrawal tx fails
23
+ error WithdrawEthFail(address destination);
24
+
25
+ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware, GasRefundEnabled {
13
26
  using Address for address;
14
27
 
15
- function initialize() external initializer onlyDelegated {
28
+ /// @dev a executor is allowed to call only certain contracts
29
+ mapping(address => bool) public executors;
30
+
31
+ /// @dev allowed addresses which can be called by an executor
32
+ mapping(address => bool) public allowedExecutorDestinations;
33
+
34
+ modifier onlyExecutorOrOwner() {
35
+ if (!executors[_msgSender()] && owner() != _msgSender())
36
+ revert NotExecutorOrOwner(_msgSender());
37
+ _;
38
+ }
39
+
40
+ event ExecutorUpdated(address indexed executor, bool isExecutor);
41
+
42
+ /// @dev updates the executor addresses
43
+ function setExecutor(address[] calldata newExecutors, bool[] calldata isExecutor)
44
+ external
45
+ onlyOwner
46
+ {
47
+ if (newExecutors.length != isExecutor.length)
48
+ revert BadArrayLength(newExecutors.length, isExecutor.length);
49
+ unchecked {
50
+ for (uint64 i = 0; i < newExecutors.length; ++i) {
51
+ executors[newExecutors[i]] = isExecutor[i];
52
+ emit ExecutorUpdated(newExecutors[i], isExecutor[i]);
53
+ }
54
+ }
55
+ }
56
+
57
+ function initialize(
58
+ address _executor,
59
+ address _owner,
60
+ address[] calldata initialExecutorAllowedDests
61
+ ) external initializer onlyDelegated {
16
62
  __Ownable_init();
63
+ transferOwnership(_owner);
64
+
65
+ executors[_executor] = true;
66
+ emit ExecutorUpdated(_executor, true);
67
+
68
+ unchecked {
69
+ for (uint64 i = 0; i < initialExecutorAllowedDests.length; ++i) {
70
+ allowedExecutorDestinations[initialExecutorAllowedDests[i]] = true;
71
+ emit AllowedExecutorDestinationsUpdated(initialExecutorAllowedDests[i], true);
72
+ }
73
+ }
74
+ }
75
+
76
+ event AllowedExecutorDestinationsUpdated(address indexed destination, bool isSet);
77
+
78
+ /// @notice updates the destination addresses which executors are allowed to call
79
+ function setAllowedExecutorDestinations(address[] calldata destinations, bool[] calldata isSet)
80
+ external
81
+ onlyOwner
82
+ {
83
+ if (destinations.length != isSet.length)
84
+ revert BadArrayLength(destinations.length, isSet.length);
85
+ unchecked {
86
+ for (uint256 i = 0; i < destinations.length; ++i) {
87
+ allowedExecutorDestinations[destinations[i]] = isSet[i];
88
+ emit AllowedExecutorDestinationsUpdated(destinations[i], isSet[i]);
89
+ }
90
+ }
91
+ }
92
+
93
+ /// @dev reverts if the current function can't be called
94
+ function validateExecuteTransaction(address destination) public view {
95
+ if (!allowedExecutorDestinations[destination] && owner() != _msgSender())
96
+ revert OnlyOwnerDestination(owner(), _msgSender(), destination);
17
97
  }
18
98
 
19
99
  function executeTransactions(
20
100
  bytes[] calldata data,
21
101
  address[] calldata destination,
22
102
  uint256[] calldata amount
23
- ) external payable onlyOwner {
103
+ ) external payable {
104
+ executeTransactionsWithGasRefunder(IGasRefunder(address(0)), data, destination, amount);
105
+ }
106
+
107
+ function executeTransactionsWithGasRefunder(
108
+ IGasRefunder gasRefunder,
109
+ bytes[] calldata data,
110
+ address[] calldata destination,
111
+ uint256[] calldata amount
112
+ ) public payable onlyExecutorOrOwner refundsGas(gasRefunder) {
24
113
  uint256 numTxes = data.length;
114
+ if (numTxes != destination.length) revert BadArrayLength(numTxes, destination.length);
115
+ if (numTxes != amount.length) revert BadArrayLength(numTxes, amount.length);
116
+
25
117
  for (uint256 i = 0; i < numTxes; i++) {
26
118
  if (data[i].length > 0) require(destination[i].isContract(), "NO_CODE_AT_ADDR");
119
+ validateExecuteTransaction(destination[i]);
27
120
  // We use a low level call here to allow for contract and non-contract calls
28
121
  // solhint-disable-next-line avoid-low-level-calls
29
122
  (bool success, ) = address(destination[i]).call{value: amount[i]}(data[i]);
@@ -42,8 +135,18 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware {
42
135
  bytes calldata data,
43
136
  address destination,
44
137
  uint256 amount
45
- ) external payable onlyOwner {
138
+ ) external payable {
139
+ executeTransactionWithGasRefunder(IGasRefunder(address(0)), data, destination, amount);
140
+ }
141
+
142
+ function executeTransactionWithGasRefunder(
143
+ IGasRefunder gasRefunder,
144
+ bytes calldata data,
145
+ address destination,
146
+ uint256 amount
147
+ ) public payable onlyExecutorOrOwner refundsGas(gasRefunder) {
46
148
  if (data.length > 0) require(destination.isContract(), "NO_CODE_AT_ADDR");
149
+ validateExecuteTransaction(destination);
47
150
  // We use a low level call here to allow for contract and non-contract calls
48
151
  // solhint-disable-next-line avoid-low-level-calls
49
152
  (bool success, ) = destination.call{value: amount}(data);
@@ -57,10 +160,15 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware {
57
160
  }
58
161
  }
59
162
 
60
- function timeoutChallenges(IChallengeManager manager, uint64[] calldata challenges)
61
- external
62
- onlyOwner
63
- {
163
+ function timeoutChallenges(IChallengeManager manager, uint64[] calldata challenges) external {
164
+ timeoutChallengesWithGasRefunder(IGasRefunder(address(0)), manager, challenges);
165
+ }
166
+
167
+ function timeoutChallengesWithGasRefunder(
168
+ IGasRefunder gasRefunder,
169
+ IChallengeManager manager,
170
+ uint64[] calldata challenges
171
+ ) public onlyExecutorOrOwner refundsGas(gasRefunder) {
64
172
  uint256 challengesCount = challenges.length;
65
173
  for (uint256 i = 0; i < challengesCount; i++) {
66
174
  try manager.timeout(challenges[i]) {} catch (bytes memory error) {
@@ -72,4 +180,13 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware {
72
180
  }
73
181
  }
74
182
  }
183
+
184
+ receive() external payable {}
185
+
186
+ /// @dev allows the owner to withdraw eth held by this contract
187
+ function withdrawEth(uint256 amount, address destination) external onlyOwner {
188
+ // solhint-disable-next-line avoid-low-level-calls
189
+ (bool success, ) = destination.call{value: amount}("");
190
+ if (!success) revert WithdrawEthFail(destination);
191
+ }
75
192
  }