Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Holocene: contracts part for separating dynamic&static l1 attributes #11600

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,12 @@ contract L2Genesis is Deployer {

/// @notice This predeploy is following the safety invariant #1.
function setL1Block() public {
if (cfg.useInterop()) {
if (cfg.useHolocene()) {
string memory cname = "L1BlockHolocene";
address impl = Predeploys.predeployToCodeNamespace(Predeploys.L1_BLOCK_ATTRIBUTES);
console.log("Setting %s implementation at: %s", cname, impl);
vm.etch(impl, vm.getDeployedCode(string.concat(cname, ".sol:", cname)));
} else if (cfg.useInterop()) {
string memory cname = "L1BlockInterop";
address impl = Predeploys.predeployToCodeNamespace(Predeploys.L1_BLOCK_ATTRIBUTES);
console.log("Setting %s implementation at: %s", cname, impl);
Expand Down
5 changes: 4 additions & 1 deletion packages/contracts-bedrock/scripts/deploy/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC2
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol";
import { SystemConfigHolocene } from "src/L1/SystemConfigHolocene.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { DataAvailabilityChallenge } from "src/L1/DataAvailabilityChallenge.sol";
import { Constants } from "src/libraries/Constants.sol";
Expand Down Expand Up @@ -827,7 +828,9 @@ contract Deploy is Deployer {
/// @notice Deploy the SystemConfig
function deploySystemConfig() public broadcast returns (address addr_) {
console.log("Deploying SystemConfig implementation");
if (cfg.useInterop()) {
if (cfg.useHolocene()) {
addr_ = address(new SystemConfigHolocene{ salt: _implSalt() }());
} else if (cfg.useInterop()) {
addr_ = address(new SystemConfigInterop{ salt: _implSalt() }());
} else {
addr_ = address(new SystemConfig{ salt: _implSalt() }());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ contract DeployConfig is Script {
address public customGasTokenAddress;

bool public useInterop;
bool public useHolocene;

function read(string memory _path) public {
console.log("DeployConfig: reading file %s", _path);
Expand Down Expand Up @@ -176,6 +177,7 @@ contract DeployConfig is Script {
customGasTokenAddress = _readOr(_json, "$.customGasTokenAddress", address(0));

useInterop = _readOr(_json, "$.useInterop", false);
useHolocene = _readOr(_json, "$.useHolocene", false);
}

function fork() public view returns (Fork fork_) {
Expand Down
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/src/L1/SystemConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {

/// @notice Internal function for updating the batcher hash.
/// @param _batcherHash New batcher hash.
function _setBatcherHash(bytes32 _batcherHash) internal {
function _setBatcherHash(bytes32 _batcherHash) internal virtual {
batcherHash = _batcherHash;

bytes memory data = abi.encode(_batcherHash);
Expand All @@ -367,7 +367,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @notice Internal function for updating the gas config.
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function _setGasConfig(uint256 _overhead, uint256 _scalar) internal {
function _setGasConfig(uint256 _overhead, uint256 _scalar) internal virtual {
require((uint256(0xff) << 248) & _scalar == 0, "SystemConfig: scalar exceeds max.");

overhead = _overhead;
Expand All @@ -387,7 +387,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// @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 {
function _setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) internal virtual {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we separate out the gasconfig into two update types like I suggested, we can deprecate setGasConfigEcotone entirely. (Of course we have make sure this one works if someone calls it, so we'll still have to override this implementation in SystemConfigHolocene.)

Copy link
Collaborator

@roberto-bayardo roberto-bayardo Aug 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and you'll also want to change "_setGasConfig" to virtual so we can override it to make it do the right thing. Though since setGasConfig() has been deprecated since Ecotone, you might also just remove _setGasConfig() entirely, then make setGasConfig() revert, with the error indicating to use set(Blob)BasefeeScalar() instead.

Copy link
Collaborator

@roberto-bayardo roberto-bayardo Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to my statement above, setGasConfig() was actually not deprecated until after Ecotone (Ecotone didn't involve any changes to L1 contracts, which is why we had to hack in the new behavior using the old API). The setGasConfigEcotone() was added in a later update of the L1 contracts. IMO it should have been named setFeeScalars() -- I don't think hardfork names should be in public APIs if not entirely necessary. Regardless, since setGasConfig() wasn't deprecated until later, probably we want to keep it working for a bit longer rather than removing it entirely or just making it revert. Its implementation of course can just be made to call the new contract function(s) after appropriately parsing out the values.

basefeeScalar = _basefeeScalar;
blobbasefeeScalar = _blobbasefeeScalar;

Expand All @@ -405,7 +405,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {

/// @notice Internal function for updating the L2 gas limit.
/// @param _gasLimit New gas limit.
function _setGasLimit(uint64 _gasLimit) internal {
function _setGasLimit(uint64 _gasLimit) internal virtual {
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
require(_gasLimit <= maximumGasLimit(), "SystemConfig: gas limit too high");
gasLimit = _gasLimit;
Expand Down
76 changes: 76 additions & 0 deletions packages/contracts-bedrock/src/L1/SystemConfigHolocene.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { OptimismPortalInterop as OptimismPortal } from "src/L1/OptimismPortalInterop.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { ConfigType } from "src/L2/L1BlockInterop.sol";
import { StaticConfig } from "src/libraries/StaticConfig.sol";

/// @title SystemConfigHolocene
/// @notice The SystemConfig contract is used to manage configuration of an Optimism network.
/// All configuration is stored on L1 and picked up by L2 as part of the derviation of
/// the L2 chain.
contract SystemConfigHolocene is SystemConfig {
error Deprecated();

/// @notice Internal function for updating the gas config.
/// `setGasConfig` is deprecated, use `setFeeScalars` instead.
/// `setGasConfig` will be removed when SystemConfigHolocene is
/// pulled into SystemConfig.
function _setGasConfig(uint256, uint256) internal pure override {
revert Deprecated();
}

/// @notice Internal function for updating the fee scalars as of the Ecotone upgrade.
/// Deprecated, use `setFeeScalars` instead.
/// @param _basefeeScalar New basefeeScalar value.
/// @param _blobBasefeeScalar New blobBasefeeScalar value.
function _setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobBasefeeScalar) internal override {
_setFeeScalars(_basefeeScalar, _blobBasefeeScalar);
}

/// @notice Updates the fee scalars. Can only be called by the owner.
/// @param _basefeeScalar New basefeeScalar value.
/// @param _blobBasefeeScalar New blobBasefeeScalar value.
function setFeeScalars(uint32 _basefeeScalar, uint32 _blobBasefeeScalar) external onlyOwner {
_setFeeScalars(_basefeeScalar, _blobBasefeeScalar);
}

/// @notice Internal function for updating the fee scalars.
/// @param _basefeeScalar New basefeeScalar value.
/// @param _blobBasefeeScalar New blobBasefeeScalar value.
function _setFeeScalars(uint32 _basefeeScalar, uint32 _blobBasefeeScalar) internal {
basefeeScalar = _basefeeScalar;
blobbasefeeScalar = _blobBasefeeScalar;

uint256 _scalar = StaticConfig.packScalar(_basefeeScalar, _blobBasefeeScalar);
scalar = _scalar;

OptimismPortal(payable(optimismPortal())).setConfig(
ConfigType.SET_FEE_SCALARS, StaticConfig.encodeSetFeeScalars({ _scalar: _scalar })
);
}

/// @notice Internal setter for the batcher hash.
/// @param _batcherHash New batcher hash.
function _setBatcherHash(bytes32 _batcherHash) internal override {
if (batcherHash != _batcherHash) {
batcherHash = _batcherHash;
OptimismPortal(payable(optimismPortal())).setConfig(
ConfigType.SET_BATCHER_HASH, StaticConfig.encodeSetBatcherHash({ _batcherHash: _batcherHash })
);
}
}

/// @notice Internal function for updating the L2 gas limit.
/// @param _gasLimit New gas limit.
function _setGasLimit(uint64 _gasLimit) internal override {
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
require(_gasLimit <= maximumGasLimit(), "SystemConfig: gas limit too high");
gasLimit = _gasLimit;

OptimismPortal(payable(optimismPortal())).setConfig(
ConfigType.SET_GAS_LIMIT, StaticConfig.encodeSetGasLimit({ _gasLimit: _gasLimit })
);
}
}
19 changes: 19 additions & 0 deletions packages/contracts-bedrock/src/L2/L1Block.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import { Constants } from "src/libraries/Constants.sol";
import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol";
import "src/libraries/L1BlockErrors.sol";

/// @notice Enum representing different types of configurations that can be set on L1Block.
/// @custom:value SET_GAS_PAYING_TOKEN Represents the config type for setting the gas paying token.
/// @custom:value ADD_DEPENDENCY Represents the config type for adding a chain to the interop dependency set.
/// @custom:value REMOVE_DEPENDENCY Represents the config type for removing a chain from the interop dependency set.
enum ConfigType {
SET_GAS_PAYING_TOKEN,
ADD_DEPENDENCY,
REMOVE_DEPENDENCY,
SET_BATCHER_HASH,
SET_FEE_SCALARS,
SET_GAS_LIMIT
}

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @title L1Block
Expand Down Expand Up @@ -162,4 +175,10 @@ contract L1Block is ISemver, IGasToken {

emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol });
}

/// @notice Sets static configuration options for the L2 system. Can only be called by the special
/// depositor account.
/// @param _type The type of configuration to set.
/// @param _value The encoded value with which to set the configuration.
function setConfig(ConfigType _type, bytes calldata _value) public virtual { }
}
110 changes: 110 additions & 0 deletions packages/contracts-bedrock/src/L2/L1BlockHolocene.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { L1Block, ConfigType } from "src/L2/L1Block.sol";
import { StaticConfig } from "src/libraries/StaticConfig.sol";
import "src/libraries/L1BlockErrors.sol";

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @title L1BlockHolocene
/// @notice Holocene extenstions of L1Block.
contract L1BlockHolocene is L1Block {
/// @notice Event emitted when a new batcher hash is set.
event BatcherHashSet(bytes32 indexed batcherHash);

/// @notice Event emitted when new fee scalars are set.
event FeeScalarsSet(uint32 indexed blobBasefeeScalar, uint32 indexed basefeeScalar);

/// @notice Event emitted when a new gas limit is set.
event GasLimitSet(uint64 indexed gasLimit);

/// @notice The gas limit of L2 blocks in the same epoch.
uint64 public gasLimit;

/// @notice Sets static configuration options for the L2 system. Can only be called by the special
/// depositor account.
/// @param _type The type of configuration to set.
/// @param _value The encoded value with which to set the configuration.
function setConfig(ConfigType _type, bytes calldata _value) public override {
if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor();

if (_type == ConfigType.SET_BATCHER_HASH) {
_setBatcherHash(_value);
} else if (_type == ConfigType.SET_FEE_SCALARS) {
_setFeeScalars(_value);
} else if (_type == ConfigType.SET_GAS_LIMIT) {
_setGasLimit(_value);
} else {
super.setConfig(_type, _value);
}
}

/// @notice Internal method to set new batcher hash.
/// @param _value The encoded value with which to set the new batcher hash.
function _setBatcherHash(bytes calldata _value) internal {
bytes32 _batcherHash = StaticConfig.decodeSetBatcherHash(_value);

batcherHash = _batcherHash;

emit BatcherHashSet(_batcherHash);
}

/// @notice Internal method to set new fee scalars.
/// @param _value The encoded value with which to set the new fee scalars.
function _setFeeScalars(bytes calldata _value) internal {
uint256 _scalar = StaticConfig.decodeSetFeeScalars(_value);

(uint32 _basefeeScalar, uint32 _blobBasefeeScalar) = StaticConfig.unpackScalar(_scalar);

blobBaseFeeScalar = _blobBasefeeScalar;
baseFeeScalar = _basefeeScalar;

emit FeeScalarsSet(_blobBasefeeScalar, _basefeeScalar);
}

/// @notice Internal method to set new gas limit.
/// @param _value The encoded value with which to set the new gas limit.
function _setGasLimit(bytes calldata _value) internal {
uint64 _gasLimit = StaticConfig.decodeSetGasLimit(_value);
gasLimit = _gasLimit;
emit GasLimitSet(_gasLimit);
}

/// @notice Updates the L1 block values for an Holocene upgraded chain.
/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
/// Params are expected to be in the following order:
/// 1. _sequenceNumber Number of L2 blocks since epoch start.
/// 2. _timestamp L1 timestamp.
/// 3. _number L1 blocknumber.
/// 4. _basefee L1 base fee.
/// 5. _blobBaseFee L1 blob base fee.
/// 6. _hash L1 blockhash.
function setL1BlockValuesHolocene() external {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will want to implement the following comment as setL1BlockValuesHolocene: ethereum-optimism/specs#122 (comment)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4618303

address depositor = DEPOSITOR_ACCOUNT();
uint64 _sequenceNumber;
assembly {
// Revert if the caller is not the depositor account.
if xor(caller(), depositor) {
mstore(0x00, 0x3cc50b45) // 0x3cc50b45 is the 4-byte selector of "NotDepositor()"
revert(0x1C, 0x04) // returns the stored 4-byte selector from above
}
// sequencenum (uint64)
_sequenceNumber := shr(192, calldataload(4))
}

sequenceNumber = _sequenceNumber;

// for each L2 block, include only the sequence number, except for L2 blocks with sequencer #0,
// and they'd have all the L1 origin related attributes following the 0 sequence number.
if (_sequenceNumber != 0) return;

assembly {
// number (uint64) and timestamp (uint64)
sstore(number.slot, shr(128, calldataload(12)))
sstore(basefee.slot, calldataload(28)) // uint256
sstore(blobBaseFee.slot, calldataload(60)) // uint256
sstore(hash.slot, calldataload(92)) // bytes32
}
}
}
16 changes: 4 additions & 12 deletions packages/contracts-bedrock/src/L2/L1BlockInterop.sol
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { L1Block } from "src/L2/L1Block.sol";
import { L1Block, ConfigType } from "src/L2/L1Block.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { GasPayingToken } from "src/libraries/GasPayingToken.sol";
import { StaticConfig } from "src/libraries/StaticConfig.sol";
import "src/libraries/L1BlockErrors.sol";

/// @notice Enum representing different types of configurations that can be set on L1BlockInterop.
/// @custom:value SET_GAS_PAYING_TOKEN Represents the config type for setting the gas paying token.
/// @custom:value ADD_DEPENDENCY Represents the config type for adding a chain to the interop dependency set.
/// @custom:value REMOVE_DEPENDENCY Represents the config type for removing a chain from the interop dependency set.
enum ConfigType {
SET_GAS_PAYING_TOKEN,
ADD_DEPENDENCY,
REMOVE_DEPENDENCY
}

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @title L1BlockInterop
Expand Down Expand Up @@ -56,7 +46,7 @@ contract L1BlockInterop is L1Block {
/// depositor account.
/// @param _type The type of configuration to set.
/// @param _value The encoded value with which to set the configuration.
function setConfig(ConfigType _type, bytes calldata _value) external {
function setConfig(ConfigType _type, bytes calldata _value) public override {
if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor();

if (_type == ConfigType.SET_GAS_PAYING_TOKEN) {
Expand All @@ -65,6 +55,8 @@ contract L1BlockInterop is L1Block {
_addDependency(_value);
} else if (_type == ConfigType.REMOVE_DEPENDENCY) {
_removeDependency(_value);
} else {
super.setConfig(_type, _value);
}
}

Expand Down
Loading