Commit 2c63288b authored by clabby's avatar clabby Committed by GitHub

feat(ctb): Enforce maximum L2 gas limit (#10500)

* feat(ctb): Enforce maximum L2 gas limit

Enforces a maximum L2 gas limit within the `SystemConfig`. This change
helps ensure that OP Stack chain governors keep the L2 block gas limit
within a reasonable range in order to guarantee that the L2 blocks may
be proven.

* In the `_setResourceConfig` function, the new minimum gas limit is
checked to be less than the current `gasLimit`. This value may never be
larger than `MAX_GAS_LIMIT`, per the checks in `_setGasLimit`. This
ensures that the `minimumGasLimit <= maximumGasLimit`

* Update SystemConfig.t.sol
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>

---------
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>
parent 2fadb4c1
# `SystemConfig` Invariants
## The gas limit of the `SystemConfig` contract can never be lower than the hard-coded lower bound.
**Test:** [`SystemConfig.t.sol#L69`](../test/invariants/SystemConfig.t.sol#L69)
## Gas limit boundaries
**Test:** [`SystemConfig.t.sol#L70`](../test/invariants/SystemConfig.t.sol#L70)
The gas limit of the `SystemConfig` contract can never be lower than the hard-coded lower bound or higher than the hard-coded upper bound. The lower bound must never be higher than the upper bound.
\ No newline at end of file
......@@ -48,8 +48,8 @@
"sourceCodeHash": "0xd6a894e371c2c7182b5960c507491f81c3775dda0efedd29475f7c30ca07b004"
},
"src/L1/SystemConfig.sol": {
"initCodeHash": "0xace1ef0e25a5416a686c7555acd20e9b0191ab2a8c8320d57606ed12092ccbb6",
"sourceCodeHash": "0x1f35f55aa230a686f6457e42bd08acc8bd94d691f89ac23a4b2efd7cc1eefdc6"
"initCodeHash": "0x68a862089f654e951850708a48025e93231c041b862d05086e1a05be9d9ac4e9",
"sourceCodeHash": "0x3a7cd5254306a01b50c9c68d9e57a57db7ad8163326eb30dff23e368c35ff166"
},
"src/L2/BaseFeeVault.sol": {
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474",
......
......@@ -404,6 +404,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maximumGasLimit",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "minimumGasLimit",
......
......@@ -84,6 +84,11 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @notice The number of decimals that the gas paying token has.
uint8 internal constant GAS_PAYING_TOKEN_DECIMALS = 18;
/// @notice The maximum gas limit that can be set for L2 blocks. This limit is used to enforce that the blocks
/// on L2 are not too large to process and prove. Over time, this value can be increased as various
/// optimizations and improvements are made to the system at large.
uint64 internal constant MAX_GAS_LIMIT = 200_000_000;
/// @notice Fixed L2 gas overhead. Used as part of the L2 fee calculation.
uint256 public overhead;
......@@ -110,8 +115,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
/// @notice Semantic version.
/// @custom:semver 2.1.0
string public constant version = "2.1.0";
/// @custom:semver 2.2.0
string public constant version = "2.2.0";
/// @notice Constructs the SystemConfig contract. Cannot set
/// the owner to `address(0)` due to the Ownable contract's
......@@ -208,6 +213,14 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
return uint64(_resourceConfig.maxResourceLimit) + uint64(_resourceConfig.systemTxMaxGas);
}
/// @notice Returns the maximum L2 gas limit that can be safely set for the system to
/// operate. This bound is used to prevent the gas limit from being set too high
/// and causing the system to be unable to process and/or prove L2 blocks.
/// @return uint64 Maximum gas limit.
function maximumGasLimit() public pure returns (uint64) {
return MAX_GAS_LIMIT;
}
/// @notice High level getter for the unsafe block signer address.
/// Unsafe blocks can be propagated across the p2p network if they are signed by the
/// key corresponding to this address.
......@@ -360,6 +373,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @param _gasLimit New gas limit.
function _setGasLimit(uint64 _gasLimit) internal {
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
require(_gasLimit <= maximumGasLimit(), "SystemConfig: gas limit too high");
gasLimit = _gasLimit;
bytes memory data = abi.encode(_gasLimit);
......
......@@ -398,6 +398,22 @@ contract SystemConfig_Setters_TestFail is SystemConfig_Init {
systemConfig.setResourceConfig(config);
}
/// @dev Tests that `setGasLimit` reverts if the gas limit is too low.
function test_setGasLimit_lowGasLimit_reverts() external {
uint64 minimumGasLimit = systemConfig.minimumGasLimit();
vm.prank(systemConfig.owner());
vm.expectRevert("SystemConfig: gas limit too low");
systemConfig.setGasLimit(minimumGasLimit - 1);
}
/// @dev Tests that `setGasLimit` reverts if the gas limit is too high.
function test_setGasLimit_highGasLimit_reverts() external {
uint64 maximumGasLimit = systemConfig.maximumGasLimit();
vm.prank(systemConfig.owner());
vm.expectRevert("SystemConfig: gas limit too high");
systemConfig.setGasLimit(maximumGasLimit + 1);
}
/// @dev Tests that `setResourceConfig` reverts if the min base fee
/// is greater than the maximum allowed base fee.
function test_setResourceConfig_badMinMax_reverts() external {
......@@ -489,7 +505,8 @@ contract SystemConfig_Setters_Test is SystemConfig_Init {
/// @dev Tests that `setGasLimit` updates the gas limit successfully.
function testFuzz_setGasLimit_succeeds(uint64 newGasLimit) external {
uint64 minimumGasLimit = systemConfig.minimumGasLimit();
newGasLimit = uint64(bound(uint256(newGasLimit), uint256(minimumGasLimit), uint256(type(uint64).max)));
uint64 maximumGasLimit = systemConfig.maximumGasLimit();
newGasLimit = uint64(bound(uint256(newGasLimit), uint256(minimumGasLimit), uint256(maximumGasLimit)));
vm.expectEmit(address(systemConfig));
emit ConfigUpdate(0, SystemConfig.UpdateType.GAS_LIMIT, abi.encode(newGasLimit));
......
......@@ -6,7 +6,7 @@ import { SystemConfig } from "src/L1/SystemConfig.sol";
import { Proxy } from "src/universal/Proxy.sol";
import { Constants } from "src/libraries/Constants.sol";
contract SystemConfig_GasLimitLowerBound_Invariant is Test {
contract SystemConfig_GasLimitBoundaries_Invariant is Test {
SystemConfig public config;
function setUp() external {
......@@ -63,9 +63,13 @@ contract SystemConfig_GasLimitLowerBound_Invariant is Test {
targetInterface(target);
}
/// @custom:invariant The gas limit of the `SystemConfig` contract can never be lower
/// than the hard-coded lower bound.
function invariant_gasLimitLowerBound() external view {
/// @custom:invariant Gas limit boundaries
///
/// The gas limit of the `SystemConfig` contract can never be lower than the hard-coded lower bound or higher than
/// the hard-coded upper bound. The lower bound must never be higher than the upper bound.
function invariant_gasLimitBoundaries() external view {
assertTrue(config.gasLimit() >= config.minimumGasLimit());
assertTrue(config.gasLimit() <= config.maximumGasLimit());
assertTrue(config.minimumGasLimit() <= config.maximumGasLimit());
}
}
......@@ -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 = 0x809abd1c13738dE7a76C85839779FCaa59FA32CE;
address internal constant systemConfigAddress = 0x1Fa4ABc046c3B6e20e072df7F869D67566974301;
address internal constant systemConfigProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4;
address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02;
......@@ -281,7 +281,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000003";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"000000000000000000000000809abd1c13738de7a76c85839779fcaa59fa32ce";
value = hex"0000000000000000000000001fa4abc046c3b6e20e072df7f869d67566974301";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......
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