From 8167f36341974bf27a5b5d734ed37037b4743ca4 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Thu, 4 Apr 2024 22:44:51 +0300 Subject: [PATCH] contracts-bedrock: fix deploy config for mainnet MCP upgrade (#9865) * contracts-bedrock: fix deploy config for mainnet MCP upgrade The config param for the scalar was not updated in the deploy config when it was changed on chain. If we can enforce that the deploy config is always used as the source of truth for when doing on chain config changes, it can help to scale the team as other teams can use the same config file and know that the values in there represent the truth. This is a fundamental problem with the `initialize` pattern, we need to move away from it eventually. The deploy config is updated with the value that is used on mainnet and the parsing is updated to handle the new ecotone style config, which tightly packs the values into a single bytes32. * op-chain-ops: more cleanup * op-e2e: fix build * cleanup: modularize scalar encoding and decoding Ensures that the same consensus code is used to encode and decode the scalar in various places. * op-chain-ops: fix L2 genesis generation * config: fix serialization * op-chain-ops: refactor config Make backwards compatible * op-chain-ops: fix build * deploy-config: update mainnet fee scalar config Should match mainnet values * op-chain-ops: fix test * genesis: test L1Block predeploy state setting * op-upgrade: delete dead code * build: fix * op-chain-ops: add deprecation warning Co-authored-by: Sebastian Stammler * deploy-config: use mainnet values Co-authored-by: Sebastian Stammler * deploy-config: use mainnet values Co-authored-by: Sebastian Stammler * op-service: end to end encode/decode scalar tests * tests: cleanup * op-chain-ops: fix nits, adapt to breaking simulated backend changes * op-chain-ops: fix comment and address-type conversion nits --------- Co-authored-by: Sebastian Stammler Co-authored-by: protolambda --- op-chain-ops/cmd/ecotone-scalar/main.go | 15 +-- op-chain-ops/cmd/op-upgrade-mcp/main.go | 9 +- op-chain-ops/cmd/op-upgrade/main.go | 4 +- op-chain-ops/genesis/config.go | 56 ++++++++-- op-chain-ops/genesis/layer_two_test.go | 100 ++++++++++++++++++ .../testdata/test-deploy-config-full.json | 2 + op-chain-ops/upgrades/l1.go | 43 ++++---- op-e2e/e2eutils/setup.go | 2 +- op-e2e/system_test.go | 6 +- op-node/rollup/derive/l1_block_info.go | 6 +- op-service/Makefile | 1 + op-service/eth/blobs_api_test.go | 3 +- op-service/eth/types.go | 41 +++++-- op-service/eth/types_test.go | 17 ++- .../deploy-config/hardhat.json | 2 + .../deploy-config/mainnet.json | 6 +- 16 files changed, 246 insertions(+), 67 deletions(-) diff --git a/op-chain-ops/cmd/ecotone-scalar/main.go b/op-chain-ops/cmd/ecotone-scalar/main.go index 53ee823fc0bc..cee4f48ec9b2 100644 --- a/op-chain-ops/cmd/ecotone-scalar/main.go +++ b/op-chain-ops/cmd/ecotone-scalar/main.go @@ -1,12 +1,13 @@ package main import ( - "encoding/binary" "flag" "fmt" "math" "math/big" "os" + + "github.com/ethereum-optimism/optimism/op-service/eth" ) func main() { @@ -26,15 +27,15 @@ func main() { os.Exit(2) } - var n [32]byte - n[0] = 1 // version - binary.BigEndian.PutUint32(n[32-4:], uint32(scalar)) - binary.BigEndian.PutUint32(n[32-8:], uint32(blobScalar)) - i := new(big.Int).SetBytes(n[:]) + encoded := eth.EncodeScalar(eth.EcostoneScalars{ + BlobBaseFeeScalar: uint32(blobScalar), + BaseFeeScalar: uint32(scalar), + }) + i := new(big.Int).SetBytes(encoded[:]) fmt.Println("# base fee scalar :", scalar) fmt.Println("# blob base fee scalar:", blobScalar) - fmt.Printf("# v1 hex encoding : 0x%x\n", n[:]) + fmt.Printf("# v1 hex encoding : 0x%x\n", encoded[:]) fmt.Println("# uint value for the 'scalar' parameter in SystemConfigProxy.setGasConfig():") fmt.Println(i) } diff --git a/op-chain-ops/cmd/op-upgrade-mcp/main.go b/op-chain-ops/cmd/op-upgrade-mcp/main.go index 2dd2dc8ca90d..2da6469b8b8c 100644 --- a/op-chain-ops/cmd/op-upgrade-mcp/main.go +++ b/op-chain-ops/cmd/op-upgrade-mcp/main.go @@ -92,7 +92,7 @@ func main() { oplog.SetGlobalLogHandler(log.NewTerminalHandler(os.Stderr, color)) app := &cli.App{ - Name: "op-upgrade", + Name: "op-upgrade-mcp", Usage: "Build transactions useful for upgrading the Superchain", Flags: []cli.Flag{ &cli.StringFlag{ @@ -179,6 +179,11 @@ func entrypoint(ctx *cli.Context) error { return fmt.Errorf("no chain config for chain ID %d", l2ChainID) } + superchainConfig, ok := superchain.Superchains[chainConfig.Superchain] + if !ok { + return fmt.Errorf("no superchain config for superchain %s", chainConfig.Superchain) + } + log.Info("Upgrading to the following versions") log.Info("L1CrossDomainMessenger", "version", list.L1CrossDomainMessenger.Version, "address", list.L1CrossDomainMessenger.Address) log.Info("L1ERC721Bridge", "version", list.L1ERC721Bridge.Version, "address", list.L1ERC721Bridge.Address) @@ -193,7 +198,7 @@ func entrypoint(ctx *cli.Context) error { } // Build the batch - if err := upgrades.L1(&batch, list, *proxyAddresses, config, chainConfig, clients.L1Client); err != nil { + if err := upgrades.L1(&batch, list, *proxyAddresses, config, chainConfig, superchainConfig, clients.L1Client); err != nil { return fmt.Errorf("cannot build L1 upgrade batch: %w", err) } diff --git a/op-chain-ops/cmd/op-upgrade/main.go b/op-chain-ops/cmd/op-upgrade/main.go index 36e0d4111a01..a59d7ee0fe64 100644 --- a/op-chain-ops/cmd/op-upgrade/main.go +++ b/op-chain-ops/cmd/op-upgrade/main.go @@ -203,7 +203,9 @@ func entrypoint(ctx *cli.Context) error { } // Build the batch - if err := upgrades.L1(&batch, list, *addresses, config, chainConfig, clients.L1Client); err != nil { + // op-upgrade assumes a superchain config for L1 contract-implementations set. + // The nil superchainConfig here is a placeholder, until op-upgrade and op-upgrade-mcp are consolidated. + if err := upgrades.L1(&batch, list, *addresses, config, chainConfig, nil, clients.L1Client); err != nil { return err } } diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index badf461d21c2..3bcd2286a7c8 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" gstate "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -169,9 +170,15 @@ type DeployConfig struct { // as part of the derivation pipeline. OptimismPortalProxy common.Address `json:"optimismPortalProxy"` // GasPriceOracleOverhead represents the initial value of the gas overhead in the GasPriceOracle predeploy. + // Deprecated: Since Ecotone, this field is superseded by GasPriceOracleBaseFeeScalar and GasPriceOracleBlobBaseFeeScalar. GasPriceOracleOverhead uint64 `json:"gasPriceOracleOverhead"` // GasPriceOracleScalar represents the initial value of the gas scalar in the GasPriceOracle predeploy. + // Deprecated: Since Ecotone, this field is superseded by GasPriceOracleBaseFeeScalar and GasPriceOracleBlobBaseFeeScalar. GasPriceOracleScalar uint64 `json:"gasPriceOracleScalar"` + // GasPriceOracleBaseFeeScalar represents the value of the base fee scalar used for fee calculations. + GasPriceOracleBaseFeeScalar uint32 `json:"gasPriceOracleBaseFeeScalar"` + // GasPriceOracleBlobBaseFeeScalar represents the value of the blob base fee scalar used for fee calculations. + GasPriceOracleBlobBaseFeeScalar uint32 `json:"gasPriceOracleBlobBaseFeeScalar"` // EnableGovernance configures whether or not include governance token predeploy. EnableGovernance bool `json:"enableGovernance"` // GovernanceTokenSymbol represents the ERC20 symbol of the GovernanceToken. @@ -356,7 +363,13 @@ func (d *DeployConfig) Check() error { log.Warn("GasPriceOracleOverhead is 0") } if d.GasPriceOracleScalar == 0 { - return fmt.Errorf("%w: GasPriceOracleScalar cannot be 0", ErrInvalidDeployConfig) + log.Warn("GasPriceOracleScalar is 0") + } + if d.GasPriceOracleBaseFeeScalar == 0 { + log.Warn("GasPriceOracleBaseFeeScalar is 0") + } + if d.GasPriceOracleBlobBaseFeeScalar == 0 { + log.Warn("GasPriceOracleBlobBaseFeeScalar is 0") } if d.EIP1559Denominator == 0 { return fmt.Errorf("%w: EIP1559Denominator cannot be 0", ErrInvalidDeployConfig) @@ -447,6 +460,18 @@ func (d *DeployConfig) Check() error { return nil } +// FeeScalar returns the raw serialized fee scalar. Uses pre-Ecotone if legacy config is present, +// otherwise uses the post-Ecotone scalar serialization. +func (d *DeployConfig) FeeScalar() [32]byte { + if d.GasPriceOracleScalar != 0 { + return common.BigToHash(big.NewInt(int64(d.GasPriceOracleScalar))) + } + return eth.EncodeScalar(eth.EcostoneScalars{ + BlobBaseFeeScalar: d.GasPriceOracleBlobBaseFeeScalar, + BaseFeeScalar: d.GasPriceOracleBaseFeeScalar, + }) +} + // CheckAddresses will return an error if the addresses are not set. // These values are required to create the L2 genesis state and are present in the deploy config // even though the deploy config is required to deploy the contracts on L1. This creates a @@ -573,7 +598,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas SystemConfig: eth.SystemConfig{ BatcherAddr: d.BatchSenderAddress, Overhead: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(d.GasPriceOracleOverhead))), - Scalar: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(d.GasPriceOracleScalar))), + Scalar: eth.Bytes32(d.FeeScalar()), GasLimit: uint64(d.L2GenesisBlockGasLimit), }, }, @@ -874,7 +899,7 @@ func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (*immutables return &cfg, nil } -// NewL2StorageConfig will create a StorageConfig given an instance of a DeployConfig and genesis block. +// NewL2StorageConfig will create a StorageConfig given an instance of a DeployConfig and genesis L1 anchor block. func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.StorageConfig, error) { storage := make(state.StorageConfig) @@ -912,15 +937,24 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage "_initializing": false, "bridge": predeploys.L2StandardBridgeAddr, } + + excessBlobGas := block.ExcessBlobGas() + if excessBlobGas == nil { + excessBlobGas = u64ptr(0) + } + storage["L1Block"] = state.StorageValues{ - "number": block.Number(), - "timestamp": block.Time(), - "basefee": block.BaseFee(), - "hash": block.Hash(), - "sequenceNumber": 0, - "batcherHash": eth.AddressAsLeftPaddedHash(config.BatchSenderAddress), - "l1FeeOverhead": config.GasPriceOracleOverhead, - "l1FeeScalar": config.GasPriceOracleScalar, + "number": block.Number(), + "timestamp": block.Time(), + "basefee": block.BaseFee(), + "hash": block.Hash(), + "sequenceNumber": 0, + "blobBaseFeeScalar": config.GasPriceOracleBlobBaseFeeScalar, + "baseFeeScalar": config.GasPriceOracleBaseFeeScalar, + "batcherHash": eth.AddressAsLeftPaddedHash(config.BatchSenderAddress), + "l1FeeOverhead": config.GasPriceOracleOverhead, + "l1FeeScalar": config.GasPriceOracleScalar, + "blobBaseFee": eip4844.CalcBlobFee(*excessBlobGas), } storage["LegacyERC20ETH"] = state.StorageValues{ "_name": "Ether", diff --git a/op-chain-ops/genesis/layer_two_test.go b/op-chain-ops/genesis/layer_two_test.go index 4115b752baf1..7d724a29bfcb 100644 --- a/op-chain-ops/genesis/layer_two_test.go +++ b/op-chain-ops/genesis/layer_two_test.go @@ -10,15 +10,19 @@ import ( "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" ) var writeFile bool @@ -47,9 +51,52 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi proxyBytecode, err := bindings.GetDeployedBytecode("Proxy") require.NoError(t, err) + // for simulation we need a regular EVM, not with system-deposit information. + chainConfig := params.ChainConfig{ + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + // Activated proof of stake. We manually build/commit blocks in the simulator anyway, + // and the timestamp verification of PoS is not against the wallclock, + // preventing blocks from getting stuck temporarily in the future-blocks queue, decreasing setup time a lot. + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + TerminalTotalDifficultyPassed: true, + ShanghaiTime: new(uint64), + } + + // Apply the genesis to the backend + cfg := ethconfig.Defaults + cfg.Preimages = true + cfg.Genesis = &core.Genesis{ + Config: &chainConfig, + Timestamp: 1234567, + Difficulty: big.NewInt(0), + Alloc: gen.Alloc, + GasLimit: 30_000_000, + } + backend = backends.NewSimulatedBackendFromConfig(cfg) + for name, predeploy := range predeploys.Predeploys { addr := predeploy.Address + if addr == predeploys.L1BlockAddr { + testL1Block(t, backend, config, block) + } + account, ok := gen.Alloc[addr] require.Equal(t, true, ok, name) require.Greater(t, len(account.Code), 0) @@ -84,6 +131,59 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi return gen } +// testL1Block tests that the state is set correctly in the L1Block predeploy +func testL1Block(t *testing.T, caller bind.ContractCaller, config *genesis.DeployConfig, block *types.Block) { + contract, err := bindings.NewL1BlockCaller(predeploys.L1BlockAddr, caller) + require.NoError(t, err) + + number, err := contract.Number(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, block.Number().Uint64(), number) + + timestamp, err := contract.Timestamp(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, block.Time(), timestamp) + + basefee, err := contract.Basefee(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, block.BaseFee(), basefee) + + hash, err := contract.Hash(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, block.Hash(), common.Hash(hash)) + + sequenceNumber, err := contract.SequenceNumber(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, uint64(0), sequenceNumber) + + blobBaseFeeScalar, err := contract.BlobBaseFeeScalar(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, config.GasPriceOracleBlobBaseFeeScalar, blobBaseFeeScalar) + + baseFeeScalar, err := contract.BaseFeeScalar(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, config.GasPriceOracleBaseFeeScalar, baseFeeScalar) + + batcherHeader, err := contract.BatcherHash(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, eth.AddressAsLeftPaddedHash(config.BatchSenderAddress), common.Hash(batcherHeader)) + + l1FeeOverhead, err := contract.L1FeeOverhead(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, config.GasPriceOracleOverhead, l1FeeOverhead.Uint64()) + + l1FeeScalar, err := contract.L1FeeScalar(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, config.GasPriceOracleScalar, l1FeeScalar.Uint64()) + + blobBaseFee, err := contract.BlobBaseFee(&bind.CallOpts{}) + require.NoError(t, err) + if excessBlobGas := block.ExcessBlobGas(); excessBlobGas != nil { + require.Equal(t, uint64(0), *excessBlobGas) + } + require.Equal(t, big.NewInt(1), blobBaseFee) +} + func TestBuildL2MainnetGenesis(t *testing.T) { config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json") require.Nil(t, err) diff --git a/op-chain-ops/genesis/testdata/test-deploy-config-full.json b/op-chain-ops/genesis/testdata/test-deploy-config-full.json index 65390c661acc..37f4bb83be65 100644 --- a/op-chain-ops/genesis/testdata/test-deploy-config-full.json +++ b/op-chain-ops/genesis/testdata/test-deploy-config-full.json @@ -54,6 +54,8 @@ "systemConfigProxy": "0x4200000000000000000000000000000000000061", "optimismPortalProxy": "0x4200000000000000000000000000000000000062", "proxyAdminOwner": "0x0000000000000000000000000000000000000222", + "gasPriceOracleBaseFeeScalar": 0, + "gasPriceOracleBlobBaseFeeScalar": 0, "gasPriceOracleOverhead": 2100, "gasPriceOracleScalar": 1000000, "enableGovernance": true, diff --git a/op-chain-ops/upgrades/l1.go b/op-chain-ops/upgrades/l1.go index 56383710e919..053b7a8a5ceb 100644 --- a/op-chain-ops/upgrades/l1.go +++ b/op-chain-ops/upgrades/l1.go @@ -26,41 +26,36 @@ const ( var ( // storageSetterAddr represents the address of the StorageSetter contract. storageSetterAddr = common.HexToAddress("0xd81f43eDBCAcb4c29a9bA38a13Ee5d79278270cC") - - // superchainConfigProxy refers to the address of the Sepolia superchain config proxy. - // NOTE: this is currently hardcoded and we will need to move this to the superchain-registry - // and have 1 deployed for each superchain target. - superchainConfigProxy = common.HexToAddress("0xC2Be75506d5724086DEB7245bd260Cc9753911Be") ) // L1 will add calls for upgrading each of the L1 contracts. -func L1(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { - if err := L1CrossDomainMessenger(batch, implementations, list, config, chainConfig, backend); err != nil { +func L1(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { + if err := L1CrossDomainMessenger(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading L1CrossDomainMessenger: %w", err) } - if err := L1ERC721Bridge(batch, implementations, list, config, chainConfig, backend); err != nil { + if err := L1ERC721Bridge(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading L1ERC721Bridge: %w", err) } - if err := L1StandardBridge(batch, implementations, list, config, chainConfig, backend); err != nil { + if err := L1StandardBridge(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading L1StandardBridge: %w", err) } - if err := L2OutputOracle(batch, implementations, list, config, chainConfig, backend); err != nil { + if err := L2OutputOracle(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading L2OutputOracle: %w", err) } - if err := OptimismMintableERC20Factory(batch, implementations, list, config, chainConfig, backend); err != nil { + if err := OptimismMintableERC20Factory(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading OptimismMintableERC20Factory: %w", err) } - if err := OptimismPortal(batch, implementations, list, config, chainConfig, backend); err != nil { + if err := OptimismPortal(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading OptimismPortal: %w", err) } - if err := SystemConfig(batch, implementations, list, config, chainConfig, backend); err != nil { + if err := SystemConfig(batch, implementations, list, config, chainConfig, superchainConfig, backend); err != nil { return fmt.Errorf("upgrading SystemConfig: %w", err) } return nil } // L1CrossDomainMessenger will add a call to the batch that upgrades the L1CrossDomainMessenger. -func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err @@ -122,7 +117,7 @@ func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.Implem return fmt.Errorf("OtherMessenger address doesn't match config") } - calldata, err := l1CrossDomainMessengerABI.Pack("initialize", superchainConfigProxy, optimismPortal) + calldata, err := l1CrossDomainMessengerABI.Pack("initialize", common.Address(*superchainConfig.Config.SuperchainConfigAddr), optimismPortal) if err != nil { return err } @@ -142,7 +137,7 @@ func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.Implem } // L1ERC721Bridge will add a call to the batch that upgrades the L1ERC721Bridge. -func L1ERC721Bridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func L1ERC721Bridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err @@ -204,7 +199,7 @@ func L1ERC721Bridge(batch *safe.Batch, implementations superchain.Implementation return fmt.Errorf("OtherBridge address doesn't match config") } - calldata, err := l1ERC721BridgeABI.Pack("initialize", messenger, superchainConfigProxy) + calldata, err := l1ERC721BridgeABI.Pack("initialize", messenger, common.Address(*(superchainConfig.Config.SuperchainConfigAddr))) if err != nil { return err } @@ -224,7 +219,7 @@ func L1ERC721Bridge(batch *safe.Batch, implementations superchain.Implementation } // L1StandardBridge will add a call to the batch that upgrades the L1StandardBridge. -func L1StandardBridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func L1StandardBridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err @@ -288,7 +283,7 @@ func L1StandardBridge(batch *safe.Batch, implementations superchain.Implementati return fmt.Errorf("OtherBridge address doesn't match config") } - calldata, err := l1StandardBridgeABI.Pack("initialize", messenger, superchainConfigProxy) + calldata, err := l1StandardBridgeABI.Pack("initialize", messenger, common.Address(*(superchainConfig.Config.SuperchainConfigAddr))) if err != nil { return err } @@ -308,7 +303,7 @@ func L1StandardBridge(batch *safe.Batch, implementations superchain.Implementati } // L2OutputOracle will add a call to the batch that upgrades the L2OutputOracle. -func L2OutputOracle(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func L2OutputOracle(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err @@ -452,7 +447,7 @@ func L2OutputOracle(batch *safe.Batch, implementations superchain.Implementation } // OptimismMintableERC20Factory will add a call to the batch that upgrades the OptimismMintableERC20Factory. -func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err @@ -527,7 +522,7 @@ func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain. } // OptimismPortal will add a call to the batch that upgrades the OptimismPortal. -func OptimismPortal(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func OptimismPortal(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err @@ -589,7 +584,7 @@ func OptimismPortal(batch *safe.Batch, implementations superchain.Implementation return fmt.Errorf("SystemConfig address doesn't match config") } - calldata, err := optimismPortalABI.Pack("initialize", l2OutputOracle, systemConfig, superchainConfigProxy) + calldata, err := optimismPortalABI.Pack("initialize", l2OutputOracle, systemConfig, common.Address(*superchainConfig.Config.SuperchainConfigAddr)) if err != nil { return err } @@ -609,7 +604,7 @@ func OptimismPortal(batch *safe.Batch, implementations superchain.Implementation } // SystemConfig will add a call to the batch that upgrades the SystemConfig. -func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error { +func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, superchainConfig *superchain.Superchain, backend bind.ContractBackend) error { proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() if err != nil { return err diff --git a/op-e2e/e2eutils/setup.go b/op-e2e/e2eutils/setup.go index a417060a7399..912e6f558f99 100644 --- a/op-e2e/e2eutils/setup.go +++ b/op-e2e/e2eutils/setup.go @@ -188,7 +188,7 @@ func SystemConfigFromDeployConfig(deployConfig *genesis.DeployConfig) eth.System return eth.SystemConfig{ BatcherAddr: deployConfig.BatchSenderAddress, Overhead: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(deployConfig.GasPriceOracleOverhead))), - Scalar: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(deployConfig.GasPriceOracleScalar))), + Scalar: eth.Bytes32(deployConfig.FeeScalar()), GasLimit: uint64(deployConfig.L2GenesisBlockGasLimit), } } diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index aea62345e6de..626ddab4d18a 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -1004,10 +1004,10 @@ func TestL1InfoContract(t *testing.T) { BatcherAddr: sys.RollupConfig.Genesis.SystemConfig.BatcherAddr, } if sys.RollupConfig.IsEcotone(b.Time()) && !sys.RollupConfig.IsEcotoneActivationBlock(b.Time()) { - blobBaseFeeScalar, baseFeeScalar, err := sys.RollupConfig.Genesis.SystemConfig.EcotoneScalars() + scalars, err := sys.RollupConfig.Genesis.SystemConfig.EcotoneScalars() require.NoError(t, err) - l1blocks[h].BlobBaseFeeScalar = blobBaseFeeScalar - l1blocks[h].BaseFeeScalar = baseFeeScalar + l1blocks[h].BlobBaseFeeScalar = scalars.BlobBaseFeeScalar + l1blocks[h].BaseFeeScalar = scalars.BaseFeeScalar if excess := b.ExcessBlobGas(); excess != nil { l1blocks[h].BlobBaseFee = eip4844.CalcBlobFee(*excess) } else { diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index 4546c95bdb81..54788ba495cd 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -276,12 +276,12 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber // The L2 spec states to use the MIN_BLOB_GASPRICE from EIP-4844 if not yet active on L1. l1BlockInfo.BlobBaseFee = big.NewInt(1) } - blobBaseFeeScalar, baseFeeScalar, err := sysCfg.EcotoneScalars() + scalars, err := sysCfg.EcotoneScalars() if err != nil { return nil, err } - l1BlockInfo.BlobBaseFeeScalar = blobBaseFeeScalar - l1BlockInfo.BaseFeeScalar = baseFeeScalar + l1BlockInfo.BlobBaseFeeScalar = scalars.BlobBaseFeeScalar + l1BlockInfo.BaseFeeScalar = scalars.BaseFeeScalar out, err := l1BlockInfo.marshalBinaryEcotone() if err != nil { return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) diff --git a/op-service/Makefile b/op-service/Makefile index d458b3c15aa5..17766f712264 100644 --- a/op-service/Makefile +++ b/op-service/Makefile @@ -12,6 +12,7 @@ fuzz: go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzOBP01 ./eth go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzEncodeDecodeBlob ./eth go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDetectNonBijectivity ./eth + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzEncodeScalar ./eth .PHONY: \ test \ diff --git a/op-service/eth/blobs_api_test.go b/op-service/eth/blobs_api_test.go index ca8a15766ad8..f7439ff75a9c 100644 --- a/op-service/eth/blobs_api_test.go +++ b/op-service/eth/blobs_api_test.go @@ -7,8 +7,9 @@ import ( "reflect" "testing" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-service/eth" ) type dataJson struct { diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 58d16c3cdd22..e453167bab75 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -393,25 +393,48 @@ const ( L1ScalarEcotone = byte(1) ) -func (sysCfg *SystemConfig) EcotoneScalars() (blobBaseFeeScalar, baseFeeScalar uint32, err error) { +type EcostoneScalars struct { + BlobBaseFeeScalar uint32 + BaseFeeScalar uint32 +} + +func (sysCfg *SystemConfig) EcotoneScalars() (EcostoneScalars, error) { if err := CheckEcotoneL1SystemConfigScalar(sysCfg.Scalar); err != nil { if errors.Is(err, ErrBedrockScalarPaddingNotEmpty) { // L2 spec mandates we set baseFeeScalar to MaxUint32 if there are non-zero bytes in // the padding area. - return 0, math.MaxUint32, nil + return EcostoneScalars{BlobBaseFeeScalar: 0, BaseFeeScalar: math.MaxUint32}, nil } - return 0, 0, err + return EcostoneScalars{}, err } - switch sysCfg.Scalar[0] { + return DecodeScalar(sysCfg.Scalar) +} + +// DecodeScalar decodes the blobBaseFeeScalar and baseFeeScalar from a 32-byte scalar value. +// It uses the first byte to determine the scalar format. +func DecodeScalar(scalar [32]byte) (EcostoneScalars, error) { + switch scalar[0] { case L1ScalarBedrock: - blobBaseFeeScalar = 0 - baseFeeScalar = binary.BigEndian.Uint32(sysCfg.Scalar[28:32]) + return EcostoneScalars{ + BlobBaseFeeScalar: 0, + BaseFeeScalar: binary.BigEndian.Uint32(scalar[28:32]), + }, nil case L1ScalarEcotone: - blobBaseFeeScalar = binary.BigEndian.Uint32(sysCfg.Scalar[24:28]) - baseFeeScalar = binary.BigEndian.Uint32(sysCfg.Scalar[28:32]) + return EcostoneScalars{ + BlobBaseFeeScalar: binary.BigEndian.Uint32(scalar[24:28]), + BaseFeeScalar: binary.BigEndian.Uint32(scalar[28:32]), + }, nil default: - err = fmt.Errorf("unexpected system config scalar: %s", sysCfg.Scalar) + return EcostoneScalars{}, fmt.Errorf("unexpected system config scalar: %s", scalar) } +} + +// EncodeScalar encodes the EcostoneScalars into a 32-byte scalar value +// for the Ecotone serialization format. +func EncodeScalar(scalars EcostoneScalars) (scalar [32]byte) { + scalar[0] = L1ScalarEcotone + binary.BigEndian.PutUint32(scalar[24:28], scalars.BlobBaseFeeScalar) + binary.BigEndian.PutUint32(scalar[28:32], scalars.BaseFeeScalar) return } diff --git a/op-service/eth/types_test.go b/op-service/eth/types_test.go index 16f98d7b9393..53a21332701c 100644 --- a/op-service/eth/types_test.go +++ b/op-service/eth/types_test.go @@ -45,13 +45,24 @@ func TestEcotoneScalars(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { sysConfig := SystemConfig{Scalar: tc.val} - blobScalar, regScalar, err := sysConfig.EcotoneScalars() + scalars, err := sysConfig.EcotoneScalars() if tc.fail { require.NotNil(t, err) } else { - require.Equal(t, tc.blobBaseFeeScalar, blobScalar) - require.Equal(t, tc.baseFeeScalar, regScalar) + require.Equal(t, tc.blobBaseFeeScalar, scalars.BlobBaseFeeScalar) + require.Equal(t, tc.baseFeeScalar, scalars.BaseFeeScalar) + require.NoError(t, err) } }) } } + +func FuzzEncodeScalar(f *testing.F) { + f.Fuzz(func(t *testing.T, blobBaseFeeScalar uint32, baseFeeScalar uint32) { + encoded := EncodeScalar(EcostoneScalars{BlobBaseFeeScalar: blobBaseFeeScalar, BaseFeeScalar: baseFeeScalar}) + scalars, err := DecodeScalar(encoded) + require.NoError(t, err) + require.Equal(t, blobBaseFeeScalar, scalars.BlobBaseFeeScalar) + require.Equal(t, baseFeeScalar, scalars.BaseFeeScalar) + }) +} diff --git a/packages/contracts-bedrock/deploy-config/hardhat.json b/packages/contracts-bedrock/deploy-config/hardhat.json index aa9cc17b9448..90bd07ff7a3b 100644 --- a/packages/contracts-bedrock/deploy-config/hardhat.json +++ b/packages/contracts-bedrock/deploy-config/hardhat.json @@ -18,6 +18,8 @@ "l2OutputOracleStartingBlockNumber": 1, "gasPriceOracleOverhead": 2100, "gasPriceOracleScalar": 1000000, + "gasPriceOracleBaseFeeScalar": 1368, + "gasPriceOracleBlobBaseFeeScalar": 810949, "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleChallenger": "0x6925B8704Ff96DEe942623d6FB5e946EF5884b63", "l2GenesisBlockBaseFeePerGas": "0x3B9ACA00", diff --git a/packages/contracts-bedrock/deploy-config/mainnet.json b/packages/contracts-bedrock/deploy-config/mainnet.json index a6be1d7cf9ec..900c5a02e2c6 100644 --- a/packages/contracts-bedrock/deploy-config/mainnet.json +++ b/packages/contracts-bedrock/deploy-config/mainnet.json @@ -34,8 +34,10 @@ "governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005", "l2GenesisBlockGasLimit": "0x1c9c380", "l2GenesisBlockBaseFeePerGas": "0x3b9aca00", - "gasPriceOracleOverhead": 188, - "gasPriceOracleScalar": 684000, + "gasPriceOracleOverhead": 0, + "gasPriceOracleScalar": 0, + "gasPriceOracleBaseFeeScalar": 1368, + "gasPriceOracleBlobBaseFeeScalar": 810949, "eip1559Denominator": 50, "eip1559Elasticity": 6, "l2GenesisRegolithTimeOffset": "0x0",