Commit 8167f363 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

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: default avatarSebastian Stammler <seb@oplabs.co>

* deploy-config: use mainnet values
Co-authored-by: default avatarSebastian Stammler <seb@oplabs.co>

* deploy-config: use mainnet values
Co-authored-by: default avatarSebastian Stammler <seb@oplabs.co>

* 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: default avatarSebastian Stammler <seb@oplabs.co>
Co-authored-by: default avatarprotolambda <proto@protolambda.com>
parent 35c0ad85
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)
}
......@@ -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)
}
......
......@@ -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
}
}
......
......@@ -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",
......
......@@ -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)
......
......@@ -54,6 +54,8 @@
"systemConfigProxy": "0x4200000000000000000000000000000000000061",
"optimismPortalProxy": "0x4200000000000000000000000000000000000062",
"proxyAdminOwner": "0x0000000000000000000000000000000000000222",
"gasPriceOracleBaseFeeScalar": 0,
"gasPriceOracleBlobBaseFeeScalar": 0,
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"enableGovernance": true,
......
......@@ -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
......
......@@ -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),
}
}
......
......@@ -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 {
......
......@@ -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)
......
......@@ -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 \
......
......@@ -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 {
......
......@@ -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
}
......
......@@ -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)
})
}
......@@ -18,6 +18,8 @@
"l2OutputOracleStartingBlockNumber": 1,
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"gasPriceOracleBaseFeeScalar": 1368,
"gasPriceOracleBlobBaseFeeScalar": 810949,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x6925B8704Ff96DEe942623d6FB5e946EF5884b63",
"l2GenesisBlockBaseFeePerGas": "0x3B9ACA00",
......
......@@ -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",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment