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",