From 06710eb41132f7b920d80053ed8b906d90c18bb3 Mon Sep 17 00:00:00 2001 From: Brecht Devos Date: Mon, 7 Aug 2023 08:38:07 +0200 Subject: [PATCH] feat(protocol): Gas limit behavior changes (#14339) Co-authored-by: David Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com> --- packages/protocol/contracts/L1/TaikoData.sol | 3 +- .../contracts/L1/libs/LibProposing.sol | 10 +--- packages/protocol/contracts/L2/TaikoL2.sol | 44 ++++++++--------- .../protocol/docs/how_taiko_proves_blocks.md | 5 +- packages/protocol/test/TaikoL1TestBase.t.sol | 1 - packages/protocol/test/TaikoL2.t.sol | 47 ++++++------------- .../test/genesis/GenerateGenesis.g.sol | 8 +--- 7 files changed, 46 insertions(+), 72 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 254918bcff5..3bf9db204fa 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -52,11 +52,10 @@ library TaikoData { uint64 numEthDeposits; } - // 3 slots + // 2 slots struct BlockMetadataInput { bytes32 txListHash; address beneficiary; - uint32 gasLimit; uint24 txListByteStart; // byte-wise start index (inclusive) uint24 txListByteEnd; // byte-wise end index (exclusive) bool cacheTxListInfo; diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 33e2becf54f..41df72ebfde 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -103,7 +103,7 @@ library LibProposing { meta.txListHash = input.txListHash; meta.txListByteStart = input.txListByteStart; meta.txListByteEnd = input.txListByteEnd; - meta.gasLimit = input.gasLimit; + meta.gasLimit = config.blockMaxGasLimit; meta.beneficiary = input.beneficiary; meta.treasury = resolver.resolve(config.chainId, "treasury", false); meta.depositsProcessed = @@ -183,13 +183,7 @@ library LibProposing { view returns (bool cacheTxListInfo) { - if ( - input.beneficiary == address(0) - // - || input.gasLimit == 0 - // - || input.gasLimit > config.blockMaxGasLimit - ) revert L1_INVALID_METADATA(); + if (input.beneficiary == address(0)) revert L1_INVALID_METADATA(); if ( state.numBlocks diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 3c5b89163e2..3fd7c7b1c31 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -199,12 +199,11 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync { uint256 basefee; EIP1559Config memory config = getEIP1559Config(); if (config.gasIssuedPerSecond != 0) { - (basefee, gasExcess) = _calcBasefee( - config, - block.timestamp - parentTimestamp, - uint32(block.gaslimit), - parentGasUsed - ); + (basefee, gasExcess) = _calcBasefee({ + config: config, + timeSinceParent: block.timestamp - parentTimestamp, + parentGasUsed: parentGasUsed + }); } // On L2, basefee is not burnt, but sent to a treasury instead. @@ -233,17 +232,18 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync { } function getBasefee( - uint32 timeSinceParent, - uint32 gasLimit, + uint64 timeSinceParent, uint32 parentGasUsed ) public view returns (uint256 _basefee) { - (_basefee,) = _calcBasefee( - getEIP1559Config(), timeSinceParent, gasLimit, parentGasUsed - ); + (_basefee,) = _calcBasefee({ + config: getEIP1559Config(), + timeSinceParent: timeSinceParent, + parentGasUsed: parentGasUsed + }); } function getCrossChainBlockHash(uint256 number) @@ -321,31 +321,31 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync { function _calcBasefee( EIP1559Config memory config, uint256 timeSinceParent, - uint32 gasLimit, uint32 parentGasUsed ) private view returns (uint256 _basefee, uint64 _gasExcess) { - // Very important to cap _gasExcess uint64 unchecked { - uint32 parentGasUsedNet = parentGasUsed - > LibL2Consts.ANCHOR_GAS_COST - ? parentGasUsed - LibL2Consts.ANCHOR_GAS_COST - : 0; - - uint256 a = uint256(gasExcess) + parentGasUsedNet; - uint256 b = config.gasIssuedPerSecond * timeSinceParent; - _gasExcess = uint64((a.max(b) - b).min(type(uint64).max)); + uint32 parentGasUsedNet; + if (parentGasUsed > LibL2Consts.ANCHOR_GAS_COST) { + parentGasUsedNet = parentGasUsed - LibL2Consts.ANCHOR_GAS_COST; + } + + uint256 issued = timeSinceParent * config.gasIssuedPerSecond; + uint256 excess = (uint256(gasExcess) + parentGasUsedNet).max(issued); + // Very important to cap _gasExcess uint64 + _gasExcess = uint64((excess - issued).min(type(uint64).max)); } _basefee = Lib1559Math.calculatePrice({ xscale: config.xscale, yscale: config.yscale, xExcess: _gasExcess, - xPurchase: gasLimit + xPurchase: 0 }); + if (_basefee == 0) { // To make sure when 1559 is enabled, the basefee is non-zero // (geth never use 0 values for basefee) diff --git a/packages/protocol/docs/how_taiko_proves_blocks.md b/packages/protocol/docs/how_taiko_proves_blocks.md index 0c02260fc78..fe66f76f540 100644 --- a/packages/protocol/docs/how_taiko_proves_blocks.md +++ b/packages/protocol/docs/how_taiko_proves_blocks.md @@ -25,7 +25,10 @@ A valid transaction (defined in the Ethereum Yellow Paper): - Has a valid transaction signature. - Has a valid transaction nonce (equivalent to the sender account's current nonce). - Has no contract code deployed on the sender account (see EIP-3607 by Feist et al. [2021]). -- Has a gas limit no smaller than the intrinsic gas, _`g0`_, used by the transaction; and the sender account balance contains at least the cost, _`v0`_, required in up-front payment. +- Has a gas limit no smaller than the intrinsic gas, _`g0`_, used by the transaction. +- The sender account balance contains at least the cost, _`v0`_, required in up-front payment. +- The transaction has a gas limit that is smaller or equal to the amount of gas left in the block (with the block gas limit being the protocol constant _`blockMaxGasLimit`_). +- The transaction has a basefee that is greater than or equal the basefee of the block. #### Slicing and Consistency diff --git a/packages/protocol/test/TaikoL1TestBase.t.sol b/packages/protocol/test/TaikoL1TestBase.t.sol index a27d88f748e..3a71a67a88c 100644 --- a/packages/protocol/test/TaikoL1TestBase.t.sol +++ b/packages/protocol/test/TaikoL1TestBase.t.sol @@ -168,7 +168,6 @@ abstract contract TaikoL1TestBase is Test { bytes memory txList = new bytes(txListSize); TaikoData.BlockMetadataInput memory input = TaikoData.BlockMetadataInput({ beneficiary: proposer, - gasLimit: gasLimit, txListHash: keccak256(txList), txListByteStart: 0, txListByteEnd: txListSize, diff --git a/packages/protocol/test/TaikoL2.t.sol b/packages/protocol/test/TaikoL2.t.sol index 2a98cc260ce..9f5205c8b68 100644 --- a/packages/protocol/test/TaikoL2.t.sol +++ b/packages/protocol/test/TaikoL2.t.sol @@ -39,7 +39,7 @@ contract TestTaikoL2 is Test { function testAnchorTxsBlocktimeConstant() external { uint256 firstBasefee; for (uint256 i = 0; i < 100; i++) { - uint256 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 basefee = _getBasefeeAndPrint2(0, BLOCK_GAS_LIMIT); vm.fee(basefee); if (firstBasefee == 0) { @@ -60,7 +60,7 @@ contract TestTaikoL2 is Test { uint256 prevBasefee; for (uint256 i = 0; i < 32; i++) { - uint256 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 basefee = _getBasefeeAndPrint2(0, BLOCK_GAS_LIMIT); vm.fee(basefee); assertGe(basefee, prevBasefee); @@ -78,7 +78,7 @@ contract TestTaikoL2 is Test { uint256 prevBasefee; for (uint256 i = 0; i < 30; i++) { - uint256 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 basefee = _getBasefeeAndPrint2(0, BLOCK_GAS_LIMIT); vm.fee(basefee); if (prevBasefee != 0) { @@ -97,7 +97,7 @@ contract TestTaikoL2 is Test { // calling anchor in the same block more than once should fail function testAnchorTxsFailInTheSameBlock() external { - uint256 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 expectedBasefee = _getBasefeeAndPrint2(0, BLOCK_GAS_LIMIT); vm.fee(expectedBasefee); vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); @@ -110,7 +110,7 @@ contract TestTaikoL2 is Test { // calling anchor in the same block more than once should fail function testAnchorTxsFailByNonTaikoL2Signer() external { - uint256 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 expectedBasefee = _getBasefeeAndPrint2(0, BLOCK_GAS_LIMIT); vm.fee(expectedBasefee); vm.expectRevert(); _anchor(BLOCK_GAS_LIMIT); @@ -133,32 +133,18 @@ contract TestTaikoL2 is Test { } function testGetBasefee() external { - uint32 timeSinceParent = uint32(block.timestamp - L2.parentTimestamp()); - assertEq(_getBasefeeAndPrint(timeSinceParent, 0, 0), 317_609_019); - assertEq(_getBasefeeAndPrint(timeSinceParent, 1, 0), 317_609_019); - assertEq( - _getBasefeeAndPrint(timeSinceParent, 1_000_000, 0), 320_423_332 - ); - assertEq( - _getBasefeeAndPrint(timeSinceParent, 5_000_000, 0), 332_018_053 - ); - assertEq( - _getBasefeeAndPrint(timeSinceParent, 10_000_000, 0), 347_305_199 - ); + uint64 timeSinceParent = uint64(block.timestamp - L2.parentTimestamp()); + assertEq(_getBasefeeAndPrint(timeSinceParent, 0), 317_609_019); - timeSinceParent = uint32(100 + block.timestamp - L2.parentTimestamp()); - assertEq(_getBasefeeAndPrint(timeSinceParent, 0, 0), 54_544_902); - assertEq(_getBasefeeAndPrint(timeSinceParent, 1, 0), 54_544_902); - assertEq(_getBasefeeAndPrint(timeSinceParent, 1_000_000, 0), 55_028_221); - assertEq(_getBasefeeAndPrint(timeSinceParent, 5_000_000, 0), 57_019_452); - assertEq( - _getBasefeeAndPrint(timeSinceParent, 10_000_000, 0), 59_644_805 - ); + timeSinceParent += 100; + assertEq(_getBasefeeAndPrint(timeSinceParent, 0), 54_544_902); + + timeSinceParent += 10_000; + assertEq(_getBasefeeAndPrint(timeSinceParent, 0), 1); } function _getBasefeeAndPrint( - uint32 timeSinceParent, - uint32 gasLimit, + uint64 timeSinceParent, uint32 parentGasUsed ) private @@ -175,12 +161,10 @@ contract TestTaikoL2 is Test { Strings.toString(timeSinceParent), ", gasIssued=", Strings.toString(gasIssued), - ", gasLimit=", - Strings.toString(gasLimit), ", parentGasUsed=", Strings.toString(parentGasUsed) ); - _basefee = L2.getBasefee(timeSinceParent, gasLimit, parentGasUsed); + _basefee = L2.getBasefee(timeSinceParent, parentGasUsed); assertTrue(_basefee != 0); _msg = string.concat( @@ -194,7 +178,7 @@ contract TestTaikoL2 is Test { console2.log(_msg); } - function _getBasefeeAndPrint( + function _getBasefeeAndPrint2( uint32 timeSinceNow, uint32 gasLimit ) @@ -203,7 +187,6 @@ contract TestTaikoL2 is Test { { return _getBasefeeAndPrint( uint32(timeSinceNow + block.timestamp - L2.parentTimestamp()), - gasLimit, gasLimit + ANCHOR_GAS_COST ); } diff --git a/packages/protocol/test/genesis/GenerateGenesis.g.sol b/packages/protocol/test/genesis/GenerateGenesis.g.sol index 747aa73133c..85abe572580 100644 --- a/packages/protocol/test/genesis/GenerateGenesis.g.sol +++ b/packages/protocol/test/genesis/GenerateGenesis.g.sol @@ -34,7 +34,7 @@ contract TestGenerateGenesis is Test, AddressResolver { address private owner = configJSON.readAddress(".contractOwner"); address private admin = configJSON.readAddress(".contractAdmin"); - uint32 public constant BLOCK_GAS_LIMIT = 30_000_000; + // uint32 public constant BLOCK_GAS_LIMIT = 30_000_000; function testContractDeployment() public { assertEq(block.chainid, 167); @@ -107,11 +107,7 @@ contract TestGenerateGenesis is Test, AddressResolver { for (uint32 i = 0; i < 300; i++) { vm.roll(block.number + 1); vm.warp(taikoL2.parentTimestamp() + 12); - vm.fee( - taikoL2.getBasefee( - 12, BLOCK_GAS_LIMIT, i + LibL2Consts.ANCHOR_GAS_COST - ) - ); + vm.fee(taikoL2.getBasefee(12, i + LibL2Consts.ANCHOR_GAS_COST)); uint256 gasLeftBefore = gasleft();