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

fix(protocol): refine EIP-1559 basefee calculation and gas excess adjustment in TaikoL2 #17871

Merged
merged 17 commits into from
Aug 6, 2024
4 changes: 2 additions & 2 deletions packages/protocol/contract_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| l2Hashes | mapping(uint256 => bytes32) | 251 | 0 | 32 | contracts/L2/TaikoL2.sol:TaikoL2 |
| publicInputHash | bytes32 | 252 | 0 | 32 | contracts/L2/TaikoL2.sol:TaikoL2 |
| gasExcess | uint64 | 253 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| parentGasExcess | uint64 | 253 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| lastSyncedBlock | uint64 | 253 | 8 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| parentTimestamp | uint64 | 253 | 16 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __deprecated2 | uint64 | 253 | 24 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| parentGasTarget | uint64 | 253 | 24 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| l1ChainId | uint64 | 254 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[46] | 255 | 0 | 1472 | contracts/L2/TaikoL2.sol:TaikoL2 |

Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ library TaikoData {
// ---------------------------------------------------------------------
uint8 basefeeAdjustmentQuotient;
uint8 basefeeSharingPctg;
uint32 gasTargetPerL1Block;
uint32 gasIssuancePerSecond;
// ---------------------------------------------------------------------
// Group 6: Others
// ---------------------------------------------------------------------
Expand Down Expand Up @@ -126,7 +126,7 @@ library TaikoData {
uint8 blobIndex;
uint8 basefeeAdjustmentQuotient;
uint8 basefeeSharingPctg;
uint32 gasTargetPerL1Block;
uint32 gasIssuancePerSecond;
}

/// @dev Struct representing transition to be proven.
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents {
maxAnchorHeightOffset: 64,
basefeeAdjustmentQuotient: 8,
basefeeSharingPctg: 75,
gasTargetPerL1Block: 60_000_000,
gasIssuancePerSecond: 5_000_000,
ontakeForkHeight: 374_400 // = 7200 * 52
});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L1/libs/LibData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ library LibData {
blobIndex: 0,
basefeeAdjustmentQuotient: 0,
basefeeSharingPctg: 0,
gasTargetPerL1Block: 0
gasIssuancePerSecond: 0
});
}
}
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ library LibProposing {
blobIndex: local.params.blobIndex,
basefeeAdjustmentQuotient: _config.basefeeAdjustmentQuotient,
basefeeSharingPctg: _config.basefeeSharingPctg,
gasTargetPerL1Block: _config.gasTargetPerL1Block
gasIssuancePerSecond: _config.gasIssuancePerSecond
});
}

