Commit 9300db95 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

contracts-bedrock: ecotone gas config support (#10226)

* contracts-bedrock: ecotone gas config support

Updates the gas config on the `SystemConfig` to be ecotone first.
This resolves chain operator ux issues with setting the gas config,
rendering the additional tooling for calculating the encoded
scalar version obsolete. This version of the system config
is technically backwards compatible with pre-ecotone, but it
requires a follow up tx to call the old `setGasConfig` method.
The old gas config method was updated such that it cannot
be used to set ecotone style config to prevent footguns.
A new method `setGasConfigEcotone` should be used instead.

* tests: fix build

* config: fixup

* deploy-config: fixup

* chain-assertions: improve

* lint: fix

* scripts: remove overhead and scalar config

Reduces tech debt

* contracts-bedrock: fix build

* snapshots: update

* gas-snapshot: update

* contracts-bedrock: fix tests

* semver-lock: regenerate

* build: fix

* build: fix

* e2e: fix build

* config: fix test

* op-e2e: cleanup

Still support pre-ecotone tests

* test: fix

* kontrol: fixup

* op-e2e: turn on ecotone

* ecotone: fix test

* op-e2e: fix test

* op-e2e: fix failing test

* op-e2e: update tests

* deps: update

* kontrol: update

* semver-lock: update

* snapshots: update after version change

* typo: fix
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>

---------
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>
parent d2787077
......@@ -383,12 +383,6 @@ func (d *DeployConfig) Check() error {
if !d.SequencerFeeVaultWithdrawalNetwork.Valid() {
return fmt.Errorf("%w: SequencerFeeVaultWithdrawalNetwork can only be 0 (L1) or 1 (L2)", ErrInvalidDeployConfig)
}
if d.GasPriceOracleOverhead == 0 {
log.Warn("GasPriceOracleOverhead is 0")
}
if d.GasPriceOracleScalar == 0 {
log.Warn("GasPriceOracleScalar is 0")
}
if d.GasPriceOracleBaseFeeScalar == 0 {
log.Warn("GasPriceOracleBaseFeeScalar is 0")
}
......@@ -606,7 +600,8 @@ func (d *DeployConfig) InteropTime(genesisTime uint64) *uint64 {
return &v
}
// RollupConfig converts a DeployConfig to a rollup.Config
// RollupConfig converts a DeployConfig to a rollup.Config. If Ecotone is active at genesis, the
// Overhead value is considered a noop.
func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHash common.Hash, l2GenesisBlockNumber uint64) (*rollup.Config, error) {
if d.OptimismPortalProxy == (common.Address{}) {
return nil, errors.New("OptimismPortalProxy cannot be address(0)")
......
......@@ -700,12 +700,6 @@ func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationLi
}
if config != nil {
if gasPriceOracleOverhead.Uint64() != config.GasPriceOracleOverhead {
return fmt.Errorf("GasPriceOracleOverhead address doesn't match config")
}
if gasPriceOracleScalar.Uint64() != config.GasPriceOracleScalar {
return fmt.Errorf("GasPriceOracleScalar address doesn't match config")
}
if batcherHash != common.BytesToHash(config.BatchSenderAddress.Bytes()) {
return fmt.Errorf("BatchSenderAddress address doesn't match config")
}
......
......@@ -74,7 +74,8 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) {
scalar, err := gasPriceOracle.Scalar(nil)
require.NoError(t, err)
require.True(t, scalar.Cmp(big.NewInt(0)) > 0, "scalar must start non-zero")
require.True(t, scalar.Cmp(new(big.Int).SetUint64(dp.DeployConfig.GasPriceOracleScalar)) == 0, "must match deploy config")
feeScalar := dp.DeployConfig.FeeScalar()
require.Equal(t, scalar, new(big.Int).SetBytes(feeScalar[:]), "must match deploy config")
// Get current implementations addresses (by slot) for L1Block + GasPriceOracle
initialGasPriceOracleAddress, err := ethCl.StorageAt(context.Background(), predeploys.GasPriceOracleAddr, genesis.ImplementationSlot, nil)
......@@ -193,7 +194,7 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) {
// test if the migrated scalar matches the deploy config
basefeeScalar, err := gasPriceOracle.BaseFeeScalar(nil)
require.NoError(t, err)
require.True(t, uint64(basefeeScalar) == dp.DeployConfig.GasPriceOracleScalar, "must match deploy config")
require.Equal(t, uint64(basefeeScalar), dp.DeployConfig.GasPriceOracleScalar, "must match deploy config")
cost, err = gasPriceOracle.GetL1Fee(nil, []byte{0, 1, 2, 3, 4})
require.NoError(t, err)
......
This diff is collapsed.
......@@ -14,6 +14,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
......@@ -23,6 +24,12 @@ func TestCustomGasToken(t *testing.T) {
InitParallel(t, SkipOnFPAC) // Custom Gas Token feature is not yet compatible with FPAC
cfg := DefaultSystemConfig(t)
offset := hexutil.Uint64(0)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = &offset
cfg.DeployConfig.L1CancunTimeOffset = &offset
cfg.DeployConfig.L2GenesisCanyonTimeOffset = &offset
cfg.DeployConfig.L2GenesisDeltaTimeOffset = &offset
cfg.DeployConfig.L2GenesisEcotoneTimeOffset = &offset
sys, err := cfg.Start(t)
require.NoError(t, err, "Error starting up system")
......@@ -324,9 +331,9 @@ func setCustomGasToken(t *testing.T, cfg SystemConfig, sys *System, cgtAddress c
// Get existing parameters from SystemConfigProxy contract
owner, err := systemConfig.Owner(&bind.CallOpts{})
require.NoError(t, err)
overhead, err := systemConfig.Overhead(&bind.CallOpts{})
basefeeScalar, err := systemConfig.BasefeeScalar(&bind.CallOpts{})
require.NoError(t, err)
scalar, err := systemConfig.Scalar(&bind.CallOpts{})
blobbasefeeScalar, err := systemConfig.BlobbasefeeScalar(&bind.CallOpts{})
require.NoError(t, err)
batcherHash, err := systemConfig.BatcherHash(&bind.CallOpts{})
require.NoError(t, err)
......@@ -406,8 +413,8 @@ func setCustomGasToken(t *testing.T, cfg SystemConfig, sys *System, cgtAddress c
// Reinitialise with existing initializer values but with custom gas token set
tx, err = systemConfig.Initialize(deployerOpts, owner,
overhead,
scalar,
basefeeScalar,
blobbasefeeScalar,
batcherHash,
gasLimit,
unsafeBlockSigner,
......
......@@ -1266,7 +1266,8 @@ func testFees(t *testing.T, cfg SystemConfig) {
scalar, err := gpoContract.Scalar(&bind.CallOpts{})
require.Nil(t, err, "reading gpo scalar")
require.Equal(t, scalar.Uint64(), cfg.DeployConfig.GasPriceOracleScalar, "wrong gpo scalar")
feeScalar := cfg.DeployConfig.FeeScalar()
require.Equal(t, scalar, new(big.Int).SetBytes(feeScalar[:]), "wrong gpo scalar")
} else {
_, err := gpoContract.Overhead(&bind.CallOpts{})
require.ErrorContains(t, err, "deprecated")
......
......@@ -43,8 +43,9 @@ func TestGasPriceOracleFeeUpdates(t *testing.T) {
InitParallel(t)
// Define our values to set in the GasPriceOracle (we set them high to see if it can lock L2 or stop bindings
// from updating the prices once again.
overheadValue := abi.MaxUint256
scalarValue := abi.MaxUint256
overheadValue := new(big.Int).Set(abi.MaxUint256)
// Ensure the most significant byte is 0x00
scalarValue := new(big.Int).Rsh(new(big.Int).Set(abi.MaxUint256), 8)
var cancel context.CancelFunc
// Create our system configuration for L1/L2 and start it
......
......@@ -25,7 +25,6 @@ var (
ErrGenesisHashesSame = errors.New("achievement get! rollup inception: L1 and L2 genesis cannot be the same")
ErrMissingGenesisL2Time = errors.New("missing L2 genesis time")
ErrMissingBatcherAddr = errors.New("missing genesis system config batcher address")
ErrMissingOverhead = errors.New("missing genesis system config overhead")
ErrMissingScalar = errors.New("missing genesis system config scalar")
ErrMissingGasLimit = errors.New("missing genesis system config gas limit")
ErrMissingBatchInboxAddress = errors.New("missing batch inbox address")
......@@ -278,9 +277,6 @@ func (cfg *Config) Check() error {
if cfg.Genesis.SystemConfig.BatcherAddr == (common.Address{}) {
return ErrMissingBatcherAddr
}
if cfg.Genesis.SystemConfig.Overhead == (eth.Bytes32{}) {
return ErrMissingOverhead
}
if cfg.Genesis.SystemConfig.Scalar == (eth.Bytes32{}) {
return ErrMissingScalar
}
......
......@@ -405,11 +405,6 @@ func TestConfig_Check(t *testing.T) {
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.BatcherAddr = common.Address{} },
expectedErr: ErrMissingBatcherAddr,
},
{
name: "NoOverhead",
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.Overhead = eth.Bytes32{} },
expectedErr: ErrMissingOverhead,
},
{
name: "NoScalar",
modifier: func(cfg *Config) { cfg.Genesis.SystemConfig.Scalar = eth.Bytes32{} },
......
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369357)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967497)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 561969)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4074012)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 466924)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512606)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369380)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967520)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 561992)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4074035)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 466947)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512629)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72629)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68410)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68880)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68433)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68903)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155618)
\ No newline at end of file
......@@ -35,6 +35,8 @@
"l2GenesisBlockBaseFeePerGas": "0x1",
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"gasPriceOracleBaseFeeScalar": 1368,
"gasPriceOracleBlobBaseFeeScalar": 810949,
"enableGovernance": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
......
......@@ -16,8 +16,6 @@
"l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": 1,
"l2OutputOracleStartingBlockNumber": 1,
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"gasPriceOracleBaseFeeScalar": 1368,
"gasPriceOracleBlobBaseFeeScalar": 810949,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
......
......@@ -34,8 +34,6 @@
"governanceTokenOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
"l2GenesisBlockGasLimit": "0x17D7840",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"eip1559Denominator": 50,
"eip1559Elasticity": 10,
"systemConfigStartBlock": 8364212,
......
......@@ -34,8 +34,6 @@
"governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005",
"l2GenesisBlockGasLimit": "0x1c9c380",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"gasPriceOracleOverhead": 0,
"gasPriceOracleScalar": 0,
"gasPriceOracleBaseFeeScalar": 1368,
"gasPriceOracleBlobBaseFeeScalar": 810949,
"eip1559Denominator": 50,
......
......@@ -57,8 +57,6 @@
"l1ERC721BridgeProxy": "0x0000000000000000000000000000000000000000",
"systemConfigProxy": "0x0000000000000000000000000000000000000000",
"optimismPortalProxy": "0x0000000000000000000000000000000000000000",
"gasPriceOracleOverhead": 188,
"gasPriceOracleScalar": 684000,
"enableGovernance": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
......
......@@ -28,8 +28,6 @@
"baseFeeVaultWithdrawalNetwork": 0,
"l1FeeVaultWithdrawalNetwork": 0,
"sequencerFeeVaultWithdrawalNetwork": 0,
"gasPriceOracleOverhead": 188,
"gasPriceOracleScalar": 684000,
"enableGovernance": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
......
......@@ -71,11 +71,12 @@ library ChainAssertions {
if (_isProxy) {
require(config.owner() == _cfg.finalSystemOwner());
require(config.overhead() == _cfg.gasPriceOracleOverhead());
require(config.scalar() == _cfg.gasPriceOracleScalar());
require(config.basefeeScalar() == _cfg.basefeeScalar());
require(config.blobbasefeeScalar() == _cfg.blobbasefeeScalar());
require(config.batcherHash() == bytes32(uint256(uint160(_cfg.batchSenderAddress()))));
require(config.gasLimit() == uint64(_cfg.l2GenesisBlockGasLimit()));
require(config.unsafeBlockSigner() == _cfg.p2pSequencerAddress());
require(config.scalar() >> 248 == 1);
// Check _config
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit);
......@@ -98,7 +99,9 @@ library ChainAssertions {
} else {
require(config.owner() == address(0xdead));
require(config.overhead() == 0);
require(config.scalar() == 0);
require(config.scalar() == uint256(0x01) << 248); // version 1
require(config.basefeeScalar() == 0);
require(config.blobbasefeeScalar() == 0);
require(config.batcherHash() == bytes32(0));
require(config.gasLimit() == 1);
require(config.unsafeBlockSigner() == address(0));
......
......@@ -985,8 +985,8 @@ contract Deploy is Deployer {
SystemConfig.initialize,
(
cfg.finalSystemOwner(),
cfg.gasPriceOracleOverhead(),
cfg.gasPriceOracleScalar(),
cfg.basefeeScalar(),
cfg.blobbasefeeScalar(),
batcherHash,
uint64(cfg.l2GenesisBlockGasLimit()),
cfg.p2pSequencerAddress(),
......
......@@ -46,8 +46,8 @@ contract DeployConfig is Script {
string public governanceTokenSymbol;
address public governanceTokenOwner;
uint256 public l2GenesisBlockGasLimit;
uint256 public gasPriceOracleOverhead;
uint256 public gasPriceOracleScalar;
uint32 public basefeeScalar;
uint32 public blobbasefeeScalar;
bool public enableGovernance;
uint256 public eip1559Denominator;
uint256 public eip1559Elasticity;
......@@ -119,8 +119,9 @@ contract DeployConfig is Script {
governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol");
governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner");
l2GenesisBlockGasLimit = stdJson.readUint(_json, "$.l2GenesisBlockGasLimit");
gasPriceOracleOverhead = stdJson.readUint(_json, "$.gasPriceOracleOverhead");
gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar");
basefeeScalar = uint32(_readOr(_json, "$.gasPriceOracleBaseFeeScalar", 1368));
blobbasefeeScalar = uint32(_readOr(_json, "$.gasPriceOracleBlobBaseFeeScalar", 810949));
enableGovernance = stdJson.readBool(_json, "$.enableGovernance");
eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator");
eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity");
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
......@@ -35,6 +36,8 @@ var (
{Type: fixedBytes},
}
uint32Type, _ = abi.NewType("uint32", "", nil)
// Decoded nonce tuple (nonce, version)
decodedNonce, _ = abi.NewType("tuple", "DecodedNonce", []abi.ArgumentMarshaling{
{Name: "nonce", Type: "uint256"},
......@@ -44,6 +47,12 @@ var (
{Name: "encodedNonce", Type: decodedNonce},
}
// Decoded ecotone scalars (uint32, uint32)
decodedScalars = abi.Arguments{
{Name: "basefeeScalar", Type: uint32Type},
{Name: "blobbasefeeScalar", Type: uint32Type},
}
// WithdrawalHash slot tuple (bytes32, bytes32)
withdrawalSlot, _ = abi.NewType("tuple", "SlotHash", []abi.ArgumentMarshaling{
{Name: "withdrawalHash", Type: "bytes32"},
......@@ -361,6 +370,25 @@ func DiffTestUtils() {
packed, err := cannonMemoryProofArgs.Pack(&output)
checkErr(err, "Error encoding output")
fmt.Print(hexutil.Encode(packed[32:]))
case "encodeScalarEcotone":
basefeeScalar, err := strconv.ParseUint(args[1], 10, 32)
checkErr(err, "Error decocding basefeeScalar")
blobbasefeeScalar, err := strconv.ParseUint(args[2], 10, 32)
checkErr(err, "Error decocding blobbasefeeScalar")
encoded := eth.EncodeScalar(eth.EcotoneScalars{
BaseFeeScalar: uint32(basefeeScalar),
BlobBaseFeeScalar: uint32(blobbasefeeScalar),
})
fmt.Print(hexutil.Encode(encoded[:]))
case "decodeScalarEcotone":
scalar := common.HexToHash(args[1])
scalars, err := eth.DecodeScalar([32]byte(scalar[:]))
checkErr(err, "Error decoding scalar")
packed, err := decodedScalars.Pack(scalars.BaseFeeScalar, scalars.BlobBaseFeeScalar)
checkErr(err, "Error encoding output")
fmt.Print(hexutil.Encode(packed))
default:
panic(fmt.Errorf("Unknown command: %s", args[0]))
}
......
......@@ -48,8 +48,8 @@
"sourceCodeHash": "0xd6a894e371c2c7182b5960c507491f81c3775dda0efedd29475f7c30ca07b004"
},
"src/L1/SystemConfig.sol": {
"initCodeHash": "0xe937c5747e42d6701ff3fbb10471302288fa765fb40cbd9822e22547a42dfa25",
"sourceCodeHash": "0x9fb21393eb206f94bdf7e034c667535c415b7de93a1355058626d1c9e8abbff5"
"initCodeHash": "0x6160c9c96d0972e0761ab3b9d4f6d1b8543288c4c3aea9e132008a40e4c5d202",
"sourceCodeHash": "0xd27df6e227cc4728471641aec25a1fe6f79a12ce96d3ce6c1774d9411a54133e"
},
"src/L2/BaseFeeVault.sol": {
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474",
......
......@@ -134,6 +134,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "basefeeScalar",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "batchInbox",
......@@ -160,6 +173,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "blobbasefeeScalar",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "disputeGameFactory",
......@@ -238,14 +264,14 @@
"type": "address"
},
{
"internalType": "uint256",
"name": "_overhead",
"type": "uint256"
"internalType": "uint32",
"name": "_basefeeScalar",
"type": "uint32"
},
{
"internalType": "uint256",
"name": "_scalar",
"type": "uint256"
"internalType": "uint32",
"name": "_blobbasefeeScalar",
"type": "uint32"
},
{
"internalType": "bytes32",
......@@ -578,6 +604,24 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_basefeeScalar",
"type": "uint32"
},
{
"internalType": "uint32",
"name": "_blobbasefeeScalar",
"type": "uint32"
}
],
"name": "setGasConfigEcotone",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
......
......@@ -62,6 +62,20 @@
"slot": "104",
"type": "uint64"
},
{
"bytes": "4",
"label": "basefeeScalar",
"offset": 8,
"slot": "104",
"type": "uint32"
},
{
"bytes": "4",
"label": "blobbasefeeScalar",
"offset": 12,
"slot": "104",
"type": "uint32"
},
{
"bytes": "32",
"label": "_resourceConfig",
......
......@@ -90,9 +90,12 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
uint64 internal constant MAX_GAS_LIMIT = 200_000_000;
/// @notice Fixed L2 gas overhead. Used as part of the L2 fee calculation.
/// Deprecated since the Ecotone network upgrade
uint256 public overhead;
/// @notice Dynamic L2 gas overhead. Used as part of the L2 fee calculation.
/// The most significant byte is used to determine the version since the
/// Ecotone network upgrade.
uint256 public scalar;
/// @notice Identifier for the batcher.
......@@ -103,6 +106,12 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @notice L2 block gas limit.
uint64 public gasLimit;
/// @notice Basefee scalar value. Part of the L2 fee calculation since the Ecotone network upgrade.
uint32 public basefeeScalar;
/// @notice Blobbasefee scalar value. Part of the L2 fee calculation since the Ecotone network upgrade.
uint32 public blobbasefeeScalar;
/// @notice The configuration for the deposit fee market.
/// Used by the OptimismPortal to meter the cost of buying L2 gas on L1.
/// Set as internal with a getter so that the struct is returned instead of a tuple.
......@@ -115,8 +124,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
/// @notice Semantic version.
/// @custom:semver 2.2.0
string public constant version = "2.2.0";
/// @custom:semver 2.3.0-beta.1
string public constant version = "2.3.0-beta.1";
/// @notice Constructs the SystemConfig contract. Cannot set
/// the owner to `address(0)` due to the Ownable contract's
......@@ -127,8 +136,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
Storage.setUint(START_BLOCK_SLOT, type(uint256).max);
initialize({
_owner: address(0xdEaD),
_overhead: 0,
_scalar: 0,
_basefeeScalar: 0,
_blobbasefeeScalar: 0,
_batcherHash: bytes32(0),
_gasLimit: 1,
_unsafeBlockSigner: address(0),
......@@ -156,8 +165,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @notice Initializer.
/// The resource config must be set before the require check.
/// @param _owner Initial owner of the contract.
/// @param _overhead Initial overhead value.
/// @param _scalar Initial scalar value.
/// @param _basefeeScalar Initial basefee scalar value.
/// @param _blobbasefeeScalar Initial blobbasefee scalar value.
/// @param _batcherHash Initial batcher hash.
/// @param _gasLimit Initial gas limit.
/// @param _unsafeBlockSigner Initial unsafe block signer address.
......@@ -167,8 +176,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @param _addresses Set of L1 contract addresses. These should be the proxies.
function initialize(
address _owner,
uint256 _overhead,
uint256 _scalar,
uint32 _basefeeScalar,
uint32 _blobbasefeeScalar,
bytes32 _batcherHash,
uint64 _gasLimit,
address _unsafeBlockSigner,
......@@ -184,7 +193,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
// These are set in ascending order of their UpdateTypes.
_setBatcherHash(_batcherHash);
_setGasConfig({ _overhead: _overhead, _scalar: _scalar });
_setGasConfigEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar });
_setGasLimit(_gasLimit);
Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner);
......@@ -346,6 +355,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
}
/// @notice Updates gas config. Can only be called by the owner.
/// Deprecated in favor of setGasConfigEcotone since the Ecotone upgrade.
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function setGasConfig(uint256 _overhead, uint256 _scalar) external onlyOwner {
......@@ -356,6 +366,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function _setGasConfig(uint256 _overhead, uint256 _scalar) internal {
require((uint256(0xff) << 248) & _scalar == 0, "SystemConfig: scalar exceeds max.");
overhead = _overhead;
scalar = _scalar;
......@@ -363,6 +375,26 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data);
}
/// @notice Updates gas config as of the Ecotone upgrade. Can only be called by the owner.
/// @param _basefeeScalar New basefeeScalar value.
/// @param _blobbasefeeScalar New blobbasefeeScalar value.
function setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external onlyOwner {
_setGasConfigEcotone(_basefeeScalar, _blobbasefeeScalar);
}
/// @notice Internal function for updating the fee scalars as of the Ecotone upgrade.
/// @param _basefeeScalar New basefeeScalar value.
/// @param _blobbasefeeScalar New blobbasefeeScalar value.
function _setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) internal {
basefeeScalar = _basefeeScalar;
blobbasefeeScalar = _blobbasefeeScalar;
scalar = (uint256(0x01) << 248) | (uint256(_blobbasefeeScalar) << 32) | _basefeeScalar;
bytes memory data = abi.encode(overhead, scalar);
emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data);
}
/// @notice Updates the L2 gas limit. Can only be called by the owner.
/// @param _gasLimit New gas limit.
function setGasLimit(uint64 _gasLimit) external onlyOwner {
......
......@@ -26,20 +26,20 @@ contract SystemConfig_Init is CommonTest {
contract SystemConfig_Initialize_Test is SystemConfig_Init {
address batchInbox;
address owner;
uint256 overhead;
uint256 scalar;
bytes32 batcherHash;
uint64 gasLimit;
address unsafeBlockSigner;
address systemConfigImpl;
address optimismMintableERC20Factory;
uint32 basefeeScalar;
uint32 blobbasefeeScalar;
function setUp() public virtual override {
super.setUp();
batchInbox = deploy.cfg().batchInboxAddress();
owner = deploy.cfg().finalSystemOwner();
overhead = deploy.cfg().gasPriceOracleOverhead();
scalar = deploy.cfg().gasPriceOracleScalar();
basefeeScalar = deploy.cfg().basefeeScalar();
blobbasefeeScalar = deploy.cfg().blobbasefeeScalar();
batcherHash = bytes32(uint256(uint160(deploy.cfg().batchSenderAddress())));
gasLimit = uint64(deploy.cfg().l2GenesisBlockGasLimit());
unsafeBlockSigner = deploy.cfg().p2pSequencerAddress();
......@@ -52,10 +52,12 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init {
SystemConfig impl = SystemConfig(systemConfigImpl);
assertEq(impl.owner(), address(0xdEaD));
assertEq(impl.overhead(), 0);
assertEq(impl.scalar(), 0);
assertEq(impl.scalar(), uint256(0x01) << 248);
assertEq(impl.batcherHash(), bytes32(0));
assertEq(impl.gasLimit(), 1);
assertEq(impl.unsafeBlockSigner(), address(0));
assertEq(impl.basefeeScalar(), 0);
assertEq(impl.blobbasefeeScalar(), 0);
ResourceMetering.ResourceConfig memory actual = impl.resourceConfig();
assertEq(actual.maxResourceLimit, 1);
assertEq(actual.elasticityMultiplier, 1);
......@@ -78,14 +80,16 @@ contract SystemConfig_Initialize_Test is SystemConfig_Init {
assertEq(decimals, 18);
}
/// @dev Tests that initailization sets the correct values.
/// @dev Tests that initialization sets the correct values.
function test_initialize_succeeds() external view {
assertEq(systemConfig.owner(), owner);
assertEq(systemConfig.overhead(), overhead);
assertEq(systemConfig.scalar(), scalar);
assertEq(systemConfig.overhead(), 0);
assertEq(systemConfig.scalar() >> 248, 1);
assertEq(systemConfig.batcherHash(), batcherHash);
assertEq(systemConfig.gasLimit(), gasLimit);
assertEq(systemConfig.unsafeBlockSigner(), unsafeBlockSigner);
assertEq(systemConfig.basefeeScalar(), basefeeScalar);
assertEq(systemConfig.blobbasefeeScalar(), blobbasefeeScalar);
// Depends on `initialize` being called with defaults
ResourceMetering.ResourceConfig memory rcfg = Constants.DEFAULT_RESOURCE_CONFIG();
ResourceMetering.ResourceConfig memory actual = systemConfig.resourceConfig();
......@@ -127,8 +131,8 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test {
vm.expectRevert("SystemConfig: gas limit too low");
systemConfig.initialize({
_owner: alice,
_overhead: 2100,
_scalar: 1000000,
_basefeeScalar: basefeeScalar,
_blobbasefeeScalar: blobbasefeeScalar,
_batcherHash: bytes32(hex"abcd"),
_gasLimit: minimumGasLimit - 1,
_unsafeBlockSigner: address(1),
......@@ -157,8 +161,8 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test {
vm.prank(systemConfig.owner());
systemConfig.initialize({
_owner: alice,
_overhead: 2100,
_scalar: 1000000,
_basefeeScalar: basefeeScalar,
_blobbasefeeScalar: blobbasefeeScalar,
_batcherHash: bytes32(hex"abcd"),
_gasLimit: gasLimit,
_unsafeBlockSigner: address(1),
......@@ -188,8 +192,8 @@ contract SystemConfig_Initialize_TestFail is SystemConfig_Initialize_Test {
vm.prank(systemConfig.owner());
systemConfig.initialize({
_owner: alice,
_overhead: 2100,
_scalar: 1000000,
_basefeeScalar: basefeeScalar,
_blobbasefeeScalar: blobbasefeeScalar,
_batcherHash: bytes32(hex"abcd"),
_gasLimit: gasLimit,
_unsafeBlockSigner: address(1),
......@@ -283,8 +287,8 @@ contract SystemConfig_Init_ResourceConfig is SystemConfig_Init {
vm.expectRevert(bytes(revertMessage));
systemConfig.initialize({
_owner: address(0xdEaD),
_overhead: 0,
_scalar: 0,
_basefeeScalar: 0,
_blobbasefeeScalar: 0,
_batcherHash: bytes32(0),
_gasLimit: gasLimit,
_unsafeBlockSigner: address(0),
......@@ -321,8 +325,8 @@ contract SystemConfig_Init_CustomGasToken is SystemConfig_Init {
systemConfig.initialize({
_owner: alice,
_overhead: 2100,
_scalar: 1000000,
_basefeeScalar: 2100,
_blobbasefeeScalar: 1000000,
_batcherHash: bytes32(hex"abcd"),
_gasLimit: 30_000_000,
_unsafeBlockSigner: address(1),
......@@ -474,6 +478,18 @@ contract SystemConfig_Setters_TestFail is SystemConfig_Init {
systemConfig.setGasConfig(0, 0);
}
/// @notice Ensures that `setGasConfig` reverts if version byte is set.
function test_setGasConfig_badValues_reverts() external {
vm.prank(systemConfig.owner());
vm.expectRevert("SystemConfig: scalar exceeds max.");
systemConfig.setGasConfig({ _overhead: 0, _scalar: type(uint256).max });
}
function test_setGasConfigEcotone_notOwner_reverts() external {
vm.expectRevert("Ownable: caller is not the owner");
systemConfig.setGasConfigEcotone({ _basefeeScalar: 0, _blobbasefeeScalar: 0 });
}
/// @dev Tests that `setGasLimit` reverts if the caller is not the owner.
function test_setGasLimit_notOwner_reverts() external {
vm.expectRevert("Ownable: caller is not the owner");
......@@ -516,6 +532,8 @@ contract SystemConfig_Setters_Test is SystemConfig_Init {
/// @dev Tests that `setGasConfig` updates the overhead and scalar successfully.
function testFuzz_setGasConfig_succeeds(uint256 newOverhead, uint256 newScalar) external {
// always zero out most significant byte
newScalar = (newScalar << 16) >> 16;
vm.expectEmit(address(systemConfig));
emit ConfigUpdate(0, SystemConfig.UpdateType.GAS_CONFIG, abi.encode(newOverhead, newScalar));
......@@ -525,6 +543,24 @@ contract SystemConfig_Setters_Test is SystemConfig_Init {
assertEq(systemConfig.scalar(), newScalar);
}
function testFuzz_setGasConfigEcotone_succeeds(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external {
bytes32 encoded =
ffi.encodeScalarEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar });
vm.expectEmit(address(systemConfig));
emit ConfigUpdate(0, SystemConfig.UpdateType.GAS_CONFIG, abi.encode(systemConfig.overhead(), encoded));
vm.prank(systemConfig.owner());
systemConfig.setGasConfigEcotone({ _basefeeScalar: _basefeeScalar, _blobbasefeeScalar: _blobbasefeeScalar });
assertEq(systemConfig.basefeeScalar(), _basefeeScalar);
assertEq(systemConfig.blobbasefeeScalar(), _blobbasefeeScalar);
assertEq(systemConfig.scalar(), uint256(encoded));
(uint32 basefeeScalar, uint32 blobbbasefeeScalar) = ffi.decodeScalarEcotone(encoded);
assertEq(uint256(basefeeScalar), uint256(_basefeeScalar));
assertEq(uint256(blobbbasefeeScalar), uint256(_blobbasefeeScalar));
}
/// @dev Tests that `setGasLimit` updates the gas limit successfully.
function testFuzz_setGasLimit_succeeds(uint64 newGasLimit) external {
uint64 minimumGasLimit = systemConfig.minimumGasLimit();
......
......@@ -30,7 +30,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
address internal constant safeSingletonAddress = 0x90193C961A926261B756D1E5bb255e67ff9498A1;
address internal constant superchainConfigAddress = 0x068E44eB31e111028c41598E4535be7468674D0A;
address internal constant superchainConfigProxyAddress = 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809;
address internal constant systemConfigAddress = 0x19E90dC1Fb905f20BB3A7eC537B5B812d5FFA682;
address internal constant systemConfigAddress = 0x1182f45ae539e18534cbCC222c1C3D972B5Ba24D;
address internal constant systemConfigProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4;
address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02;
......@@ -242,6 +242,9 @@ contract DeploymentSummary is DeploymentSummaryCode {
slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"000000000000000000000000000000000000000000000000000000000000dead";
vm.store(systemConfigAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000066";
value = hex"0100000000000000000000000000000000000000000000000000000000000000";
vm.store(systemConfigAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(systemConfigAddress, slot, value);
......@@ -281,7 +284,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000003";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000019e90dc1fb905f20bb3a7ec537b5b812d5ffa682";
value = hex"0000000000000000000000001182f45ae539e18534cbcc222c1c3d972b5ba24d";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......@@ -298,14 +301,14 @@ contract DeploymentSummary is DeploymentSummaryCode {
slot = hex"0000000000000000000000000000000000000000000000000000000000000067";
value = hex"0000000000000000000000003c44cdddb6a900fa2b585dd299e03d12fa4293bc";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000065";
value = hex"0000000000000000000000000000000000000000000000000000000000000834";
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"00000000000000000000000000000000000c5fc5000005580000000000000000";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000066";
value = hex"00000000000000000000000000000000000000000000000000000000000f4240";
value = hex"010000000000000000000000000000000000000000000000000c5fc500000558";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"00000000000000000000000000000000000000000000000000000000017d7840";
value = hex"00000000000000000000000000000000000c5fc50000055800000000017d7840";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08";
value = hex"0000000000000000000000009965507d1a55bcc2695c58ba16fb37d819b0a4dc";
......
......@@ -36,7 +36,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant safeSingletonAddress = 0x90193C961A926261B756D1E5bb255e67ff9498A1;
address internal constant superchainConfigAddress = 0x068E44eB31e111028c41598E4535be7468674D0A;
address internal constant superchainConfigProxyAddress = 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809;
address internal constant systemConfigAddress = 0x19E90dC1Fb905f20BB3A7eC537B5B812d5FFA682;
address internal constant systemConfigAddress = 0x1182f45ae539e18534cbCC222c1C3D972B5Ba24D;
address internal constant systemConfigProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02;
address internal constant acc27Address = 0x12e721c390F5728200a26BBEf206A5F4F7E991f3;
......@@ -245,6 +245,9 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
slot = hex"0000000000000000000000000000000000000000000000000000000000000033";
value = hex"000000000000000000000000000000000000000000000000000000000000dead";
vm.store(systemConfigAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000066";
value = hex"0100000000000000000000000000000000000000000000000000000000000000";
vm.store(systemConfigAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(systemConfigAddress, slot, value);
......@@ -373,7 +376,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000003";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000019e90dc1fb905f20bb3a7ec537b5b812d5ffa682";
value = hex"0000000000000000000000001182f45ae539e18534cbcc222c1c3d972b5ba24d";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......@@ -390,14 +393,14 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
slot = hex"0000000000000000000000000000000000000000000000000000000000000067";
value = hex"0000000000000000000000003c44cdddb6a900fa2b585dd299e03d12fa4293bc";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000065";
value = hex"0000000000000000000000000000000000000000000000000000000000000834";
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"00000000000000000000000000000000000c5fc5000005580000000000000000";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000066";
value = hex"00000000000000000000000000000000000000000000000000000000000f4240";
value = hex"010000000000000000000000000000000000000000000000000c5fc500000558";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000068";
value = hex"00000000000000000000000000000000000000000000000000000000017d7840";
value = hex"00000000000000000000000000000000000c5fc50000055800000000017d7840";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08";
value = hex"0000000000000000000000009965507d1a55bcc2695c58ba16fb37d819b0a4dc";
......
......@@ -243,4 +243,25 @@ contract FFIInterface {
(bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes));
return (memRoot, proof);
}
function encodeScalarEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external returns (bytes32) {
string[] memory cmds = new string[](5);
cmds[0] = "scripts/go-ffi/go-ffi";
cmds[1] = "diff";
cmds[2] = "encodeScalarEcotone";
cmds[3] = vm.toString(_basefeeScalar);
cmds[4] = vm.toString(_blobbasefeeScalar);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes32));
}
function decodeScalarEcotone(bytes32 _scalar) external returns (uint32, uint32) {
string[] memory cmds = new string[](4);
cmds[0] = "scripts/go-ffi/go-ffi";
cmds[1] = "diff";
cmds[2] = "decodeScalarEcotone";
cmds[3] = vm.toString(_scalar);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (uint32, uint32));
}
}
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