diff --git a/specs/experimental/inbox-contract.md b/specs/protocol/holocene/inbox-contract.md similarity index 53% rename from specs/experimental/inbox-contract.md rename to specs/protocol/holocene/inbox-contract.md index 7834479e0..4601278c2 100644 --- a/specs/experimental/inbox-contract.md +++ b/specs/protocol/holocene/inbox-contract.md @@ -7,20 +7,23 @@ - [Motivation](#motivation) - [How It Works](#how-it-works) - [Supporting Dynamic Updates to Inbox Address](#supporting-dynamic-updates-to-inbox-address) - - [SystemConfig](#systemconfig) + - [SystemConfigHolocene](#systemconfigholocene) - [setBatchInbox](#setbatchinbox) - - [initialize](#initialize) - - [UpdateType](#updatetype) - - [L1 Info Deposit Transaction](#l1-info-deposit-transaction) - - [L1Block](#l1block) + - [L1BlockHolocene](#l1blockholocene) - [batchInbox](#batchinbox) - - [setL1BlockValuesEcotone](#setl1blockvaluesecotone) + - [ConfigType](#configtype) + - [setConfig](#setconfig) + - [StaticConfig](#staticconfig) + - [encodeSetBatchInbox](#encodesetbatchinbox) + - [decodeSetBatchInbox](#decodesetbatchinbox) + - [L2Client.SystemConfigByL2Hash](#l2clientsystemconfigbyl2hash) + - [L1Traversal.AdvanceL1Block](#l1traversaladvancel1block) - [How `op-node` knows the canonical batch inbox](#how-op-node-knows-the-canonical-batch-inbox) - [How `op-batcher` knows canonical batch inbox](#how-op-batcher-knows-canonical-batch-inbox) - [Upgrade](#upgrade) - [L1Block Deployment](#l1block-deployment) - [L1Block Proxy Update](#l1block-proxy-update) - - [SystemConfig Upgrade](#systemconfig-upgrade) + - [SystemConfigHolocene Upgrade](#systemconfigholocene-upgrade) - [Security Considerations](#security-considerations) - [Inbox Sender](#inbox-sender) - [Reference Implementation](#reference-implementation) @@ -55,188 +58,134 @@ These modifications aim to enhance the security and efficiency of the batch subm ## Supporting Dynamic Updates to Inbox Address -### SystemConfig +### SystemConfigHolocene -The `SystemConfig` is the source of truth for the address of inbox. It stores information about the inbox address and passes the information to L2 as well. +The `SystemConfigHolocene` is the source of truth for the address of inbox. It stores information about the inbox address and passes the information to L2 as well. #### setBatchInbox -A new function `setBatchInbox` is introduced to the `SystemConfig` contract, enabling dynamic updates to the inbox: +A new function `setBatchInbox` is introduced to the `SystemConfigHolocene` contract, enabling dynamic updates to the inbox: ```solidity /// @notice Updates the batch inbox address. Can only be called by the owner. /// @param _batchInbox New batch inbox address. function setBatchInbox(address _batchInbox) external onlyOwner { - _setBatchInbox(_batchInbox); + if (_batchInbox != batchInbox()) { + Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox); + OptimismPortal(payable(optimismPortal())).setConfig( + ConfigType.SET_BATCH_INBOX, + StaticConfig.encodeSetBatchInbox({ + _batchInbox: _batchInbox, + }) + ); + } } +``` -/// @notice Updates the batch inbox address. -/// @param _batchInbox New batch inbox address. -function _setBatchInbox(address _batchInbox) internal { - Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox); - bytes memory data = abi.encode(_batchInbox); - emit ConfigUpdate(VERSION, UpdateType.BATCH_INBOX, data); -} -``` +### L1BlockHolocene +The `L1BlockHolocene` stores Layer 1 (L1) related information on Layer 2 (L2). It is extended to store the dynamic inbox address. -#### initialize +#### batchInbox -The `SystemConfig` now emits an event when the inbox is initialized, while retaining its existing support for inbox configuration during initialization. +A new field `batchInbox` is added to the `L1BlockHolocene`: ```solidity -function initialize( - address _owner, - uint32 _basefeeScalar, - uint32 _blobbasefeeScalar, - bytes32 _batcherHash, - uint64 _gasLimit, - address _unsafeBlockSigner, - ResourceMetering.ResourceConfig memory _config, - address _batchInbox, - SystemConfig.Addresses memory _addresses -) - public - initializer -{ - ... - // Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox); - // initialize inbox by `_setBatchInbox` so that an event is emitted. - _setBatchInbox(_batchInbox); - ... -} + /// @notice The canonical batch inbox. + bytes32 public batchInbox; ``` -#### UpdateType +#### ConfigType -A new enum value `BATCH_INBOX` is added to the `UpdateType` enumeration. +A new enum value `SET_BATCH_INBOX` is added to the `ConfigType` enumeration. ```solidity -enum UpdateType { - BATCHER, - GAS_CONFIG, - GAS_LIMIT, - UNSAFE_BLOCK_SIGNER, - BATCH_INBOX +enum ConfigType { + SET_GAS_PAYING_TOKEN, + ADD_DEPENDENCY, + REMOVE_DEPENDENCY, + SET_BATCH_INBOX } ``` -### L1 Info Deposit Transaction -The [`L1InfoDeposit`](https://github.com/ethereum-optimism/optimism/blob/5e317379fae65b76f5a6ee27581f0e62d2fe017a/op-node/rollup/derive/l1_block_info.go#L264) function creates a L1 Info deposit transaction based on the L1 block. It is extended to add an additional `batchInbox` parameter for the [`setL1BlockValuesEcotone`](https://github.com/ethereum-optimism/optimism/blob/5e317379fae65b76f5a6ee27581f0e62d2fe017a/packages/contracts-bedrock/src/L2/L1Block.sol#L136) function after the inbox contract feature is activated. +#### setConfig -```golang -func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber uint64, block eth.BlockInfo, l2BlockTime uint64) (*types.DepositTx, error) { - ... - if isInboxForkButNotFirstBlock(rollupCfg, l2BlockTime) { - l1BlockInfo.BatchInbox = sysCfg.BatchInbox - l1BlockInfo.BlobBaseFee = block.BlobBaseFee() - if l1BlockInfo.BlobBaseFee == nil { - // The L2 spec states to use the MIN_BLOB_GASPRICE from EIP-4844 if not yet active on L1. - l1BlockInfo.BlobBaseFee = big.NewInt(1) - } - scalars, err := sysCfg.EcotoneScalars() - if err != nil { - return nil, err - } - l1BlockInfo.BlobBaseFeeScalar = scalars.BlobBaseFeeScalar - l1BlockInfo.BaseFeeScalar = scalars.BaseFeeScalar - // marshalBinaryInboxFork adds an additional `batchInbox` parameter based on marshalBinaryEcotone. - out, err := l1BlockInfo.marshalBinaryInboxFork() - if err != nil { - return nil, fmt.Errorf("failed to marshal InboxFork l1 block info: %w", err) - } - data = out - } else if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) { +This function stores Layer 1 (L1) system configs for Layer 2 (L2). It is extended to support the newly introduced `SET_BATCH_INBOX` ConfigType. + +```solidity +function setConfig(ConfigType _type, bytes calldata _value) external { ... + else if (_type == ConfigType.SET_BATCH_INBOX) { + _setBatchInbox(_value); + } } -``` - -The `marshalBinaryInboxFork` function is added to [`L1BlockInfo`](https://github.com/ethereum-optimism/optimism/blob/5e317379fae65b76f5a6ee27581f0e62d2fe017a/op-node/rollup/derive/l1_block_info.go#L41). This new function incorporates an additional `batchInbox` parameter for the [`setL1BlockValuesEcotone`](https://github.com/ethereum-optimism/optimism/blob/5e317379fae65b76f5a6ee27581f0e62d2fe017a/packages/contracts-bedrock/src/L2/L1Block.sol#L136) function: -```golang -func (info *L1BlockInfo) marshalBinaryInboxFork() ([]byte, error) { - w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) - if err := solabi.WriteSignature(w, L1InfoFuncEcotoneBytes4); err != nil { - return nil, err - } - if err := binary.Write(w, binary.BigEndian, info.BaseFeeScalar); err != nil { - return nil, err - } - if err := binary.Write(w, binary.BigEndian, info.BlobBaseFeeScalar); err != nil { - return nil, err - } - if err := binary.Write(w, binary.BigEndian, info.SequenceNumber); err != nil { - return nil, err - } - if err := binary.Write(w, binary.BigEndian, info.Time); err != nil { - return nil, err - } - if err := binary.Write(w, binary.BigEndian, info.Number); err != nil { - return nil, err - } - if err := solabi.WriteUint256(w, info.BaseFee); err != nil { - return nil, err - } - blobBasefee := info.BlobBaseFee - if blobBasefee == nil { - blobBasefee = big.NewInt(1) // set to 1, to match the min blob basefee as defined in EIP-4844 - } - if err := solabi.WriteUint256(w, blobBasefee); err != nil { - return nil, err - } - if err := solabi.WriteHash(w, info.BlockHash); err != nil { - return nil, err - } - // ABI encoding will perform the left-padding with zeroes to 32 bytes, matching the "batcherHash" SystemConfig format and version 0 byte. - if err := solabi.WriteAddress(w, info.BatcherAddr); err != nil { - return nil, err - } - // This is where marshalBinaryInboxFork differs from marshalBinaryEcotone - if err := solabi.WriteAddress(w, info.BatchInbox); err != nil { - return nil, err - } - return w.Bytes(), nil +/// @notice Internal method to set the batch inbox address. +/// @param _value The encoded value with which to set the batch inbox address. +function _setBatchInbox(bytes calldata _value) internal { + batchInbox = StaticConfig.decodeSetBatchInbox(_value); } ``` -### L1Block +### StaticConfig -The `L1Block` stores Layer 1 (L1) related information on Layer 2 (L2). It is extended to store the dynamic inbox address. +The `StaticConfig` library is used for encoding and decoding static configuration data. -#### batchInbox +#### encodeSetBatchInbox -A new field `batchInbox` is added to the `L1Block`: +This function is used on L1 to encode the static configuration data for setting a batch inbox. ```solidity - /// @notice The canonical batch inbox. - bytes32 public batchInbox; +/// @notice Encodes the static configuration data for setting a batch inbox. +/// @param _batchInbox Address of the batch inbox. +/// @return Encoded static configuration data. +function encodeSetBatchInbox(address _batchInbox) internal pure returns (bytes memory) { + return abi.encode(_batchInbox); +} ``` -#### setL1BlockValuesEcotone +#### decodeSetBatchInbox -This function stores Layer 1 (L1) block values for Layer 2 (L2) since the Ecotone upgrade of the OP Stack. It is enhanced to also store the inbox address. +This function is used on L2 to decode the static configuration data for setting a batch inbox. ```solidity -function setL1BlockValuesEcotone() external { - ... - sstore(batchInbox.slot, calldataload(164)) // bytes32 +/// @notice Decodes the static configuration data for setting a batch inbox. +/// @param _data Encoded static configuration data. +/// @return Decoded Address of the batch inbox. +function decodeSetBatchInbox(bytes memory _data) internal pure returns (address) { + return abi.decode(_data, (address)); } - ``` +### L2Client.SystemConfigByL2Hash + +This function is used to retrieve the system configuration for a specific L2 block hash. Prior to the fork, this information was obtained from the deposit transaction. However, post-fork, it should be read from the L2 state instead, in accordance with the concepts outlined in [this issue](https://github.com/ethereum-optimism/specs/issues/122). + +### L1Traversal.AdvanceL1Block + +This function is used to fetch the next L1 block. It also caches the up-to-date `SystemConfig` by iteratively applying the `SystemConfig` updates in each L1 block. The way it retrieves the `SystemConfig` updates differs pre- and post-fork: + +Pre-fork: +- It retrieves the `SystemConfig` updates by parsing the [`ConfigUpdate`](https://github.com/ethereum-optimism/optimism/blob/f20b92d3eb379355c876502c4f28e72a91ab902f/packages/contracts-bedrock/src/L1/SystemConfig.sol#L124) event. + +Post-fork: +- It retrieves the `SystemConfig` updates by parsing the `TransactionDeposited` event emitted by [`OptimismPortalInterop.setConfig`](https://github.com/ethereum-optimism/optimism/blob/f20b92d3eb379355c876502c4f28e72a91ab902f/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol#L41) function. +- It needs to parse the `SystemConfig` values according to [`ConfigType`](https://github.com/ethereum-optimism/optimism/blob/f20b92d3eb379355c876502c4f28e72a91ab902f/packages/contracts-bedrock/src/L2/L1BlockInterop.sol#L14), similar to [`L1BlockInterop.setConfig`](https://github.com/ethereum-optimism/optimism/blob/f20b92d3eb379355c876502c4f28e72a91ab902f/packages/contracts-bedrock/src/L2/L1BlockInterop.sol#L59). + + + ### How `op-node` knows the canonical batch inbox -We define that the canonical batch inbox at a specific L2 block is the batch inbox of `SystemConfig` of the origin of the L2 block. +We define that the canonical batch inbox at a specific L2 block is the batch inbox of `SystemConfig` for the origin of the L2 block. Under normal conditions, `op-node` knows the canonical batch inbox through the derivation pipeline: -1. The `L1Traversal` component first identifies the L1 `SystemConfig` changes while traversing the L1 block, via [`UpdateSystemConfigWithL1Receipts`](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-node/rollup/derive/l1_traversal.go#L78). - 1. The [`ProcessSystemConfigUpdateLogEvent`](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-node/rollup/derive/system_config.go#L59) function will be modified to parse the newly added inbox change. +1. The `L1Traversal` component first identifies the L1 `SystemConfig` updates and caches the up-to-date `SystemConfig` while traversing the L1 block, via [`UpdateSystemConfigWithL1Receipts`](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-node/rollup/derive/l1_traversal.go#L78). 2. The `L1Retrieval` component then fetches the canonical batch inbox from the `L1Traversal` componenet and [pass](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-node/rollup/derive/l1_retrieval.go#L57) it to the `DataSourceFactory` component, via `OpenData` function, similar to how [`SystemConfig.BatcherAddr`](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-service/eth/types.go#L382) is handled. -3. The `FetchingAttributesBuilder` component is updated to incorporate the canonical batch inbox into the `DepositTx`, via [`L1InfoDeposit`](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-node/rollup/derive/l1_block_info.go#L263). This modified `DepositTx` is subsequently used to obtain the `SystemConfig` during L2 chain reorganization. +3. The `FetchingAttributesBuilder` component will convert `SystemConfig` updates into deposit transactions via [`DeriveDeposits`](https://github.com/ethereum-optimism/optimism/blob/f20b92d3eb379355c876502c4f28e72a91ab902f/op-node/rollup/derive/attributes.go#L70). After the deposit transactions are executed, the `SystemConfig` will be persisted in L2 state. During L2 reorganization, `op-node` knows the canonical batch inbox using the `SystemConfig` parameter of [`ResettableStage.Reset(context.Context, eth.L1BlockRef, eth.SystemConfig)`](https://github.com/ethereum-optimism/optimism/blob/71928829ca7ece48152159daa1d231eac2df03b3/op-node/rollup/derive/pipeline.go#L38) function, where `SystemConfig` is derived from the `DepositTx` of the corresponding L2 block. @@ -246,7 +195,7 @@ Immediately before submitting a new batch, `op-batcher` fetches the current inbo ## Upgrade -The custom gas token upgrade is not yet defined to be part of a particular network upgrade, but it will be scheduled as part of a future hardfork. On the network upgrade block, a set of deposit transaction based upgrade transactions are deterministically generated by the derivation pipeline in the following order: +The inbox contract upgrade is not yet defined to be part of a particular network upgrade, but it will be scheduled as part of a future hardfork. On the network upgrade block, a set of deposit transaction based upgrade transactions are deterministically generated by the derivation pipeline in the following order: - L1 Attributes Transaction calling `setL1BlockValuesEcotone` - User deposits from L1 @@ -289,9 +238,9 @@ RPC access to the derivation pipeline and make the upgrade transactions non dete - `data`: TODO - `sourceHash`: TODO -### SystemConfig Upgrade +### SystemConfigHolocene Upgrade -Finally, to dynamically change the inbox address, `SystemConfig` on L1 will be upgraded to accept a new inbox address. +Finally, to dynamically change the inbox address, `SystemConfigHolocene` on L1 will be upgraded to accept a new inbox address. Note that according to the [Optimism Style Guide](https://github.com/ethereum-optimism/optimism/blob/9d31040ecf8590423adf267ad24b03bc1bf7273b/packages/contracts-bedrock/STYLE_GUIDE.md), The process for upgrading the implementation is as follows: 1. Upgrade the implementation to the `StorageSetter` contract.