@arbitrum/nitro-contracts 1.0.2 → 1.1.0-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbitrum/nitro-contracts",
3
- "version": "1.0.2",
3
+ "version": "1.1.0-alpha.0",
4
4
  "description": "Layer 2 precompiles and rollup for Arbitrum Nitro",
5
5
  "author": "Offchain Labs, Inc.",
6
6
  "license": "BUSL-1.1",
@@ -60,8 +60,8 @@
60
60
  "solhint": "^3.3.7",
61
61
  "solhint-plugin-prettier": "^0.0.5",
62
62
  "solidity-coverage": "^0.7.20",
63
- "tslint": "^6.1.3",
64
63
  "ts-node": "^10.4.0",
64
+ "tslint": "^6.1.3",
65
65
  "typechain": "^8.0.0",
66
66
  "typescript": "^4.5.4"
67
67
  },
@@ -230,7 +230,7 @@ contract Bridge is Initializable, DelegateCallAware, IBridge {
230
230
  InOutInfo storage info = allowedDelayedInboxesMap[inbox];
231
231
  bool alreadyEnabled = info.allowed;
232
232
  emit InboxToggle(inbox, enabled);
233
- if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
233
+ if (alreadyEnabled == enabled) {
234
234
  return;
235
235
  }
236
236
  if (enabled) {
@@ -252,7 +252,7 @@ contract Bridge is Initializable, DelegateCallAware, IBridge {
252
252
  InOutInfo storage info = allowedOutboxesMap[outbox];
253
253
  bool alreadyEnabled = info.allowed;
254
254
  emit OutboxToggle(outbox, enabled);
255
- if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
255
+ if (alreadyEnabled == enabled) {
256
256
  return;
257
257
  }
258
258
  if (enabled) {
@@ -69,6 +69,8 @@ interface ISequencerInbox is IDelayedMessageProvider {
69
69
 
70
70
  function isBatchPoster(address) external view returns (bool);
71
71
 
72
+ function isSequencer(address) external view returns (bool);
73
+
72
74
  struct DasKeySetInfo {
73
75
  bool isValidKeyset;
74
76
  uint64 creationBlock;
@@ -154,6 +156,14 @@ interface ISequencerInbox is IDelayedMessageProvider {
154
156
  */
155
157
  function invalidateKeysetHash(bytes32 ksHash) external;
156
158
 
159
+ /**
160
+ * @notice Updates whether an address is authorized to be a sequencer.
161
+ * @dev The IsSequencer information is used only off-chain by the nitro node to validate sequencer feed signer.
162
+ * @param addr the address
163
+ * @param isSequencer_ if the specified address should be authorized as a sequencer
164
+ */
165
+ function setIsSequencer(address addr, bool isSequencer_) external;
166
+
157
167
  // ---------- initializer ----------
158
168
 
159
169
  function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
@@ -352,7 +352,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
352
352
  nonce,
353
353
  uint256(uint160(address(100))), // ArbSys address
354
354
  value,
355
- abi.encode(ArbSys.withdrawEth.selector, withdrawTo)
355
+ abi.encodeWithSelector(ArbSys.withdrawEth.selector, withdrawTo)
356
356
  )
357
357
  );
358
358
  }
@@ -64,6 +64,8 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
64
64
 
65
65
  uint256 internal immutable deployTimeChainId = block.chainid;
66
66
 
67
+ mapping(address => bool) public isSequencer;
68
+
67
69
  function _chainIdChanged() internal view returns (bool) {
68
70
  return deployTimeChainId != block.chainid;
69
71
  }
@@ -450,6 +452,12 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
450
452
  emit OwnerFunctionCalled(3);
451
453
  }
452
454
 
455
+ /// @inheritdoc ISequencerInbox
456
+ function setIsSequencer(address addr, bool isSequencer_) external onlyRollupOwner {
457
+ isSequencer[addr] = isSequencer_;
458
+ emit OwnerFunctionCalled(4);
459
+ }
460
+
453
461
  function isValidKeysetHash(bytes32 ksHash) external view returns (bool) {
454
462
  return dasKeySetInfo[ksHash].isValidKeyset;
455
463
  }
@@ -107,13 +107,13 @@ contract AdminFallbackProxy is Proxy, DoubleLogicERC1967Upgrade {
107
107
  * Only the `adminAddr` is able to use the `adminLogic` functions
108
108
  * All other addresses can interact with the `userLogic` functions
109
109
  */
110
- constructor(
110
+ function _initialize(
111
111
  address adminLogic,
112
112
  bytes memory adminData,
113
113
  address userLogic,
114
114
  bytes memory userData,
115
115
  address adminAddr
116
- ) payable {
116
+ ) internal {
117
117
  assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
118
118
  assert(
119
119
  _IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
@@ -21,10 +21,7 @@ abstract contract GasRefundEnabled {
21
21
  uint256 startGasLeft = gasleft();
22
22
  _;
23
23
  if (address(gasRefunder) != address(0)) {
24
- uint256 calldataSize;
25
- assembly {
26
- calldataSize := calldatasize()
27
- }
24
+ uint256 calldataSize = msg.data.length;
28
25
  uint256 calldataWords = (calldataSize + 31) / 32;
29
26
  // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost
30
27
  startGasLeft += calldataWords * 6 + (calldataWords**2) / 512;
@@ -139,7 +139,7 @@ contract BridgeStub is IBridge {
139
139
  InOutInfo storage info = allowedDelayedInboxesMap[inbox];
140
140
  bool alreadyEnabled = info.allowed;
141
141
  emit InboxToggle(inbox, enabled);
142
- if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
142
+ if (alreadyEnabled == enabled) {
143
143
  return;
144
144
  }
145
145
  if (enabled) {
@@ -43,6 +43,10 @@ contract Simple {
43
43
  return block.number;
44
44
  }
45
45
 
46
+ function getBlockDifficulty() external view returns (uint256) {
47
+ return block.difficulty;
48
+ }
49
+
46
50
  function noop() external pure {}
47
51
 
48
52
  function pleaseRevert() external pure {
@@ -98,8 +98,10 @@ interface NodeInterface {
98
98
  /**
99
99
  * @notice Estimates a transaction's l1 costs.
100
100
  * @dev Use eth_call to call.
101
- * This method is exactly like gasEstimateComponents, but doesn't include the l2 component
101
+ * This method is similar to gasEstimateComponents, but doesn't include the l2 component
102
102
  * so that the l1 component can be known even when the tx may fail.
103
+ * This method also doesn't pad the estimate as gas estimation normally does.
104
+ * If using this value to submit a transaction, we'd recommend first padding it by 10%.
103
105
  * @param data the tx's calldata. Everything else like "From" and "Gas" are copied over
104
106
  * @param to the tx's "To" (ignored when contractCreation is true)
105
107
  * @param contractCreation whether "To" is omitted
@@ -84,6 +84,9 @@ interface ArbOwner {
84
84
  /// @notice Releases surplus funds from L1PricerFundsPoolAddress for use
85
85
  function releaseL1PricerSurplusFunds(uint256 maxWeiToRelease) external returns (uint256);
86
86
 
87
+ /// @notice Sets serialized chain config in ArbOS state
88
+ function setChainConfig(string calldata chainConfig) external;
89
+
87
90
  // Emitted when a successful call is made to this precompile
88
91
  event OwnerActs(bytes4 indexed method, address indexed owner, bytes data);
89
92
  }
@@ -84,6 +84,15 @@ interface IRollupCore {
84
84
  */
85
85
  function getNode(uint64 nodeNum) external view returns (Node memory);
86
86
 
87
+ /**
88
+ * @notice Returns the block in which the given node was created for looking up its creation event.
89
+ * Unlike the Node's createdAtBlock field, this will be the ArbSys blockNumber if the host chain is an Arbitrum chain.
90
+ * That means that the block number returned for this is usable for event queries.
91
+ * This function will revert if the given node number does not exist.
92
+ * @dev This function is meant for internal use only and has no stability guarantees.
93
+ */
94
+ function getNodeCreationBlockForLogLookup(uint64 nodeNum) external view returns (uint256);
95
+
87
96
  /**
88
97
  * @notice Check if the specified node has been staked on by the provided staker.
89
98
  * Only accurate at the latest confirmed node and afterwards.
@@ -13,5 +13,5 @@ interface IRollupEventInbox {
13
13
 
14
14
  function rollup() external view returns (address);
15
15
 
16
- function rollupInitialized(uint256 chainId) external;
16
+ function rollupInitialized(uint256 chainId, string calldata chainConfig) external;
17
17
  }
@@ -36,7 +36,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, DoubleLogicUUPSUpgradeabl
36
36
  true
37
37
  );
38
38
 
39
- connectedContracts.rollupEventInbox.rollupInitialized(config.chainId);
39
+ connectedContracts.rollupEventInbox.rollupInitialized(config.chainId, config.chainConfig);
40
40
  connectedContracts.sequencerInbox.addSequencerL2Batch(
41
41
  0,
42
42
  "",
@@ -18,6 +18,8 @@ import "../bridge/ISequencerInbox.sol";
18
18
  import "../bridge/IBridge.sol";
19
19
  import "../bridge/IOutbox.sol";
20
20
 
21
+ import "../precompiles/ArbSys.sol";
22
+
21
23
  import {NO_CHAL_INDEX} from "../libraries/Constants.sol";
22
24
 
23
25
  abstract contract RollupCore is IRollupCore, PausableUpgradeable {
@@ -76,6 +78,18 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
76
78
 
77
79
  bool public validatorWhitelistDisabled;
78
80
 
81
+ // If the chain this RollupCore is deployed on is an Arbitrum chain.
82
+ bool internal immutable _hostChainIsArbitrum;
83
+ // If the chain RollupCore is deployed on, this will contain the ArbSys.blockNumber() at each node's creation.
84
+ mapping(uint64 => uint256) internal _nodeCreatedAtArbSysBlock;
85
+
86
+ constructor() {
87
+ (bool ok, bytes memory data) = address(100).staticcall(
88
+ abi.encodeWithSelector(ArbSys.arbOSVersion.selector)
89
+ );
90
+ _hostChainIsArbitrum = ok && data.length == 32;
91
+ }
92
+
79
93
  /**
80
94
  * @notice Get a storage reference to the Node for the given node index
81
95
  * @param nodeNum Index of the node
@@ -92,6 +106,30 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
92
106
  return getNodeStorage(nodeNum);
93
107
  }
94
108
 
109
+ /**
110
+ * @notice Returns the block in which the given node was created for looking up its creation event.
111
+ * Unlike the Node's createdAtBlock field, this will be the ArbSys blockNumber if the host chain is an Arbitrum chain.
112
+ * That means that the block number returned for this is usable for event queries.
113
+ * This function will revert if the given node number does not exist.
114
+ * @dev This function is meant for internal use only and has no stability guarantees.
115
+ */
116
+ function getNodeCreationBlockForLogLookup(uint64 nodeNum)
117
+ external
118
+ view
119
+ override
120
+ returns (uint256)
121
+ {
122
+ if (_hostChainIsArbitrum) {
123
+ uint256 blockNum = _nodeCreatedAtArbSysBlock[nodeNum];
124
+ require(blockNum > 0, "NO_NODE");
125
+ return blockNum;
126
+ } else {
127
+ Node storage node = getNodeStorage(nodeNum);
128
+ require(node.deadlineBlock != 0, "NO_NODE");
129
+ return node.createdAtBlock;
130
+ }
131
+ }
132
+
95
133
  /**
96
134
  * @notice Check if the specified node has been staked on by the provided staker.
97
135
  * Only accurate at the latest confirmed node and afterwards.
@@ -250,6 +288,9 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
250
288
  __Pausable_init();
251
289
  _nodes[GENESIS_NODE] = initialNode;
252
290
  _firstUnresolvedNode = GENESIS_NODE + 1;
291
+ if (_hostChainIsArbitrum) {
292
+ _nodeCreatedAtArbSysBlock[GENESIS_NODE] = ArbSys(address(100)).arbBlockNumber();
293
+ }
253
294
  }
254
295
 
255
296
  /**
@@ -259,6 +300,9 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
259
300
  function nodeCreated(Node memory node) internal {
260
301
  _latestNodeCreated++;
261
302
  _nodes[_latestNodeCreated] = node;
303
+ if (_hostChainIsArbitrum) {
304
+ _nodeCreatedAtArbSysBlock[_latestNodeCreated] = ArbSys(address(100)).arbBlockNumber();
305
+ }
262
306
  }
263
307
 
264
308
  /// @notice Reject the next unresolved node
@@ -52,66 +52,55 @@ contract RollupCreator is Ownable {
52
52
  emit TemplatesUpdated();
53
53
  }
54
54
 
55
- struct CreateRollupFrame {
56
- ProxyAdmin admin;
57
- IBridge bridge;
58
- ISequencerInbox sequencerInbox;
59
- IInbox inbox;
60
- IRollupEventInbox rollupEventInbox;
61
- IOutbox outbox;
62
- RollupProxy rollup;
63
- }
64
-
65
55
  // After this setup:
66
56
  // Rollup should be the owner of bridge
67
57
  // RollupOwner should be the owner of Rollup's ProxyAdmin
68
58
  // RollupOwner should be the owner of Rollup
69
59
  // Bridge should have a single inbox and outbox
70
- function createRollup(Config memory config, address expectedRollupAddr)
71
- external
72
- returns (address)
73
- {
74
- CreateRollupFrame memory frame;
75
- frame.admin = new ProxyAdmin();
60
+ function createRollup(Config memory config) external returns (address) {
61
+ ProxyAdmin proxyAdmin = new ProxyAdmin();
62
+
63
+ // Create the rollup proxy to figure out the address and initialize it later
64
+ RollupProxy rollup = new RollupProxy{salt: keccak256(abi.encode(config))}();
76
65
 
77
66
  (
78
- frame.bridge,
79
- frame.sequencerInbox,
80
- frame.inbox,
81
- frame.rollupEventInbox,
82
- frame.outbox
67
+ IBridge bridge,
68
+ ISequencerInbox sequencerInbox,
69
+ IInbox inbox,
70
+ IRollupEventInbox rollupEventInbox,
71
+ IOutbox outbox
83
72
  ) = bridgeCreator.createBridge(
84
- address(frame.admin),
85
- expectedRollupAddr,
86
- config.sequencerInboxMaxTimeVariation
87
- );
73
+ address(proxyAdmin),
74
+ address(rollup),
75
+ config.sequencerInboxMaxTimeVariation
76
+ );
88
77
 
89
- frame.admin.transferOwnership(config.owner);
78
+ proxyAdmin.transferOwnership(config.owner);
90
79
 
91
80
  IChallengeManager challengeManager = IChallengeManager(
92
81
  address(
93
82
  new TransparentUpgradeableProxy(
94
83
  address(challengeManagerTemplate),
95
- address(frame.admin),
84
+ address(proxyAdmin),
96
85
  ""
97
86
  )
98
87
  )
99
88
  );
100
89
  challengeManager.initialize(
101
- IChallengeResultReceiver(expectedRollupAddr),
102
- frame.sequencerInbox,
103
- frame.bridge,
90
+ IChallengeResultReceiver(address(rollup)),
91
+ sequencerInbox,
92
+ bridge,
104
93
  osp
105
94
  );
106
95
 
107
- frame.rollup = new RollupProxy(
96
+ rollup.initializeProxy(
108
97
  config,
109
98
  ContractDependencies({
110
- bridge: frame.bridge,
111
- sequencerInbox: frame.sequencerInbox,
112
- inbox: frame.inbox,
113
- outbox: frame.outbox,
114
- rollupEventInbox: frame.rollupEventInbox,
99
+ bridge: bridge,
100
+ sequencerInbox: sequencerInbox,
101
+ inbox: inbox,
102
+ outbox: outbox,
103
+ rollupEventInbox: rollupEventInbox,
115
104
  challengeManager: challengeManager,
116
105
  rollupAdminLogic: rollupAdminLogic,
117
106
  rollupUserLogic: rollupUserLogic,
@@ -119,15 +108,14 @@ contract RollupCreator is Ownable {
119
108
  validatorWalletCreator: validatorWalletCreator
120
109
  })
121
110
  );
122
- require(address(frame.rollup) == expectedRollupAddr, "WRONG_ROLLUP_ADDR");
123
111
 
124
112
  emit RollupCreated(
125
- address(frame.rollup),
126
- address(frame.inbox),
127
- address(frame.admin),
128
- address(frame.sequencerInbox),
129
- address(frame.bridge)
113
+ address(rollup),
114
+ address(inbox),
115
+ address(proxyAdmin),
116
+ address(sequencerInbox),
117
+ address(bridge)
130
118
  );
131
- return address(frame.rollup);
119
+ return address(rollup);
132
120
  }
133
121
  }
@@ -30,8 +30,13 @@ contract RollupEventInbox is IRollupEventInbox, IDelayedMessageProvider, Delegat
30
30
  rollup = address(_bridge.rollup());
31
31
  }
32
32
 
33
- function rollupInitialized(uint256 chainId) external override onlyRollup {
34
- bytes memory initMsg = abi.encodePacked(chainId);
33
+ function rollupInitialized(uint256 chainId, string calldata chainConfig)
34
+ external
35
+ override
36
+ onlyRollup
37
+ {
38
+ require(bytes(chainConfig).length > 0, "EMPTY_CHAIN_CONFIG");
39
+ bytes memory initMsg = abi.encodePacked(chainId, uint8(0), chainConfig);
35
40
  uint256 num = bridge.enqueueDelayedMessage(
36
41
  INITIALIZATION_MSG_TYPE,
37
42
  address(0),
@@ -24,6 +24,7 @@ struct Config {
24
24
  address owner;
25
25
  address loserStakeEscrow;
26
26
  uint256 chainId;
27
+ string chainConfig;
27
28
  uint64 genesisBlockNum;
28
29
  ISequencerInbox.MaxTimeVariation sequencerInboxMaxTimeVariation;
29
30
  }
@@ -8,13 +8,27 @@ import "../libraries/AdminFallbackProxy.sol";
8
8
  import "./IRollupLogic.sol";
9
9
 
10
10
  contract RollupProxy is AdminFallbackProxy {
11
- constructor(Config memory config, ContractDependencies memory connectedContracts)
12
- AdminFallbackProxy(
13
- address(connectedContracts.rollupAdminLogic),
14
- abi.encodeWithSelector(IRollupAdmin.initialize.selector, config, connectedContracts),
15
- address(connectedContracts.rollupUserLogic),
16
- abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken),
17
- config.owner
18
- )
19
- {}
11
+ function initializeProxy(Config memory config, ContractDependencies memory connectedContracts)
12
+ external
13
+ {
14
+ if (
15
+ _getAdmin() == address(0) &&
16
+ _getImplementation() == address(0) &&
17
+ _getSecondaryImplementation() == address(0)
18
+ ) {
19
+ _initialize(
20
+ address(connectedContracts.rollupAdminLogic),
21
+ abi.encodeWithSelector(
22
+ IRollupAdmin.initialize.selector,
23
+ config,
24
+ connectedContracts
25
+ ),
26
+ address(connectedContracts.rollupUserLogic),
27
+ abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken),
28
+ config.owner
29
+ );
30
+ } else {
31
+ _fallback();
32
+ }
33
+ }
20
34
  }