diff --git a/.semgrep/rules/sol-rules.yaml b/.semgrep/rules/sol-rules.yaml index 27d6d1e7fd4f..0a2d5612eac1 100644 --- a/.semgrep/rules/sol-rules.yaml +++ b/.semgrep/rules/sol-rules.yaml @@ -209,7 +209,9 @@ rules: } paths: exclude: + - packages/contracts-bedrock/src/L1/SystemConfigJovian.sol - packages/contracts-bedrock/src/L1/SystemConfigInterop.sol + - packages/contracts-bedrock/src/L1/OptimismPortalJovian.sol - packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol - id: sol-safety-proper-initializer diff --git a/go.mod b/go.mod index 9e6bfb4c301c..f7d80ff0dfbd 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/fatih/color v1.18.0 github.com/fsnotify/fsnotify v1.8.0 github.com/go-task/slim-sprig/v3 v3.0.0 + github.com/golang-jwt/jwt/v4 v4.5.1 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/google/go-cmp v0.6.0 github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 @@ -113,7 +114,6 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect @@ -253,7 +253,9 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101411.5-rc.1 +replace github.com/ethereum/go-ethereum => github.com/mdehoog/op-geth v0.0.0-20250108045840-ccd6aa8308fc + +replace github.com/ethereum-optimism/superchain-registry/superchain => github.com/mdehoog/superchain-registry/superchain v0.0.0-20250112212108-52eef42cce64 //replace github.com/ethereum/go-ethereum => ../go-ethereum diff --git a/go.sum b/go.sum index fcb7bcaf939c..986ab2c1cae8 100644 --- a/go.sum +++ b/go.sum @@ -190,10 +190,6 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.101411.5-rc.1 h1:8fhtAycm/+xugWev5jInUxgF0Wdc29PxSODZXca6Qi8= -github.com/ethereum-optimism/op-geth v1.101411.5-rc.1/go.mod h1:n6VeI9cKFxmXCauD7Ji9lgTAg+2TYGLZu5AXgVJB4tk= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241213092551-33a63fce8214 h1:94dIMFDCafAQ3FCC1pryuhgfZc1jPoDwK4xSMOPshN8= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241213092551-33a63fce8214/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= @@ -526,6 +522,10 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mdehoog/op-geth v0.0.0-20250108045840-ccd6aa8308fc h1:j4qO2zTVezd28omh/b9r3V7ZurYwlHRucbIwbPpcwKk= +github.com/mdehoog/op-geth v0.0.0-20250108045840-ccd6aa8308fc/go.mod h1:n6VeI9cKFxmXCauD7Ji9lgTAg+2TYGLZu5AXgVJB4tk= +github.com/mdehoog/superchain-registry/superchain v0.0.0-20250112212108-52eef42cce64 h1:d3oeJzX40Omcv0QLdWwhqy/+UaomFz+jxehZczVKDHw= +github.com/mdehoog/superchain-registry/superchain v0.0.0-20250112212108-52eef42cce64/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= diff --git a/op-batcher/batcher/sync_actions.go b/op-batcher/batcher/sync_actions.go index 76cdf846ad91..0e8555828b87 100644 --- a/op-batcher/batcher/sync_actions.go +++ b/op-batcher/batcher/sync_actions.go @@ -33,7 +33,7 @@ func (s syncActions) String() string { // computeSyncActions determines the actions that should be taken based on the inputs provided. The inputs are the current // state of the batcher (blocks and channels), the new sync status, and the previous current L1 block. The actions are returned // in a struct specifying the number of blocks to prune, the number of channels to prune, whether to wait for node sync, the block -// range to load into the local state, and whether to clear the state entirely. Returns an boolean indicating if the sequencer is out of sync. +// range to load into the local state, and whether to clear the state entirely. Returns a boolean indicating if the sequencer is out of sync. func computeSyncActions[T channelStatuser](newSyncStatus eth.SyncStatus, prevCurrentL1 eth.L1BlockRef, blocks queue.Queue[*types.Block], channels []T, l log.Logger) (syncActions, bool) { // PART 1: Initial checks on the sync status diff --git a/op-chain-ops/cmd/deposit-hash/main.go b/op-chain-ops/cmd/deposit-hash/main.go index 9166a0667c09..cc52a9667652 100644 --- a/op-chain-ops/cmd/deposit-hash/main.go +++ b/op-chain-ops/cmd/deposit-hash/main.go @@ -32,8 +32,7 @@ func main() { for _, ethLog := range l1Receipt.Logs { if ethLog.Topics[0].String() == depositLogTopic.String() { - - reconstructedDep, err := derive.UnmarshalDepositLogEvent(ethLog) + reconstructedDep, err := derive.UnmarshalDepositLogEventIgnoreNonce(ethLog) if err != nil { log.Crit("Failed to parse deposit event ", "err", err) } diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index 5df3f4510fed..69874dfb867a 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -348,6 +348,9 @@ type UpgradeScheduleDeployConfig struct { // L2GenesisIsthmusTimeOffset is the number of seconds after genesis block that the Isthmus hard fork activates. // Set it to 0 to activate at genesis. Nil to disable Isthmus. L2GenesisIsthmusTimeOffset *hexutil.Uint64 `json:"l2GenesisIsthmusTimeOffset,omitempty"` + // L2GenesisJovianTimeOffset is the number of seconds after genesis block that the Jovian hard fork activates. + // Set it to 0 to activate at genesis. Nil to disable Jovian. + L2GenesisJovianTimeOffset *hexutil.Uint64 `json:"l2GenesisJovianTimeOffset,omitempty"` // L2GenesisInteropTimeOffset is the number of seconds after genesis block that the Interop hard fork activates. // Set it to 0 to activate at genesis. Nil to disable Interop. L2GenesisInteropTimeOffset *hexutil.Uint64 `json:"l2GenesisInteropTimeOffset,omitempty"` @@ -390,6 +393,8 @@ func (d *UpgradeScheduleDeployConfig) ForkTimeOffset(fork rollup.ForkName) *uint return (*uint64)(d.L2GenesisHoloceneTimeOffset) case rollup.Isthmus: return (*uint64)(d.L2GenesisIsthmusTimeOffset) + case rollup.Jovian: + return (*uint64)(d.L2GenesisJovianTimeOffset) case rollup.Interop: return (*uint64)(d.L2GenesisInteropTimeOffset) default: @@ -415,6 +420,8 @@ func (d *UpgradeScheduleDeployConfig) SetForkTimeOffset(fork rollup.ForkName, of d.L2GenesisHoloceneTimeOffset = (*hexutil.Uint64)(offset) case rollup.Isthmus: d.L2GenesisIsthmusTimeOffset = (*hexutil.Uint64)(offset) + case rollup.Jovian: + d.L2GenesisJovianTimeOffset = (*hexutil.Uint64)(offset) case rollup.Interop: d.L2GenesisInteropTimeOffset = (*hexutil.Uint64)(offset) default: @@ -483,6 +490,10 @@ func (d *UpgradeScheduleDeployConfig) IsthmusTime(genesisTime uint64) *uint64 { return offsetToUpgradeTime(d.L2GenesisIsthmusTimeOffset, genesisTime) } +func (d *UpgradeScheduleDeployConfig) JovianTime(genesisTime uint64) *uint64 { + return offsetToUpgradeTime(d.L2GenesisJovianTimeOffset, genesisTime) +} + func (d *UpgradeScheduleDeployConfig) InteropTime(genesisTime uint64) *uint64 { return offsetToUpgradeTime(d.L2GenesisInteropTimeOffset, genesisTime) } @@ -516,6 +527,7 @@ func (d *UpgradeScheduleDeployConfig) forks() []Fork { {L2GenesisTimeOffset: d.L2GenesisGraniteTimeOffset, Name: string(L2AllocsGranite)}, {L2GenesisTimeOffset: d.L2GenesisHoloceneTimeOffset, Name: string(L2AllocsHolocene)}, {L2GenesisTimeOffset: d.L2GenesisIsthmusTimeOffset, Name: string(L2AllocsIsthmus)}, + {L2GenesisTimeOffset: d.L2GenesisJovianTimeOffset, Name: string(L2AllocsJovian)}, } } @@ -996,13 +1008,8 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Header, l2GenesisBlockHa Hash: l2GenesisBlockHash, Number: l2GenesisBlockNumber, }, - L2Time: l1StartBlock.Time, - SystemConfig: eth.SystemConfig{ - BatcherAddr: d.BatchSenderAddress, - Overhead: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(d.GasPriceOracleOverhead))), - Scalar: eth.Bytes32(d.FeeScalar()), - GasLimit: uint64(d.L2GenesisBlockGasLimit), - }, + L2Time: l1StartBlock.Time, + SystemConfig: d.GenesisSystemConfig(), }, BlockTime: d.L2BlockTime, MaxSequencerDrift: d.MaxSequencerDrift, @@ -1021,12 +1028,24 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Header, l2GenesisBlockHa GraniteTime: d.GraniteTime(l1StartTime), HoloceneTime: d.HoloceneTime(l1StartTime), IsthmusTime: d.IsthmusTime(l1StartTime), + JovianTime: d.JovianTime(l1StartTime), InteropTime: d.InteropTime(l1StartTime), ProtocolVersionsAddress: d.ProtocolVersionsProxy, AltDAConfig: altDA, }, nil } +// GenesisSystemConfig converts a DeployConfig to a eth.SystemConfig. If Ecotone is active at genesis, the +// Overhead value is considered a noop. +func (d *DeployConfig) GenesisSystemConfig() eth.SystemConfig { + return eth.SystemConfig{ + BatcherAddr: d.BatchSenderAddress, + Overhead: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(d.GasPriceOracleOverhead))), + Scalar: d.FeeScalar(), + GasLimit: uint64(d.L2GenesisBlockGasLimit), + } +} + // NewDeployConfig reads a config file given a path on the filesystem. func NewDeployConfig(path string) (*DeployConfig, error) { file, err := os.ReadFile(path) diff --git a/op-chain-ops/genesis/genesis.go b/op-chain-ops/genesis/genesis.go index 16f182ffbb47..785e7acb21f5 100644 --- a/op-chain-ops/genesis/genesis.go +++ b/op-chain-ops/genesis/genesis.go @@ -71,6 +71,7 @@ func NewL2Genesis(config *DeployConfig, l1StartHeader *types.Header) (*core.Gene GraniteTime: config.GraniteTime(l1StartTime), HoloceneTime: config.HoloceneTime(l1StartTime), IsthmusTime: config.IsthmusTime(l1StartTime), + JovianTime: config.JovianTime(l1StartTime), InteropTime: config.InteropTime(l1StartTime), Optimism: ¶ms.OptimismConfig{ EIP1559Denominator: eip1559Denom, diff --git a/op-chain-ops/genesis/layer_two.go b/op-chain-ops/genesis/layer_two.go index 1e3dd2281d17..264f9d27bd24 100644 --- a/op-chain-ops/genesis/layer_two.go +++ b/op-chain-ops/genesis/layer_two.go @@ -28,6 +28,7 @@ const ( L2AllocsGranite L2AllocsMode = "granite" L2AllocsHolocene L2AllocsMode = "holocene" L2AllocsIsthmus L2AllocsMode = "isthmus" + L2AllocsJovian L2AllocsMode = "jovian" ) var ( diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index e70c69e9f481..a0f9688403b6 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -234,6 +234,8 @@ func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses) (* L2GenesisFjordTimeOffset: new(hexutil.Uint64), L2GenesisGraniteTimeOffset: new(hexutil.Uint64), L2GenesisHoloceneTimeOffset: new(hexutil.Uint64), + L2GenesisIsthmusTimeOffset: new(hexutil.Uint64), + L2GenesisJovianTimeOffset: new(hexutil.Uint64), L2GenesisInteropTimeOffset: new(hexutil.Uint64), L1CancunTimeOffset: new(hexutil.Uint64), UseInterop: true, diff --git a/op-e2e/actions/helpers/user.go b/op-e2e/actions/helpers/user.go index d7215b80650f..7ab973ce6a83 100644 --- a/op-e2e/actions/helpers/user.go +++ b/op-e2e/actions/helpers/user.go @@ -393,7 +393,7 @@ func (s *CrossLayerUser) CheckDepositTx(t Testing, l1TxHash common.Hash, index i require.False(t, l2Success) } else { require.Less(t, index, len(depositReceipt.Logs), "must have enough logs in receipt") - reconstructedDep, err := derive.UnmarshalDepositLogEvent(depositReceipt.Logs[index]) + reconstructedDep, err := derive.UnmarshalDepositLogEventIgnoreNonce(depositReceipt.Logs[index]) require.NoError(t, err, "Could not reconstruct L2 Deposit") l2Tx := types.NewTx(reconstructedDep) s.L2.CheckReceipt(t, l2Success, l2Tx.Hash()) diff --git a/op-e2e/e2eutils/setup.go b/op-e2e/e2eutils/setup.go index dbc9ecea25b9..f72b2de1b633 100644 --- a/op-e2e/e2eutils/setup.go +++ b/op-e2e/e2eutils/setup.go @@ -6,7 +6,6 @@ import ( "path" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -205,6 +204,8 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * FjordTime: deployConf.FjordTime(uint64(deployConf.L1GenesisBlockTimestamp)), GraniteTime: deployConf.GraniteTime(uint64(deployConf.L1GenesisBlockTimestamp)), HoloceneTime: deployConf.HoloceneTime(uint64(deployConf.L1GenesisBlockTimestamp)), + IsthmusTime: deployConf.IsthmusTime(uint64(deployConf.L1GenesisBlockTimestamp)), + JovianTime: deployConf.JovianTime(uint64(deployConf.L1GenesisBlockTimestamp)), InteropTime: deployConf.InteropTime(uint64(deployConf.L1GenesisBlockTimestamp)), AltDAConfig: pcfg, } @@ -226,16 +227,13 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * } func SystemConfigFromDeployConfig(deployConfig *genesis.DeployConfig) eth.SystemConfig { - return eth.SystemConfig{ - BatcherAddr: deployConfig.BatchSenderAddress, - Overhead: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(deployConfig.GasPriceOracleOverhead))), - Scalar: eth.Bytes32(deployConfig.FeeScalar()), - GasLimit: uint64(deployConfig.L2GenesisBlockGasLimit), - } + return deployConfig.GenesisSystemConfig() } func ApplyDeployConfigForks(deployConfig *genesis.DeployConfig) { - isHolocene := os.Getenv("OP_E2E_USE_HOLOCENE") == "true" + isJovian := os.Getenv("OP_E2E_USE_JOVIAN") == "true" + isIsthmus := isJovian || os.Getenv("OP_E2E_USE_ISTHMUS") == "true" + isHolocene := isIsthmus || os.Getenv("OP_E2E_USE_HOLOCENE") == "true" isGranite := isHolocene || os.Getenv("OP_E2E_USE_GRANITE") == "true" isFjord := isGranite || os.Getenv("OP_E2E_USE_FJORD") == "true" isEcotone := isFjord || os.Getenv("OP_E2E_USE_ECOTONE") == "true" @@ -255,6 +253,12 @@ func ApplyDeployConfigForks(deployConfig *genesis.DeployConfig) { if isHolocene { deployConfig.L2GenesisHoloceneTimeOffset = new(hexutil.Uint64) } + if isIsthmus { + deployConfig.L2GenesisIsthmusTimeOffset = new(hexutil.Uint64) + } + if isJovian { + deployConfig.L2GenesisJovianTimeOffset = new(hexutil.Uint64) + } // Canyon and lower is activated by default deployConfig.L2GenesisCanyonTimeOffset = new(hexutil.Uint64) deployConfig.L2GenesisRegolithTimeOffset = new(hexutil.Uint64) diff --git a/op-e2e/system/bridge/bridge_test.go b/op-e2e/system/bridge/bridge_test.go index 655cf612c9ad..8ce73cf26f4c 100644 --- a/op-e2e/system/bridge/bridge_test.go +++ b/op-e2e/system/bridge/bridge_test.go @@ -102,7 +102,7 @@ func TestERC20BridgeDeposits(t *testing.T) { depositEvent, err := receipts.FindLog(depositReceipt.Logs, portal.ParseTransactionDeposited) require.NoError(t, err, "Should emit deposit event") - depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) + depositTx, err := derive.UnmarshalDepositLogEventIgnoreNonce(&depositEvent.Raw) require.NoError(t, err) _, err = wait.ForReceiptOK(context.Background(), l2Client, types.NewTx(depositTx).Hash()) require.NoError(t, err) diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index a54a46d1e5db..3810e572215f 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -638,6 +638,8 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, FjordTime: cfg.DeployConfig.FjordTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), GraniteTime: cfg.DeployConfig.GraniteTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), HoloceneTime: cfg.DeployConfig.HoloceneTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + IsthmusTime: cfg.DeployConfig.IsthmusTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + JovianTime: cfg.DeployConfig.JovianTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), InteropTime: cfg.DeployConfig.InteropTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), ProtocolVersionsAddress: cfg.L1Deployments.ProtocolVersionsProxy, AltDAConfig: rollupAltDAConfig, diff --git a/op-e2e/system/gastoken/gastoken_test.go b/op-e2e/system/gastoken/gastoken_test.go index f9c66d539eca..0cfc3b4b3cb0 100644 --- a/op-e2e/system/gastoken/gastoken_test.go +++ b/op-e2e/system/gastoken/gastoken_test.go @@ -256,7 +256,7 @@ func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System depositEvent, err := receipts.FindLog(receipt.Logs, optimismPortal.ParseTransactionDeposited) require.NoError(t, err, "Should emit deposit event") - depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) + depositTx, err := derive.UnmarshalDepositLogEventIgnoreNonce(&depositEvent.Raw) require.NoError(t, err) l2Client := sys.NodeClient("sequencer") @@ -327,7 +327,7 @@ func checkDeposit(t *testing.T, gto gasTokenTestOpts, enabled bool) { // compute the deposit transaction hash + poll for it depositEvent, err := receipts.FindLog(receipt.Logs, optimismPortal.ParseTransactionDeposited) require.NoError(t, err, "Should emit deposit event") - depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) + depositTx, err := derive.UnmarshalDepositLogEventIgnoreNonce(&depositEvent.Raw) require.NoError(t, err) _, err = wait.ForReceiptOK(context.Background(), l2Client, types.NewTx(depositTx).Hash()) require.NoError(t, err) @@ -497,7 +497,7 @@ func checkFeeWithdrawal(t *testing.T, gto gasTokenTestOpts, enabled bool) { // Compute the deposit transaction hash + poll for it depositEvent, err := receipts.FindLog(receipt.Logs, optimismPortal.ParseTransactionDeposited) require.NoError(t, err, "Should emit deposit event") - depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) + depositTx, err := derive.UnmarshalDepositLogEventIgnoreNonce(&depositEvent.Raw) require.NoError(t, err) _, err = wait.ForReceiptOK(context.Background(), l2Client, types.NewTx(depositTx).Hash()) require.NoError(t, err) diff --git a/op-e2e/system/helpers/tx_helper.go b/op-e2e/system/helpers/tx_helper.go index 10c16c0e7465..69d603a1e213 100644 --- a/op-e2e/system/helpers/tx_helper.go +++ b/op-e2e/system/helpers/tx_helper.go @@ -53,7 +53,7 @@ func SendDepositTx(t *testing.T, cfg e2esys.SystemConfig, l1Client *ethclient.Cl t.Logf("SendDepositTx: included on L1") // Wait for transaction to be included on L2 - reconstructedDep, err := derive.UnmarshalDepositLogEvent(l1Receipt.Logs[0]) + reconstructedDep, err := derive.UnmarshalDepositLogEventIgnoreNonce(l1Receipt.Logs[0]) require.NoError(t, err, "Could not reconstruct L2 Deposit") tx = types.NewTx(reconstructedDep) l2Receipt, err := wait.ForReceipt(ctx, l2Client, tx.Hash(), l2Opts.ExpectedStatus) diff --git a/op-node/rollup/attributes/engine_consolidate.go b/op-node/rollup/attributes/engine_consolidate.go index 3cd9e8931210..1595adf42595 100644 --- a/op-node/rollup/attributes/engine_consolidate.go +++ b/op-node/rollup/attributes/engine_consolidate.go @@ -138,7 +138,9 @@ func logL1InfoTxns(rollupCfg *rollup.Config, l log.Logger, l2Number, l2Timestamp "unsafe_l1_time", unsafeInfo.Time, "unsafe_seq_num", unsafeInfo.SequenceNumber, "unsafe_l1_basefee", unsafeInfo.BaseFee, "unsafe_batcher_addr", unsafeInfo.BatcherAddr, ) - if bytes.HasPrefix(safeTxValue.Data(), types.EcotoneL1AttributesSelector) { + if bytes.HasPrefix(safeTxValue.Data(), types.EcotoneL1AttributesSelector[:]) || + bytes.HasPrefix(safeTxValue.Data(), types.JovianL1AttributesSelector[:]) || + bytes.HasPrefix(safeTxValue.Data(), types.InteropL1AttributesSelector[:]) { l.Error("L1 Info transaction differs", "safe_l1_blob_basefee", safeInfo.BlobBaseFee, "safe_l1_basefee_scalar", safeInfo.BaseFeeScalar, diff --git a/op-node/rollup/chain_spec.go b/op-node/rollup/chain_spec.go index 936bb2b17dfa..80dcf14127aa 100644 --- a/op-node/rollup/chain_spec.go +++ b/op-node/rollup/chain_spec.go @@ -42,6 +42,7 @@ const ( Granite ForkName = "granite" Holocene ForkName = "holocene" Isthmus ForkName = "isthmus" + Jovian ForkName = "jovian" Interop ForkName = "interop" // ADD NEW FORKS TO AllForks BELOW! None ForkName = "none" @@ -57,6 +58,7 @@ var AllForks = []ForkName{ Granite, Holocene, Isthmus, + Jovian, Interop, // ADD NEW FORKS HERE! } @@ -195,6 +197,9 @@ func (s *ChainSpec) CheckForkActivation(log log.Logger, block eth.L2BlockRef) { if s.config.IsIsthmus(block.Time) { s.currentFork = Isthmus } + if s.config.IsJovian(block.Time) { + s.currentFork = Jovian + } if s.config.IsInterop(block.Time) { s.currentFork = Interop } @@ -221,6 +226,8 @@ func (s *ChainSpec) CheckForkActivation(log log.Logger, block eth.L2BlockRef) { foundActivationBlock = s.config.IsHoloceneActivationBlock(block.Time) case Isthmus: foundActivationBlock = s.config.IsIsthmusActivationBlock(block.Time) + case Jovian: + foundActivationBlock = s.config.IsJovianActivationBlock(block.Time) case Interop: foundActivationBlock = s.config.IsInteropActivationBlock(block.Time) } diff --git a/op-node/rollup/chain_spec_test.go b/op-node/rollup/chain_spec_test.go index 4c466b4e261d..97c88e6ced4d 100644 --- a/op-node/rollup/chain_spec_test.go +++ b/op-node/rollup/chain_spec_test.go @@ -47,6 +47,7 @@ var testConfig = Config{ GraniteTime: u64ptr(60), HoloceneTime: u64ptr(70), IsthmusTime: u64ptr(80), + JovianTime: u64ptr(90), InteropTime: nil, BatchInboxAddress: common.HexToAddress("0xff00000000000000000000000000000000000010"), DepositContractAddress: common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"), @@ -191,10 +192,16 @@ func TestCheckForkActivation(t *testing.T) { expectedCurrentFork: Isthmus, expectedLog: "Detected hardfork activation block", }, + { + name: "Jovian activation", + block: eth.L2BlockRef{Time: 90, Number: 11, Hash: common.Hash{0xb}}, + expectedCurrentFork: Jovian, + expectedLog: "Detected hardfork activation block", + }, { name: "No more hardforks", - block: eth.L2BlockRef{Time: 700, Number: 11, Hash: common.Hash{0xb}}, - expectedCurrentFork: Isthmus, + block: eth.L2BlockRef{Time: 700, Number: 12, Hash: common.Hash{0xc}}, + expectedCurrentFork: Jovian, expectedLog: "", }, } diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 5d859a9fcd59..796c3ffeddc6 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -65,38 +65,35 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex // case we need to fetch all transaction receipts from the L1 origin block so we can scan for // user deposits. if l2Parent.L1Origin.Number != epoch.Number { - info, receipts, err := ba.l1.FetchReceipts(ctx, epoch.Hash) - if err != nil { + var receipts types.Receipts + if l1Info, receipts, err = ba.l1.FetchReceipts(ctx, epoch.Hash); err != nil { return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and receipts: %w", err)) } - if l2Parent.L1Origin.Hash != info.ParentHash() { + if l2Parent.L1Origin.Hash != l1Info.ParentHash() { return nil, NewResetError( fmt.Errorf("cannot create new block with L1 origin %s (parent %s) on top of L1 origin %s", - epoch, info.ParentHash(), l2Parent.L1Origin)) + epoch, l1Info.ParentHash(), l2Parent.L1Origin)) } - deposits, err := DeriveDeposits(receipts, ba.rollupCfg.DepositContractAddress) - if err != nil { + if depositTxs, sysConfig.DepositNonce, err = DeriveDeposits(receipts, ba.rollupCfg.DepositContractAddress, sysConfig.DepositNonce); err != nil { // deposits may never be ignored. Failing to process them is a critical error. return nil, NewCriticalError(fmt.Errorf("failed to derive some deposits: %w", err)) } + // apply sysCfg changes - if err := UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, ba.rollupCfg, info.Time()); err != nil { + if err = UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, ba.rollupCfg, l1Info.Time()); err != nil { return nil, NewCriticalError(fmt.Errorf("failed to apply derived L1 sysCfg updates: %w", err)) } - l1Info = info - depositTxs = deposits seqNumber = 0 } else { if l2Parent.L1Origin.Hash != epoch.Hash { return nil, NewResetError(fmt.Errorf("cannot create new block with L1 origin %s in conflict with L1 origin %s", epoch, l2Parent.L1Origin)) } - info, err := ba.l1.InfoByHash(ctx, epoch.Hash) + l1Info, err = ba.l1.InfoByHash(ctx, epoch.Hash) if err != nil { return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info: %w", err)) } - l1Info = info depositTxs = nil seqNumber = l2Parent.SequenceNumber + 1 } @@ -124,6 +121,14 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex upgradeTxs = append(upgradeTxs, fjord...) } + if ba.rollupCfg.IsJovianActivationBlock(nextL2Time) { + jovian, err := JovianNetworkUpgradeTransactions() + if err != nil { + return nil, NewCriticalError(fmt.Errorf("failed to build jovian network upgrade txs: %w", err)) + } + upgradeTxs = append(upgradeTxs, jovian...) + } + l1InfoTx, err := L1InfoDepositBytes(ba.rollupCfg, sysConfig, seqNumber, l1Info, nextL2Time) if err != nil { return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err)) diff --git a/op-node/rollup/derive/deposit_log.go b/op-node/rollup/derive/deposit_log.go index 74fde28c5988..7a3ae59a4215 100644 --- a/op-node/rollup/derive/deposit_log.go +++ b/op-node/rollup/derive/deposit_log.go @@ -17,9 +17,26 @@ import ( var ( DepositEventABI = "TransactionDeposited(address,address,uint256,bytes)" DepositEventABIHash = crypto.Keccak256Hash([]byte(DepositEventABI)) - DepositEventVersion0 = common.Hash{} + DepositEventVersion0 = uint64(0) + DepositEventVersion1 = uint64(1) ) +// UnmarshalDepositLogEventIgnoreNonce decodes an EVM log entry emitted by the deposit contract into typed deposit data. +// Same as UnmarshalDepositLogEvent, but it force-ensures that the deposit nonce is correct. +// This is useful for testing or non-consensus applications. +func UnmarshalDepositLogEventIgnoreNonce(ev *types.Log) (*types.DepositTx, error) { + previousNonce := uint64(0) + if len(ev.Topics) == 4 { + nonce, version := UnpackNonceAndVersion(ev.Topics[3]) + switch version { + case DepositEventVersion1: + previousNonce = nonce - 1 + } + } + dep, _, err := UnmarshalDepositLogEvent(ev, previousNonce) + return dep, err +} + // UnmarshalDepositLogEvent decodes an EVM log entry emitted by the deposit contract into typed deposit data. // // parse log data for: @@ -27,23 +44,23 @@ var ( // event TransactionDeposited( // address indexed from, // address indexed to, -// uint256 indexed version, +// uint256 indexed nonceAndVersion, // bytes opaqueData // ); // // Additionally, the event log-index and -func UnmarshalDepositLogEvent(ev *types.Log) (*types.DepositTx, error) { +func UnmarshalDepositLogEvent(ev *types.Log, currentNonce uint64) (*types.DepositTx, uint64, error) { if len(ev.Topics) != 4 { - return nil, fmt.Errorf("expected 4 event topics (event identity, indexed from, indexed to, indexed version), got %d", len(ev.Topics)) + return nil, currentNonce, fmt.Errorf("expected 4 event topics (event identity, indexed from, indexed to, indexed version), got %d", len(ev.Topics)) } if ev.Topics[0] != DepositEventABIHash { - return nil, fmt.Errorf("invalid deposit event selector: %s, expected %s", ev.Topics[0], DepositEventABIHash) + return nil, currentNonce, fmt.Errorf("invalid deposit event selector: %s, expected %s", ev.Topics[0], DepositEventABIHash) } if len(ev.Data) < 64 { - return nil, fmt.Errorf("incomplate opaqueData slice header (%d bytes): %x", len(ev.Data), ev.Data) + return nil, currentNonce, fmt.Errorf("incomplate opaqueData slice header (%d bytes): %x", len(ev.Data), ev.Data) } if len(ev.Data)%32 != 0 { - return nil, fmt.Errorf("expected log data to be multiple of 32 bytes: got %d bytes", len(ev.Data)) + return nil, currentNonce, fmt.Errorf("expected log data to be multiple of 32 bytes: got %d bytes", len(ev.Data)) } // indexed 0 @@ -51,7 +68,7 @@ func UnmarshalDepositLogEvent(ev *types.Log) (*types.DepositTx, error) { // indexed 1 to := common.BytesToAddress(ev.Topics[2][12:]) // indexed 2 - version := ev.Topics[3] + nonceAndVersion := ev.Topics[3] // unindexed data // Solidity serializes the event's Data field as follows: // abi.encode(abi.encodPacked(uint256 mint, uint256 value, uint64 gasLimit, uint8 isCreation, bytes data)) @@ -60,14 +77,14 @@ func UnmarshalDepositLogEvent(ev *types.Log) (*types.DepositTx, error) { var opaqueContentOffset uint256.Int opaqueContentOffset.SetBytes(ev.Data[0:32]) if !opaqueContentOffset.IsUint64() || opaqueContentOffset.Uint64() != 32 { - return nil, fmt.Errorf("invalid opaqueData slice header offset: %d", opaqueContentOffset.Uint64()) + return nil, currentNonce, fmt.Errorf("invalid opaqueData slice header offset: %d", opaqueContentOffset.Uint64()) } // The next 32 bytes indicate the length of the opaqueData content. var opaqueContentLength uint256.Int opaqueContentLength.SetBytes(ev.Data[32:64]) // Make sure the length is an uint64, it's not larger than the remaining data, and the log is using minimal padding (i.e. can't add 32 bytes without exceeding data) if !opaqueContentLength.IsUint64() || opaqueContentLength.Uint64() > uint64(len(ev.Data)-64) || opaqueContentLength.Uint64()+32 <= uint64(len(ev.Data)-64) { - return nil, fmt.Errorf("invalid opaqueData slice header length: %d", opaqueContentLength.Uint64()) + return nil, currentNonce, fmt.Errorf("invalid opaqueData slice header length: %d", opaqueContentLength.Uint64()) } // The remaining data is the opaqueData which is tightly packed // and then padded to 32 bytes by the EVM. @@ -83,17 +100,39 @@ func UnmarshalDepositLogEvent(ev *types.Log) (*types.DepositTx, error) { dep.From = from dep.IsSystemTransaction = false + newNonce, version := UnpackNonceAndVersion(nonceAndVersion) + var err error switch version { case DepositEventVersion0: err = unmarshalDepositVersion0(&dep, to, opaqueData) + case DepositEventVersion1: + if newNonce != currentNonce+1 { + return nil, currentNonce, fmt.Errorf("invalid deposit nonce, expected %d got %d", currentNonce+1, newNonce) + } + err = unmarshalDepositVersion1(&dep, to, opaqueData) default: - return nil, fmt.Errorf("invalid deposit version, got %s", version) + return nil, currentNonce, fmt.Errorf("invalid deposit version, got %d", version) } if err != nil { - return nil, fmt.Errorf("failed to decode deposit (version %s): %w", version, err) + return nil, currentNonce, fmt.Errorf("failed to decode deposit (version %d): %w", version, err) } - return &dep, nil + return &dep, newNonce, nil +} + +func UnpackNonceAndVersion(nonceAndVersion common.Hash) (nonce uint64, version uint64) { + i := new(big.Int).SetBytes(nonceAndVersion[:]) + mask64 := new(big.Int).SetBytes(common.Hex2Bytes("ffffffffffffffff")) + version = mask64.And(mask64, i).Uint64() + nonce = i.Rsh(i, 128).Uint64() + return +} + +func PackNonceAndVersion(nonce uint64, version uint64) common.Hash { + i := new(big.Int).SetUint64(nonce) + i.Lsh(i, 128) + i.Or(i, new(big.Int).SetUint64(version)) + return common.BytesToHash(i.Bytes()) } func unmarshalDepositVersion0(dep *types.DepositTx, to common.Address, opaqueData []byte) error { @@ -140,6 +179,11 @@ func unmarshalDepositVersion0(dep *types.DepositTx, to common.Address, opaqueDat return nil } +func unmarshalDepositVersion1(dep *types.DepositTx, to common.Address, opaqueData []byte) error { + // version 1 simply adds a nonce; the rest is the same + return unmarshalDepositVersion0(dep, to, opaqueData) +} + // MarshalDepositLogEvent returns an EVM log entry that encodes a TransactionDeposited event from the deposit contract. // This is the reverse of the deposit transaction derivation. func MarshalDepositLogEvent(depositContractAddr common.Address, deposit *types.DepositTx) (*types.Log, error) { @@ -151,7 +195,7 @@ func MarshalDepositLogEvent(depositContractAddr common.Address, deposit *types.D DepositEventABIHash, eth.AddressAsLeftPaddedHash(deposit.From), toBytes, - DepositEventVersion0, + PackNonceAndVersion(0, DepositEventVersion0), } data := make([]byte, 64, 64+3*32) diff --git a/op-node/rollup/derive/deposit_log_test.go b/op-node/rollup/derive/deposit_log_test.go index 4cb335b1bef4..03a47f8a4dc5 100644 --- a/op-node/rollup/derive/deposit_log_test.go +++ b/op-node/rollup/derive/deposit_log_test.go @@ -31,7 +31,7 @@ func TestUnmarshalLogEvent(t *testing.T) { log.TxIndex = uint(rng.Intn(10000)) log.Index = uint(source.LogIndex) log.BlockHash = source.L1BlockHash - depOutput, err := UnmarshalDepositLogEvent(log) + depOutput, _, err := UnmarshalDepositLogEvent(log, 0) if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func TestDeriveUserDeposits(t *testing.T) { if err != nil { t.Fatal(err) } - got, err := UserDeposits(receipts, MockDepositContractAddr) + got, _, err := UserDeposits(receipts, MockDepositContractAddr, 0) require.NoError(t, err) require.Equal(t, len(got), len(expectedDeposits)) for d, depTx := range got { diff --git a/op-node/rollup/derive/deposit_log_tob_test.go b/op-node/rollup/derive/deposit_log_tob_test.go index 49721104b086..ee172644bbf1 100644 --- a/op-node/rollup/derive/deposit_log_tob_test.go +++ b/op-node/rollup/derive/deposit_log_tob_test.go @@ -144,7 +144,7 @@ func FuzzDeriveDepositsRoundTrip(f *testing.F) { receipts, expectedDeposits := fuzzReceipts(typeProvider, blockHash, MockDepositContractAddr) // Derive our user deposits from the transaction receipts - derivedDeposits, err := UserDeposits(receipts, MockDepositContractAddr) + derivedDeposits, _, err := UserDeposits(receipts, MockDepositContractAddr, 0) require.NoError(t, err) // Ensure all deposits we derived matched what we expected to receive. @@ -188,13 +188,13 @@ func FuzzDeriveDepositsBadVersion(f *testing.F) { // Generate any topic but the deposit event versions we support. // TODO: As opposed to keeping this hardcoded, a method such as IsValidVersion(v) should be // used here. - badTopic := DepositEventVersion0 - for badTopic == DepositEventVersion0 { - typeProvider.Fuzz(&badTopic) + badVersion := DepositEventVersion0 + for badVersion == DepositEventVersion0 { + typeProvider.Fuzz(&badVersion) } // Set our bad topic and update our state - log.Topics[3] = badTopic + log.Topics[3] = PackNonceAndVersion(0, badVersion) hasBadDepositVersion = true } } @@ -202,7 +202,7 @@ func FuzzDeriveDepositsBadVersion(f *testing.F) { } // Derive our user deposits from the transaction receipts - _, err := UserDeposits(receipts, MockDepositContractAddr) + _, _, err := UserDeposits(receipts, MockDepositContractAddr, 0) // If we patched a bad deposit version this iteration, we should expect an error and not be able to proceed // further diff --git a/op-node/rollup/derive/deposits.go b/op-node/rollup/derive/deposits.go index b71cd619678c..10fbb5f3f328 100644 --- a/op-node/rollup/derive/deposits.go +++ b/op-node/rollup/derive/deposits.go @@ -11,7 +11,7 @@ import ( ) // UserDeposits transforms the L2 block-height and L1 receipts into the transaction inputs for a full L2 block -func UserDeposits(receipts []*types.Receipt, depositContractAddr common.Address) ([]*types.DepositTx, error) { +func UserDeposits(receipts []*types.Receipt, depositContractAddr common.Address, currentNonce uint64) ([]*types.DepositTx, uint64, error) { var out []*types.DepositTx var result error for i, rec := range receipts { @@ -20,7 +20,8 @@ func UserDeposits(receipts []*types.Receipt, depositContractAddr common.Address) } for j, log := range rec.Logs { if log.Address == depositContractAddr && len(log.Topics) > 0 && log.Topics[0] == DepositEventABIHash { - dep, err := UnmarshalDepositLogEvent(log) + dep, newNonce, err := UnmarshalDepositLogEvent(log, currentNonce) + currentNonce = newNonce if err != nil { result = multierror.Append(result, fmt.Errorf("malformatted L1 deposit log in receipt %d, log %d: %w", i, j, err)) } else { @@ -29,12 +30,13 @@ func UserDeposits(receipts []*types.Receipt, depositContractAddr common.Address) } } } - return out, result + return out, currentNonce, result } -func DeriveDeposits(receipts []*types.Receipt, depositContractAddr common.Address) ([]hexutil.Bytes, error) { +func DeriveDeposits(receipts []*types.Receipt, depositContractAddr common.Address, currentNonce uint64) ([]hexutil.Bytes, uint64, error) { var result error - userDeposits, err := UserDeposits(receipts, depositContractAddr) + userDeposits, newNonce, err := UserDeposits(receipts, depositContractAddr, currentNonce) + currentNonce = newNonce if err != nil { result = multierror.Append(result, err) } @@ -47,5 +49,5 @@ func DeriveDeposits(receipts []*types.Receipt, depositContractAddr common.Addres encodedTxs = append(encodedTxs, opaqueTx) } } - return encodedTxs, result + return encodedTxs, currentNonce, result } diff --git a/op-node/rollup/derive/fuzz_parsers_test.go b/op-node/rollup/derive/fuzz_parsers_test.go index afb7e0850e26..abc5c70f5f3f 100644 --- a/op-node/rollup/derive/fuzz_parsers_test.go +++ b/op-node/rollup/derive/fuzz_parsers_test.go @@ -299,7 +299,7 @@ func FuzzUnmarshallLogEvent(f *testing.F) { depositEvent.Raw = types.Log{} // Clear out the log // Verify that is passes our custom unmarshalling logic - dep, err := UnmarshalDepositLogEvent(logs[0]) + dep, _, err := UnmarshalDepositLogEvent(logs[0], 0) if err != nil { t.Fatalf("Could not unmarshal log that was emitted by the deposit contract: %v", err) } diff --git a/op-node/rollup/derive/jovian_upgrade_transactions.go b/op-node/rollup/derive/jovian_upgrade_transactions.go new file mode 100644 index 000000000000..e55c15a5e353 --- /dev/null +++ b/op-node/rollup/derive/jovian_upgrade_transactions.go @@ -0,0 +1,39 @@ +package derive + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +var ( + JovianL1BlockDeployerAddress = common.HexToAddress("0x4210000000000000000000000000000000000005") + deployJovianL1BlockSource = UpgradeDepositSource{Intent: "Jovian: L1 Block Deployment"} + jovianL1BlockDeploymentBytecode = common.FromHex("0x608060405234801561001057600080fd5b50610b8c806100206000396000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806371cfaa3f116100e3578063c59859181161008c578063e591b28211610066578063e591b28214610387578063e81b2c6d146103a9578063f8206140146103b257600080fd5b8063c59859181461034b578063d84447151461036b578063de35f5cb1461037357600080fd5b80638b239f73116100bd5780638b239f73146103195780639e8c496614610322578063b80777ea1461032b57600080fd5b806371cfaa3f146102d257806374dd5eab146102e55780638381f58a1461030557600080fd5b8063440a5e20116101455780635cf249691161011f5780635cf249691461026b57806364ca23ef1461027457806368d5dca6146102a157600080fd5b8063440a5e201461021957806354fd4d5014610221578063550fcdc91461026357600080fd5b8063213268491161017657806321326849146101c35780633db6be2b146101db5780634397dfef146101e357600080fd5b8063015d8eb91461019257806309bd5a60146101a7575b600080fd5b6101a56101a03660046109f7565b6103bb565b005b6101b060025481565b6040519081526020015b60405180910390f35b6101cb6104fa565b60405190151581526020016101ba565b6101a5610539565b6101eb610543565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260ff9091166020830152016101ba565b6101a5610557565b60408051808201909152600581527f312e362e3000000000000000000000000000000000000000000000000000000060208201525b6040516101ba9190610a69565b6102566105ae565b6101b060015481565b6003546102889067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101ba565b6003546102bd9068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016101ba565b6101a56102e0366004610adc565b6105bd565b6008546102889068010000000000000000900467ffffffffffffffff1681565b6000546102889067ffffffffffffffff1681565b6101b060055481565b6101b060065481565b6000546102889068010000000000000000900467ffffffffffffffff1681565b6003546102bd906c01000000000000000000000000900463ffffffff1681565b610256610672565b6008546102889067ffffffffffffffff1681565b60405173deaddeaddeaddeaddeaddeaddeaddeaddead000181526020016101ba565b6101b060045481565b6101b060075481565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610462576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4c31426c6f636b3a206f6e6c7920746865206465706f7369746f72206163636f60448201527f756e742063616e20736574204c3120626c6f636b2076616c7565730000000000606482015260840160405180910390fd5b6000805467ffffffffffffffff98891668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116998916999099179890981790975560019490945560029290925560038054919094167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009190911617909255600491909155600555600655565b600080610505610543565b5073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141592915050565b61054161067c565b565b60008061054e6106dc565b90939092509050565b73deaddeaddeaddeaddeaddeaddeaddeaddead000133811461058157633cc50b456000526004601cfd5b60043560801c60035560143560801c60005560243560015560443560075560643560025560843560045550565b60606105b861075d565b905090565b3373deaddeaddeaddeaddeaddeaddeaddeaddead00011461060a576040517f3cc50b4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106168484848461081e565b604080518381526020810183905260ff85169173ffffffffffffffffffffffffffffffffffffffff8716917f10e43c4d58f3ef4edae7c1ca2e7f02d46b2cadbcc046737038527ed8486ffeb0910160405180910390a350505050565b60606105b86108f0565b73deaddeaddeaddeaddeaddeaddeaddeaddead00013381146106a657633cc50b456000526004601cfd5b60043560801c60035560143560801c60005560243560015560443560075560643560025560843560045560a43560801c60085550565b6000808061071261070e60017f04adb1412b2ddc16fcc0d4538d5c8f07cf9c83abecc6b41f6f69037b708fbcec610b41565b5490565b73ffffffffffffffffffffffffffffffffffffffff81169350905082610751575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee92601292509050565b60a081901c9150509091565b606060006107696106dc565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016107e257505060408051808201909152600381527f4554480000000000000000000000000000000000000000000000000000000000602082015290565b61081861081361070e60017fa48b38a4b44951360fbdcbfaaeae5ed6ae92585412e9841b70ec72ed8cd05764610b41565b6109a6565b91505090565b61088461084c60017f04adb1412b2ddc16fcc0d4538d5c8f07cf9c83abecc6b41f6f69037b708fbcec610b41565b74ff000000000000000000000000000000000000000060a086901b1673ffffffffffffffffffffffffffffffffffffffff8716179055565b6108b76108b260017f657c3582c29b3176614e3a33ddd1ec48352696a04e92b3c0566d72010fa8863d610b41565b839055565b6108ea6108e560017fa48b38a4b44951360fbdcbfaaeae5ed6ae92585412e9841b70ec72ed8cd05764610b41565b829055565b50505050565b606060006108fc6106dc565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff82160161097557505060408051808201909152600581527f4574686572000000000000000000000000000000000000000000000000000000602082015290565b61081861081361070e60017f657c3582c29b3176614e3a33ddd1ec48352696a04e92b3c0566d72010fa8863d610b41565b60405160005b82811a156109bc576001016109ac565b80825260208201838152600082820152505060408101604052919050565b803567ffffffffffffffff811681146109f257600080fd5b919050565b600080600080600080600080610100898b031215610a1457600080fd5b610a1d896109da565b9750610a2b60208a016109da565b96506040890135955060608901359450610a4760808a016109da565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600060208083528351808285015260005b81811015610a9657858101830151858201604001528201610a7a565b81811115610aa8576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60008060008060808587031215610af257600080fd5b843573ffffffffffffffffffffffffffffffffffffffff81168114610b1657600080fd5b9350602085013560ff81168114610b2c57600080fd5b93969395505050506040820135916060013590565b600082821015610b7a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b50039056fea164736f6c634300080f000a") +) + +// JovianNetworkUpgradeTransactions returns the transactions required to upgrade the Jovian network. +func JovianNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { + upgradeTxns := make([]hexutil.Bytes, 0, 1) + + deployL1BlockTransaction, err := types.NewTx(&types.DepositTx{ + SourceHash: deployJovianL1BlockSource.SourceHash(), + From: JovianL1BlockDeployerAddress, + To: nil, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 375_000, + IsSystemTransaction: false, + Data: jovianL1BlockDeploymentBytecode, + }).MarshalBinary() + + if err != nil { + return nil, err + } + + upgradeTxns = append(upgradeTxns, deployL1BlockTransaction) + + return upgradeTxns, nil +} diff --git a/op-node/rollup/derive/jovian_upgrade_transactions_test.go b/op-node/rollup/derive/jovian_upgrade_transactions_test.go new file mode 100644 index 000000000000..bd3b715acf76 --- /dev/null +++ b/op-node/rollup/derive/jovian_upgrade_transactions_test.go @@ -0,0 +1,49 @@ +package derive + +import ( + "math/big" + "testing" + + "github.com/ethereum-optimism/optimism/op-chain-ops/interopgen" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" +) + +func TestJovianSourcesMatchSpec(t *testing.T) { + for _, test := range []struct { + source UpgradeDepositSource + expectedHash string + }{ + { + source: deployJovianL1BlockSource, + expectedHash: "0xbb1a656f65401240fac3db12e7a79ebb954b11e62f7626eb11691539b798d3bf", + }, + } { + require.Equal(t, common.HexToHash(test.expectedHash), test.source.SourceHash()) + } +} + +func TestJovianNetworkTransactions(t *testing.T) { + upgradeTxns, err := JovianNetworkUpgradeTransactions() + require.NoError(t, err) + require.Len(t, upgradeTxns, 1) + + deployL1BlockSender, deployL1Block := toDepositTxn(t, upgradeTxns[0]) + require.Equal(t, deployL1BlockSender, common.HexToAddress("0x4210000000000000000000000000000000000005")) + require.Equal(t, deployJovianL1BlockSource.SourceHash(), deployL1Block.SourceHash()) + require.Nil(t, deployL1Block.To()) + require.Equal(t, uint64(375_000), deployL1Block.Gas()) + require.Equal(t, jovianL1BlockDeploymentBytecode, deployL1Block.Data()) + + l1 := interopgen.CreateL1(log.Root(), nil, nil, &interopgen.L1Config{ + ChainID: big.NewInt(1337), + }) + address, err := l1.Create(deployL1BlockSender, jovianL1BlockDeploymentBytecode) + require.NoError(t, err) + require.Equal(t, address, common.HexToAddress("0x4fa2Be8cd41504037F1838BcE3bCC93bC68Ff537")) + codeHash := crypto.Keccak256Hash(l1.GetCode(address)) + require.Equal(t, codeHash, common.HexToHash("0xea1f176e3bcab831c781395fca0974d470ea540e602c230b471814fb43883e74")) +} diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index a01fe5bca6b9..00d3c2f06c64 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -20,12 +20,14 @@ import ( const ( L1InfoFuncBedrockSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" L1InfoFuncEcotoneSignature = "setL1BlockValuesEcotone()" + L1InfoFuncJovianSignature = "setL1BlockValuesJovian()" L1InfoFuncInteropSignature = "setL1BlockValuesInterop()" DepositsCompleteSignature = "depositsComplete()" L1InfoArguments = 8 L1InfoBedrockLen = 4 + 32*L1InfoArguments - L1InfoEcotoneLen = 4 + 32*5 // after Ecotone upgrade, args are packed into 5 32-byte slots - DepositsCompleteLen = 4 // only the selector + L1InfoEcotoneLen = 4 + 32*5 // after Ecotone upgrade, args are packed into 5 32-byte slots + L1InfoJovianLen = 4 + 32*5 + 16 // after Jovian upgrade, args are packed into 5 32-byte slots and 1 16-byte slot + DepositsCompleteLen = 4 // only the selector // DepositsCompleteGas allocates 21k gas for intrinsic tx costs, and // an additional 15k to ensure that the DepositsComplete call does not run out of gas. // GasBenchMark_L1BlockInterop_DepositsComplete:test_depositsComplete_benchmark() (gas: 7768) @@ -37,6 +39,7 @@ const ( var ( L1InfoFuncBedrockBytes4 = crypto.Keccak256([]byte(L1InfoFuncBedrockSignature))[:4] L1InfoFuncEcotoneBytes4 = crypto.Keccak256([]byte(L1InfoFuncEcotoneSignature))[:4] + L1InfoFuncJovianBytes4 = crypto.Keccak256([]byte(L1InfoFuncJovianSignature))[:4] L1InfoFuncInteropBytes4 = crypto.Keccak256([]byte(L1InfoFuncInteropSignature))[:4] DepositsCompleteBytes4 = crypto.Keccak256([]byte(DepositsCompleteSignature))[:4] L1InfoDepositerAddress = common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001") @@ -66,6 +69,9 @@ type L1BlockInfo struct { BlobBaseFee *big.Int // added by Ecotone upgrade BaseFeeScalar uint32 // added by Ecotone upgrade BlobBaseFeeScalar uint32 // added by Ecotone upgrade + + DepositNonce uint64 // added by the Jovian upgrade + ConfigUpdateNonce uint64 // added by the Jovian upgrade } // Bedrock Binary Format @@ -155,7 +161,7 @@ func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error { return nil } -// Interop & Ecotone Binary Format +// Ecotone Binary Format // +---------+--------------------------+ // | Bytes | Field | // +---------+--------------------------+ @@ -171,24 +177,55 @@ func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error { // | 32 | BatcherHash | // +---------+--------------------------+ +// Jovian & Interop Binary Format +// +---------+--------------------------+ +// | Bytes | Field | +// +---------+--------------------------+ +// | 4 | Function signature | +// | 4 | BaseFeeScalar | +// | 4 | BlobBaseFeeScalar | +// | 8 | SequenceNumber | +// | 8 | Timestamp | +// | 8 | L1BlockNumber | +// | 32 | BaseFee | +// | 32 | BlobBaseFee | +// | 32 | BlockHash | +// | 32 | BatcherHash | +// | 8 | DepositNonce | +// | 8 | ConfigUpdateNonce | +// +---------+--------------------------+ + func (info *L1BlockInfo) marshalBinaryEcotone() ([]byte, error) { - out, err := marshalBinaryWithSignature(info, L1InfoFuncEcotoneBytes4) + out, err := marshalBinaryWithSignature(info, L1InfoFuncEcotoneBytes4, false) if err != nil { return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) } return out, nil } +func (info *L1BlockInfo) marshalBinaryJovian() ([]byte, error) { + out, err := marshalBinaryWithSignature(info, L1InfoFuncJovianBytes4, true) + if err != nil { + return nil, fmt.Errorf("failed to marshal Jovian l1 block info: %w", err) + } + return out, nil +} + func (info *L1BlockInfo) marshalBinaryInterop() ([]byte, error) { - out, err := marshalBinaryWithSignature(info, L1InfoFuncInteropBytes4) + out, err := marshalBinaryWithSignature(info, L1InfoFuncInteropBytes4, true) if err != nil { return nil, fmt.Errorf("failed to marshal Interop l1 block info: %w", err) } return out, nil } -func marshalBinaryWithSignature(info *L1BlockInfo, signature []byte) ([]byte, error) { - w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) // Ecotone and Interop have the same length +func marshalBinaryWithSignature(info *L1BlockInfo, signature []byte, includeNonces bool) ([]byte, error) { + var w *bytes.Buffer + if includeNonces { + w = bytes.NewBuffer(make([]byte, 0, L1InfoJovianLen)) // Jovian and Interop have the same length + } else { + w = bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) + } if err := solabi.WriteSignature(w, signature); err != nil { return nil, err } @@ -224,19 +261,35 @@ func marshalBinaryWithSignature(info *L1BlockInfo, signature []byte) ([]byte, er if err := solabi.WriteAddress(w, info.BatcherAddr); err != nil { return nil, err } + if includeNonces { + if err := binary.Write(w, binary.BigEndian, info.DepositNonce); err != nil { + return nil, err + } + if err := binary.Write(w, binary.BigEndian, info.ConfigUpdateNonce); err != nil { + return nil, err + } + } return w.Bytes(), nil } func (info *L1BlockInfo) unmarshalBinaryEcotone(data []byte) error { - return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncEcotoneBytes4, data) + return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncEcotoneBytes4, data, false) +} + +func (info *L1BlockInfo) unmarshalBinaryJovian(data []byte) error { + return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncJovianBytes4, data, true) } func (info *L1BlockInfo) unmarshalBinaryInterop(data []byte) error { - return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncInteropBytes4, data) + return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncInteropBytes4, data, true) } -func unmarshalBinaryWithSignatureAndData(info *L1BlockInfo, signature []byte, data []byte) error { - if len(data) != L1InfoEcotoneLen { +func unmarshalBinaryWithSignatureAndData(info *L1BlockInfo, signature []byte, data []byte, includeNonces bool) error { + expectedLength := L1InfoEcotoneLen + if includeNonces { + expectedLength = L1InfoJovianLen + } + if len(data) != expectedLength { return fmt.Errorf("data is unexpected length: %d", len(data)) } r := bytes.NewReader(data) @@ -273,6 +326,14 @@ func unmarshalBinaryWithSignatureAndData(info *L1BlockInfo, signature []byte, da if info.BatcherAddr, err = solabi.ReadAddress(r); err != nil { return err } + if includeNonces { + if err := binary.Read(r, binary.BigEndian, &info.DepositNonce); err != nil { + return ErrInvalidFormat + } + if err := binary.Read(r, binary.BigEndian, &info.ConfigUpdateNonce); err != nil { + return ErrInvalidFormat + } + } if !solabi.EmptyReader(r) { return errors.New("too many bytes") } @@ -285,6 +346,12 @@ func isEcotoneButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) boo return rollupCfg.IsEcotone(l2Timestamp) && !rollupCfg.IsEcotoneActivationBlock(l2Timestamp) } +// isJovianButNotFirstBlock returns whether the specified block is subject to the Jovian upgrade, +// but is not the activation block itself. +func isJovianButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) bool { + return rollupCfg.IsJovian(l2Timestamp) && !rollupCfg.IsJovianActivationBlock(l2Timestamp) +} + // isInteropButNotFirstBlock returns whether the specified block is subject to the Interop upgrade, // but is not the activation block itself. func isInteropButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) bool { @@ -302,6 +369,9 @@ func L1BlockInfoFromBytes(rollupCfg *rollup.Config, l2BlockTime uint64, data []b if isInteropButNotFirstBlock(rollupCfg, l2BlockTime) { return &info, info.unmarshalBinaryInterop(data) } + if isJovianButNotFirstBlock(rollupCfg, l2BlockTime) { + return &info, info.unmarshalBinaryJovian(data) + } if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) { return &info, info.unmarshalBinaryEcotone(data) } @@ -319,6 +389,15 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber SequenceNumber: seqNumber, BatcherAddr: sysCfg.BatcherAddr, } + jovian := isJovianButNotFirstBlock(rollupCfg, l2Timestamp) + if !jovian { + if sysCfg.DepositNonce != 0 { + return nil, fmt.Errorf("found non-zero deposit nonce for non-Jovian block") + } + if sysCfg.ConfigUpdateNonce != 0 { + return nil, fmt.Errorf("found non-zero config update nonce for non-Jovian block") + } + } var data []byte if isEcotoneButNotFirstBlock(rollupCfg, l2Timestamp) { l1BlockInfo.BlobBaseFee = block.BlobBaseFee() @@ -332,27 +411,34 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber } l1BlockInfo.BlobBaseFeeScalar = scalars.BlobBaseFeeScalar l1BlockInfo.BaseFeeScalar = scalars.BaseFeeScalar - if isInteropButNotFirstBlock(rollupCfg, l2Timestamp) { - out, err := l1BlockInfo.marshalBinaryInterop() - if err != nil { - return nil, fmt.Errorf("failed to marshal Interop l1 block info: %w", err) + if jovian { + l1BlockInfo.DepositNonce = sysCfg.DepositNonce + l1BlockInfo.ConfigUpdateNonce = sysCfg.ConfigUpdateNonce + if isInteropButNotFirstBlock(rollupCfg, l2Timestamp) { + data, err = l1BlockInfo.marshalBinaryInterop() + if err != nil { + return nil, fmt.Errorf("failed to marshal Interop l1 block info: %w", err) + } + } else { + data, err = l1BlockInfo.marshalBinaryJovian() + if err != nil { + return nil, fmt.Errorf("failed to marshal Jovian l1 block info: %w", err) + } } - data = out } else { - out, err := l1BlockInfo.marshalBinaryEcotone() + data, err = l1BlockInfo.marshalBinaryEcotone() if err != nil { return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) } - data = out } } else { l1BlockInfo.L1FeeOverhead = sysCfg.Overhead l1BlockInfo.L1FeeScalar = sysCfg.Scalar - out, err := l1BlockInfo.marshalBinaryBedrock() + var err error + data, err = l1BlockInfo.marshalBinaryBedrock() if err != nil { return nil, fmt.Errorf("failed to marshal Bedrock l1 block info: %w", err) } - data = out } source := L1InfoDepositSource{ diff --git a/op-node/rollup/derive/l1_block_info_test.go b/op-node/rollup/derive/l1_block_info_test.go index 3f7dd0647e6d..1765f5f63188 100644 --- a/op-node/rollup/derive/l1_block_info_test.go +++ b/op-node/rollup/derive/l1_block_info_test.go @@ -63,6 +63,19 @@ func TestParseL1InfoDepositTxData(t *testing.T) { return 0 }}, } + assertInfo := func(rollupCfg *rollup.Config, info *testutils.MockBlockInfo, depTx *types.DepositTx, l2BlockTime, seqNr uint64, l1Cfg eth.SystemConfig) { + res, err := L1BlockInfoFromBytes(rollupCfg, l2BlockTime, depTx.Data) + require.NoError(t, err, "expected valid deposit info") + assert.Equal(t, res.Number, info.NumberU64()) + assert.Equal(t, res.Time, info.Time()) + assert.True(t, res.BaseFee.Sign() >= 0) + assert.Equal(t, res.BaseFee.Bytes(), info.BaseFee().Bytes()) + assert.Equal(t, res.BlockHash, info.Hash()) + assert.Equal(t, res.SequenceNumber, seqNr) + assert.Equal(t, res.BatcherAddr, l1Cfg.BatcherAddr) + assert.Equal(t, res.L1FeeOverhead, l1Cfg.Overhead) + assert.Equal(t, res.L1FeeScalar, l1Cfg.Scalar) + } var rollupCfg rollup.Config for i, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { @@ -72,17 +85,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { seqNr := testCase.seqNr(rng) depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, 0) require.NoError(t, err) - res, err := L1BlockInfoFromBytes(&rollupCfg, info.Time(), depTx.Data) - require.NoError(t, err, "expected valid deposit info") - assert.Equal(t, res.Number, info.NumberU64()) - assert.Equal(t, res.Time, info.Time()) - assert.True(t, res.BaseFee.Sign() >= 0) - assert.Equal(t, res.BaseFee.Bytes(), info.BaseFee().Bytes()) - assert.Equal(t, res.BlockHash, info.Hash()) - assert.Equal(t, res.SequenceNumber, seqNr) - assert.Equal(t, res.BatcherAddr, l1Cfg.BatcherAddr) - assert.Equal(t, res.L1FeeOverhead, l1Cfg.Overhead) - assert.Equal(t, res.L1FeeScalar, l1Cfg.Scalar) + assertInfo(&rollupCfg, info, depTx, 0, seqNr, l1Cfg) }) } t.Run("no data", func(t *testing.T) { @@ -112,10 +115,13 @@ func TestParseL1InfoDepositTxData(t *testing.T) { info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{} rollupCfg.ActivateAtGenesis(rollup.Regolith) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, 0) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) + assertInfo(&rollupCfg, info, depTx, 0, seqNr, l1Cfg) }) t.Run("ecotone", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) @@ -124,11 +130,14 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Ecotone) // run 1 block after ecotone transition timestamp := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, timestamp) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) + assertInfo(&rollupCfg, info, depTx, timestamp, seqNr, l1Cfg) }) t.Run("activation-block ecotone", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) @@ -137,22 +146,77 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Delta) ecotoneTime := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime // activate ecotone just after genesis rollupCfg.EcotoneTime = &ecotoneTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, ecotoneTime) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, ecotoneTime) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoBedrockLen, len(depTx.Data)) + assertInfo(&rollupCfg, info, depTx, ecotoneTime, seqNr, l1Cfg) }) t.Run("genesis-block ecotone", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} rollupCfg.ActivateAtGenesis(rollup.Ecotone) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, rollupCfg.Genesis.L2Time) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) + assertInfo(&rollupCfg, info, depTx, rollupCfg.Genesis.L2Time, seqNr, l1Cfg) + }) + t.Run("jovian", func(t *testing.T) { + rng := rand.New(rand.NewSource(1234)) + info := testutils.MakeBlockInfo(nil)(rng) + rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} + rollupCfg.ActivateAtGenesis(rollup.Jovian) + // run 1 block after jovian transition + timestamp := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, timestamp) + require.NoError(t, err) + require.False(t, depTx.IsSystemTransaction) + require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) + require.Equal(t, L1InfoJovianLen, len(depTx.Data), "the length is same in jovian") + require.Equal(t, L1InfoFuncJovianBytes4, depTx.Data[:4], "upgrade is active, need jovian signature") + assertInfo(&rollupCfg, info, depTx, timestamp, seqNr, l1Cfg) + }) + t.Run("activation-block jovian", func(t *testing.T) { + rng := rand.New(rand.NewSource(1234)) + info := testutils.MakeBlockInfo(nil)(rng) + rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} + rollupCfg.ActivateAtGenesis(rollup.Holocene) + jovianTime := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime // activate jovian just after genesis + rollupCfg.JovianTime = &jovianTime + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, jovianTime) + require.NoError(t, err) + require.False(t, depTx.IsSystemTransaction) + require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) + // Jovian activates, but ecotone L1 info is still used at this upgrade block + require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) + require.Equal(t, L1InfoFuncEcotoneBytes4, depTx.Data[:4]) + assertInfo(&rollupCfg, info, depTx, jovianTime, seqNr, l1Cfg) + }) + t.Run("genesis-block jovian", func(t *testing.T) { + rng := rand.New(rand.NewSource(1234)) + info := testutils.MakeBlockInfo(nil)(rng) + rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} + rollupCfg.ActivateAtGenesis(rollup.Jovian) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, rollupCfg.Genesis.L2Time) + require.NoError(t, err) + require.False(t, depTx.IsSystemTransaction) + require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) + require.Equal(t, L1InfoJovianLen, len(depTx.Data)) + assertInfo(&rollupCfg, info, depTx, rollupCfg.Genesis.L2Time, seqNr, l1Cfg) }) t.Run("interop", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) @@ -161,12 +225,15 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Interop) // run 1 block after interop transition timestamp := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, timestamp) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) - require.Equal(t, L1InfoEcotoneLen, len(depTx.Data), "the length is same in interop") + require.Equal(t, L1InfoJovianLen, len(depTx.Data), "the length is same in interop") require.Equal(t, L1InfoFuncInteropBytes4, depTx.Data[:4], "upgrade is active, need interop signature") + assertInfo(&rollupCfg, info, depTx, timestamp, seqNr, l1Cfg) }) t.Run("activation-block interop", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) @@ -175,24 +242,30 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Fjord) interopTime := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime // activate interop just after genesis rollupCfg.InteropTime = &interopTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, interopTime) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, interopTime) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) // Interop activates, but ecotone L1 info is still used at this upgrade block require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) require.Equal(t, L1InfoFuncEcotoneBytes4, depTx.Data[:4]) + assertInfo(&rollupCfg, info, depTx, interopTime, seqNr, l1Cfg) }) t.Run("genesis-block interop", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} rollupCfg.ActivateAtGenesis(rollup.Interop) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) + l1Cfg := randomL1Cfg(rng, info) + seqNr := randomSeqNr(rng) + depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, rollupCfg.Genesis.L2Time) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) - require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) + require.Equal(t, L1InfoJovianLen, len(depTx.Data)) + assertInfo(&rollupCfg, info, depTx, rollupCfg.Genesis.L2Time, seqNr, l1Cfg) }) } diff --git a/op-node/rollup/derive/l1_block_info_tob_test.go b/op-node/rollup/derive/l1_block_info_tob_test.go index d7c9f2f8931d..1454603c9164 100644 --- a/op-node/rollup/derive/l1_block_info_tob_test.go +++ b/op-node/rollup/derive/l1_block_info_tob_test.go @@ -26,6 +26,8 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) { typeProvider.Fuzz(&seqNr) var sysCfg eth.SystemConfig typeProvider.Fuzz(&sysCfg) + sysCfg.DepositNonce = 0 + sysCfg.ConfigUpdateNonce = 0 var rollupCfg rollup.Config // Create our deposit tx from our info diff --git a/op-node/rollup/derive/payload_util.go b/op-node/rollup/derive/payload_util.go index 6da1a952b5fe..2d304d9ae27b 100644 --- a/op-node/rollup/derive/payload_util.go +++ b/op-node/rollup/derive/payload_util.go @@ -85,10 +85,12 @@ func PayloadToSystemConfig(rollupCfg *rollup.Config, payload *eth.ExecutionPaylo binary.BigEndian.PutUint32(info.L1FeeScalar[28:32], info.BaseFeeScalar) } r := eth.SystemConfig{ - BatcherAddr: info.BatcherAddr, - Overhead: info.L1FeeOverhead, - Scalar: info.L1FeeScalar, - GasLimit: uint64(payload.GasLimit), + BatcherAddr: info.BatcherAddr, + Overhead: info.L1FeeOverhead, + Scalar: info.L1FeeScalar, + GasLimit: uint64(payload.GasLimit), + DepositNonce: info.DepositNonce, + ConfigUpdateNonce: info.ConfigUpdateNonce, } if rollupCfg.IsHolocene(uint64(payload.Timestamp)) { if err := eip1559.ValidateHoloceneExtraData(payload.ExtraData); err != nil { diff --git a/op-node/rollup/derive/system_config.go b/op-node/rollup/derive/system_config.go index 72c4e713c30e..1c38c4e57722 100644 --- a/op-node/rollup/derive/system_config.go +++ b/op-node/rollup/derive/system_config.go @@ -27,7 +27,8 @@ var ( var ( ConfigUpdateEventABI = "ConfigUpdate(uint256,uint8,bytes)" ConfigUpdateEventABIHash = crypto.Keccak256Hash([]byte(ConfigUpdateEventABI)) - ConfigUpdateEventVersion0 = common.Hash{} + ConfigUpdateEventVersion0 = uint64(0) + ConfigUpdateEventVersion1 = uint64(1) ) // UpdateSystemConfigWithL1Receipts filters all L1 receipts to find config updates and applies the config updates to the given sysCfg @@ -39,7 +40,8 @@ func UpdateSystemConfigWithL1Receipts(sysCfg *eth.SystemConfig, receipts []*type } for j, log := range rec.Logs { if log.Address == cfg.L1SystemConfigAddress && len(log.Topics) > 0 && log.Topics[0] == ConfigUpdateEventABIHash { - if err := ProcessSystemConfigUpdateLogEvent(sysCfg, log, cfg, l1Time); err != nil { + var err error + if err = ProcessSystemConfigUpdateLogEvent(sysCfg, log, cfg, l1Time); err != nil { result = multierror.Append(result, fmt.Errorf("malformatted L1 system sysCfg log in receipt %d, log %d: %w", i, j, err)) } } @@ -53,7 +55,7 @@ func UpdateSystemConfigWithL1Receipts(sysCfg *eth.SystemConfig, receipts []*type // parse log data for: // // event ConfigUpdate( -// uint256 indexed version, +// uint256 indexed nonceAndVersion, // UpdateType indexed updateType, // bytes data // ); @@ -66,10 +68,18 @@ func ProcessSystemConfigUpdateLogEvent(destSysCfg *eth.SystemConfig, ev *types.L } // indexed 0 - version := ev.Topics[1] - if version != ConfigUpdateEventVersion0 { - return fmt.Errorf("unrecognized SystemConfig update event version: %s", version) + newNonce, version := UnpackNonceAndVersion(ev.Topics[1]) + switch version { + case ConfigUpdateEventVersion0: + case ConfigUpdateEventVersion1: + if newNonce != destSysCfg.ConfigUpdateNonce+1 { + return fmt.Errorf("invalid config update nonce, expected %d got %d", destSysCfg.ConfigUpdateNonce+1, newNonce) + } + destSysCfg.ConfigUpdateNonce = newNonce + default: + return fmt.Errorf("unrecognized SystemConfig update event version: %d", version) } + // indexed 1 updateType := ev.Topics[2] diff --git a/op-node/rollup/derive/system_config_test.go b/op-node/rollup/derive/system_config_test.go index add179093879..e9530af62828 100644 --- a/op-node/rollup/derive/system_config_test.go +++ b/op-node/rollup/derive/system_config_test.go @@ -57,7 +57,7 @@ func TestProcessSystemConfigUpdateLogEvent(t *testing.T) { log: &types.Log{ Topics: []common.Hash{ ConfigUpdateEventABIHash, - ConfigUpdateEventVersion0, + PackNonceAndVersion(0, ConfigUpdateEventVersion0), SystemConfigUpdateUnsafeBlockSigner, }, }, @@ -77,7 +77,7 @@ func TestProcessSystemConfigUpdateLogEvent(t *testing.T) { log: &types.Log{ Topics: []common.Hash{ ConfigUpdateEventABIHash, - ConfigUpdateEventVersion0, + PackNonceAndVersion(0, ConfigUpdateEventVersion0), SystemConfigUpdateBatcher, }, }, @@ -101,7 +101,7 @@ func TestProcessSystemConfigUpdateLogEvent(t *testing.T) { log: &types.Log{ Topics: []common.Hash{ ConfigUpdateEventABIHash, - ConfigUpdateEventVersion0, + PackNonceAndVersion(0, ConfigUpdateEventVersion0), SystemConfigUpdateFeeScalars, }, }, @@ -127,7 +127,7 @@ func TestProcessSystemConfigUpdateLogEvent(t *testing.T) { log: &types.Log{ Topics: []common.Hash{ ConfigUpdateEventABIHash, - ConfigUpdateEventVersion0, + PackNonceAndVersion(0, ConfigUpdateEventVersion0), SystemConfigUpdateGasLimit, }, }, @@ -151,7 +151,7 @@ func TestProcessSystemConfigUpdateLogEvent(t *testing.T) { log: &types.Log{ Topics: []common.Hash{ ConfigUpdateEventABIHash, - ConfigUpdateEventVersion0, + PackNonceAndVersion(0, ConfigUpdateEventVersion0), SystemConfigUpdateFeeScalars, }, }, @@ -191,7 +191,7 @@ func TestProcessSystemConfigUpdateLogEvent(t *testing.T) { log: &types.Log{ Topics: []common.Hash{ ConfigUpdateEventABIHash, - ConfigUpdateEventVersion0, + PackNonceAndVersion(0, ConfigUpdateEventVersion0), SystemConfigUpdateEIP1559Params, }, }, diff --git a/op-node/rollup/sequencing/sequencer.go b/op-node/rollup/sequencing/sequencer.go index 2476b9b639f9..1cc6e1de60f0 100644 --- a/op-node/rollup/sequencing/sequencer.go +++ b/op-node/rollup/sequencing/sequencer.go @@ -543,11 +543,17 @@ func (d *Sequencer) startBuildingBlock() { d.log.Info("Sequencing Fjord upgrade block") } - // For the Granite activation block we shouldn't include any sequencer transactions. + // For the Granite activation block we allow sequencer transactions. if d.rollupCfg.IsGraniteActivationBlock(uint64(attrs.Timestamp)) { d.log.Info("Sequencing Granite upgrade block") } + // For the Isthmus activation block we shouldn't include any sequencer transactions. + if d.rollupCfg.IsIsthmusActivationBlock(uint64(attrs.Timestamp)) { + attrs.NoTxPool = true + d.log.Info("Sequencing Isthmus upgrade block") + } + d.log.Debug("prepared attributes for new block", "num", l2Head.Number+1, "time", uint64(attrs.Timestamp), "origin", l1Origin, "origin_time", l1Origin.Time, "noTxPool", attrs.NoTxPool) diff --git a/op-node/rollup/superchain.go b/op-node/rollup/superchain.go index ef6ed1f51676..c7b8a64dda51 100644 --- a/op-node/rollup/superchain.go +++ b/op-node/rollup/superchain.go @@ -92,6 +92,8 @@ func LoadOPStackRollupConfig(chainID uint64) (*Config, error) { FjordTime: chConfig.FjordTime, GraniteTime: chConfig.GraniteTime, HoloceneTime: chConfig.HoloceneTime, + IsthmusTime: chConfig.IsthmusTime, + JovianTime: chConfig.JovianTime, BatchInboxAddress: common.Address(chConfig.BatchInboxAddr), DepositContractAddress: common.Address(addrs.OptimismPortalProxy), L1SystemConfigAddress: common.Address(addrs.SystemConfigProxy), diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 6201fc6be9c6..c895f77ed4fb 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -124,6 +124,10 @@ type Config struct { // Active if IsthmusTime != nil && L2 block timestamp >= *IsthmusTime, inactive otherwise. IsthmusTime *uint64 `json:"isthmus_time,omitempty"` + // JovianTime sets the activation time of the Jovian network upgrade. + // Active if JovianTime != nil && L2 block timestamp >= *JovianTime, inactive otherwise. + JovianTime *uint64 `json:"jovian_time,omitempty"` + // InteropTime sets the activation time for an experimental feature-set, activated like a hardfork. // Active if InteropTime != nil && L2 block timestamp >= *InteropTime, inactive otherwise. InteropTime *uint64 `json:"interop_time,omitempty"` @@ -417,6 +421,11 @@ func (c *Config) IsIsthmus(timestamp uint64) bool { return c.IsthmusTime != nil && timestamp >= *c.IsthmusTime } +// IsJovian returns true if the Jovian hardfork is active at or past the given timestamp. +func (c *Config) IsJovian(timestamp uint64) bool { + return c.JovianTime != nil && timestamp >= *c.JovianTime +} + // IsInterop returns true if the Interop hardfork is active at or past the given timestamp. func (c *Config) IsInterop(timestamp uint64) bool { return c.InteropTime != nil && timestamp >= *c.InteropTime @@ -480,6 +489,14 @@ func (c *Config) IsIsthmusActivationBlock(l2BlockTime uint64) bool { !c.IsIsthmus(l2BlockTime-c.BlockTime) } +// IsJovianActivationBlock returns whether the specified block is the first block subject to the +// Jovian upgrade. +func (c *Config) IsJovianActivationBlock(l2BlockTime uint64) bool { + return c.IsJovian(l2BlockTime) && + l2BlockTime >= c.BlockTime && + !c.IsJovian(l2BlockTime-c.BlockTime) +} + func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool { return c.IsInterop(l2BlockTime) && l2BlockTime >= c.BlockTime && @@ -503,6 +520,9 @@ func (c *Config) ActivateAtGenesis(hardfork ForkName) { case Interop: c.InteropTime = new(uint64) fallthrough + case Jovian: + c.JovianTime = new(uint64) + fallthrough case Isthmus: c.IsthmusTime = new(uint64) fallthrough @@ -646,6 +666,7 @@ func (c *Config) Description(l2Chains map[string]string) string { banner += fmt.Sprintf(" - Granite: %s\n", fmtForkTimeOrUnset(c.GraniteTime)) banner += fmt.Sprintf(" - Holocene: %s\n", fmtForkTimeOrUnset(c.HoloceneTime)) banner += fmt.Sprintf(" - Isthmus: %s\n", fmtForkTimeOrUnset(c.IsthmusTime)) + banner += fmt.Sprintf(" - Jovian: %s\n", fmtForkTimeOrUnset(c.JovianTime)) banner += fmt.Sprintf(" - Interop: %s\n", fmtForkTimeOrUnset(c.InteropTime)) // Report the protocol version banner += fmt.Sprintf("Node supports up to OP-Stack Protocol Version: %s\n", OPStackSupport) @@ -683,6 +704,7 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) { "granite_time", fmtForkTimeOrUnset(c.GraniteTime), "holocene_time", fmtForkTimeOrUnset(c.HoloceneTime), "isthmus_time", fmtForkTimeOrUnset(c.IsthmusTime), + "jovian_time", fmtForkTimeOrUnset(c.JovianTime), "interop_time", fmtForkTimeOrUnset(c.InteropTime), "alt_da", c.AltDAConfig != nil, ) diff --git a/op-node/rollup/types_test.go b/op-node/rollup/types_test.go index dc7b5ded1673..e6e8ca4fe2fd 100644 --- a/op-node/rollup/types_test.go +++ b/op-node/rollup/types_test.go @@ -185,7 +185,9 @@ func TestRandomConfigDescription(t *testing.T) { config.HoloceneTime = &h i := uint64(1677119341) config.IsthmusTime = &i - it := uint64(1677119342) + j := uint64(1677119342) + config.JovianTime = &j + it := uint64(1677119343) config.InteropTime = &it out := config.Description(nil) @@ -290,6 +292,15 @@ func TestActivations(t *testing.T) { return c.IsIsthmus(t) }, }, + { + name: "Jovian", + setUpgradeTime: func(t *uint64, c *Config) { + c.JovianTime = t + }, + checkEnabled: func(t uint64, c *Config) bool { + return c.IsJovian(t) + }, + }, { name: "Interop", setUpgradeTime: func(t *uint64, c *Config) { @@ -562,7 +573,8 @@ func TestConfig_Check(t *testing.T) { graniteTime := uint64(6) holoceneTime := uint64(7) isthmusTime := uint64(8) - interopTime := uint64(9) + jovianTime := uint64(9) + interopTime := uint64(10) cfg.RegolithTime = ®olithTime cfg.CanyonTime = &canyonTime cfg.DeltaTime = &deltaTime @@ -571,6 +583,7 @@ func TestConfig_Check(t *testing.T) { cfg.GraniteTime = &graniteTime cfg.HoloceneTime = &holoceneTime cfg.IsthmusTime = &isthmusTime + cfg.JovianTime = &jovianTime cfg.InteropTime = &interopTime }, expectedErr: nil, diff --git a/op-program/client/l2/engineapi/l2_engine_api.go b/op-program/client/l2/engineapi/l2_engine_api.go index 45343d8c7fd8..33156bf9d175 100644 --- a/op-program/client/l2/engineapi/l2_engine_api.go +++ b/op-program/client/l2/engineapi/l2_engine_api.go @@ -486,23 +486,24 @@ func (ea *L2EngineAPI) newPayload(_ context.Context, payload *eth.ExecutionPaylo txs[i] = tx } block, err := engine.ExecutableDataToBlock(engine.ExecutableData{ - ParentHash: payload.ParentHash, - FeeRecipient: payload.FeeRecipient, - StateRoot: common.Hash(payload.StateRoot), - ReceiptsRoot: common.Hash(payload.ReceiptsRoot), - LogsBloom: payload.LogsBloom[:], - Random: common.Hash(payload.PrevRandao), - Number: uint64(payload.BlockNumber), - GasLimit: uint64(payload.GasLimit), - GasUsed: uint64(payload.GasUsed), - Timestamp: uint64(payload.Timestamp), - ExtraData: payload.ExtraData, - BaseFeePerGas: (*uint256.Int)(&payload.BaseFeePerGas).ToBig(), - BlockHash: payload.BlockHash, - Transactions: txs, - Withdrawals: toGethWithdrawals(payload), - ExcessBlobGas: (*uint64)(payload.ExcessBlobGas), - BlobGasUsed: (*uint64)(payload.BlobGasUsed), + ParentHash: payload.ParentHash, + FeeRecipient: payload.FeeRecipient, + StateRoot: common.Hash(payload.StateRoot), + ReceiptsRoot: common.Hash(payload.ReceiptsRoot), + LogsBloom: payload.LogsBloom[:], + Random: common.Hash(payload.PrevRandao), + Number: uint64(payload.BlockNumber), + GasLimit: uint64(payload.GasLimit), + GasUsed: uint64(payload.GasUsed), + Timestamp: uint64(payload.Timestamp), + ExtraData: payload.ExtraData, + BaseFeePerGas: (*uint256.Int)(&payload.BaseFeePerGas).ToBig(), + BlockHash: payload.BlockHash, + Transactions: txs, + Withdrawals: toGethWithdrawals(payload), + ExcessBlobGas: (*uint64)(payload.ExcessBlobGas), + BlobGasUsed: (*uint64)(payload.BlobGasUsed), + WithdrawalsRoot: payload.WithdrawalsRoot, }, hashes, root, ea.backend.Config()) if err != nil { log.Debug("Invalid NewPayload params", "params", payload, "error", err) diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 122e9a4df783..d1a22e249142 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -231,6 +231,8 @@ type ExecutionPayload struct { BlobGasUsed *Uint64Quantity `json:"blobGasUsed,omitempty"` // Nil if not present (Bedrock, Canyon, Delta) ExcessBlobGas *Uint64Quantity `json:"excessBlobGas,omitempty"` + // Nil if not present (Bedrock, Canyon, Delta, Ecotone, Fjord, Granite, Holocene) + WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` } func (payload *ExecutionPayload) ID() BlockID { @@ -307,22 +309,23 @@ func BlockAsPayload(bl *types.Block, shanghaiTime *uint64) (*ExecutionPayload, e } payload := &ExecutionPayload{ - ParentHash: bl.ParentHash(), - FeeRecipient: bl.Coinbase(), - StateRoot: Bytes32(bl.Root()), - ReceiptsRoot: Bytes32(bl.ReceiptHash()), - LogsBloom: Bytes256(bl.Bloom()), - PrevRandao: Bytes32(bl.MixDigest()), - BlockNumber: Uint64Quantity(bl.NumberU64()), - GasLimit: Uint64Quantity(bl.GasLimit()), - GasUsed: Uint64Quantity(bl.GasUsed()), - Timestamp: Uint64Quantity(bl.Time()), - ExtraData: bl.Extra(), - BaseFeePerGas: Uint256Quantity(*baseFee), - BlockHash: bl.Hash(), - Transactions: opaqueTxs, - ExcessBlobGas: (*Uint64Quantity)(bl.ExcessBlobGas()), - BlobGasUsed: (*Uint64Quantity)(bl.BlobGasUsed()), + ParentHash: bl.ParentHash(), + FeeRecipient: bl.Coinbase(), + StateRoot: Bytes32(bl.Root()), + ReceiptsRoot: Bytes32(bl.ReceiptHash()), + LogsBloom: Bytes256(bl.Bloom()), + PrevRandao: Bytes32(bl.MixDigest()), + BlockNumber: Uint64Quantity(bl.NumberU64()), + GasLimit: Uint64Quantity(bl.GasLimit()), + GasUsed: Uint64Quantity(bl.GasUsed()), + Timestamp: Uint64Quantity(bl.Time()), + ExtraData: bl.Extra(), + BaseFeePerGas: Uint256Quantity(*baseFee), + BlockHash: bl.Hash(), + Transactions: opaqueTxs, + ExcessBlobGas: (*Uint64Quantity)(bl.ExcessBlobGas()), + BlobGasUsed: (*Uint64Quantity)(bl.BlobGasUsed()), + WithdrawalsRoot: bl.WithdrawalsRoot(), } if shanghaiTime != nil && uint64(payload.Timestamp) >= *shanghaiTime { @@ -455,6 +458,10 @@ type SystemConfig struct { // value will be 0 if Holocene is not active, or if derivation has yet to // process any EIP_1559_PARAMS system config update events. EIP1559Params Bytes8 `json:"eip1559Params"` + // DepositNonce identifies the nonce of the last TransactionDeposited event processed by this chain. + DepositNonce uint64 `json:"depositNonce"` + // ConfigUpdateNonce identifies the nonce of the last ConfigUpdate event processed by this chain. + ConfigUpdateNonce uint64 `json:"configUpdateNonce"` // More fields can be added for future SystemConfig versions. // MarshalPreHolocene indicates whether or not this struct should be diff --git a/op-service/eth/types_test.go b/op-service/eth/types_test.go index 03409e16008d..40d69343e3eb 100644 --- a/op-service/eth/types_test.go +++ b/op-service/eth/types_test.go @@ -86,7 +86,7 @@ func TestSystemConfigMarshaling(t *testing.T) { } j, err := json.Marshal(sysConfig) require.NoError(t, err) - require.Equal(t, `{"batcherAddr":"0x4100000000000000000000000000000000000000","overhead":"0x0405060000000000000000000000000000000000000000000000000000000000","scalar":"0x0708090000000000000000000000000000000000000000000000000000000000","gasLimit":1234,"eip1559Params":"0x0000000000000000"}`, string(j)) + require.Equal(t, `{"batcherAddr":"0x4100000000000000000000000000000000000000","overhead":"0x0405060000000000000000000000000000000000000000000000000000000000","scalar":"0x0708090000000000000000000000000000000000000000000000000000000000","gasLimit":1234,"eip1559Params":"0x0000000000000000","depositNonce":0,"configUpdateNonce":0}`, string(j)) sysConfig.MarshalPreHolocene = true j, err = json.Marshal(sysConfig) require.NoError(t, err) diff --git a/op-service/sources/types.go b/op-service/sources/types.go index 4177ac666875..f31e88038a21 100644 --- a/op-service/sources/types.go +++ b/op-service/sources/types.go @@ -223,23 +223,24 @@ func (block *RPCBlock) ExecutionPayloadEnvelope(trustCache bool) (*eth.Execution } payload := ð.ExecutionPayload{ - ParentHash: block.ParentHash, - FeeRecipient: block.Coinbase, - StateRoot: eth.Bytes32(block.Root), - ReceiptsRoot: eth.Bytes32(block.ReceiptHash), - LogsBloom: block.Bloom, - PrevRandao: eth.Bytes32(block.MixDigest), // mix-digest field is used for prevRandao post-merge - BlockNumber: block.Number, - GasLimit: block.GasLimit, - GasUsed: block.GasUsed, - Timestamp: block.Time, - ExtraData: eth.BytesMax32(block.Extra), - BaseFeePerGas: eth.Uint256Quantity(baseFee), - BlockHash: block.Hash, - Transactions: opaqueTxs, - Withdrawals: block.Withdrawals, - BlobGasUsed: block.BlobGasUsed, - ExcessBlobGas: block.ExcessBlobGas, + ParentHash: block.ParentHash, + FeeRecipient: block.Coinbase, + StateRoot: eth.Bytes32(block.Root), + ReceiptsRoot: eth.Bytes32(block.ReceiptHash), + LogsBloom: block.Bloom, + PrevRandao: eth.Bytes32(block.MixDigest), // mix-digest field is used for prevRandao post-merge + BlockNumber: block.Number, + GasLimit: block.GasLimit, + GasUsed: block.GasUsed, + Timestamp: block.Time, + ExtraData: eth.BytesMax32(block.Extra), + BaseFeePerGas: eth.Uint256Quantity(baseFee), + BlockHash: block.Hash, + Transactions: opaqueTxs, + Withdrawals: block.Withdrawals, + BlobGasUsed: block.BlobGasUsed, + ExcessBlobGas: block.ExcessBlobGas, + WithdrawalsRoot: block.WithdrawalsRoot, } return ð.ExecutionPayloadEnvelope{ diff --git a/op-wheel/commands.go b/op-wheel/commands.go index 521578a34cb6..49232ed4fdec 100644 --- a/op-wheel/commands.go +++ b/op-wheel/commands.go @@ -256,6 +256,9 @@ func rollupFromGethConfig(cfg *params.ChainConfig) *rollup.Config { CanyonTime: cfg.CanyonTime, EcotoneTime: cfg.EcotoneTime, GraniteTime: cfg.GraniteTime, + HoloceneTime: cfg.HoloceneTime, + IsthmusTime: cfg.IsthmusTime, + JovianTime: cfg.JovianTime, InteropTime: cfg.InteropTime, } } diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol index 436a467ce291..f1ba317f294b 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol @@ -39,7 +39,7 @@ interface IOptimismPortal2 { event DisputeGameBlacklisted(IDisputeGame indexed disputeGame); event Initialized(uint8 version); event RespectedGameTypeSet(GameType indexed newGameType, Timestamp indexed updatedAt); - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); + event TransactionDeposited(address indexed from, address indexed to, uint256 indexed nonceAndVersion, bytes opaqueData); event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol index 3de6e5eea475..efa36100b0b2 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol @@ -40,13 +40,14 @@ interface IOptimismPortalInterop { event DisputeGameBlacklisted(IDisputeGame indexed disputeGame); event Initialized(uint8 version); event RespectedGameTypeSet(GameType indexed newGameType, Timestamp indexed updatedAt); - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); + event TransactionDeposited(address indexed from, address indexed to, uint256 indexed nonceAndVersion, bytes opaqueData); event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); receive() external payable; + function DEPOSIT_NONCE_SLOT() external view returns (bytes32); function balance() external view returns (uint256); function blacklistDisputeGame(IDisputeGame _disputeGame) external; function checkWithdrawal(bytes32 _withdrawalHash, address _proofSubmitter) external view; @@ -94,6 +95,7 @@ interface IOptimismPortalInterop { function paused() external view returns (bool); function proofMaturityDelaySeconds() external view returns (uint256); function proofSubmitters(bytes32, uint256) external view returns (address); + function depositNonce() external view returns (uint64 nonce_); function proveWithdrawalTransaction( Types.WithdrawalTransaction memory _tx, uint256 _disputeGameIndex, diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalJovian.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalJovian.sol new file mode 100644 index 000000000000..4c56166cd6b5 --- /dev/null +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalJovian.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Types } from "src/libraries/Types.sol"; +import { GameType, Timestamp } from "src/dispute/lib/LibUDT.sol"; +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; + +interface IOptimismPortalJovian { + error CustomGasTokenNotSupported(); + error AlreadyFinalized(); + error BadTarget(); + error Blacklisted(); + error CallPaused(); + error ContentLengthMismatch(); + error EmptyItem(); + error GasEstimation(); + error InvalidDataRemainder(); + error InvalidDisputeGame(); + error InvalidGameType(); + error InvalidHeader(); + error InvalidMerkleProof(); + error InvalidProof(); + error LargeCalldata(); + error NoValue(); + error NonReentrant(); + error OnlyCustomGasToken(); + error OutOfGas(); + error ProposalNotValidated(); + error SmallGasLimit(); + error TransferFailed(); + error Unauthorized(); + error UnexpectedList(); + error UnexpectedString(); + error Unproven(); + + event DisputeGameBlacklisted(IDisputeGame indexed disputeGame); + event Initialized(uint8 version); + event RespectedGameTypeSet(GameType indexed newGameType, Timestamp indexed updatedAt); + event TransactionDeposited(address indexed from, address indexed to, uint256 indexed nonceAndVersion, bytes opaqueData); + event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); + event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); + event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); + + receive() external payable; + + function DEPOSIT_NONCE_SLOT() external view returns (bytes32); + function balance() external view returns (uint256); + function blacklistDisputeGame(IDisputeGame _disputeGame) external; + function checkWithdrawal(bytes32 _withdrawalHash, address _proofSubmitter) external view; + function depositERC20Transaction( + address _to, + uint256 _mint, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data + ) + external; + function depositTransaction( + address _to, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data + ) + external + payable; + function disputeGameBlacklist(IDisputeGame) external view returns (bool); + function disputeGameFactory() external view returns (IDisputeGameFactory); + function disputeGameFinalityDelaySeconds() external view returns (uint256); + function donateETH() external payable; + function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external; + function finalizeWithdrawalTransactionExternalProof( + Types.WithdrawalTransaction memory _tx, + address _proofSubmitter + ) + external; + function finalizedWithdrawals(bytes32) external view returns (bool); + function guardian() external view returns (address); + function initialize( + IDisputeGameFactory _disputeGameFactory, + ISystemConfig _systemConfig, + ISuperchainConfig _superchainConfig, + GameType _initialRespectedGameType + ) + external; + function l2Sender() external view returns (address); + function minimumGasLimit(uint64 _byteCount) external pure returns (uint64); + function numProofSubmitters(bytes32 _withdrawalHash) external view returns (uint256); + function params() external view returns (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum); // nosemgrep + function paused() external view returns (bool); + function proofMaturityDelaySeconds() external view returns (uint256); + function proofSubmitters(bytes32, uint256) external view returns (address); + function depositNonce() external view returns (uint64 nonce_); + function proveWithdrawalTransaction( + Types.WithdrawalTransaction memory _tx, + uint256 _disputeGameIndex, + Types.OutputRootProof memory _outputRootProof, + bytes[] memory _withdrawalProof + ) + external; + function provenWithdrawals( + bytes32, + address + ) + external + view + returns (IDisputeGame disputeGameProxy, uint64 timestamp); // nosemgrep + function respectedGameType() external view returns (GameType); + function respectedGameTypeUpdatedAt() external view returns (uint64); + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; + function setRespectedGameType(GameType _gameType) external; + function superchainConfig() external view returns (ISuperchainConfig); + function systemConfig() external view returns (ISystemConfig); + function version() external pure returns (string memory); + + function __constructor__(uint256 _proofMaturityDelaySeconds, uint256 _disputeGameFinalityDelaySeconds) external; +} diff --git a/packages/contracts-bedrock/interfaces/L1/ISystemConfig.sol b/packages/contracts-bedrock/interfaces/L1/ISystemConfig.sol index 7ed0138ec5bd..cc7b9ee03d3a 100644 --- a/packages/contracts-bedrock/interfaces/L1/ISystemConfig.sol +++ b/packages/contracts-bedrock/interfaces/L1/ISystemConfig.sol @@ -25,7 +25,7 @@ interface ISystemConfig { address gasPayingToken; } - event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); + event ConfigUpdate(uint256 indexed nonceAndVersion, UpdateType indexed updateType, bytes data); event Initialized(uint8 version); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); @@ -48,6 +48,7 @@ interface ISystemConfig { function eip1559Denominator() external view returns (uint32); function eip1559Elasticity() external view returns (uint32); function gasPayingToken() external view returns (address addr_, uint8 decimals_); + function gasPayingTokenAddress() external view returns (address addr_); function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); function initialize( diff --git a/packages/contracts-bedrock/interfaces/L1/ISystemConfigInterop.sol b/packages/contracts-bedrock/interfaces/L1/ISystemConfigInterop.sol index 59950cc35360..68263cd34cfd 100644 --- a/packages/contracts-bedrock/interfaces/L1/ISystemConfigInterop.sol +++ b/packages/contracts-bedrock/interfaces/L1/ISystemConfigInterop.sol @@ -7,7 +7,7 @@ import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; interface ISystemConfigInterop { error CustomGasTokenNotSupported(); - event ConfigUpdate(uint256 indexed version, ISystemConfig.UpdateType indexed updateType, bytes data); + event ConfigUpdate(uint256 indexed nonceAndVersion, ISystemConfig.UpdateType indexed updateType, bytes data); event Initialized(uint8 version); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); @@ -20,7 +20,9 @@ interface ISystemConfigInterop { function OPTIMISM_PORTAL_SLOT() external view returns (bytes32); function START_BLOCK_SLOT() external view returns (bytes32); function UNSAFE_BLOCK_SIGNER_SLOT() external view returns (bytes32); + function CONFIG_UPDATE_NONCE_SLOT() external view returns (bytes32); function VERSION() external view returns (uint256); + function VERSION_1() external view returns (uint256); function basefeeScalar() external view returns (uint32); function batchInbox() external view returns (address addr_); function batcherHash() external view returns (bytes32); @@ -29,7 +31,9 @@ interface ISystemConfigInterop { function gasLimit() external view returns (uint64); function eip1559Denominator() external view returns (uint32); function eip1559Elasticity() external view returns (uint32); + function configUpdateNonce() external view returns (uint64 nonce_); function gasPayingToken() external view returns (address addr_, uint8 decimals_); + function gasPayingTokenAddress() external view returns (address addr_); function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); function isCustomGasToken() external view returns (bool); diff --git a/packages/contracts-bedrock/interfaces/L1/ISystemConfigJovian.sol b/packages/contracts-bedrock/interfaces/L1/ISystemConfigJovian.sol new file mode 100644 index 000000000000..28f5735eaacb --- /dev/null +++ b/packages/contracts-bedrock/interfaces/L1/ISystemConfigJovian.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; + +/// @notice This interface corresponds to the Custom Gas Token version of the SystemConfig contract. +interface ISystemConfigJovian { + error CustomGasTokenNotSupported(); + + event ConfigUpdate(uint256 indexed nonceAndVersion, ISystemConfig.UpdateType indexed updateType, bytes data); + event Initialized(uint8 version); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + function BATCH_INBOX_SLOT() external view returns (bytes32); + function DISPUTE_GAME_FACTORY_SLOT() external view returns (bytes32); + function L1_CROSS_DOMAIN_MESSENGER_SLOT() external view returns (bytes32); + function L1_ERC_721_BRIDGE_SLOT() external view returns (bytes32); + function L1_STANDARD_BRIDGE_SLOT() external view returns (bytes32); + function OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT() external view returns (bytes32); + function OPTIMISM_PORTAL_SLOT() external view returns (bytes32); + function START_BLOCK_SLOT() external view returns (bytes32); + function UNSAFE_BLOCK_SIGNER_SLOT() external view returns (bytes32); + function CONFIG_UPDATE_NONCE_SLOT() external view returns (bytes32); + function VERSION() external view returns (uint256); + function VERSION_1() external view returns (uint256); + function basefeeScalar() external view returns (uint32); + function batchInbox() external view returns (address addr_); + function batcherHash() external view returns (bytes32); + function blobbasefeeScalar() external view returns (uint32); + function disputeGameFactory() external view returns (address addr_); + function gasLimit() external view returns (uint64); + function eip1559Denominator() external view returns (uint32); + function eip1559Elasticity() external view returns (uint32); + function configUpdateNonce() external view returns (uint64 nonce_); + function gasPayingToken() external view returns (address addr_, uint8 decimals_); + function gasPayingTokenAddress() external view returns (address addr_); + function gasPayingTokenName() external view returns (string memory name_); + function gasPayingTokenSymbol() external view returns (string memory symbol_); + function initialize( + address _owner, + uint32 _basefeeScalar, + uint32 _blobbasefeeScalar, + bytes32 _batcherHash, + uint64 _gasLimit, + address _unsafeBlockSigner, + IResourceMetering.ResourceConfig memory _config, + address _batchInbox, + ISystemConfig.Addresses memory _addresses + ) + external; + function isCustomGasToken() external view returns (bool); + function l1CrossDomainMessenger() external view returns (address addr_); + function l1ERC721Bridge() external view returns (address addr_); + function l1StandardBridge() external view returns (address addr_); + function maximumGasLimit() external pure returns (uint64); + function minimumGasLimit() external view returns (uint64); + function optimismMintableERC20Factory() external view returns (address addr_); + function optimismPortal() external view returns (address addr_); + function overhead() external view returns (uint256); + function owner() external view returns (address); + function renounceOwnership() external; + function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory); + function scalar() external view returns (uint256); + function setBatcherHash(bytes32 _batcherHash) external; + function setGasConfig(uint256 _overhead, uint256 _scalar) external; + function setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external; + function setGasLimit(uint64 _gasLimit) external; + function setUnsafeBlockSigner(address _unsafeBlockSigner) external; + function setEIP1559Params(uint32 _denominator, uint32 _elasticity) external; + function startBlock() external view returns (uint256 startBlock_); + function transferOwnership(address newOwner) external; // nosemgrep + function unsafeBlockSigner() external view returns (address addr_); + function version() external pure returns (string memory); + + function __constructor__() external; +} diff --git a/packages/contracts-bedrock/interfaces/L2/IL1Block.sol b/packages/contracts-bedrock/interfaces/L2/IL1Block.sol index a43b3c7c3963..5ebd88aa2359 100644 --- a/packages/contracts-bedrock/interfaces/L2/IL1Block.sol +++ b/packages/contracts-bedrock/interfaces/L2/IL1Block.sol @@ -12,6 +12,8 @@ interface IL1Block { function batcherHash() external view returns (bytes32); function blobBaseFee() external view returns (uint256); function blobBaseFeeScalar() external view returns (uint32); + function depositNonce() external view returns (uint64); + function configUpdateNonce() external view returns (uint64); function gasPayingToken() external view returns (address addr_, uint8 decimals_); function gasPayingTokenName() external view returns (string memory name_); function gasPayingTokenSymbol() external view returns (string memory symbol_); @@ -34,6 +36,7 @@ interface IL1Block { ) external; function setL1BlockValuesEcotone() external; + function setL1BlockValuesJovian() external; function timestamp() external view returns (uint64); function version() external pure returns (string memory); diff --git a/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol b/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol index dd72e3fa6f89..69ee7229a97f 100644 --- a/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol +++ b/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol @@ -25,6 +25,8 @@ interface IL1BlockInterop { function batcherHash() external view returns (bytes32); function blobBaseFee() external view returns (uint256); function blobBaseFeeScalar() external view returns (uint32); + function depositNonce() external view returns (uint64); + function configUpdateNonce() external view returns (uint64); function dependencySetSize() external view returns (uint8); function depositsComplete() external; function gasPayingToken() external view returns (address addr_, uint8 decimals_); @@ -52,6 +54,7 @@ interface IL1BlockInterop { ) external; function setL1BlockValuesEcotone() external; + function setL1BlockValuesJovian() external; function setL1BlockValuesInterop() external; function timestamp() external view returns (uint64); function version() external pure returns (string memory); diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index e5a4e2366e1a..60c45899b09c 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -69,11 +69,12 @@ print-pinned-block-number: # rpc call responses in ~/.foundry/cache/rpc. The default block will need to be updated # when the L1 chain is upgraded. # TODO(opcm upgrades): unskip the "NMC" tests which fail on the forked upgrade path with "UnexpectedRootClaim" errors. +# TODO(isthmus upgrades): unskip the "*Isthmus*" tests once mainnet L1 is upgraded, post Isthmus. test-upgrade *ARGS: build-go-ffi #!/bin/bash echo "Running upgrade tests at block $pinnedBlockNumber" export FORK_BLOCK_NUMBER=$pinnedBlockNumber - export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|AnchorStateRegistry_Initialize_Test|AnchorStateRegistry_TryUpdateAnchorState_Test|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test" + export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|AnchorStateRegistry_Initialize_Test|AnchorStateRegistry_TryUpdateAnchorState_Test|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test|OptimismPortalJovian_Test|SystemConfigJovian_Test|SystemConfigJovian_Setters_Test" FORK_RPC_URL=$ETH_RPC_URL \ FORK_TEST=true \ forge test --match-path "test/{L1,dispute}/**" \ diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 98edde181cc7..eb4c74ffee62 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -182,9 +182,14 @@ contract L2Genesis is Deployer { if (writeForkGenesisAllocs(_fork, Fork.HOLOCENE, _mode)) { return; } + if (writeForkGenesisAllocs(_fork, Fork.ISTHMUS, _mode)) { return; } + + if (writeForkGenesisAllocs(_fork, Fork.JOVIAN, _mode)) { + return; + } } function writeForkGenesisAllocs(Fork _latest, Fork _current, OutputMode _mode) internal returns (bool isLatest_) { diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 62ef74cbab48..c051e77baa71 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -20,6 +20,7 @@ import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from import { DeployImplementationsInput, DeployImplementations, + DeployImplementationsJovian, DeployImplementationsInterop, DeployImplementationsOutput } from "scripts/deploy/DeployImplementations.s.sol"; @@ -200,7 +201,7 @@ contract Deploy is Deployer { deploySuperchain(); } - deployImplementations({ _isInterop: cfg.useInterop() }); + deployImplementations(); // Deploy Current OPChain Contracts deployOpChain(); @@ -275,10 +276,7 @@ contract Deploy is Deployer { } /// @notice Deploy all of the implementations - /// @param _isInterop Whether to use interop - function deployImplementations(bool _isInterop) public { - require(_isInterop == cfg.useInterop(), "Deploy: Interop setting mismatch."); - + function deployImplementations() public { console.log("Deploying implementations"); DeployImplementations di = new DeployImplementations(); @@ -299,8 +297,10 @@ contract Deploy is Deployer { dii.set(dii.protocolVersionsProxy.selector, artifacts.mustGetAddress("ProtocolVersionsProxy")); dii.set(dii.salt.selector, _implSalt()); - if (_isInterop) { + if (cfg.useInterop()) { di = DeployImplementations(new DeployImplementationsInterop()); + } else if (cfg.l2GenesisJovianTimeOffset() == 0) { + di = DeployImplementations(new DeployImplementationsJovian()); } di.run(dii, dio); @@ -353,7 +353,7 @@ contract Deploy is Deployer { _opcm: OPContractsManager(address(dio.opcm())), _mips: IMIPS(address(dio.mipsSingleton())) }); - if (_isInterop) { + if (cfg.useInterop()) { ChainAssertions.checkSystemConfigInterop({ _contracts: contracts, _cfg: cfg, _isProxy: false }); } else { ChainAssertions.checkSystemConfig({ _contracts: contracts, _cfg: cfg, _isProxy: false }); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index cf8e4d38739b..a9aeb752b418 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -30,6 +30,8 @@ contract DeployConfig is Script { uint256 public l2GenesisFjordTimeOffset; uint256 public l2GenesisGraniteTimeOffset; uint256 public l2GenesisHoloceneTimeOffset; + uint256 public l2GenesisIsthmusTimeOffset; + uint256 public l2GenesisJovianTimeOffset; uint256 public maxSequencerDrift; uint256 public sequencerWindowSize; uint256 public channelTimeout; @@ -109,6 +111,8 @@ contract DeployConfig is Script { l2GenesisFjordTimeOffset = _readOr(_json, "$.l2GenesisFjordTimeOffset", NULL_OFFSET); l2GenesisGraniteTimeOffset = _readOr(_json, "$.l2GenesisGraniteTimeOffset", NULL_OFFSET); l2GenesisHoloceneTimeOffset = _readOr(_json, "$.l2GenesisHoloceneTimeOffset", NULL_OFFSET); + l2GenesisIsthmusTimeOffset = _readOr(_json, "$.l2GenesisIsthmusTimeOffset", NULL_OFFSET); + l2GenesisJovianTimeOffset = _readOr(_json, "$.l2GenesisJovianTimeOffset", NULL_OFFSET); maxSequencerDrift = stdJson.readUint(_json, "$.maxSequencerDrift"); sequencerWindowSize = stdJson.readUint(_json, "$.sequencerWindowSize"); @@ -225,6 +229,11 @@ contract DeployConfig is Script { useInterop = _useInterop; } + /// @notice Allow the `l2GenesisJovianTimeOffset` to be overridden in testing environments + function setL2GenesisJovianTimeOffset(uint256 _l2GenesisJovianTimeOffset) public { + l2GenesisJovianTimeOffset = _l2GenesisJovianTimeOffset; + } + /// @notice Allow the `fundDevAccounts` config to be overridden. function setFundDevAccounts(bool _fundDevAccounts) public { fundDevAccounts = _fundDevAccounts; diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index 9fd8f025bd3e..0c9ddd857ec0 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -22,6 +22,9 @@ import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "interfaces/L1/IL1StandardBridge.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; +import { IOptimismPortalJovian } from "interfaces/L1/IOptimismPortalJovian.sol"; +import { ISystemConfigJovian } from "interfaces/L1/ISystemConfigJovian.sol"; + import { OPContractsManagerInterop } from "src/L1/OPContractsManagerInterop.sol"; import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { ISystemConfigInterop } from "interfaces/L1/ISystemConfigInterop.sol"; @@ -895,6 +898,89 @@ contract DeployImplementations is Script { } } +// Using the base scripts and contracts (DeploySuperchain, DeployImplementations, DeployOPChain, and +// the corresponding OPContractsManager) deploys a standard chain. For nonstandard and in-development +// features we need to modify some or all of those contracts, and we do that via inheritance. Using +// Jovian as an example, we've made the following changes to L1 contracts: +// - `OptimismPortalJovian is OptimismPortal`: A different portal implementation is used, and +// it has an additional method for retrieving the TransactionDeposited nonce. +// - `SystemConfigJovian is SystemConfig`: A different system config implementation is used, and +// it has an additional method for retrieving the ConfigUpdate nonce. +// +// Similar to how inheritance was used to develop the new portal and system config contracts, we use +// inheritance to modify up to all of the deployer contracts. For this Jovian example, what this +// means is we need: +// - A `DeployImplementationsJovian is DeployImplementations` that: +// - Deploys OptimismPortalJovian instead of OptimismPortal. +// - Deploys SystemConfigJovian instead of SystemConfig. +contract DeployImplementationsJovian is DeployImplementations { + function deployOptimismPortalImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + override + { + string memory release = _dii.l1ContractsRelease(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "optimism_portal"; + IOptimismPortalJovian impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = IOptimismPortalJovian(payable(existingImplementation)); + } else { + uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); + uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); + vm.broadcast(msg.sender); + impl = IOptimismPortalJovian( + DeployUtils.create1({ + _name: "OptimismPortalJovian", + _args: DeployUtils.encodeConstructor( + abi.encodeCall( + IOptimismPortalJovian.__constructor__, + (proofMaturityDelaySeconds, disputeGameFinalityDelaySeconds) + ) + ) + }) + ); + } + + vm.label(address(impl), "OptimismPortalImpl"); + _dio.set(_dio.optimismPortalImpl.selector, address(impl)); + } + + function deploySystemConfigImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + override + { + string memory release = _dii.l1ContractsRelease(); + string memory stdVerToml = _dii.standardVersionsToml(); + + string memory contractName = "system_config"; + ISystemConfigJovian impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = ISystemConfigJovian(existingImplementation); + } else { + vm.broadcast(msg.sender); + impl = ISystemConfigJovian( + DeployUtils.create1({ + _name: "SystemConfigJovian", + _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfigJovian.__constructor__, ())) + }) + ); + } + + vm.label(address(impl), "SystemConfigImpl"); + _dio.set(_dio.systemConfigImpl.selector, address(impl)); + } +} + // Similar to how DeploySuperchain.s.sol contains a lot of comments to thoroughly document the script // architecture, this comment block documents how to update the deploy scripts to support new features. // diff --git a/packages/contracts-bedrock/scripts/libraries/Config.sol b/packages/contracts-bedrock/scripts/libraries/Config.sol index d00043e0e92e..c4c893699db6 100644 --- a/packages/contracts-bedrock/scripts/libraries/Config.sol +++ b/packages/contracts-bedrock/scripts/libraries/Config.sol @@ -35,10 +35,11 @@ enum Fork { FJORD, GRANITE, HOLOCENE, - ISTHMUS + ISTHMUS, + JOVIAN } -Fork constant LATEST_FORK = Fork.ISTHMUS; +Fork constant LATEST_FORK = Fork.JOVIAN; library ForkUtils { function toString(Fork _fork) internal pure returns (string memory) { @@ -56,6 +57,8 @@ library ForkUtils { return "holocene"; } else if (_fork == Fork.ISTHMUS) { return "isthmus"; + } else if (_fork == Fork.JOVIAN) { + return "jovian"; } else { return "unknown"; } @@ -166,6 +169,8 @@ library Config { return Fork.HOLOCENE; } else if (forkHash == keccak256(bytes("isthmus"))) { return Fork.ISTHMUS; + } else if (forkHash == keccak256(bytes("jovian"))) { + return Fork.JOVIAN; } else { revert(string.concat("Config: unknown fork: ", forkStr)); } diff --git a/packages/contracts-bedrock/snapshots/.gas-snapshot b/packages/contracts-bedrock/snapshots/.gas-snapshot index f4258caa9d69..d63d1b2d6d9f 100644 --- a/packages/contracts-bedrock/snapshots/.gas-snapshot +++ b/packages/contracts-bedrock/snapshots/.gas-snapshot @@ -1,13 +1,13 @@ -GasBenchMark_L1BlockInterop_DepositsComplete:test_depositsComplete_benchmark() (gas: 7567) -GasBenchMark_L1BlockInterop_DepositsComplete_Warm:test_depositsComplete_benchmark() (gas: 5567) -GasBenchMark_L1BlockInterop_SetValuesInterop:test_setL1BlockValuesInterop_benchmark() (gas: 175722) +GasBenchMark_L1BlockInterop_DepositsComplete:test_depositsComplete_benchmark() (gas: 7611) +GasBenchMark_L1BlockInterop_DepositsComplete_Warm:test_depositsComplete_benchmark() (gas: 5611) +GasBenchMark_L1BlockInterop_SetValuesInterop:test_setL1BlockValuesInterop_benchmark() (gas: 197772) GasBenchMark_L1BlockInterop_SetValuesInterop_Warm:test_setL1BlockValuesInterop_benchmark() (gas: 5144) -GasBenchMark_L1Block_SetValuesEcotone:test_setL1BlockValuesEcotone_benchmark() (gas: 158531) -GasBenchMark_L1Block_SetValuesEcotone_Warm:test_setL1BlockValuesEcotone_benchmark() (gas: 7597) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369264) -GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967493) -GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564413) -GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076606) -GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 467092) -GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512819) -GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72667) \ No newline at end of file +GasBenchMark_L1Block_SetValuesEcotone:test_setL1BlockValuesEcotone_benchmark() (gas: 158485) +GasBenchMark_L1Block_SetValuesEcotone_Warm:test_setL1BlockValuesEcotone_benchmark() (gas: 7619) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369219) +GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967470) +GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564368) +GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076561) +GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 467003) +GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512730) +GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72579) \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1Block.json b/packages/contracts-bedrock/snapshots/abi/L1Block.json index 020c9e942c75..28fe286627bb 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1Block.json +++ b/packages/contracts-bedrock/snapshots/abi/L1Block.json @@ -77,6 +77,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "configUpdateNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "depositNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "gasPayingToken", @@ -282,6 +308,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "setL1BlockValuesJovian", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "timestamp", diff --git a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json index ab089f0cec55..86955d6c03ed 100644 --- a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json @@ -77,6 +77,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "configUpdateNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "dependencySetSize", @@ -90,6 +103,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "depositNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "depositsComplete", @@ -359,6 +385,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "setL1BlockValuesJovian", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "timestamp", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index 99f31298fe63..428e9840aed4 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -745,7 +745,7 @@ { "indexed": true, "internalType": "uint256", - "name": "version", + "name": "nonceAndVersion", "type": "uint256" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json index 1509831f4f18..e64ae5358ada 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json @@ -19,6 +19,19 @@ "stateMutability": "payable", "type": "receive" }, + { + "inputs": [], + "name": "DEPOSIT_NONCE_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "balance", @@ -101,6 +114,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "depositNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "nonce_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -763,7 +789,7 @@ { "indexed": true, "internalType": "uint256", - "name": "version", + "name": "nonceAndVersion", "type": "uint256" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortalJovian.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortalJovian.json new file mode 100644 index 000000000000..82b750fd23cc --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortalJovian.json @@ -0,0 +1,980 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_proofMaturityDelaySeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_disputeGameFinalityDelaySeconds", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "stateMutability": "payable", + "type": "receive" + }, + { + "inputs": [], + "name": "DEPOSIT_NONCE_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "balance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDisputeGame", + "name": "_disputeGame", + "type": "address" + } + ], + "name": "blacklistDisputeGame", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_withdrawalHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_proofSubmitter", + "type": "address" + } + ], + "name": "checkWithdrawal", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_mint", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "_gasLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "_isCreation", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "depositERC20Transaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "nonce_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "_gasLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "_isCreation", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "depositTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDisputeGame", + "name": "", + "type": "address" + } + ], + "name": "disputeGameBlacklist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disputeGameFactory", + "outputs": [ + { + "internalType": "contract IDisputeGameFactory", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disputeGameFinalityDelaySeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "donateETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct Types.WithdrawalTransaction", + "name": "_tx", + "type": "tuple" + } + ], + "name": "finalizeWithdrawalTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct Types.WithdrawalTransaction", + "name": "_tx", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_proofSubmitter", + "type": "address" + } + ], + "name": "finalizeWithdrawalTransactionExternalProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "finalizedWithdrawals", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "guardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDisputeGameFactory", + "name": "_disputeGameFactory", + "type": "address" + }, + { + "internalType": "contract ISystemConfig", + "name": "_systemConfig", + "type": "address" + }, + { + "internalType": "contract ISuperchainConfig", + "name": "_superchainConfig", + "type": "address" + }, + { + "internalType": "GameType", + "name": "_initialRespectedGameType", + "type": "uint32" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l2Sender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_byteCount", + "type": "uint64" + } + ], + "name": "minimumGasLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_withdrawalHash", + "type": "bytes32" + } + ], + "name": "numProofSubmitters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "params", + "outputs": [ + { + "internalType": "uint128", + "name": "prevBaseFee", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "prevBoughtGas", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "prevBlockNum", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofMaturityDelaySeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "proofSubmitters", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct Types.WithdrawalTransaction", + "name": "_tx", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "_disputeGameIndex", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "version", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "messagePasserStorageRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "latestBlockhash", + "type": "bytes32" + } + ], + "internalType": "struct Types.OutputRootProof", + "name": "_outputRootProof", + "type": "tuple" + }, + { + "internalType": "bytes[]", + "name": "_withdrawalProof", + "type": "bytes[]" + } + ], + "name": "proveWithdrawalTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "provenWithdrawals", + "outputs": [ + { + "internalType": "contract IDisputeGame", + "name": "disputeGameProxy", + "type": "address" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "respectedGameType", + "outputs": [ + { + "internalType": "GameType", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "respectedGameTypeUpdatedAt", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_symbol", + "type": "bytes32" + } + ], + "name": "setGasPayingToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "GameType", + "name": "_gameType", + "type": "uint32" + } + ], + "name": "setRespectedGameType", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "superchainConfig", + "outputs": [ + { + "internalType": "contract ISuperchainConfig", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "systemConfig", + "outputs": [ + { + "internalType": "contract ISystemConfig", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IDisputeGame", + "name": "disputeGame", + "type": "address" + } + ], + "name": "DisputeGameBlacklisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "GameType", + "name": "newGameType", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "Timestamp", + "name": "updatedAt", + "type": "uint64" + } + ], + "name": "RespectedGameTypeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "nonceAndVersion", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "opaqueData", + "type": "bytes" + } + ], + "name": "TransactionDeposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "withdrawalHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "name": "WithdrawalFinalized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "withdrawalHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "WithdrawalProven", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "withdrawalHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "proofSubmitter", + "type": "address" + } + ], + "name": "WithdrawalProvenExtension1", + "type": "event" + }, + { + "inputs": [], + "name": "AlreadyFinalized", + "type": "error" + }, + { + "inputs": [], + "name": "BadTarget", + "type": "error" + }, + { + "inputs": [], + "name": "Blacklisted", + "type": "error" + }, + { + "inputs": [], + "name": "CallPaused", + "type": "error" + }, + { + "inputs": [], + "name": "ContentLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "CustomGasTokenNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyItem", + "type": "error" + }, + { + "inputs": [], + "name": "GasEstimation", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataRemainder", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDisputeGame", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidGameType", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidHeader", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidMerkleProof", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProof", + "type": "error" + }, + { + "inputs": [], + "name": "LargeCalldata", + "type": "error" + }, + { + "inputs": [], + "name": "NoValue", + "type": "error" + }, + { + "inputs": [], + "name": "NonReentrant", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCustomGasToken", + "type": "error" + }, + { + "inputs": [], + "name": "OutOfGas", + "type": "error" + }, + { + "inputs": [], + "name": "ProposalNotValidated", + "type": "error" + }, + { + "inputs": [], + "name": "SmallGasLimit", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "UnexpectedList", + "type": "error" + }, + { + "inputs": [], + "name": "UnexpectedString", + "type": "error" + }, + { + "inputs": [], + "name": "Unproven", + "type": "error" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfig.json b/packages/contracts-bedrock/snapshots/abi/SystemConfig.json index eaa8cb1ecbfd..ef23db5cc4f7 100644 --- a/packages/contracts-bedrock/snapshots/abi/SystemConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfig.json @@ -256,6 +256,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "gasPayingTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "gasPayingTokenName", @@ -750,7 +763,7 @@ { "indexed": true, "internalType": "uint256", - "name": "version", + "name": "nonceAndVersion", "type": "uint256" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json b/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json index c7dae3c2b8e4..cf3f30f88e89 100644 --- a/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfigInterop.json @@ -12,6 +12,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "CONFIG_UPDATE_NONCE_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "DISPUTE_GAME_FACTORY_SLOT", @@ -129,6 +142,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "VERSION_1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -194,6 +220,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "configUpdateNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "nonce_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "dependencyManager", @@ -277,6 +316,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "gasPayingTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "gasPayingTokenName", @@ -911,7 +963,7 @@ { "indexed": true, "internalType": "uint256", - "name": "version", + "name": "nonceAndVersion", "type": "uint256" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/SystemConfigJovian.json b/packages/contracts-bedrock/snapshots/abi/SystemConfigJovian.json new file mode 100644 index 000000000000..72cec28408da --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/SystemConfigJovian.json @@ -0,0 +1,856 @@ +[ + { + "inputs": [], + "name": "BATCH_INBOX_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CONFIG_UPDATE_NONCE_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DISPUTE_GAME_FACTORY_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L1_CROSS_DOMAIN_MESSENGER_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L1_ERC_721_BRIDGE_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L1_STANDARD_BRIDGE_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_PORTAL_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "START_BLOCK_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UNSAFE_BLOCK_SIGNER_SLOT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VERSION_1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "basefeeScalar", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchInbox", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batcherHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "blobbasefeeScalar", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "configUpdateNonce", + "outputs": [ + { + "internalType": "uint64", + "name": "nonce_", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disputeGameFactory", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eip1559Denominator", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eip1559Elasticity", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasPayingToken", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasPayingTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasPayingTokenName", + "outputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasPayingTokenSymbol", + "outputs": [ + { + "internalType": "string", + "name": "symbol_", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint32", + "name": "_basefeeScalar", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_blobbasefeeScalar", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "_batcherHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "_gasLimit", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_unsafeBlockSigner", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxResourceLimit", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "elasticityMultiplier", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "baseFeeMaxChangeDenominator", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "minimumBaseFee", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "systemTxMaxGas", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "maximumBaseFee", + "type": "uint128" + } + ], + "internalType": "struct IResourceMetering.ResourceConfig", + "name": "_config", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_batchInbox", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "l1CrossDomainMessenger", + "type": "address" + }, + { + "internalType": "address", + "name": "l1ERC721Bridge", + "type": "address" + }, + { + "internalType": "address", + "name": "l1StandardBridge", + "type": "address" + }, + { + "internalType": "address", + "name": "disputeGameFactory", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismPortal", + "type": "address" + }, + { + "internalType": "address", + "name": "optimismMintableERC20Factory", + "type": "address" + }, + { + "internalType": "address", + "name": "gasPayingToken", + "type": "address" + } + ], + "internalType": "struct SystemConfig.Addresses", + "name": "_addresses", + "type": "tuple" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isCustomGasToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1CrossDomainMessenger", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1ERC721Bridge", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1StandardBridge", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maximumGasLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "minimumGasLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "optimismMintableERC20Factory", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "optimismPortal", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "overhead", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resourceConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "maxResourceLimit", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "elasticityMultiplier", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "baseFeeMaxChangeDenominator", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "minimumBaseFee", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "systemTxMaxGas", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "maximumBaseFee", + "type": "uint128" + } + ], + "internalType": "struct IResourceMetering.ResourceConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "scalar", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_batcherHash", + "type": "bytes32" + } + ], + "name": "setBatcherHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_denominator", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_elasticity", + "type": "uint32" + } + ], + "name": "setEIP1559Params", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_overhead", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_scalar", + "type": "uint256" + } + ], + "name": "setGasConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_basefeeScalar", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_blobbasefeeScalar", + "type": "uint32" + } + ], + "name": "setGasConfigEcotone", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_gasLimit", + "type": "uint64" + } + ], + "name": "setGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_unsafeBlockSigner", + "type": "address" + } + ], + "name": "setUnsafeBlockSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "startBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "startBlock_", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unsafeBlockSigner", + "outputs": [ + { + "internalType": "address", + "name": "addr_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "nonceAndVersion", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "enum SystemConfig.UpdateType", + "name": "updateType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ConfigUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "CustomGasTokenNotSupported", + "type": "error" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 81d746fa6776..e229dede1d8c 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -20,12 +20,16 @@ "sourceCodeHash": "0xe0f5413e0a0a335016d773f02ef6bd25551416635981e83b7a4da601b9b65bc4" }, "src/L1/OptimismPortal2.sol": { - "initCodeHash": "0x7e533474310583593c2d57d30fcd1ec11e1568dbaaf37a2dd28c5cc574068bac", - "sourceCodeHash": "0xe67f0c01c9c9ba67d279304f9db84eebeb2e93d9f2728fea95c7a194f0ae338a" + "initCodeHash": "0xed1e7bfce7c8d19167427e7f0e64bd9a06f2472e02692346cbb770fdd6f57b9e", + "sourceCodeHash": "0xc53b468455c2e6f4cab1ffd46dab1d10494cf112ae2f4432bacd21d771ea1ed1" }, "src/L1/OptimismPortalInterop.sol": { - "initCodeHash": "0x917b3b31a149b5aab96539208f4810d207875f6f2563a45c50ed13a7940066c0", - "sourceCodeHash": "0xae2fbe02c0f8685692babeed0252ae8a624dc6d3bfb082fc3807d7b84869004b" + "initCodeHash": "0x6496f675ecb21bbbc3a0a153e8f8ea421426ac0e6b85a12f4c180137eddfe57b", + "sourceCodeHash": "0xd10c5d59d03d4e36a3be0ae42ea71f188917b45aa1f6139c288e2644911dc6b5" + }, + "src/L1/OptimismPortalJovian.sol": { + "initCodeHash": "0xe9320dba29d18198f8456a4b74e6a6b7a0e72efff6590de5d6c7b6facf46b6ab", + "sourceCodeHash": "0x9fd9c247a1c988428ec39cfc8843dac1afd12f4e6c2639278a2c50299dde5357" }, "src/L1/ProtocolVersions.sol": { "initCodeHash": "0x0000ec89712d8b4609873f1ba76afffd4205bf9110818995c90134dbec12e91e", @@ -36,12 +40,16 @@ "sourceCodeHash": "0xafa784ea78818a382ff3a61e2d84be58c7978110c06b9273db68c0213ead02d3" }, "src/L1/SystemConfig.sol": { - "initCodeHash": "0xbb18eef17cdc1d0d307b0241e818820063e3ce3c7021ea3bb3a85ff6e79659e1", - "sourceCodeHash": "0x5d6a9ef41fed54479f742345368e693ec1fcabdb60118081c03fe9da3a5d27ed" + "initCodeHash": "0xd081fe793600dc44c9b60365368774929e8fe2a4194c991ddc6c42c8bc925d44", + "sourceCodeHash": "0xc0f84262fcfcf962fce6f023d434f92feafbf4b3a4463e8491d19de3415d3aea" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0x0d61e2a95122417e9bf074a8fc0bce6f2b03268985d09580a83099dc9016c72d", - "sourceCodeHash": "0x673ec83b89680b44945af89229b286f26df07a5938d497df5def47872d331cf3" + "initCodeHash": "0xd9520478fe88af6a8f04965a00c0f61283efc07447097b038581672b1ca0d369", + "sourceCodeHash": "0xc8dc21fd6d2a1e8745d160c781033f267a5663adf9ae3bde6a8c77ebc976cdd9" + }, + "src/L1/SystemConfigJovian.sol": { + "initCodeHash": "0xc56c85988424683a7d5240c2636a6064ab3cc7829a087743f32debb7c36caed3", + "sourceCodeHash": "0xb333712a798272cc836acca9e4e02235b3aec0b52d5c66831804bf551d0ef043" }, "src/L2/BaseFeeVault.sol": { "initCodeHash": "0xc403d4c555d8e69a2699e01d192ae7327136701fa02da10a6d75a584b3c364c9", @@ -60,12 +68,12 @@ "sourceCodeHash": "0x305c72d7be9149fce7095bd4641a1a19acada3126fbc43599f674cadbf6e7d6c" }, "src/L2/L1Block.sol": { - "initCodeHash": "0x22f9b9277e33dc27df8366c2dd6e8340d294947b57116db35c6d14c41225633f", - "sourceCodeHash": "0xffb6cf768097b2d6cb6ecb2d6463c176af9acd70415aa0d2e4f017758f737eee" + "initCodeHash": "0xd805575182901b29a2ef1f0b98f3870b49737a838b8f1a6816785c3f10b1a437", + "sourceCodeHash": "0x2e1d6ecf1dd483366e92dc4865c5b2042da7a8cf4ffdbc04c6f7d9adaef6f8cd" }, "src/L2/L1BlockInterop.sol": { - "initCodeHash": "0x67e99306d9a09cac587f65cfa2c0de55da9eca184fd1fc3f4b885d2c47114483", - "sourceCodeHash": "0x9493f90136917fc95d2ac942f061c1b9cffeff6d327afb46fe4e69784e7f2100" + "initCodeHash": "0x07e2bdbb1e0ba44486cecb2b2e9d4e5bfcb4a604a0d485f60cc4791955d65aa4", + "sourceCodeHash": "0x76183bbb4c04730c62f6d4340ceb9b1b2a3574d1a5d2bd28945c15e9288c0aa2" }, "src/L2/L1FeeVault.sol": { "initCodeHash": "0x6745b7be3895a5e8d373df0066d931bae29c47672ac46c2f5829bd0052cc6d9e", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json b/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json index 2928d2147b5c..82eac2b0f225 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1Block.json @@ -75,5 +75,19 @@ "offset": 0, "slot": "7", "type": "uint256" + }, + { + "bytes": "8", + "label": "depositNonce", + "offset": 0, + "slot": "8", + "type": "uint64" + }, + { + "bytes": "8", + "label": "configUpdateNonce", + "offset": 8, + "slot": "8", + "type": "uint64" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json index 14ee2ff9609a..8af802ff2712 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json @@ -76,11 +76,25 @@ "slot": "7", "type": "uint256" }, + { + "bytes": "8", + "label": "depositNonce", + "offset": 0, + "slot": "8", + "type": "uint64" + }, + { + "bytes": "8", + "label": "configUpdateNonce", + "offset": 8, + "slot": "8", + "type": "uint64" + }, { "bytes": "64", "label": "dependencySet", "offset": 0, - "slot": "8", + "slot": "9", "type": "struct EnumerableSet.UintSet" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortalJovian.json b/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortalJovian.json new file mode 100644 index 000000000000..0fdd65b3e88f --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortalJovian.json @@ -0,0 +1,128 @@ +[ + { + "bytes": "1", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "32", + "label": "params", + "offset": 0, + "slot": "1", + "type": "struct ResourceMetering.ResourceParams" + }, + { + "bytes": "1536", + "label": "__gap", + "offset": 0, + "slot": "2", + "type": "uint256[48]" + }, + { + "bytes": "20", + "label": "l2Sender", + "offset": 0, + "slot": "50", + "type": "address" + }, + { + "bytes": "32", + "label": "finalizedWithdrawals", + "offset": 0, + "slot": "51", + "type": "mapping(bytes32 => bool)" + }, + { + "bytes": "32", + "label": "spacer_52_0_32", + "offset": 0, + "slot": "52", + "type": "bytes32" + }, + { + "bytes": "1", + "label": "spacer_53_0_1", + "offset": 0, + "slot": "53", + "type": "bool" + }, + { + "bytes": "20", + "label": "superchainConfig", + "offset": 1, + "slot": "53", + "type": "contract ISuperchainConfig" + }, + { + "bytes": "20", + "label": "spacer_54_0_20", + "offset": 0, + "slot": "54", + "type": "address" + }, + { + "bytes": "20", + "label": "systemConfig", + "offset": 0, + "slot": "55", + "type": "contract ISystemConfig" + }, + { + "bytes": "20", + "label": "disputeGameFactory", + "offset": 0, + "slot": "56", + "type": "contract IDisputeGameFactory" + }, + { + "bytes": "32", + "label": "provenWithdrawals", + "offset": 0, + "slot": "57", + "type": "mapping(bytes32 => mapping(address => struct OptimismPortal2.ProvenWithdrawal))" + }, + { + "bytes": "32", + "label": "disputeGameBlacklist", + "offset": 0, + "slot": "58", + "type": "mapping(contract IDisputeGame => bool)" + }, + { + "bytes": "4", + "label": "respectedGameType", + "offset": 0, + "slot": "59", + "type": "GameType" + }, + { + "bytes": "8", + "label": "respectedGameTypeUpdatedAt", + "offset": 4, + "slot": "59", + "type": "uint64" + }, + { + "bytes": "32", + "label": "proofSubmitters", + "offset": 0, + "slot": "60", + "type": "mapping(bytes32 => address[])" + }, + { + "bytes": "32", + "label": "_balance", + "offset": 0, + "slot": "61", + "type": "uint256" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigJovian.json b/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigJovian.json new file mode 100644 index 000000000000..a6184a1f10dd --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigJovian.json @@ -0,0 +1,100 @@ +[ + { + "bytes": "1", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "1600", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "uint256[50]" + }, + { + "bytes": "20", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "address" + }, + { + "bytes": "1568", + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "uint256[49]" + }, + { + "bytes": "32", + "label": "overhead", + "offset": 0, + "slot": "101", + "type": "uint256" + }, + { + "bytes": "32", + "label": "scalar", + "offset": 0, + "slot": "102", + "type": "uint256" + }, + { + "bytes": "32", + "label": "batcherHash", + "offset": 0, + "slot": "103", + "type": "bytes32" + }, + { + "bytes": "8", + "label": "gasLimit", + "offset": 0, + "slot": "104", + "type": "uint64" + }, + { + "bytes": "4", + "label": "basefeeScalar", + "offset": 8, + "slot": "104", + "type": "uint32" + }, + { + "bytes": "4", + "label": "blobbasefeeScalar", + "offset": 12, + "slot": "104", + "type": "uint32" + }, + { + "bytes": "32", + "label": "_resourceConfig", + "offset": 0, + "slot": "105", + "type": "struct IResourceMetering.ResourceConfig" + }, + { + "bytes": "4", + "label": "eip1559Denominator", + "offset": 0, + "slot": "106", + "type": "uint32" + }, + { + "bytes": "4", + "label": "eip1559Elasticity", + "offset": 4, + "slot": "106", + "type": "uint32" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 7447a4a3592f..f00d9b1ab0a4 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -147,11 +147,13 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { /// @notice Emitted when a transaction is deposited from L1 to L2. /// The parameters of this event are read by the rollup node and used to derive deposit /// transactions on L2. - /// @param from Address that triggered the deposit transaction. - /// @param to Address that the deposit transaction is directed to. - /// @param version Version of this deposit transaction event. - /// @param opaqueData ABI encoded deposit data to be parsed off-chain. - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); + /// @param from Address that triggered the deposit transaction. + /// @param to Address that the deposit transaction is directed to. + /// @param nonceAndVersion Nonce (first 128-bits) and version (second 128-bits). + /// @param opaqueData ABI encoded deposit data to be parsed off-chain. + event TransactionDeposited( + address indexed from, address indexed to, uint256 indexed nonceAndVersion, bytes opaqueData + ); /// @notice Emitted when a withdrawal transaction is proven. /// @param withdrawalHash Hash of the withdrawal transaction. @@ -186,9 +188,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { } /// @notice Semantic version. - /// @custom:semver 3.11.0-beta.10 + /// @custom:semver 3.11.0-beta.11 function version() public pure virtual returns (string memory) { - return "3.11.0-beta.10"; + return "3.11.0-beta.11"; } /// @notice Constructs the OptimismPortal contract. @@ -234,8 +236,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { /// @notice Getter for the balance of the contract. function balance() public view returns (uint256) { - (address token,) = gasPayingToken(); - if (token == Constants.ETHER) { + if (gasPayingToken() == Constants.ETHER) { return address(this).balance; } else { // Temporary revert till we support custom gas tokens @@ -295,8 +296,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { } /// @notice Returns the gas paying token and its decimals. - function gasPayingToken() internal view returns (address addr_, uint8 decimals_) { - (addr_, decimals_) = systemConfig.gasPayingToken(); + function gasPayingToken() internal view returns (address addr_) { + addr_ = systemConfig.gasPayingTokenAddress(); } /// @notice Getter for the resource config. @@ -361,12 +362,12 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { // bugs, then we know that this withdrawal was actually triggered on L2 and can therefore // be relayed on L1. if ( - SecureMerkleTrie.verifyInclusionProof({ + !SecureMerkleTrie.verifyInclusionProof({ _key: abi.encode(storageKey), _value: hex"01", _proof: _withdrawalProof, _root: _outputRootProof.messagePasserStorageRoot - }) == false + }) ) revert InvalidMerkleProof(); // Designate the withdrawalHash as proven by storing the `disputeGameProxy` & `timestamp` in the @@ -418,7 +419,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { l2Sender = _tx.sender; bool success; - (address token,) = gasPayingToken(); + address token = gasPayingToken(); if (token == Constants.ETHER) { // Trigger the call to the target contract. We use a custom low level method // SafeCall.callWithMinGas to ensure two key properties @@ -505,7 +506,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { if (true) revert CustomGasTokenNotSupported(); // Can only be called if an ERC20 token is used for gas paying on L2 - (address token,) = gasPayingToken(); + address token = gasPayingToken(); if (token == Constants.ETHER) revert OnlyCustomGasToken(); // Gives overflow protection for L2 account balances. @@ -552,7 +553,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { payable metered(_gasLimit) { - (address token,) = gasPayingToken(); + address token = gasPayingToken(); // Temporary revert till we support custom gas tokens if (token != Constants.ETHER) revert CustomGasTokenNotSupported(); @@ -602,18 +603,16 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { // Transform the from-address to its alias if the caller is a contract. address from = msg.sender; - if (msg.sender != tx.origin) { - from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); + if (from != tx.origin) { + from = AddressAliasHelper.applyL1ToL2Alias(from); } // Compute the opaque data that will be emitted as part of the TransactionDeposited event. // We use opaque data so that we can update the TransactionDeposited event in the future // without breaking the current interface. - bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data); - // Emit a TransactionDeposited event so that the rollup node can derive a deposit // transaction for this deposit. - emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); + _emitTransactionDeposited(from, _to, abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data)); } /// @notice Sets the gas paying token for the L2 system. This token is used as the @@ -630,10 +629,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { // Emit the special deposit transaction directly that sets the gas paying // token in the L1Block predeploy contract. - emit TransactionDeposited( + _emitTransactionDeposited( Constants.DEPOSITOR_ACCOUNT, Predeploys.L1_BLOCK_ATTRIBUTES, - DEPOSIT_VERSION, abi.encodePacked( uint256(0), // mint uint256(0), // value @@ -644,6 +642,14 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { ); } + function _emitTransactionDeposited(address _from, address _to, bytes memory _opaqueData) internal { + emit TransactionDeposited(_from, _to, _transactionDepositedNonceAndVersion(), _opaqueData); + } + + function _transactionDepositedNonceAndVersion() internal virtual returns (uint256) { + return DEPOSIT_VERSION; + } + /// @notice Blacklists a dispute game. Should only be used in the event that a dispute game resolves incorrectly. /// @param _disputeGame Dispute game to blacklist. function blacklistDisputeGame(IDisputeGame _disputeGame) external { diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol index 811c7136191e..a6ed03962b4c 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Contracts -import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; +import { OptimismPortalJovian } from "src/L1/OptimismPortalJovian.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -17,17 +17,17 @@ import { IL1BlockInterop, ConfigType } from "interfaces/L2/IL1BlockInterop.sol"; /// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1 /// and L2. Messages sent directly to the OptimismPortal have no form of replayability. /// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. -contract OptimismPortalInterop is OptimismPortal2 { +contract OptimismPortalInterop is OptimismPortalJovian { constructor( uint256 _proofMaturityDelaySeconds, uint256 _disputeGameFinalityDelaySeconds ) - OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds) + OptimismPortalJovian(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds) { } - /// @custom:semver +interop-beta.7 + /// @custom:semver +interop-beta.8 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.7"); + return string.concat(super.version(), "+interop-beta.8"); } /// @notice Sets static configuration options for the L2 system. @@ -41,10 +41,9 @@ contract OptimismPortalInterop is OptimismPortal2 { useGas(SYSTEM_DEPOSIT_GAS_LIMIT); // Emit the special deposit transaction directly that sets the config in the L1Block predeploy contract. - emit TransactionDeposited( + _emitTransactionDeposited( Constants.DEPOSITOR_ACCOUNT, Predeploys.L1_BLOCK_ATTRIBUTES, - DEPOSIT_VERSION, abi.encodePacked( uint256(0), // mint uint256(0), // value diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalJovian.sol b/packages/contracts-bedrock/src/L1/OptimismPortalJovian.sol new file mode 100644 index 000000000000..fb5f079ab233 --- /dev/null +++ b/packages/contracts-bedrock/src/L1/OptimismPortalJovian.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Contracts +import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; + +/// @custom:proxied true +/// @title OptimismPortalJovian +/// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1 +/// and L2. Messages sent directly to the OptimismPortal have no form of replayability. +/// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. +contract OptimismPortalJovian is OptimismPortal2 { + /// @notice Version of the deposit event. + uint256 internal constant DEPOSIT_VERSION_1 = 1; + + /// @notice The storage slot that holds the deposit nonce. + /// @dev `bytes32(uint256(keccak256('optimismportal.depositnonce')) - 1)` + bytes32 public constant DEPOSIT_NONCE_SLOT = 0xfbdb6804978a124792ffaccc985bc1ad9ad7a5b3ff3fc4eb6936a7c373b67089; + + constructor( + uint256 _proofMaturityDelaySeconds, + uint256 _disputeGameFinalityDelaySeconds + ) + OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds) + { } + + /// @custom:semver +jovian-beta.1 + function version() public pure virtual override returns (string memory) { + return string.concat(super.version(), "+jovian-beta.1"); + } + + /// @notice Nonce incremented for each TransactionDeposited event + function depositNonce() public view returns (uint64 nonce_) { + assembly { + nonce_ := sload(DEPOSIT_NONCE_SLOT) + } + } + + function _transactionDepositedNonceAndVersion() internal virtual override returns (uint256) { + uint64 nonce = depositNonce() + 1; + assembly { + sstore(DEPOSIT_NONCE_SLOT, nonce) + } + return uint256(nonce) << 128 | DEPOSIT_VERSION_1; + } +} diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index 8a9279bd9229..467512d3bee6 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -134,15 +134,15 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { uint32 public eip1559Elasticity; /// @notice Emitted when configuration is updated. - /// @param version SystemConfig version. - /// @param updateType Type of update. - /// @param data Encoded update data. - event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); + /// @param nonceAndVersion Nonce (first 128-bits) and version (second 128-bits). + /// @param updateType Type of update. + /// @param data Encoded update data. + event ConfigUpdate(uint256 indexed nonceAndVersion, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.3.0-beta.10 + /// @custom:semver 2.3.0-beta.11 function version() public pure virtual returns (string memory) { - return "2.3.0-beta.10"; + return "2.3.0-beta.11"; } /// @notice Constructs the SystemConfig contract. @@ -268,11 +268,16 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { startBlock_ = Storage.getUint(START_BLOCK_SLOT); } - /// @notice Getter for the gas paying asset address. + /// @notice Getter for the gas paying asset address + decimals. function gasPayingToken() public view returns (address addr_, uint8 decimals_) { (addr_, decimals_) = GasPayingToken.getToken(); } + /// @notice Getter for the gas paying asset address. + function gasPayingTokenAddress() public view returns (address addr_) { + (addr_,) = GasPayingToken.getToken(); + } + /// @notice Getter for custom gas token paying networks. Returns true if the /// network uses a custom gas token. function isCustomGasToken() public view returns (bool) { @@ -329,7 +334,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner); bytes memory data = abi.encode(_unsafeBlockSigner); - emit ConfigUpdate(VERSION, UpdateType.UNSAFE_BLOCK_SIGNER, data); + emit ConfigUpdate(_configUpdateNonceAndVersion(), UpdateType.UNSAFE_BLOCK_SIGNER, data); } /// @notice Updates the batcher hash. Can only be called by the owner. @@ -344,7 +349,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { batcherHash = _batcherHash; bytes memory data = abi.encode(_batcherHash); - emit ConfigUpdate(VERSION, UpdateType.BATCHER, data); + emit ConfigUpdate(_configUpdateNonceAndVersion(), UpdateType.BATCHER, data); } /// @notice Updates gas config. Can only be called by the owner. @@ -365,7 +370,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { scalar = _scalar; bytes memory data = abi.encode(_overhead, _scalar); - emit ConfigUpdate(VERSION, UpdateType.FEE_SCALARS, data); + emit ConfigUpdate(_configUpdateNonceAndVersion(), UpdateType.FEE_SCALARS, data); } /// @notice Updates gas config as of the Ecotone upgrade. Can only be called by the owner. @@ -385,7 +390,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { scalar = (uint256(0x01) << 248) | (uint256(_blobbasefeeScalar) << 32) | _basefeeScalar; bytes memory data = abi.encode(overhead, scalar); - emit ConfigUpdate(VERSION, UpdateType.FEE_SCALARS, data); + emit ConfigUpdate(_configUpdateNonceAndVersion(), UpdateType.FEE_SCALARS, data); } /// @notice Updates the L2 gas limit. Can only be called by the owner. @@ -402,7 +407,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { gasLimit = _gasLimit; bytes memory data = abi.encode(_gasLimit); - emit ConfigUpdate(VERSION, UpdateType.GAS_LIMIT, data); + emit ConfigUpdate(_configUpdateNonceAndVersion(), UpdateType.GAS_LIMIT, data); } /// @notice Updates the EIP-1559 parameters of the chain. Can only be called by the owner. @@ -421,7 +426,11 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { eip1559Elasticity = _elasticity; bytes memory data = abi.encode(uint256(_denominator) << 32 | uint64(_elasticity)); - emit ConfigUpdate(VERSION, UpdateType.EIP_1559_PARAMS, data); + emit ConfigUpdate(_configUpdateNonceAndVersion(), UpdateType.EIP_1559_PARAMS, data); + } + + function _configUpdateNonceAndVersion() internal virtual returns (uint256) { + return VERSION; } /// @notice Sets the start block in a backwards compatible way. Proxies diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index 203405461903..74e8ec15d695 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.15; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; +import { SystemConfigJovian } from "src/L1/SystemConfigJovian.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; @@ -24,7 +25,7 @@ error CustomGasTokenNotSupported(); /// @notice The SystemConfig contract is used to manage configuration of an Optimism network. /// All configuration is stored on L1 and picked up by L2 as part of the derviation of /// the L2 chain. -contract SystemConfigInterop is SystemConfig { +contract SystemConfigInterop is SystemConfigJovian { /// @notice Storage slot where the dependency manager address is stored /// @dev Equal to bytes32(uint256(keccak256("systemconfig.dependencymanager")) - 1) bytes32 internal constant DEPENDENCY_MANAGER_SLOT = @@ -71,9 +72,9 @@ contract SystemConfigInterop is SystemConfig { Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); } - /// @custom:semver +interop-beta.9 + /// @custom:semver +interop-beta.10 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.9"); + return string.concat(super.version(), "+interop-beta.10"); } /// @notice Internal setter for the gas paying token address, includes validation. diff --git a/packages/contracts-bedrock/src/L1/SystemConfigJovian.sol b/packages/contracts-bedrock/src/L1/SystemConfigJovian.sol new file mode 100644 index 000000000000..3a8743903607 --- /dev/null +++ b/packages/contracts-bedrock/src/L1/SystemConfigJovian.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Contracts +import { SystemConfig } from "src/L1/SystemConfig.sol"; + +/// @custom:proxied true +/// @title SystemConfigJovian +/// @notice The SystemConfig contract is used to manage configuration of an Optimism network. +/// All configuration is stored on L1 and picked up by L2 as part of the derviation of +/// the L2 chain. +contract SystemConfigJovian is SystemConfig { + /// @notice Version identifier, used for upgrades. + uint256 public constant VERSION_1 = 1; + + /// @notice The storage slot that holds the deposit nonce. + /// @dev `bytes32(uint256(keccak256('systemconfig.configupdatenonce')) - 1)` + bytes32 public constant CONFIG_UPDATE_NONCE_SLOT = + 0x93fcce48e210616d14f0f2849f0028a91b366cdf4152de896d874c59cb47c5ee; + + /// @custom:semver +jovian-beta.1 + function version() public pure virtual override returns (string memory) { + return string.concat(super.version(), "+jovian-beta.1"); + } + + /// @notice Nonce incremented for each ConfigUpdate event + function configUpdateNonce() public view returns (uint64 nonce_) { + assembly { + nonce_ := sload(CONFIG_UPDATE_NONCE_SLOT) + } + } + + function _configUpdateNonceAndVersion() internal virtual override returns (uint256) { + uint64 nonce = configUpdateNonce() + 1; + assembly { + sstore(CONFIG_UPDATE_NONCE_SLOT, nonce) + } + return uint256(nonce) << 128 | VERSION_1; + } +} diff --git a/packages/contracts-bedrock/src/L2/L1Block.sol b/packages/contracts-bedrock/src/L2/L1Block.sol index 3767b80988da..6fceec950025 100644 --- a/packages/contracts-bedrock/src/L2/L1Block.sol +++ b/packages/contracts-bedrock/src/L2/L1Block.sol @@ -60,9 +60,15 @@ contract L1Block is ISemver, IGasToken { /// @notice The latest L1 blob base fee. uint256 public blobBaseFee; - /// @custom:semver 1.5.1-beta.5 + /// @notice Nonce incremented for each TransactionDeposited event + uint64 public depositNonce; + + /// @notice Nonce incremented for each ConfigUpdate event + uint64 public configUpdateNonce; + + /// @custom:semver 1.6.0 function version() public pure virtual returns (string memory) { - return "1.5.1-beta.5"; + return "1.6.0"; } /// @notice Returns the gas paying token, its decimals, name and symbol. @@ -136,11 +142,26 @@ contract L1Block is ISemver, IGasToken { /// 7. _blobBaseFee L1 blob base fee. /// 8. _hash L1 blockhash. /// 9. _batcherHash Versioned hash to authenticate batcher by. - function setL1BlockValuesEcotone() public { - _setL1BlockValuesEcotone(); + function setL1BlockValuesEcotone() external { + address depositor = DEPOSITOR_ACCOUNT(); + assembly { + // Revert if the caller is not the depositor account. + if xor(caller(), depositor) { + mstore(0x00, 0x3cc50b45) // 0x3cc50b45 is the 4-byte selector of "NotDepositor()" + revert(0x1C, 0x04) // returns the stored 4-byte selector from above + } + // sequencenum (uint64), blobBaseFeeScalar (uint32), baseFeeScalar (uint32) + sstore(sequenceNumber.slot, shr(128, calldataload(4))) + // number (uint64) and timestamp (uint64) + sstore(number.slot, shr(128, calldataload(20))) + sstore(basefee.slot, calldataload(36)) // uint256 + sstore(blobBaseFee.slot, calldataload(68)) // uint256 + sstore(hash.slot, calldataload(100)) // bytes32 + sstore(batcherHash.slot, calldataload(132)) // bytes32 + } } - /// @notice Updates the L1 block values for an Ecotone upgraded chain. + /// @notice Updates the L1 block values for an Jovian upgraded chain. /// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size. /// Params are expected to be in the following order: /// 1. _baseFeeScalar L1 base fee scalar @@ -152,7 +173,13 @@ contract L1Block is ISemver, IGasToken { /// 7. _blobBaseFee L1 blob base fee. /// 8. _hash L1 blockhash. /// 9. _batcherHash Versioned hash to authenticate batcher by. - function _setL1BlockValuesEcotone() internal { + /// 10. _depositNonce Nonce of the latest TransactionDeposited event processed up to this block. + /// 11. _configUpdateNonce Nonce of the latest ConfigUpdate event processed up to this block. + function setL1BlockValuesJovian() external { + _setL1BlockValuesJovian(); + } + + function _setL1BlockValuesJovian() internal { address depositor = DEPOSITOR_ACCOUNT(); assembly { // Revert if the caller is not the depositor account. @@ -168,6 +195,8 @@ contract L1Block is ISemver, IGasToken { sstore(blobBaseFee.slot, calldataload(68)) // uint256 sstore(hash.slot, calldataload(100)) // bytes32 sstore(batcherHash.slot, calldataload(132)) // bytes32 + // depositNonce (uint64) and configUpdateNonce (uint64) + sstore(depositNonce.slot, shr(128, calldataload(164))) } } diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol index 7b92b202a052..f7dc0f858132 100644 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol @@ -49,9 +49,9 @@ contract L1BlockInterop is L1Block { /// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff)) uint256 internal constant IS_DEPOSIT_SLOT = 0x921bd3a089295c6e5540e8fba8195448d253efd6f2e3e495b499b627dc36a300; - /// @custom:semver +interop-beta.3 + /// @custom:semver +interop-beta.4 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.3"); + return string.concat(super.version(), "+interop-beta.4"); } /// @notice Returns whether the call was triggered from a a deposit or not. @@ -78,15 +78,15 @@ contract L1BlockInterop is L1Block { } /// @notice Updates the `isDeposit` flag and sets the L1 block values for an Interop upgraded chain. - /// It updates the L1 block values through the `setL1BlockValuesEcotone` function. - /// It forwards the calldata to the internally-used `setL1BlockValuesEcotone` function. + /// It updates the L1 block values through the `_setL1BlockValuesJovian` function. + /// It forwards the calldata to the internally-used `_setL1BlockValuesJovian` function. function setL1BlockValuesInterop() external { // Set the isDeposit flag to true. assembly { sstore(IS_DEPOSIT_SLOT, 1) } - _setL1BlockValuesEcotone(); + _setL1BlockValuesJovian(); } /// @notice Resets the isDeposit flag. diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index 5aa4ee7d3d8a..fb30bf3e717d 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -177,6 +177,52 @@ library Encoding { ); } + /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesJovian + /// @param _baseFeeScalar L1 base fee Scalar + /// @param _blobBaseFeeScalar L1 blob base fee Scalar + /// @param _sequenceNumber Number of L2 blocks since epoch start. + /// @param _timestamp L1 timestamp. + /// @param _number L1 blocknumber. + /// @param _baseFee L1 base fee. + /// @param _blobBaseFee L1 blob base fee. + /// @param _hash L1 blockhash. + /// @param _batcherHash Versioned hash to authenticate batcher by. + /// @param _depositNonce Nonce of the latest TransactionDeposited event processed up to this block. + /// @param _configUpdateNonce Nonce of the latest ConfigUpdate event processed up to this block. + function encodeSetL1BlockValuesJovian( + uint32 _baseFeeScalar, + uint32 _blobBaseFeeScalar, + uint64 _sequenceNumber, + uint64 _timestamp, + uint64 _number, + uint256 _baseFee, + uint256 _blobBaseFee, + bytes32 _hash, + bytes32 _batcherHash, + uint64 _depositNonce, + uint64 _configUpdateNonce + ) + internal + pure + returns (bytes memory) + { + bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesJovian()")); + return abi.encodePacked( + functionSignature, + _baseFeeScalar, + _blobBaseFeeScalar, + _sequenceNumber, + _timestamp, + _number, + _baseFee, + _blobBaseFee, + _hash, + _batcherHash, + _depositNonce, + _configUpdateNonce + ); + } + /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesInterop /// @param _baseFeeScalar L1 base fee Scalar /// @param _blobBaseFeeScalar L1 blob base fee Scalar @@ -187,6 +233,8 @@ library Encoding { /// @param _blobBaseFee L1 blob base fee. /// @param _hash L1 blockhash. /// @param _batcherHash Versioned hash to authenticate batcher by. + /// @param _depositNonce Nonce of the latest TransactionDeposited event processed up to this block. + /// @param _configUpdateNonce Nonce of the latest ConfigUpdate event processed up to this block. function encodeSetL1BlockValuesInterop( uint32 _baseFeeScalar, uint32 _blobBaseFeeScalar, @@ -196,7 +244,9 @@ library Encoding { uint256 _baseFee, uint256 _blobBaseFee, bytes32 _hash, - bytes32 _batcherHash + bytes32 _batcherHash, + uint64 _depositNonce, + uint64 _configUpdateNonce ) internal pure @@ -213,7 +263,9 @@ library Encoding { _baseFee, _blobBaseFee, _hash, - _batcherHash + _batcherHash, + _depositNonce, + _configUpdateNonce ); } } diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index 23c9cee7ccc8..7efa99442542 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -706,7 +706,7 @@ contract L1CrossDomainMessenger_Test is CommonTest { // Mock the gasPayingToken function to return a custom gas token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(1), uint8(18)) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(1)) ); vm.prank(alice); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index 46e10d037170..8b338868fdb7 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -136,7 +136,7 @@ contract OptimismPortal2_Test is CommonTest { assertEq(optimismPortal2.paused(), true); } - /// @dev Tests that `receive` successdully deposits ETH. + /// @dev Tests that `receive` successfully deposits ETH. function testFuzz_receive_succeeds(uint256 _value) external { uint256 balanceBefore = address(optimismPortal2).balance; _value = bound(_value, 0, type(uint256).max - balanceBefore); @@ -458,7 +458,9 @@ contract OptimismPortal2_Test is CommonTest { // TODO(opcm upgrades): remove skip once upgrade path is implemented skipIfForkTest("OptimismPortal2_Test: gas paying token functionality DNE on op mainnet"); - vm.mockCall(address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(42), 18)); + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(42)) + ); // The balance slot vm.store(address(optimismPortal2), bytes32(uint256(61)), bytes32(type(uint256).max)); @@ -1052,7 +1054,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { // modify the gas token to be non ether vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(L1Token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(L1Token)) ); vm.expectEmit(address(optimismPortal2)); @@ -1123,7 +1125,7 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { // modify the gas token to be non ether vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(L1Token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(L1Token)) ); uint256 bobBalanceBefore = L1Token.balanceOf(bob); @@ -1151,6 +1153,76 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { assert(L1Token.balanceOf(bob) == bobBalanceBefore + 100); } + /// @dev Tests that `finalizeWithdrawalTransaction` fails with a broken custom gas token. + function test_finalizeWithdrawalTransaction_brokenGasToken_reverts() external { + Types.WithdrawalTransaction memory _defaultTx_noData = Types.WithdrawalTransaction({ + nonce: 0, + sender: alice, + target: bob, + value: 100, + gasLimit: 100_000, + data: hex"" + }); + // Get withdrawal proof data we can use for testing. + ( + bytes32 _stateRoot_noData, + bytes32 _storageRoot_noData, + bytes32 _outputRoot_noData, + , + bytes[] memory _withdrawalProof_noData + ) = ffi.getProveWithdrawalTransactionInputs(_defaultTx_noData); + // Setup a dummy output root proof for reuse. + Types.OutputRootProof memory _outputRootProof_noData = Types.OutputRootProof({ + version: bytes32(uint256(0)), + stateRoot: _stateRoot_noData, + messagePasserStorageRoot: _storageRoot_noData, + latestBlockhash: bytes32(uint256(0)) + }); + uint256 _proposedBlockNumber_noData = 0xFF; + IFaultDisputeGame game_noData = IFaultDisputeGame( + payable( + address( + disputeGameFactory.create( + optimismPortal2.respectedGameType(), + Claim.wrap(_outputRoot_noData), + abi.encode(_proposedBlockNumber_noData) + ) + ) + ) + ); + uint256 _proposedGameIndex_noData = disputeGameFactory.gameCount() - 1; + // Warp beyond the chess clocks and finalize the game. + vm.warp(block.timestamp + game_noData.maxClockDuration().raw() + 1 seconds); + // Fund the portal so that we can withdraw ETH. + vm.store(address(optimismPortal2), bytes32(uint256(61)), bytes32(uint256(0xFFFFFFFF))); + deal(address(L1Token), address(optimismPortal2), 0xFFFFFFFF); + + // modify the gas token to be non ether + vm.mockCall( + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(L1Token)) + ); + + // make the ERC20.transfer function a no-op (success without any transfer) + vm.mockCall(address(L1Token), abi.encodeCall(L1Token.transfer, (bob, uint256(100))), abi.encode(true)); + + optimismPortal2.proveWithdrawalTransaction({ + _tx: _defaultTx_noData, + _disputeGameIndex: _proposedGameIndex_noData, + _outputRootProof: _outputRootProof_noData, + _withdrawalProof: _withdrawalProof_noData + }); + + // Warp and resolve the dispute game. + game_noData.resolveClaim(0, 0); + game_noData.resolve(); + vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1 seconds); + + // finalizeWithdrawalTransaction should revert, as the balance didn't change + // TODO fix this + vm.expectRevert(IOptimismPortal2.CustomGasTokenNotSupported.selector); + optimismPortal2.finalizeWithdrawalTransaction(_defaultTx_noData); + } + /// @dev Tests that `finalizeWithdrawalTransaction` succeeds. function test_finalizeWithdrawalTransaction_provenWithdrawalHashEther_succeeds() external { uint256 bobBalanceBefore = address(bob).balance; @@ -1245,8 +1317,8 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest { vm.mockCall( address(systemConfig), - abi.encodeCall(systemConfig.gasPayingToken, ()), - abi.encode(address(_defaultTx.target), 18) + abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), + abi.encode(address(_defaultTx.target)) ); optimismPortal2.proveWithdrawalTransaction({ @@ -1925,7 +1997,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data); @@ -2006,7 +2078,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); vm.expectRevert(stdError.arithmeticError); // Deposit the token into the portal @@ -2023,7 +2095,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); // Mock the token balance @@ -2044,7 +2116,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); // Call minimumGasLimit(0) before vm.expectRevert to ensure vm.expectRevert is for depositERC20Transaction @@ -2061,7 +2133,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); vm.expectRevert(SmallGasLimit.selector); @@ -2078,7 +2150,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); uint64 gasLimit = optimismPortal2.minimumGasLimit(120_001); @@ -2097,7 +2169,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); // Deposit the token into the portal @@ -2117,7 +2189,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); // Deposit the token into the portal @@ -2178,7 +2250,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); bytes memory opaqueData = abi.encodePacked(uint256(0), _value, _gasLimit, _isCreation, _data); @@ -2255,7 +2327,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal // Mock the gas paying token to be the ERC20 token vm.mockCall( - address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(token), 18) + address(systemConfig), abi.encodeCall(systemConfig.gasPayingTokenAddress, ()), abi.encode(address(token)) ); vm.expectRevert(NoValue.selector); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol index 1e395b3279c4..fedcdf3b71a8 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol @@ -19,19 +19,21 @@ contract OptimismPortalInterop_Test is CommonTest { function setUp() public virtual override { super.enableInterop(); super.setUp(); + optimismPortal2.version(); } /// @dev Tests that the config for the gas paying token can be set. function testFuzz_setConfig_gasPayingToken_succeeds(bytes calldata _value) public { vm.expectEmit(address(optimismPortal2)); - emitTransactionDeposited({ + emitTransactionDepositedJovian({ _from: Constants.DEPOSITOR_ACCOUNT, _to: Predeploys.L1_BLOCK_ATTRIBUTES, _value: 0, _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.SET_GAS_PAYING_TOKEN, _value)) + _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.SET_GAS_PAYING_TOKEN, _value)), + _nonce: 1 }); vm.prank(address(_optimismPortalInterop().systemConfig())); @@ -47,14 +49,15 @@ contract OptimismPortalInterop_Test is CommonTest { /// @dev Tests that the config for adding a dependency can be set. function testFuzz_setConfig_addDependency_succeeds(bytes calldata _value) public { vm.expectEmit(address(optimismPortal2)); - emitTransactionDeposited({ + emitTransactionDepositedJovian({ _from: Constants.DEPOSITOR_ACCOUNT, _to: Predeploys.L1_BLOCK_ATTRIBUTES, _value: 0, _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.ADD_DEPENDENCY, _value)) + _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.ADD_DEPENDENCY, _value)), + _nonce: 1 }); vm.prank(address(_optimismPortalInterop().systemConfig())); @@ -70,14 +73,15 @@ contract OptimismPortalInterop_Test is CommonTest { /// @dev Tests that the config for removing a dependency can be set. function testFuzz_setConfig_removeDependency_succeeds(bytes calldata _value) public { vm.expectEmit(address(optimismPortal2)); - emitTransactionDeposited({ + emitTransactionDepositedJovian({ _from: Constants.DEPOSITOR_ACCOUNT, _to: Predeploys.L1_BLOCK_ATTRIBUTES, _value: 0, _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.REMOVE_DEPENDENCY, _value)) + _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.REMOVE_DEPENDENCY, _value)), + _nonce: 1 }); vm.prank(address(_optimismPortalInterop().systemConfig())); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortalJovian.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortalJovian.t.sol new file mode 100644 index 000000000000..db6d09f17725 --- /dev/null +++ b/packages/contracts-bedrock/test/L1/OptimismPortalJovian.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Testing +import { CommonTest } from "test/setup/CommonTest.sol"; + +// Interfaces +import { IOptimismPortalJovian } from "interfaces/L1/IOptimismPortalJovian.sol"; + +contract OptimismPortalJovian_Test is CommonTest { + /// @notice Marked virtual to be overridden in + /// test/kontrol/deployment/DeploymentSummary.t.sol + function setUp() public virtual override { + super.enableJovian(); + super.setUp(); + optimismPortal2.version(); + } + + /// @dev Tests that `receive` successfully deposits ETH. + function testFuzz_receive_succeeds(uint256 _value) external { + uint256 balanceBefore = address(optimismPortal2).balance; + _value = bound(_value, 0, type(uint256).max - balanceBefore); + + vm.expectEmit(address(optimismPortal2)); + emitTransactionDepositedJovian(alice, alice, _value, _value, 100_000, false, hex"", 1); + + // give alice money and send as an eoa + vm.deal(alice, _value); + vm.prank(alice, alice); + (bool s,) = address(optimismPortal2).call{ value: _value }(hex""); + + assertTrue(s); + assertEq(address(optimismPortal2).balance, balanceBefore + _value); + } + + function test_nonce_increment_works() external { + vm.deal(alice, 100_000_000); + + uint256 value = 2; + for (uint64 i = 1; i <= 10; i++) { + vm.expectEmit(address(optimismPortal2)); + emitTransactionDepositedJovian(alice, alice, value, value, 100_000, false, hex"", i); + vm.prank(alice, alice); + (bool s,) = address(optimismPortal2).call{ value: value }(hex""); + assertTrue(s); + } + } + + function test_depositNonce_works() external { + vm.deal(alice, 100_000_000); + + uint64 nonce = IOptimismPortalJovian(payable(address(optimismPortal2))).depositNonce(); + assertEq(0, nonce); + + uint256 value = 2; + for (uint64 i = 1; i <= 2; i++) { + vm.prank(alice, alice); + (bool s,) = address(optimismPortal2).call{ value: value }(hex""); + assertTrue(s); + } + + nonce = IOptimismPortalJovian(payable(address(optimismPortal2))).depositNonce(); + assertEq(2, nonce); + } +} diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index a5ca2594f04b..674543fc3b2d 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -19,7 +19,7 @@ import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IL1Block } from "interfaces/L2/IL1Block.sol"; contract SystemConfig_Init is CommonTest { - event ConfigUpdate(uint256 indexed version, ISystemConfig.UpdateType indexed updateType, bytes data); + event ConfigUpdate(uint256 indexed nonceAndVersion, ISystemConfig.UpdateType indexed updateType, bytes data); } contract SystemConfig_Initialize_Test is SystemConfig_Init { diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index bc01f347c910..ef6eb098a083 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -24,6 +24,7 @@ contract SystemConfigInterop_Test is CommonTest { function setUp() public virtual override { super.enableInterop(); super.setUp(); + systemConfig.version(); } /// @dev Temporary test that checks that correct calls to initialize when using a custom gas token revert with the diff --git a/packages/contracts-bedrock/test/L1/SystemConfigJovian.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigJovian.t.sol new file mode 100644 index 000000000000..c2d1413173d1 --- /dev/null +++ b/packages/contracts-bedrock/test/L1/SystemConfigJovian.t.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Testing +import { CommonTest } from "test/setup/CommonTest.sol"; + +// Interfaces +import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { ISystemConfigJovian } from "interfaces/L1/ISystemConfigJovian.sol"; + +contract SystemConfig_Init is CommonTest { + event ConfigUpdate(uint256 indexed nonceAndVersion, ISystemConfig.UpdateType indexed updateType, bytes data); +} + +contract SystemConfigJovian_Test is SystemConfig_Init { + /// @notice Marked virtual to be overridden in + /// test/kontrol/deployment/DeploymentSummary.t.sol + function setUp() public virtual override { + super.enableJovian(); + super.setUp(); + systemConfig.version(); + } + + function test_nonce_increment_works() external { + bytes32 newBatcherHash = bytes32(uint256(1234)); + for (uint256 i = 4; i <= 10; i++) { + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate(i << 128 | 1, ISystemConfig.UpdateType.BATCHER, abi.encode(newBatcherHash)); + vm.prank(systemConfig.owner()); + systemConfig.setBatcherHash(newBatcherHash); + } + } + + function test_configUpdateNonce_works() external { + // genesis emits 3 logs, so nonce starts at 3 + uint64 nonce = ISystemConfigJovian(payable(address(systemConfig))).configUpdateNonce(); + assertEq(3, nonce); + + for (uint64 i = 1; i <= 2; i++) { + vm.prank(systemConfig.owner()); + systemConfig.setBatcherHash(bytes32(0)); + } + + nonce = ISystemConfigJovian(payable(address(systemConfig))).configUpdateNonce(); + assertEq(5, nonce); + } +} + +contract SystemConfigJovian_Setters_Test is SystemConfig_Init { + /// @notice Marked virtual to be overridden in + /// test/kontrol/deployment/DeploymentSummary.t.sol + function setUp() public virtual override { + super.enableJovian(); + super.setUp(); + } + + /// @dev Tests that `setBatcherHash` updates the batcher hash successfully. + function testFuzz_setBatcherHash_succeeds(bytes32 newBatcherHash) external { + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate(4 << 128 | 1, ISystemConfig.UpdateType.BATCHER, abi.encode(newBatcherHash)); + + vm.prank(systemConfig.owner()); + systemConfig.setBatcherHash(newBatcherHash); + assertEq(systemConfig.batcherHash(), newBatcherHash); + } + + /// @dev Tests that `setGasConfig` updates the overhead and scalar successfully. + function testFuzz_setGasConfig_succeeds(uint256 newOverhead, uint256 newScalar) external { + // always zero out most significant byte + newScalar = (newScalar << 16) >> 16; + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate(4 << 128 | 1, ISystemConfig.UpdateType.FEE_SCALARS, abi.encode(newOverhead, newScalar)); + + vm.prank(systemConfig.owner()); + systemConfig.setGasConfig(newOverhead, newScalar); + assertEq(systemConfig.overhead(), newOverhead); + assertEq(systemConfig.scalar(), newScalar); + } + + function testFuzz_setGasConfigEcotone_succeeds(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external { + // TODO(opcm upgrades): remove skip once upgrade is implemented + skipIfForkTest("SystemConfig_Setters_TestFail: 'setGasConfigEcotone' method DNE on op mainnet"); + bytes32 encoded = + ffi.encodeScalarEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar }); + + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate( + 4 << 128 | 1, ISystemConfig.UpdateType.FEE_SCALARS, abi.encode(systemConfig.overhead(), encoded) + ); + + vm.prank(systemConfig.owner()); + systemConfig.setGasConfigEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar }); + assertEq(systemConfig.basefeeScalar(), _basefeeScalar); + assertEq(systemConfig.blobbasefeeScalar(), _blobbasefeeScalar); + assertEq(systemConfig.scalar(), uint256(encoded)); + + (uint32 basefeeScalar, uint32 blobbbasefeeScalar) = ffi.decodeScalarEcotone(encoded); + assertEq(uint256(basefeeScalar), uint256(_basefeeScalar)); + assertEq(uint256(blobbbasefeeScalar), uint256(_blobbasefeeScalar)); + } + + /// @dev Tests that `setGasLimit` updates the gas limit successfully. + function testFuzz_setGasLimit_succeeds(uint64 newGasLimit) external { + uint64 minimumGasLimit = systemConfig.minimumGasLimit(); + uint64 maximumGasLimit = systemConfig.maximumGasLimit(); + newGasLimit = uint64(bound(uint256(newGasLimit), uint256(minimumGasLimit), uint256(maximumGasLimit))); + + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate(4 << 128 | 1, ISystemConfig.UpdateType.GAS_LIMIT, abi.encode(newGasLimit)); + + vm.prank(systemConfig.owner()); + systemConfig.setGasLimit(newGasLimit); + assertEq(systemConfig.gasLimit(), newGasLimit); + } + + /// @dev Tests that `setUnsafeBlockSigner` updates the block signer successfully. + function testFuzz_setUnsafeBlockSigner_succeeds(address newUnsafeSigner) external { + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate(4 << 128 | 1, ISystemConfig.UpdateType.UNSAFE_BLOCK_SIGNER, abi.encode(newUnsafeSigner)); + + vm.prank(systemConfig.owner()); + systemConfig.setUnsafeBlockSigner(newUnsafeSigner); + assertEq(systemConfig.unsafeBlockSigner(), newUnsafeSigner); + } + + /// @dev Tests that `setEIP1559Params` updates the EIP1559 parameters successfully. + function testFuzz_setEIP1559Params_succeeds(uint32 _denominator, uint32 _elasticity) external { + // TODO(opcm upgrades): remove skip once upgrade is implemented + skipIfForkTest("SystemConfig_Setters_TestFail: 'setEIP1559Params' method DNE on op mainnet"); + _denominator = uint32(bound(_denominator, 2, type(uint32).max)); + _elasticity = uint32(bound(_elasticity, 2, type(uint32).max)); + + vm.expectEmit(address(systemConfig)); + emit ConfigUpdate( + 4 << 128 | 1, + ISystemConfig.UpdateType.EIP_1559_PARAMS, + abi.encode(uint256(_denominator) << 32 | uint64(_elasticity)) + ); + + vm.prank(systemConfig.owner()); + systemConfig.setEIP1559Params(_denominator, _elasticity); + assertEq(systemConfig.eip1559Denominator(), _denominator); + assertEq(systemConfig.eip1559Elasticity(), _elasticity); + } +} diff --git a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol index 40dfa459e16d..80fd61b03d10 100644 --- a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol @@ -28,6 +28,7 @@ contract L1BlockInteropTest is CommonTest { function setUp() public virtual override { super.enableInterop(); super.setUp(); + l1Block.version(); } /// @dev Tests that an arbitrary chain ID can be added to the dependency set. diff --git a/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol b/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol index b112547cd562..3c0732b2fa56 100644 --- a/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol +++ b/packages/contracts-bedrock/test/safe/DeputyGuardianModule.t.sol @@ -289,12 +289,14 @@ contract DeputyGuardianModule_NoPortalCollisions_Test is DeputyGuardianModule_Te /// @dev tests that no function selectors in the L1 contracts collide with the OptimismPortal2 functions called by /// the DeputyGuardianModule. function test_noPortalCollisions_succeeds() external { - string[] memory excludes = new string[](5); + string[] memory excludes = new string[](7); excludes[0] = "src/dispute/lib/*"; excludes[1] = "src/L1/OptimismPortal2.sol"; - excludes[2] = "src/L1/OptimismPortalInterop.sol"; - excludes[3] = "interfaces/L1/IOptimismPortal2.sol"; - excludes[4] = "interfaces/L1/IOptimismPortalInterop.sol"; + excludes[2] = "src/L1/OptimismPortalJovian.sol"; + excludes[3] = "src/L1/OptimismPortalInterop.sol"; + excludes[4] = "interfaces/L1/IOptimismPortal2.sol"; + excludes[5] = "interfaces/L1/IOptimismPortalJovian.sol"; + excludes[6] = "interfaces/L1/IOptimismPortalInterop.sol"; Abi[] memory abis = ForgeArtifacts.getContractFunctionAbis("src/{L1,dispute,universal}", excludes); for (uint256 i; i < abis.length; i++) { for (uint256 j; j < abis[i].entries.length; j++) { diff --git a/packages/contracts-bedrock/test/setup/CommonTest.sol b/packages/contracts-bedrock/test/setup/CommonTest.sol index aea7c629b695..c493c3d3d61a 100644 --- a/packages/contracts-bedrock/test/setup/CommonTest.sol +++ b/packages/contracts-bedrock/test/setup/CommonTest.sol @@ -35,6 +35,7 @@ contract CommonTest is Test, Setup, Events { bool useAltDAOverride; address customGasToken; + bool useJovian; bool useInteropOverride; ERC20 L1Token; @@ -62,6 +63,9 @@ contract CommonTest is Test, Setup, Events { if (customGasToken != address(0)) { deploy.cfg().setUseCustomGasToken(customGasToken); } + if (useJovian) { + deploy.cfg().setL2GenesisJovianTimeOffset(0); + } if (useInteropOverride) { deploy.cfg().setUseInterop(true); } @@ -166,6 +170,25 @@ contract CommonTest is Test, Setup, Events { emit TransactionDeposited(_from, _to, 0, abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data)); } + /// @dev Helper function that wraps `TransactionDeposited` event. + /// The magic `nonce << 128 | 1` is the nonce | version. + function emitTransactionDepositedJovian( + address _from, + address _to, + uint256 _mint, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data, + uint64 _nonce + ) + internal + { + emit TransactionDeposited( + _from, _to, uint256(_nonce) << 128 | 1, abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data) + ); + } + function enableAltDA() public { // Check if the system has already been deployed, based off of the heuristic that alice and bob have not been // set by the `setUp` function yet. @@ -187,6 +210,16 @@ contract CommonTest is Test, Setup, Events { customGasToken = _token; } + function enableJovian() public { + // Check if the system has already been deployed, based off of the heuristic that alice and bob have not been + // set by the `setUp` function yet. + if (!(alice == address(0) && bob == address(0))) { + revert("CommonTest: Cannot enable jovian after deployment. Consider overriding `setUp`."); + } + + useJovian = true; + } + function enableInterop() public { // Check if the system has already been deployed, based off of the heuristic that alice and bob have not been // set by the `setUp` function yet. diff --git a/packages/contracts-bedrock/test/setup/Events.sol b/packages/contracts-bedrock/test/setup/Events.sol index 7056f0cbdd6b..d79e3cdba136 100644 --- a/packages/contracts-bedrock/test/setup/Events.sol +++ b/packages/contracts-bedrock/test/setup/Events.sol @@ -13,7 +13,9 @@ import { Types } from "src/libraries/Types.sol"; contract Events { /// @dev OpenZeppelin Ownable.sol transferOwnership event event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); + event TransactionDeposited( + address indexed from, address indexed to, uint256 indexed nonceAndVersion, bytes opaqueData + ); event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); diff --git a/packages/contracts-bedrock/test/setup/ForkLive.s.sol b/packages/contracts-bedrock/test/setup/ForkLive.s.sol index 02ae600c2534..7f9a0d4321f1 100644 --- a/packages/contracts-bedrock/test/setup/ForkLive.s.sol +++ b/packages/contracts-bedrock/test/setup/ForkLive.s.sol @@ -116,7 +116,7 @@ contract ForkLive is Deployer { /// environment, and deploys new implementations. function _deployNewImplementations() internal { Deploy deploy = Deploy(address(uint160(uint256(keccak256(abi.encode("optimism.deploy")))))); - deploy.deployImplementations({ _isInterop: false }); + deploy.deployImplementations(); } /// @notice Saves the proxy and implementation addresses for a contract name diff --git a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol index ae283ef8dba1..390d2791f8a4 100644 --- a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol +++ b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol @@ -178,7 +178,9 @@ contract GasBenchMark_L1BlockInterop is GasBenchMark_L1Block { type(uint256).max, type(uint256).max, keccak256(abi.encode(1)), - bytes32(type(uint256).max) + bytes32(type(uint256).max), + type(uint64).max, + type(uint64).max ); } } diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 60da8c58ba50..866e5fa49301 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -15,9 +15,11 @@ import { OPContractsManager } from "src/L1/OPContractsManager.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { ISystemConfigJovian } from "interfaces/L1/ISystemConfigJovian.sol"; import { ISystemConfigInterop } from "interfaces/L1/ISystemConfigInterop.sol"; import { IDataAvailabilityChallenge } from "interfaces/L1/IDataAvailabilityChallenge.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; +import { SystemConfigJovian } from "../../src/L1/SystemConfigJovian.sol"; /// @title Specification_Test /// @dev Specifies common security properties of entrypoints to L1 contracts, including authorization and @@ -210,6 +212,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "L1StandardBridge", _sel: _getSel("systemConfig()") }); // OptimismPortalInterop + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("DEPOSIT_NONCE_SLOT()") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") @@ -261,6 +264,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("respectedGameTypeUpdatedAt()") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("proofSubmitters(bytes32,uint256)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("numProofSubmitters(bytes32)") }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("depositNonce()") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("balance()") }); _addSpec({ _name: "OptimismPortalInterop", @@ -273,6 +277,67 @@ contract Specification_Test is CommonTest { _auth: Role.SYSTEMCONFIGOWNER }); + // OptimismPortalJovian + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("DEPOSIT_NONCE_SLOT()") }); + _addSpec({ + _name: "OptimismPortalJovian", + _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") + }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("donateETH()") }); + _addSpec({ + _name: "OptimismPortalJovian", + _sel: IOptimismPortal2.finalizeWithdrawalTransaction.selector, + _pausable: true + }); + _addSpec({ + _name: "OptimismPortalJovian", + _sel: IOptimismPortal2.finalizeWithdrawalTransactionExternalProof.selector, + _pausable: true + }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("finalizedWithdrawals(bytes32)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("guardian()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("initialize(address,address,address,uint32)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("l2Sender()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("minimumGasLimit(uint64)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("params()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("paused()") }); + _addSpec({ + _name: "OptimismPortalJovian", + _sel: IOptimismPortal2.proveWithdrawalTransaction.selector, + _pausable: true + }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("provenWithdrawals(bytes32,address)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("superchainConfig()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("systemConfig()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("version()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("disputeGameFactory()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("disputeGameBlacklist(address)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("respectedGameType()") }); + // Comment out the auth to not disturb the testDeputyGuardianAuth test. This code is not meant to run in + // production, + // and will be merged into the OptimismPortal2 contract itself in the future. + _addSpec({ + _name: "OptimismPortalJovian", + _sel: _getSel("blacklistDisputeGame(address)") /*, _auth: Role.GUARDIAN*/ + }); + _addSpec({ + _name: "OptimismPortalJovian", + _sel: _getSel("setRespectedGameType(uint32)") /*, _auth: Role.GUARDIAN*/ + }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("checkWithdrawal(bytes32,address)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("proofMaturityDelaySeconds()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("disputeGameFinalityDelaySeconds()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("respectedGameTypeUpdatedAt()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("proofSubmitters(bytes32,uint256)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("numProofSubmitters(bytes32)") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("depositNonce()") }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("balance()") }); + _addSpec({ + _name: "OptimismPortalJovian", + _sel: _getSel("depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)") + }); + _addSpec({ _name: "OptimismPortalJovian", _sel: _getSel("setGasPayingToken(address,uint8,bytes32,bytes32)") }); + // OptimismPortal2 _addSpec({ _name: "OptimismPortal2", _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("donateETH()") }); @@ -396,6 +461,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfig", _sel: _getSel("OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("BATCH_INBOX_SLOT()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("gasPayingToken()") }); + _addSpec({ _name: "SystemConfig", _sel: _getSel("gasPayingTokenAddress()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("gasPayingTokenName()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("gasPayingTokenSymbol()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("isCustomGasToken()") }); @@ -410,6 +476,85 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfig", _sel: _getSel("blobbasefeeScalar()") }); _addSpec({ _name: "SystemConfig", _sel: _getSel("maximumGasLimit()") }); + // SystemConfigJovian + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("UNSAFE_BLOCK_SIGNER_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("START_BLOCK_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("VERSION()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("batcherHash()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("gasLimit()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("eip1559Denominator()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("eip1559Elasticity()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: ISystemConfig.initialize.selector }); + _addSpec({ _name: "SystemConfigJovian", _sel: ISystemConfigJovian.minimumGasLimit.selector }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("overhead()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("owner()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("renounceOwnership()"), _auth: Role.SYSTEMCONFIGOWNER }); + _addSpec({ _name: "SystemConfigJovian", _sel: ISystemConfigJovian.resourceConfig.selector }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("scalar()") }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: ISystemConfigJovian.setBatcherHash.selector, + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: ISystemConfigJovian.setGasConfig.selector, + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: ISystemConfigJovian.setGasLimit.selector, + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: ISystemConfigJovian.setEIP1559Params.selector, + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: ISystemConfigJovian.setUnsafeBlockSigner.selector, + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: _getSel("transferOwnership(address)"), + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ _name: "SystemConfigJovian", _sel: ISystemConfigJovian.unsafeBlockSigner.selector }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("version()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("l1CrossDomainMessenger()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("l1ERC721Bridge()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("l1StandardBridge()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("optimismPortal()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("optimismMintableERC20Factory()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("batchInbox()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("startBlock()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("L1_CROSS_DOMAIN_MESSENGER_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("L1_ERC_721_BRIDGE_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("L1_STANDARD_BRIDGE_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("OPTIMISM_PORTAL_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("BATCH_INBOX_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("gasPayingToken()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("gasPayingTokenAddress()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("gasPayingTokenName()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("gasPayingTokenSymbol()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("isCustomGasToken()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("DISPUTE_GAME_FACTORY_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("disputeGameFactory()") }); + _addSpec({ + _name: "SystemConfigJovian", + _sel: _getSel("setGasConfigEcotone(uint32,uint32)"), + _auth: Role.SYSTEMCONFIGOWNER + }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("basefeeScalar()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("blobbasefeeScalar()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("maximumGasLimit()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("CONFIG_UPDATE_NONCE_SLOT()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("VERSION_1()") }); + _addSpec({ _name: "SystemConfigJovian", _sel: _getSel("configUpdateNonce()") }); + // SystemConfigInterop _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("UNSAFE_BLOCK_SIGNER_SLOT()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("START_BLOCK_SLOT()") }); @@ -472,6 +617,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("BATCH_INBOX_SLOT()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("gasPayingToken()") }); + _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("gasPayingTokenAddress()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("gasPayingTokenName()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("gasPayingTokenSymbol()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("isCustomGasToken()") }); @@ -485,6 +631,9 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("basefeeScalar()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("blobbasefeeScalar()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("maximumGasLimit()") }); + _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("CONFIG_UPDATE_NONCE_SLOT()") }); + _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("VERSION_1()") }); + _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("configUpdateNonce()") }); _addSpec({ _name: "SystemConfigInterop", _sel: _getSel("addDependency(uint256)"), _auth: Role.DEPENDENCYMANAGER }); _addSpec({ _name: "SystemConfigInterop", diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index f8984c5f72d2..749d50d977d0 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -344,27 +344,29 @@ contract Initializer_Test is CommonTest { /// 3. The `initialize()` function of each contract cannot be called again. function test_cannotReinitialize_succeeds() public { // Collect exclusions. - string[] memory excludes = new string[](9); + string[] memory excludes = new string[](11); // TODO: Neither of these contracts are labeled properly in the deployment script. Both are // currently being labeled as their non-interop versions. Remove these exclusions once // the deployment script is fixed. - excludes[0] = "src/L1/SystemConfigInterop.sol"; - excludes[1] = "src/L1/OptimismPortalInterop.sol"; + excludes[0] = "src/L1/SystemConfigJovian.sol"; + excludes[1] = "src/L1/OptimismPortalJovian.sol"; + excludes[2] = "src/L1/SystemConfigInterop.sol"; + excludes[3] = "src/L1/OptimismPortalInterop.sol"; // Contract is currently not being deployed as part of the standard deployment script. - excludes[2] = "src/L2/OptimismSuperchainERC20.sol"; + excludes[4] = "src/L2/OptimismSuperchainERC20.sol"; // Periphery contracts don't get deployed as part of the standard deployment script. - excludes[3] = "src/periphery/*"; + excludes[5] = "src/periphery/*"; // TODO: Deployment script is currently "broken" in the sense that it doesn't properly // label the FaultDisputeGame and PermissionedDisputeGame contracts and instead // simply deploys them anonymously. Means that functions like "getInitializedSlot" // don't work properly. Remove these exclusions once the deployment script is fixed. - excludes[4] = "src/dispute/FaultDisputeGame.sol"; - excludes[5] = "src/dispute/PermissionedDisputeGame.sol"; + excludes[6] = "src/dispute/FaultDisputeGame.sol"; + excludes[7] = "src/dispute/PermissionedDisputeGame.sol"; // TODO: Eventually remove this exclusion. Same reason as above dispute contracts. - excludes[6] = "src/L1/OPContractsManager.sol"; - excludes[7] = "src/L1/OPContractsManagerInterop.sol"; + excludes[8] = "src/L1/OPContractsManager.sol"; + excludes[9] = "src/L1/OPContractsManagerInterop.sol"; // L2 contract initialization is tested in Predeploys.t.sol - excludes[8] = "src/L2/*"; + excludes[10] = "src/L2/*"; // Get all contract names in the src directory, minus the excluded contracts. string[] memory contractNames = ForgeArtifacts.getContractNames("src/*", excludes);