Skip to content

Commit

Permalink
fix(protocol): refine EIP-1559 basefee calculation and gas excess adj…
Browse files Browse the repository at this point in the history
…ustment in TaikoL2 (#17871)

Co-authored-by: dantaik <dantaik@users.noreply.github.com>
Co-authored-by: Brecht Devos <Brechtp.Devos@gmail.com>
  • Loading branch information
3 people authored Aug 6, 2024
1 parent 699cab1 commit 4470005
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 94 deletions.
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
)
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

0 comments on commit 4470005

Please sign in to comment.