From fbe5f816581e29ecc874329ba2ee560fff51a4c6 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Wed, 18 Sep 2024 10:17:02 -0600 Subject: [PATCH] holocene: smart contract changes (#358) * holocene: standard L2 genesis This scopes out the work for implementing the standard L2 genesis smart contract changes. The additional diff on top of this smart contract work is defining the network upgrade transactions, the public interface for the `SystemConfig` for setting these values and the enum `ConfigType` definition. * lint * lint: escape * specs: fixup * specs: fixup * specs: fixup * lint: fix * lint: fix * specs: update * specs: lintspecs: lintspecs: lintspecs: lintspecs: lintspecs: lintspecs: lintspecs: lint * lint: fix * specs: cleanup * specs: toc * specs: update * toc: update * specs: fix * lint: fix * lint: fix * lint: fix * lint * specs: update * lint: specs * specs: update * specs: more updates * specs: toc * specs: update name * specs: update * specs: specify initialize * specs: toc * lint: fix * specs: delete dead spec * specs: lint fix --- specs/SUMMARY.md | 5 + specs/protocol/holocene/configurability.md | 157 ++++++++++++ specs/protocol/holocene/derivation.md | 22 ++ specs/protocol/holocene/exec-engine.md | 4 +- specs/protocol/holocene/l1-attributes.md | 28 +++ specs/protocol/holocene/overview.md | 6 + specs/protocol/holocene/predeploys.md | 268 +++++++++++++++++++++ 7 files changed, 488 insertions(+), 2 deletions(-) create mode 100644 specs/protocol/holocene/configurability.md create mode 100644 specs/protocol/holocene/derivation.md create mode 100644 specs/protocol/holocene/l1-attributes.md create mode 100644 specs/protocol/holocene/predeploys.md diff --git a/specs/SUMMARY.md b/specs/SUMMARY.md index c8cc681f5..1781c5658 100644 --- a/specs/SUMMARY.md +++ b/specs/SUMMARY.md @@ -51,6 +51,11 @@ - [Execution Engine](./protocol/granite/exec-engine.md) - [Derivation](./protocol/granite/derivation.md) - [Holocene](./protocol/holocene/overview.md) + - [Derivation](./protocol/holocene/derivation.md) + - [Execution Engine](./protocol/holocene/exec-engine.md) + - [Predeploys](./protocol/holocene/predeploys.md) + - [L1 Block Attributes](./protocol/holocene/l1-attributes.md) + - [Configurability](./protocol/holocene/configurability.md) - [Governance]() - [Governance Token](./governance/gov-token.md) - [Experimental]() diff --git a/specs/protocol/holocene/configurability.md b/specs/protocol/holocene/configurability.md new file mode 100644 index 000000000..4d544bde2 --- /dev/null +++ b/specs/protocol/holocene/configurability.md @@ -0,0 +1,157 @@ +# Configurability + + + +**Table of Contents** + +- [Overview](#overview) +- [Constants](#constants) + - [`ConfigType`](#configtype) +- [`SystemConfig`](#systemconfig) + - [`ConfigUpdate`](#configupdate) + - [Initialization](#initialization) + - [Modifying EIP-1559 Parameters](#modifying-eip-1559-parameters) + - [Interface](#interface) + - [EIP-1559 Params](#eip-1559-params) + - [`setEIP1559Params`](#seteip1559params) + - [Fee Vault Config](#fee-vault-config) + - [`setBaseFeeVaultConfig`](#setbasefeevaultconfig) + - [`setL1FeeVaultConfig`](#setl1feevaultconfig) + - [`setSequencerFeeVaultConfig`](#setsequencerfeevaultconfig) +- [`OptimismPortal`](#optimismportal) + - [Interface](#interface-1) + - [`setConfig`](#setconfig) + + + +## Overview + +The `SystemConfig` and `OptimismPortal` are updated with a new flow for chain +configurability. + +## Constants + +### `ConfigType` + +The `ConfigType` enum represents configuration that can be modified. + +| Name | Value | Description | +| ---- | ----- | --- | +| `SET_GAS_PAYING_TOKEN` | `uint8(0)` | Modifies the gas paying token for the chain | +| `SET_BASE_FEE_VAULT_CONFIG` | `uint8(1)` | Sets the Fee Vault Config for the `BaseFeeVault` | +| `SET_L1_FEE_VAULT_CONFIG` | `uint8(2)` | Sets the Fee Vault Config for the `L1FeeVault` | +| `SET_SEQUENCER_FEE_VAULT_CONFIG` | `uint8(3)` | Sets the Fee Vault Config for the `SequencerFeeVault` | +| `SET_L1_CROSS_DOMAIN_MESSENGER_ADDRESS` | `uint8(4)` | Sets the `L1CrossDomainMessenger` address | +| `SET_L1_ERC_721_BRIDGE_ADDRESS` | `uint8(5)` | Sets the `L1ERC721Bridge` address | +| `SET_L1_STANDARD_BRIDGE_ADDRESS` | `uint8(6)` | Sets the `L1StandardBridge` address | +| `SET_REMOTE_CHAIN_ID` | `uint8(7)` | Sets the chain id of the base chain | + +## `SystemConfig` + +### `ConfigUpdate` + +The following `ConfigUpdate` enum is defined where the `CONFIG_VERSION` is `uint256(0)`: + +| Name | Value | Definition | Usage | +| ---- | ----- | --- | -- | +| `BATCHER` | `uint8(0)` | `abi.encode(address)` | Modifies the account that is authorized to progress the safe chain | +| `FEE_SCALARS` | `uint8(1)` | `(uint256(0x01) << 248) \| (uint256(_blobbasefeeScalar) << 32) \| _basefeeScalar` | Modifies the fee scalars | +| `GAS_LIMIT` | `uint8(2)` | `abi.encode(uint64 _gasLimit)` | Modifies the L2 gas limit | +| `UNSAFE_BLOCK_SIGNER` | `uint8(3)` | `abi.encode(address)` | Modifies the account that is authorized to progress the unsafe chain | +| `EIP_1559_PARAMS` | `uint8(4)` | `uint256(uint64(_denominator)) << 32 | uint64(_elasticity)` | Modifies the EIP-1559 denominator and elasticity | + +### Initialization + +The following actions should happen during the initialization of the `SystemConfig`: + +- `emit ConfigUpdate.BATCHER` +- `emit ConfigUpdate.FEE_SCALARS` +- `emit ConfigUpdate.GAS_LIMIT` +- `emit ConfigUpdate.UNSAFE_BLOCK_SIGNER` +- `emit ConfigUpdate.EIP_1559_PARAMS` +- `setConfig(SET_GAS_PAYING_TOKEN)` +- `setConfig(SET_BASE_FEE_VAULT_CONFIG)` +- `setConfig(SET_L1_FEE_VAULT_CONFIG)` +- `setConfig(SET_SEQUENCER_FEE_VAULT_CONFIG)` +- `setConfig(SET_L1_CROSS_DOMAIN_MESSENGER_ADDRESS)` +- `setConfig(SET_L1_ERC_721_BRIDGE_ADDRESS)` +- `setConfig(SET_L1_STANDARD_BRIDGE_ADDRESS)` +- `setConfig(SET_REMOTE_CHAIN_ID)` + +These actions MAY only be triggered if there is a diff to the value. + +### Modifying EIP-1559 Parameters + +A new `SystemConfig` `UpdateType` is introduced that enables the modification of +[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) parameters. This allows for the chain +operator to modify the `BASE_FEE_MAX_CHANGE_DENOMINATOR` and the `ELASTICITY_MULTIPLIER`. + +### Interface + +#### EIP-1559 Params + +##### `setEIP1559Params` + +This function MUST only be callable by the chain governor. + +```solidity +function setEIP1559Params(uint64 _denominator, uint64 _elasticity) +``` + +The `_denominator` and `_elasticity` MUST be set to values greater to than 0. +It is possible for the chain operator to set EIP-1559 parameters that result in poor user experience. + +#### Fee Vault Config + +For each `FeeVault`, there is a setter for its config. The arguments to the setter include +the `RECIPIENT`, the `MIN_WITHDRAWAL_AMOUNT` and the `WithdrawalNetwork`. +Each of these functions should be `public` and only callable by the chain governor. + +Each function calls `OptimismPortal.setConfig(ConfigType,bytes)` with its corresponding `ConfigType`. + +##### `setBaseFeeVaultConfig` + +```solidity +function setBaseFeeVaultConfig(address,uint256,WithdrawalNetwork) +``` + +##### `setL1FeeVaultConfig` + +```solidity +function setL1FeeVaultConfig(address,uint256,WithdrawalNetwork) +``` + +##### `setSequencerFeeVaultConfig` + +```solidity +function setSequencerFeeVaultConfig(address,uint256,WithdrawalNetwork) +``` + +## `OptimismPortal` + +The `OptimismPortal` is updated to emit a special system `TransactionDeposited` event. + +### Interface + +#### `setConfig` + +The `setConfig` function MUST only be callable by the `SystemConfig`. This ensures that the `SystemConfig` +is the single source of truth for chain operator ownership. + +```solidity +function setConfig(ConfigType,bytes) +``` + +This function emits a `TransactionDeposited` event. + +```solidity +event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); +``` + +The following fields are included: + +- `from` is the `DEPOSITOR_ACCOUNT` +- `to` is `Predeploys.L1Block` +- `version` is `uint256(0)` +- `opaqueData` is the tightly packed transaction data where `mint` is `0`, `value` is `0`, the `gasLimit` + is `200_000`, `isCreation` is `false` and the `data` is `abi.encodeCall(L1Block.setConfig, (_type, _value))` diff --git a/specs/protocol/holocene/derivation.md b/specs/protocol/holocene/derivation.md new file mode 100644 index 000000000..463e1fbf8 --- /dev/null +++ b/specs/protocol/holocene/derivation.md @@ -0,0 +1,22 @@ +# Derivation + + + +**Table of Contents** + +- [Network upgrade automation transactions](#network-upgrade-automation-transactions) + + + +# Network upgrade automation transactions + +The Holocene hardfork activation block contains the following transactions, in this order: + +- L1 Attributes Transaction +- User deposits from L1 +- Network Upgrade Transactions + - L1Block deployment + - Update L1Block Proxy ERC-1967 Implementation + - L1Block Enable Holocene + - OptimismMintableERC20Factory deployment + - Update OptimismMintableERC20Factory Proxy ERC-1967 Implementation diff --git a/specs/protocol/holocene/exec-engine.md b/specs/protocol/holocene/exec-engine.md index 73ca23bca..f0be5c2bd 100644 --- a/specs/protocol/holocene/exec-engine.md +++ b/specs/protocol/holocene/exec-engine.md @@ -71,5 +71,5 @@ an outbound withdrawal for a long period of time, the node may not have access t clients are able to at the very least reconstruct the account storage root at a given block on the fly if it does not directly store this information. -[l2-to-l1-mp]: ../protocol/predeploys.md#L2ToL1MessagePasser -[output-root]: ../glossary.md#l2-output-root +[l2-to-l1-mp]: ../../protocol/predeploys.md#L2ToL1MessagePasser +[output-root]: ../../glossary.md#l2-output-root diff --git a/specs/protocol/holocene/l1-attributes.md b/specs/protocol/holocene/l1-attributes.md new file mode 100644 index 000000000..a21d841b7 --- /dev/null +++ b/specs/protocol/holocene/l1-attributes.md @@ -0,0 +1,28 @@ +# L1 Block Attributes + + + +**Table of Contents** + +- [Overview](#overview) + + + +## Overview + +The L1 block attributes transaction is updated to include the EIP-1559 parameters. + +| Input arg | Type | Calldata bytes | Segment | +| ----------------- | ------- | -------------- | ------- | +| {0xd1fbe15b} | | 0-3 | n/a | +| baseFeeScalar | uint32 | 4-7 | 1 | +| blobBaseFeeScalar | uint32 | 8-11 | | +| sequenceNumber | uint64 | 12-19 | | +| l1BlockTimestamp | uint64 | 20-27 | | +| l1BlockNumber | uint64 | 28-35 | | +| basefee | uint256 | 36-67 | 2 | +| blobBaseFee | uint256 | 68-99 | 3 | +| l1BlockHash | bytes32 | 100-131 | 4 | +| batcherHash | bytes32 | 132-163 | 5 | +| eip1559Denominator | uint64 | 164-171 | 6 | +| eip1559Elasticity | uint64 | 172-179 | | diff --git a/specs/protocol/holocene/overview.md b/specs/protocol/holocene/overview.md index 28ab76bc7..ce31862df 100644 --- a/specs/protocol/holocene/overview.md +++ b/specs/protocol/holocene/overview.md @@ -18,4 +18,10 @@ This document is not finalized and should be considered experimental. ## Consensus Layer +- [Derivation](./derivation.md) + ## Smart Contracts + +- [Predeploys](./predeploys.md) +- [L1 Block Attributes](./l1-attributes.md) +- [Configurability](./configurability.md) diff --git a/specs/protocol/holocene/predeploys.md b/specs/protocol/holocene/predeploys.md new file mode 100644 index 000000000..99002982d --- /dev/null +++ b/specs/protocol/holocene/predeploys.md @@ -0,0 +1,268 @@ +# Overview + + + +**Table of Contents** + +- [Constants](#constants) +- [Predeploys](#predeploys) + - [L1Block](#l1block) + - [Storage](#storage) + - [Interface](#interface) + - [`setL1BlockValuesHolocene`](#setl1blockvaluesholocene) + - [`setHolocene`](#setholocene) + - [`eip1559Elasticity`](#eip1559elasticity) + - [`eip1559Denominator`](#eip1559denominator) + - [`setConfig`](#setconfig) + - [`baseFeeVaultConfig`](#basefeevaultconfig) + - [`sequencerFeeVaultConfig`](#sequencerfeevaultconfig) + - [`l1FeeVaultConfig`](#l1feevaultconfig) + - [`l1CrossDomainMessenger`](#l1crossdomainmessenger) + - [`l1StandardBridge`](#l1standardbridge) + - [`l1ERC721Bridge`](#l1erc721bridge) + - [`remoteChainId`](#remotechainid) + - [FeeVault](#feevault) + - [Interface](#interface-1) + - [`config`](#config) + - [L2CrossDomainMessenger](#l2crossdomainmessenger) + - [Interface](#interface-2) + - [L2ERC721Bridge](#l2erc721bridge) + - [Interface](#interface-3) + - [L2StandardBridge](#l2standardbridge) + - [Interface](#interface-4) + - [OptimismMintableERC721Factory](#optimismmintableerc721factory) +- [Security Considerations](#security-considerations) + - [GovernanceToken](#governancetoken) + + + +This upgrade enables a deterministic L2 genesis state by moving all network +specific configuration out of the initial L2 genesis state. All network specific +configuration is sourced from deposit transactions during the initialization +of the `SystemConfig`. + +## Constants + +| Name | Value | Definition | +| --------- | ------------------------- | -- | +| `ConfigType` | `uint8` | An enum representing the type of config being set | +| `WithdrawalNetwork` | `uint8(0)` or `uint8(1)` | `0` means withdraw to L1, `1` means withdraw to L2 | +| `RECIPIENT` | `address` | The account that will receive funds sent out of the `FeeVault` | +| `MIN_WITHDRAWAL_AMOUNT` | `uint256` | The minimum amount of native asset held in the `FeeVault` before withdrawal is authorized | +| Fee Vault Config | `bytes32` | `bytes32((WithdrawalNetwork << 248) \|\| uint256(uint88(MIN_WITHDRAWAL_AMOUNT)) \|\| uint256(uint160(RECIPIENT)))` | +| `BASE_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.basefeevaultconfig")) - 1)` | The Fee Vault Config for the `BaseFeeVault` | +| `L1_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.l1feevaultconfig")) - 1)` | The Fee Vault Config for the `L1FeeVault` | +| `SEQUENCER_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.sequencerfeevaultconfig")) - 1)` | The Fee Vault Config for the `SequencerFeeVault` | +| `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1crossdomainmessengeraddress")) - 1)` | `abi.encode(address(L1CrossDomainMessengerProxy))` | +| `L1_ERC_721_BRIDGE_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1erc721bridgeaddress")) - 1)` | `abi.encode(address(L1ERC721BridgeProxy))` | +| `L1_STANDARD_BRIDGE_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1standardbridgeaddress")) - 1)` | `abi.encode(address(L1StandardBridgeProxy))` | +| `REMOTE_CHAIN_ID` | `bytes32(uint256(keccak256("opstack.remotechainid")) - 1)` | Chain ID of the remote chain | + +## Predeploys + +All network specific configuration is moved to a single contract, the `L1Block` predeploy. +All predeploys make calls to the `L1Block` contract to fetch network specific configuration +rather than reading it from local state. + +```mermaid +graph LR + subgraph L1 + SystemConfig -- "setConfig(uint8,bytes)" --> OptimismPortal + end + subgraph L2 + L1Block + BaseFeeVault -- "baseFeeVaultConfig()(address,uint256,uint8)" --> L1Block + SequencerFeeVault -- "sequencerFeeVaultConfig()(address,uint256,uint8)" --> L1Block + L1FeeVault -- "l1FeeVaultConfig()(address,uint256,uint8)" --> L1Block + L2CrossDomainMessenger -- "l1CrossDomainMessenger()(address)" --> L1Block + L2StandardBridge -- "l1StandardBridge()(address)" --> L1Block + L2ERC721Bridge -- "l1ERC721Bridge()(address)" --> L1Block + OptimismMintableERC721Factory -- "remoteChainId()(uint256)" --> L1Block + end + OptimismPortal -- "setConfig(uint8,bytes)" --> L1Block +``` + +### L1Block + +#### Storage + +The following storage slots are defined: + +- `BASE_FEE_VAULT_CONFIG` +- `L1_FEE_VAULT_CONFIG` +- `SEQUENCER_FEE_VAULT_CONFIG` +- `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` +- `L1_ERC_721_BRIDGE_ADDRESS` +- `L1_STANDARD_BRIDGE_ADDRESS` +- `REMOTE_CHAIN_ID` + +Each slot MUST have a defined `ConfigType` that authorizes the setting of the storage slot +via a deposit transaction from the `DEPOSITOR_ACCOUNT`. + +#### Interface + +##### `setL1BlockValuesHolocene` + +This function MUST only be callable by the `DEPOSITOR_ACCOUNT`. It is a replacement +for `setL1BlockValuesEcotone` and its calldata is defined in [L1 Attributes](./l1-attributes.md). + +```function +function setL1BlockValuesHolocene() +``` + +##### `setHolocene` + +This function is meant to be called once on the activation block of the holocene network upgrade. +It MUST only be callable by the `DEPOSITOR_ACCOUNT` once. When it is called, it MUST call +call each getter for the network specific config and set the returndata into storage. + +##### `eip1559Elasticity` + +This function returns the currently configured EIP-1559 elasticity. + +```solidity +function eip1559Elasticity()(uint64) +``` + +##### `eip1559Denominator` + +This function returns the currently configured EIP-1559 denominator. + +```solidity +function eip1559Denominator()(uint64) +``` + +##### `setConfig` + +This function MUST only be callable by the `DEPOSITOR_ACCOUNT`. It modifies the storage directly +of the `L1Block` contract. It MUST handle all defined `ConfigType`s. To ensure a simple ABI, the +`bytes` value MUST be abi decoded based on the `ConfigType`. + +```solidity +function setConfig(ConfigType,bytes) +``` + +Note that `ConfigType` is an enum which is an alias for a `uint8`. + +##### `baseFeeVaultConfig` + +This function MUST be called by the `BaseFeeVault` to fetch network specific configuration. + +```solidity +function baseFeeVaultConfig()(address,uint256,WithdrawalNetwork) +``` + +##### `sequencerFeeVaultConfig` + +This function MUST be called by the `SequencerFeeVault` to fetch network specific configuration. + +```solidity +function sequencerFeeVaultConfig()(address,uint256,WithdrawalNetwork) +``` + +##### `l1FeeVaultConfig` + +This function MUST be called by the `L1FeeVault` to fetch network specific configuration. + +```solidity +function l1FeeVaultConfig()(address,uint256,WithdrawalNetwork) +``` + +##### `l1CrossDomainMessenger` + +This function MUST be called by the `L2CrossDomainMessenger` to fetch the address of the `L1CrossDomainMessenger`. + +```solidity +function l1CrossDomainMessenger()(address) +``` + +##### `l1StandardBridge` + +This function MUST be called by the `L2StandardBridge` to fetch the address of the `L2CrossDomainMessenger`. + +```solidity +function l1StandardBridge()(address) +``` + +##### `l1ERC721Bridge` + +This function MUST be called by the `L2ERC721Bridge` to fetch the address of the `L1ERC721Bridge`. + +```solidity +function l1ERC721Bridge()(address) +``` + +##### `remoteChainId` + +This function MUST be called by the `OptimismMintableERC721Factory` to fetch the chain id of the remote chain. +For an L2, this is the L1 chain id. + +```solidity +function remoteChainId()(uint256) +``` + +### FeeVault + +The following changes apply to each of the `BaseFeeVault`, the `L1FeeVault` and the `SequencerFeeVault`. + +#### Interface + +The following functions are updated to read from the `L1Block` contract: + +- `recipient()(address)` +- `withdrawalNetwork()(WithdrawalNetwork)` +- `minWithdrawalAmount()(uint256)` +- `withdraw()` + +| Name | Call | +| ---- | -------- | +| `BaseFeeVault` | `L1Block.baseFeeVaultConfig()` | +| `SequencerFeeVault` | `L1Block.sequencerFeeVaultConfig()` | +| `L1FeeVault` | `L1Block.l1FeeVaultConfig()` | + +##### `config` + +A new function is added to fetch the full Fee Vault Config. + +```solidity +function config()(address,uint256,WithdrawalNetwork) +``` + +### L2CrossDomainMessenger + +#### Interface + +The following functions are updated to read from the `L1Block` contract by calling `L1Block.l1CrossDomainMessenger()`: + +- `otherMessenger()(address)` +- `OTHER_MESSENGER()(address)` + +### L2ERC721Bridge + +#### Interface + +The following functions are updated to read from the `L1Block` contract by calling `L1Block.l1ERC721Bridge()` + +- `otherBridge()(address)` +- `OTHER_BRIDGE()(address)` + +### L2StandardBridge + +#### Interface + +The following functions are updated to read from the `L1Block` contract by calling `L1Block.l1StandardBridge()` + +- `otherBridge()(address)` +- `OTHER_BRIDGE()(address)` + +### OptimismMintableERC721Factory + +The chain id is no longer read from storage but instead is read from the `L1Block` contract by calling +`L1Block.remoteChainId()` + +## Security Considerations + +### GovernanceToken + +The predeploy defined by `GovernanceToken` should be an empty account until it is defined by +a future hardfork.