Expand Down
61 changes: 34 additions & 27 deletions packages/protocol/contracts/L2/Lib1559Math.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ library Lib1559Math {
error EIP1559_INVALID_PARAMS();

function calc1559BaseFee(
uint32 _gasTarget,
uint8 _adjustmentQuotient,
uint256 _gasTarget,
uint64 _gasExcess,
uint64 _gasIssuance,
uint32 _parentGasUsed
Expand All @@ -35,42 +34,50 @@ library Lib1559Math {
// bonding curve, regardless the actual amount of gas used by this
// block, however, this block's gas used will affect the next
// block's base fee.
basefee_ = basefee(gasExcess_, uint256(_adjustmentQuotient) * _gasTarget);

// Always make sure basefee is nonzero, this is required by the node.
if (basefee_ == 0) basefee_ = 1;
basefee_ = basefee(gasExcess_, _gasTarget);
}

/// @dev eth_qty(excess_gas_issued) / (TARGET * ADJUSTMENT_QUOTIENT)
/// @param _gasExcess The gas excess value
/// @param _adjustmentFactor The product of gasTarget and adjustmentQuotient
function basefee(
uint256 _gasExcess,
uint256 _adjustmentFactor
/// @dev Returns the new gas excess that will keep the basefee the same.
/// `_newGasTarget * ln(_newGasTarget / _target) + _gasExcess * _newGasTarget / _target`
function adjustExcess(
uint64 _gasExcess,
uint64 _gasTarget,
uint64 _newGasTarget
Brechtpd marked this conversation as resolved.
Show resolved Hide resolved
)
internal
pure
returns (uint256)
returns (uint64)
{
if (_adjustmentFactor == 0) {
revert EIP1559_INVALID_PARAMS();
if (_gasTarget == 0) revert EIP1559_INVALID_PARAMS();

uint256 f = LibFixedPointMath.SCALING_FACTOR;
uint256 ratio = f * _newGasTarget / _gasTarget;
if (ratio > uint256(type(int256).max)) revert EIP1559_INVALID_PARAMS();

int256 lnRatio = LibFixedPointMath.ln(int256(ratio)); // may be negative

uint256 newGasExcess;
assembly {
newGasExcess := sdiv(add(mul(lnRatio, _newGasTarget), mul(ratio, _gasExcess)), f)
}
return _ethQty(_gasExcess, _adjustmentFactor) / LibFixedPointMath.SCALING_FACTOR;

return uint64(newGasExcess.min(type(uint64).max));
}

/// @dev exp(gas_qty / TARGET / ADJUSTMENT_QUOTIENT)
function _ethQty(
uint256 _gasExcess,
uint256 _adjustmentFactor
)
private
pure
returns (uint256)
{
uint256 input = _gasExcess * LibFixedPointMath.SCALING_FACTOR / _adjustmentFactor;
/// @dev exp(_gasExcess / _gasTarget) / _gasTarget
function basefee(uint256 _gasExcess, uint256 _gasTarget) internal pure returns (uint256) {
uint256 fee = ethQty(_gasExcess, _gasTarget) / _gasTarget;
return fee == 0 ? 1 : fee;
}

/// @dev exp(_gasExcess / _gasTarget)
function ethQty(uint256 _gasExcess, uint256 _gasTarget) internal pure returns (uint256) {
if (_gasTarget == 0) revert EIP1559_INVALID_PARAMS();

uint256 input = LibFixedPointMath.SCALING_FACTOR * _gasExcess / _gasTarget;
if (input > LibFixedPointMath.MAX_EXP_INPUT) {
input = LibFixedPointMath.MAX_EXP_INPUT;
}
return uint256(LibFixedPointMath.exp(int256(input)));
return uint256(LibFixedPointMath.exp(int256(input))) / LibFixedPointMath.SCALING_FACTOR;
}
}
88 changes: 53 additions & 35 deletions packages/protocol/contracts/L2/TaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ contract TaikoL2 is EssentialContract {

/// @notice The gas excess value used to calculate the base fee.
/// @dev Slot 3.
uint64 public gasExcess;
uint64 public parentGasExcess;

/// @notice The last synced L1 block height.
uint64 public lastSyncedBlock;
uint64 private parentTimestamp;
uint64 private __deprecated2; // was __currentBlockTimestamp
uint64 private parentGasTarget;

/// @notice The L1's chain ID.
uint64 public l1ChainId;
Expand All @@ -49,8 +49,8 @@ contract TaikoL2 is EssentialContract {

/// @notice Emitted when the latest L1 block details are anchored to L2.
/// @param parentHash The hash of the parent block.
/// @param gasExcess The gas excess value used to calculate the base fee.
event Anchored(bytes32 parentHash, uint64 gasExcess);
/// @param parentGasExcess The gas excess value used to calculate the base fee.
event Anchored(bytes32 parentHash, uint64 parentGasExcess);

error L2_BASEFEE_MISMATCH();
error L2_FORK_ERROR();
Expand All @@ -65,12 +65,12 @@ contract TaikoL2 is EssentialContract {
/// @param _owner The owner of this contract. msg.sender will be used if this value is zero.
/// @param _rollupAddressManager The address of the {AddressManager} contract.
/// @param _l1ChainId The ID of the base layer.
/// @param _gasExcess The initial gasExcess.
/// @param _initialGasExcess The initial parentGasExcess.
function init(
address _owner,
address _rollupAddressManager,
uint64 _l1ChainId,
uint64 _gasExcess
uint64 _initialGasExcess
)
external
initializer
Expand All @@ -95,11 +95,21 @@ contract TaikoL2 is EssentialContract {
}

l1ChainId = _l1ChainId;
gasExcess = _gasExcess;
parentTimestamp = uint64(block.timestamp);
parentGasExcess = _initialGasExcess;
(publicInputHash,) = _calcPublicInputHash(block.number);
}

/// @dev Reinitialize some state variables.
/// We may want to init the basefee to a default value using one of the following values.
/// - _initialGasExcess = 274*5_000_000 => basefee =0.01 gwei
/// - _initialGasExcess = 282*5_000_000 => basefee =0.05 gwei
/// - _initialGasExcess = 288*5_000_000 => basefee =0.1 gwei
function init2(uint64 _initialGasExcess) external onlyOwner reinitializer(2) {
parentGasExcess = _initialGasExcess;
parentTimestamp = uint64(block.timestamp);
parentGasTarget = 0;
}

/// @notice Anchors the latest L1 block details to L2 for cross-layer
/// message verification.
/// @dev This function can be called freely as the golden touch private key is publicly known,
Expand Down Expand Up @@ -134,7 +144,7 @@ contract TaikoL2 is EssentialContract {
uint64 _anchorBlockId,
bytes32 _anchorStateRoot,
uint32 _parentGasUsed,
uint32 _gasTargetPerL1Block,
uint32 _gasIssuancePerSecond,
uint8 _basefeeAdjustmentQuotient
)
external
Expand All @@ -145,7 +155,7 @@ contract TaikoL2 is EssentialContract {
_anchorBlockId,
_anchorStateRoot,
_parentGasUsed,
_gasTargetPerL1Block,
_gasIssuancePerSecond,
_basefeeAdjustmentQuotient
);
}
Expand Down Expand Up @@ -177,21 +187,20 @@ contract TaikoL2 is EssentialContract {
/// @param _anchorBlockId The synced L1 height in the next Taiko block
/// @param _parentGasUsed Gas used in the parent block.
/// @return basefee_ The calculated EIP-1559 base fee per gas.
/// @return gasExcess_ The new gasExcess value.
/// @return parentGasExcess_ The new parentGasExcess value.
function getBasefee(
uint64 _anchorBlockId,
uint32 _parentGasUsed
)
public
view
returns (uint256 basefee_, uint64 gasExcess_)
returns (uint256 basefee_, uint64 parentGasExcess_)
{
LibL2Config.Config memory config = getConfig();

(basefee_, gasExcess_) = Lib1559Math.calc1559BaseFee(
config.gasTargetPerL1Block,
config.basefeeAdjustmentQuotient,
gasExcess,
(basefee_, parentGasExcess_) = Lib1559Math.calc1559BaseFee(
uint256(config.gasTargetPerL1Block) * config.basefeeAdjustmentQuotient,
parentGasExcess,
uint64(_anchorBlockId - lastSyncedBlock) * config.gasTargetPerL1Block,
_parentGasUsed
);
Expand Down Expand Up @@ -225,29 +234,28 @@ contract TaikoL2 is EssentialContract {

/// @notice Calculates the basefee and the new gas excess value based on parent gas used and gas
/// excess.
/// @param _gasTargetPerL1Block The gas target for L2 based on each L1 block.
/// @param _gasIssuancePerSecond The gas target for L2 per second.
/// @param _blocktime The time between this block and the parent block.
/// @param _adjustmentQuotient The gas adjustment quotient.
/// @param _gasExcess The current gas excess value.
/// @param _parentGasExcess The current gas excess value.
/// @param _parentGasUsed Total gas used by the parent block.
/// @return basefee_ Next block's base fee.
/// @return gasExcess_ The new gas excess value.
/// @return parentGasExcess_ The new gas excess value.
function calculateBaseFee(
uint32 _gasTargetPerL1Block,
uint32 _gasIssuancePerSecond,
uint64 _blocktime,
uint8 _adjustmentQuotient,
uint64 _gasExcess,
uint64 _parentGasExcess,
uint32 _parentGasUsed
)
public
pure
returns (uint256 basefee_, uint64 gasExcess_)
returns (uint256 basefee_, uint64 parentGasExcess_)
{
return Lib1559Math.calc1559BaseFee(
_gasTargetPerL1Block,
_adjustmentQuotient,
_gasExcess,
_blocktime * _gasTargetPerL1Block / 12,
uint256(_gasIssuancePerSecond) * _adjustmentQuotient,
_parentGasExcess,
_blocktime * _gasIssuancePerSecond,
_parentGasUsed
);
}
Expand All @@ -256,7 +264,7 @@ contract TaikoL2 is EssentialContract {
uint64 _anchorBlockId,
bytes32 _anchorStateRoot,
uint32 _parentGasUsed,
uint32 _gasTargetPerL1Block,
uint32 _gasIssuancePerSecond,
uint8 _basefeeAdjustmentQuotient
)
private
Expand All @@ -276,16 +284,26 @@ contract TaikoL2 is EssentialContract {
_calcPublicInputHash(parentId);
if (publicInputHash != currentPublicInputHash) revert L2_PUBLIC_INPUT_HASH_MISMATCH();

// Check if the gas settings has changed
bool postFork = block.number >= ontakeForkHeight();
uint64 newGasTarget = uint64(_gasIssuancePerSecond) * _basefeeAdjustmentQuotient;
if (postFork && newGasTarget != parentGasTarget) {
// adjust parentGasExcess to keep the basefee unchanged. Note that due to math
// calculation precision, the basefee may change slightly.
parentGasExcess =
Lib1559Math.adjustExcess(parentGasExcess, parentGasTarget, newGasTarget);
}

// Verify the base fee per gas is correct
(uint256 basefee, uint64 newGasExcess) = block.number < ontakeForkHeight()
? getBasefee(_anchorBlockId, _parentGasUsed)
: calculateBaseFee(
_gasTargetPerL1Block,
(uint256 basefee, uint64 newGasExcess) = postFork
? calculateBaseFee(
_gasIssuancePerSecond,
uint64(block.timestamp - parentTimestamp),
_basefeeAdjustmentQuotient,
gasExcess,
parentGasExcess,
_parentGasUsed
);
)
: getBasefee(_anchorBlockId, _parentGasUsed);

if (!skipFeeCheck() && block.basefee != basefee) revert L2_BASEFEE_MISMATCH();

Expand All @@ -300,13 +318,13 @@ contract TaikoL2 is EssentialContract {
}

// Update state variables

bytes32 parentHash = blockhash(parentId);
l2Hashes[parentId] = parentHash;

publicInputHash = newPublicInputHash;
gasExcess = newGasExcess;
parentGasExcess = newGasExcess;
parentTimestamp = uint64(block.timestamp);
parentGasTarget = newGasTarget;

emit Anchored(parentHash, newGasExcess);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/hekla/HeklaTaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ contract HeklaTaikoL1 is TaikoL1 {
maxAnchorHeightOffset: 64,
basefeeAdjustmentQuotient: 8,
basefeeSharingPctg: 75,
gasTargetPerL1Block: 60_000_000,
gasIssuancePerSecond: 5_000_000,
ontakeForkHeight: 720_000 // = 7200 * 100
});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/mainnet/MainnetTaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract MainnetTaikoL1 is TaikoL1 {
maxAnchorHeightOffset: 64,
basefeeAdjustmentQuotient: 8,
basefeeSharingPctg: 75,
gasTargetPerL1Block: 60_000_000,
gasIssuancePerSecond: 5_000_000,
ontakeForkHeight: 374_400 // = 7200 * 52
});
}
Expand Down
Loading