From 167d957eb340135b9224707395c5ce15ed3e991f Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 24 Mar 2023 13:40:26 +0800 Subject: [PATCH 01/90] change timebased fee calculation --- .../protocol/contracts/L1/TaikoConfig.sol | 10 +- packages/protocol/contracts/L1/TaikoData.sol | 6 +- .../contracts/L1/libs/LibTokenomics.sol | 45 ++- .../contracts/L1/libs/LibVerifying.sol | 19 +- .../contracts/test/L1/TestTaikoL1.sol | 10 +- .../test/L1/TestTaikoL1EnableTokenomics.sol | 10 +- packages/protocol/test2/LibTokenomics.t.sol | 292 ++++++++++++++++-- packages/protocol/test2/TaikoL1.t.sol | 10 +- .../contract-documentation/L1/TaikoData.md | 5 +- 9 files changed, 310 insertions(+), 97 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 96d5fbc47ac..0cbc521caee 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -47,17 +47,11 @@ library TaikoConfig { skipZKPVerification: false, proposingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, - avgTimeCap: 60 seconds * 1000, - gracePeriodPctg: 200, - maxPeriodPctg: 400, - multiplerPctg: 300 + startBips: 5000 }), provingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, - avgTimeCap: 30 minutes * 1000, - gracePeriodPctg: 200, - maxPeriodPctg: 400, - multiplerPctg: 300 + startBips: 5000 }) }); } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index e34bbed85f3..f039c91faa6 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -9,11 +9,7 @@ pragma solidity ^0.8.18; library TaikoData { struct FeeConfig { uint16 avgTimeMAF; - uint64 avgTimeCap; // miliseconds - uint16 gracePeriodPctg; - uint16 maxPeriodPctg; - // extra fee/reward on top of baseFee - uint16 multiplerPctg; + uint16 startBips; } struct Config { diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol index c588687a2ad..c31c556fa8f 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -14,6 +14,8 @@ import { import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; +// import {console2} from "forge-std/console2.sol"; + library LibTokenomics { using LibMath for uint256; uint256 private constant TWEI_TO_WEI = 1E12; @@ -89,9 +91,8 @@ library LibTokenomics { feeConfig: config.proposingConfig, feeBase: feeBase, isProposal: true, - tNow: block.timestamp, - tLast: state.lastProposedAt, - tAvg: state.avgBlockTime + timeUsed: block.timestamp - state.lastProposedAt, + timeAverage: state.avgBlockTime }); fee = getSlotsAdjustedFee({ state: state, @@ -124,7 +125,7 @@ library LibTokenomics { ) internal view - returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) + returns (uint256 newFeeBase, uint256 reward, uint256 premiumRate) { if (proposedAt > provenAt) revert L1_INVALID_PARAM(); @@ -132,15 +133,14 @@ library LibTokenomics { if (state.lastVerifiedBlockId <= config.constantFeeRewardBlocks) { reward = feeBase; newFeeBase = feeBase; - // tRelBp = 0; + // premiumRate = 0; } else { - (newFeeBase, tRelBp) = getTimeAdjustedFee({ + (newFeeBase, premiumRate) = getTimeAdjustedFee({ feeConfig: config.provingConfig, feeBase: feeBase, isProposal: false, - tNow: provenAt, - tLast: proposedAt, - tAvg: state.avgProofTime + timeUsed: provenAt - proposedAt, + timeAverage: state.avgProofTime }); reward = getSlotsAdjustedFee({ state: state, @@ -180,26 +180,23 @@ library LibTokenomics { TaikoData.FeeConfig memory feeConfig, uint256 feeBase, bool isProposal, - uint256 tNow, // seconds - uint256 tLast, // seconds - uint256 tAvg // milliseconds - ) internal pure returns (uint256 newFeeBase, uint256 tRelBp) { - if (tAvg == 0 || tNow == tLast) { + uint256 timeUsed, // seconds + uint256 timeAverage // milliseconds + ) internal pure returns (uint256 newFeeBase, uint256 premiumRate) { + if (timeAverage == 0) { return (feeBase, 0); } - unchecked { - tAvg = tAvg.min(feeConfig.avgTimeCap); - uint256 max = (feeConfig.maxPeriodPctg * tAvg) / 100; - uint256 grace = (feeConfig.gracePeriodPctg * tAvg) / 100; - uint256 t = ((tNow - tLast) * 1000).max(grace).min(max); - tRelBp = (10000 * (t - grace)) / (max - grace); // [0-10000] - uint256 alpha = 10000 + (tRelBp * feeConfig.multiplerPctg) / 100; + uint p = feeConfig.startBips; // [0-10000] + uint a = timeAverage; + uint t = (timeUsed * 1000).min(a * 2); // millisconds + + newFeeBase = (feeBase * (10000 + (t * p) / a - p)) / 10000; if (isProposal) { - newFeeBase = (feeBase * 10000) / alpha; // fee - } else { - newFeeBase = (feeBase * alpha) / 10000; // reward + newFeeBase = (feeBase * 2) - newFeeBase; + } else if (p > 0) { + premiumRate = ((t.max(a) - a) * 10000) / a; } } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 846d4e2fe12..43f20523f81 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -123,8 +123,11 @@ library LibVerifying { TaikoData.ProposedBlock storage blk ) private returns (bytes32 blockHash, bytes32 signalRoot) { if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 amount, uint256 tRelBp) = LibTokenomics - .getProofReward({ + ( + uint256 newFeeBase, + uint256 amount, + uint256 premiumRate + ) = LibTokenomics.getProofReward({ state: state, config: config, provenAt: fc.provenAt, @@ -134,10 +137,9 @@ library LibVerifying { // reward the prover _addToBalance(state, fc.prover, amount); - // refund proposer deposit for valid blocks unchecked { - // tRelBp in [0-10000] - amount = (blk.deposit * (10000 - tRelBp)) / 10000; + // premiumRate in [0-10000] + amount = (blk.deposit * (10000 - premiumRate)) / 10000; } _addToBalance(state, blk.proposer, amount); @@ -216,10 +218,7 @@ library LibVerifying { function _checkFeeConfig( TaikoData.FeeConfig memory feeConfig ) private pure { - if ( - feeConfig.avgTimeMAF <= 1 || - feeConfig.avgTimeCap == 0 || - feeConfig.gracePeriodPctg > feeConfig.maxPeriodPctg - ) revert L1_INVALID_CONFIG(); + if (feeConfig.avgTimeMAF <= 1 || feeConfig.startBips > 10000) + revert L1_INVALID_CONFIG(); } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index e4e40490fe3..5a0ddbe2cfa 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -39,18 +39,12 @@ contract TestTaikoL1 is TaikoL1 { config.proposingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - avgTimeCap: 48 seconds * 1000, - gracePeriodPctg: 125, - maxPeriodPctg: 375, - multiplerPctg: 300 + startBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - avgTimeCap: 4 seconds * 1000, - gracePeriodPctg: 125, - maxPeriodPctg: 375, - multiplerPctg: 300 + startBips: 5000 }); } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 521fca91f06..04180d5657e 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -41,18 +41,12 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { config.proposingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - avgTimeCap: 48 seconds * 1000, - gracePeriodPctg: 125, - maxPeriodPctg: 375, - multiplerPctg: 300 + startBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - avgTimeCap: 5 seconds * 1000, - gracePeriodPctg: 125, - maxPeriodPctg: 375, - multiplerPctg: 300 + startBips: 5000 }); } } diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index 5c20e840c4e..0a2080eb6ab 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -16,30 +16,278 @@ contract TaikoL1WithConfig is Test { uint64 multiplerPctg; } - function test_getTimeAdjustedFee() public { - uint256 feeBase = 10 ether; - uint256 tLast = 100000; - uint256 tAvg = 40; - uint256 tNow = tLast + tAvg; + function testTokenomicsFeeCalcWithNonZeroStartBips() public { + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 0 seconds, + isProposal: true, + startBips: 4000, // 40% + expectedFeeBase: 140 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 20 seconds, + isProposal: true, + startBips: 4000, // 40% + expectedFeeBase: 120 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 40 seconds, + isProposal: true, + startBips: 4000, // 40% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 60 seconds, + isProposal: true, + startBips: 4000, // 40% + expectedFeeBase: 80 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 80 seconds, + isProposal: true, + startBips: 4000, // 40% + expectedFeeBase: 60 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 81 seconds, + isProposal: true, + startBips: 4000, // 40% + expectedFeeBase: 60 ether, + expectedPreimumRate: 0 + }); + } + + function testTokenomicsFeeCalcWithZeroStartBips() public { + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 0 seconds, + isProposal: true, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 20 seconds, + isProposal: true, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 40 seconds, + isProposal: true, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 60 seconds, + isProposal: true, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 80 seconds, + isProposal: true, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 81 seconds, + isProposal: true, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + } + + function testTokenomicsRewardCalcWithNonZeroStartBips() public { + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 0 seconds, + isProposal: false, + startBips: 4000, // 40% + expectedFeeBase: 60 ether, + expectedPreimumRate: 0 + }); + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 20 seconds, + isProposal: false, + startBips: 4000, // 40% + expectedFeeBase: 80 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 40 seconds, + isProposal: false, + startBips: 4000, // 40% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 60 seconds, + isProposal: false, + startBips: 4000, // 40% + expectedFeeBase: 120 ether, + expectedPreimumRate: 5000 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 80 seconds, + isProposal: false, + startBips: 4000, // 40% + expectedFeeBase: 140 ether, + expectedPreimumRate: 10000 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 81 seconds, + isProposal: false, + startBips: 4000, // 40% + expectedFeeBase: 140 ether, + expectedPreimumRate: 10000 + }); + } + + function testTokenomicsRewardCalcWithZeroStartBips() public { + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 0 seconds, + isProposal: false, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 20 seconds, + isProposal: false, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 40 seconds, + isProposal: false, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 60 seconds, + isProposal: false, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 80 seconds, + isProposal: false, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + + _oneTest({ + feeBase: 100 ether, + timeAverageSec: 40 seconds, + timeUsedSec: 81 seconds, + isProposal: false, + startBips: 0, // 0% + expectedFeeBase: 100 ether, + expectedPreimumRate: 0 + }); + } + + function _oneTest( + uint256 feeBase, + uint256 timeAverageSec, + uint256 timeUsedSec, + bool isProposal, + uint16 startBips, + uint256 expectedFeeBase, + uint256 expectedPreimumRate + ) internal { TaikoData.FeeConfig memory feeConfig = TaikoData.FeeConfig({ avgTimeMAF: 1024, - avgTimeCap: uint16(tAvg * 1000), - gracePeriodPctg: 100, - maxPeriodPctg: 400, - multiplerPctg: 200 - }); - - (uint256 newFeeBase, uint256 tRelBp) = LibTokenomics.getTimeAdjustedFee( - feeConfig, - feeBase, - true, - tNow, // seconds - tLast, // seconds - (tAvg * 1000) // miliseconds - ); - - assertEq(newFeeBase, feeBase); - assertEq(tRelBp, 0); + startBips: startBips + }); + + (uint256 _feeBase, uint256 _premiumRate) = LibTokenomics + .getTimeAdjustedFee( + feeConfig, + feeBase, + isProposal, + timeUsedSec, + timeAverageSec * 1000 + ); + + assertEq(_premiumRate, expectedPreimumRate); + assertEq(_feeBase, expectedFeeBase); } } diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index be427ea5d9a..9f41c6fa1c6 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -37,18 +37,12 @@ contract TaikoL1WithConfig is TaikoL1 { config.proposingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - avgTimeCap: 10 minutes * 1000, - gracePeriodPctg: 100, - maxPeriodPctg: 400, - multiplerPctg: 400 + startBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - avgTimeCap: 10 minutes * 1000, - gracePeriodPctg: 100, - maxPeriodPctg: 400, - multiplerPctg: 400 + startBips: 5000 }); } } diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 5763d14f532..43d7db7b17a 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -9,10 +9,7 @@ title: TaikoData ```solidity struct FeeConfig { uint16 avgTimeMAF; - uint64 avgTimeCap; - uint16 gracePeriodPctg; - uint16 maxPeriodPctg; - uint16 multiplerPctg; + uint16 startBips; } ``` From c693da51d12ad3dbc575a45c3912ae06f61bae81 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 24 Mar 2023 13:42:59 +0800 Subject: [PATCH 02/90] change timebased fee calculation --- packages/protocol/contracts/L1/TaikoConfig.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 0cbc521caee..c3819c14b99 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -47,11 +47,11 @@ library TaikoConfig { skipZKPVerification: false, proposingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, - startBips: 5000 + startBips: 2500 // [125% -> 75%] }), provingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, - startBips: 5000 + startBips: 2500 // [75% -> 125%] }) }); } From 749c63fc60c7e12aa8e1b4796564d00ab2e2936c Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 24 Mar 2023 13:57:30 +0800 Subject: [PATCH 03/90] change timebased fee calculation --- packages/protocol/test2/LibTokenomics.t.sol | 52 ++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index 0a2080eb6ab..06d2adef48c 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -17,7 +17,7 @@ contract TaikoL1WithConfig is Test { } function testTokenomicsFeeCalcWithNonZeroStartBips() public { - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -27,7 +27,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -37,7 +37,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -47,7 +47,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -57,7 +57,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -67,7 +67,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -79,7 +79,7 @@ contract TaikoL1WithConfig is Test { } function testTokenomicsFeeCalcWithZeroStartBips() public { - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -89,7 +89,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -99,7 +99,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -109,7 +109,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -119,7 +119,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -129,7 +129,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -141,7 +141,7 @@ contract TaikoL1WithConfig is Test { } function testTokenomicsRewardCalcWithNonZeroStartBips() public { - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -151,7 +151,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -161,7 +161,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -171,7 +171,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -181,7 +181,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 5000 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -191,7 +191,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 10000 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -203,7 +203,7 @@ contract TaikoL1WithConfig is Test { } function testTokenomicsRewardCalcWithZeroStartBips() public { - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -213,7 +213,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -223,7 +223,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -233,7 +233,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -243,7 +243,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -253,7 +253,7 @@ contract TaikoL1WithConfig is Test { expectedPreimumRate: 0 }); - _oneTest({ + testTimeAdjustedFee({ feeBase: 100 ether, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -264,7 +264,7 @@ contract TaikoL1WithConfig is Test { }); } - function _oneTest( + function testTimeAdjustedFee( uint256 feeBase, uint256 timeAverageSec, uint256 timeUsedSec, @@ -272,7 +272,7 @@ contract TaikoL1WithConfig is Test { uint16 startBips, uint256 expectedFeeBase, uint256 expectedPreimumRate - ) internal { + ) private { TaikoData.FeeConfig memory feeConfig = TaikoData.FeeConfig({ avgTimeMAF: 1024, startBips: startBips From 699a045483415515011c2f87a91b98aad2cbdabf Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Sun, 26 Mar 2023 20:11:00 +0800 Subject: [PATCH 04/90] rename --- .../protocol/contracts/L1/TaikoConfig.sol | 4 +- packages/protocol/contracts/L1/TaikoData.sol | 2 +- .../contracts/L1/libs/LibTokenomics.sol | 2 +- .../contracts/L1/libs/LibVerifying.sol | 2 +- .../contracts/test/L1/TestTaikoL1.sol | 4 +- .../test/L1/TestTaikoL1EnableTokenomics.sol | 4 +- packages/protocol/foundry.toml | 2 +- packages/protocol/snapshot.txt | 2 +- packages/protocol/test2/LibTokenomics.t.sol | 52 +++++++++---------- packages/protocol/test2/TaikoL1.t.sol | 4 +- 10 files changed, 39 insertions(+), 39 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 3c23674494c..01e698b6e90 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -47,11 +47,11 @@ library TaikoConfig { skipZKPVerification: false, proposingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, - startBips: 2500 // [125% -> 75%] + dampingFactorBips: 2500 // [125% -> 75%] }), provingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, - startBips: 2500 // [75% -> 125%] + dampingFactorBips: 2500 // [75% -> 125%] }) }); } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 0df9df2ac7f..9420fbdd38f 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -9,7 +9,7 @@ pragma solidity ^0.8.18; library TaikoData { struct FeeConfig { uint16 avgTimeMAF; - uint16 startBips; + uint16 dampingFactorBips; } struct Config { diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol index b981740fbbe..f887f219414 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -166,7 +166,7 @@ library LibTokenomics { return (feeBase, 0); } unchecked { - uint p = feeConfig.startBips; // [0-10000] + uint p = feeConfig.dampingFactorBips; // [0-10000] uint a = timeAverage; uint t = (timeUsed * 1000).min(a * 2); // millisconds diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 8b5db163b77..1c8a598b5c4 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -206,7 +206,7 @@ library LibVerifying { function _checkFeeConfig( TaikoData.FeeConfig memory feeConfig ) private pure { - if (feeConfig.avgTimeMAF <= 1 || feeConfig.startBips > 10000) + if (feeConfig.avgTimeMAF <= 1 || feeConfig.dampingFactorBips > 10000) revert L1_INVALID_CONFIG(); } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index 39519c406ea..104b98f4810 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -39,12 +39,12 @@ contract TestTaikoL1 is TaikoL1 { config.proposingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - startBips: 5000 + dampingFactorBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - startBips: 5000 + dampingFactorBips: 5000 }); } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index adaab8add17..7d456fa1113 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -41,12 +41,12 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { config.proposingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - startBips: 5000 + dampingFactorBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - startBips: 5000 + dampingFactorBips: 5000 }); } } diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index 80aacc34bcd..b20a5c1e9d4 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -5,6 +5,6 @@ test = 'test2' libs = ['lib'] optimizer = true optimizer_runs = 200 -gas_reports = ["TaikoL1", "TaikoL1WithConfig", "TaikoL2", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] +gas_reports = ["TaikoL1", "TaikoWithConfig", "TaikoL2", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/packages/protocol/snapshot.txt b/packages/protocol/snapshot.txt index 04538961afc..eb13359d002 100644 --- a/packages/protocol/snapshot.txt +++ b/packages/protocol/snapshot.txt @@ -8,4 +8,4 @@ TaikoL1Test:test_block_time_increases_but_fee_decreases() (gas: 18550268) TaikoL1Test:test_more_blocks_than_ring_buffer_size() (gas: 20880651) TaikoL1Test:test_multiple_blocks_in_one_L1_block() (gas: 757668) TaikoL1Test:test_verifying_multiple_blocks_once() (gas: 2724631) -TaikoL1WithConfig:test_getTimeAdjustedFee() (gas: 1461) \ No newline at end of file +TaikoWithConfig:test_getTimeAdjustedFee() (gas: 1461) \ No newline at end of file diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index 06d2adef48c..88b83d92b82 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -22,7 +22,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: true, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 140 ether, expectedPreimumRate: 0 }); @@ -32,7 +32,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: true, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 120 ether, expectedPreimumRate: 0 }); @@ -42,7 +42,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: true, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -52,7 +52,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: true, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 80 ether, expectedPreimumRate: 0 }); @@ -62,7 +62,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: true, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 60 ether, expectedPreimumRate: 0 }); @@ -72,7 +72,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: true, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 60 ether, expectedPreimumRate: 0 }); @@ -84,7 +84,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: true, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -94,7 +94,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: true, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -104,7 +104,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: true, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -114,7 +114,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: true, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -124,7 +124,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: true, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -134,7 +134,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: true, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -146,7 +146,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: false, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 60 ether, expectedPreimumRate: 0 }); @@ -156,7 +156,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: false, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 80 ether, expectedPreimumRate: 0 }); @@ -166,7 +166,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: false, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -176,7 +176,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: false, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 120 ether, expectedPreimumRate: 5000 }); @@ -186,7 +186,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: false, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 140 ether, expectedPreimumRate: 10000 }); @@ -196,7 +196,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: false, - startBips: 4000, // 40% + dampingFactorBips: 4000, // 40% expectedFeeBase: 140 ether, expectedPreimumRate: 10000 }); @@ -208,7 +208,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: false, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -218,7 +218,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: false, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -228,7 +228,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: false, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -238,7 +238,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: false, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -248,7 +248,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: false, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -258,7 +258,7 @@ contract TaikoL1WithConfig is Test { timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: false, - startBips: 0, // 0% + dampingFactorBips: 0, // 0% expectedFeeBase: 100 ether, expectedPreimumRate: 0 }); @@ -269,13 +269,13 @@ contract TaikoL1WithConfig is Test { uint256 timeAverageSec, uint256 timeUsedSec, bool isProposal, - uint16 startBips, + uint16 dampingFactorBips, uint256 expectedFeeBase, uint256 expectedPreimumRate ) private { TaikoData.FeeConfig memory feeConfig = TaikoData.FeeConfig({ avgTimeMAF: 1024, - startBips: startBips + dampingFactorBips: dampingFactorBips }); (uint256 _feeBase, uint256 _premiumRate) = LibTokenomics diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index cbb02462ac9..9f124f683a1 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -37,12 +37,12 @@ contract TaikoL1WithConfig is TaikoL1 { config.proposingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - startBips: 5000 + dampingFactorBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, - startBips: 5000 + dampingFactorBips: 5000 }); } } From 975b057114ddd0675b8512a63169826a37881be0 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 10:09:17 +0800 Subject: [PATCH 05/90] rename --- packages/protocol/contracts/L1/TaikoL1.sol | 10 +++++----- .../L1/libs/{LibTokenomics.sol => LibL1Tokenomics.sol} | 2 +- packages/protocol/contracts/L1/libs/LibProposing.sol | 4 ++-- packages/protocol/contracts/L1/libs/LibProving.sol | 2 +- packages/protocol/contracts/L1/libs/LibUtils.sol | 2 +- packages/protocol/contracts/L1/libs/LibVerifying.sol | 4 ++-- .../{LibTokenomics.t.sol => LibL1Tokenomics.t.sol} | 6 +++--- packages/protocol/test2/TaikoL2.t.sol | 2 +- .../reference/contract-documentation/L1/TaikoData.md | 4 +--- .../reference/contract-documentation/L1/TaikoErrors.md | 6 ------ .../reference/contract-documentation/L1/TaikoEvents.md | 6 ++++++ .../contract-documentation/bridge/TokenVault.md | 9 +++------ 12 files changed, 26 insertions(+), 31 deletions(-) rename packages/protocol/contracts/L1/libs/{LibTokenomics.sol => LibL1Tokenomics.sol} (99%) rename packages/protocol/test2/{LibTokenomics.t.sol => LibL1Tokenomics.t.sol} (97%) diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 9191b8b7f8c..d39c349faca 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -11,7 +11,7 @@ import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; import {LibProposing} from "./libs/LibProposing.sol"; import {LibProving} from "./libs/LibProving.sol"; -import {LibTokenomics} from "./libs/LibVerifying.sol"; +import {LibL1Tokenomics} from "./libs/LibVerifying.sol"; import {LibUtils} from "./libs/LibUtils.sol"; import {LibVerifying} from "./libs/LibVerifying.sol"; import {TaikoConfig} from "./TaikoConfig.sol"; @@ -115,11 +115,11 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { } function deposit(uint256 amount) external nonReentrant { - LibTokenomics.deposit(state, AddressResolver(this), amount); + LibL1Tokenomics.deposit(state, AddressResolver(this), amount); } function withdraw(uint256 amount) external nonReentrant { - LibTokenomics.withdraw(state, AddressResolver(this), amount); + LibL1Tokenomics.withdraw(state, AddressResolver(this), amount); } function getBalance(address addr) public view returns (uint256) { @@ -131,7 +131,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { view returns (uint256 feeAmount, uint256 depositAmount) { - (, feeAmount, depositAmount) = LibTokenomics.getBlockFee( + (, feeAmount, depositAmount) = LibL1Tokenomics.getBlockFee( state, getConfig() ); @@ -141,7 +141,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { uint64 provenAt, uint64 proposedAt ) public view returns (uint256 reward) { - (, reward, ) = LibTokenomics.getProofReward({ + (, reward, ) = LibL1Tokenomics.getProofReward({ state: state, config: getConfig(), provenAt: provenAt, diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol similarity index 99% rename from packages/protocol/contracts/L1/libs/LibTokenomics.sol rename to packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index f887f219414..0d17b9c20a5 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -16,7 +16,7 @@ import {TaikoToken} from "../TaikoToken.sol"; // import {console2} from "forge-std/console2.sol"; -library LibTokenomics { +library LibL1Tokenomics { using LibMath for uint256; uint256 private constant TWEI_TO_WEI = 1E12; diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 44963d97236..d9ac44a22cf 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; -import {LibTokenomics} from "./LibTokenomics.sol"; +import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -89,7 +89,7 @@ library LibProposing { blk.proposer = msg.sender; if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 fee, uint256 deposit) = LibTokenomics + (uint256 newFeeBase, uint256 fee, uint256 deposit) = LibL1Tokenomics .getBlockFee(state, config); uint256 burnAmount = fee + deposit; diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 2b63a8b9162..bc51cb67192 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; -import {LibTokenomics} from "./LibTokenomics.sol"; +import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import {TaikoData} from "../../L1/TaikoData.sol"; diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 35288749c35..38fa4eb3a2d 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.18; import {LibMath} from "../../libs/LibMath.sol"; -import {LibTokenomics} from "./LibTokenomics.sol"; +import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 60e07c0c6d8..de7a59235a1 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; -import {LibTokenomics} from "./LibTokenomics.sol"; +import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -120,7 +120,7 @@ library LibVerifying { uint256 newFeeBase, uint256 amount, uint256 premiumRate - ) = LibTokenomics.getProofReward({ + ) = LibL1Tokenomics.getProofReward({ state: state, config: config, provenAt: fc.provenAt, diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol similarity index 97% rename from packages/protocol/test2/LibTokenomics.t.sol rename to packages/protocol/test2/LibL1Tokenomics.t.sol index 88b83d92b82..61da8fd4fc6 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {TaikoData} from "../contracts/L1/TaikoData.sol"; -import {LibTokenomics} from "../contracts/L1/libs/LibTokenomics.sol"; +import {LibL1Tokenomics} from "../contracts/L1/libs/LibL1Tokenomics.sol"; -contract TaikoL1WithConfig is Test { +contract TestLibL1Tokenomics is Test { struct FeeConfig { uint64 avgTimeMAF; uint64 avgTimeCap; @@ -278,7 +278,7 @@ contract TaikoL1WithConfig is Test { dampingFactorBips: dampingFactorBips }); - (uint256 _feeBase, uint256 _premiumRate) = LibTokenomics + (uint256 _feeBase, uint256 _premiumRate) = LibL1Tokenomics .getTimeAdjustedFee( feeConfig, feeBase, diff --git a/packages/protocol/test2/TaikoL2.t.sol b/packages/protocol/test2/TaikoL2.t.sol index 02e37a5f036..f8f5525e045 100644 --- a/packages/protocol/test2/TaikoL2.t.sol +++ b/packages/protocol/test2/TaikoL2.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import "forge-std/console2.sol"; import "../contracts/L2/TaikoL2.sol"; -contract ReadBlockhashVsCalldata is Test { +contract TestTaikoL2 is Test { TaikoL2 public L2; function setUp() public { diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 0291080fa9d..64febe555b0 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -9,7 +9,7 @@ title: TaikoData ```solidity struct FeeConfig { uint16 avgTimeMAF; - uint16 startBips; + uint16 dampingFactorBips; } ``` @@ -27,7 +27,6 @@ struct Config { uint256 maxBytesPerTxList; uint256 minTxGasLimit; uint256 slotSmoothingFactor; - uint256 anchorTxGasLimit; uint256 rewardBurnBips; uint256 proposerDepositPctg; uint256 feeBaseMAF; @@ -107,7 +106,6 @@ struct BlockEvidence { bytes32 blockHash; bytes32 signalRoot; address prover; - uint32 gasUsed; } ``` diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index d783ef3470c..4028d4ecf94 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -16,12 +16,6 @@ error L1_ALREADY_PROVEN() error L1_BLOCK_ID() ``` -### L1_CONFLICT_PROOF - -```solidity -error L1_CONFLICT_PROOF() -``` - ### L1_CONTRACT_NOT_ALLOWED ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md index 0fe2a1627e8..e80c2e6d47c 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md @@ -16,6 +16,12 @@ event BlockProposed(uint256 id, struct TaikoData.BlockMetadata meta, bool txList event BlockProven(uint256 id, bytes32 parentHash, bytes32 blockHash, bytes32 signalRoot, address prover) ``` +### ConflictingProof + +```solidity +event ConflictingProof(uint64 blockId, bytes32 parentHash, bytes32 conflictingBlockHash, bytes32 conflictingSignalRoot, bytes32 blockHash, bytes32 signalRoot) +``` + ### BlockVerified ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/bridge/TokenVault.md b/packages/website/pages/docs/reference/contract-documentation/bridge/TokenVault.md index a41b923110d..7be15ffaecd 100644 --- a/packages/website/pages/docs/reference/contract-documentation/bridge/TokenVault.md +++ b/packages/website/pages/docs/reference/contract-documentation/bridge/TokenVault.md @@ -98,12 +98,6 @@ error TOKENVAULT_INVALID_TO() error TOKENVAULT_INVALID_VALUE() ``` -### TOKENVAULT_INVALID_CALL_VALUE - -```solidity -error TOKENVAULT_INVALID_CALL_VALUE() -``` - ### TOKENVAULT_INVALID_TOKEN ```solidity @@ -161,6 +155,9 @@ function sendEther(uint256 destChainId, address to, uint256 gasLimit, uint256 pr Receives Ether and constructs a Bridge message. Sends the Ether and message along to the Bridge. +_This function doesn't' seem to belong here as it has nothing to +do with ERC20 tokens. It's added here only for convenience._ + #### Parameters | Name | Type | Description | From 0e6242c4044757c30f5ca36641314f9cb5fa92a8 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 10:11:05 +0800 Subject: [PATCH 06/90] rename --- packages/protocol/contracts/L1/TaikoL1.sol | 2 +- packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index d39c349faca..2330511a38b 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -9,9 +9,9 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../common/AddressResolver.sol"; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; +import {LibL1Tokenomics} from "./libs/LibVerifying.sol"; import {LibProposing} from "./libs/LibProposing.sol"; import {LibProving} from "./libs/LibProving.sol"; -import {LibL1Tokenomics} from "./libs/LibVerifying.sol"; import {LibUtils} from "./libs/LibUtils.sol"; import {LibVerifying} from "./libs/LibVerifying.sol"; import {TaikoConfig} from "./TaikoConfig.sol"; diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 0d17b9c20a5..41342081682 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -14,8 +14,6 @@ import { import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; -// import {console2} from "forge-std/console2.sol"; - library LibL1Tokenomics { using LibMath for uint256; uint256 private constant TWEI_TO_WEI = 1E12; From b256bc14eaae44bd4ccd74672a327e82f1d2910e Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 14:40:01 +0800 Subject: [PATCH 07/90] implement 1559 for L2 --- .../protocol/contracts/L1/TaikoConfig.sol | 2 + packages/protocol/contracts/L1/TaikoData.sol | 5 +- .../contracts/L1/libs/LibL1Tokenomics.sol | 1 - .../contracts/L1/libs/LibL2Tokenmocis.sol | 63 +++++++++++++++++++ packages/protocol/contracts/libs/LibMath.sol | 9 +++ packages/protocol/foundry.toml | 2 +- 6 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index ce912aae1f0..de9d28a9ea2 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,6 +23,8 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, + gasTarget: 60000000, + adjustmentQuotient: 8, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 7eff5ca49aa..ca159bd7965 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,6 +21,8 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; + uint256 gasTarget; + uint256 adjustmentQuotient; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; @@ -124,6 +126,7 @@ library TaikoData { mapping(uint256 blockId => mapping(bytes32 parentHash => uint256 forkChoiceId)) forkChoiceIds; mapping(address account => uint256 balance) balances; mapping(bytes32 txListHash => TxListInfo) txListInfo; + uint256 gasExcess; // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; @@ -143,6 +146,6 @@ library TaikoData { uint64 avgProofTime; // miliseconds uint64 feeBase; // Reserved - uint256[42] __gap; + uint256[41] __gap; } } diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 41342081682..f0069fa84ea 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -16,7 +16,6 @@ import {TaikoToken} from "../TaikoToken.sol"; library LibL1Tokenomics { using LibMath for uint256; - uint256 private constant TWEI_TO_WEI = 1E12; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol new file mode 100644 index 00000000000..aca17081376 --- /dev/null +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.18; + +import {AddressResolver} from "../../common/AddressResolver.sol"; +import {LibMath} from "../../libs/LibMath.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; +import {TaikoData} from "../TaikoData.sol"; + +library LibL2Tokenomics { + using LibMath for uint256; + uint256 private constant ETH_BLOCK_TIME = 12 seconds; + + function get1559Basefee( + TaikoData.State storage state, + TaikoData.Config memory config, + uint256 gasInBlock, + uint256 blockTime, + uint256 adjustmentQuotient + ) + internal + view + returns (uint256 newGasExcess, uint256 basefee, uint256 gasPurchaseCost) + { + return + calculate1559Basefee( + state.gasExcess, + config.gasTarget, + config.adjustmentQuotient, + gasInBlock, + block.timestamp - state.lastProposedAt + ); + } + + function calculate1559Basefee( + uint256 gasExcess, + uint256 gasTarget, + uint256 adjustmentQuotient, + uint256 gasInBlock, + uint256 blockTime + ) + internal + pure + returns (uint256 newGasExcess, uint256 basefee, uint256 gasPurchaseCost) + { + uint256 adjustment = (gasTarget * blockTime) / ETH_BLOCK_TIME; + newGasExcess = gasExcess.max(adjustment) - adjustment; + + gasPurchaseCost = + LibMath.exp( + (newGasExcess + gasInBlock) / gasTarget / adjustmentQuotient + ) - + LibMath.exp(newGasExcess / gasTarget / adjustmentQuotient); + basefee = gasPurchaseCost / gasInBlock; + newGasExcess += gasInBlock; + } +} diff --git a/packages/protocol/contracts/libs/LibMath.sol b/packages/protocol/contracts/libs/LibMath.sol index e6f20e92d95..e9716917251 100644 --- a/packages/protocol/contracts/libs/LibMath.sol +++ b/packages/protocol/contracts/libs/LibMath.sol @@ -30,4 +30,13 @@ library LibMath { function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } + + /** + * @notice Returns the power of Eular number. + * @param x The base. + * @return The power of Eular number. + */ + function exp(uint256 x) internal pure returns (uint256) { + // TODO(Daniel): Implement & test this function. + } } diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index b20a5c1e9d4..e0650a09a29 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -5,6 +5,6 @@ test = 'test2' libs = ['lib'] optimizer = true optimizer_runs = 200 -gas_reports = ["TaikoL1", "TaikoWithConfig", "TaikoL2", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] +gas_reports = ["TaikoL1", "TaikoL2", "TaikoL1WithConfig", "LibL1Tokenomics", "LibL2Tokenomics", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] # See more config options https://github.com/foundry-rs/foundry/tree/master/config From 0725c7a0fe3c36715f8c9198c89d9c2c4daa010b Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 15:05:59 +0800 Subject: [PATCH 08/90] implement 1559 for L2 --- packages/protocol/contracts/L1/TaikoData.sol | 1 + .../protocol/contracts/L1/TaikoErrors.sol | 1 + packages/protocol/contracts/L1/TaikoL1.sol | 15 ++- .../contracts/L1/libs/LibL1Tokenomics.sol | 28 +++-- ...ibL2Tokenmocis.sol => LibL2Tokenomics.sol} | 12 +- .../contracts/L1/libs/LibProposing.sol | 16 ++- packages/protocol/test2/GasComparison.t.sol | 2 + packages/protocol/test2/LibL1Tokenomics.t.sol | 103 +++++++++--------- 8 files changed, 108 insertions(+), 70 deletions(-) rename packages/protocol/contracts/L1/libs/{LibL2Tokenmocis.sol => LibL2Tokenomics.sol} (84%) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index ca159bd7965..baef5d52f41 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -69,6 +69,7 @@ library TaikoData { uint64 timestamp; uint64 l1Height; uint32 gasLimit; + uint32 basefee; // L2 1559 basefee TODO(daniel): uint32 good enough? bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 4d0a74135b6..8ca5c7b9591 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -13,6 +13,7 @@ abstract contract TaikoErrors { error L1_CONTRACT_NOT_ALLOWED(); error L1_EVIDENCE_MISMATCH(); error L1_FORK_CHOICE_NOT_FOUND(); + error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_CONFIG(); error L1_INVALID_EVIDENCE(); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 2330511a38b..6dea9586c9b 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -9,7 +9,8 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../common/AddressResolver.sol"; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; -import {LibL1Tokenomics} from "./libs/LibVerifying.sol"; +import {LibL1Tokenomics} from "./libs/LibL1Tokenomics.sol"; +import {LibL2Tokenomics} from "./libs/LibL2Tokenomics.sol"; import {LibProposing} from "./libs/LibProposing.sol"; import {LibProving} from "./libs/LibProving.sol"; import {LibUtils} from "./libs/LibUtils.sol"; @@ -53,7 +54,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { function proposeBlock( bytes calldata input, bytes calldata txList - ) external nonReentrant { + ) external payable nonReentrant { TaikoData.Config memory config = getConfig(); LibProposing.proposeBlock({ state: state, @@ -214,6 +215,16 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { : bytes32(0); } + function get1559Basefee( + uint256 gasLimit + ) + public + view + returns (uint256 newGasExcess, uint256 basefee, uint256 gasPurchaseCost) + { + return LibL2Tokenomics.get1559Basefee(state, getConfig(), gasLimit); + } + function getStateVariables() public view diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index f0069fa84ea..4fc8a7e7f3e 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -16,6 +16,7 @@ import {TaikoToken} from "../TaikoToken.sol"; library LibL1Tokenomics { using LibMath for uint256; + using SafeCastUpgradeable for uint256; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -58,7 +59,7 @@ library LibL1Tokenomics { ) internal view - returns (uint256 newFeeBase, uint256 fee, uint256 depositAmount) + returns (uint64 newFeeBase, uint64 fee, uint64 depositAmount) { if (state.numBlocks <= config.constantFeeRewardBlocks) { fee = state.feeBase; @@ -85,12 +86,13 @@ library LibL1Tokenomics { block.timestamp - state.genesisTimestamp ) / config.bootstrapDiscountHalvingPeriod; uint256 gamma = 1024 - (1024 >> (1 + halves)); - fee = (fee * gamma) / 1024; + fee = ((fee * gamma) / 1024).toUint64(); } } unchecked { - depositAmount = (fee * config.proposerDepositPctg) / 100; + depositAmount = ((fee * config.proposerDepositPctg) / 100) + .toUint64(); } } @@ -102,7 +104,7 @@ library LibL1Tokenomics { ) internal view - returns (uint256 newFeeBase, uint256 reward, uint256 premiumRate) + returns (uint64 newFeeBase, uint64 reward, uint64 premiumRate) { if (proposedAt > provenAt) revert L1_INVALID_PARAM(); @@ -126,7 +128,8 @@ library LibL1Tokenomics { }); } unchecked { - reward = (reward * (10000 - config.rewardBurnBips)) / 10000; + reward = ((reward * (10000 - config.rewardBurnBips)) / 10000) + .toUint64(); } } @@ -135,8 +138,8 @@ library LibL1Tokenomics { TaikoData.State storage state, TaikoData.Config memory config, bool isProposal, - uint256 feeBase - ) internal view returns (uint256) { + uint64 feeBase + ) internal view returns (uint64) { unchecked { // m is the `n'` in the whitepaper uint256 m = 1000 * @@ -147,18 +150,18 @@ library LibL1Tokenomics { (state.numBlocks - state.lastVerifiedBlockId - 1); // k is `m − n + 1` or `m − n - 1`in the whitepaper uint256 k = isProposal ? m - n - 1000 : m - n + 1000; - return (feeBase * (m - 1000) * m) / (m - n) / k; + return ((feeBase * (m - 1000) * m) / (m - n) / k).toUint64(); } } // Implement "Incentive Multipliers", see the whitepaper. function getTimeAdjustedFee( TaikoData.FeeConfig memory feeConfig, - uint256 feeBase, + uint64 feeBase, bool isProposal, uint256 timeUsed, // seconds uint256 timeAverage // milliseconds - ) internal pure returns (uint256 newFeeBase, uint256 premiumRate) { + ) internal pure returns (uint64 newFeeBase, uint64 premiumRate) { if (timeAverage == 0) { return (feeBase, 0); } @@ -167,12 +170,13 @@ library LibL1Tokenomics { uint a = timeAverage; uint t = (timeUsed * 1000).min(a * 2); // millisconds - newFeeBase = (feeBase * (10000 + (t * p) / a - p)) / 10000; + newFeeBase = ((feeBase * (10000 + (t * p) / a - p)) / 10000) + .toUint64(); if (isProposal) { newFeeBase = (feeBase * 2) - newFeeBase; } else if (p > 0) { - premiumRate = ((t.max(a) - a) * 10000) / a; + premiumRate = (((t.max(a) - a) * 10000) / a).toUint64(); } } } diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol similarity index 84% rename from packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol rename to packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index aca17081376..8dce19eb61a 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenmocis.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -15,18 +15,18 @@ import {TaikoData} from "../TaikoData.sol"; library LibL2Tokenomics { using LibMath for uint256; + using SafeCastUpgradeable for uint256; + uint256 private constant ETH_BLOCK_TIME = 12 seconds; function get1559Basefee( TaikoData.State storage state, TaikoData.Config memory config, - uint256 gasInBlock, - uint256 blockTime, - uint256 adjustmentQuotient + uint256 gasInBlock ) internal view - returns (uint256 newGasExcess, uint256 basefee, uint256 gasPurchaseCost) + returns (uint256 newGasExcess, uint32 basefee, uint256 gasPurchaseCost) { return calculate1559Basefee( @@ -47,7 +47,7 @@ library LibL2Tokenomics { ) internal pure - returns (uint256 newGasExcess, uint256 basefee, uint256 gasPurchaseCost) + returns (uint256 newGasExcess, uint32 basefee, uint256 gasPurchaseCost) { uint256 adjustment = (gasTarget * blockTime) / ETH_BLOCK_TIME; newGasExcess = gasExcess.max(adjustment) - adjustment; @@ -57,7 +57,7 @@ library LibL2Tokenomics { (newGasExcess + gasInBlock) / gasTarget / adjustmentQuotient ) - LibMath.exp(newGasExcess / gasTarget / adjustmentQuotient); - basefee = gasPurchaseCost / gasInBlock; + basefee = (gasPurchaseCost / gasInBlock).toUint32(); newGasExcess += gasInBlock; } } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index d9ac44a22cf..94689ec2c61 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -7,7 +7,9 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; +import {LibAddress} from "../../libs/LibAddress.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; +import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -16,6 +18,7 @@ import {TaikoData} from "../TaikoData.sol"; library LibProposing { using SafeCastUpgradeable for uint256; + using LibAddress for address; using LibUtils for TaikoData.State; event BlockProposed( @@ -25,6 +28,7 @@ library LibProposing { ); error L1_BLOCK_ID(); + error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_METADATA(); error L1_NOT_SOLO_PROPOSER(); @@ -67,6 +71,7 @@ library LibProposing { timestamp: uint64(block.timestamp), l1Height: uint64(block.number - 1), gasLimit: input.gasLimit, + basefee: 0, // will be set later l1Hash: blockhash(block.number - 1), mixHash: bytes32(block.prevrandao * state.numBlocks), txListHash: input.txListHash, @@ -76,6 +81,15 @@ library LibProposing { }); } + { + // calculate L2 EIP-1559 gas fee and cost + uint256 gasPurchaseCost; + (state.gasExcess, meta.basefee, gasPurchaseCost) = LibL2Tokenomics + .get1559Basefee(state, config, input.gasLimit); + if (msg.value < gasPurchaseCost) revert L1_INSUFFICIENT_ETHER(); + msg.sender.sendEther(msg.value - gasPurchaseCost); + } + TaikoData.Block storage blk = state.blocks[ state.numBlocks % config.ringBufferSize ]; @@ -89,7 +103,7 @@ library LibProposing { blk.proposer = msg.sender; if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 fee, uint256 deposit) = LibL1Tokenomics + (uint256 newFeeBase, uint256 fee, uint64 deposit) = LibL1Tokenomics .getBlockFee(state, config); uint256 burnAmount = fee + deposit; diff --git a/packages/protocol/test2/GasComparison.t.sol b/packages/protocol/test2/GasComparison.t.sol index ecb17f9b68f..5a712573bdd 100644 --- a/packages/protocol/test2/GasComparison.t.sol +++ b/packages/protocol/test2/GasComparison.t.sol @@ -81,6 +81,7 @@ contract FooBar { txListByteStart: 0, txListByteEnd: 1000, gasLimit: 1, + basefee: 1000000, mixHash: bytes32(uint256(1)), timestamp: 1 }); @@ -96,6 +97,7 @@ contract FooBar { txListByteStart: 0, txListByteEnd: 1000, gasLimit: 1, + basefee: 1000000, mixHash: bytes32(uint256(1)), timestamp: 1 }); diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 61da8fd4fc6..4f37d8dd94d 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -5,8 +5,13 @@ import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {TaikoData} from "../contracts/L1/TaikoData.sol"; import {LibL1Tokenomics} from "../contracts/L1/libs/LibL1Tokenomics.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; contract TestLibL1Tokenomics is Test { + using SafeCastUpgradeable for uint256; + struct FeeConfig { uint64 avgTimeMAF; uint64 avgTimeCap; @@ -18,248 +23,248 @@ contract TestLibL1Tokenomics is Test { function testTokenomicsFeeCalcWithNonZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 ether, + expectedFeeBase: 140 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 120 ether, + expectedFeeBase: 120 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 80 ether, + expectedFeeBase: 80 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 ether, + expectedFeeBase: 60 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 ether, + expectedFeeBase: 60 * 1E8, expectedPreimumRate: 0 }); } function testTokenomicsFeeCalcWithZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); } function testTokenomicsRewardCalcWithNonZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 ether, + expectedFeeBase: 60 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 80 ether, + expectedFeeBase: 80 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 120 ether, + expectedFeeBase: 120 * 1E8, expectedPreimumRate: 5000 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 ether, + expectedFeeBase: 140 * 1E8, expectedPreimumRate: 10000 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 ether, + expectedFeeBase: 140 * 1E8, expectedPreimumRate: 10000 }); } function testTokenomicsRewardCalcWithZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); } @@ -281,7 +286,7 @@ contract TestLibL1Tokenomics is Test { (uint256 _feeBase, uint256 _premiumRate) = LibL1Tokenomics .getTimeAdjustedFee( feeConfig, - feeBase, + feeBase.toUint64(), isProposal, timeUsedSec, timeAverageSec * 1000 From ccec53844c2cf997a1e440e20ec63ea9c3db9e04 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 15:18:48 +0800 Subject: [PATCH 09/90] implement 1559 for L2 --- .../protocol/contracts/L1/TaikoConfig.sol | 1 - packages/protocol/contracts/L1/TaikoData.sol | 1 - packages/protocol/contracts/L1/TaikoL1.sol | 12 +++---- .../contracts/L1/libs/LibL1Tokenomics.sol | 33 +++++-------------- .../contracts/test/L1/TestTaikoL1.sol | 1 - .../test/L1/TestTaikoL1EnableTokenomics.sol | 1 - .../protocol/test/tokenomics/blockFee.test.ts | 16 --------- packages/protocol/test/utils/onNewL2Block.ts | 4 +-- packages/protocol/test2/TaikoL1.t.sol | 1 - 9 files changed, 15 insertions(+), 55 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index de9d28a9ea2..dc6ade72e9c 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -39,7 +39,6 @@ library TaikoConfig { proposerDepositPctg: 25, // - 25% // Moving average factors feeBaseMAF: 1024, - bootstrapDiscountHalvingPeriod: 1 seconds, // owner:daniel constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, enableSoloProposer: false, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index baef5d52f41..abb1b041686 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -31,7 +31,6 @@ library TaikoData { uint256 proposerDepositPctg; // Moving average factors uint256 feeBaseMAF; - uint64 bootstrapDiscountHalvingPeriod; uint64 constantFeeRewardBlocks; uint64 txListCacheExpiry; bool enableSoloProposer; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 6dea9586c9b..c8f7b8bcbbc 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -217,12 +217,12 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { function get1559Basefee( uint256 gasLimit - ) - public - view - returns (uint256 newGasExcess, uint256 basefee, uint256 gasPurchaseCost) - { - return LibL2Tokenomics.get1559Basefee(state, getConfig(), gasLimit); + ) public view returns (uint32 basefee) { + (, basefee, ) = LibL2Tokenomics.get1559Basefee( + state, + getConfig(), + gasLimit + ); } function getStateVariables() diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 4fc8a7e7f3e..29c7c0bdd05 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -8,15 +8,11 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; -import { - SafeCastUpgradeable -} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; library LibL1Tokenomics { using LibMath for uint256; - using SafeCastUpgradeable for uint256; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -80,19 +76,8 @@ library LibL1Tokenomics { }); } - if (config.bootstrapDiscountHalvingPeriod > 0) { - unchecked { - uint256 halves = uint256( - block.timestamp - state.genesisTimestamp - ) / config.bootstrapDiscountHalvingPeriod; - uint256 gamma = 1024 - (1024 >> (1 + halves)); - fee = ((fee * gamma) / 1024).toUint64(); - } - } - unchecked { - depositAmount = ((fee * config.proposerDepositPctg) / 100) - .toUint64(); + depositAmount = uint64((fee * config.proposerDepositPctg) / 100); } } @@ -128,8 +113,7 @@ library LibL1Tokenomics { }); } unchecked { - reward = ((reward * (10000 - config.rewardBurnBips)) / 10000) - .toUint64(); + reward = uint64((reward * (10000 - config.rewardBurnBips)) / 10000); } } @@ -150,7 +134,7 @@ library LibL1Tokenomics { (state.numBlocks - state.lastVerifiedBlockId - 1); // k is `m − n + 1` or `m − n - 1`in the whitepaper uint256 k = isProposal ? m - n - 1000 : m - n + 1000; - return ((feeBase * (m - 1000) * m) / (m - n) / k).toUint64(); + return uint64((feeBase * (m - 1000) * m) / (m - n) / k); } } @@ -166,17 +150,16 @@ library LibL1Tokenomics { return (feeBase, 0); } unchecked { - uint p = feeConfig.dampingFactorBips; // [0-10000] - uint a = timeAverage; - uint t = (timeUsed * 1000).min(a * 2); // millisconds + uint256 p = feeConfig.dampingFactorBips; // [0-10000] + uint256 a = timeAverage; + uint256 t = (timeUsed * 1000).min(a * 2); // millisconds - newFeeBase = ((feeBase * (10000 + (t * p) / a - p)) / 10000) - .toUint64(); + newFeeBase = uint64((feeBase * (10000 + (t * p) / a - p)) / 10000); if (isProposal) { newFeeBase = (feeBase * 2) - newFeeBase; } else if (p > 0) { - premiumRate = (((t.max(a) - a) * 10000) / a).toUint64(); + premiumRate = uint64(((t.max(a) - a) * 10000) / a); } } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index b55e87593aa..b631fed880f 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -31,7 +31,6 @@ contract TestTaikoL1 is TaikoL1 { config.rewardBurnBips = 100; // 100 basis points or 1% config.proposerDepositPctg = 25; // 25% - config.bootstrapDiscountHalvingPeriod = 1 seconds; config.enableTokenomics = false; config.skipZKPVerification = true; config.feeBaseMAF = 1024; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index ee6aad6679d..8d55b35a6e4 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -34,7 +34,6 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { // Moving average factors config.feeBaseMAF = 1024; - config.bootstrapDiscountHalvingPeriod = 1 seconds; config.enableTokenomics = true; config.skipZKPVerification = true; diff --git a/packages/protocol/test/tokenomics/blockFee.test.ts b/packages/protocol/test/tokenomics/blockFee.test.ts index 98a37f5f490..75529c54aa5 100644 --- a/packages/protocol/test/tokenomics/blockFee.test.ts +++ b/packages/protocol/test/tokenomics/blockFee.test.ts @@ -50,22 +50,6 @@ describe("tokenomics: blockFee", function () { expect(blockFee).to.be.eq(0); }); - it("block fee should increase as the halving period passes, while no blocks are proposed", async function () { - const iterations: number = 5; - const period: number = config.bootstrapDiscountHalvingPeriod - .mul(1000) - .toNumber(); - - let lastBlockFee: BigNumber = await taikoL1.getBlockFee(); - - for (let i = 0; i < iterations; i++) { - await sleep(period); - const blockFee = await taikoL1.getBlockFee(); - expect(blockFee).to.be.gt(lastBlockFee); - lastBlockFee = blockFee; - } - }); - it( "proposes blocks on interval, proposer's balance for TkoToken should decrease as it pays proposer fee, " + "proofReward should increase since more slots are used and " + diff --git a/packages/protocol/test/utils/onNewL2Block.ts b/packages/protocol/test/utils/onNewL2Block.ts index dea3097801e..a9a969e206e 100644 --- a/packages/protocol/test/utils/onNewL2Block.ts +++ b/packages/protocol/test/utils/onNewL2Block.ts @@ -2,7 +2,6 @@ import { BigNumber, ethers } from "ethers"; import { TaikoL1, TaikoToken } from "../../typechain"; import { BlockProposedEvent } from "../../typechain/LibProposing"; import Proposer from "./proposer"; -import sleep from "./sleep"; // onNewL2Block should be called from a tokenomics test case when a new block // is generated from the l2Provider. @@ -22,7 +21,7 @@ async function onNewL2Block( newBlockFee: BigNumber; newProofReward: BigNumber; }> { - const config = await taikoL1.getConfig(); + // const config = await taikoL1.getConfig(); const block = await l2Provider.getBlock(blockNumber); const { proposedEvent } = await proposer.proposeBlock(block); @@ -39,7 +38,6 @@ async function onNewL2Block( ? await taikoTokenL1.balanceOf(await proposerSigner.getAddress()) : BigNumber.from(0); - await sleep(1000 * config.bootstrapDiscountHalvingPeriod.toNumber()); const newBlockFee = await taikoL1.getBlockFee(); console.log("-------------------proposed----------", id); diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index a08d3c59d77..75b493e645f 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -22,7 +22,6 @@ contract TaikoL1WithConfig is TaikoL1 { config = TaikoConfig.getConfig(); config.enableTokenomics = true; - config.bootstrapDiscountHalvingPeriod = 0; config.constantFeeRewardBlocks = 0; config.txListCacheExpiry = 5 minutes; config.proposerDepositPctg = 0; From 4f0162ed6710347d38ba11f0138e5b2d25744946 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 15:24:22 +0800 Subject: [PATCH 10/90] implement 1559 for L2 --- packages/protocol/contracts/L1/libs/LibProposing.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 94689ec2c61..978e3408efc 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -19,6 +19,7 @@ import {TaikoData} from "../TaikoData.sol"; library LibProposing { using SafeCastUpgradeable for uint256; using LibAddress for address; + using LibAddress for address payable; using LibUtils for TaikoData.State; event BlockProposed( @@ -87,6 +88,8 @@ library LibProposing { (state.gasExcess, meta.basefee, gasPurchaseCost) = LibL2Tokenomics .get1559Basefee(state, config, input.gasLimit); if (msg.value < gasPurchaseCost) revert L1_INSUFFICIENT_ETHER(); + + resolver.resolve("treasure", false).sendEther(gasPurchaseCost); msg.sender.sendEther(msg.value - gasPurchaseCost); } From 64548f081d613fd7b665481d279cfe8ffa993db0 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 15:25:25 +0800 Subject: [PATCH 11/90] implement 1559 for L2 --- packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 8dce19eb61a..efd144e8b20 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -38,6 +38,8 @@ library LibL2Tokenomics { ); } + // @dev Return adjusted basefee per gas for the next L2 block. + // See https://ethresear.ch/t/make-eip-1559-more-like-an-amm-curve/9082 function calculate1559Basefee( uint256 gasExcess, uint256 gasTarget, From c85f74ee172c6e13cbe70a3e4381c0a0f4cbefb3 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 15:32:47 +0800 Subject: [PATCH 12/90] implement 1559 for L2 --- packages/protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index dc6ade72e9c..9581e750d64 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasTarget: 60000000, + gasTarget: 6000000, // per second (not per 12 seconds) adjustmentQuotient: 8, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index efd144e8b20..892e1cbbdad 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -17,8 +17,6 @@ library LibL2Tokenomics { using LibMath for uint256; using SafeCastUpgradeable for uint256; - uint256 private constant ETH_BLOCK_TIME = 12 seconds; - function get1559Basefee( TaikoData.State storage state, TaikoData.Config memory config, @@ -51,7 +49,7 @@ library LibL2Tokenomics { pure returns (uint256 newGasExcess, uint32 basefee, uint256 gasPurchaseCost) { - uint256 adjustment = (gasTarget * blockTime) / ETH_BLOCK_TIME; + uint256 adjustment = gasTarget * blockTime; newGasExcess = gasExcess.max(adjustment) - adjustment; gasPurchaseCost = From c10f480cae1d729b5165665e0384222d81eef838 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 16:46:39 +0800 Subject: [PATCH 13/90] more --- .../contracts/L1/libs/LibL2Tokenomics.sol | 18 +++++++++++++++--- packages/protocol/contracts/libs/LibMath.sol | 9 --------- .../protocol/contracts/libs/LibRealMath.sol | 13 +++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 packages/protocol/contracts/libs/LibRealMath.sol diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 892e1cbbdad..c2bbeb8ce5c 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; +import {LibRealMath} from "../../libs/LibRealMath.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; @@ -53,11 +54,22 @@ library LibL2Tokenomics { newGasExcess = gasExcess.max(adjustment) - adjustment; gasPurchaseCost = - LibMath.exp( - (newGasExcess + gasInBlock) / gasTarget / adjustmentQuotient + _ethAmount( + newGasExcess + gasInBlock, + gasTarget, + adjustmentQuotient ) - - LibMath.exp(newGasExcess / gasTarget / adjustmentQuotient); + _ethAmount(newGasExcess, gasTarget, adjustmentQuotient); basefee = (gasPurchaseCost / gasInBlock).toUint32(); newGasExcess += gasInBlock; } + + function _ethAmount( + uint256 gasExcess, + uint256 gasTarget, + uint256 adjustmentQuotient + ) internal pure returns (uint256) { + uint128 x = (gasExcess / gasTarget / adjustmentQuotient).toUint128(); + return LibRealMath.exp(x); + } } diff --git a/packages/protocol/contracts/libs/LibMath.sol b/packages/protocol/contracts/libs/LibMath.sol index e9716917251..e6f20e92d95 100644 --- a/packages/protocol/contracts/libs/LibMath.sol +++ b/packages/protocol/contracts/libs/LibMath.sol @@ -30,13 +30,4 @@ library LibMath { function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } - - /** - * @notice Returns the power of Eular number. - * @param x The base. - * @return The power of Eular number. - */ - function exp(uint256 x) internal pure returns (uint256) { - // TODO(Daniel): Implement & test this function. - } } diff --git a/packages/protocol/contracts/libs/LibRealMath.sol b/packages/protocol/contracts/libs/LibRealMath.sol new file mode 100644 index 00000000000..1c6678b7275 --- /dev/null +++ b/packages/protocol/contracts/libs/LibRealMath.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.18; + +library LibRealMath { + function exp(uint128 b) internal pure returns (uint256) { + // https://github.com/NovakDistributed/macroverse/blob/master/contracts/RealMath.sol + } +} From bafe0ebf9eabcdca5490a190f5956ae5b2952a65 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 19:01:54 +0800 Subject: [PATCH 14/90] fix tests --- packages/protocol/test2/TaikoL1.t.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index a08d3c59d77..cd87527c94a 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -139,8 +139,8 @@ contract TaikoL1Test is TaikoL1TestBase { printVariables("after verify"); } - /// @dev Test block timeincrease and fee shall decrease. - function test_block_time_increases_but_fee_decreases() external { + /// @dev Test block time increases and fee decreases. + function test_block_time_increases_and_fee_decreases() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); @@ -166,8 +166,7 @@ contract TaikoL1Test is TaikoL1TestBase { printVariables(""); } - /// @dev Test block time goes down lover time and the fee should remain - // the same. + /// @dev Test block time decreases and the fee increases function test_block_time_decreases_but_fee_remains() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); From 8ff0da33ffc24f5b262c29d95a4b577d401bce6a Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 20:24:02 +0800 Subject: [PATCH 15/90] fix tests --- .../protocol/contracts/L1/TaikoConfig.sol | 4 +- packages/protocol/contracts/L1/TaikoData.sol | 8 +-- .../protocol/contracts/L1/TaikoErrors.sol | 1 + packages/protocol/contracts/L1/TaikoL1.sol | 2 +- .../contracts/L1/libs/LibL2Tokenomics.sol | 59 ++++++++++--------- .../contracts/L1/libs/LibProposing.sol | 2 +- packages/protocol/test2/LibL2Tokenomics.t.sol | 48 +++++++++++++++ 7 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 packages/protocol/test2/LibL2Tokenomics.t.sol diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 9581e750d64..4ad31287b83 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,8 +23,8 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasTarget: 6000000, // per second (not per 12 seconds) - adjustmentQuotient: 8, + gasTargetPerSecond: 6000000, + gasPoolProduct: 8, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index abb1b041686..6811f98ba35 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,8 +21,8 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint256 gasTarget; - uint256 adjustmentQuotient; + uint256 gasTargetPerSecond; + uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; @@ -67,13 +67,13 @@ library TaikoData { uint64 id; uint64 timestamp; uint64 l1Height; - uint32 gasLimit; - uint32 basefee; // L2 1559 basefee TODO(daniel): uint32 good enough? + uint64 basefee; bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; uint24 txListByteStart; uint24 txListByteEnd; + uint32 gasLimit; address beneficiary; } diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 8ca5c7b9591..22e79b60d70 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -22,6 +22,7 @@ abstract contract TaikoErrors { error L1_INVALID_PROOF(); error L1_NOT_ORACLE_PROVER(); error L1_NOT_SOLO_PROPOSER(); + error L1_OUT_OF_BLOCK_SPACE(); error L1_TOO_MANY_BLOCKS(); error L1_TX_LIST_NOT_EXIST(); error L1_TX_LIST_HASH(); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index c8f7b8bcbbc..6812275d535 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -217,7 +217,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { function get1559Basefee( uint256 gasLimit - ) public view returns (uint32 basefee) { + ) public view returns (uint64 basefee) { (, basefee, ) = LibL2Tokenomics.get1559Basefee( state, getConfig(), diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index c2bbeb8ce5c..281d3bea3f0 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -6,6 +6,8 @@ pragma solidity ^0.8.18; +import {console2} from "forge-std/console2.sol"; + import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; import {LibRealMath} from "../../libs/LibRealMath.sol"; @@ -18,6 +20,8 @@ library LibL2Tokenomics { using LibMath for uint256; using SafeCastUpgradeable for uint256; + error L1_OUT_OF_BLOCK_SPACE(); + function get1559Basefee( TaikoData.State storage state, TaikoData.Config memory config, @@ -25,13 +29,13 @@ library LibL2Tokenomics { ) internal view - returns (uint256 newGasExcess, uint32 basefee, uint256 gasPurchaseCost) + returns (uint256 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) { return - calculate1559Basefee( + calc1559Basefee( state.gasExcess, - config.gasTarget, - config.adjustmentQuotient, + config.gasTargetPerSecond, + config.gasPoolProduct, gasInBlock, block.timestamp - state.lastProposedAt ); @@ -39,37 +43,36 @@ library LibL2Tokenomics { // @dev Return adjusted basefee per gas for the next L2 block. // See https://ethresear.ch/t/make-eip-1559-more-like-an-amm-curve/9082 - function calculate1559Basefee( + // But the current implementation use AMM style math as we don't yet + // have a solidity exp(uint256 x) implementation. + function calc1559Basefee( uint256 gasExcess, - uint256 gasTarget, - uint256 adjustmentQuotient, + uint256 gasTargetPerSecond, + uint256 gasPoolProduct, uint256 gasInBlock, uint256 blockTime ) internal - pure - returns (uint256 newGasExcess, uint32 basefee, uint256 gasPurchaseCost) + view + returns (uint256 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) { - uint256 adjustment = gasTarget * blockTime; - newGasExcess = gasExcess.max(adjustment) - adjustment; + unchecked { + uint256 _gasExcess = gasExcess + (gasTargetPerSecond * blockTime); + console2.log("----- _gasExcess:", _gasExcess); + console2.log("----- newGasExcess:", _gasExcess - gasInBlock); + console2.log("----- gasInBlock:", gasInBlock); - gasPurchaseCost = - _ethAmount( - newGasExcess + gasInBlock, - gasTarget, - adjustmentQuotient - ) - - _ethAmount(newGasExcess, gasTarget, adjustmentQuotient); - basefee = (gasPurchaseCost / gasInBlock).toUint32(); - newGasExcess += gasInBlock; - } + if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); + newGasExcess = _gasExcess - gasInBlock; - function _ethAmount( - uint256 gasExcess, - uint256 gasTarget, - uint256 adjustmentQuotient - ) internal pure returns (uint256) { - uint128 x = (gasExcess / gasTarget / adjustmentQuotient).toUint128(); - return LibRealMath.exp(x); + console2.log("----- larger:", (gasPoolProduct / newGasExcess)); + console2.log("----- smaller:", (gasPoolProduct / _gasExcess)); + + gasPurchaseCost = + (gasPoolProduct / newGasExcess) - + (gasPoolProduct / _gasExcess); + + basefee = (gasPurchaseCost / gasInBlock).toUint64(); + } } } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 978e3408efc..d5f152c4a12 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -71,13 +71,13 @@ library LibProposing { id: state.numBlocks, timestamp: uint64(block.timestamp), l1Height: uint64(block.number - 1), - gasLimit: input.gasLimit, basefee: 0, // will be set later l1Hash: blockhash(block.number - 1), mixHash: bytes32(block.prevrandao * state.numBlocks), txListHash: input.txListHash, txListByteStart: input.txListByteStart, txListByteEnd: input.txListByteEnd, + gasLimit: input.gasLimit, beneficiary: input.beneficiary }); } diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol new file mode 100644 index 00000000000..f98a91b75bb --- /dev/null +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {LibL2Tokenomics} from "../contracts/L1/libs/LibL2Tokenomics.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; + +contract TestLibL2Tokenomics is Test { + using SafeCastUpgradeable for uint256; + + uint256 initialBaseFee = 5000000000; + uint256 gasTargetPerSecond = 1000000; + uint256 gasExcess = gasTargetPerSecond * 1000; + uint256 gasPoolProduct = gasExcess ** 2 * initialBaseFee; + + function setUp() public { + console2.log("===== gasPoolProduct:", gasPoolProduct); + } + + function test1559Basefee() public { + _purchaseGas(gasTargetPerSecond * 12, 12); + _purchaseGas(gasTargetPerSecond * 12, 12); + _purchaseGas(gasTargetPerSecond * 12, 0); + _purchaseGas(gasTargetPerSecond * 12, 0); + _purchaseGas(gasTargetPerSecond * 12, 0); + _purchaseGas(gasTargetPerSecond * 12, 0); + _purchaseGas(gasTargetPerSecond * 12, 0); + _purchaseGas(gasTargetPerSecond * 12, 0); + _purchaseGas(gasTargetPerSecond * 12, 0); + } + + function _purchaseGas(uint256 amount, uint256 blockTime) private { + uint64 basefee; + uint256 gasPurchaseCost; + (gasExcess, basefee, gasPurchaseCost) = LibL2Tokenomics.calc1559Basefee( + gasExcess, + gasTargetPerSecond, + gasPoolProduct, + amount, + blockTime + ); + console2.log("===== gasPurchaseCost:", gasPurchaseCost); + console2.log("===== basefee:", basefee); + } +} From 8a22c8853b2b381d282af6de700f0af2b140d9fa Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 20:41:07 +0800 Subject: [PATCH 16/90] fix tests --- packages/protocol/contracts/L1/TaikoData.sol | 2 ++ packages/protocol/contracts/L1/TaikoL1.sol | 8 +++++--- .../protocol/contracts/L1/libs/LibL2Tokenomics.sol | 4 +++- packages/protocol/contracts/L1/libs/LibUtils.sol | 11 +++++++++-- packages/protocol/contracts/L1/libs/LibVerifying.sol | 4 +++- packages/protocol/test2/LibL2Tokenomics.t.sol | 4 ---- packages/protocol/test2/TaikoL1TestBase.sol | 11 ++++++++++- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 6811f98ba35..d4c695b90ab 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -50,6 +50,8 @@ library TaikoData { uint64 avgBlockTime; uint64 avgProofTime; uint64 lastProposedAt; + uint64 basefee; + uint256 gasExcess; } // 3 slots diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 6812275d535..5fcbaee77df 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -29,14 +29,16 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { function init( address _addressManager, bytes32 _genesisBlockHash, - uint64 _feeBase + uint64 _feeBase, + uint256 _gasExcess ) external initializer { EssentialContract._init(_addressManager); LibVerifying.init({ state: state, config: getConfig(), genesisBlockHash: _genesisBlockHash, - feeBase: _feeBase + feeBase: _feeBase, + gasExcess: _gasExcess }); } @@ -230,7 +232,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { view returns (TaikoData.StateVariables memory) { - return state.getStateVariables(); + return state.getStateVariables(getConfig()); } function getConfig() public pure virtual returns (TaikoData.Config memory) { diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 281d3bea3f0..d735400bfbf 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -72,7 +72,9 @@ library LibL2Tokenomics { (gasPoolProduct / newGasExcess) - (gasPoolProduct / _gasExcess); - basefee = (gasPurchaseCost / gasInBlock).toUint64(); + basefee = uint64( + (gasPurchaseCost / gasInBlock).min(type(uint64).max) + ); } } } diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 38fa4eb3a2d..88d209036e2 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -29,7 +29,8 @@ library LibUtils { } function getStateVariables( - TaikoData.State storage state + TaikoData.State storage state, + TaikoData.Config memory config ) internal view returns (TaikoData.StateVariables memory) { return TaikoData.StateVariables({ @@ -40,7 +41,13 @@ library LibUtils { lastProposedAt: state.lastProposedAt, avgBlockTime: state.avgBlockTime, lastVerifiedBlockId: state.lastVerifiedBlockId, - avgProofTime: state.avgProofTime + avgProofTime: state.avgProofTime, + basefee: uint64( + (config.gasPoolProduct / state.gasExcess).min( + type(uint64).max + ) + ), + gasExcess: state.gasExcess }); } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index de7a59235a1..572eafc58da 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -31,7 +31,8 @@ library LibVerifying { TaikoData.State storage state, TaikoData.Config memory config, bytes32 genesisBlockHash, - uint64 feeBase + uint64 feeBase, + uint256 gasExcess ) internal { _checkConfig(config); @@ -40,6 +41,7 @@ library LibVerifying { state.genesisTimestamp = timeNow; state.feeBase = feeBase; state.numBlocks = 1; + state.gasExcess = gasExcess; TaikoData.Block storage blk = state.blocks[0]; blk.proposedAt = timeNow; diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index f98a91b75bb..23967e2d20e 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -16,10 +16,6 @@ contract TestLibL2Tokenomics is Test { uint256 gasExcess = gasTargetPerSecond * 1000; uint256 gasPoolProduct = gasExcess ** 2 * initialBaseFee; - function setUp() public { - console2.log("===== gasPoolProduct:", gasPoolProduct); - } - function test1559Basefee() public { _purchaseGas(gasTargetPerSecond * 12, 12); _purchaseGas(gasTargetPerSecond * 12, 12); diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.sol index ce0eae47ed0..ade52904173 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.sol @@ -22,7 +22,10 @@ abstract contract TaikoL1TestBase is Test { bytes32 public constant GENESIS_BLOCK_HASH = keccak256("GENESIS_BLOCK_HASH"); uint64 feeBase = 1E8; // 1 TKO + uint256 gasExcess = 1E18; + address public constant Treasure = + 0x859d74b52762d9ed07D1b2B8d7F93d26B1EA78Bb; address public constant L2SS = 0xa008AE5Ba00656a3Cc384de589579e3E52aC030C; address public constant L2TaikoL2 = 0x0082D90249342980d011C58105a03b35cCb4A315; @@ -41,7 +44,12 @@ abstract contract TaikoL1TestBase is Test { addressManager.init(); L1 = deployTaikoL1(); - L1.init(address(addressManager), GENESIS_BLOCK_HASH, feeBase); + L1.init( + address(addressManager), + GENESIS_BLOCK_HASH, + feeBase, + gasExcess + ); conf = L1.getConfig(); tko = new TaikoToken(); @@ -66,6 +74,7 @@ abstract contract TaikoL1TestBase is Test { _registerAddress("taiko_token", address(tko)); _registerAddress("proto_broker", address(L1)); _registerAddress("signal_service", address(ss)); + _registerAddress("treasure", Treasure); _registerL2Address("signal_service", address(L2SS)); _registerL2Address("taiko_l2", address(L2TaikoL2)); From b161046b2539bfcdfc9b268d17623e5b9654dfc9 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 21:32:42 +0800 Subject: [PATCH 17/90] fix tests --- .../contracts/L1/libs/LibL2Tokenomics.sol | 16 ++--- .../contracts/L1/libs/LibProposing.sol | 1 + packages/protocol/test2/LibL2Tokenomics.t.sol | 65 ++++++++++++++----- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index d735400bfbf..b8e6d553c28 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -6,8 +6,6 @@ pragma solidity ^0.8.18; -import {console2} from "forge-std/console2.sol"; - import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; import {LibRealMath} from "../../libs/LibRealMath.sol"; @@ -53,28 +51,22 @@ library LibL2Tokenomics { uint256 blockTime ) internal - view + pure returns (uint256 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) { unchecked { uint256 _gasExcess = gasExcess + (gasTargetPerSecond * blockTime); - console2.log("----- _gasExcess:", _gasExcess); - console2.log("----- newGasExcess:", _gasExcess - gasInBlock); - console2.log("----- gasInBlock:", gasInBlock); if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); newGasExcess = _gasExcess - gasInBlock; - console2.log("----- larger:", (gasPoolProduct / newGasExcess)); - console2.log("----- smaller:", (gasPoolProduct / _gasExcess)); - gasPurchaseCost = (gasPoolProduct / newGasExcess) - (gasPoolProduct / _gasExcess); - basefee = uint64( - (gasPurchaseCost / gasInBlock).min(type(uint64).max) - ); + basefee = (gasInBlock == 0) + ? 0 + : uint64((gasPurchaseCost / gasInBlock).min(type(uint64).max)); } } } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index d5f152c4a12..9c7552342d1 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -172,6 +172,7 @@ library LibProposing { if ( input.beneficiary == address(0) || + input.gasLimit == 0 || input.gasLimit > config.blockMaxGasLimit ) revert L1_INVALID_METADATA(); diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 23967e2d20e..7584fe69afd 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -13,24 +13,61 @@ contract TestLibL2Tokenomics is Test { uint256 initialBaseFee = 5000000000; uint256 gasTargetPerSecond = 1000000; - uint256 gasExcess = gasTargetPerSecond * 1000; + uint256 gasExcess = gasTargetPerSecond * 100; uint256 gasPoolProduct = gasExcess ** 2 * initialBaseFee; function test1559Basefee() public { - _purchaseGas(gasTargetPerSecond * 12, 12); - _purchaseGas(gasTargetPerSecond * 12, 12); - _purchaseGas(gasTargetPerSecond * 12, 0); - _purchaseGas(gasTargetPerSecond * 12, 0); - _purchaseGas(gasTargetPerSecond * 12, 0); - _purchaseGas(gasTargetPerSecond * 12, 0); - _purchaseGas(gasTargetPerSecond * 12, 0); - _purchaseGas(gasTargetPerSecond * 12, 0); - _purchaseGas(gasTargetPerSecond * 12, 0); - } + (uint64 basefee1, uint256 cost1) = _purchaseGas( + gasTargetPerSecond * 12, + 12 + ); + + (uint64 basefee2, uint256 cost2) = _purchaseGas( + gasTargetPerSecond * 12, + 12 + ); + + assertEq(basefee1, basefee2); + assertEq(cost1, cost2); + + (uint64 basefee3, uint256 cost3) = _purchaseGas(1, 12); + + console2.log("when purchase a block of size: 1"); + console2.log( + "basefee decreases by:", + 100 - (basefee3 * 100) / basefee2 + ); + + (uint64 basefee4, uint256 cost4) = _purchaseGas( + gasTargetPerSecond * 24, + 0 + ); + + console2.log("when purchase a block of size: 2 * target"); + console2.log( + "basefee increases by:", + (basefee4 * 100) / basefee2 - 100 + ); - function _purchaseGas(uint256 amount, uint256 blockTime) private { uint64 basefee; - uint256 gasPurchaseCost; + for (uint i = 0; i < 5; i++) { + (uint64 _basefee, ) = _purchaseGas(gasTargetPerSecond * 12, 0); + assertGt(_basefee, basefee); + if (basefee > 0) { + console2.log( + "gas price", + (_basefee * 100) / basefee - 100, + "% larger than parent" + ); + } + basefee = _basefee; + } + } + + function _purchaseGas( + uint256 amount, + uint256 blockTime + ) private returns (uint64 basefee, uint256 gasPurchaseCost) { (gasExcess, basefee, gasPurchaseCost) = LibL2Tokenomics.calc1559Basefee( gasExcess, gasTargetPerSecond, @@ -38,7 +75,5 @@ contract TestLibL2Tokenomics is Test { amount, blockTime ); - console2.log("===== gasPurchaseCost:", gasPurchaseCost); - console2.log("===== basefee:", basefee); } } From ca8dec264e716bdf5e4425b502c0e3116e4d6470 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:24:18 +0800 Subject: [PATCH 18/90] fix tests --- .../protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/contracts/L1/TaikoData.sol | 7 +++--- packages/protocol/contracts/L1/TaikoL1.sol | 4 ++-- .../contracts/L1/libs/LibL2Tokenomics.sol | 24 +++++++++++-------- .../contracts/L1/libs/LibVerifying.sol | 2 +- packages/protocol/test2/LibL2Tokenomics.t.sol | 12 +++++----- packages/protocol/test2/TaikoL1TestBase.sol | 2 +- 7 files changed, 28 insertions(+), 25 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 4ad31287b83..644cfc2094d 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -24,7 +24,7 @@ library TaikoConfig { // testnet's circuits. blockMaxGasLimit: 6000000, gasTargetPerSecond: 6000000, - gasPoolProduct: 8, + gasPoolProduct: (6000000 * 12) ** 2 * 50000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index d4c695b90ab..1966ffc89b0 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,7 +21,7 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint256 gasTargetPerSecond; + uint64 gasTargetPerSecond; uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; @@ -51,7 +51,7 @@ library TaikoData { uint64 avgProofTime; uint64 lastProposedAt; uint64 basefee; - uint256 gasExcess; + uint64 gasExcess; } // 3 slots @@ -128,7 +128,6 @@ library TaikoData { mapping(uint256 blockId => mapping(bytes32 parentHash => uint256 forkChoiceId)) forkChoiceIds; mapping(address account => uint256 balance) balances; mapping(bytes32 txListHash => TxListInfo) txListInfo; - uint256 gasExcess; // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; @@ -139,7 +138,7 @@ library TaikoData { uint64 numBlocks; uint64 lastProposedAt; // Timestamp when the last block is proposed. uint64 avgBlockTime; // miliseconds - uint64 __reserved3; + uint64 gasExcess; // Changed when a block is proven/finalized uint64 __reserved4; uint64 lastVerifiedBlockId; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 5fcbaee77df..c154c7a2fb4 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -30,7 +30,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, - uint256 _gasExcess + uint64 _gasExcess ) external initializer { EssentialContract._init(_addressManager); LibVerifying.init({ @@ -218,7 +218,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { } function get1559Basefee( - uint256 gasLimit + uint32 gasLimit ) public view returns (uint64 basefee) { (, basefee, ) = LibL2Tokenomics.get1559Basefee( state, diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index b8e6d553c28..8d2b2151ef8 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -23,11 +23,11 @@ library LibL2Tokenomics { function get1559Basefee( TaikoData.State storage state, TaikoData.Config memory config, - uint256 gasInBlock + uint32 gasInBlock ) internal view - returns (uint256 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) + returns (uint64 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) { return calc1559Basefee( @@ -35,7 +35,7 @@ library LibL2Tokenomics { config.gasTargetPerSecond, config.gasPoolProduct, gasInBlock, - block.timestamp - state.lastProposedAt + uint64(block.timestamp - state.lastProposedAt) ); } @@ -44,21 +44,25 @@ library LibL2Tokenomics { // But the current implementation use AMM style math as we don't yet // have a solidity exp(uint256 x) implementation. function calc1559Basefee( - uint256 gasExcess, - uint256 gasTargetPerSecond, + uint64 gasExcess, + uint64 gasTargetPerSecond, uint256 gasPoolProduct, - uint256 gasInBlock, - uint256 blockTime + uint32 gasInBlock, + uint64 blockTime ) internal pure - returns (uint256 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) + returns (uint64 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) { unchecked { - uint256 _gasExcess = gasExcess + (gasTargetPerSecond * blockTime); + uint256 _gasExcess = uint256(gasExcess) + + (gasTargetPerSecond * blockTime); + + _gasExcess = _gasExcess.min(type(uint64).max); if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); - newGasExcess = _gasExcess - gasInBlock; + + newGasExcess = uint64(_gasExcess - gasInBlock); gasPurchaseCost = (gasPoolProduct / newGasExcess) - diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 572eafc58da..88b1f53f1ab 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -32,7 +32,7 @@ library LibVerifying { TaikoData.Config memory config, bytes32 genesisBlockHash, uint64 feeBase, - uint256 gasExcess + uint64 gasExcess ) internal { _checkConfig(config); diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 7584fe69afd..9be26b30eb8 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -11,10 +11,10 @@ import { contract TestLibL2Tokenomics is Test { using SafeCastUpgradeable for uint256; - uint256 initialBaseFee = 5000000000; - uint256 gasTargetPerSecond = 1000000; - uint256 gasExcess = gasTargetPerSecond * 100; - uint256 gasPoolProduct = gasExcess ** 2 * initialBaseFee; + uint64 initialBaseFee = 5000000000; + uint32 gasTargetPerSecond = 1000000; + uint64 gasExcess = gasTargetPerSecond * 100; + uint256 gasPoolProduct = uint(gasExcess) * uint(gasExcess) * initialBaseFee; function test1559Basefee() public { (uint64 basefee1, uint256 cost1) = _purchaseGas( @@ -65,8 +65,8 @@ contract TestLibL2Tokenomics is Test { } function _purchaseGas( - uint256 amount, - uint256 blockTime + uint32 amount, + uint64 blockTime ) private returns (uint64 basefee, uint256 gasPurchaseCost) { (gasExcess, basefee, gasPurchaseCost) = LibL2Tokenomics.calc1559Basefee( gasExcess, diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.sol index ade52904173..32919e79df1 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.sol @@ -22,7 +22,7 @@ abstract contract TaikoL1TestBase is Test { bytes32 public constant GENESIS_BLOCK_HASH = keccak256("GENESIS_BLOCK_HASH"); uint64 feeBase = 1E8; // 1 TKO - uint256 gasExcess = 1E18; + uint64 gasExcess = 1E18; address public constant Treasure = 0x859d74b52762d9ed07D1b2B8d7F93d26B1EA78Bb; From cd4605d0150e03d49620fef590ade8b24f912f56 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:27:44 +0800 Subject: [PATCH 19/90] fix tests --- packages/protocol/tasks/deploy_L1.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index f3e4c6a9e2e..436172dc430 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -154,10 +154,16 @@ export async function deployContracts(hre: any) { ); const feeBase = hre.ethers.BigNumber.from(10).pow(18); + const gasExcess = hre.ethers.BigNumber.from(100000000); await utils.waitTx( hre, - await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase) + await TaikoL1.init( + AddressManager.address, + l2GenesisBlockHash, + feeBase, + gasExcess + ) ); // Used by LibBridgeRead From 467d1ada65a141225c8ba46efdd46b88500ac97a Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:29:05 +0800 Subject: [PATCH 20/90] fix tests --- packages/protocol/contracts/L1/TaikoData.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 1966ffc89b0..30ced682b3c 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -147,6 +147,6 @@ library TaikoData { uint64 avgProofTime; // miliseconds uint64 feeBase; // Reserved - uint256[41] __gap; + uint256[43] __gap; } } From dcd55ec70511e20b20abf03f8133794f4c95909b Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:29:42 +0800 Subject: [PATCH 21/90] fix tests --- packages/protocol/contracts/L1/TaikoData.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 30ced682b3c..f056de46dc1 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -140,8 +140,8 @@ library TaikoData { uint64 avgBlockTime; // miliseconds uint64 gasExcess; // Changed when a block is proven/finalized - uint64 __reserved4; uint64 lastVerifiedBlockId; + uint64 __reserved4; // the proof time moving average, note that for each block, only the // first proof's time is considered. uint64 avgProofTime; // miliseconds From ec38ef3e36ddce251cdd7ad67f0c8a9850166f28 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:30:06 +0800 Subject: [PATCH 22/90] fix tests --- .../contract-documentation/L1/TaikoData.md | 14 +++++++++----- .../contract-documentation/L1/TaikoErrors.md | 12 ++++++++++++ .../reference/contract-documentation/L1/TaikoL1.md | 10 ++++++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 64febe555b0..895d254244a 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -23,6 +23,8 @@ struct Config { uint256 maxNumVerifiedBlocks; uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; + uint64 gasTargetPerSecond; + uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; @@ -30,7 +32,6 @@ struct Config { uint256 rewardBurnBips; uint256 proposerDepositPctg; uint256 feeBaseMAF; - uint64 bootstrapDiscountHalvingPeriod; uint64 constantFeeRewardBlocks; uint64 txListCacheExpiry; bool enableSoloProposer; @@ -54,6 +55,8 @@ struct StateVariables { uint64 avgBlockTime; uint64 avgProofTime; uint64 lastProposedAt; + uint64 basefee; + uint64 gasExcess; } ``` @@ -77,12 +80,13 @@ struct BlockMetadata { uint64 id; uint64 timestamp; uint64 l1Height; - uint32 gasLimit; + uint64 basefee; bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; uint24 txListByteStart; uint24 txListByteEnd; + uint32 gasLimit; address beneficiary; } ``` @@ -159,11 +163,11 @@ struct State { uint64 numBlocks; uint64 lastProposedAt; uint64 avgBlockTime; - uint64 __reserved3; - uint64 __reserved4; + uint64 gasExcess; uint64 lastVerifiedBlockId; + uint64 __reserved4; uint64 avgProofTime; uint64 feeBase; - uint256[42] __gap; + uint256[43] __gap; } ``` diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index 4028d4ecf94..4df12500046 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -34,6 +34,12 @@ error L1_EVIDENCE_MISMATCH() error L1_FORK_CHOICE_NOT_FOUND() ``` +### L1_INSUFFICIENT_ETHER + +```solidity +error L1_INSUFFICIENT_ETHER() +``` + ### L1_INSUFFICIENT_TOKEN ```solidity @@ -82,6 +88,12 @@ error L1_NOT_ORACLE_PROVER() error L1_NOT_SOLO_PROPOSER() ``` +### L1_OUT_OF_BLOCK_SPACE + +```solidity +error L1_OUT_OF_BLOCK_SPACE() +``` + ### L1_TOO_MANY_BLOCKS ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index 472d9fedf6a..dffe4364e0a 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -13,13 +13,13 @@ struct TaikoData.State state ### init ```solidity -function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase) external +function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, uint64 _gasExcess) external ``` ### proposeBlock ```solidity -function proposeBlock(bytes input, bytes txList) external +function proposeBlock(bytes input, bytes txList) external payable ``` Propose a Taiko L2 block. @@ -115,6 +115,12 @@ function getXchainBlockHash(uint256 blockId) public view returns (bytes32) function getXchainSignalRoot(uint256 blockId) public view returns (bytes32) ``` +### get1559Basefee + +```solidity +function get1559Basefee(uint32 gasLimit) public view returns (uint64 basefee) +``` + ### getStateVariables ```solidity From a48a6b727162f29286149f7d6c77f0a55a79716c Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:33:30 +0800 Subject: [PATCH 23/90] fix tests --- .../contracts/L1/libs/LibL2Tokenomics.sol | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 8d2b2151ef8..02745a69807 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -54,23 +54,26 @@ library LibL2Tokenomics { pure returns (uint64 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) { - unchecked { - uint256 _gasExcess = uint256(gasExcess) + - (gasTargetPerSecond * blockTime); + if (gasInBlock == 0) { + return (gasExcess, 0, 0); + } else { + unchecked { + uint256 _gasExcess = uint256(gasExcess) + + (gasTargetPerSecond * blockTime); - _gasExcess = _gasExcess.min(type(uint64).max); + _gasExcess = _gasExcess.min(type(uint64).max); - if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); + if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); - newGasExcess = uint64(_gasExcess - gasInBlock); + newGasExcess = uint64(_gasExcess - gasInBlock); - gasPurchaseCost = - (gasPoolProduct / newGasExcess) - - (gasPoolProduct / _gasExcess); + gasPurchaseCost = + (gasPoolProduct / newGasExcess) - + (gasPoolProduct / _gasExcess); - basefee = (gasInBlock == 0) - ? 0 - : uint64((gasPurchaseCost / gasInBlock).min(type(uint64).max)); + uint256 _basefee = gasPurchaseCost / gasInBlock; + basefee = uint64(_basefee.min(type(uint64).max)); + } } } } From 51df62e6e48e53b5dae95bcc587295afa2ad7258 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 22:59:57 +0800 Subject: [PATCH 24/90] fix tests --- packages/protocol/test2/LibL2Tokenomics.t.sol | 59 +++++++++++++------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 9be26b30eb8..a02a9829ed8 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -13,55 +13,76 @@ contract TestLibL2Tokenomics is Test { uint64 initialBaseFee = 5000000000; uint32 gasTargetPerSecond = 1000000; - uint64 gasExcess = gasTargetPerSecond * 100; - uint256 gasPoolProduct = uint(gasExcess) * uint(gasExcess) * initialBaseFee; + uint64 gasExcess0 = gasTargetPerSecond * 512; + uint256 gasPoolProduct = + uint(gasExcess0) * uint(gasExcess0) * initialBaseFee; + uint64 gasExcess = gasExcess0; - function test1559Basefee() public { + function test1559PurchaseMaxSizeGasWontOverflow() public { + gasExcess = type(uint64).max; + + (uint64 basefee, uint256 cost) = _purchaseGas( + type(uint32).max, + 0 seconds + ); + assertEq(basefee, 0); + assertEq(cost, 0); + } + + function test1559Basefee_NoChangeAfterRefillTheSameAmount() public { (uint64 basefee1, uint256 cost1) = _purchaseGas( gasTargetPerSecond * 12, - 12 + 12 seconds ); (uint64 basefee2, uint256 cost2) = _purchaseGas( gasTargetPerSecond * 12, - 12 + 12 seconds ); assertEq(basefee1, basefee2); assertEq(cost1, cost2); + gasExcess = gasExcess0; + } - (uint64 basefee3, uint256 cost3) = _purchaseGas(1, 12); + function test1559Basefee_Compare_T_vs_2T() public { + (uint64 basefee, ) = _purchaseGas(1, 24 seconds); + gasExcess = gasExcess0; - console2.log("when purchase a block of size: 1"); - console2.log( - "basefee decreases by:", - 100 - (basefee3 * 100) / basefee2 - ); + (uint64 basefeeT, ) = _purchaseGas(gasTargetPerSecond * 12, 0 seconds); + gasExcess = gasExcess0; + + (uint64 basefee2T, ) = _purchaseGas(gasTargetPerSecond * 24, 0 seconds); + gasExcess = gasExcess0; - (uint64 basefee4, uint256 cost4) = _purchaseGas( - gasTargetPerSecond * 24, - 0 + console2.log("when purchase a block of size gasTargetPerSecond * 12:"); + console2.log( + unicode"👉 basefee increases by %%:", + (basefeeT * 100) / basefee - 100 ); - console2.log("when purchase a block of size: 2 * target"); + console2.log("when purchase a block of size gasTargetPerSecond * 24:"); console2.log( - "basefee increases by:", - (basefee4 * 100) / basefee2 - 100 + unicode"👉 basefee increases by %%:", + (basefee2T * 100) / basefee - 100 ); + } + function test1559Basefee() public { uint64 basefee; for (uint i = 0; i < 5; i++) { (uint64 _basefee, ) = _purchaseGas(gasTargetPerSecond * 12, 0); assertGt(_basefee, basefee); if (basefee > 0) { console2.log( - "gas price", + unicode"👉 gas price %%", (_basefee * 100) / basefee - 100, - "% larger than parent" + "larger than parent" ); } basefee = _basefee; } + gasExcess = gasExcess0; } function _purchaseGas( From 9010aea4a885e08ddcffaf11c1869d9a81139583 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 23:00:50 +0800 Subject: [PATCH 25/90] fix tests --- packages/protocol/test2/LibL2Tokenomics.t.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index a02a9829ed8..448694fe186 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -27,6 +27,7 @@ contract TestLibL2Tokenomics is Test { ); assertEq(basefee, 0); assertEq(cost, 0); + gasExcess = gasExcess0; } function test1559Basefee_NoChangeAfterRefillTheSameAmount() public { @@ -68,7 +69,7 @@ contract TestLibL2Tokenomics is Test { ); } - function test1559Basefee() public { + function test1559Basefee_EverIncreaseing() public { uint64 basefee; for (uint i = 0; i < 5; i++) { (uint64 _basefee, ) = _purchaseGas(gasTargetPerSecond * 12, 0); From 95fc8cdcb0de1b6fad962fe1172d07b9641e2807 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 23:17:42 +0800 Subject: [PATCH 26/90] fix tests --- .../protocol/contracts/L1/TaikoConfig.sol | 2 +- .../contracts/L1/libs/LibL2Tokenomics.sol | 29 +++++++++---------- packages/protocol/test2/LibL2Tokenomics.t.sol | 17 +++++++---- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 644cfc2094d..73be22baa5c 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasTargetPerSecond: 6000000, + gasTargetPerSecond: 30000000, gasPoolProduct: (6000000 * 12) ** 2 * 50000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 02745a69807..8f2e8537467 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -45,10 +45,10 @@ library LibL2Tokenomics { // have a solidity exp(uint256 x) implementation. function calc1559Basefee( uint64 gasExcess, - uint64 gasTargetPerSecond, + uint256 gasTargetPerSecond, uint256 gasPoolProduct, uint32 gasInBlock, - uint64 blockTime + uint256 blockTime ) internal pure @@ -56,24 +56,23 @@ library LibL2Tokenomics { { if (gasInBlock == 0) { return (gasExcess, 0, 0); - } else { - unchecked { - uint256 _gasExcess = uint256(gasExcess) + - (gasTargetPerSecond * blockTime); + } + unchecked { + uint256 _gasExcess = gasTargetPerSecond * blockTime + gasExcess; - _gasExcess = _gasExcess.min(type(uint64).max); + _gasExcess = _gasExcess.min(type(uint64).max); - if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); + if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); - newGasExcess = uint64(_gasExcess - gasInBlock); + newGasExcess = uint64(_gasExcess - gasInBlock); - gasPurchaseCost = - (gasPoolProduct / newGasExcess) - - (gasPoolProduct / _gasExcess); + gasPurchaseCost = + (gasPoolProduct / newGasExcess) - + (gasPoolProduct / _gasExcess); - uint256 _basefee = gasPurchaseCost / gasInBlock; - basefee = uint64(_basefee.min(type(uint64).max)); - } + uint256 _basefee = gasPurchaseCost / gasInBlock; + basefee = uint64(_basefee.min(type(uint64).max)); + gasPurchaseCost = uint256(basefee) * gasInBlock; } } } diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 448694fe186..d2b25d39ec3 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -11,11 +11,14 @@ import { contract TestLibL2Tokenomics is Test { using SafeCastUpgradeable for uint256; + // Ethereum offers 15M gas per 12 seconds, if we scale it by 24 times, + // then each second, Taiko can offer 30M gas. uint64 initialBaseFee = 5000000000; - uint32 gasTargetPerSecond = 1000000; - uint64 gasExcess0 = gasTargetPerSecond * 512; + uint32 gasTargetPerSecond = 30000000; // 30M gas per second + uint64 gasExcess0 = uint64(gasTargetPerSecond) * 200; uint256 gasPoolProduct = uint(gasExcess0) * uint(gasExcess0) * initialBaseFee; + uint64 gasExcess = gasExcess0; function test1559PurchaseMaxSizeGasWontOverflow() public { @@ -47,22 +50,24 @@ contract TestLibL2Tokenomics is Test { } function test1559Basefee_Compare_T_vs_2T() public { + uint32 blockMaxGasLimit = 6000000; + (uint64 basefee, ) = _purchaseGas(1, 24 seconds); gasExcess = gasExcess0; - (uint64 basefeeT, ) = _purchaseGas(gasTargetPerSecond * 12, 0 seconds); + (uint64 basefeeT, ) = _purchaseGas(blockMaxGasLimit / 2, 0 seconds); gasExcess = gasExcess0; - (uint64 basefee2T, ) = _purchaseGas(gasTargetPerSecond * 24, 0 seconds); + (uint64 basefee2T, ) = _purchaseGas(blockMaxGasLimit, 0 seconds); gasExcess = gasExcess0; - console2.log("when purchase a block of size gasTargetPerSecond * 12:"); + console2.log("when purchase a block of size blockMaxGasLimit/2:"); console2.log( unicode"👉 basefee increases by %%:", (basefeeT * 100) / basefee - 100 ); - console2.log("when purchase a block of size gasTargetPerSecond * 24:"); + console2.log("when purchase a block of size blockMaxGasLimit:"); console2.log( unicode"👉 basefee increases by %%:", (basefee2T * 100) / basefee - 100 From 80388a2d257977534c9094645ac56c478b24dc29 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 23:21:00 +0800 Subject: [PATCH 27/90] fix tests --- packages/protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/test2/LibL2Tokenomics.t.sol | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 73be22baa5c..6e13346a7aa 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -24,7 +24,7 @@ library TaikoConfig { // testnet's circuits. blockMaxGasLimit: 6000000, gasTargetPerSecond: 30000000, - gasPoolProduct: (6000000 * 12) ** 2 * 50000000000, + gasPoolProduct: (30000000 * 200) ** 2 * 5000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index d2b25d39ec3..9d456a1a299 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -11,9 +11,11 @@ import { contract TestLibL2Tokenomics is Test { using SafeCastUpgradeable for uint256; + uint64 initialBaseFee = 5000000000; + // Ethereum offers 15M gas per 12 seconds, if we scale it by 24 times, // then each second, Taiko can offer 30M gas. - uint64 initialBaseFee = 5000000000; + uint32 gasTargetPerSecond = 30000000; // 30M gas per second uint64 gasExcess0 = uint64(gasTargetPerSecond) * 200; uint256 gasPoolProduct = @@ -21,6 +23,10 @@ contract TestLibL2Tokenomics is Test { uint64 gasExcess = gasExcess0; + function setUp() public view { + console2.log("gasPoolProduct:", gasPoolProduct); + } + function test1559PurchaseMaxSizeGasWontOverflow() public { gasExcess = type(uint64).max; From 718f25f71df5acd7a743cccf1d06b2fd65c2b85f Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Mon, 27 Mar 2023 23:21:20 +0800 Subject: [PATCH 28/90] fix tests --- packages/protocol/test2/LibL2Tokenomics.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 9d456a1a299..ccb0fc2f0ed 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -24,7 +24,7 @@ contract TestLibL2Tokenomics is Test { uint64 gasExcess = gasExcess0; function setUp() public view { - console2.log("gasPoolProduct:", gasPoolProduct); + // console2.log("gasPoolProduct:", gasPoolProduct); } function test1559PurchaseMaxSizeGasWontOverflow() public { From da2b35f08530170e87fa27411f433083bf826bb9 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 28 Mar 2023 10:48:06 +0800 Subject: [PATCH 29/90] fix tests --- .../protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/contracts/L1/TaikoData.sol | 6 +-- packages/protocol/contracts/L1/TaikoL1.sol | 4 +- .../contracts/L1/libs/LibL2Tokenomics.sol | 40 +++++++++++------ .../contracts/L1/libs/LibProposing.sol | 7 ++- .../protocol/contracts/L1/libs/LibUtils.sol | 4 +- .../contracts/L1/libs/LibVerifying.sol | 4 +- packages/protocol/tasks/deploy_L1.ts | 4 +- packages/protocol/test2/LibL2Tokenomics.t.sol | 43 ++++++++++--------- packages/protocol/test2/TaikoL1TestBase.sol | 4 +- .../contract-documentation/L1/TaikoData.md | 6 +-- .../contract-documentation/L1/TaikoL1.md | 2 +- 12 files changed, 72 insertions(+), 54 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 6e13346a7aa..f40db194906 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasTargetPerSecond: 30000000, + gasAccumulatedPerSecond: 30000000, gasPoolProduct: (30000000 * 200) ** 2 * 5000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index f056de46dc1..97801c0eaeb 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,7 +21,7 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint64 gasTargetPerSecond; + uint64 gasAccumulatedPerSecond; uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; @@ -51,7 +51,7 @@ library TaikoData { uint64 avgProofTime; uint64 lastProposedAt; uint64 basefee; - uint64 gasExcess; + uint64 gasAccumulated; } // 3 slots @@ -138,7 +138,7 @@ library TaikoData { uint64 numBlocks; uint64 lastProposedAt; // Timestamp when the last block is proposed. uint64 avgBlockTime; // miliseconds - uint64 gasExcess; + uint64 gasAccumulated; // Changed when a block is proven/finalized uint64 lastVerifiedBlockId; uint64 __reserved4; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index c154c7a2fb4..c20109b1f1f 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -30,7 +30,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, - uint64 _gasExcess + uint64 _gasAccumulated ) external initializer { EssentialContract._init(_addressManager); LibVerifying.init({ @@ -38,7 +38,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { config: getConfig(), genesisBlockHash: _genesisBlockHash, feeBase: _feeBase, - gasExcess: _gasExcess + gasAccumulated: _gasAccumulated }); } diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 8f2e8537467..e0920f79abb 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -27,12 +27,16 @@ library LibL2Tokenomics { ) internal view - returns (uint64 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) + returns ( + uint64 newGasAccumulated, + uint64 basefee, + uint256 gasPurchaseCost + ) { return calc1559Basefee( - state.gasExcess, - config.gasTargetPerSecond, + state.gasAccumulated, + config.gasAccumulatedPerSecond, config.gasPoolProduct, gasInBlock, uint64(block.timestamp - state.lastProposedAt) @@ -44,31 +48,41 @@ library LibL2Tokenomics { // But the current implementation use AMM style math as we don't yet // have a solidity exp(uint256 x) implementation. function calc1559Basefee( - uint64 gasExcess, - uint256 gasTargetPerSecond, + uint64 gasAccumulated, + uint256 gasAccumulatedPerSecond, uint256 gasPoolProduct, uint32 gasInBlock, uint256 blockTime ) internal pure - returns (uint64 newGasExcess, uint64 basefee, uint256 gasPurchaseCost) + returns ( + uint64 newGasAccumulated, + uint64 basefee, + uint256 gasPurchaseCost + ) { if (gasInBlock == 0) { - return (gasExcess, 0, 0); + return ( + gasAccumulated, + uint64(gasPoolProduct / gasAccumulated / gasAccumulated), + 0 + ); } unchecked { - uint256 _gasExcess = gasTargetPerSecond * blockTime + gasExcess; + uint256 _gasAccumulated = gasAccumulatedPerSecond * + blockTime + + gasAccumulated; - _gasExcess = _gasExcess.min(type(uint64).max); + _gasAccumulated = _gasAccumulated.min(type(uint64).max); - if (gasInBlock >= _gasExcess) revert L1_OUT_OF_BLOCK_SPACE(); + if (gasInBlock >= _gasAccumulated) revert L1_OUT_OF_BLOCK_SPACE(); - newGasExcess = uint64(_gasExcess - gasInBlock); + newGasAccumulated = uint64(_gasAccumulated - gasInBlock); gasPurchaseCost = - (gasPoolProduct / newGasExcess) - - (gasPoolProduct / _gasExcess); + (gasPoolProduct / newGasAccumulated) - + (gasPoolProduct / _gasAccumulated); uint256 _basefee = gasPurchaseCost / gasInBlock; basefee = uint64(_basefee.min(type(uint64).max)); diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 9c7552342d1..6413c6e5838 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -85,8 +85,11 @@ library LibProposing { { // calculate L2 EIP-1559 gas fee and cost uint256 gasPurchaseCost; - (state.gasExcess, meta.basefee, gasPurchaseCost) = LibL2Tokenomics - .get1559Basefee(state, config, input.gasLimit); + ( + state.gasAccumulated, + meta.basefee, + gasPurchaseCost + ) = LibL2Tokenomics.get1559Basefee(state, config, input.gasLimit); if (msg.value < gasPurchaseCost) revert L1_INSUFFICIENT_ETHER(); resolver.resolve("treasure", false).sendEther(gasPurchaseCost); diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 88d209036e2..0614e758537 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -43,11 +43,11 @@ library LibUtils { lastVerifiedBlockId: state.lastVerifiedBlockId, avgProofTime: state.avgProofTime, basefee: uint64( - (config.gasPoolProduct / state.gasExcess).min( + (config.gasPoolProduct / state.gasAccumulated).min( type(uint64).max ) ), - gasExcess: state.gasExcess + gasAccumulated: state.gasAccumulated }); } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 88b1f53f1ab..fd2477a8113 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -32,7 +32,7 @@ library LibVerifying { TaikoData.Config memory config, bytes32 genesisBlockHash, uint64 feeBase, - uint64 gasExcess + uint64 gasAccumulated ) internal { _checkConfig(config); @@ -41,7 +41,7 @@ library LibVerifying { state.genesisTimestamp = timeNow; state.feeBase = feeBase; state.numBlocks = 1; - state.gasExcess = gasExcess; + state.gasAccumulated = gasAccumulated; TaikoData.Block storage blk = state.blocks[0]; blk.proposedAt = timeNow; diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 436172dc430..45ed452b1ad 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -154,7 +154,7 @@ export async function deployContracts(hre: any) { ); const feeBase = hre.ethers.BigNumber.from(10).pow(18); - const gasExcess = hre.ethers.BigNumber.from(100000000); + const gasAccumulated = hre.ethers.BigNumber.from(100000000); await utils.waitTx( hre, @@ -162,7 +162,7 @@ export async function deployContracts(hre: any) { AddressManager.address, l2GenesisBlockHash, feeBase, - gasExcess + gasAccumulated ) ); diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index ccb0fc2f0ed..1ae1e45a0d2 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -16,19 +16,19 @@ contract TestLibL2Tokenomics is Test { // Ethereum offers 15M gas per 12 seconds, if we scale it by 24 times, // then each second, Taiko can offer 30M gas. - uint32 gasTargetPerSecond = 30000000; // 30M gas per second - uint64 gasExcess0 = uint64(gasTargetPerSecond) * 200; + uint32 gasAccumulatedPerSecond = 30000000; // 30M gas per second + uint64 gasAccumulated0 = uint64(gasAccumulatedPerSecond) * 200; uint256 gasPoolProduct = - uint(gasExcess0) * uint(gasExcess0) * initialBaseFee; + uint(gasAccumulated0) * uint(gasAccumulated0) * initialBaseFee; - uint64 gasExcess = gasExcess0; + uint64 gasAccumulated = gasAccumulated0; function setUp() public view { // console2.log("gasPoolProduct:", gasPoolProduct); } function test1559PurchaseMaxSizeGasWontOverflow() public { - gasExcess = type(uint64).max; + gasAccumulated = type(uint64).max; (uint64 basefee, uint256 cost) = _purchaseGas( type(uint32).max, @@ -36,36 +36,36 @@ contract TestLibL2Tokenomics is Test { ); assertEq(basefee, 0); assertEq(cost, 0); - gasExcess = gasExcess0; + gasAccumulated = gasAccumulated0; } function test1559Basefee_NoChangeAfterRefillTheSameAmount() public { (uint64 basefee1, uint256 cost1) = _purchaseGas( - gasTargetPerSecond * 12, + gasAccumulatedPerSecond * 12, 12 seconds ); (uint64 basefee2, uint256 cost2) = _purchaseGas( - gasTargetPerSecond * 12, + gasAccumulatedPerSecond * 12, 12 seconds ); assertEq(basefee1, basefee2); assertEq(cost1, cost2); - gasExcess = gasExcess0; + gasAccumulated = gasAccumulated0; } function test1559Basefee_Compare_T_vs_2T() public { uint32 blockMaxGasLimit = 6000000; (uint64 basefee, ) = _purchaseGas(1, 24 seconds); - gasExcess = gasExcess0; + gasAccumulated = gasAccumulated0; (uint64 basefeeT, ) = _purchaseGas(blockMaxGasLimit / 2, 0 seconds); - gasExcess = gasExcess0; + gasAccumulated = gasAccumulated0; (uint64 basefee2T, ) = _purchaseGas(blockMaxGasLimit, 0 seconds); - gasExcess = gasExcess0; + gasAccumulated = gasAccumulated0; console2.log("when purchase a block of size blockMaxGasLimit/2:"); console2.log( @@ -83,7 +83,7 @@ contract TestLibL2Tokenomics is Test { function test1559Basefee_EverIncreaseing() public { uint64 basefee; for (uint i = 0; i < 5; i++) { - (uint64 _basefee, ) = _purchaseGas(gasTargetPerSecond * 12, 0); + (uint64 _basefee, ) = _purchaseGas(gasAccumulatedPerSecond * 12, 0); assertGt(_basefee, basefee); if (basefee > 0) { console2.log( @@ -94,19 +94,20 @@ contract TestLibL2Tokenomics is Test { } basefee = _basefee; } - gasExcess = gasExcess0; + gasAccumulated = gasAccumulated0; } function _purchaseGas( uint32 amount, uint64 blockTime ) private returns (uint64 basefee, uint256 gasPurchaseCost) { - (gasExcess, basefee, gasPurchaseCost) = LibL2Tokenomics.calc1559Basefee( - gasExcess, - gasTargetPerSecond, - gasPoolProduct, - amount, - blockTime - ); + (gasAccumulated, basefee, gasPurchaseCost) = LibL2Tokenomics + .calc1559Basefee( + gasAccumulated, + gasAccumulatedPerSecond, + gasPoolProduct, + amount, + blockTime + ); } } diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.sol index 32919e79df1..c25f48e3e4c 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.sol @@ -22,7 +22,7 @@ abstract contract TaikoL1TestBase is Test { bytes32 public constant GENESIS_BLOCK_HASH = keccak256("GENESIS_BLOCK_HASH"); uint64 feeBase = 1E8; // 1 TKO - uint64 gasExcess = 1E18; + uint64 gasAccumulated = 1E18; address public constant Treasure = 0x859d74b52762d9ed07D1b2B8d7F93d26B1EA78Bb; @@ -48,7 +48,7 @@ abstract contract TaikoL1TestBase is Test { address(addressManager), GENESIS_BLOCK_HASH, feeBase, - gasExcess + gasAccumulated ); conf = L1.getConfig(); diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 895d254244a..0c999b9637d 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -23,7 +23,7 @@ struct Config { uint256 maxNumVerifiedBlocks; uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint64 gasTargetPerSecond; + uint64 gasAccumulatedPerSecond; uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; @@ -56,7 +56,7 @@ struct StateVariables { uint64 avgProofTime; uint64 lastProposedAt; uint64 basefee; - uint64 gasExcess; + uint64 gasAccumulated; } ``` @@ -163,7 +163,7 @@ struct State { uint64 numBlocks; uint64 lastProposedAt; uint64 avgBlockTime; - uint64 gasExcess; + uint64 gasAccumulated; uint64 lastVerifiedBlockId; uint64 __reserved4; uint64 avgProofTime; diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index dffe4364e0a..1cee555e2f3 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -13,7 +13,7 @@ struct TaikoData.State state ### init ```solidity -function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, uint64 _gasExcess) external +function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, uint64 _gasAccumulated) external ``` ### proposeBlock From 8c6e3fa92606b6ffa5bbfa77892cab7f90de8a42 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 28 Mar 2023 10:49:30 +0800 Subject: [PATCH 30/90] fix tests --- packages/protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/contracts/L1/TaikoData.sol | 2 +- .../protocol/contracts/L1/libs/LibL2Tokenomics.sol | 6 +++--- packages/protocol/test2/LibL2Tokenomics.t.sol | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index f40db194906..6e13346a7aa 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasAccumulatedPerSecond: 30000000, + gasTargetPerSecond: 30000000, gasPoolProduct: (30000000 * 200) ** 2 * 5000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 97801c0eaeb..b07a58b9250 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,7 +21,7 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint64 gasAccumulatedPerSecond; + uint64 gasTargetPerSecond; uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index e0920f79abb..e9efaf4d9ec 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -36,7 +36,7 @@ library LibL2Tokenomics { return calc1559Basefee( state.gasAccumulated, - config.gasAccumulatedPerSecond, + config.gasTargetPerSecond, config.gasPoolProduct, gasInBlock, uint64(block.timestamp - state.lastProposedAt) @@ -49,7 +49,7 @@ library LibL2Tokenomics { // have a solidity exp(uint256 x) implementation. function calc1559Basefee( uint64 gasAccumulated, - uint256 gasAccumulatedPerSecond, + uint256 gasTargetPerSecond, uint256 gasPoolProduct, uint32 gasInBlock, uint256 blockTime @@ -70,7 +70,7 @@ library LibL2Tokenomics { ); } unchecked { - uint256 _gasAccumulated = gasAccumulatedPerSecond * + uint256 _gasAccumulated = gasTargetPerSecond * blockTime + gasAccumulated; diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 1ae1e45a0d2..6cddac62bd9 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -16,8 +16,8 @@ contract TestLibL2Tokenomics is Test { // Ethereum offers 15M gas per 12 seconds, if we scale it by 24 times, // then each second, Taiko can offer 30M gas. - uint32 gasAccumulatedPerSecond = 30000000; // 30M gas per second - uint64 gasAccumulated0 = uint64(gasAccumulatedPerSecond) * 200; + uint32 gasTargetPerSecond = 30000000; // 30M gas per second + uint64 gasAccumulated0 = uint64(gasTargetPerSecond) * 200; uint256 gasPoolProduct = uint(gasAccumulated0) * uint(gasAccumulated0) * initialBaseFee; @@ -41,12 +41,12 @@ contract TestLibL2Tokenomics is Test { function test1559Basefee_NoChangeAfterRefillTheSameAmount() public { (uint64 basefee1, uint256 cost1) = _purchaseGas( - gasAccumulatedPerSecond * 12, + gasTargetPerSecond * 12, 12 seconds ); (uint64 basefee2, uint256 cost2) = _purchaseGas( - gasAccumulatedPerSecond * 12, + gasTargetPerSecond * 12, 12 seconds ); @@ -83,7 +83,7 @@ contract TestLibL2Tokenomics is Test { function test1559Basefee_EverIncreaseing() public { uint64 basefee; for (uint i = 0; i < 5; i++) { - (uint64 _basefee, ) = _purchaseGas(gasAccumulatedPerSecond * 12, 0); + (uint64 _basefee, ) = _purchaseGas(gasTargetPerSecond * 12, 0); assertGt(_basefee, basefee); if (basefee > 0) { console2.log( @@ -104,7 +104,7 @@ contract TestLibL2Tokenomics is Test { (gasAccumulated, basefee, gasPurchaseCost) = LibL2Tokenomics .calc1559Basefee( gasAccumulated, - gasAccumulatedPerSecond, + gasTargetPerSecond, gasPoolProduct, amount, blockTime From dd658eb4d9ca5ae5a8c26071624c0a18ac86af0f Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 28 Mar 2023 10:51:00 +0800 Subject: [PATCH 31/90] fix tests --- packages/protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/contracts/L1/TaikoData.sol | 2 +- .../protocol/contracts/L1/libs/LibL2Tokenomics.sol | 10 +++++----- packages/protocol/contracts/L1/libs/LibUtils.sol | 2 +- packages/protocol/test2/LibL2Tokenomics.t.sol | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 6e13346a7aa..778219b876f 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -24,7 +24,7 @@ library TaikoConfig { // testnet's circuits. blockMaxGasLimit: 6000000, gasTargetPerSecond: 30000000, - gasPoolProduct: (30000000 * 200) ** 2 * 5000000000, + gasAdjustmentFactor: (30000000 * 200) ** 2 * 5000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index b07a58b9250..73cab542a91 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -22,7 +22,7 @@ library TaikoData { uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; uint64 gasTargetPerSecond; - uint256 gasPoolProduct; + uint256 gasAdjustmentFactor; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index e9efaf4d9ec..ac0d503f22c 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -37,7 +37,7 @@ library LibL2Tokenomics { calc1559Basefee( state.gasAccumulated, config.gasTargetPerSecond, - config.gasPoolProduct, + config.gasAdjustmentFactor, gasInBlock, uint64(block.timestamp - state.lastProposedAt) ); @@ -50,7 +50,7 @@ library LibL2Tokenomics { function calc1559Basefee( uint64 gasAccumulated, uint256 gasTargetPerSecond, - uint256 gasPoolProduct, + uint256 gasAdjustmentFactor, uint32 gasInBlock, uint256 blockTime ) @@ -65,7 +65,7 @@ library LibL2Tokenomics { if (gasInBlock == 0) { return ( gasAccumulated, - uint64(gasPoolProduct / gasAccumulated / gasAccumulated), + uint64(gasAdjustmentFactor / gasAccumulated / gasAccumulated), 0 ); } @@ -81,8 +81,8 @@ library LibL2Tokenomics { newGasAccumulated = uint64(_gasAccumulated - gasInBlock); gasPurchaseCost = - (gasPoolProduct / newGasAccumulated) - - (gasPoolProduct / _gasAccumulated); + (gasAdjustmentFactor / newGasAccumulated) - + (gasAdjustmentFactor / _gasAccumulated); uint256 _basefee = gasPurchaseCost / gasInBlock; basefee = uint64(_basefee.min(type(uint64).max)); diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 0614e758537..38dccf324a6 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -43,7 +43,7 @@ library LibUtils { lastVerifiedBlockId: state.lastVerifiedBlockId, avgProofTime: state.avgProofTime, basefee: uint64( - (config.gasPoolProduct / state.gasAccumulated).min( + (config.gasAdjustmentFactor / state.gasAccumulated).min( type(uint64).max ) ), diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 6cddac62bd9..2a2f87e05bf 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -18,13 +18,13 @@ contract TestLibL2Tokenomics is Test { uint32 gasTargetPerSecond = 30000000; // 30M gas per second uint64 gasAccumulated0 = uint64(gasTargetPerSecond) * 200; - uint256 gasPoolProduct = + uint256 gasAdjustmentFactor = uint(gasAccumulated0) * uint(gasAccumulated0) * initialBaseFee; uint64 gasAccumulated = gasAccumulated0; function setUp() public view { - // console2.log("gasPoolProduct:", gasPoolProduct); + // console2.log("gasAdjustmentFactor:", gasAdjustmentFactor); } function test1559PurchaseMaxSizeGasWontOverflow() public { @@ -105,7 +105,7 @@ contract TestLibL2Tokenomics is Test { .calc1559Basefee( gasAccumulated, gasTargetPerSecond, - gasPoolProduct, + gasAdjustmentFactor, amount, blockTime ); From f9bb2a9dd715adbac9d3fc7983f6f4411ba16e6d Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 28 Mar 2023 12:09:01 +0200 Subject: [PATCH 32/90] Add FeeAndRewardConfig structure to calculate fee-rewards based on prooftime --- .../protocol/contracts/L1/TaikoConfig.sol | 7 + packages/protocol/contracts/L1/TaikoData.sol | 13 + .../contracts/L1/libs/LibL1Tokenomics.sol | 63 ++ .../contracts/L1/libs/LibVerifying.sol | 23 + .../contracts/thirdparty/ABDKMath64x64.sol | 842 ++++++++++++++++++ 5 files changed, 948 insertions(+) create mode 100644 packages/protocol/contracts/thirdparty/ABDKMath64x64.sol diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 778219b876f..7196ba37e27 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -52,6 +52,13 @@ library TaikoConfig { provingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, dampingFactorBips: 2500 // [75% -> 125%] + }), + feeConfig: TaikoData.FeeAndRewardConfig({ + baseFeeProof: 0, + rewardIssued: 0, + rewardTargetPerGas: 100000000, // 0.1 TAI + targetDelayBonusPerGas: 1000000, // 0.001 TAI + adjustmentQuotient: 32 }) }); } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 73cab542a91..5eb7bf58383 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -12,6 +12,16 @@ library TaikoData { uint16 dampingFactorBips; } + struct FeeAndRewardConfig { + uint256 baseFeeProof; + uint64 rewardIssued; + // avgProofTimeMAF is known already + //uint16 avgProofTimeMAF; + uint64 rewardTargetPerGas; + uint64 targetDelayBonusPerGas; + uint8 adjustmentQuotient; + } + struct Config { uint256 chainId; uint256 maxNumProposedBlocks; @@ -39,6 +49,8 @@ library TaikoData { bool skipZKPVerification; FeeConfig proposingConfig; FeeConfig provingConfig; + // Hopefully the above 2 can be deleted later if that fits + FeeAndRewardConfig feeConfig; } struct StateVariables { @@ -108,6 +120,7 @@ library TaikoData { uint64 blockId; uint64 proposedAt; uint64 deposit; + uint32 gasLimit; uint24 nextForkChoiceId; uint24 verifiedForkChoiceId; bytes32 metaHash; diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 5c4df857fa7..2ecaf62023a 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -13,12 +13,15 @@ import { } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; +import {ABDKMath64x64} from "../../thirdparty/ABDKMath64x64.sol"; library LibL1Tokenomics { using LibMath for uint256; + using LibMath for uint64; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); + error L1_NO_GAS(); function withdraw( TaikoData.State storage state, @@ -166,4 +169,64 @@ library LibL1Tokenomics { } } } + + /// @notice Calculate the block reward multiplier based on delay + /// @dev Bigger delays and smaller the blocks results greater the reward + /// @dev Smaller the block - greater the reward. + /// @param config - Config, containing FeeAndRewardConfig + /// @param usedGas - Gas in the block + /// @param delay - Delay compare to avgProofTime + function getBlockRewardMultiplier( + TaikoData.Config memory config, + uint32 usedGas, + uint256 delay + ) internal pure returns (uint256 blockReward) { + if (usedGas == 0) revert L1_NO_GAS(); + + blockReward = + config.feeConfig.rewardTargetPerGas * + (delay / (config.feeConfig.targetDelayBonusPerGas * usedGas)); + } + + /// @notice Update the baseFee for proofs + /// @param feeConfig - Config, containing FeeAndRewardConfig + /// @param usedGas - Gas in the block + /// @param blockRewardMultiplier - Block reward + function updateBaseProof( + TaikoData.FeeAndRewardConfig memory feeConfig, + uint256 blockRewardMultiplier, + uint32 usedGas + ) internal pure returns (uint64 rewardIssued, uint256 newBaseFeeProof) { + rewardIssued = uint64( + (uint64(0)).max( + feeConfig.rewardIssued + + blockRewardMultiplier - + feeConfig.rewardTargetPerGas * + usedGas + ) + ); + newBaseFeeProof = + ethAmount( + rewardIssued / usedGas, + feeConfig.rewardTargetPerGas, + feeConfig.adjustmentQuotient + ) - + (feeConfig.rewardTargetPerGas * feeConfig.adjustmentQuotient); + } + + /// @notice Calculating the exponential via ABDKMath64x64 + /// @param value - Result of rewardIssued / usedGas + /// @param target - Reward targer per gas + /// @param quotient - Quotient + function ethAmount( + uint256 value, + uint256 target, + uint256 quotient + ) internal pure returns (uint256) { + int128 valueInt128 = ABDKMath64x64.exp( + ABDKMath64x64.divu(value, target * quotient) + ); + + return uint256(ABDKMath64x64.toUInt(valueInt128)); + } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index fd2477a8113..ffc27a694fa 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -152,6 +152,29 @@ library LibVerifying { unchecked { proofTime = (fc.provenAt - blk.proposedAt) * 1000; } + + // Calculate block reward multiplier + uint256 blockRewardMultiplier; + if (proofTime > state.avgProofTime) { + blockRewardMultiplier = LibL1Tokenomics.getBlockRewardMultiplier( + config, + blk.gasLimit, + proofTime - state.avgProofTime + ); + } else { + blockRewardMultiplier = LibL1Tokenomics.getBlockRewardMultiplier( + config, + blk.gasLimit, + state.avgProofTime + ); + } + + LibL1Tokenomics.updateBaseProof( + config.feeConfig, + blockRewardMultiplier, + blk.gasLimit + ); + state.avgProofTime = LibUtils .movingAverage({ maValue: state.avgProofTime, diff --git a/packages/protocol/contracts/thirdparty/ABDKMath64x64.sol b/packages/protocol/contracts/thirdparty/ABDKMath64x64.sol new file mode 100644 index 00000000000..e346cd5dc26 --- /dev/null +++ b/packages/protocol/contracts/thirdparty/ABDKMath64x64.sol @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: BSD-4-Clause +/* + * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. + * Author: Mikhail Vladimirov + */ +pragma solidity ^0.8.18; + +/** + * Smart contract library of mathematical functions operating with signed + * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is + * basically a simple fraction whose numerator is signed 128-bit integer and + * denominator is 2^64. As long as denominator is always the same, there is no + * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are + * represented by int128 type holding only the numerator. + */ +library ABDKMath64x64 { + /* + * Minimum value signed 64.64-bit fixed point number may have. + */ + int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; + + /* + * Maximum value signed 64.64-bit fixed point number may have. + */ + int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + /** + * Convert signed 256-bit integer number into signed 64.64-bit fixed point + * number. Revert on overflow. + * + * @param x signed 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function fromInt(int256 x) internal pure returns (int128) { + unchecked { + require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); + return int128(x << 64); + } + } + + /** + * Convert signed 64.64 fixed point number into signed 64-bit integer number + * rounding down. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64-bit integer number + */ + function toInt(int128 x) internal pure returns (int64) { + unchecked { + return int64(x >> 64); + } + } + + /** + * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point + * number. Revert on overflow. + * + * @param x unsigned 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function fromUInt(uint256 x) internal pure returns (int128) { + unchecked { + require(x <= 0x7FFFFFFFFFFFFFFF); + return int128(int256(x << 64)); + } + } + + /** + * Convert signed 64.64 fixed point number into unsigned 64-bit integer + * number rounding down. Revert on underflow. + * + * @param x signed 64.64-bit fixed point number + * @return unsigned 64-bit integer number + */ + function toUInt(int128 x) internal pure returns (uint64) { + unchecked { + require(x >= 0); + return uint64(uint128(x >> 64)); + } + } + + /** + * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point + * number rounding down. Revert on overflow. + * + * @param x signed 128.128-bin fixed point number + * @return signed 64.64-bit fixed point number + */ + function from128x128(int256 x) internal pure returns (int128) { + unchecked { + int256 result = x >> 64; + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Convert signed 64.64 fixed point number into signed 128.128 fixed point + * number. + * + * @param x signed 64.64-bit fixed point number + * @return signed 128.128 fixed point number + */ + function to128x128(int128 x) internal pure returns (int256) { + unchecked { + return int256(x) << 64; + } + } + + /** + * Calculate x + y. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function add(int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) + y; + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Calculate x - y. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function sub(int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) - y; + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Calculate x * y rounding down. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function mul(int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = (int256(x) * y) >> 64; + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point + * number and y is signed 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64 fixed point number + * @param y signed 256-bit integer number + * @return signed 256-bit integer number + */ + function muli(int128 x, int256 y) internal pure returns (int256) { + unchecked { + if (x == MIN_64x64) { + require( + y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && + y <= 0x1000000000000000000000000000000000000000000000000 + ); + return -y << 63; + } else { + bool negativeResult = false; + if (x < 0) { + x = -x; + negativeResult = true; + } + if (y < 0) { + y = -y; // We rely on overflow behavior here + negativeResult = !negativeResult; + } + uint256 absoluteResult = mulu(x, uint256(y)); + if (negativeResult) { + require( + absoluteResult <= + 0x8000000000000000000000000000000000000000000000000000000000000000 + ); + return -int256(absoluteResult); // We rely on overflow behavior here + } else { + require( + absoluteResult <= + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ); + return int256(absoluteResult); + } + } + } + } + + /** + * Calculate x * y rounding down, where x is signed 64.64 fixed point number + * and y is unsigned 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64 fixed point number + * @param y unsigned 256-bit integer number + * @return unsigned 256-bit integer number + */ + function mulu(int128 x, uint256 y) internal pure returns (uint256) { + unchecked { + if (y == 0) return 0; + + require(x >= 0); + + uint256 lo = (uint256(int256(x)) * + (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; + uint256 hi = uint256(int256(x)) * (y >> 128); + + require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + hi <<= 64; + + require( + hi <= + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - + lo + ); + return hi + lo; + } + } + + /** + * Calculate x / y rounding towards zero. Revert on overflow or when y is + * zero. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function div(int128 x, int128 y) internal pure returns (int128) { + unchecked { + require(y != 0); + int256 result = (int256(x) << 64) / y; + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are signed 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x signed 256-bit integer number + * @param y signed 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function divi(int256 x, int256 y) internal pure returns (int128) { + unchecked { + require(y != 0); + + bool negativeResult = false; + if (x < 0) { + x = -x; // We rely on overflow behavior here + negativeResult = true; + } + if (y < 0) { + y = -y; // We rely on overflow behavior here + negativeResult = !negativeResult; + } + uint128 absoluteResult = divuu(uint256(x), uint256(y)); + if (negativeResult) { + require(absoluteResult <= 0x80000000000000000000000000000000); + return -int128(absoluteResult); // We rely on overflow behavior here + } else { + require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return int128(absoluteResult); // We rely on overflow behavior here + } + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x unsigned 256-bit integer number + * @param y unsigned 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function divu(uint256 x, uint256 y) internal pure returns (int128) { + unchecked { + require(y != 0); + uint128 result = divuu(x, y); + require(result <= uint128(MAX_64x64)); + return int128(result); + } + } + + /** + * Calculate -x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function neg(int128 x) internal pure returns (int128) { + unchecked { + require(x != MIN_64x64); + return -x; + } + } + + /** + * Calculate |x|. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function abs(int128 x) internal pure returns (int128) { + unchecked { + require(x != MIN_64x64); + return x < 0 ? -x : x; + } + } + + /** + * Calculate 1 / x rounding towards zero. Revert on overflow or when x is + * zero. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function inv(int128 x) internal pure returns (int128) { + unchecked { + require(x != 0); + int256 result = int256(0x100000000000000000000000000000000) / x; + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function avg(int128 x, int128 y) internal pure returns (int128) { + unchecked { + return int128((int256(x) + int256(y)) >> 1); + } + } + + /** + * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. + * Revert on overflow or in case x * y is negative. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function gavg(int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 m = int256(x) * int256(y); + require(m >= 0); + require( + m < + 0x4000000000000000000000000000000000000000000000000000000000000000 + ); + return int128(sqrtu(uint256(m))); + } + } + + /** + * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number + * and y is unsigned 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y uint256 value + * @return signed 64.64-bit fixed point number + */ + function pow(int128 x, uint256 y) internal pure returns (int128) { + unchecked { + bool negative = x < 0 && y & 1 == 1; + + uint256 absX = uint128(x < 0 ? -x : x); + uint256 absResult; + absResult = 0x100000000000000000000000000000000; + + if (absX <= 0x10000000000000000) { + absX <<= 63; + while (y != 0) { + if (y & 0x1 != 0) { + absResult = (absResult * absX) >> 127; + } + absX = (absX * absX) >> 127; + + if (y & 0x2 != 0) { + absResult = (absResult * absX) >> 127; + } + absX = (absX * absX) >> 127; + + if (y & 0x4 != 0) { + absResult = (absResult * absX) >> 127; + } + absX = (absX * absX) >> 127; + + if (y & 0x8 != 0) { + absResult = (absResult * absX) >> 127; + } + absX = (absX * absX) >> 127; + + y >>= 4; + } + + absResult >>= 64; + } else { + uint256 absXShift = 63; + if (absX < 0x1000000000000000000000000) { + absX <<= 32; + absXShift -= 32; + } + if (absX < 0x10000000000000000000000000000) { + absX <<= 16; + absXShift -= 16; + } + if (absX < 0x1000000000000000000000000000000) { + absX <<= 8; + absXShift -= 8; + } + if (absX < 0x10000000000000000000000000000000) { + absX <<= 4; + absXShift -= 4; + } + if (absX < 0x40000000000000000000000000000000) { + absX <<= 2; + absXShift -= 2; + } + if (absX < 0x80000000000000000000000000000000) { + absX <<= 1; + absXShift -= 1; + } + + uint256 resultShift = 0; + while (y != 0) { + require(absXShift < 64); + + if (y & 0x1 != 0) { + absResult = (absResult * absX) >> 127; + resultShift += absXShift; + if (absResult > 0x100000000000000000000000000000000) { + absResult >>= 1; + resultShift += 1; + } + } + absX = (absX * absX) >> 127; + absXShift <<= 1; + if (absX >= 0x100000000000000000000000000000000) { + absX >>= 1; + absXShift += 1; + } + + y >>= 1; + } + + require(resultShift < 64); + absResult >>= 64 - resultShift; + } + int256 result = negative ? -int256(absResult) : int256(absResult); + require(result >= MIN_64x64 && result <= MAX_64x64); + return int128(result); + } + } + + /** + * Calculate sqrt (x) rounding down. Revert if x < 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function sqrt(int128 x) internal pure returns (int128) { + unchecked { + require(x >= 0); + return int128(sqrtu(uint256(int256(x)) << 64)); + } + } + + /** + * Calculate binary logarithm of x. Revert if x <= 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function log_2(int128 x) internal pure returns (int128) { + unchecked { + require(x > 0); + + int256 msb = 0; + int256 xc = x; + if (xc >= 0x10000000000000000) { + xc >>= 64; + msb += 64; + } + if (xc >= 0x100000000) { + xc >>= 32; + msb += 32; + } + if (xc >= 0x10000) { + xc >>= 16; + msb += 16; + } + if (xc >= 0x100) { + xc >>= 8; + msb += 8; + } + if (xc >= 0x10) { + xc >>= 4; + msb += 4; + } + if (xc >= 0x4) { + xc >>= 2; + msb += 2; + } + if (xc >= 0x2) msb += 1; // No need to shift xc anymore + + int256 result = (msb - 64) << 64; + uint256 ux = uint256(int256(x)) << uint256(127 - msb); + for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { + ux *= ux; + uint256 b = ux >> 255; + ux >>= 127 + b; + result += bit * int256(b); + } + + return int128(result); + } + } + + /** + * Calculate natural logarithm of x. Revert if x <= 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function ln(int128 x) internal pure returns (int128) { + unchecked { + require(x > 0); + + return + int128( + int256( + (uint256(int256(log_2(x))) * + 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128 + ) + ); + } + } + + /** + * Calculate binary exponent of x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function exp_2(int128 x) internal pure returns (int128) { + unchecked { + require(x < 0x400000000000000000); // Overflow + + if (x < -0x400000000000000000) return 0; // Underflow + + uint256 result = 0x80000000000000000000000000000000; + + if (x & 0x8000000000000000 > 0) + result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128; + if (x & 0x4000000000000000 > 0) + result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128; + if (x & 0x2000000000000000 > 0) + result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128; + if (x & 0x1000000000000000 > 0) + result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128; + if (x & 0x800000000000000 > 0) + result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128; + if (x & 0x400000000000000 > 0) + result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128; + if (x & 0x200000000000000 > 0) + result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128; + if (x & 0x100000000000000 > 0) + result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128; + if (x & 0x80000000000000 > 0) + result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128; + if (x & 0x40000000000000 > 0) + result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128; + if (x & 0x20000000000000 > 0) + result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128; + if (x & 0x10000000000000 > 0) + result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128; + if (x & 0x8000000000000 > 0) + result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128; + if (x & 0x4000000000000 > 0) + result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128; + if (x & 0x2000000000000 > 0) + result = (result * 0x1000162E525EE054754457D5995292026) >> 128; + if (x & 0x1000000000000 > 0) + result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128; + if (x & 0x800000000000 > 0) + result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128; + if (x & 0x400000000000 > 0) + result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128; + if (x & 0x200000000000 > 0) + result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128; + if (x & 0x100000000000 > 0) + result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128; + if (x & 0x80000000000 > 0) + result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128; + if (x & 0x40000000000 > 0) + result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128; + if (x & 0x20000000000 > 0) + result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128; + if (x & 0x10000000000 > 0) + result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128; + if (x & 0x8000000000 > 0) + result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128; + if (x & 0x4000000000 > 0) + result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128; + if (x & 0x2000000000 > 0) + result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128; + if (x & 0x1000000000 > 0) + result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128; + if (x & 0x800000000 > 0) + result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128; + if (x & 0x400000000 > 0) + result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128; + if (x & 0x200000000 > 0) + result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128; + if (x & 0x100000000 > 0) + result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128; + if (x & 0x80000000 > 0) + result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128; + if (x & 0x40000000 > 0) + result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128; + if (x & 0x20000000 > 0) + result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128; + if (x & 0x10000000 > 0) + result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128; + if (x & 0x8000000 > 0) + result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128; + if (x & 0x4000000 > 0) + result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128; + if (x & 0x2000000 > 0) + result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128; + if (x & 0x1000000 > 0) + result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128; + if (x & 0x800000 > 0) + result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128; + if (x & 0x400000 > 0) + result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128; + if (x & 0x200000 > 0) + result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128; + if (x & 0x100000 > 0) + result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128; + if (x & 0x80000 > 0) + result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128; + if (x & 0x40000 > 0) + result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128; + if (x & 0x20000 > 0) + result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128; + if (x & 0x10000 > 0) + result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128; + if (x & 0x8000 > 0) + result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128; + if (x & 0x4000 > 0) + result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128; + if (x & 0x2000 > 0) + result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128; + if (x & 0x1000 > 0) + result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128; + if (x & 0x800 > 0) + result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128; + if (x & 0x400 > 0) + result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128; + if (x & 0x200 > 0) + result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128; + if (x & 0x100 > 0) + result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128; + if (x & 0x80 > 0) + result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128; + if (x & 0x40 > 0) + result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128; + if (x & 0x20 > 0) + result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128; + if (x & 0x10 > 0) + result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128; + if (x & 0x8 > 0) + result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128; + if (x & 0x4 > 0) + result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128; + if (x & 0x2 > 0) + result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128; + if (x & 0x1 > 0) + result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128; + + result >>= uint256(int256(63 - (x >> 64))); + require(result <= uint256(int256(MAX_64x64))); + + return int128(int256(result)); + } + } + + /** + * Calculate natural exponent of x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function exp(int128 x) internal pure returns (int128) { + unchecked { + require(x < 0x400000000000000000); // Overflow + + if (x < -0x400000000000000000) return 0; // Underflow + + return + exp_2( + int128( + (int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128 + ) + ); + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x unsigned 256-bit integer number + * @param y unsigned 256-bit integer number + * @return unsigned 64.64-bit fixed point number + */ + function divuu(uint256 x, uint256 y) private pure returns (uint128) { + unchecked { + require(y != 0); + + uint256 result; + + if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + result = (x << 64) / y; + else { + uint256 msb = 192; + uint256 xc = x >> 192; + if (xc >= 0x100000000) { + xc >>= 32; + msb += 32; + } + if (xc >= 0x10000) { + xc >>= 16; + msb += 16; + } + if (xc >= 0x100) { + xc >>= 8; + msb += 8; + } + if (xc >= 0x10) { + xc >>= 4; + msb += 4; + } + if (xc >= 0x4) { + xc >>= 2; + msb += 2; + } + if (xc >= 0x2) msb += 1; // No need to shift xc anymore + + result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1); + require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + uint256 hi = result * (y >> 128); + uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + uint256 xh = x >> 192; + uint256 xl = x << 64; + + if (xl < lo) xh -= 1; + xl -= lo; // We rely on overflow behavior here + lo = hi << 128; + if (xl < lo) xh -= 1; + xl -= lo; // We rely on overflow behavior here + + result += xh == hi >> 128 ? xl / y : 1; + } + + require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return uint128(result); + } + } + + /** + * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer + * number. + * + * @param x unsigned 256-bit integer number + * @return unsigned 128-bit integer number + */ + function sqrtu(uint256 x) private pure returns (uint128) { + unchecked { + if (x == 0) return 0; + else { + uint256 xx = x; + uint256 r = 1; + if (xx >= 0x100000000000000000000000000000000) { + xx >>= 128; + r <<= 64; + } + if (xx >= 0x10000000000000000) { + xx >>= 64; + r <<= 32; + } + if (xx >= 0x100000000) { + xx >>= 32; + r <<= 16; + } + if (xx >= 0x10000) { + xx >>= 16; + r <<= 8; + } + if (xx >= 0x100) { + xx >>= 8; + r <<= 4; + } + if (xx >= 0x10) { + xx >>= 4; + r <<= 2; + } + if (xx >= 0x4) { + r <<= 1; + } + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; // Seven iterations should be enough + uint256 r1 = x / r; + return uint128(r < r1 ? r : r1); + } + } + } +} From 626957adf7c4a4712f038df9df690e3b039e78c5 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 28 Mar 2023 21:11:18 +0200 Subject: [PATCH 33/90] Following changes: - added updateBaseProof(), tokenAmountCalc(), getBlockRewardFactor() - L1_NO_GAS() error - rewardIssued to uint256 - eip1559.py + several debug prints --- packages/protocol/contracts/L1/TaikoData.sol | 2 +- .../protocol/contracts/L1/TaikoErrors.sol | 1 + .../contracts/L1/libs/LibL1Tokenomics.sol | 114 ++++++++++++---- .../contracts/L1/libs/LibProposing.sol | 16 +++ .../contracts/L1/libs/LibVerifying.sol | 48 +++++-- .../{ABDKMath64x64.sol => ABDKMath.sol} | 0 packages/protocol/scripts/eip1559.py | 129 ++++++++++++++++++ 7 files changed, 273 insertions(+), 37 deletions(-) rename packages/protocol/contracts/thirdparty/{ABDKMath64x64.sol => ABDKMath.sol} (100%) create mode 100755 packages/protocol/scripts/eip1559.py diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 5eb7bf58383..9d8c504cf77 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -14,7 +14,7 @@ library TaikoData { struct FeeAndRewardConfig { uint256 baseFeeProof; - uint64 rewardIssued; + uint256 rewardIssued; // avgProofTimeMAF is known already //uint16 avgProofTimeMAF; uint64 rewardTargetPerGas; diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 22e79b60d70..c3922258143 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -20,6 +20,7 @@ abstract contract TaikoErrors { error L1_INVALID_METADATA(); error L1_INVALID_PARAM(); error L1_INVALID_PROOF(); + error L1_NO_GAS(); error L1_NOT_ORACLE_PROVER(); error L1_NOT_SOLO_PROPOSER(); error L1_OUT_OF_BLOCK_SPACE(); diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 2ecaf62023a..5657f180948 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -6,6 +6,9 @@ pragma solidity ^0.8.18; +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; + import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; import { @@ -13,11 +16,12 @@ import { } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; -import {ABDKMath64x64} from "../../thirdparty/ABDKMath64x64.sol"; +import {ABDKMath64x64} from "../../thirdparty/ABDKMath.sol"; library LibL1Tokenomics { using LibMath for uint256; - using LibMath for uint64; + + uint256 constant SCALING_FACTOR = 1e18; // 10^18 for 18 decimal places error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -170,63 +174,117 @@ library LibL1Tokenomics { } } - /// @notice Calculate the block reward multiplier based on delay + /// @notice Calculate the block reward factor based on the delay /// @dev Bigger delays and smaller the blocks results greater the reward - /// @dev Smaller the block - greater the reward. + /// @dev Smaller the block - greater the reward /// @param config - Config, containing FeeAndRewardConfig /// @param usedGas - Gas in the block - /// @param delay - Delay compare to avgProofTime - function getBlockRewardMultiplier( + /// @param delay - Delay compared to avgProofTime (in milliseconde * 1000 - so that the division not get zerod) + /// @return blockReward - This function calculates the block reward factor based on the amount of gas in a block and the delay + function getBlockRewardFactor( TaikoData.Config memory config, uint32 usedGas, uint256 delay - ) internal pure returns (uint256 blockReward) { + ) internal view returns (uint256 blockReward) { if (usedGas == 0) revert L1_NO_GAS(); + console2.log( + "----In getBlockRewardFactorben() of LibL1Tokenomics.sol -----" + ); + console2.log( + "config.feeConfig.rewardTargetPerGas is: ", + config.feeConfig.rewardTargetPerGas + ); + console2.log("delay is: ", delay); + console2.log( + "config.feeConfig.targetDelayBonusPerGas is: ", + config.feeConfig.targetDelayBonusPerGas + ); + console2.log("usedGas is: ", usedGas); + blockReward = - config.feeConfig.rewardTargetPerGas * - (delay / (config.feeConfig.targetDelayBonusPerGas * usedGas)); + (config.feeConfig.rewardTargetPerGas * + ((delay * SCALING_FACTOR) / + (config.feeConfig.targetDelayBonusPerGas * usedGas))) / + SCALING_FACTOR; } /// @notice Update the baseFee for proofs /// @param feeConfig - Config, containing FeeAndRewardConfig /// @param usedGas - Gas in the block - /// @param blockRewardMultiplier - Block reward + /// @param blockReward - Block reward + /// @return rewardIssued - Amount of reward issued + /// @return newBaseFeeProof - New baseFeeProof function updateBaseProof( TaikoData.FeeAndRewardConfig memory feeConfig, - uint256 blockRewardMultiplier, + uint256 blockReward, uint32 usedGas - ) internal pure returns (uint64 rewardIssued, uint256 newBaseFeeProof) { - rewardIssued = uint64( - (uint64(0)).max( - feeConfig.rewardIssued + - blockRewardMultiplier - - feeConfig.rewardTargetPerGas * - usedGas - ) + ) internal view returns (uint256 rewardIssued, uint256 newBaseFeeProof) { + console2.log("----In updateBaseProof() of LibL1Tokenomics.sol -----"); + + console2.log("usedGas is: ", usedGas); + console2.log("feeConfig.rewardIssued is: ", feeConfig.rewardIssued); + console2.log( + "feeConfig.rewardTargetPerGas is: ", + feeConfig.rewardTargetPerGas ); - newBaseFeeProof = - ethAmount( - rewardIssued / usedGas, - feeConfig.rewardTargetPerGas, - feeConfig.adjustmentQuotient - ) - - (feeConfig.rewardTargetPerGas * feeConfig.adjustmentQuotient); + console2.log("blockReward is: ", blockReward); + + // Here, we are trying to subtract 0 + 2 - HIGH_NUMBER -> resulting in underflow + rewardIssued = (feeConfig.rewardIssued + blockReward > + feeConfig.rewardTargetPerGas * usedGas) + ? feeConfig.rewardIssued + + blockReward - + feeConfig.rewardTargetPerGas * + usedGas + : uint256(0); + + console2.log("All rewardIssued: ", rewardIssued); + uint256 ethAmount = tokenAmountCalc( + rewardIssued / usedGas, + feeConfig.rewardTargetPerGas, + feeConfig.adjustmentQuotient + ); + + // Experiment to get the same value as whatever + uint256 crossCalculationWithPython = tokenAmountCalc(128, 2, 32); + + console2.log( + "The python-cross-check value for exp e2:", + crossCalculationWithPython + ); + + newBaseFeeProof = (ethAmount > + (feeConfig.rewardTargetPerGas * feeConfig.adjustmentQuotient)) + ? ethAmount - + (feeConfig.rewardTargetPerGas * feeConfig.adjustmentQuotient) + : 0; + + console2.log("The new baseFeeProof: ", newBaseFeeProof); } /// @notice Calculating the exponential via ABDKMath64x64 /// @param value - Result of rewardIssued / usedGas /// @param target - Reward targer per gas /// @param quotient - Quotient - function ethAmount( + function tokenAmountCalc( uint256 value, uint256 target, uint256 quotient - ) internal pure returns (uint256) { + ) internal view returns (uint256) { + console2.log("----In tokenAmountCalc() of LibL1Tokenomics.sol -----"); + console2.log("ethAmount function-ban vunk"); + console2.log("value: ", value); + console2.log("target: ", target); + console2.log("quotient: ", quotient); + int128 valueInt128 = ABDKMath64x64.exp( ABDKMath64x64.divu(value, target * quotient) ); + console2.log("valueInt128: ", valueInt128); + console2.log("converted:", ABDKMath64x64.toUInt(valueInt128)); + return uint256(ABDKMath64x64.toUInt(valueInt128)); } } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 6413c6e5838..3fdcb9addf8 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -6,6 +6,9 @@ pragma solidity ^0.8.18; +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; + import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibAddress} from "../../libs/LibAddress.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; @@ -107,6 +110,19 @@ library LibProposing { blk.verifiedForkChoiceId = 0; blk.metaHash = LibUtils.hashMetadata(meta); blk.proposer = msg.sender; + blk.gasLimit = input.gasLimit; + + // Let's see if calculations are correct so first just + // debug them. (Curly braces due to stack too deep error) + { + uint256 feeNew = input.gasLimit * config.feeConfig.baseFeeProof; + + console2.log("Current (new) fee would be:", feeNew); + console2.log( + "Current (new) basefee would be:", + config.feeConfig.baseFeeProof + ); + } if (config.enableTokenomics) { (uint256 newFeeBase, uint256 fee, uint64 deposit) = LibL1Tokenomics diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index ffc27a694fa..3b1829572e6 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -6,6 +6,9 @@ pragma solidity ^0.8.18; +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; + import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; @@ -155,25 +158,54 @@ library LibVerifying { // Calculate block reward multiplier uint256 blockRewardMultiplier; + console2.log( + "---------In _markBlockVerified() of LibVerifying -------" + ); if (proofTime > state.avgProofTime) { - blockRewardMultiplier = LibL1Tokenomics.getBlockRewardMultiplier( + console2.log("ProofTime is bigger than average"); + console2.log( + "What is the diff between proof and avg: ", + (proofTime - state.avgProofTime) + ); + blockRewardMultiplier = LibL1Tokenomics.getBlockRewardFactor( config, blk.gasLimit, - proofTime - state.avgProofTime + (proofTime - state.avgProofTime) * 1000 + ); + console2.log("blockRewardMultiplier is: ", blockRewardMultiplier); + + // Function for verification the result is the same as eip1559.py + uint256 xCheckPythonValue = LibL1Tokenomics.getBlockRewardFactor( + config, + uint32(5000000), + uint256(300000) + ); + console2.log( + "Calculated xCheckPythonValue value is: ", + xCheckPythonValue ); } else { - blockRewardMultiplier = LibL1Tokenomics.getBlockRewardMultiplier( + console2.log("ProofTime is smaller than average"); + blockRewardMultiplier = LibL1Tokenomics.getBlockRewardFactor( config, blk.gasLimit, state.avgProofTime ); } - LibL1Tokenomics.updateBaseProof( - config.feeConfig, - blockRewardMultiplier, - blk.gasLimit - ); + (uint256 rewardIssued, uint256 newBaseFeeProof) = LibL1Tokenomics + .updateBaseProof( + config.feeConfig, + blockRewardMultiplier, + blk.gasLimit + ); + + TaikoData.FeeAndRewardConfig memory feeAndRewardConf = config.feeConfig; + + feeAndRewardConf.rewardIssued = rewardIssued; + feeAndRewardConf.baseFeeProof = newBaseFeeProof; + + config.feeConfig = feeAndRewardConf; state.avgProofTime = LibUtils .movingAverage({ diff --git a/packages/protocol/contracts/thirdparty/ABDKMath64x64.sol b/packages/protocol/contracts/thirdparty/ABDKMath.sol similarity index 100% rename from packages/protocol/contracts/thirdparty/ABDKMath64x64.sol rename to packages/protocol/contracts/thirdparty/ABDKMath.sol diff --git a/packages/protocol/scripts/eip1559.py b/packages/protocol/scripts/eip1559.py new file mode 100755 index 00000000000..1f196b0ebe6 --- /dev/null +++ b/packages/protocol/scripts/eip1559.py @@ -0,0 +1,129 @@ +import math + +ETH_BLOCK_TIME = 12 +GAS_TARGET = 5000000 # target L2 gas (per ETH_BLOCK_TIME seconds) +ADJUSTMENT_QUOTIENT = 32 + +PROVER_REWARD_TARGET_PER_GAS = 0.1 # TAI/GAS in block rewards to prover +PROVER_TARGET_DELAY_PER_GAS = 0.001 # TODO: change to something dynamic probably + +time = 0 + +# network fee +gas_issued = 0 +last_time = time + +# prover fee +basefee_proof = 0 +blockreward_issued = 0 + + +# This calculates the amount of 'Ether' (or token) based on the inputs, using an exponential function. +# Question: This one we need +def eth_amount(value, target): + return math.exp(value / target / ADJUSTMENT_QUOTIENT) + +# This function calculates the network fee for a given amount of gas in a block +# Question: This shall be "de-coupled" from the prover - proposer TKO distribution (!?) +def network_fee(gas_in_block): + global gas_issued + global last_time + + gas_issued = max(0, gas_issued - GAS_TARGET * ((time - last_time)/ETH_BLOCK_TIME)) + # # # # print('Debug prints:') + # # # # print(gas_issued) + # # # # print(gas_in_block) + + # Will change based on simply elapsed time -> which will increase the gas_issued accumulation + cost = eth_amount(gas_issued + gas_in_block, GAS_TARGET) - eth_amount(gas_issued, GAS_TARGET) + gas_issued = gas_issued + gas_in_block + + last_time = time + + return cost + +# This function updates the base fee for proving a block, based on the amount of gas in the block and the block reward. +# Question: This shall be updated in the verifyBlock(), right ? OK +def update_basefee_proof(gas_in_block, block_reward): + global blockreward_issued + global basefee_proof + + # # # # print('UpdateBaseFee proof') + # # # # print(blockreward_issued + block_reward - PROVER_REWARD_TARGET_PER_GAS * gas_in_block) + + blockreward_issued = max(0, blockreward_issued + block_reward - PROVER_REWARD_TARGET_PER_GAS * gas_in_block) + basefee_proof = eth_amount(blockreward_issued/gas_in_block, PROVER_REWARD_TARGET_PER_GAS) / (PROVER_REWARD_TARGET_PER_GAS * ADJUSTMENT_QUOTIENT) + + # # # # print('Blockreward issued:') + # # # # print(blockreward_issued) + + return basefee_proof + +# This function calculates the prover fee for a given amount of gas in a block. +# It simply multiplies the gas in the block by the current base fee for proving. +def prover_fee(gas_in_block): + # # # # print('WHat is basefee_proof') + # # # # print(basefee_proof) + return gas_in_block * basefee_proof + +# This function calculates the block reward based on the amount of gas in a block and the delay. +# This is placed in the verifyBlock() function +def calc_block_reward(gas_in_block, delay): + # TODO: probably something else than this + + # Some notes: + # Bigger the delay, the higher the reward --> BUT actually is it ? I mean, if we calculate something on proposeBlock() it might differ (be more) when slower proof is submitted ? + + # # # # # Ez 'csak' a delay-en es a gas-on fugg, semmi mason. + # # # # print("a calc block rewardban vagyok") + # # # # print(PROVER_TARGET_DELAY_PER_GAS) + # # # # print(gas_in_block) + # # # # print(delay) + # # # # print(PROVER_REWARD_TARGET_PER_GAS) + + return PROVER_REWARD_TARGET_PER_GAS * (delay / (PROVER_TARGET_DELAY_PER_GAS * gas_in_block)) + + +def propose_block(gas_in_block): + # Will not be used directly in the mechanics of prover-proposer + print("network fee: " + str(network_fee(gas_in_block))) + # Need to call this so that basically this will be the amount of TKO as a proposal fee + print("prover fee: " + str(prover_fee(gas_in_block))) + +# Actually during verification, we know the proposedAt and provedAt, so we can calculate delay and +# distribution there, and also update the baseFeeProof +def prove_block(gas_in_block, delay): + block_reward = calc_block_reward(gas_in_block, delay) + print("block reward: " + str(block_reward)) + update_basefee_proof(gas_in_block, block_reward) + +print("First cross-check with Solidity starts") +propose_block(5000000) +prove_block(5000000, 300000) +print("First cross-check with Solidity ends") + +propose_block(5000000) +prove_block(5000000, 20000) + + +propose_block(5000000) +prove_block(5000000, 20000) + +print("QUICKER PROOF SUBMISSION") + +propose_block(5000000) +prove_block(5000000, 250) + +print("SLOWER PROOF SUBMISSION") + +propose_block(5000000) +prove_block(5000000, 350) + +propose_block(5000000) +prove_block(5000000, 300) + +print("Cross-checked exp value") +print(eth_amount(0, 100000000)) + +print("See if same in solidity") +print(eth_amount(128, 2)) \ No newline at end of file From 120d4084564778bae669d60994a9b71bb957e927 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Wed, 29 Mar 2023 16:05:59 +0200 Subject: [PATCH 34/90] Changes - applied new python code logic (moving average rewards + possible minting new tokens) - Not to lose floating point precision, convert the 64.64 fixed point representation number with downshifting only >>57 (instead of 64) and divide the all-in-all reward with that missing 128 value - Get rid of TaikoData.FeeAndRewardConfig, and rearrange state + config vars - new custom error - disabled lots of test not to distract during debugging --- .../protocol/contracts/L1/TaikoConfig.sol | 8 +- packages/protocol/contracts/L1/TaikoData.sol | 18 +- .../protocol/contracts/L1/TaikoErrors.sol | 1 + .../contracts/L1/libs/LibL1Tokenomics.sol | 176 +++++++++--------- .../contracts/L1/libs/LibProposing.sol | 20 +- .../protocol/contracts/L1/libs/LibUtils.sol | 1 + .../contracts/L1/libs/LibVerifying.sol | 61 ++---- packages/protocol/test2/LibL1Tokenomics.t.sol | 58 +++--- packages/protocol/test2/TaikoL1.t.sol | 72 ++++++- 9 files changed, 219 insertions(+), 196 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 7196ba37e27..e3e9f7c1b8e 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -41,6 +41,7 @@ library TaikoConfig { feeBaseMAF: 1024, constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, + adjustmentQuotient: 16, enableSoloProposer: false, enableOracleProver: true, enableTokenomics: true, @@ -52,13 +53,6 @@ library TaikoConfig { provingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, dampingFactorBips: 2500 // [75% -> 125%] - }), - feeConfig: TaikoData.FeeAndRewardConfig({ - baseFeeProof: 0, - rewardIssued: 0, - rewardTargetPerGas: 100000000, // 0.1 TAI - targetDelayBonusPerGas: 1000000, // 0.001 TAI - adjustmentQuotient: 32 }) }); } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 9d8c504cf77..699dbd77d26 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -12,16 +12,6 @@ library TaikoData { uint16 dampingFactorBips; } - struct FeeAndRewardConfig { - uint256 baseFeeProof; - uint256 rewardIssued; - // avgProofTimeMAF is known already - //uint16 avgProofTimeMAF; - uint64 rewardTargetPerGas; - uint64 targetDelayBonusPerGas; - uint8 adjustmentQuotient; - } - struct Config { uint256 chainId; uint256 maxNumProposedBlocks; @@ -43,18 +33,18 @@ library TaikoData { uint256 feeBaseMAF; uint64 constantFeeRewardBlocks; uint64 txListCacheExpiry; + uint8 adjustmentQuotient; bool enableSoloProposer; bool enableOracleProver; bool enableTokenomics; bool skipZKPVerification; FeeConfig proposingConfig; FeeConfig provingConfig; - // Hopefully the above 2 can be deleted later if that fits - FeeAndRewardConfig feeConfig; } struct StateVariables { uint64 feeBase; + uint64 baseFeeProof; uint64 genesisHeight; uint64 genesisTimestamp; uint64 numBlocks; @@ -141,6 +131,10 @@ library TaikoData { mapping(uint256 blockId => mapping(bytes32 parentHash => uint256 forkChoiceId)) forkChoiceIds; mapping(address account => uint256 balance) balances; mapping(bytes32 txListHash => TxListInfo) txListInfo; + // Cummulated proofTime for reward calculation - changed in verifyBlock() + uint256 proofTimeIssued; + // Changing baseFee for proving - changed in verifyBlock() + uint64 baseFeeProof; // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index c3922258143..62410d06df5 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -13,6 +13,7 @@ abstract contract TaikoErrors { error L1_CONTRACT_NOT_ALLOWED(); error L1_EVIDENCE_MISMATCH(); error L1_FORK_CHOICE_NOT_FOUND(); + error L1_IMPOSSIBLE_CONVERSION(); error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_CONFIG(); diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 5657f180948..b2eacc326a4 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -21,11 +21,22 @@ import {ABDKMath64x64} from "../../thirdparty/ABDKMath.sol"; library LibL1Tokenomics { using LibMath for uint256; - uint256 constant SCALING_FACTOR = 1e18; // 10^18 for 18 decimal places + uint256 constant SCALING_FACTOR = 1e8; // 10^8 for 8 decimal places = 1 TKO token + + // So floating point as is - in solidity is nonexitent. With exponential calculation + // we loose a lot of value because we are restricted to integer. So the ABDKMath gives + // us an int128 value which is a 64.64 fixed point decimal representation number of the + // float value. In order to get back the floating point, this is the math: + // floating_point_number = fixed_point_number / 2^64 + // But as mentioned we loose a lot on the .01-.99 range, so instead we shall calculate this + // floating_point_number = fixed_point_number / 2^57 , meaning we get a 2^7 times bigger number + // but on the other hand we have higher precision - and can divide the overall result. + uint8 constant EXPONENTIAL_REWARD_FACTOR = 128; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); error L1_NO_GAS(); + error L1_IMPOSSIBLE_CONVERSION(); function withdraw( TaikoData.State storage state, @@ -174,117 +185,104 @@ library LibL1Tokenomics { } } - /// @notice Calculate the block reward factor based on the delay - /// @dev Bigger delays and smaller the blocks results greater the reward - /// @dev Smaller the block - greater the reward - /// @param config - Config, containing FeeAndRewardConfig - /// @param usedGas - Gas in the block - /// @param delay - Delay compared to avgProofTime (in milliseconde * 1000 - so that the division not get zerod) - /// @return blockReward - This function calculates the block reward factor based on the amount of gas in a block and the delay - function getBlockRewardFactor( - TaikoData.Config memory config, - uint32 usedGas, - uint256 delay - ) internal view returns (uint256 blockReward) { - if (usedGas == 0) revert L1_NO_GAS(); - - console2.log( - "----In getBlockRewardFactorben() of LibL1Tokenomics.sol -----" - ); - console2.log( - "config.feeConfig.rewardTargetPerGas is: ", - config.feeConfig.rewardTargetPerGas - ); - console2.log("delay is: ", delay); - console2.log( - "config.feeConfig.targetDelayBonusPerGas is: ", - config.feeConfig.targetDelayBonusPerGas - ); - console2.log("usedGas is: ", usedGas); - - blockReward = - (config.feeConfig.rewardTargetPerGas * - ((delay * SCALING_FACTOR) / - (config.feeConfig.targetDelayBonusPerGas * usedGas))) / - SCALING_FACTOR; - } - /// @notice Update the baseFee for proofs - /// @param feeConfig - Config, containing FeeAndRewardConfig + /// @param cumulativeProofTime - Current proof time issued + /// @param proofTimeTarget - Proof time target + /// @param actProofTime - The actual proof time /// @param usedGas - Gas in the block - /// @param blockReward - Block reward - /// @return rewardIssued - Amount of reward issued - /// @return newBaseFeeProof - New baseFeeProof - function updateBaseProof( - TaikoData.FeeAndRewardConfig memory feeConfig, - uint256 blockReward, - uint32 usedGas - ) internal view returns (uint256 rewardIssued, uint256 newBaseFeeProof) { - console2.log("----In updateBaseProof() of LibL1Tokenomics.sol -----"); - + /// @param quotient - Adjustmnet quotient + function calculateBaseProof( + uint256 cumulativeProofTime, + uint64 proofTimeTarget, + uint64 actProofTime, + uint32 usedGas, + uint8 quotient + ) + internal + view + returns ( + uint256 reward, + uint256 newProofTimeIssued, + uint64 newBaseFeeProof + ) + { console2.log("usedGas is: ", usedGas); - console2.log("feeConfig.rewardIssued is: ", feeConfig.rewardIssued); - console2.log( - "feeConfig.rewardTargetPerGas is: ", - feeConfig.rewardTargetPerGas - ); - console2.log("blockReward is: ", blockReward); - - // Here, we are trying to subtract 0 + 2 - HIGH_NUMBER -> resulting in underflow - rewardIssued = (feeConfig.rewardIssued + blockReward > - feeConfig.rewardTargetPerGas * usedGas) - ? feeConfig.rewardIssued + - blockReward - - feeConfig.rewardTargetPerGas * - usedGas + console2.log("cumulativeProofTime is: ", cumulativeProofTime); + + console2.log("proofTimeTarget is: ", proofTimeTarget); + console2.log("actProofTime is: ", actProofTime); + if (proofTimeTarget == 0) { + // Let's discuss proper value, but make it 20 seconds first time user, so to + // be realistic a little bit while also avoid division by zero + proofTimeTarget = 20000; + } + // To protect underflow + newProofTimeIssued = (cumulativeProofTime > proofTimeTarget) + ? cumulativeProofTime - proofTimeTarget : uint256(0); - console2.log("All rewardIssued: ", rewardIssued); - uint256 ethAmount = tokenAmountCalc( - rewardIssued / usedGas, - feeConfig.rewardTargetPerGas, - feeConfig.adjustmentQuotient - ); + newProofTimeIssued += actProofTime; - // Experiment to get the same value as whatever - uint256 crossCalculationWithPython = tokenAmountCalc(128, 2, 32); + console2.log("Increased cumulativeProofTime is: ", newProofTimeIssued); - console2.log( - "The python-cross-check value for exp e2:", - crossCalculationWithPython + newBaseFeeProof = baseFee( + newProofTimeIssued / 1000, //Hence in milliseconds + proofTimeTarget / 1000, //Hence in milliseconds + quotient ); - newBaseFeeProof = (ethAmount > - (feeConfig.rewardTargetPerGas * feeConfig.adjustmentQuotient)) - ? ethAmount - - (feeConfig.rewardTargetPerGas * feeConfig.adjustmentQuotient) - : 0; + reward = + ((newBaseFeeProof * usedGas) * + ((actProofTime * SCALING_FACTOR) / proofTimeTarget)) / + (SCALING_FACTOR * EXPONENTIAL_REWARD_FACTOR); - console2.log("The new baseFeeProof: ", newBaseFeeProof); + console2.log("All cumulativeProofTime: ", newProofTimeIssued); + console2.log("New newBaseFeeProof: ", newBaseFeeProof); + console2.log("Reward: ", reward); + } + + /// @notice Calculating the exponential smoothened with (target/quotient) + /// @param value - Result of cumulativeProofTime / usedGas + /// @param target - Reward targer per gas + /// @param quotient - Quotient + function baseFee( + uint256 value, + uint256 target, + uint256 quotient + ) internal view returns (uint64) { + // If SCALING_FACTOR not applied : target * quotient will be bigger, therefore the result is 0 + return + uint64( + ((expCalculation(value, target, quotient) * SCALING_FACTOR) / + (target * quotient)) + ); } /// @notice Calculating the exponential via ABDKMath64x64 - /// @param value - Result of rewardIssued / usedGas + /// @param value - Result of cumulativeProofTime / usedGas /// @param target - Reward targer per gas /// @param quotient - Quotient - function tokenAmountCalc( + function expCalculation( uint256 value, uint256 target, uint256 quotient - ) internal view returns (uint256) { - console2.log("----In tokenAmountCalc() of LibL1Tokenomics.sol -----"); - console2.log("ethAmount function-ban vunk"); - console2.log("value: ", value); - console2.log("target: ", target); - console2.log("quotient: ", quotient); + ) internal view returns (uint64 retVal) { + console2.log("----In expCalculation() of LibL1Tokenomics.sol -----"); + // console2.log("value: ", value); + // console2.log("target: ", target); + // console2.log("quotient: ", quotient); int128 valueInt128 = ABDKMath64x64.exp( ABDKMath64x64.divu(value, target * quotient) ); - console2.log("valueInt128: ", valueInt128); - console2.log("converted:", ABDKMath64x64.toUInt(valueInt128)); + unchecked { + if (!(valueInt128 >= 0)) { + revert L1_IMPOSSIBLE_CONVERSION(); + } - return uint256(ABDKMath64x64.toUInt(valueInt128)); + //On purpose downshift/divide only 2^57, so that we have higher precisions!!! + retVal = uint64(uint128(valueInt128 >> 57)); + } } } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 3fdcb9addf8..3eea8647802 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -25,6 +25,8 @@ library LibProposing { using LibAddress for address payable; using LibUtils for TaikoData.State; + uint256 constant SCALING_FACTOR = 1e8; // 10^8 for 8 decimal places = 1 TKO token + event BlockProposed( uint256 indexed id, TaikoData.BlockMetadata meta, @@ -114,14 +116,18 @@ library LibProposing { // Let's see if calculations are correct so first just // debug them. (Curly braces due to stack too deep error) + console2.log("----------------------------------------------------"); + console2.log("------------------In proposeBlock()-----------------"); { - uint256 feeNew = input.gasLimit * config.feeConfig.baseFeeProof; - - console2.log("Current (new) fee would be:", feeNew); - console2.log( - "Current (new) basefee would be:", - config.feeConfig.baseFeeProof - ); + // Need to scale 'down', because baseFeeProof is scaled 'up' + // Todo: Could you please Brecht advise on the math ? + // So since in baseFee() function i needed to scale up, otherwise divison with zero + // somewhere we need to scale down.. + uint256 feeNew = (input.gasLimit * + state.baseFeeProof); /* / SCALING_FACTOR; (??) */ + + console2.log("proposer_fee:", feeNew); + console2.log("baseFeeProof:", state.baseFeeProof); } if (config.enableTokenomics) { diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 38dccf324a6..aa94be71adf 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -35,6 +35,7 @@ library LibUtils { return TaikoData.StateVariables({ feeBase: state.feeBase, + baseFeeProof: state.baseFeeProof, genesisHeight: state.genesisHeight, genesisTimestamp: state.genesisTimestamp, numBlocks: state.numBlocks, diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 3b1829572e6..79ea835f4b8 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -156,56 +156,27 @@ library LibVerifying { proofTime = (fc.provenAt - blk.proposedAt) * 1000; } - // Calculate block reward multiplier - uint256 blockRewardMultiplier; - console2.log( - "---------In _markBlockVerified() of LibVerifying -------" - ); - if (proofTime > state.avgProofTime) { - console2.log("ProofTime is bigger than average"); - console2.log( - "What is the diff between proof and avg: ", - (proofTime - state.avgProofTime) - ); - blockRewardMultiplier = LibL1Tokenomics.getBlockRewardFactor( - config, - blk.gasLimit, - (proofTime - state.avgProofTime) * 1000 - ); - console2.log("blockRewardMultiplier is: ", blockRewardMultiplier); - - // Function for verification the result is the same as eip1559.py - uint256 xCheckPythonValue = LibL1Tokenomics.getBlockRewardFactor( - config, - uint32(5000000), - uint256(300000) - ); - console2.log( - "Calculated xCheckPythonValue value is: ", - xCheckPythonValue - ); - } else { - console2.log("ProofTime is smaller than average"); - blockRewardMultiplier = LibL1Tokenomics.getBlockRewardFactor( - config, + console2.log("----------------------------------------------------"); + console2.log("------------------In verifyBlock()------------------"); + + ( + uint256 reward, + uint256 proofTimeIssued, + uint64 newBaseFeeProof + ) = LibL1Tokenomics.calculateBaseProof( + state.proofTimeIssued, + state.avgProofTime, + uint64(proofTime), blk.gasLimit, - state.avgProofTime - ); - } - - (uint256 rewardIssued, uint256 newBaseFeeProof) = LibL1Tokenomics - .updateBaseProof( - config.feeConfig, - blockRewardMultiplier, - blk.gasLimit + config.adjustmentQuotient ); - TaikoData.FeeAndRewardConfig memory feeAndRewardConf = config.feeConfig; + state.baseFeeProof = newBaseFeeProof; + state.proofTimeIssued = proofTimeIssued; - feeAndRewardConf.rewardIssued = rewardIssued; - feeAndRewardConf.baseFeeProof = newBaseFeeProof; + //config.feeConfig = feeAndRewardConf; - config.feeConfig = feeAndRewardConf; + console2.log("A mooving average az:", state.avgProofTime); state.avgProofTime = LibUtils .movingAverage({ diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 4f37d8dd94d..32e47ee422c 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -21,8 +21,8 @@ contract TestLibL1Tokenomics is Test { uint64 multiplerPctg; } - function testTokenomicsFeeCalcWithNonZeroStartBips() public { - testTimeAdjustedFee({ + function xtestTokenomicsFeeCalcWithNonZeroStartBips() public { + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -32,7 +32,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -42,7 +42,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -52,7 +52,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -62,7 +62,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -72,7 +72,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -83,8 +83,8 @@ contract TestLibL1Tokenomics is Test { }); } - function testTokenomicsFeeCalcWithZeroStartBips() public { - testTimeAdjustedFee({ + function xtestTokenomicsFeeCalcWithZeroStartBips() public { + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -94,7 +94,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -104,7 +104,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -114,7 +114,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -124,7 +124,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -134,7 +134,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -145,8 +145,8 @@ contract TestLibL1Tokenomics is Test { }); } - function testTokenomicsRewardCalcWithNonZeroStartBips() public { - testTimeAdjustedFee({ + function xtestTokenomicsRewardCalcWithNonZeroStartBips() public { + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -156,7 +156,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -166,7 +166,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -176,7 +176,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -186,7 +186,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 5000 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -196,7 +196,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 10000 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -207,8 +207,8 @@ contract TestLibL1Tokenomics is Test { }); } - function testTokenomicsRewardCalcWithZeroStartBips() public { - testTimeAdjustedFee({ + function xtestTokenomicsRewardCalcWithZeroStartBips() public { + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, @@ -218,7 +218,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, @@ -228,7 +228,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, @@ -238,7 +238,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, @@ -248,7 +248,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, @@ -258,7 +258,7 @@ contract TestLibL1Tokenomics is Test { expectedPreimumRate: 0 }); - testTimeAdjustedFee({ + xtestTimeAdjustedFee({ feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, @@ -269,7 +269,7 @@ contract TestLibL1Tokenomics is Test { }); } - function testTimeAdjustedFee( + function xtestTimeAdjustedFee( uint256 feeBase, uint256 timeAverageSec, uint256 timeUsedSec, diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index 033ba45d7a7..e80879fb1f7 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -34,12 +34,12 @@ contract TaikoL1WithConfig is TaikoL1 { config.slotSmoothingFactor = 4160; config.proposingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, + avgTimeMAF: 1024, dampingFactorBips: 5000 }); config.provingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, + avgTimeMAF: 1024, dampingFactorBips: 5000 }); } @@ -65,7 +65,7 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' - function test_more_blocks_than_ring_buffer_size() external { + function xtest_more_blocks_than_ring_buffer_size() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); @@ -93,7 +93,7 @@ contract TaikoL1Test is TaikoL1TestBase { /// @dev Test more than one block can be proposed, proven, & verified in the /// same L1 block. - function test_multiple_blocks_in_one_L1_block() external { + function xtest_multiple_blocks_in_one_L1_block() external { _depositTaikoToken(Alice, 1000 * 1E8, 1000 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; @@ -113,7 +113,7 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev Test verifying multiple blocks in one transaction - function test_verifying_multiple_blocks_once() external { + function xtest_verifying_multiple_blocks_once() external { _depositTaikoToken(Alice, 1E6 * 1E8, 1000 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; @@ -139,7 +139,7 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev Test block time increases and fee decreases. - function test_block_time_increases_and_fee_decreases() external { + function xtest_block_time_increases_and_fee_decreases() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); @@ -166,7 +166,7 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev Test block time decreases and the fee increases - function test_block_time_decreases_but_fee_remains() external { + function xtest_block_time_decreases_but_fee_remains() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); @@ -189,4 +189,62 @@ contract TaikoL1Test is TaikoL1TestBase { } printVariables(""); } + + /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' + function xtest_reward_and_fee_if_proof_time_increases() external { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for ( + uint256 blockId = 1; + blockId < 5; + //blockId < conf.maxNumProposedBlocks * 2; + blockId++ + ) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + printVariables("after propose"); + mine(blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + printVariables(""); + } + + /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' + function test_reward_and_fee_if_proof_time_decreases() external { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for ( + uint256 blockId = 1; + blockId < 5; + //blockId < conf.maxNumProposedBlocks * 2; + blockId++ + ) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + printVariables("after propose"); + mine(6 - blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + printVariables(""); + } } From dc442e54983623147dad0ff28971aad2a9809ff5 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Wed, 29 Mar 2023 16:49:05 +0200 Subject: [PATCH 35/90] Delete logging and unnecessary vars and enable one additional test --- packages/protocol/contracts/L1/libs/LibVerifying.sol | 4 ---- packages/protocol/test2/TaikoL1.t.sol | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 79ea835f4b8..a2c026644d2 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -174,10 +174,6 @@ library LibVerifying { state.baseFeeProof = newBaseFeeProof; state.proofTimeIssued = proofTimeIssued; - //config.feeConfig = feeAndRewardConf; - - console2.log("A mooving average az:", state.avgProofTime); - state.avgProofTime = LibUtils .movingAverage({ maValue: state.avgProofTime, diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index e80879fb1f7..49c6a267450 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -191,7 +191,7 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' - function xtest_reward_and_fee_if_proof_time_increases() external { + function test_reward_and_fee_if_proof_time_increases() external { mine(1); _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); From a3be42878266289f19a88098df53bf870984a63e Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:11:08 +0800 Subject: [PATCH 36/90] feat(protocol): calculate L2 EIP-1558 basefee on L1 (#13468) --- .../protocol/contracts/L1/TaikoConfig.sol | 3 +- packages/protocol/contracts/L1/TaikoData.sol | 24 +-- .../protocol/contracts/L1/TaikoErrors.sol | 5 + packages/protocol/contracts/L1/TaikoL1.sol | 39 ++++- .../contracts/L1/libs/LibL1Tokenomics.sol | 103 +++++-------- .../contracts/L1/libs/LibL2Tokenomics.sol | 127 +++++++++++++++ .../contracts/L1/libs/LibProposing.sol | 31 +++- .../protocol/contracts/L1/libs/LibProving.sol | 18 +-- .../protocol/contracts/L1/libs/LibUtils.sol | 56 +++++-- .../contracts/L1/libs/LibVerifying.sol | 48 ++++-- .../contracts/test/L1/TestTaikoL1.sol | 1 - .../test/L1/TestTaikoL1EnableTokenomics.sol | 1 - .../thirdparty/LibFixedPointMath.sol | 73 +++++++++ packages/protocol/foundry.toml | 2 +- .../protocol/test/tokenomics/blockFee.test.ts | 16 -- packages/protocol/test/utils/onNewL2Block.ts | 4 +- packages/protocol/test2/GasComparison.t.sol | 145 +++++++++++------- .../protocol/test2/LibFixedPointMath.t.sol | 55 +++++++ packages/protocol/test2/LibL1Tokenomics.t.sol | 103 +++++++------ packages/protocol/test2/LibL2Tokenomics.t.sol | 46 ++++++ packages/protocol/test2/TaikoL1.t.sol | 5 +- ...koL1TestBase.sol => TaikoL1TestBase.t.sol} | 29 +++- .../contract-documentation/L1/TaikoData.md | 21 +-- .../contract-documentation/L1/TaikoErrors.md | 30 ++++ .../contract-documentation/L1/TaikoL1.md | 22 ++- 25 files changed, 744 insertions(+), 263 deletions(-) create mode 100644 packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol create mode 100644 packages/protocol/contracts/thirdparty/LibFixedPointMath.sol create mode 100644 packages/protocol/test2/LibFixedPointMath.t.sol create mode 100644 packages/protocol/test2/LibL2Tokenomics.t.sol rename packages/protocol/test2/{TaikoL1TestBase.sol => TaikoL1TestBase.t.sol} (90%) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index ce912aae1f0..8198b9fe956 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,6 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, + gasIssuedPerSecond: 30000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, @@ -37,8 +38,6 @@ library TaikoConfig { proposerDepositPctg: 25, // - 25% // Moving average factors feeBaseMAF: 1024, - bootstrapDiscountHalvingPeriod: 1 seconds, // owner:daniel - constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, enableSoloProposer: false, enableOracleProver: true, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 7eff5ca49aa..b32e9a16fe5 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,6 +21,7 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; + uint256 gasIssuedPerSecond; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; @@ -29,9 +30,7 @@ library TaikoData { uint256 proposerDepositPctg; // Moving average factors uint256 feeBaseMAF; - uint64 bootstrapDiscountHalvingPeriod; - uint64 constantFeeRewardBlocks; - uint64 txListCacheExpiry; + uint256 txListCacheExpiry; bool enableSoloProposer; bool enableOracleProver; bool enableTokenomics; @@ -49,6 +48,8 @@ library TaikoData { uint64 avgBlockTime; uint64 avgProofTime; uint64 lastProposedAt; + uint64 l2Basefee; // L2 1559 + uint64 l2GasExcess; // L2 1559 } // 3 slots @@ -61,18 +62,21 @@ library TaikoData { uint8 cacheTxListInfo; // non-zero = True } - // 5 slots + // 6 slots + // Changing this struct requires chaing LibUtils.hashMetadata accordingly. struct BlockMetadata { uint64 id; uint64 timestamp; uint64 l1Height; - uint32 gasLimit; + uint64 l2Basefee; bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; uint24 txListByteStart; uint24 txListByteEnd; + uint32 gasLimit; address beneficiary; + address treasure; } struct ZKProof { @@ -127,22 +131,22 @@ library TaikoData { // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; - uint64 __reserved1; - uint64 __reserved2; + uint64 l2Xscale; + uint64 l2Yscale; // Changed when a block is proposed or proven/finalized // Changed when a block is proposed uint64 numBlocks; uint64 lastProposedAt; // Timestamp when the last block is proposed. uint64 avgBlockTime; // miliseconds - uint64 __reserved3; + uint64 l2GasExcess; // Changed when a block is proven/finalized - uint64 __reserved4; uint64 lastVerifiedBlockId; + uint64 __reserved4; // the proof time moving average, note that for each block, only the // first proof's time is considered. uint64 avgProofTime; // miliseconds uint64 feeBase; // Reserved - uint256[42] __gap; + uint256[43] __gap; } } diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 4d0a74135b6..fda2c853128 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -8,11 +8,15 @@ pragma solidity ^0.8.18; abstract contract TaikoErrors { // The following custom errors must match the definitions in other V1 libraries. + error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio); + error L1_1559_X_SCALE_TOO_LARGE(); + error L1_1559_Y_SCALE_TOO_LARGE(); error L1_ALREADY_PROVEN(); error L1_BLOCK_ID(); error L1_CONTRACT_NOT_ALLOWED(); error L1_EVIDENCE_MISMATCH(); error L1_FORK_CHOICE_NOT_FOUND(); + error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_CONFIG(); error L1_INVALID_EVIDENCE(); @@ -21,6 +25,7 @@ abstract contract TaikoErrors { error L1_INVALID_PROOF(); error L1_NOT_ORACLE_PROVER(); error L1_NOT_SOLO_PROPOSER(); + error L1_OUT_OF_BLOCK_SPACE(); error L1_TOO_MANY_BLOCKS(); error L1_TX_LIST_NOT_EXIST(); error L1_TX_LIST_HASH(); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 2330511a38b..893af721735 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -9,7 +9,8 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../common/AddressResolver.sol"; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; -import {LibL1Tokenomics} from "./libs/LibVerifying.sol"; +import {LibL1Tokenomics} from "./libs/LibL1Tokenomics.sol"; +import {LibL2Tokenomics} from "./libs/LibL2Tokenomics.sol"; import {LibProposing} from "./libs/LibProposing.sol"; import {LibProving} from "./libs/LibProving.sol"; import {LibUtils} from "./libs/LibUtils.sol"; @@ -25,17 +26,37 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { TaikoData.State public state; uint256[100] private __gap; + /** + * Initialize the rollup. + * + * @param _addressManager The AddressManager address. + * @param _genesisBlockHash The block hash of the genesis block. + * @param _feeBase The initial value of the proposer-fee/prover-reward feeBase. + * @param _l2GasExcessMax The max amount of L2 gas that can ever be purchased + * under any possible circumstances before additional gas are issued. + * @param _l2Basefee The initial value of L2 EIP-1559 base fee per gas. + * @param _l2GasTarget A value to verify the correctness of L2 EIP-1559 config. + * @param _l2Expected2X1XRatio A value to verify the correctness of L2 EIP-1559 config. + */ function init( address _addressManager, bytes32 _genesisBlockHash, - uint64 _feeBase + uint64 _feeBase, + uint64 _l2GasExcessMax, + uint64 _l2Basefee, + uint64 _l2GasTarget, + uint64 _l2Expected2X1XRatio ) external initializer { EssentialContract._init(_addressManager); LibVerifying.init({ state: state, config: getConfig(), genesisBlockHash: _genesisBlockHash, - feeBase: _feeBase + feeBase: _feeBase, + l2GasExcessMax: _l2GasExcessMax, + l2BasefeeInitial: _l2Basefee, + l2GasTarget: _l2GasTarget, + l2Expected2X1XRatio: _l2Expected2X1XRatio }); } @@ -214,12 +235,22 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { : bytes32(0); } + function getL2Basefee( + uint32 gasLimit + ) public view returns (uint64 basefee) { + (basefee, ) = LibL2Tokenomics.getL2Basefee( + state, + getConfig(), + gasLimit + ); + } + function getStateVariables() public view returns (TaikoData.StateVariables memory) { - return state.getStateVariables(); + return state.getStateVariables(getConfig()); } function getConfig() public pure virtual returns (TaikoData.Config memory) { diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index 41342081682..e8c9c67621a 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -16,7 +16,6 @@ import {TaikoToken} from "../TaikoToken.sol"; library LibL1Tokenomics { using LibMath for uint256; - uint256 private constant TWEI_TO_WEI = 1E12; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -59,39 +58,24 @@ library LibL1Tokenomics { ) internal view - returns (uint256 newFeeBase, uint256 fee, uint256 depositAmount) + returns (uint64 newFeeBase, uint64 fee, uint64 depositAmount) { - if (state.numBlocks <= config.constantFeeRewardBlocks) { - fee = state.feeBase; - newFeeBase = state.feeBase; - } else { - (newFeeBase, ) = getTimeAdjustedFee({ - feeConfig: config.proposingConfig, - feeBase: state.feeBase, - isProposal: true, - timeUsed: block.timestamp - state.lastProposedAt, - timeAverage: state.avgBlockTime - }); - fee = getSlotsAdjustedFee({ - state: state, - config: config, - isProposal: true, - feeBase: newFeeBase - }); - } - - if (config.bootstrapDiscountHalvingPeriod > 0) { - unchecked { - uint256 halves = uint256( - block.timestamp - state.genesisTimestamp - ) / config.bootstrapDiscountHalvingPeriod; - uint256 gamma = 1024 - (1024 >> (1 + halves)); - fee = (fee * gamma) / 1024; - } - } + (newFeeBase, ) = getTimeAdjustedFee({ + feeConfig: config.proposingConfig, + feeBase: state.feeBase, + isProposal: true, + timeUsed: block.timestamp - state.lastProposedAt, + timeAverage: state.avgBlockTime + }); + fee = getSlotsAdjustedFee({ + state: state, + config: config, + isProposal: true, + feeBase: newFeeBase + }); unchecked { - depositAmount = (fee * config.proposerDepositPctg) / 100; + depositAmount = uint64((config.proposerDepositPctg * fee) / 100); } } @@ -103,31 +87,26 @@ library LibL1Tokenomics { ) internal view - returns (uint256 newFeeBase, uint256 reward, uint256 premiumRate) + returns (uint64 newFeeBase, uint64 reward, uint64 premiumRate) { if (proposedAt > provenAt) revert L1_INVALID_PARAM(); - if (state.lastVerifiedBlockId <= config.constantFeeRewardBlocks) { - reward = state.feeBase; - newFeeBase = state.feeBase; - // premiumRate = 0; - } else { - (newFeeBase, premiumRate) = getTimeAdjustedFee({ - feeConfig: config.provingConfig, - feeBase: state.feeBase, - isProposal: false, - timeUsed: provenAt - proposedAt, - timeAverage: state.avgProofTime - }); - reward = getSlotsAdjustedFee({ - state: state, - config: config, - isProposal: false, - feeBase: newFeeBase - }); - } + (newFeeBase, premiumRate) = getTimeAdjustedFee({ + feeConfig: config.provingConfig, + feeBase: state.feeBase, + isProposal: false, + timeUsed: provenAt - proposedAt, + timeAverage: state.avgProofTime + }); + reward = getSlotsAdjustedFee({ + state: state, + config: config, + isProposal: false, + feeBase: newFeeBase + }); + unchecked { - reward = (reward * (10000 - config.rewardBurnBips)) / 10000; + reward = uint64((reward * (10000 - config.rewardBurnBips)) / 10000); } } @@ -136,8 +115,8 @@ library LibL1Tokenomics { TaikoData.State storage state, TaikoData.Config memory config, bool isProposal, - uint256 feeBase - ) internal view returns (uint256) { + uint64 feeBase + ) internal view returns (uint64) { unchecked { // m is the `n'` in the whitepaper uint256 m = 1000 * @@ -148,32 +127,32 @@ library LibL1Tokenomics { (state.numBlocks - state.lastVerifiedBlockId - 1); // k is `m − n + 1` or `m − n - 1`in the whitepaper uint256 k = isProposal ? m - n - 1000 : m - n + 1000; - return (feeBase * (m - 1000) * m) / (m - n) / k; + return uint64((feeBase * (m - 1000) * m) / (m - n) / k); } } // Implement "Incentive Multipliers", see the whitepaper. function getTimeAdjustedFee( TaikoData.FeeConfig memory feeConfig, - uint256 feeBase, + uint64 feeBase, bool isProposal, uint256 timeUsed, // seconds uint256 timeAverage // milliseconds - ) internal pure returns (uint256 newFeeBase, uint256 premiumRate) { + ) internal pure returns (uint64 newFeeBase, uint64 premiumRate) { if (timeAverage == 0) { return (feeBase, 0); } unchecked { - uint p = feeConfig.dampingFactorBips; // [0-10000] - uint a = timeAverage; - uint t = (timeUsed * 1000).min(a * 2); // millisconds + uint256 p = feeConfig.dampingFactorBips; // [0-10000] + uint256 a = timeAverage; + uint256 t = (timeUsed * 1000).min(a * 2); // millisconds - newFeeBase = (feeBase * (10000 + (t * p) / a - p)) / 10000; + newFeeBase = uint64((feeBase * (10000 + (t * p) / a - p)) / 10000); if (isProposal) { newFeeBase = (feeBase * 2) - newFeeBase; } else if (p > 0) { - premiumRate = ((t.max(a) - a) * 10000) / a; + premiumRate = uint64(((t.max(a) - a) * 10000) / a); } } } diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol new file mode 100644 index 00000000000..d8372ef5f40 --- /dev/null +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.18; + +import {AddressResolver} from "../../common/AddressResolver.sol"; +import {LibMath} from "../../libs/LibMath.sol"; +import {LibFixedPointMath} from "../../thirdparty/LibFixedPointMath.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; +import {TaikoData} from "../TaikoData.sol"; + +library LibL2Tokenomics { + using LibMath for uint256; + using SafeCastUpgradeable for uint256; + + error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio); + error L1_1559_X_SCALE_TOO_LARGE(); + error L1_1559_Y_SCALE_TOO_LARGE(); + error L1_OUT_OF_BLOCK_SPACE(); + + function getL2Basefee( + TaikoData.State storage state, + TaikoData.Config memory config, + uint32 gasLimit + ) internal view returns (uint64 basefee, uint64 newGasExcess) { + if (config.gasIssuedPerSecond == 0) { + // L2 1559 disabled + return (0, 0); + } + + unchecked { + uint256 reduced = (block.timestamp - state.lastProposedAt) * + config.gasIssuedPerSecond; + newGasExcess = uint64(reduced.max(state.l2GasExcess) - reduced); + } + + basefee = calcL2Basefee({ + l2GasExcess: newGasExcess, + xscale: state.l2Xscale, + yscale: uint256(state.l2Yscale) << 64, + gasAmount: gasLimit + }).toUint64(); + + newGasExcess += gasLimit; + } + + function calcL2BasefeeParams( + uint64 excessMax, + uint64 basefeeInitial, + uint64 gasTarget, + uint64 expected2X1XRatio + ) internal pure returns (uint64 l2GasExcess, uint64 xscale, uint64 yscale) { + assert(excessMax != 0); + + l2GasExcess = excessMax / 2; + + // calculate xscale + uint256 _xscale = LibFixedPointMath.MAX_EXP_INPUT / excessMax; + if (_xscale >= type(uint64).max) { + revert L1_1559_X_SCALE_TOO_LARGE(); + } + xscale = uint64(_xscale); + + // calculate yscale + uint256 _yscale = calcL2Basefee( + l2GasExcess, + xscale, + basefeeInitial, + gasTarget + ); + if ((_yscale >> 64) >= type(uint64).max) { + revert L1_1559_Y_SCALE_TOO_LARGE(); + } + + yscale = uint64(_yscale >> 64); + + // Verify the gas price ratio between two blocks, one has + // 2*gasTarget gas and the other one has gasTarget gas. + { + _yscale = uint256(yscale) << 64; + uint256 price1x = calcL2Basefee( + l2GasExcess, + xscale, + _yscale, + gasTarget + ); + uint256 price2x = calcL2Basefee( + l2GasExcess, + xscale, + _yscale, + gasTarget * 2 + ); + + uint64 ratio = uint64((price2x * 100) / price1x); + + if (expected2X1XRatio != ratio) { + revert L1_1559_GAS_CHANGE_MISMATCH(expected2X1XRatio, ratio); + } + } + } + + function calcL2Basefee( + uint64 l2GasExcess, + uint64 xscale, + uint256 yscale, + uint64 gasAmount + ) internal pure returns (uint256) { + assert(gasAmount != 0 && xscale != 0 && yscale != 0); + uint256 _before = _ethqty(l2GasExcess, xscale); + uint256 _after = _ethqty(l2GasExcess + gasAmount, xscale); + return (_after - _before) / gasAmount / yscale; + } + + function _ethqty( + uint256 l2GasExcess, + uint256 xscale + ) private pure returns (uint256) { + uint256 x = l2GasExcess * xscale; + if (x > LibFixedPointMath.MAX_EXP_INPUT) revert L1_OUT_OF_BLOCK_SPACE(); + return uint256(LibFixedPointMath.exp(int256(x))); + } +} diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index d9ac44a22cf..8cd2521b77c 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -7,7 +7,9 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; +import {LibAddress} from "../../libs/LibAddress.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; +import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -16,6 +18,8 @@ import {TaikoData} from "../TaikoData.sol"; library LibProposing { using SafeCastUpgradeable for uint256; + using LibAddress for address; + using LibAddress for address payable; using LibUtils for TaikoData.State; event BlockProposed( @@ -25,6 +29,7 @@ library LibProposing { ); error L1_BLOCK_ID(); + error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_METADATA(); error L1_NOT_SOLO_PROPOSER(); @@ -66,13 +71,32 @@ library LibProposing { id: state.numBlocks, timestamp: uint64(block.timestamp), l1Height: uint64(block.number - 1), - gasLimit: input.gasLimit, + l2Basefee: 0, // will be set later l1Hash: blockhash(block.number - 1), mixHash: bytes32(block.prevrandao * state.numBlocks), txListHash: input.txListHash, txListByteStart: input.txListByteStart, txListByteEnd: input.txListByteEnd, - beneficiary: input.beneficiary + gasLimit: input.gasLimit, + beneficiary: input.beneficiary, + treasure: resolver.resolve(config.chainId, "treasure", false) + }); + } + + // Calculate L2 EIP-1559 basefee per gas + // + // Note that we do not charge basefee * gaslimit on L1 as we do not + // know the actual gas used in the L2 block. If we charge the proposer + // here, the proposer may suffer a loss depends on how many enclosed + // transactions become invalid and are filtered out. + // + // On L2, EIP-1559's basefee will not be burned but send to a Taiko + // treasure address. + if (config.gasIssuedPerSecond != 0) { + (meta.l2Basefee, state.l2GasExcess) = LibL2Tokenomics.getL2Basefee({ + state: state, + config: config, + gasLimit: input.gasLimit }); } @@ -89,7 +113,7 @@ library LibProposing { blk.proposer = msg.sender; if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 fee, uint256 deposit) = LibL1Tokenomics + (uint256 newFeeBase, uint256 fee, uint64 deposit) = LibL1Tokenomics .getBlockFee(state, config); uint256 burnAmount = fee + deposit; @@ -155,6 +179,7 @@ library LibProposing { if ( input.beneficiary == address(0) || + input.gasLimit == 0 || input.gasLimit > config.blockMaxGasLimit ) revert L1_INVALID_METADATA(); diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index bc51cb67192..baed1f63416 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -144,15 +144,15 @@ library LibProving { false ); - bytes32[8] memory inputs; - inputs[0] = bytes32(uint256(uint160(l1SignalService))); - inputs[1] = bytes32(uint256(uint160(l2SignalService))); - inputs[2] = bytes32(uint256(uint160(taikoL2))); - inputs[3] = evidence.parentHash; - inputs[4] = evidence.blockHash; - inputs[5] = evidence.signalRoot; - inputs[6] = bytes32(uint256(uint160(evidence.prover))); - inputs[7] = blk.metaHash; + uint256[8] memory inputs; + inputs[0] = uint160(l1SignalService); + inputs[1] = uint160(l2SignalService); + inputs[2] = uint160(taikoL2); + inputs[3] = uint256(evidence.parentHash); + inputs[4] = uint256(evidence.blockHash); + inputs[5] = uint256(evidence.signalRoot); + inputs[6] = uint160(evidence.prover); + inputs[7] = uint256(blk.metaHash); assembly { instance := keccak256(inputs, mul(32, 8)) diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 38fa4eb3a2d..1ac7580a905 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {LibMath} from "../../libs/LibMath.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; +import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; @@ -29,8 +30,14 @@ library LibUtils { } function getStateVariables( - TaikoData.State storage state + TaikoData.State storage state, + TaikoData.Config memory config ) internal view returns (TaikoData.StateVariables memory) { + (uint64 basefee1559, ) = LibL2Tokenomics.getL2Basefee({ + state: state, + config: config, + gasLimit: 1 + }); return TaikoData.StateVariables({ feeBase: state.feeBase, @@ -40,7 +47,9 @@ library LibUtils { lastProposedAt: state.lastProposedAt, avgBlockTime: state.avgBlockTime, lastVerifiedBlockId: state.lastVerifiedBlockId, - avgProofTime: state.avgProofTime + avgProofTime: state.avgProofTime, + l2Basefee: basefee1559, + l2GasExcess: state.l2GasExcess }); } @@ -56,23 +65,46 @@ library LibUtils { return _ma > 0 ? _ma : maValue; } + struct BlockMetadata { + uint64 id; + uint64 timestamp; + uint64 l1Height; + uint64 basefee; + bytes32 l1Hash; + bytes32 mixHash; + bytes32 txListHash; + uint24 txListByteStart; + uint24 txListByteEnd; + uint32 gasLimit; + address beneficiary; + address treasure; + } + function hashMetadata( TaikoData.BlockMetadata memory meta ) internal pure returns (bytes32 hash) { - bytes32[5] memory inputs; + uint256[6] memory inputs; + inputs[0] = - bytes32(uint256(meta.id) << 192) | - bytes32(uint256(meta.gasLimit) << 128) | - bytes32(uint256(meta.timestamp) << 64) | - bytes32(uint256(meta.l1Height)); + (uint256(meta.id) << 192) | + (uint256(meta.timestamp) << 128) | + (uint256(meta.l1Height) << 64) | + uint256(meta.l2Basefee); + + inputs[1] = uint256(meta.l1Hash); + inputs[2] = uint256(meta.mixHash); + inputs[3] = uint256(meta.txListHash); + + inputs[4] = + (uint256(meta.txListByteStart) << 232) | + (uint256(meta.txListByteEnd) << 208) | + (uint256(meta.gasLimit) << 176) | + (uint256(uint160(meta.beneficiary)) << 16); - inputs[1] = meta.l1Hash; - inputs[2] = meta.mixHash; - inputs[3] = meta.txListHash; - inputs[4] = bytes32(uint256(uint160(meta.beneficiary))); + inputs[5] = (uint256(uint160(meta.treasure)) << 96); assembly { - hash := keccak256(inputs, mul(5, 32)) + hash := keccak256(inputs, mul(6, 32)) } } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index de7a59235a1..60e4264e77c 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; +import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -31,24 +32,43 @@ library LibVerifying { TaikoData.State storage state, TaikoData.Config memory config, bytes32 genesisBlockHash, - uint64 feeBase + uint64 feeBase, + uint64 l2GasExcessMax, + uint64 l2BasefeeInitial, + uint64 l2GasTarget, + uint64 l2Expected2X1XRatio ) internal { _checkConfig(config); - uint64 timeNow = uint64(block.timestamp); - state.genesisHeight = timeNow; - state.genesisTimestamp = timeNow; - state.feeBase = feeBase; - state.numBlocks = 1; - - TaikoData.Block storage blk = state.blocks[0]; - blk.proposedAt = timeNow; - blk.nextForkChoiceId = 2; - blk.verifiedForkChoiceId = 1; + { + uint64 timeNow = uint64(block.timestamp); + state.genesisHeight = timeNow; + state.genesisTimestamp = timeNow; + state.feeBase = feeBase; + state.numBlocks = 1; + + TaikoData.Block storage blk = state.blocks[0]; + blk.proposedAt = timeNow; + blk.nextForkChoiceId = 2; + blk.verifiedForkChoiceId = 1; + + TaikoData.ForkChoice storage fc = state.blocks[0].forkChoices[1]; + fc.blockHash = genesisBlockHash; + fc.provenAt = timeNow; + } - TaikoData.ForkChoice storage fc = state.blocks[0].forkChoices[1]; - fc.blockHash = genesisBlockHash; - fc.provenAt = timeNow; + if (config.gasIssuedPerSecond != 0) { + ( + state.l2GasExcess, + state.l2Xscale, + state.l2Yscale + ) = LibL2Tokenomics.calcL2BasefeeParams( + l2GasExcessMax, + l2BasefeeInitial, + l2GasTarget, + l2Expected2X1XRatio + ); + } emit BlockVerified(0, genesisBlockHash); } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index b55e87593aa..b631fed880f 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -31,7 +31,6 @@ contract TestTaikoL1 is TaikoL1 { config.rewardBurnBips = 100; // 100 basis points or 1% config.proposerDepositPctg = 25; // 25% - config.bootstrapDiscountHalvingPeriod = 1 seconds; config.enableTokenomics = false; config.skipZKPVerification = true; config.feeBaseMAF = 1024; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index ee6aad6679d..8d55b35a6e4 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -34,7 +34,6 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { // Moving average factors config.feeBaseMAF = 1024; - config.bootstrapDiscountHalvingPeriod = 1 seconds; config.enableTokenomics = true; config.skipZKPVerification = true; diff --git a/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol new file mode 100644 index 00000000000..6bdf6ec4194 --- /dev/null +++ b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED +// Taken from: https://github.com/recmo/experiment-solexp/blob/main/src/FixedPointMathLib.sol +pragma solidity ^0.8.18; + +library LibFixedPointMath { + uint256 public constant MAX_EXP_INPUT = 135305999368893231588; + + error Overflow(); + + // Computes e^x in 1e18 fixed point. + function exp(int256 x) internal pure returns (int256 r) { + unchecked { + // Input x is in fixed point format, with scale factor 1/1e18. + + // When the result is < 0.5 we return zero. This happens when + // x <= floor(log(0.5e18) * 1e18) ~ -42e18 + if (x <= -42139678854452767551) { + return 0; + } + + // When the result is > (2**255 - 1) / 1e18 we can not represent it + // as an int256. This happens when x >= floor(log((2**255 -1) / 1e18) * 1e18) ~ 135. + if (x >= 135305999368893231589) revert Overflow(); + + // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 + // for more intermediate precision and a binary basis. This base conversion + // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. + x = (x << 78) / 5 ** 18; + + // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers of two + // such that exp(x) = exp(x') * 2**k, where k is an integer. + // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). + int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> + 96; + x = x - k * 54916777467707473351141471128; + // k is in the range [-61, 195]. + + // Evaluate using a (6, 7)-term rational approximation + // p is made monic, we will multiply by a scale factor later + int256 p = x + 2772001395605857295435445496992; + p = ((p * x) >> 96) + 44335888930127919016834873520032; + p = ((p * x) >> 96) + 398888492587501845352592340339721; + p = ((p * x) >> 96) + 1993839819670624470859228494792842; + p = p * x + (4385272521454847904632057985693276 << 96); + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + // Evaluate using using Knuth's scheme from p. 491. + int256 z = x + 750530180792738023273180420736; + z = ((z * x) >> 96) + 32788456221302202726307501949080; + int256 w = x - 2218138959503481824038194425854; + w = ((w * z) >> 96) + 892943633302991980437332862907700; + int256 q = z + w - 78174809823045304726920794422040; + q = ((q * w) >> 96) + 4203224763890128580604056984195872; + assembly { + // Div in assembly because solidity adds a zero check despite the `unchecked`. + // The q polynomial is known not to have zeros in the domain. (All roots are complex) + // No scaling required because p is already 2**96 too large. + r := sdiv(p, q) + } + // r should be in the range (0.09, 0.25) * 2**96. + + // We now need to multiply r by + // * the scale factor s = ~6.031367120..., + // * the 2**k factor from the range reduction, and + // * the 1e18 / 2**96 factor for base converison. + // We do all of this at once, with an intermediate result in 2**213 basis + // so the final right shift is always by a positive amount. + r = int( + (uint(r) * 3822833074963236453042738258902158003155416615667) >> + uint256(195 - k) + ); + } + } +} diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index b20a5c1e9d4..e0650a09a29 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -5,6 +5,6 @@ test = 'test2' libs = ['lib'] optimizer = true optimizer_runs = 200 -gas_reports = ["TaikoL1", "TaikoWithConfig", "TaikoL2", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] +gas_reports = ["TaikoL1", "TaikoL2", "TaikoL1WithConfig", "LibL1Tokenomics", "LibL2Tokenomics", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/packages/protocol/test/tokenomics/blockFee.test.ts b/packages/protocol/test/tokenomics/blockFee.test.ts index 98a37f5f490..75529c54aa5 100644 --- a/packages/protocol/test/tokenomics/blockFee.test.ts +++ b/packages/protocol/test/tokenomics/blockFee.test.ts @@ -50,22 +50,6 @@ describe("tokenomics: blockFee", function () { expect(blockFee).to.be.eq(0); }); - it("block fee should increase as the halving period passes, while no blocks are proposed", async function () { - const iterations: number = 5; - const period: number = config.bootstrapDiscountHalvingPeriod - .mul(1000) - .toNumber(); - - let lastBlockFee: BigNumber = await taikoL1.getBlockFee(); - - for (let i = 0; i < iterations; i++) { - await sleep(period); - const blockFee = await taikoL1.getBlockFee(); - expect(blockFee).to.be.gt(lastBlockFee); - lastBlockFee = blockFee; - } - }); - it( "proposes blocks on interval, proposer's balance for TkoToken should decrease as it pays proposer fee, " + "proofReward should increase since more slots are used and " + diff --git a/packages/protocol/test/utils/onNewL2Block.ts b/packages/protocol/test/utils/onNewL2Block.ts index dea3097801e..a9a969e206e 100644 --- a/packages/protocol/test/utils/onNewL2Block.ts +++ b/packages/protocol/test/utils/onNewL2Block.ts @@ -2,7 +2,6 @@ import { BigNumber, ethers } from "ethers"; import { TaikoL1, TaikoToken } from "../../typechain"; import { BlockProposedEvent } from "../../typechain/LibProposing"; import Proposer from "./proposer"; -import sleep from "./sleep"; // onNewL2Block should be called from a tokenomics test case when a new block // is generated from the l2Provider. @@ -22,7 +21,7 @@ async function onNewL2Block( newBlockFee: BigNumber; newProofReward: BigNumber; }> { - const config = await taikoL1.getConfig(); + // const config = await taikoL1.getConfig(); const block = await l2Provider.getBlock(blockNumber); const { proposedEvent } = await proposer.proposeBlock(block); @@ -39,7 +38,6 @@ async function onNewL2Block( ? await taikoTokenL1.balanceOf(await proposerSigner.getAddress()) : BigNumber.from(0); - await sleep(1000 * config.bootstrapDiscountHalvingPeriod.toNumber()); const newBlockFee = await taikoL1.getBlockFee(); console.log("-------------------proposed----------", id); diff --git a/packages/protocol/test2/GasComparison.t.sol b/packages/protocol/test2/GasComparison.t.sol index ecb17f9b68f..c9b6547ff6f 100644 --- a/packages/protocol/test2/GasComparison.t.sol +++ b/packages/protocol/test2/GasComparison.t.sol @@ -23,19 +23,19 @@ struct MyStruct { } contract FooBar { - function loadBlockMetadata_1(bytes memory data) public { - MyStruct memory meta = abi.decode(data, (MyStruct)); + function loadBlockMetadata_1(bytes memory data) public pure { + abi.decode(data, (MyStruct)); } - function loadBlockMetadata_2(bytes calldata data) public { - MyStruct memory meta = abi.decode(data, (MyStruct)); + function loadBlockMetadata_2(bytes calldata data) public pure { + abi.decode(data, (MyStruct)); } - function loadBlockMetadata_3(MyStruct memory data) public {} + function loadBlockMetadata_3(MyStruct memory data) public pure {} - function loadBlockMetadata_4(MyStruct calldata data) public {} + function loadBlockMetadata_4(MyStruct calldata data) public pure {} - function loadBlockMetadata_5(bytes calldata data) public { + function loadBlockMetadata_5(bytes calldata data) public pure { MyStruct memory meta; meta.id = uint256(bytes32(data[0:32])); meta.l1Height = uint256(bytes32(data[32:64])); @@ -44,7 +44,7 @@ contract FooBar { meta.timestamp = uint64(bytes8(data[104:112])); } - function loadBlockMetadata_6() public { + function loadBlockMetadata_6() public pure { MyStruct memory meta; uint256 a; assembly { @@ -71,50 +71,65 @@ contract FooBar { meta.timestamp = uint64(uint256((a << (128 + 64)) >> (128 + 64))); } - function return_1() public returns (TaikoData.BlockMetadata memory meta) { + function return_1() + public + view + returns (TaikoData.BlockMetadata memory meta) + { meta = TaikoData.BlockMetadata({ id: 1, l1Height: 1, l1Hash: bytes32(uint256(1)), beneficiary: address(this), + treasure: address(this), txListHash: bytes32(uint256(1)), txListByteStart: 0, txListByteEnd: 1000, gasLimit: 1, + l2Basefee: 1000000, mixHash: bytes32(uint256(1)), timestamp: 1 }); } - function return_2() public { - TaikoData.BlockMetadata memory meta = TaikoData.BlockMetadata({ + function return_2() public view { + TaikoData.BlockMetadata({ id: 1, l1Height: 1, l1Hash: bytes32(uint256(1)), beneficiary: address(this), + treasure: address(this), txListHash: bytes32(uint256(1)), txListByteStart: 0, txListByteEnd: 1000, gasLimit: 1, + l2Basefee: 1000000, mixHash: bytes32(uint256(1)), timestamp: 1 }); } //------ - function hashString_1(string memory str) public returns (bytes32 hash) { + function hashString_1( + string memory str + ) public pure returns (bytes32 hash) { assembly { hash := keccak256(add(str, 32), mload(str)) } } - function hashString_2(string memory str) public returns (bytes32 hash) { + function hashString_2( + string memory str + ) public pure returns (bytes32 hash) { hash = keccak256(bytes(str)); } //------ - function hashTwo_1(address a, bytes32 b) public returns (bytes32 hash) { + function hashTwo_1( + address a, + bytes32 b + ) public pure returns (bytes32 hash) { assembly { // Load the free memory pointer and allocate memory for the concatenated arguments let input := mload(64) @@ -130,7 +145,10 @@ contract FooBar { } } - function hashTwo_2(address a, bytes32 b) public returns (bytes32 hash) { + function hashTwo_2( + address a, + bytes32 b + ) public pure returns (bytes32 hash) { hash = keccak256(bytes.concat(bytes20(uint160(a)), b)); // the following will work too. // hash = keccak256(abi.encodePacked(a, b)); @@ -138,21 +156,21 @@ contract FooBar { //------ - function increment_1(uint256 count) public { + function increment_1(uint256 count) public pure { uint256 a; for (uint256 i = 0; i < count; i++) { a += i; } } - function increment_2(uint256 count) public { + function increment_2(uint256 count) public pure { uint256 a; for (uint256 i = 0; i < count; ++i) { a += i; } } - function increment_3(uint256 count) public { + function increment_3(uint256 count) public pure { uint256 a; for (uint256 i = 0; i < count; ) { a += i; @@ -162,7 +180,7 @@ contract FooBar { } } - function increment_4(uint256 count) public { + function increment_4(uint256 count) public pure { uint256 a; for (uint256 i = 0; i < count; ) { a += i; @@ -176,14 +194,14 @@ contract FooBar { function hashKey_1( uint256 chainId, string memory name - ) public view returns (bytes32) { + ) public pure returns (bytes32) { return keccak256(bytes(string.concat(Strings.toString(chainId), name))); } function hashKey_2( uint256 chainId, string memory name - ) public view returns (bytes32) { + ) public pure returns (bytes32) { return keccak256(abi.encodePacked(chainId, name)); } @@ -205,32 +223,44 @@ contract GasComparisonTest is Test { foobar = new FooBar(); } - function testCompareHashString(uint256 count) external { + function testCompare_hashString(uint256 count) public { vm.assume(count > 10 && count < 1000); + string memory str = string(new bytes(count)); assertEq( foobar.hashString_1(str), foobar.hashString_2(str) //best ); + } + + function testCompare_increment(uint256 count) public view { + vm.assume(count > 10 && count < 1000); + foobar.increment_1(count); + foobar.increment_2(count); + foobar.increment_3(count); // best + foobar.increment_4(count); + } + function testCompare_hashTwo() public { address a = address(this); bytes32 b = blockhash(block.number - 1); assertEq( foobar.hashTwo_1(a, b), //best foobar.hashTwo_2(a, b) ); + } - foobar.increment_1(count); - foobar.increment_2(count); - foobar.increment_3(count); // best - foobar.increment_4(count); + function testCompare_hashKey() public view { + foobar.hashKey_1(123, "abc"); + foobar.hashKey_2(123, "abc"); + } + function testCompare_return() public view { foobar.return_1(); foobar.return_2(); // cheaper + } - foobar.hashKey_1(123, "abc"); - foobar.hashKey_2(123, "abc"); - + function testCompare_loadBlockMetadata() public { MyStruct memory meta = MyStruct({ id: 123, l1Height: 456, @@ -238,36 +268,33 @@ contract GasComparisonTest is Test { gasLimit: 333, timestamp: 999 }); - { - bytes memory b = abi.encode(meta); - foobar.loadBlockMetadata_1(b); - foobar.loadBlockMetadata_2(b); - foobar.loadBlockMetadata_3(meta); - foobar.loadBlockMetadata_4(meta); // best - } - { - bytes memory b = bytes.concat( - bytes32(meta.id), - bytes32(meta.l1Height), - meta.l1Hash, - bytes8(meta.gasLimit), - bytes8(meta.timestamp) - ); - - foobar.loadBlockMetadata_5(b); - - bytes memory c = bytes.concat( - FooBar.loadBlockMetadata_6.selector, - b - ); - - address(foobar).call(c); - } - { - address to = 0x50081b12838240B1bA02b3177153Bca678a86078; - foobar.send0Ether_CheckInside(to, 0); - foobar.send0Ether_CheckOutside(to, 0); - } + bytes memory b; + + b = abi.encode(meta); + foobar.loadBlockMetadata_1(b); + foobar.loadBlockMetadata_2(b); + foobar.loadBlockMetadata_3(meta); + foobar.loadBlockMetadata_4(meta); // best + + b = bytes.concat( + bytes32(meta.id), + bytes32(meta.l1Height), + meta.l1Hash, + bytes8(meta.gasLimit), + bytes8(meta.timestamp) + ); + + foobar.loadBlockMetadata_5(b); + + bytes memory c = bytes.concat(FooBar.loadBlockMetadata_6.selector, b); + + address(foobar).call(c); + } + + function testCompare_send0Ether() public { + address to = 0x50081b12838240B1bA02b3177153Bca678a86078; + foobar.send0Ether_CheckInside(to, 0); + foobar.send0Ether_CheckOutside(to, 0); } } diff --git a/packages/protocol/test2/LibFixedPointMath.t.sol b/packages/protocol/test2/LibFixedPointMath.t.sol new file mode 100644 index 00000000000..e531dd8dd6a --- /dev/null +++ b/packages/protocol/test2/LibFixedPointMath.t.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED +// Some of the tests are taken from: +// https://github.com/recmo/experiment-solexp/blob/main/src/test/FixedPointMathLib.t.sol +pragma solidity 0.8.18; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import "../contracts/thirdparty/LibFixedPointMath.sol"; + +contract LibFixedPointMathTest is Test { + function testExp1() public { + assertEq(LibFixedPointMath.exp(-1e18), 367879441171442321); + } + + function testExpSmallest() public view { + int y = LibFixedPointMath.exp(-42139678854452767550); + + console2.log( + "LibFixedPointMath.exp(-42139678854452767550)=", + uint256(y) + ); + } + + function testExpLargest() public view { + int y = LibFixedPointMath.exp(int(LibFixedPointMath.MAX_EXP_INPUT)); + console2.log( + "LibFixedPointMath.exp(135305999368893231588)=", + uint256(y) + ); + } + + function testExpSome() public view { + int y = LibFixedPointMath.exp(5e18); + console2.log("LibFixedPointMath.exp(5e18)=", uint256(y)); + } + + function testExpGas() public view { + uint g0 = gasleft(); + LibFixedPointMath.exp(133e18); + uint g1 = gasleft(); + LibFixedPointMath.exp(-23e18); + uint g2 = gasleft(); + LibFixedPointMath.exp(5e18); + uint g3 = gasleft(); + console2.logUint(g0 - g1); + console2.logUint(g1 - g2); + console2.logUint(g2 - g3); + } + + function testExp3() public pure { + LibFixedPointMath.exp(133e18); + LibFixedPointMath.exp(10e18); + LibFixedPointMath.exp(-23e18); + } +} diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 61da8fd4fc6..4f37d8dd94d 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -5,8 +5,13 @@ import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {TaikoData} from "../contracts/L1/TaikoData.sol"; import {LibL1Tokenomics} from "../contracts/L1/libs/LibL1Tokenomics.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; contract TestLibL1Tokenomics is Test { + using SafeCastUpgradeable for uint256; + struct FeeConfig { uint64 avgTimeMAF; uint64 avgTimeCap; @@ -18,248 +23,248 @@ contract TestLibL1Tokenomics is Test { function testTokenomicsFeeCalcWithNonZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 ether, + expectedFeeBase: 140 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 120 ether, + expectedFeeBase: 120 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 80 ether, + expectedFeeBase: 80 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 ether, + expectedFeeBase: 60 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: true, dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 ether, + expectedFeeBase: 60 * 1E8, expectedPreimumRate: 0 }); } function testTokenomicsFeeCalcWithZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: true, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); } function testTokenomicsRewardCalcWithNonZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 ether, + expectedFeeBase: 60 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 80 ether, + expectedFeeBase: 80 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 120 ether, + expectedFeeBase: 120 * 1E8, expectedPreimumRate: 5000 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 ether, + expectedFeeBase: 140 * 1E8, expectedPreimumRate: 10000 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: false, dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 ether, + expectedFeeBase: 140 * 1E8, expectedPreimumRate: 10000 }); } function testTokenomicsRewardCalcWithZeroStartBips() public { testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 0 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 20 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 40 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 60 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 80 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); testTimeAdjustedFee({ - feeBase: 100 ether, + feeBase: 100 * 1E8, timeAverageSec: 40 seconds, timeUsedSec: 81 seconds, isProposal: false, dampingFactorBips: 0, // 0% - expectedFeeBase: 100 ether, + expectedFeeBase: 100 * 1E8, expectedPreimumRate: 0 }); } @@ -281,7 +286,7 @@ contract TestLibL1Tokenomics is Test { (uint256 _feeBase, uint256 _premiumRate) = LibL1Tokenomics .getTimeAdjustedFee( feeConfig, - feeBase, + feeBase.toUint64(), isProposal, timeUsedSec, timeAverageSec * 1000 diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol new file mode 100644 index 00000000000..2eb27615e8d --- /dev/null +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {LibL2Tokenomics as T} from "../contracts/L1/libs/LibL2Tokenomics.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; + +import { + LibFixedPointMath as M +} from "../contracts/thirdparty/LibFixedPointMath.sol"; + +contract TestLibL2Tokenomics is Test { + using SafeCastUpgradeable for uint256; + + function test1559PurchaseMaxSizeGasWontOverflow() public view { + uint64 basefeeInitial = 5000000000; + uint64 l2GasExcessMax = 15000000 * 256; + uint64 gasTarget = 6000000; + uint64 expected2X1XRatio = 111; // 11 %% + + (uint64 l2GasExcess, uint64 xscale, uint256 yscale) = T + .calcL2BasefeeParams( + l2GasExcessMax, + basefeeInitial, + gasTarget, + expected2X1XRatio + ); + + uint64 _basefee = basefeeInitial; + console2.log("basefee", _basefee); + l2GasExcess += gasTarget; + + for (uint i = 0; i < 10; ++i) { + uint64 newBasefee = T + .calcL2Basefee(l2GasExcess, xscale, yscale << 64, gasTarget) + .toUint64(); + uint ratio = (newBasefee * 100) / _basefee - 100; + console2.log("basefee", newBasefee, "+%", ratio); + _basefee = newBasefee; + l2GasExcess += gasTarget; + } + } +} diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index cd87527c94a..1018e7d1043 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -10,7 +10,7 @@ import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; import {SignalService} from "../contracts/signal/SignalService.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {TaikoL1TestBase} from "./TaikoL1TestBase.sol"; +import {TaikoL1TestBase} from "./TaikoL1TestBase.t.sol"; contract TaikoL1WithConfig is TaikoL1 { function getConfig() @@ -22,14 +22,13 @@ contract TaikoL1WithConfig is TaikoL1 { config = TaikoConfig.getConfig(); config.enableTokenomics = true; - config.bootstrapDiscountHalvingPeriod = 0; - config.constantFeeRewardBlocks = 0; config.txListCacheExpiry = 5 minutes; config.proposerDepositPctg = 0; config.maxVerificationsPerTx = 0; config.enableSoloProposer = false; config.enableOracleProver = false; config.maxNumProposedBlocks = 10; + config.gasIssuedPerSecond = 0; config.ringBufferSize = 12; // this value must be changed if `maxNumProposedBlocks` is changed. config.slotSmoothingFactor = 4160; diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.t.sol similarity index 90% rename from packages/protocol/test2/TaikoL1TestBase.sol rename to packages/protocol/test2/TaikoL1TestBase.t.sol index ce0eae47ed0..37fee081682 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.t.sol @@ -22,7 +22,10 @@ abstract contract TaikoL1TestBase is Test { bytes32 public constant GENESIS_BLOCK_HASH = keccak256("GENESIS_BLOCK_HASH"); uint64 feeBase = 1E8; // 1 TKO + uint64 l2GasExcess = 1E18; + address public constant L2Treasure = + 0x859d74b52762d9ed07D1b2B8d7F93d26B1EA78Bb; address public constant L2SS = 0xa008AE5Ba00656a3Cc384de589579e3E52aC030C; address public constant L2TaikoL2 = 0x0082D90249342980d011C58105a03b35cCb4A315; @@ -40,8 +43,21 @@ abstract contract TaikoL1TestBase is Test { addressManager = new AddressManager(); addressManager.init(); + uint64 basefeeInitial = 5000000000; + uint64 l2GasExcessMax = 15000000 * 256; + uint64 gasTarget = 6000000; + uint64 expected2X1XRatio = 111; // 11 %% + L1 = deployTaikoL1(); - L1.init(address(addressManager), GENESIS_BLOCK_HASH, feeBase); + L1.init( + address(addressManager), + GENESIS_BLOCK_HASH, + feeBase, + l2GasExcessMax, + basefeeInitial, + gasTarget, + expected2X1XRatio + ); conf = L1.getConfig(); tko = new TaikoToken(); @@ -66,6 +82,7 @@ abstract contract TaikoL1TestBase is Test { _registerAddress("taiko_token", address(tko)); _registerAddress("proto_broker", address(L1)); _registerAddress("signal_service", address(ss)); + _registerL2Address("treasure", L2Treasure); _registerL2Address("signal_service", address(L2SS)); _registerL2Address("taiko_l2", address(L2TaikoL2)); @@ -96,13 +113,17 @@ abstract contract TaikoL1TestBase is Test { } meta.id = variables.numBlocks; + meta.timestamp = uint64(block.timestamp); meta.l1Height = uint64(block.number - 1); + meta.l2Basefee = 0; meta.l1Hash = blockhash(block.number - 1); - meta.beneficiary = proposer; - meta.txListHash = keccak256(txList); meta.mixHash = bytes32(_mixHash); + meta.txListHash = keccak256(txList); + meta.txListByteStart = 0; + meta.txListByteEnd = txListSize; meta.gasLimit = gasLimit; - meta.timestamp = uint64(block.timestamp); + meta.beneficiary = proposer; + meta.treasure = L2Treasure; vm.prank(proposer, proposer); L1.proposeBlock(abi.encode(input), txList); diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 64febe555b0..f6d93cfe9a2 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -23,6 +23,7 @@ struct Config { uint256 maxNumVerifiedBlocks; uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; + uint256 gasIssuedPerSecond; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; @@ -30,9 +31,7 @@ struct Config { uint256 rewardBurnBips; uint256 proposerDepositPctg; uint256 feeBaseMAF; - uint64 bootstrapDiscountHalvingPeriod; - uint64 constantFeeRewardBlocks; - uint64 txListCacheExpiry; + uint256 txListCacheExpiry; bool enableSoloProposer; bool enableOracleProver; bool enableTokenomics; @@ -54,6 +53,8 @@ struct StateVariables { uint64 avgBlockTime; uint64 avgProofTime; uint64 lastProposedAt; + uint64 l2Basefee; + uint64 l2GasExcess; } ``` @@ -77,13 +78,15 @@ struct BlockMetadata { uint64 id; uint64 timestamp; uint64 l1Height; - uint32 gasLimit; + uint64 l2Basefee; bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; uint24 txListByteStart; uint24 txListByteEnd; + uint32 gasLimit; address beneficiary; + address treasure; } ``` @@ -154,16 +157,16 @@ struct State { mapping(bytes32 => struct TaikoData.TxListInfo) txListInfo; uint64 genesisHeight; uint64 genesisTimestamp; - uint64 __reserved1; - uint64 __reserved2; + uint64 l2Xscale; + uint64 l2Yscale; uint64 numBlocks; uint64 lastProposedAt; uint64 avgBlockTime; - uint64 __reserved3; - uint64 __reserved4; + uint64 l2GasExcess; uint64 lastVerifiedBlockId; + uint64 __reserved4; uint64 avgProofTime; uint64 feeBase; - uint256[42] __gap; + uint256[43] __gap; } ``` diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index 4028d4ecf94..a53c3aef76b 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -4,6 +4,24 @@ title: TaikoErrors ## TaikoErrors +### L1_1559_GAS_CHANGE_MISMATCH + +```solidity +error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio) +``` + +### L1_1559_X_SCALE_TOO_LARGE + +```solidity +error L1_1559_X_SCALE_TOO_LARGE() +``` + +### L1_1559_Y_SCALE_TOO_LARGE + +```solidity +error L1_1559_Y_SCALE_TOO_LARGE() +``` + ### L1_ALREADY_PROVEN ```solidity @@ -34,6 +52,12 @@ error L1_EVIDENCE_MISMATCH() error L1_FORK_CHOICE_NOT_FOUND() ``` +### L1_INSUFFICIENT_ETHER + +```solidity +error L1_INSUFFICIENT_ETHER() +``` + ### L1_INSUFFICIENT_TOKEN ```solidity @@ -82,6 +106,12 @@ error L1_NOT_ORACLE_PROVER() error L1_NOT_SOLO_PROPOSER() ``` +### L1_OUT_OF_BLOCK_SPACE + +```solidity +error L1_OUT_OF_BLOCK_SPACE() +``` + ### L1_TOO_MANY_BLOCKS ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index 472d9fedf6a..c49e694479f 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -13,9 +13,23 @@ struct TaikoData.State state ### init ```solidity -function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase) external +function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, uint64 _l2GasExcessMax, uint64 _l2Basefee, uint64 _l2GasTarget, uint64 _l2Expected2X1XRatio) external ``` +Initialize the rollup. + +#### Parameters + +| Name | Type | Description | +| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- | +| \_addressManager | address | The AddressManager address. | +| \_genesisBlockHash | bytes32 | The block hash of the genesis block. | +| \_feeBase | uint64 | The initial value of the proposer-fee/prover-reward feeBase. | +| \_l2GasExcessMax | uint64 | The max amount of L2 gas that can ever be purchased under any possible circumstances before additional gas are issued. | +| \_l2Basefee | uint64 | The initial value of L2 EIP-1559 base fee per gas. | +| \_l2GasTarget | uint64 | A value to verify the correctness of L2 EIP-1559 config. | +| \_l2Expected2X1XRatio | uint64 | A value to verify the correctness of L2 EIP-1559 config. | + ### proposeBlock ```solidity @@ -115,6 +129,12 @@ function getXchainBlockHash(uint256 blockId) public view returns (bytes32) function getXchainSignalRoot(uint256 blockId) public view returns (bytes32) ``` +### getL2Basefee + +```solidity +function getL2Basefee(uint32 gasLimit) public view returns (uint64 basefee) +``` + ### getStateVariables ```solidity From 81509c6b6351150268467f9c84ba145f08054fff Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 16:14:55 +0800 Subject: [PATCH 37/90] update foundry.toml --- packages/protocol/foundry.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index e0650a09a29..93f7f39da5c 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -5,6 +5,4 @@ test = 'test2' libs = ['lib'] optimizer = true optimizer_runs = 200 -gas_reports = ["TaikoL1", "TaikoL2", "TaikoL1WithConfig", "LibL1Tokenomics", "LibL2Tokenomics", "GasComparision", "SignalService", "Bridge", "BridgedERC20", "TaikoToken", "EtherVault", "TokenVault"] - # See more config options https://github.com/foundry-rs/foundry/tree/master/config From a4eae93133e030f7d3f927437a7ee66e2a6a8814 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 16:43:45 +0800 Subject: [PATCH 38/90] minor fix TaikoL2 --- packages/protocol/contracts/L2/TaikoL2.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 790de867db3..49d8d4d0298 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -127,7 +127,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { number: block.number, parentHash: parentHash, timestamp: block.timestamp, - basefee: 0, //block.basefee, + basefee: block.basefee, prevrandao: block.prevrandao, coinbase: block.coinbase, gaslimit: block.gaslimit, @@ -181,7 +181,6 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { } inputs[255] = bytes32(block.chainid); - // inputs[256] = bytes32(block.basefee); assembly { prevPIH := keccak256(inputs, mul(256, 32)) From 9c1592c67e66072d1bda63ad1ae0c594b580063c Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 21:29:11 +0800 Subject: [PATCH 39/90] fix --- packages/protocol/contracts/L1/TaikoL1.sol | 6 +++--- .../contracts/L1/libs/LibVerifying.sol | 6 +++--- packages/protocol/script/DeployOnL1.s.sol | 18 +++++++++++++++++- packages/protocol/test2/TaikoL1TestBase.t.sol | 2 +- .../contract-documentation/L1/TaikoL1.md | 4 ++-- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 893af721735..4b57c407b80 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -30,8 +30,8 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { * Initialize the rollup. * * @param _addressManager The AddressManager address. - * @param _genesisBlockHash The block hash of the genesis block. * @param _feeBase The initial value of the proposer-fee/prover-reward feeBase. + * @param _l2GenesisBlockHash The block hash of the genesis block. * @param _l2GasExcessMax The max amount of L2 gas that can ever be purchased * under any possible circumstances before additional gas are issued. * @param _l2Basefee The initial value of L2 EIP-1559 base fee per gas. @@ -40,8 +40,8 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { */ function init( address _addressManager, - bytes32 _genesisBlockHash, uint64 _feeBase, + bytes32 _l2GenesisBlockHash, uint64 _l2GasExcessMax, uint64 _l2Basefee, uint64 _l2GasTarget, @@ -51,8 +51,8 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { LibVerifying.init({ state: state, config: getConfig(), - genesisBlockHash: _genesisBlockHash, feeBase: _feeBase, + l2GenesisBlockHash: _l2GenesisBlockHash, l2GasExcessMax: _l2GasExcessMax, l2BasefeeInitial: _l2Basefee, l2GasTarget: _l2GasTarget, diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 60e4264e77c..cf02b774bbb 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -31,8 +31,8 @@ library LibVerifying { function init( TaikoData.State storage state, TaikoData.Config memory config, - bytes32 genesisBlockHash, uint64 feeBase, + bytes32 l2GenesisBlockHash, uint64 l2GasExcessMax, uint64 l2BasefeeInitial, uint64 l2GasTarget, @@ -53,7 +53,7 @@ library LibVerifying { blk.verifiedForkChoiceId = 1; TaikoData.ForkChoice storage fc = state.blocks[0].forkChoices[1]; - fc.blockHash = genesisBlockHash; + fc.blockHash = l2GenesisBlockHash; fc.provenAt = timeNow; } @@ -70,7 +70,7 @@ library LibVerifying { ); } - emit BlockVerified(0, genesisBlockHash); + emit BlockVerified(0, l2GenesisBlockHash); } function verifyBlocks( diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index 96873fa3762..c2a724d5121 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -9,6 +9,7 @@ pragma solidity ^0.8.18; import "forge-std/Script.sol"; import "forge-std/console2.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "../contracts/common/AddressResolver.sol"; import "../contracts/L1/TaikoToken.sol"; import "../contracts/L1/TaikoL1.sol"; @@ -20,10 +21,17 @@ import "../contracts/test/erc20/FreeMintERC20.sol"; import "../contracts/test/erc20/MayFailFreeMintERC20.sol"; contract DeployOnL1 is Script, AddressResolver { + using SafeCastUpgradeable for uint256; uint256 public l2ChainId = vm.envUint("L2_CHAIN_ID"); bytes32 public l2GensisHash = vm.envBytes32("L2_GENESIS_HASH"); + uint public l2GasExcessMax = vm.envUint("L2_GAS_EXCESS_MAX").toUint64(); + uint64 l2Basefee = vm.envUint("L2_BASE_FEE").toUint64(); + uint64 l2GasTarget = vm.envUint("L2_GAS_TARGET").toUint64(); + uint64 l2Expected2X1XRatio = + vm.envUint("L2_EXPECTED_2X1X_RATIO").toUint64(); + uint256 public deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address public taikoL2Address = vm.envAddress("TAIKO_L2_ADDRESS"); @@ -111,7 +119,15 @@ contract DeployOnL1 is Script, AddressResolver { address(taikoL1), bytes.concat( taikoL1.init.selector, - abi.encode(addressManagerProxy, l2GensisHash, feeBase) + abi.encode( + addressManagerProxy, + feeBase, + l2GensisHash, + l2GasExcessMax, + l2Basefee, + l2GasTarget, + l2Expected2X1XRatio + ) ) ); setAddress("proto_broker", taikoL1Proxy); diff --git a/packages/protocol/test2/TaikoL1TestBase.t.sol b/packages/protocol/test2/TaikoL1TestBase.t.sol index 37fee081682..087c7eca732 100644 --- a/packages/protocol/test2/TaikoL1TestBase.t.sol +++ b/packages/protocol/test2/TaikoL1TestBase.t.sol @@ -51,8 +51,8 @@ abstract contract TaikoL1TestBase is Test { L1 = deployTaikoL1(); L1.init( address(addressManager), - GENESIS_BLOCK_HASH, feeBase, + GENESIS_BLOCK_HASH, l2GasExcessMax, basefeeInitial, gasTarget, diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index c49e694479f..e6b23503eb8 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -13,7 +13,7 @@ struct TaikoData.State state ### init ```solidity -function init(address _addressManager, bytes32 _genesisBlockHash, uint64 _feeBase, uint64 _l2GasExcessMax, uint64 _l2Basefee, uint64 _l2GasTarget, uint64 _l2Expected2X1XRatio) external +function init(address _addressManager, uint64 _feeBase, bytes32 _l2GenesisBlockHash, uint64 _l2GasExcessMax, uint64 _l2Basefee, uint64 _l2GasTarget, uint64 _l2Expected2X1XRatio) external ``` Initialize the rollup. @@ -23,8 +23,8 @@ Initialize the rollup. | Name | Type | Description | | --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- | | \_addressManager | address | The AddressManager address. | -| \_genesisBlockHash | bytes32 | The block hash of the genesis block. | | \_feeBase | uint64 | The initial value of the proposer-fee/prover-reward feeBase. | +| \_l2GenesisBlockHash | bytes32 | The block hash of the genesis block. | | \_l2GasExcessMax | uint64 | The max amount of L2 gas that can ever be purchased under any possible circumstances before additional gas are issued. | | \_l2Basefee | uint64 | The initial value of L2 EIP-1559 base fee per gas. | | \_l2GasTarget | uint64 | A value to verify the correctness of L2 EIP-1559 config. | From d2879423788c2d6e526fa74a160a3f36a7572bfc Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 21:47:23 +0800 Subject: [PATCH 40/90] fix --- .../protocol/contracts/L1/TaikoConfig.sol | 2 +- .../protocol/contracts/L1/TaikoErrors.sol | 1 + .../contracts/L1/libs/LibL2Tokenomics.sol | 8 +++---- .../contracts/L1/libs/LibVerifying.sol | 8 +++++++ packages/protocol/package.json | 2 +- packages/protocol/script/DeployOnL1.s.sol | 23 ++++++++++++++----- 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 8198b9fe956..720aeac0330 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasIssuedPerSecond: 30000000, + gasIssuedPerSecond: 0, //30000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index fda2c853128..8f05494d444 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -20,6 +20,7 @@ abstract contract TaikoErrors { error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_CONFIG(); error L1_INVALID_EVIDENCE(); + error L1_INVALID_L21559_PARAMS(); error L1_INVALID_METADATA(); error L1_INVALID_PARAM(); error L1_INVALID_PROOF(); diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index d8372ef5f40..7ca8cb525ef 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -50,17 +50,17 @@ library LibL2Tokenomics { } function calcL2BasefeeParams( - uint64 excessMax, + uint64 gasExcessMax, uint64 basefeeInitial, uint64 gasTarget, uint64 expected2X1XRatio ) internal pure returns (uint64 l2GasExcess, uint64 xscale, uint64 yscale) { - assert(excessMax != 0); + assert(gasExcessMax != 0); - l2GasExcess = excessMax / 2; + l2GasExcess = gasExcessMax / 2; // calculate xscale - uint256 _xscale = LibFixedPointMath.MAX_EXP_INPUT / excessMax; + uint256 _xscale = LibFixedPointMath.MAX_EXP_INPUT / gasExcessMax; if (_xscale >= type(uint64).max) { revert L1_1559_X_SCALE_TOO_LARGE(); } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index cf02b774bbb..404112941e4 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -20,6 +20,7 @@ library LibVerifying { using LibUtils for TaikoData.State; error L1_INVALID_CONFIG(); + error L1_INVALID_L21559_PARAMS(); event BlockVerified(uint256 indexed id, bytes32 blockHash); event XchainSynced( @@ -58,6 +59,13 @@ library LibVerifying { } if (config.gasIssuedPerSecond != 0) { + if ( + l2GasExcessMax == 0 || + l2BasefeeInitial == 0 || + l2GasTarget == 0 || + l2Expected2X1XRatio == 0 + ) revert L1_INVALID_L21559_PARAMS(); + ( state.l2GasExcess, state.l2Xscale, diff --git a/packages/protocol/package.json b/packages/protocol/package.json index fbc4c0bbca2..88627a447a0 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -21,7 +21,7 @@ "test:integration": "TEST_TYPE=integration ./test/test_integration.sh", "test:tokenomics": "TEST_TYPE=tokenomics ./test/test_integration.sh", "test:all": "pnpm run test && pnpm run test:integration && pnpm run test:tokenomics && pnpm run test:genesis", - "deploy:foundry": "./script/download_solc.sh && PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 ORACLE_PROVER_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 SOLO_PROPOSER=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 OWNER=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 TAIKO_L2_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 TAIKO_TOKEN_PREMINT_AMOUNT=0xffff L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 L2_CHAIN_ID=167001 forge script script/DeployOnL1.s.sol:DeployOnL1 --fork-url http://localhost:8545 --broadcast --ffi -vvvv --via-ir", + "deploy:foundry": "./script/download_solc.sh && PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 ORACLE_PROVER_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 SOLO_PROPOSER=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 OWNER=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 TAIKO_L2_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 TAIKO_TOKEN_PREMINT_AMOUNT=0xffff L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 L2_CHAIN_ID=167001 forge script script/DeployOnL1.s.sol:DeployOnL1 --fork-url http://localhost:8545 --broadcast --ffi -vvvv --via-ir", "lint-staged": "lint-staged --allow-empty", "sizer": "pnpm hardhat size-contracts" }, diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index c2a724d5121..4df8793148d 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -26,12 +26,6 @@ contract DeployOnL1 is Script, AddressResolver { bytes32 public l2GensisHash = vm.envBytes32("L2_GENESIS_HASH"); - uint public l2GasExcessMax = vm.envUint("L2_GAS_EXCESS_MAX").toUint64(); - uint64 l2Basefee = vm.envUint("L2_BASE_FEE").toUint64(); - uint64 l2GasTarget = vm.envUint("L2_GAS_TARGET").toUint64(); - uint64 l2Expected2X1XRatio = - vm.envUint("L2_EXPECTED_2X1X_RATIO").toUint64(); - uint256 public deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address public taikoL2Address = vm.envAddress("TAIKO_L2_ADDRESS"); @@ -112,7 +106,24 @@ contract DeployOnL1 is Script, AddressResolver { console.log("BullToken", bullToken); // TaikoL1 + TaikoL1 taikoL1 = new TaikoL1(); + uint64 l2GasExcessMax; + uint64 l2Basefee; + uint64 l2GasTarget; + uint64 l2Expected2X1XRatio; + + if (taikoL1.getConfig().gasIssuedPerSecond != 0) { + l2GasExcessMax = vm.envUint("L2_GAS_EXCESS_MAX").toUint64(); + + l2Basefee = vm.envUint("L2_BASE_FEE").toUint64(); + + l2GasTarget = vm.envUint("L2_GAS_TARGET").toUint64(); + l2Expected2X1XRatio = vm + .envUint("L2_EXPECTED_2X1X_RATIO") + .toUint64(); + } + uint64 feeBase = 1 ** 8; // Taiko Token's decimals is 8, not 18 address taikoL1Proxy = deployProxy( "taiko", From e4ddb8d649873f7e2c0997f9a456d0b277815278 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 21:52:19 +0800 Subject: [PATCH 41/90] +deploy_on_l1.sh --- packages/protocol/package.json | 2 +- packages/protocol/script/deploy_on_l1.sh | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100755 packages/protocol/script/deploy_on_l1.sh diff --git a/packages/protocol/package.json b/packages/protocol/package.json index 88627a447a0..468b09389b8 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -21,7 +21,7 @@ "test:integration": "TEST_TYPE=integration ./test/test_integration.sh", "test:tokenomics": "TEST_TYPE=tokenomics ./test/test_integration.sh", "test:all": "pnpm run test && pnpm run test:integration && pnpm run test:tokenomics && pnpm run test:genesis", - "deploy:foundry": "./script/download_solc.sh && PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 ORACLE_PROVER_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 SOLO_PROPOSER=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 OWNER=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 TAIKO_L2_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 TAIKO_TOKEN_PREMINT_AMOUNT=0xffff L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 L2_CHAIN_ID=167001 forge script script/DeployOnL1.s.sol:DeployOnL1 --fork-url http://localhost:8545 --broadcast --ffi -vvvv --via-ir", + "deploy:foundry": "./script/download_solc.sh && ./script/deploy_on_l1.sh", "lint-staged": "lint-staged --allow-empty", "sizer": "pnpm hardhat size-contracts" }, diff --git a/packages/protocol/script/deploy_on_l1.sh b/packages/protocol/script/deploy_on_l1.sh new file mode 100755 index 00000000000..5a3281692ec --- /dev/null +++ b/packages/protocol/script/deploy_on_l1.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ +ORACLE_PROVER_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ +SOLO_PROPOSER=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ +OWNER=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ +TAIKO_L2_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ +TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ +TAIKO_TOKEN_PREMINT_AMOUNT=0xffff \ +L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 \ +L2_CHAIN_ID=167001 \ +forge script script/DeployOnL1.s.sol:DeployOnL1 \ + --fork-url http://localhost:8545 \ + --broadcast \ + --ffi \ + -vvvv \ + --via-ir \ From ee225b8a8055f304e30e9115c899c53fcd429d2e Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 21:54:08 +0800 Subject: [PATCH 42/90] +deploy_on_l1.sh --- packages/protocol/script/DeployOnL1.s.sol | 3 --- packages/protocol/script/deploy_on_l1.sh | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index 4df8793148d..71ac8d46f0d 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -106,7 +106,6 @@ contract DeployOnL1 is Script, AddressResolver { console.log("BullToken", bullToken); // TaikoL1 - TaikoL1 taikoL1 = new TaikoL1(); uint64 l2GasExcessMax; uint64 l2Basefee; @@ -115,9 +114,7 @@ contract DeployOnL1 is Script, AddressResolver { if (taikoL1.getConfig().gasIssuedPerSecond != 0) { l2GasExcessMax = vm.envUint("L2_GAS_EXCESS_MAX").toUint64(); - l2Basefee = vm.envUint("L2_BASE_FEE").toUint64(); - l2GasTarget = vm.envUint("L2_GAS_TARGET").toUint64(); l2Expected2X1XRatio = vm .envUint("L2_EXPECTED_2X1X_RATIO") diff --git a/packages/protocol/script/deploy_on_l1.sh b/packages/protocol/script/deploy_on_l1.sh index 5a3281692ec..546237cafbb 100755 --- a/packages/protocol/script/deploy_on_l1.sh +++ b/packages/protocol/script/deploy_on_l1.sh @@ -11,6 +11,10 @@ TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ TAIKO_TOKEN_PREMINT_AMOUNT=0xffff \ L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 \ L2_CHAIN_ID=167001 \ +L2_GAS_EXCESS_MAX= \ +L2_BASE_FEE= \ +L2_GAS_TARGET= \ +L2_EXPECTED_2X1X_RATIO= \ forge script script/DeployOnL1.s.sol:DeployOnL1 \ --fork-url http://localhost:8545 \ --broadcast \ From c56dcbffde7c668efae1b3fa3ea0b465b122312b Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Thu, 30 Mar 2023 21:57:29 +0800 Subject: [PATCH 43/90] +deploy_on_l1.sh --- packages/protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/script/deploy_on_l1.sh | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 720aeac0330..8198b9fe956 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,7 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasIssuedPerSecond: 0, //30000000, + gasIssuedPerSecond: 30000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/script/deploy_on_l1.sh b/packages/protocol/script/deploy_on_l1.sh index 546237cafbb..554b0f3842b 100755 --- a/packages/protocol/script/deploy_on_l1.sh +++ b/packages/protocol/script/deploy_on_l1.sh @@ -1,5 +1,6 @@ #!/bin/sh +# Please reference LibL2Tokenomics.t.sol for L2 EIP-1559 related variables. set -e PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ @@ -11,10 +12,10 @@ TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ TAIKO_TOKEN_PREMINT_AMOUNT=0xffff \ L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 \ L2_CHAIN_ID=167001 \ -L2_GAS_EXCESS_MAX= \ -L2_BASE_FEE= \ -L2_GAS_TARGET= \ -L2_EXPECTED_2X1X_RATIO= \ +L2_GAS_EXCESS_MAX=3840000000 \ +L2_BASE_FEE=5000000000 \ +L2_GAS_TARGET=6000000 \ +L2_EXPECTED_2X1X_RATIO=111 \ forge script script/DeployOnL1.s.sol:DeployOnL1 \ --fork-url http://localhost:8545 \ --broadcast \ From 50660819e4cc47985f5da224fa6ca79b6e472e74 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Thu, 30 Mar 2023 21:50:28 +0200 Subject: [PATCH 44/90] Changes - integrate token emission logic within propose and verify - remove getTimeAdjustedFee, getSlotsAdjustedFee - logic cross-verified with python algorithm - proposerDepositPctg removed - use the LibFixedPointMath.sol library instead of ABDKMath64x64 - remove debug logging - remove test corpus from LibL1Tokenomics.t.sol --- .../protocol/contracts/L1/TaikoConfig.sol | 3 +- packages/protocol/contracts/L1/TaikoData.sol | 3 +- packages/protocol/contracts/L1/TaikoL1.sol | 21 +- .../contracts/L1/libs/LibL1Tokenomics.sol | 210 ++---- .../contracts/L1/libs/LibProposing.sol | 37 +- .../contracts/L1/libs/LibVerifying.sol | 72 +-- .../contracts/libs/LibTrieProof_explained.bak | 65 ++ .../contracts/test/L1/TestTaikoL1.sol | 1 - .../test/L1/TestTaikoL1EnableTokenomics.sol | 1 - ...ol => ABDKMath64x64_sol_original_name.bak} | 0 .../thirdparty/LibFixedPointMath.sol | 156 +++++ .../thirdparty/LibRLPReader_from_hamil.bak | 606 ++++++++++++++++++ .../thirdparty/LibRLPWriter_origi.bak | 281 ++++++++ packages/protocol/test2/LibL1Tokenomics.t.sol | 17 +- packages/protocol/test2/TaikoL1.t.sol | 45 +- .../test2/TaikoL1.t_sol_my_added_tests.bak | 220 +++++++ packages/protocol/test2/TaikoL1TestBase.sol | 5 +- 17 files changed, 1443 insertions(+), 300 deletions(-) create mode 100644 packages/protocol/contracts/libs/LibTrieProof_explained.bak rename packages/protocol/contracts/thirdparty/{ABDKMath.sol => ABDKMath64x64_sol_original_name.bak} (100%) create mode 100644 packages/protocol/contracts/thirdparty/LibFixedPointMath.sol create mode 100644 packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak create mode 100644 packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak create mode 100644 packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index e3e9f7c1b8e..2c5df8fb81b 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -36,11 +36,12 @@ library TaikoConfig { slotSmoothingFactor: 946649, // 100 basis points or 1% rewardBurnBips: 100, - proposerDepositPctg: 25, // - 25% + proposerDepositPctg: 0, // - 25% // Moving average factors feeBaseMAF: 1024, constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, + proofTimeTarget: 60, // 60sec general adjustmentQuotient: 16, enableSoloProposer: false, enableOracleProver: true, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 699dbd77d26..29515f194f0 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -33,6 +33,7 @@ library TaikoData { uint256 feeBaseMAF; uint64 constantFeeRewardBlocks; uint64 txListCacheExpiry; + uint64 proofTimeTarget; uint8 adjustmentQuotient; bool enableSoloProposer; bool enableOracleProver; @@ -110,7 +111,7 @@ library TaikoData { uint64 blockId; uint64 proposedAt; uint64 deposit; - uint32 gasLimit; + uint32 gasConsumed; uint24 nextForkChoiceId; uint24 verifiedForkChoiceId; bytes32 metaHash; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index c20109b1f1f..936482db456 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -129,26 +129,23 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { return state.balances[addr]; } - function getBlockFee() - public - view - returns (uint256 feeAmount, uint256 depositAmount) - { - (, feeAmount, depositAmount) = LibL1Tokenomics.getBlockFee( - state, - getConfig() - ); + function getBlockFee( + uint32 gasUsed + ) public view returns (uint256 feeAmount) { + (, feeAmount) = LibL1Tokenomics.getBlockFee(state, gasUsed); } function getProofReward( uint64 provenAt, - uint64 proposedAt + uint64 proposedAt, + uint32 usedGas ) public view returns (uint256 reward) { - (, reward, ) = LibL1Tokenomics.getProofReward({ + (, reward) = LibL1Tokenomics.getProofReward({ state: state, config: getConfig(), provenAt: provenAt, - proposedAt: proposedAt + proposedAt: proposedAt, + usedGas: usedGas }); } diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index b2eacc326a4..a9579ee375f 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -6,9 +6,6 @@ pragma solidity ^0.8.18; -import {Test} from "forge-std/Test.sol"; -import {console2} from "forge-std/console2.sol"; - import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; import { @@ -16,22 +13,15 @@ import { } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; -import {ABDKMath64x64} from "../../thirdparty/ABDKMath.sol"; +import { + LibFixedPointMath as Math +} from "../../thirdparty/LibFixedPointMath.sol"; library LibL1Tokenomics { using LibMath for uint256; - uint256 constant SCALING_FACTOR = 1e8; // 10^8 for 8 decimal places = 1 TKO token - - // So floating point as is - in solidity is nonexitent. With exponential calculation - // we loose a lot of value because we are restricted to integer. So the ABDKMath gives - // us an int128 value which is a 64.64 fixed point decimal representation number of the - // float value. In order to get back the floating point, this is the math: - // floating_point_number = fixed_point_number / 2^64 - // But as mentioned we loose a lot on the .01-.99 range, so instead we shall calculate this - // floating_point_number = fixed_point_number / 2^57 , meaning we get a 2^7 times bigger number - // but on the other hand we have higher precision - and can divide the overall result. - uint8 constant EXPONENTIAL_REWARD_FACTOR = 128; + uint256 constant SCALING_FACTOR_1E8 = 1e8; // 10^8 for 8 decimal places = 1 TKO token + uint256 constant SCALING_FACTOR_1E18 = 1e18; // For fixed point representation factor error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -72,117 +62,26 @@ library LibL1Tokenomics { function getBlockFee( TaikoData.State storage state, - TaikoData.Config memory config - ) - internal - view - returns (uint64 newFeeBase, uint64 fee, uint64 depositAmount) - { - if (state.numBlocks <= config.constantFeeRewardBlocks) { - fee = state.feeBase; - newFeeBase = state.feeBase; - } else { - (newFeeBase, ) = getTimeAdjustedFee({ - feeConfig: config.proposingConfig, - feeBase: state.feeBase, - isProposal: true, - timeUsed: block.timestamp - state.lastProposedAt, - timeAverage: state.avgBlockTime - }); - fee = getSlotsAdjustedFee({ - state: state, - config: config, - isProposal: true, - feeBase: newFeeBase - }); - } - - unchecked { - depositAmount = uint64((fee * config.proposerDepositPctg) / 100); - } + uint32 gasUsed + ) internal view returns (uint64 newFeeBase, uint64 fee) { + newFeeBase = fee = state.baseFeeProof; + fee = newFeeBase * gasUsed; } function getProofReward( TaikoData.State storage state, TaikoData.Config memory config, uint64 provenAt, - uint64 proposedAt - ) - internal - view - returns (uint64 newFeeBase, uint64 reward, uint64 premiumRate) - { - if (proposedAt > provenAt) revert L1_INVALID_PARAM(); - - if (state.lastVerifiedBlockId <= config.constantFeeRewardBlocks) { - reward = state.feeBase; - newFeeBase = state.feeBase; - // premiumRate = 0; - } else { - (newFeeBase, premiumRate) = getTimeAdjustedFee({ - feeConfig: config.provingConfig, - feeBase: state.feeBase, - isProposal: false, - timeUsed: provenAt - proposedAt, - timeAverage: state.avgProofTime - }); - reward = getSlotsAdjustedFee({ - state: state, - config: config, - isProposal: false, - feeBase: newFeeBase - }); - } - unchecked { - reward = uint64((reward * (10000 - config.rewardBurnBips)) / 10000); - } - } - - // Implement "Slot-availability Multipliers", see the whitepaper. - function getSlotsAdjustedFee( - TaikoData.State storage state, - TaikoData.Config memory config, - bool isProposal, - uint64 feeBase - ) internal view returns (uint64) { - unchecked { - // m is the `n'` in the whitepaper - uint256 m = 1000 * - config.maxNumProposedBlocks + - config.slotSmoothingFactor; - // n is the number of unverified blocks - uint256 n = 1000 * - (state.numBlocks - state.lastVerifiedBlockId - 1); - // k is `m − n + 1` or `m − n - 1`in the whitepaper - uint256 k = isProposal ? m - n - 1000 : m - n + 1000; - return uint64((feeBase * (m - 1000) * m) / (m - n) / k); - } - } - - // Implement "Incentive Multipliers", see the whitepaper. - function getTimeAdjustedFee( - TaikoData.FeeConfig memory feeConfig, - uint64 feeBase, - bool isProposal, - uint256 timeUsed, // seconds - uint256 timeAverage // milliseconds - ) internal pure returns (uint64 newFeeBase, uint64 premiumRate) { - if (timeAverage == 0) { - return (feeBase, 0); - } - unchecked { - uint256 p = feeConfig.dampingFactorBips; // [0-10000] - uint256 a = timeAverage; - uint256 t = (timeUsed * 1000).min(a * 2); // millisconds - - newFeeBase = uint64((feeBase * (10000 + (t * p) / a - p)) / 10000); - - if (isProposal) { - newFeeBase = (feeBase * 2) - newFeeBase; - } else if (p > 0) { - premiumRate = uint64(((t.max(a) - a) * 10000) / a); - } - } + uint64 proposedAt, + uint32 usedGas + ) internal view returns (uint64 newFeeBase, uint256 reward) { + (reward, , newFeeBase) = calculateBaseProof( + state.proofTimeIssued, + config.proofTimeTarget, + (provenAt - proposedAt), + usedGas, + config.adjustmentQuotient + ); } /// @notice Update the baseFee for proofs @@ -199,46 +98,31 @@ library LibL1Tokenomics { uint8 quotient ) internal - view + pure returns ( uint256 reward, uint256 newProofTimeIssued, uint64 newBaseFeeProof ) { - console2.log("usedGas is: ", usedGas); - console2.log("cumulativeProofTime is: ", cumulativeProofTime); - - console2.log("proofTimeTarget is: ", proofTimeTarget); - console2.log("actProofTime is: ", actProofTime); - if (proofTimeTarget == 0) { - // Let's discuss proper value, but make it 20 seconds first time user, so to - // be realistic a little bit while also avoid division by zero - proofTimeTarget = 20000; - } // To protect underflow - newProofTimeIssued = (cumulativeProofTime > proofTimeTarget) + cumulativeProofTime = (cumulativeProofTime > proofTimeTarget) ? cumulativeProofTime - proofTimeTarget : uint256(0); - newProofTimeIssued += actProofTime; - - console2.log("Increased cumulativeProofTime is: ", newProofTimeIssued); + cumulativeProofTime += actProofTime; - newBaseFeeProof = baseFee( - newProofTimeIssued / 1000, //Hence in milliseconds - proofTimeTarget / 1000, //Hence in milliseconds - quotient + newBaseFeeProof = uint64( + baseFee(cumulativeProofTime, proofTimeTarget, quotient) / + SCALING_FACTOR_1E18 ); reward = ((newBaseFeeProof * usedGas) * - ((actProofTime * SCALING_FACTOR) / proofTimeTarget)) / - (SCALING_FACTOR * EXPONENTIAL_REWARD_FACTOR); + ((actProofTime * SCALING_FACTOR_1E8) / proofTimeTarget)) / + SCALING_FACTOR_1E8; - console2.log("All cumulativeProofTime: ", newProofTimeIssued); - console2.log("New newBaseFeeProof: ", newBaseFeeProof); - console2.log("Reward: ", reward); + newProofTimeIssued = cumulativeProofTime; } /// @notice Calculating the exponential smoothened with (target/quotient) @@ -249,13 +133,11 @@ library LibL1Tokenomics { uint256 value, uint256 target, uint256 quotient - ) internal view returns (uint64) { - // If SCALING_FACTOR not applied : target * quotient will be bigger, therefore the result is 0 - return - uint64( - ((expCalculation(value, target, quotient) * SCALING_FACTOR) / - (target * quotient)) - ); + ) internal pure returns (uint256) { + return ( + ((expCalculation(value, target, quotient) * SCALING_FACTOR_1E8) / + (target * quotient)) + ); } /// @notice Calculating the exponential via ABDKMath64x64 @@ -266,23 +148,13 @@ library LibL1Tokenomics { uint256 value, uint256 target, uint256 quotient - ) internal view returns (uint64 retVal) { - console2.log("----In expCalculation() of LibL1Tokenomics.sol -----"); - // console2.log("value: ", value); - // console2.log("target: ", target); - // console2.log("quotient: ", quotient); - - int128 valueInt128 = ABDKMath64x64.exp( - ABDKMath64x64.divu(value, target * quotient) - ); - - unchecked { - if (!(valueInt128 >= 0)) { - revert L1_IMPOSSIBLE_CONVERSION(); - } - - //On purpose downshift/divide only 2^57, so that we have higher precisions!!! - retVal = uint64(uint128(valueInt128 >> 57)); - } + ) internal pure returns (uint256 retVal) { + // Overflow handled by the code + return + uint256( + Math.exp( + int256((value * SCALING_FACTOR_1E18) / (target * quotient)) + ) + ); } } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 3eea8647802..19767f7fee4 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -6,9 +6,6 @@ pragma solidity ^0.8.18; -import {Test} from "forge-std/Test.sol"; -import {console2} from "forge-std/console2.sol"; - import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibAddress} from "../../libs/LibAddress.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; @@ -25,8 +22,6 @@ library LibProposing { using LibAddress for address payable; using LibUtils for TaikoData.State; - uint256 constant SCALING_FACTOR = 1e8; // 10^8 for 8 decimal places = 1 TKO token - event BlockProposed( uint256 indexed id, TaikoData.BlockMetadata meta, @@ -112,34 +107,20 @@ library LibProposing { blk.verifiedForkChoiceId = 0; blk.metaHash = LibUtils.hashMetadata(meta); blk.proposer = msg.sender; - blk.gasLimit = input.gasLimit; - - // Let's see if calculations are correct so first just - // debug them. (Curly braces due to stack too deep error) - console2.log("----------------------------------------------------"); - console2.log("------------------In proposeBlock()-----------------"); - { - // Need to scale 'down', because baseFeeProof is scaled 'up' - // Todo: Could you please Brecht advise on the math ? - // So since in baseFee() function i needed to scale up, otherwise divison with zero - // somewhere we need to scale down.. - uint256 feeNew = (input.gasLimit * - state.baseFeeProof); /* / SCALING_FACTOR; (??) */ - - console2.log("proposer_fee:", feeNew); - console2.log("baseFeeProof:", state.baseFeeProof); - } + // Later on we might need to have actual gas consumed in that L2 block + blk.gasConsumed = input.gasLimit; if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 fee, uint64 deposit) = LibL1Tokenomics - .getBlockFee(state, config); + (uint256 newFeeBase, uint256 fee) = LibL1Tokenomics.getBlockFee( + state, + input.gasLimit + ); - uint256 burnAmount = fee + deposit; - if (state.balances[msg.sender] < burnAmount) + if (state.balances[msg.sender] < fee) revert L1_INSUFFICIENT_TOKEN(); unchecked { - state.balances[msg.sender] -= burnAmount; + state.balances[msg.sender] -= fee; } // Update feeBase and avgBlockTime @@ -150,8 +131,6 @@ library LibProposing { maf: config.feeBaseMAF }) .toUint64(); - - blk.deposit = uint64(deposit); } unchecked { diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index a2c026644d2..d9e81160e07 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -6,9 +6,6 @@ pragma solidity ^0.8.18; -import {Test} from "forge-std/Test.sol"; -import {console2} from "forge-std/console2.sol"; - import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; @@ -120,64 +117,35 @@ library LibVerifying { TaikoData.ForkChoice storage fc, uint24 fcId ) private returns (bytes32 blockHash, bytes32 signalRoot) { - if (config.enableTokenomics) { - ( - uint256 newFeeBase, - uint256 amount, - uint256 premiumRate - ) = LibL1Tokenomics.getProofReward({ - state: state, - config: config, - provenAt: fc.provenAt, - proposedAt: blk.proposedAt - }); - - // reward the prover - _addToBalance(state, fc.prover, amount); - - unchecked { - // premiumRate in [0-10000] - amount = (blk.deposit * (10000 - premiumRate)) / 10000; - } - _addToBalance(state, blk.proposer, amount); - - // Update feeBase and avgProofTime - state.feeBase = LibUtils - .movingAverage({ - maValue: state.feeBase, - newValue: newFeeBase, - maf: config.feeBaseMAF - }) - .toUint64(); - } - uint256 proofTime; unchecked { - proofTime = (fc.provenAt - blk.proposedAt) * 1000; + proofTime = (fc.provenAt - blk.proposedAt); } - console2.log("----------------------------------------------------"); - console2.log("------------------In verifyBlock()------------------"); - - ( - uint256 reward, - uint256 proofTimeIssued, - uint64 newBaseFeeProof - ) = LibL1Tokenomics.calculateBaseProof( - state.proofTimeIssued, - state.avgProofTime, - uint64(proofTime), - blk.gasLimit, - config.adjustmentQuotient - ); + if (config.enableTokenomics) { + ( + uint256 reward, + uint256 proofTimeIssued, + uint64 newBaseFeeProof + ) = LibL1Tokenomics.calculateBaseProof( + state.proofTimeIssued, + config.proofTimeTarget, + uint64(proofTime), // in milliseconds + blk.gasConsumed, + config.adjustmentQuotient + ); + + state.baseFeeProof = newBaseFeeProof; + state.proofTimeIssued = proofTimeIssued; - state.baseFeeProof = newBaseFeeProof; - state.proofTimeIssued = proofTimeIssued; + // reward the prover + _addToBalance(state, fc.prover, reward); + } state.avgProofTime = LibUtils .movingAverage({ maValue: state.avgProofTime, - newValue: proofTime, + newValue: proofTime * 1000, maf: config.provingConfig.avgTimeMAF }) .toUint64(); diff --git a/packages/protocol/contracts/libs/LibTrieProof_explained.bak b/packages/protocol/contracts/libs/LibTrieProof_explained.bak new file mode 100644 index 00000000000..8f046cde2ae --- /dev/null +++ b/packages/protocol/contracts/libs/LibTrieProof_explained.bak @@ -0,0 +1,65 @@ +/** + * Verifies that the value of a slot in the storage tree of `addr` + * is `value`. + * + * @param stateRoot The merkle root of state tree. + * @param addr The contract address. + * @param slot The slot in the contract. + * @param value The value to be verified. + * @param mkproof The proof obtained by encoding state proof and storage + * proof. + * @return verified The verification result. + */ +function verify( + bytes32 stateRoot, + address addr, + bytes32 slot, + bytes32 value, + bytes calldata mkproof +) public pure returns (bool verified) { + // Decode the `mkproof` parameter into separate `accountProof` and `storageProof` byte arrays. + (bytes memory accountProof, bytes memory storageProof) = abi.decode( + mkproof, + (bytes, bytes) + ); + + // Retrieve the RLP-encoded account state for the specified address using the `get` function from the `LibSecureMerkleTrie` library. + //Megnézi hogy a 'world' stateRoot-ból ki tudja-e szedni az RLP-encoded account-t. + (bool exists, bytes memory rlpAccount) = LibSecureMerkleTrie.get( + abi.encodePacked(addr), + accountProof, + stateRoot + ); + + + // Example contract accountProof: + // { + // nonce: 1, + // balance: 1000, + // codeHash: 0x123456789abcdef, + // storageHash: 0x987654321fedcba + // } + // Example of a contracts storageProof: + // [ "0x", [ { "key": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "value": { "nonce": "0x1", "balance": "0x0", "storageHash": "0xc0a49e08e1c8f3b3de04ce4fa4eeb8071594da124f92da6da4fa5b5ce5ddc8cc", "codeHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } } ], + // [ { "key": "0x1234", "value": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" } ] + // ] + + // Check that the account exists in the state tree. + require(exists, "LTP:invalid account proof"); + + // Parse the RLP-encoded account state to extract the storage root hash. + LibRLPReader.RLPItem[] memory accountState = LibRLPReader.readList( + rlpAccount + ); + bytes32 storageRoot = LibRLPReader.readBytes32( + accountState[ACCOUNT_FIELD_INDEX_STORAGE_HASH] + ); + + // Verify the inclusion of the specified slot and value in the storage tree using the `verifyInclusionProof` function from the `LibSecureMerkleTrie` library. + verified = LibSecureMerkleTrie.verifyInclusionProof( + abi.encodePacked(slot), + LibRLPWriter.writeBytes32(value), + storageProof, + storageRoot + ); +} diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index b631fed880f..02d8b783e7c 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -29,7 +29,6 @@ contract TestTaikoL1 is TaikoL1 { config.minTxGasLimit = 21000; config.slotSmoothingFactor = 590000; config.rewardBurnBips = 100; // 100 basis points or 1% - config.proposerDepositPctg = 25; // 25% config.enableTokenomics = false; config.skipZKPVerification = true; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 8d55b35a6e4..0517f4167f6 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -29,7 +29,6 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { config.minTxGasLimit = 21000; config.slotSmoothingFactor = 590000; config.rewardBurnBips = 100; // 100 basis points or 1% - config.proposerDepositPctg = 25; // 25% // Moving average factors config.feeBaseMAF = 1024; diff --git a/packages/protocol/contracts/thirdparty/ABDKMath.sol b/packages/protocol/contracts/thirdparty/ABDKMath64x64_sol_original_name.bak similarity index 100% rename from packages/protocol/contracts/thirdparty/ABDKMath.sol rename to packages/protocol/contracts/thirdparty/ABDKMath64x64_sol_original_name.bak diff --git a/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol new file mode 100644 index 00000000000..b3a3eeb59e8 --- /dev/null +++ b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: UNLICENSED +// Taken from: https://github.com/recmo/experiment-solexp/blob/main/src/FixedPointMathLib.sol +pragma solidity ^0.8.18; + +library LibFixedPointMath { + error Overflow(); + error LnNegativeUndefined(); + + // Integer log2 + // @returns floor(log2(x)) if x is nonzero, otherwise 0. This is the same + // as the location of the highest set bit. + // Consumes 232 gas. This could have been an 3 gas EVM opcode though. + function ilog2(uint256 x) internal pure returns (uint256 r) { + assembly { + r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) + r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) + r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) + r := or(r, shl(4, lt(0xffff, shr(r, x)))) + r := or(r, shl(3, lt(0xff, shr(r, x)))) + r := or(r, shl(2, lt(0xf, shr(r, x)))) + r := or(r, shl(1, lt(0x3, shr(r, x)))) + r := or(r, lt(0x1, shr(r, x))) + } + } + + // Computes ln(x) in 1e18 fixed point. + // Reverts if x is negative or zero. + // Consumes 670 gas. + function ln(int256 x) internal pure returns (int256 r) { + unchecked { + if (x < 1) { + if (x < 0) revert LnNegativeUndefined(); + revert Overflow(); + } + + // We want to convert x from 10**18 fixed point to 2**96 fixed point. + // We do this by multiplying by 2**96 / 10**18. + // But since ln(x * C) = ln(x) + ln(C), we can simply do nothing here + // and add ln(2**96 / 10**18) at the end. + + // Reduce range of x to (1, 2) * 2**96 + // ln(2^k * x) = k * ln(2) + ln(x) + // Note: inlining ilog2 saves 8 gas. + int256 k = int256(ilog2(uint256(x))) - 96; + x <<= uint256(159 - k); + x = int256(uint256(x) >> 159); + + // Evaluate using a (8, 8)-term rational approximation + // p is made monic, we will multiply by a scale factor later + int256 p = x + 3273285459638523848632254066296; + p = ((p * x) >> 96) + 24828157081833163892658089445524; + p = ((p * x) >> 96) + 43456485725739037958740375743393; + p = ((p * x) >> 96) - 11111509109440967052023855526967; + p = ((p * x) >> 96) - 45023709667254063763336534515857; + p = ((p * x) >> 96) - 14706773417378608786704636184526; + p = p * x - (795164235651350426258249787498 << 96); + //emit log_named_int("p", p); + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + // q is monic by convention + int256 q = x + 5573035233440673466300451813936; + q = ((q * x) >> 96) + 71694874799317883764090561454958; + q = ((q * x) >> 96) + 283447036172924575727196451306956; + q = ((q * x) >> 96) + 401686690394027663651624208769553; + q = ((q * x) >> 96) + 204048457590392012362485061816622; + q = ((q * x) >> 96) + 31853899698501571402653359427138; + q = ((q * x) >> 96) + 909429971244387300277376558375; + assembly { + // Div in assembly because solidity adds a zero check despite the `unchecked`. + // The q polynomial is known not to have zeros in the domain. (All roots are complex) + // No scaling required because p is already 2**96 too large. + r := sdiv(p, q) + } + // r is in the range (0, 0.125) * 2**96 + + // Finalization, we need to + // * multiply by the scale factor s = 5.549… + // * add ln(2**96 / 10**18) + // * add k * ln(2) + // * multiply by 10**18 / 2**96 = 5**18 >> 78 + // mul s * 5e18 * 2**96, base is now 5**18 * 2**192 + r *= 1677202110996718588342820967067443963516166; + // add ln(2) * k * 5e18 * 2**192 + r += + 16597577552685614221487285958193947469193820559219878177908093499208371 * + k; + // add ln(2**96 / 10**18) * 5e18 * 2**192 + r += 600920179829731861736702779321621459595472258049074101567377883020018308; + // base conversion: mul 2**18 / 2**192 + r >>= 174; + } + } + + // Computes e^x in 1e18 fixed point. + function exp(int256 x) internal pure returns (int256 r) { + unchecked { + // Input x is in fixed point format, with scale factor 1/1e18. + + // When the result is < 0.5 we return zero. This happens when + // x <= floor(log(0.5e18) * 1e18) ~ -42e18 + if (x <= -42139678854452767551) { + return 0; + } + + // When the result is > (2**255 - 1) / 1e18 we can not represent it + // as an int256. This happens when x >= floor(log((2**255 -1) / 1e18) * 1e18) ~ 135. + if (x >= 135305999368893231589) revert Overflow(); + + // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 + // for more intermediate precision and a binary basis. This base conversion + // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. + x = (x << 78) / 5 ** 18; + + // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers of two + // such that exp(x) = exp(x') * 2**k, where k is an integer. + // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). + int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> + 96; + x = x - k * 54916777467707473351141471128; + // k is in the range [-61, 195]. + + // Evaluate using a (6, 7)-term rational approximation + // p is made monic, we will multiply by a scale factor later + int256 p = x + 2772001395605857295435445496992; + p = ((p * x) >> 96) + 44335888930127919016834873520032; + p = ((p * x) >> 96) + 398888492587501845352592340339721; + p = ((p * x) >> 96) + 1993839819670624470859228494792842; + p = p * x + (4385272521454847904632057985693276 << 96); + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + // Evaluate using using Knuth's scheme from p. 491. + int256 z = x + 750530180792738023273180420736; + z = ((z * x) >> 96) + 32788456221302202726307501949080; + int256 w = x - 2218138959503481824038194425854; + w = ((w * z) >> 96) + 892943633302991980437332862907700; + int256 q = z + w - 78174809823045304726920794422040; + q = ((q * w) >> 96) + 4203224763890128580604056984195872; + assembly { + // Div in assembly because solidity adds a zero check despite the `unchecked`. + // The q polynomial is known not to have zeros in the domain. (All roots are complex) + // No scaling required because p is already 2**96 too large. + r := sdiv(p, q) + } + // r should be in the range (0.09, 0.25) * 2**96. + + // We now need to multiply r by + // * the scale factor s = ~6.031367120..., + // * the 2**k factor from the range reduction, and + // * the 1e18 / 2**96 factor for base converison. + // We do all of this at once, with an intermediate result in 2**213 basis + // so the final right shift is always by a positive amount. + r = int( + (uint(r) * 3822833074963236453042738258902158003155416615667) >> + uint256(195 - k) + ); + } + } +} diff --git a/packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak b/packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak new file mode 100644 index 00000000000..e735134f1cb --- /dev/null +++ b/packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * @author Hamdi Allam hamdi.allam97@gmail.com + * Please reach out with any questions or concerns + */ +pragma solidity ^0.8.18; + +library LibRLPReader { + uint8 constant STRING_SHORT_START = 0x80; + uint8 constant STRING_LONG_START = 0xb8; + uint8 constant LIST_SHORT_START = 0xc0; + uint8 constant LIST_LONG_START = 0xf8; + uint8 constant WORD_SIZE = 32; + + enum RLPItemType { + DATA_ITEM, + LIST_ITEM + } + + struct RLPItem { + uint256 length; + uint256 ptr; + } + + struct Iterator { + RLPItem item; // Item that's being iterated over. + uint256 nextPtr; // Position of the next item in the list. + } + + /* + * @dev Returns the next element in the iteration. Reverts if it has not next element. + * @param self The iterator. + * @return The next element in the iteration. + */ + function next(Iterator memory self) internal pure returns (RLPItem memory) { + require(hasNext(self)); + + uint256 ptr = self.nextPtr; + uint256 itemLength = _itemLength(ptr); + self.nextPtr = ptr + itemLength; + + return RLPItem(itemLength, ptr); + } + + /* + * @dev Returns true if the iteration has more elements. + * @param self The iterator. + * @return true if the iteration has more elements. + */ + function hasNext(Iterator memory self) internal pure returns (bool) { + RLPItem memory item = self.item; + return self.nextPtr < item.ptr + item.length; + } + + /* + * @param item RLP encoded bytes + */ + function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { + uint256 ptr; + assembly { + ptr := add(item, 0x20) + } + + return RLPItem(item.length, ptr); + } + + /* + * @dev Create an iterator. Reverts if item is not a list. + * @param self The RLP item. + * @return An 'Iterator' over the item. + */ + function iterator(RLPItem memory self) internal pure returns (Iterator memory) { + require(isList(self)); + + uint256 ptr = self.ptr + _payloadOffset(self.ptr); + return Iterator(self, ptr); + } + + /* + * @param the RLP item. + */ + function rlpLen(RLPItem memory item) internal pure returns (uint256) { + return item.length; + } + + /* + * @param the RLP item. + * @return (ptr, length) pair: location of the item's payload in memory. + */ + function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { + uint256 offset = _payloadOffset(item.ptr); + uint256 ptr = item.ptr + offset; + uint256 length = item.length - offset; // data length + return (ptr, length); + } + + /* + * @param the RLP item. + */ + function payloadLen(RLPItem memory item) internal pure returns (uint256) { + (, uint256 length) = payloadLocation(item); + return length; + } + + /** + * Reads an RLP string value into a string. + * @param _in RLP string value. + * @return Decoded string. + */ + function readString( + RLPItem memory _in + ) internal pure returns (string memory) { + return string(readBytes(_in)); + } + + /** + * Reads an RLP string value into a string. + * @param _in RLP string value. + * @return Decoded string. + */ + function readString( + bytes memory _in + ) internal pure returns (string memory) { + return readString(toRlpItem(_in)); + } + + function readList( + bytes memory _in + ) internal pure returns (RLPItem[] memory) { + return readList(toRlpItem(_in)); + } + + /* + * @param the RLP item containing the encoded list. + */ + // function readList(RLPItem memory item) internal pure returns (RLPItem[] memory) { + // require(isList(item)); + + // uint256 items = numItems(item); + // RLPItem[] memory result = new RLPItem[](items); + + // uint256 ptr = item.ptr + _payloadOffset(item.ptr); + // uint256 dataLen; + // for (uint256 i = 0; i < items; i++) { + // dataLen = _itemLength(ptr); + // result[i] = RLPItem(dataLen, ptr); + // ptr = ptr + dataLen; + // } + + // return result; + // } + function readList( + RLPItem memory _in + ) internal pure returns (RLPItem[] memory) { + (uint256 listOffset, , RLPItemType itemType) = _decodeLength(_in); + + require(itemType == RLPItemType.LIST_ITEM, "Invalid RLP list value."); + + // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by + // writing to the length. Since we can't know the number of RLP items without looping over + // the entire input, we'd have to loop twice to accurately size this array. It's easier to + // simply set a reasonable maximum list length and decrease the size before we finish. + RLPItem[] memory out = new RLPItem[](WORD_SIZE); + + uint256 itemCount; + uint256 offset = listOffset; + while (offset < _in.length) { + require( + itemCount < WORD_SIZE, + "Provided RLP list exceeds max list length." + ); + + (uint256 itemOffset, uint256 itemLength, ) = _decodeLength( + RLPItem({length: _in.length - offset, ptr: _in.ptr + offset}) + ); + + out[itemCount] = RLPItem({ + length: itemLength + itemOffset, + ptr: _in.ptr + offset + }); + + itemCount += 1; + offset += itemOffset + itemLength; + } + + // Decrease the array size to match the actual item count. + assembly { + mstore(out, itemCount) + } + + return out; + } + + // @return indicator whether encoded payload is a list. negate this function call for isData. + function isList(RLPItem memory item) internal pure returns (bool) { + if (item.length == 0) return false; + + uint8 byte0; + uint256 ptr = item.ptr; + assembly { + byte0 := byte(0, mload(ptr)) + } + + if (byte0 < LIST_SHORT_START) return false; + return true; + } + + /* + * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. + * @return keccak256 hash of RLP encoded bytes. + */ + function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { + uint256 ptr = item.ptr; + uint256 length = item.length; + bytes32 result; + assembly { + result := keccak256(ptr, length) + } + return result; + } + + /* + * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. + * @return keccak256 hash of the item payload. + */ + function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { + (uint256 ptr, uint256 length) = payloadLocation(item); + bytes32 result; + assembly { + result := keccak256(ptr, length) + } + return result; + } + + /** RLPItem conversions into data types **/ + // @returns raw rlp encoding in bytes + function readRawBytes(RLPItem memory item) internal pure returns (bytes memory) { + bytes memory result = new bytes(item.length); + if (result.length == 0) return result; + + uint256 ptr; + assembly { + ptr := add(0x20, result) + } + + copy(item.ptr, ptr, item.length); + return result; + } + + function readBytes(bytes memory _in) internal pure returns (bytes memory) { + return readBytes(toRlpItem(_in)); + } + + // function readBytes(RLPItem memory item) internal pure returns (bytes memory) { + // require(item.length > 0); + + // (uint256 ptr, uint256 length) = payloadLocation(item); + // bytes memory result = new bytes(length); + + // uint256 destPtr; + // assembly { + // destPtr := add(0x20, result) + // } + + // copy(ptr, destPtr, length); + // return result; + // } + function readBytes( + RLPItem memory _in + ) internal pure returns (bytes memory) { + ( + uint256 itemOffset, + uint256 itemLength, + RLPItemType itemType + ) = _decodeLength(_in); + + require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes value."); + + return _copy(_in.ptr, itemOffset, itemLength); + } + + function readBytes32(bytes memory _in) internal pure returns (bytes32) { + return readBytes32(toRlpItem(_in)); + } + + function readBytes32(RLPItem memory item) internal pure returns (bytes32) { + require(item.length <= 33); + + (uint256 ptr,) = payloadLocation(item); + bytes32 result; + uint256 destPtr; + assembly { + destPtr := add(0x20, result) + } + + copy(ptr, destPtr, 32); + return result; + } + + function readBool(bytes memory _in) internal pure returns (bool) { + return readBool(toRlpItem(_in)); + } + + // any non-zero byte except "0x80" is considered true + function readBool(RLPItem memory item) internal pure returns (bool) { + require(item.length == 1); + uint256 result; + uint256 ptr = item.ptr; + assembly { + result := byte(0, mload(ptr)) + } + + // SEE Github Issue #5. + // Summary: Most commonly used RLP libraries (i.e Geth) will encode + // "0" as "0x80" instead of as "0". We handle this edge case explicitly + // here. + if (result == 0 || result == STRING_SHORT_START) { + return false; + } else { + return true; + } + } + + function readAddress(bytes memory _in) internal pure returns (address) { + return readAddress(toRlpItem(_in)); + } + + function readAddress(RLPItem memory item) internal pure returns (address) { + // 1 byte for the length prefix + require(item.length == 21); + + return address(uint160(readUint256(item))); + } + + /** + * Reads an RLP uint256 value into a uint256. + * @param _in RLP uint256 value. + * @return Decoded uint256. + */ + function readUint256(bytes memory _in) internal pure returns (uint256) { + return readUint256(toRlpItem(_in)); + } + + function readUint256(RLPItem memory item) internal pure returns (uint256) { + require(item.length > 0 && item.length <= 33); + + (uint256 ptr, uint256 length) = payloadLocation(item); + + uint256 result; + assembly { + result := mload(ptr) + + // shift to the correct location if neccesary + if lt(length, 32) { + result := div(result, exp(256, sub(32, length))) + } + } + + return result; + } + + // enforces 32 byte length + function toUintStrict(RLPItem memory item) internal pure returns (uint256) { + // one byte prefix + require(item.length == 33); + + uint256 result; + uint256 ptr = item.ptr + 1; + assembly { + result := mload(ptr) + } + + return result; + } + + + /* + * Private Helpers + */ + + // @return number of payload items inside an encoded list. + function numItems(RLPItem memory item) private pure returns (uint256) { + if (item.length == 0) return 0; + + uint256 count = 0; + uint256 currPtr = item.ptr + _payloadOffset(item.ptr); + uint256 endPtr = item.ptr + item.length; + while (currPtr < endPtr) { + currPtr = currPtr + _itemLength(currPtr); // skip over an item + count++; + } + + return count; + } + + // @return entire rlp item byte length + function _itemLength(uint256 ptr) private pure returns (uint256) { + uint256 itemLen; + uint256 byte0; + assembly { + byte0 := byte(0, mload(ptr)) + } + + if (byte0 < STRING_SHORT_START) { + itemLen = 1; + } else if (byte0 < STRING_LONG_START) { + itemLen = byte0 - STRING_SHORT_START + 1; + } else if (byte0 < LIST_SHORT_START) { + assembly { + let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + ptr := add(ptr, 1) // skip over the first byte + + /* 32 byte word size */ + let dataLen := div(mload(ptr), exp(256, sub(32, byteLen))) // right shifting to get the length + itemLen := add(dataLen, add(byteLen, 1)) + } + } else if (byte0 < LIST_LONG_START) { + itemLen = byte0 - LIST_SHORT_START + 1; + } else { + assembly { + let byteLen := sub(byte0, 0xf7) + ptr := add(ptr, 1) + + let dataLen := div(mload(ptr), exp(256, sub(32, byteLen))) // right shifting to the correct length + itemLen := add(dataLen, add(byteLen, 1)) + } + } + + return itemLen; + } + + // @return number of bytes until the data + function _payloadOffset(uint256 ptr) private pure returns (uint256) { + uint256 byte0; + assembly { + byte0 := byte(0, mload(ptr)) + } + + if (byte0 < STRING_SHORT_START) { + return 0; + } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { + return 1; + } else if (byte0 < LIST_SHORT_START) { + // being explicit + return byte0 - (STRING_LONG_START - 1) + 1; + } else { + return byte0 - (LIST_LONG_START - 1) + 1; + } + } + + /* + * @param src Pointer to source + * @param dest Pointer to destination + * @param length Amount of memory to copy from the source + */ + function copy(uint256 src, uint256 dest, uint256 length) private pure { + if (length == 0) return; + + // copy as many word sizes as possible + for (; length >= WORD_SIZE; length -= WORD_SIZE) { + assembly { + mstore(dest, mload(src)) + } + + src += WORD_SIZE; + dest += WORD_SIZE; + } + + if (length > 0) { + // left over bytes. Mask is used to remove unwanted bytes from the word + uint256 mask = 256**(WORD_SIZE - length) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) // zero out src + let destpart := and(mload(dest), mask) // retrieve the bytes + mstore(dest, or(destpart, srcpart)) + } + } + } + + /** + * Decodes the length of an RLP item. + * @param _in RLP item to decode. + * @return Offset of the encoded data. + * @return Length of the encoded data. + * @return RLP item type (LIST_ITEM or DATA_ITEM). + */ + function _decodeLength( + RLPItem memory _in + ) private pure returns (uint256, uint256, RLPItemType) { + require(_in.length > 0, "RLP item cannot be null."); + + uint256 ptr = _in.ptr; + uint256 prefix; + assembly { + prefix := byte(0, mload(ptr)) + } + + if (prefix <= 0x7f) { + // Single byte. + + return (0, 1, RLPItemType.DATA_ITEM); + } else if (prefix <= 0xb7) { + // Short string. + + // slither-disable-next-line variable-scope + uint256 strLen = prefix - 0x80; + + require(_in.length > strLen, "Invalid RLP short string."); + + return (1, strLen, RLPItemType.DATA_ITEM); + } else if (prefix <= 0xbf) { + // Long string. + uint256 lenOfStrLen = prefix - 0xb7; + + require( + _in.length > lenOfStrLen, + "Invalid RLP long string length." + ); + + uint256 strLen; + assembly { + // Pick out the string length. + strLen := div( + mload(add(ptr, 1)), + exp(256, sub(32, lenOfStrLen)) + ) + } + + require( + _in.length > lenOfStrLen + strLen, + "Invalid RLP long string." + ); + + return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM); + } else if (prefix <= 0xf7) { + // Short list. + // slither-disable-next-line variable-scope + uint256 listLen = prefix - 0xc0; + + require(_in.length > listLen, "Invalid RLP short list."); + + return (1, listLen, RLPItemType.LIST_ITEM); + } else { + // Long list. + uint256 lenOfListLen = prefix - 0xf7; + + require(_in.length > lenOfListLen, "Invalid RLP long list length."); + + uint256 listLen; + assembly { + // Pick out the list length. + listLen := div( + mload(add(ptr, 1)), + exp(256, sub(32, lenOfListLen)) + ) + } + + require( + _in.length > lenOfListLen + listLen, + "Invalid RLP long list." + ); + + return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM); + } + } + + function _copy( + uint256 _src, + uint256 _offset, + uint256 _length + ) internal pure returns (bytes memory) { + bytes memory result = new bytes(_length); + if (result.length == 0) { + return result; + } + + bytes memory src; + bytes memory dst; + assembly { + src := add(_src, _offset) + + dst := add(result, 32) + + for { + let i := 0 + } lt(i, _length) { + i := add(i, 32) + } { + mstore(add(dst, i), mload(add(src, i))) + } + } + + // Pick out the remaining bytes. + uint256 mask; + unchecked { + mask = 256 ** (32 - (_length % 32)) - 1; + } + + assembly { + mstore(dst, or(and(mload(src), not(mask)), and(mload(dst), mask))) + } + + return result; + } +} diff --git a/packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak b/packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak new file mode 100644 index 00000000000..58934a53c16 --- /dev/null +++ b/packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: MIT +// Taken from https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/rlp/LibRLPWriter.sol +// Modified to support writeBytes32/writeUint64 +// (The MIT License) +// +// Copyright 2020-2021 Optimism +// Copyright 2022-2023 Taiko Labs +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +pragma solidity ^0.8.18; + +/** + * @title LibRLPWriter + * @author Bakaoh (with modifications) + */ +library LibRLPWriter { + /********************** + * Internal Functions * + **********************/ + + /** + * RLP encodes a byte string. + * @param _in The byte string to encode. + * @return The RLP encoded string in bytes. + */ + function writeBytes(bytes memory _in) internal pure returns (bytes memory) { + bytes memory encoded; + + if (_in.length == 1 && uint8(_in[0]) < 128) { + encoded = _in; + } else { + encoded = abi.encodePacked(_writeLength(_in.length, 128), _in); + } + + return encoded; + } + + /** + * RLP encodes a list of RLP encoded byte byte strings. + * @param _in The list of RLP encoded byte strings. + * @return The RLP encoded list of items in bytes. + */ + function writeList( + bytes[] memory _in + ) internal pure returns (bytes memory) { + bytes memory list = _flatten(_in); + return abi.encodePacked(_writeLength(list.length, 192), list); + } + + /** + * RLP encodes a string. + * @param _in The string to encode. + * @return The RLP encoded string in bytes. + */ + function writeString( + string memory _in + ) internal pure returns (bytes memory) { + return writeBytes(bytes(_in)); + } + + /** + * RLP encodes an address. + * @param _in The address to encode. + * @return The RLP encoded address in bytes. + */ + function writeAddress(address _in) internal pure returns (bytes memory) { + return writeBytes(abi.encodePacked(_in)); + } + + /** + * RLP encodes a uint. + * @param _in The uint256 to encode. + * @return The RLP encoded uint256 in bytes. + */ + function writeUint(uint256 _in) internal pure returns (bytes memory) { + return writeBytes(_toBinary(_in)); + } + + function writeBytes32(bytes32 _in) internal pure returns (bytes memory) { + return writeBytes(_toBinary(uint256(_in))); + } + + /** + * RLP encodes a hash, we should use this function but not writeBytes32 to + * encode a hash, since writeBytes32 will remove the leading zeros of the + * given bytes. + * @param _in The hash to encode. + * @return The RLP encoded hash in bytes. + */ + function writeHash(bytes32 _in) internal pure returns (bytes memory) { + return writeBytes(_toBinaryWithLeadingZeros(uint256(_in))); + } + + function writeUint64(uint64 _in) internal pure returns (bytes memory) { + return writeBytes(_toBinary(_in)); + } + + /** + * RLP encodes a bool. + * @param _in The bool to encode. + * @return The RLP encoded bool in bytes. + */ + function writeBool(bool _in) internal pure returns (bytes memory) { + bytes memory encoded = new bytes(1); + encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80)); + return encoded; + } + + /********************* + * Private Functions * + *********************/ + + /** + * Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * @param _len The length of the string or the payload. + * @param _offset 128 if item is string, 192 if item is list. + * @return RLP encoded bytes. + */ + function _writeLength( + uint256 _len, + uint256 _offset + ) private pure returns (bytes memory) { + bytes memory encoded; + + if (_len < 56) { + encoded = new bytes(1); + encoded[0] = bytes1(uint8(_len) + uint8(_offset)); + } else { + uint256 lenLen; + uint256 i = 1; + while (_len / i != 0) { + ++lenLen; + i *= 256; + } + + encoded = new bytes(lenLen + 1); + encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); + for (i = 1; i <= lenLen; ++i) { + encoded[i] = bytes1( + uint8((_len / (256 ** (lenLen - i))) % 256) + ); + } + } + + return encoded; + } + + /** + * Encode integer in big endian binary form with no leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return RLP encoded bytes. + */ + function _toBinary(uint256 _x) private pure returns (bytes memory) { + bytes memory b = abi.encodePacked(_x); + + uint256 i; + for (; i < 32; ++i) { + if (b[i] != 0) { + break; + } + } + + bytes memory res = new bytes(32 - i); + for (uint256 j; j < res.length; ++j) { + res[j] = b[i++]; + } + + return res; + } + + /** + * Encode integer in big endian binary form with leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return RLP encoded bytes. + */ + function _toBinaryWithLeadingZeros( + uint256 _x + ) private pure returns (bytes memory) { + bytes memory b = abi.encodePacked(_x); + + uint256 i; + + bytes memory res = new bytes(32); + for (uint256 j; j < res.length; ++j) { + res[j] = b[i++]; + } + + return res; + } + + /** + * Copies a piece of memory to another location. + * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. + * @param _dest Destination location. + * @param _src Source location. + * @param _len Length of memory to copy. + */ + function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure { + uint256 dest = _dest; + uint256 src = _src; + uint256 len = _len; + + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + uint256 mask; + unchecked { + mask = 256 ** (32 - len) - 1; + } + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /** + * Flattens a list of byte strings into one byte string. + * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. + * @param _list List of byte strings to flatten. + * @return The flattened byte string. + */ + function _flatten( + bytes[] memory _list + ) private pure returns (bytes memory) { + if (_list.length == 0) { + return new bytes(0); + } + + uint256 len; + uint256 i; + for (; i < _list.length; ++i) { + len += _list[i].length; + } + + bytes memory flattened = new bytes(len); + uint256 flattenedPtr; + assembly { + flattenedPtr := add(flattened, 0x20) + } + + for (i = 0; i < _list.length; ++i) { + bytes memory item = _list[i]; + + uint256 listPtr; + assembly { + listPtr := add(item, 0x20) + } + + _memcpy(flattenedPtr, listPtr, item.length); + flattenedPtr += _list[i].length; + } + + return flattened; + } +} diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 32e47ee422c..e7da16095b8 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -278,21 +278,6 @@ contract TestLibL1Tokenomics is Test { uint256 expectedFeeBase, uint256 expectedPreimumRate ) private { - TaikoData.FeeConfig memory feeConfig = TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: dampingFactorBips - }); - - (uint256 _feeBase, uint256 _premiumRate) = LibL1Tokenomics - .getTimeAdjustedFee( - feeConfig, - feeBase.toUint64(), - isProposal, - timeUsedSec, - timeAverageSec * 1000 - ); - - assertEq(_premiumRate, expectedPreimumRate); - assertEq(_feeBase, expectedFeeBase); + // Todo:Implement the verification tests } } diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index 49c6a267450..d309c286717 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -24,7 +24,6 @@ contract TaikoL1WithConfig is TaikoL1 { config.enableTokenomics = true; config.constantFeeRewardBlocks = 0; config.txListCacheExpiry = 5 minutes; - config.proposerDepositPctg = 0; config.maxVerificationsPerTx = 0; config.enableSoloProposer = false; config.enableOracleProver = false; @@ -190,7 +189,7 @@ contract TaikoL1Test is TaikoL1TestBase { printVariables(""); } - /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' + /// @dev Test what happens when proof time increases function test_reward_and_fee_if_proof_time_increases() external { mine(1); _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); @@ -199,12 +198,7 @@ contract TaikoL1Test is TaikoL1TestBase { bytes32 parentHash = GENESIS_BLOCK_HASH; - for ( - uint256 blockId = 1; - blockId < 5; - //blockId < conf.maxNumProposedBlocks * 2; - blockId++ - ) { + for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); printVariables("after propose"); @@ -219,8 +213,32 @@ contract TaikoL1Test is TaikoL1TestBase { printVariables(""); } - /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' + /// @dev Test what happens when proof time decreases function test_reward_and_fee_if_proof_time_decreases() external { + mine(1); + _depositTaikoToken(Alice, 1E7 * 1E8, 1 ether); + _depositTaikoToken(Bob, 1E7 * 1E8, 1 ether); + _depositTaikoToken(Carol, 1E7 * 1E8, 1 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(11 - blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + printVariables("after proved"); + parentHash = blockHash; + } + printVariables(""); + } + + /// @dev Test what happens when proof time stable + function test_reward_and_fee_if_proof_time_stable() external { mine(1); _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); @@ -228,16 +246,11 @@ contract TaikoL1Test is TaikoL1TestBase { bytes32 parentHash = GENESIS_BLOCK_HASH; - for ( - uint256 blockId = 1; - blockId < 5; - //blockId < conf.maxNumProposedBlocks * 2; - blockId++ - ) { + for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); printVariables("after propose"); - mine(6 - blockId); + mine(1); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); diff --git a/packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak b/packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak new file mode 100644 index 00000000000..e3c6a8bc1b0 --- /dev/null +++ b/packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {AddressManager} from "../contracts/thirdparty/AddressManager.sol"; +import {TaikoConfig} from "../contracts/L1/TaikoConfig.sol"; +import {TaikoData} from "../contracts/L1/TaikoData.sol"; +import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; +import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; +import {SignalService} from "../contracts/signal/SignalService.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {TaikoL1TestBase} from "./TaikoL1TestBase.sol"; + +contract TaikoL1WithConfig is TaikoL1 { + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config = TaikoConfig.getConfig(); + + config.enableTokenomics = true; + config.bootstrapDiscountHalvingPeriod = 0; + config.constantFeeRewardBlocks = 0; + config.txListCacheExpiry = 5 minutes; + config.proposerDepositPctg = 0; + config.maxVerificationsPerTx = 0; + config.enableSoloProposer = false; + config.enableOracleProver = false; + config.maxNumProposedBlocks = 10; + config.ringBufferSize = 12; + // this value must be changed if `maxNumProposedBlocks` is changed. + config.slotSmoothingFactor = 4160; + + config.proposingConfig = TaikoData.FeeConfig({ + avgTimeMAF: 64, + dampingFactorBips: 5000 + }); + + config.provingConfig = TaikoData.FeeConfig({ + avgTimeMAF: 64, + dampingFactorBips: 5000 + }); + } +} + +contract Verifier { + fallback(bytes calldata) external returns (bytes memory) { + return bytes.concat(keccak256("taiko")); + } +} + +contract TaikoL1Test is TaikoL1TestBase { + function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { + taikoL1 = new TaikoL1WithConfig(); + } + + function setUp() public override { + TaikoL1TestBase.setUp(); + _registerAddress( + string(abi.encodePacked("verifier_", uint16(100))), + address(new Verifier()) + ); + } + + /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' + function test_more_blocks_than_ring_buffer_size() external { + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for ( + uint256 blockId = 1; + blockId < conf.maxNumProposedBlocks * 10; + blockId++ + ) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + printVariables("after propose"); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + printVariables(""); + } + + /// @dev Test more than one block can be proposed, proven, & verified in the + /// same L1 block. + function test_multiple_blocks_in_one_L1_block() external { + _depositTaikoToken(Alice, 1000 * 1E8, 1000 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for (uint256 blockId = 1; blockId <= 2; blockId++) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + printVariables("after propose"); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Alice, meta, parentHash, blockHash, signalRoot); + verifyBlock(Alice, 2); + parentHash = blockHash; + } + printVariables(""); + } + + /// @dev Test verifying multiple blocks in one transaction + function test_verifying_multiple_blocks_once() external { + _depositTaikoToken(Alice, 1E6 * 1E8, 1000 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for ( + uint256 blockId = 1; + blockId <= conf.maxNumProposedBlocks; + blockId++ + ) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + printVariables("after propose"); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Alice, meta, parentHash, blockHash, signalRoot); + parentHash = blockHash; + } + verifyBlock(Alice, conf.maxNumProposedBlocks - 1); + printVariables("after verify"); + verifyBlock(Alice, conf.maxNumProposedBlocks); + printVariables("after verify"); + } + + /// @dev Test block time increases and fee decreases. + function test_block_time_increases_and_fee_decreases() external { + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for ( + uint256 blockId = 1; + blockId < conf.maxNumProposedBlocks * 10; + blockId++ + ) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + mine(blockId); + parentHash = blockHash; + } + printVariables(""); + } + + /// @dev Test block time decreases and the fee increases + function test_block_time_decreases_but_fee_remains() external { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + uint256 total = conf.maxNumProposedBlocks * 10; + + for (uint256 blockId = 1; blockId < total; blockId++) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + mine(total + 1 - blockId); + parentHash = blockHash; + } + printVariables(""); + } + + /// @dev Test block time decreases and the fee increases + function test_block_time_is_bigger_than_2x_average_and_prospoing_fee_decreases() external { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + uint256 total = conf.maxNumProposedBlocks * 10; + + for (uint256 blockId = 1; blockId < total; blockId++) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + mine(blockId * 3); + parentHash = blockHash; + } + printVariables(""); + } +} diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.sol index c25f48e3e4c..2d6aadcbc53 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.sol @@ -171,8 +171,9 @@ abstract contract TaikoL1TestBase is Test { } function printVariables(string memory comment) internal { + uint32 gasLimit = 1000000; TaikoData.StateVariables memory vars = L1.getStateVariables(); - (uint256 fee, ) = L1.getBlockFee(); + uint256 fee = L1.getBlockFee(gasLimit); string memory str = string.concat( Strings.toString(logCount++), ":[", @@ -180,7 +181,7 @@ abstract contract TaikoL1TestBase is Test { unicode"→", Strings.toString(vars.numBlocks), "] feeBase:", - Strings.toString(vars.feeBase), + Strings.toString(vars.baseFeeProof), " fee:", Strings.toString(fee), " avgBlockTime:", From 51da7386034146ea6154cd75ae56410d0a039386 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Thu, 30 Mar 2023 21:58:56 +0200 Subject: [PATCH 45/90] Remove old archived files --- .../ABDKMath64x64_sol_original_name.bak | 842 ------------------ .../thirdparty/LibRLPReader_from_hamil.bak | 606 ------------- .../thirdparty/LibRLPWriter_origi.bak | 281 ------ 3 files changed, 1729 deletions(-) delete mode 100644 packages/protocol/contracts/thirdparty/ABDKMath64x64_sol_original_name.bak delete mode 100644 packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak delete mode 100644 packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak diff --git a/packages/protocol/contracts/thirdparty/ABDKMath64x64_sol_original_name.bak b/packages/protocol/contracts/thirdparty/ABDKMath64x64_sol_original_name.bak deleted file mode 100644 index e346cd5dc26..00000000000 --- a/packages/protocol/contracts/thirdparty/ABDKMath64x64_sol_original_name.bak +++ /dev/null @@ -1,842 +0,0 @@ -// SPDX-License-Identifier: BSD-4-Clause -/* - * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. - * Author: Mikhail Vladimirov - */ -pragma solidity ^0.8.18; - -/** - * Smart contract library of mathematical functions operating with signed - * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is - * basically a simple fraction whose numerator is signed 128-bit integer and - * denominator is 2^64. As long as denominator is always the same, there is no - * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are - * represented by int128 type holding only the numerator. - */ -library ABDKMath64x64 { - /* - * Minimum value signed 64.64-bit fixed point number may have. - */ - int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; - - /* - * Maximum value signed 64.64-bit fixed point number may have. - */ - int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - - /** - * Convert signed 256-bit integer number into signed 64.64-bit fixed point - * number. Revert on overflow. - * - * @param x signed 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function fromInt(int256 x) internal pure returns (int128) { - unchecked { - require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); - return int128(x << 64); - } - } - - /** - * Convert signed 64.64 fixed point number into signed 64-bit integer number - * rounding down. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64-bit integer number - */ - function toInt(int128 x) internal pure returns (int64) { - unchecked { - return int64(x >> 64); - } - } - - /** - * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point - * number. Revert on overflow. - * - * @param x unsigned 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function fromUInt(uint256 x) internal pure returns (int128) { - unchecked { - require(x <= 0x7FFFFFFFFFFFFFFF); - return int128(int256(x << 64)); - } - } - - /** - * Convert signed 64.64 fixed point number into unsigned 64-bit integer - * number rounding down. Revert on underflow. - * - * @param x signed 64.64-bit fixed point number - * @return unsigned 64-bit integer number - */ - function toUInt(int128 x) internal pure returns (uint64) { - unchecked { - require(x >= 0); - return uint64(uint128(x >> 64)); - } - } - - /** - * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point - * number rounding down. Revert on overflow. - * - * @param x signed 128.128-bin fixed point number - * @return signed 64.64-bit fixed point number - */ - function from128x128(int256 x) internal pure returns (int128) { - unchecked { - int256 result = x >> 64; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Convert signed 64.64 fixed point number into signed 128.128 fixed point - * number. - * - * @param x signed 64.64-bit fixed point number - * @return signed 128.128 fixed point number - */ - function to128x128(int128 x) internal pure returns (int256) { - unchecked { - return int256(x) << 64; - } - } - - /** - * Calculate x + y. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function add(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 result = int256(x) + y; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x - y. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function sub(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 result = int256(x) - y; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x * y rounding down. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function mul(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 result = (int256(x) * y) >> 64; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point - * number and y is signed 256-bit integer number. Revert on overflow. - * - * @param x signed 64.64 fixed point number - * @param y signed 256-bit integer number - * @return signed 256-bit integer number - */ - function muli(int128 x, int256 y) internal pure returns (int256) { - unchecked { - if (x == MIN_64x64) { - require( - y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && - y <= 0x1000000000000000000000000000000000000000000000000 - ); - return -y << 63; - } else { - bool negativeResult = false; - if (x < 0) { - x = -x; - negativeResult = true; - } - if (y < 0) { - y = -y; // We rely on overflow behavior here - negativeResult = !negativeResult; - } - uint256 absoluteResult = mulu(x, uint256(y)); - if (negativeResult) { - require( - absoluteResult <= - 0x8000000000000000000000000000000000000000000000000000000000000000 - ); - return -int256(absoluteResult); // We rely on overflow behavior here - } else { - require( - absoluteResult <= - 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - ); - return int256(absoluteResult); - } - } - } - } - - /** - * Calculate x * y rounding down, where x is signed 64.64 fixed point number - * and y is unsigned 256-bit integer number. Revert on overflow. - * - * @param x signed 64.64 fixed point number - * @param y unsigned 256-bit integer number - * @return unsigned 256-bit integer number - */ - function mulu(int128 x, uint256 y) internal pure returns (uint256) { - unchecked { - if (y == 0) return 0; - - require(x >= 0); - - uint256 lo = (uint256(int256(x)) * - (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; - uint256 hi = uint256(int256(x)) * (y >> 128); - - require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - hi <<= 64; - - require( - hi <= - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - - lo - ); - return hi + lo; - } - } - - /** - * Calculate x / y rounding towards zero. Revert on overflow or when y is - * zero. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function div(int128 x, int128 y) internal pure returns (int128) { - unchecked { - require(y != 0); - int256 result = (int256(x) << 64) / y; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x / y rounding towards zero, where x and y are signed 256-bit - * integer numbers. Revert on overflow or when y is zero. - * - * @param x signed 256-bit integer number - * @param y signed 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function divi(int256 x, int256 y) internal pure returns (int128) { - unchecked { - require(y != 0); - - bool negativeResult = false; - if (x < 0) { - x = -x; // We rely on overflow behavior here - negativeResult = true; - } - if (y < 0) { - y = -y; // We rely on overflow behavior here - negativeResult = !negativeResult; - } - uint128 absoluteResult = divuu(uint256(x), uint256(y)); - if (negativeResult) { - require(absoluteResult <= 0x80000000000000000000000000000000); - return -int128(absoluteResult); // We rely on overflow behavior here - } else { - require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - return int128(absoluteResult); // We rely on overflow behavior here - } - } - } - - /** - * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit - * integer numbers. Revert on overflow or when y is zero. - * - * @param x unsigned 256-bit integer number - * @param y unsigned 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function divu(uint256 x, uint256 y) internal pure returns (int128) { - unchecked { - require(y != 0); - uint128 result = divuu(x, y); - require(result <= uint128(MAX_64x64)); - return int128(result); - } - } - - /** - * Calculate -x. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function neg(int128 x) internal pure returns (int128) { - unchecked { - require(x != MIN_64x64); - return -x; - } - } - - /** - * Calculate |x|. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function abs(int128 x) internal pure returns (int128) { - unchecked { - require(x != MIN_64x64); - return x < 0 ? -x : x; - } - } - - /** - * Calculate 1 / x rounding towards zero. Revert on overflow or when x is - * zero. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function inv(int128 x) internal pure returns (int128) { - unchecked { - require(x != 0); - int256 result = int256(0x100000000000000000000000000000000) / x; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function avg(int128 x, int128 y) internal pure returns (int128) { - unchecked { - return int128((int256(x) + int256(y)) >> 1); - } - } - - /** - * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. - * Revert on overflow or in case x * y is negative. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function gavg(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 m = int256(x) * int256(y); - require(m >= 0); - require( - m < - 0x4000000000000000000000000000000000000000000000000000000000000000 - ); - return int128(sqrtu(uint256(m))); - } - } - - /** - * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number - * and y is unsigned 256-bit integer number. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y uint256 value - * @return signed 64.64-bit fixed point number - */ - function pow(int128 x, uint256 y) internal pure returns (int128) { - unchecked { - bool negative = x < 0 && y & 1 == 1; - - uint256 absX = uint128(x < 0 ? -x : x); - uint256 absResult; - absResult = 0x100000000000000000000000000000000; - - if (absX <= 0x10000000000000000) { - absX <<= 63; - while (y != 0) { - if (y & 0x1 != 0) { - absResult = (absResult * absX) >> 127; - } - absX = (absX * absX) >> 127; - - if (y & 0x2 != 0) { - absResult = (absResult * absX) >> 127; - } - absX = (absX * absX) >> 127; - - if (y & 0x4 != 0) { - absResult = (absResult * absX) >> 127; - } - absX = (absX * absX) >> 127; - - if (y & 0x8 != 0) { - absResult = (absResult * absX) >> 127; - } - absX = (absX * absX) >> 127; - - y >>= 4; - } - - absResult >>= 64; - } else { - uint256 absXShift = 63; - if (absX < 0x1000000000000000000000000) { - absX <<= 32; - absXShift -= 32; - } - if (absX < 0x10000000000000000000000000000) { - absX <<= 16; - absXShift -= 16; - } - if (absX < 0x1000000000000000000000000000000) { - absX <<= 8; - absXShift -= 8; - } - if (absX < 0x10000000000000000000000000000000) { - absX <<= 4; - absXShift -= 4; - } - if (absX < 0x40000000000000000000000000000000) { - absX <<= 2; - absXShift -= 2; - } - if (absX < 0x80000000000000000000000000000000) { - absX <<= 1; - absXShift -= 1; - } - - uint256 resultShift = 0; - while (y != 0) { - require(absXShift < 64); - - if (y & 0x1 != 0) { - absResult = (absResult * absX) >> 127; - resultShift += absXShift; - if (absResult > 0x100000000000000000000000000000000) { - absResult >>= 1; - resultShift += 1; - } - } - absX = (absX * absX) >> 127; - absXShift <<= 1; - if (absX >= 0x100000000000000000000000000000000) { - absX >>= 1; - absXShift += 1; - } - - y >>= 1; - } - - require(resultShift < 64); - absResult >>= 64 - resultShift; - } - int256 result = negative ? -int256(absResult) : int256(absResult); - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate sqrt (x) rounding down. Revert if x < 0. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function sqrt(int128 x) internal pure returns (int128) { - unchecked { - require(x >= 0); - return int128(sqrtu(uint256(int256(x)) << 64)); - } - } - - /** - * Calculate binary logarithm of x. Revert if x <= 0. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function log_2(int128 x) internal pure returns (int128) { - unchecked { - require(x > 0); - - int256 msb = 0; - int256 xc = x; - if (xc >= 0x10000000000000000) { - xc >>= 64; - msb += 64; - } - if (xc >= 0x100000000) { - xc >>= 32; - msb += 32; - } - if (xc >= 0x10000) { - xc >>= 16; - msb += 16; - } - if (xc >= 0x100) { - xc >>= 8; - msb += 8; - } - if (xc >= 0x10) { - xc >>= 4; - msb += 4; - } - if (xc >= 0x4) { - xc >>= 2; - msb += 2; - } - if (xc >= 0x2) msb += 1; // No need to shift xc anymore - - int256 result = (msb - 64) << 64; - uint256 ux = uint256(int256(x)) << uint256(127 - msb); - for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { - ux *= ux; - uint256 b = ux >> 255; - ux >>= 127 + b; - result += bit * int256(b); - } - - return int128(result); - } - } - - /** - * Calculate natural logarithm of x. Revert if x <= 0. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function ln(int128 x) internal pure returns (int128) { - unchecked { - require(x > 0); - - return - int128( - int256( - (uint256(int256(log_2(x))) * - 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128 - ) - ); - } - } - - /** - * Calculate binary exponent of x. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function exp_2(int128 x) internal pure returns (int128) { - unchecked { - require(x < 0x400000000000000000); // Overflow - - if (x < -0x400000000000000000) return 0; // Underflow - - uint256 result = 0x80000000000000000000000000000000; - - if (x & 0x8000000000000000 > 0) - result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128; - if (x & 0x4000000000000000 > 0) - result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128; - if (x & 0x2000000000000000 > 0) - result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128; - if (x & 0x1000000000000000 > 0) - result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128; - if (x & 0x800000000000000 > 0) - result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128; - if (x & 0x400000000000000 > 0) - result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128; - if (x & 0x200000000000000 > 0) - result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128; - if (x & 0x100000000000000 > 0) - result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128; - if (x & 0x80000000000000 > 0) - result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128; - if (x & 0x40000000000000 > 0) - result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128; - if (x & 0x20000000000000 > 0) - result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128; - if (x & 0x10000000000000 > 0) - result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128; - if (x & 0x8000000000000 > 0) - result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128; - if (x & 0x4000000000000 > 0) - result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128; - if (x & 0x2000000000000 > 0) - result = (result * 0x1000162E525EE054754457D5995292026) >> 128; - if (x & 0x1000000000000 > 0) - result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128; - if (x & 0x800000000000 > 0) - result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128; - if (x & 0x400000000000 > 0) - result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128; - if (x & 0x200000000000 > 0) - result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128; - if (x & 0x100000000000 > 0) - result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128; - if (x & 0x80000000000 > 0) - result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128; - if (x & 0x40000000000 > 0) - result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128; - if (x & 0x20000000000 > 0) - result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128; - if (x & 0x10000000000 > 0) - result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128; - if (x & 0x8000000000 > 0) - result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128; - if (x & 0x4000000000 > 0) - result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128; - if (x & 0x2000000000 > 0) - result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128; - if (x & 0x1000000000 > 0) - result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128; - if (x & 0x800000000 > 0) - result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128; - if (x & 0x400000000 > 0) - result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128; - if (x & 0x200000000 > 0) - result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128; - if (x & 0x100000000 > 0) - result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128; - if (x & 0x80000000 > 0) - result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128; - if (x & 0x40000000 > 0) - result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128; - if (x & 0x20000000 > 0) - result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128; - if (x & 0x10000000 > 0) - result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128; - if (x & 0x8000000 > 0) - result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128; - if (x & 0x4000000 > 0) - result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128; - if (x & 0x2000000 > 0) - result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128; - if (x & 0x1000000 > 0) - result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128; - if (x & 0x800000 > 0) - result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128; - if (x & 0x400000 > 0) - result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128; - if (x & 0x200000 > 0) - result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128; - if (x & 0x100000 > 0) - result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128; - if (x & 0x80000 > 0) - result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128; - if (x & 0x40000 > 0) - result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128; - if (x & 0x20000 > 0) - result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128; - if (x & 0x10000 > 0) - result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128; - if (x & 0x8000 > 0) - result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128; - if (x & 0x4000 > 0) - result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128; - if (x & 0x2000 > 0) - result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128; - if (x & 0x1000 > 0) - result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128; - if (x & 0x800 > 0) - result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128; - if (x & 0x400 > 0) - result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128; - if (x & 0x200 > 0) - result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128; - if (x & 0x100 > 0) - result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128; - if (x & 0x80 > 0) - result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128; - if (x & 0x40 > 0) - result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128; - if (x & 0x20 > 0) - result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128; - if (x & 0x10 > 0) - result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128; - if (x & 0x8 > 0) - result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128; - if (x & 0x4 > 0) - result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128; - if (x & 0x2 > 0) - result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128; - if (x & 0x1 > 0) - result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128; - - result >>= uint256(int256(63 - (x >> 64))); - require(result <= uint256(int256(MAX_64x64))); - - return int128(int256(result)); - } - } - - /** - * Calculate natural exponent of x. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function exp(int128 x) internal pure returns (int128) { - unchecked { - require(x < 0x400000000000000000); // Overflow - - if (x < -0x400000000000000000) return 0; // Underflow - - return - exp_2( - int128( - (int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128 - ) - ); - } - } - - /** - * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit - * integer numbers. Revert on overflow or when y is zero. - * - * @param x unsigned 256-bit integer number - * @param y unsigned 256-bit integer number - * @return unsigned 64.64-bit fixed point number - */ - function divuu(uint256 x, uint256 y) private pure returns (uint128) { - unchecked { - require(y != 0); - - uint256 result; - - if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) - result = (x << 64) / y; - else { - uint256 msb = 192; - uint256 xc = x >> 192; - if (xc >= 0x100000000) { - xc >>= 32; - msb += 32; - } - if (xc >= 0x10000) { - xc >>= 16; - msb += 16; - } - if (xc >= 0x100) { - xc >>= 8; - msb += 8; - } - if (xc >= 0x10) { - xc >>= 4; - msb += 4; - } - if (xc >= 0x4) { - xc >>= 2; - msb += 2; - } - if (xc >= 0x2) msb += 1; // No need to shift xc anymore - - result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1); - require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - uint256 hi = result * (y >> 128); - uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - uint256 xh = x >> 192; - uint256 xl = x << 64; - - if (xl < lo) xh -= 1; - xl -= lo; // We rely on overflow behavior here - lo = hi << 128; - if (xl < lo) xh -= 1; - xl -= lo; // We rely on overflow behavior here - - result += xh == hi >> 128 ? xl / y : 1; - } - - require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - return uint128(result); - } - } - - /** - * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer - * number. - * - * @param x unsigned 256-bit integer number - * @return unsigned 128-bit integer number - */ - function sqrtu(uint256 x) private pure returns (uint128) { - unchecked { - if (x == 0) return 0; - else { - uint256 xx = x; - uint256 r = 1; - if (xx >= 0x100000000000000000000000000000000) { - xx >>= 128; - r <<= 64; - } - if (xx >= 0x10000000000000000) { - xx >>= 64; - r <<= 32; - } - if (xx >= 0x100000000) { - xx >>= 32; - r <<= 16; - } - if (xx >= 0x10000) { - xx >>= 16; - r <<= 8; - } - if (xx >= 0x100) { - xx >>= 8; - r <<= 4; - } - if (xx >= 0x10) { - xx >>= 4; - r <<= 2; - } - if (xx >= 0x4) { - r <<= 1; - } - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; // Seven iterations should be enough - uint256 r1 = x / r; - return uint128(r < r1 ? r : r1); - } - } - } -} diff --git a/packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak b/packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak deleted file mode 100644 index e735134f1cb..00000000000 --- a/packages/protocol/contracts/thirdparty/LibRLPReader_from_hamil.bak +++ /dev/null @@ -1,606 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -/* - * @author Hamdi Allam hamdi.allam97@gmail.com - * Please reach out with any questions or concerns - */ -pragma solidity ^0.8.18; - -library LibRLPReader { - uint8 constant STRING_SHORT_START = 0x80; - uint8 constant STRING_LONG_START = 0xb8; - uint8 constant LIST_SHORT_START = 0xc0; - uint8 constant LIST_LONG_START = 0xf8; - uint8 constant WORD_SIZE = 32; - - enum RLPItemType { - DATA_ITEM, - LIST_ITEM - } - - struct RLPItem { - uint256 length; - uint256 ptr; - } - - struct Iterator { - RLPItem item; // Item that's being iterated over. - uint256 nextPtr; // Position of the next item in the list. - } - - /* - * @dev Returns the next element in the iteration. Reverts if it has not next element. - * @param self The iterator. - * @return The next element in the iteration. - */ - function next(Iterator memory self) internal pure returns (RLPItem memory) { - require(hasNext(self)); - - uint256 ptr = self.nextPtr; - uint256 itemLength = _itemLength(ptr); - self.nextPtr = ptr + itemLength; - - return RLPItem(itemLength, ptr); - } - - /* - * @dev Returns true if the iteration has more elements. - * @param self The iterator. - * @return true if the iteration has more elements. - */ - function hasNext(Iterator memory self) internal pure returns (bool) { - RLPItem memory item = self.item; - return self.nextPtr < item.ptr + item.length; - } - - /* - * @param item RLP encoded bytes - */ - function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { - uint256 ptr; - assembly { - ptr := add(item, 0x20) - } - - return RLPItem(item.length, ptr); - } - - /* - * @dev Create an iterator. Reverts if item is not a list. - * @param self The RLP item. - * @return An 'Iterator' over the item. - */ - function iterator(RLPItem memory self) internal pure returns (Iterator memory) { - require(isList(self)); - - uint256 ptr = self.ptr + _payloadOffset(self.ptr); - return Iterator(self, ptr); - } - - /* - * @param the RLP item. - */ - function rlpLen(RLPItem memory item) internal pure returns (uint256) { - return item.length; - } - - /* - * @param the RLP item. - * @return (ptr, length) pair: location of the item's payload in memory. - */ - function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { - uint256 offset = _payloadOffset(item.ptr); - uint256 ptr = item.ptr + offset; - uint256 length = item.length - offset; // data length - return (ptr, length); - } - - /* - * @param the RLP item. - */ - function payloadLen(RLPItem memory item) internal pure returns (uint256) { - (, uint256 length) = payloadLocation(item); - return length; - } - - /** - * Reads an RLP string value into a string. - * @param _in RLP string value. - * @return Decoded string. - */ - function readString( - RLPItem memory _in - ) internal pure returns (string memory) { - return string(readBytes(_in)); - } - - /** - * Reads an RLP string value into a string. - * @param _in RLP string value. - * @return Decoded string. - */ - function readString( - bytes memory _in - ) internal pure returns (string memory) { - return readString(toRlpItem(_in)); - } - - function readList( - bytes memory _in - ) internal pure returns (RLPItem[] memory) { - return readList(toRlpItem(_in)); - } - - /* - * @param the RLP item containing the encoded list. - */ - // function readList(RLPItem memory item) internal pure returns (RLPItem[] memory) { - // require(isList(item)); - - // uint256 items = numItems(item); - // RLPItem[] memory result = new RLPItem[](items); - - // uint256 ptr = item.ptr + _payloadOffset(item.ptr); - // uint256 dataLen; - // for (uint256 i = 0; i < items; i++) { - // dataLen = _itemLength(ptr); - // result[i] = RLPItem(dataLen, ptr); - // ptr = ptr + dataLen; - // } - - // return result; - // } - function readList( - RLPItem memory _in - ) internal pure returns (RLPItem[] memory) { - (uint256 listOffset, , RLPItemType itemType) = _decodeLength(_in); - - require(itemType == RLPItemType.LIST_ITEM, "Invalid RLP list value."); - - // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by - // writing to the length. Since we can't know the number of RLP items without looping over - // the entire input, we'd have to loop twice to accurately size this array. It's easier to - // simply set a reasonable maximum list length and decrease the size before we finish. - RLPItem[] memory out = new RLPItem[](WORD_SIZE); - - uint256 itemCount; - uint256 offset = listOffset; - while (offset < _in.length) { - require( - itemCount < WORD_SIZE, - "Provided RLP list exceeds max list length." - ); - - (uint256 itemOffset, uint256 itemLength, ) = _decodeLength( - RLPItem({length: _in.length - offset, ptr: _in.ptr + offset}) - ); - - out[itemCount] = RLPItem({ - length: itemLength + itemOffset, - ptr: _in.ptr + offset - }); - - itemCount += 1; - offset += itemOffset + itemLength; - } - - // Decrease the array size to match the actual item count. - assembly { - mstore(out, itemCount) - } - - return out; - } - - // @return indicator whether encoded payload is a list. negate this function call for isData. - function isList(RLPItem memory item) internal pure returns (bool) { - if (item.length == 0) return false; - - uint8 byte0; - uint256 ptr = item.ptr; - assembly { - byte0 := byte(0, mload(ptr)) - } - - if (byte0 < LIST_SHORT_START) return false; - return true; - } - - /* - * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. - * @return keccak256 hash of RLP encoded bytes. - */ - function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { - uint256 ptr = item.ptr; - uint256 length = item.length; - bytes32 result; - assembly { - result := keccak256(ptr, length) - } - return result; - } - - /* - * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. - * @return keccak256 hash of the item payload. - */ - function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { - (uint256 ptr, uint256 length) = payloadLocation(item); - bytes32 result; - assembly { - result := keccak256(ptr, length) - } - return result; - } - - /** RLPItem conversions into data types **/ - // @returns raw rlp encoding in bytes - function readRawBytes(RLPItem memory item) internal pure returns (bytes memory) { - bytes memory result = new bytes(item.length); - if (result.length == 0) return result; - - uint256 ptr; - assembly { - ptr := add(0x20, result) - } - - copy(item.ptr, ptr, item.length); - return result; - } - - function readBytes(bytes memory _in) internal pure returns (bytes memory) { - return readBytes(toRlpItem(_in)); - } - - // function readBytes(RLPItem memory item) internal pure returns (bytes memory) { - // require(item.length > 0); - - // (uint256 ptr, uint256 length) = payloadLocation(item); - // bytes memory result = new bytes(length); - - // uint256 destPtr; - // assembly { - // destPtr := add(0x20, result) - // } - - // copy(ptr, destPtr, length); - // return result; - // } - function readBytes( - RLPItem memory _in - ) internal pure returns (bytes memory) { - ( - uint256 itemOffset, - uint256 itemLength, - RLPItemType itemType - ) = _decodeLength(_in); - - require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes value."); - - return _copy(_in.ptr, itemOffset, itemLength); - } - - function readBytes32(bytes memory _in) internal pure returns (bytes32) { - return readBytes32(toRlpItem(_in)); - } - - function readBytes32(RLPItem memory item) internal pure returns (bytes32) { - require(item.length <= 33); - - (uint256 ptr,) = payloadLocation(item); - bytes32 result; - uint256 destPtr; - assembly { - destPtr := add(0x20, result) - } - - copy(ptr, destPtr, 32); - return result; - } - - function readBool(bytes memory _in) internal pure returns (bool) { - return readBool(toRlpItem(_in)); - } - - // any non-zero byte except "0x80" is considered true - function readBool(RLPItem memory item) internal pure returns (bool) { - require(item.length == 1); - uint256 result; - uint256 ptr = item.ptr; - assembly { - result := byte(0, mload(ptr)) - } - - // SEE Github Issue #5. - // Summary: Most commonly used RLP libraries (i.e Geth) will encode - // "0" as "0x80" instead of as "0". We handle this edge case explicitly - // here. - if (result == 0 || result == STRING_SHORT_START) { - return false; - } else { - return true; - } - } - - function readAddress(bytes memory _in) internal pure returns (address) { - return readAddress(toRlpItem(_in)); - } - - function readAddress(RLPItem memory item) internal pure returns (address) { - // 1 byte for the length prefix - require(item.length == 21); - - return address(uint160(readUint256(item))); - } - - /** - * Reads an RLP uint256 value into a uint256. - * @param _in RLP uint256 value. - * @return Decoded uint256. - */ - function readUint256(bytes memory _in) internal pure returns (uint256) { - return readUint256(toRlpItem(_in)); - } - - function readUint256(RLPItem memory item) internal pure returns (uint256) { - require(item.length > 0 && item.length <= 33); - - (uint256 ptr, uint256 length) = payloadLocation(item); - - uint256 result; - assembly { - result := mload(ptr) - - // shift to the correct location if neccesary - if lt(length, 32) { - result := div(result, exp(256, sub(32, length))) - } - } - - return result; - } - - // enforces 32 byte length - function toUintStrict(RLPItem memory item) internal pure returns (uint256) { - // one byte prefix - require(item.length == 33); - - uint256 result; - uint256 ptr = item.ptr + 1; - assembly { - result := mload(ptr) - } - - return result; - } - - - /* - * Private Helpers - */ - - // @return number of payload items inside an encoded list. - function numItems(RLPItem memory item) private pure returns (uint256) { - if (item.length == 0) return 0; - - uint256 count = 0; - uint256 currPtr = item.ptr + _payloadOffset(item.ptr); - uint256 endPtr = item.ptr + item.length; - while (currPtr < endPtr) { - currPtr = currPtr + _itemLength(currPtr); // skip over an item - count++; - } - - return count; - } - - // @return entire rlp item byte length - function _itemLength(uint256 ptr) private pure returns (uint256) { - uint256 itemLen; - uint256 byte0; - assembly { - byte0 := byte(0, mload(ptr)) - } - - if (byte0 < STRING_SHORT_START) { - itemLen = 1; - } else if (byte0 < STRING_LONG_START) { - itemLen = byte0 - STRING_SHORT_START + 1; - } else if (byte0 < LIST_SHORT_START) { - assembly { - let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is - ptr := add(ptr, 1) // skip over the first byte - - /* 32 byte word size */ - let dataLen := div(mload(ptr), exp(256, sub(32, byteLen))) // right shifting to get the length - itemLen := add(dataLen, add(byteLen, 1)) - } - } else if (byte0 < LIST_LONG_START) { - itemLen = byte0 - LIST_SHORT_START + 1; - } else { - assembly { - let byteLen := sub(byte0, 0xf7) - ptr := add(ptr, 1) - - let dataLen := div(mload(ptr), exp(256, sub(32, byteLen))) // right shifting to the correct length - itemLen := add(dataLen, add(byteLen, 1)) - } - } - - return itemLen; - } - - // @return number of bytes until the data - function _payloadOffset(uint256 ptr) private pure returns (uint256) { - uint256 byte0; - assembly { - byte0 := byte(0, mload(ptr)) - } - - if (byte0 < STRING_SHORT_START) { - return 0; - } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { - return 1; - } else if (byte0 < LIST_SHORT_START) { - // being explicit - return byte0 - (STRING_LONG_START - 1) + 1; - } else { - return byte0 - (LIST_LONG_START - 1) + 1; - } - } - - /* - * @param src Pointer to source - * @param dest Pointer to destination - * @param length Amount of memory to copy from the source - */ - function copy(uint256 src, uint256 dest, uint256 length) private pure { - if (length == 0) return; - - // copy as many word sizes as possible - for (; length >= WORD_SIZE; length -= WORD_SIZE) { - assembly { - mstore(dest, mload(src)) - } - - src += WORD_SIZE; - dest += WORD_SIZE; - } - - if (length > 0) { - // left over bytes. Mask is used to remove unwanted bytes from the word - uint256 mask = 256**(WORD_SIZE - length) - 1; - assembly { - let srcpart := and(mload(src), not(mask)) // zero out src - let destpart := and(mload(dest), mask) // retrieve the bytes - mstore(dest, or(destpart, srcpart)) - } - } - } - - /** - * Decodes the length of an RLP item. - * @param _in RLP item to decode. - * @return Offset of the encoded data. - * @return Length of the encoded data. - * @return RLP item type (LIST_ITEM or DATA_ITEM). - */ - function _decodeLength( - RLPItem memory _in - ) private pure returns (uint256, uint256, RLPItemType) { - require(_in.length > 0, "RLP item cannot be null."); - - uint256 ptr = _in.ptr; - uint256 prefix; - assembly { - prefix := byte(0, mload(ptr)) - } - - if (prefix <= 0x7f) { - // Single byte. - - return (0, 1, RLPItemType.DATA_ITEM); - } else if (prefix <= 0xb7) { - // Short string. - - // slither-disable-next-line variable-scope - uint256 strLen = prefix - 0x80; - - require(_in.length > strLen, "Invalid RLP short string."); - - return (1, strLen, RLPItemType.DATA_ITEM); - } else if (prefix <= 0xbf) { - // Long string. - uint256 lenOfStrLen = prefix - 0xb7; - - require( - _in.length > lenOfStrLen, - "Invalid RLP long string length." - ); - - uint256 strLen; - assembly { - // Pick out the string length. - strLen := div( - mload(add(ptr, 1)), - exp(256, sub(32, lenOfStrLen)) - ) - } - - require( - _in.length > lenOfStrLen + strLen, - "Invalid RLP long string." - ); - - return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM); - } else if (prefix <= 0xf7) { - // Short list. - // slither-disable-next-line variable-scope - uint256 listLen = prefix - 0xc0; - - require(_in.length > listLen, "Invalid RLP short list."); - - return (1, listLen, RLPItemType.LIST_ITEM); - } else { - // Long list. - uint256 lenOfListLen = prefix - 0xf7; - - require(_in.length > lenOfListLen, "Invalid RLP long list length."); - - uint256 listLen; - assembly { - // Pick out the list length. - listLen := div( - mload(add(ptr, 1)), - exp(256, sub(32, lenOfListLen)) - ) - } - - require( - _in.length > lenOfListLen + listLen, - "Invalid RLP long list." - ); - - return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM); - } - } - - function _copy( - uint256 _src, - uint256 _offset, - uint256 _length - ) internal pure returns (bytes memory) { - bytes memory result = new bytes(_length); - if (result.length == 0) { - return result; - } - - bytes memory src; - bytes memory dst; - assembly { - src := add(_src, _offset) - - dst := add(result, 32) - - for { - let i := 0 - } lt(i, _length) { - i := add(i, 32) - } { - mstore(add(dst, i), mload(add(src, i))) - } - } - - // Pick out the remaining bytes. - uint256 mask; - unchecked { - mask = 256 ** (32 - (_length % 32)) - 1; - } - - assembly { - mstore(dst, or(and(mload(src), not(mask)), and(mload(dst), mask))) - } - - return result; - } -} diff --git a/packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak b/packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak deleted file mode 100644 index 58934a53c16..00000000000 --- a/packages/protocol/contracts/thirdparty/LibRLPWriter_origi.bak +++ /dev/null @@ -1,281 +0,0 @@ -// SPDX-License-Identifier: MIT -// Taken from https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/rlp/LibRLPWriter.sol -// Modified to support writeBytes32/writeUint64 -// (The MIT License) -// -// Copyright 2020-2021 Optimism -// Copyright 2022-2023 Taiko Labs -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -pragma solidity ^0.8.18; - -/** - * @title LibRLPWriter - * @author Bakaoh (with modifications) - */ -library LibRLPWriter { - /********************** - * Internal Functions * - **********************/ - - /** - * RLP encodes a byte string. - * @param _in The byte string to encode. - * @return The RLP encoded string in bytes. - */ - function writeBytes(bytes memory _in) internal pure returns (bytes memory) { - bytes memory encoded; - - if (_in.length == 1 && uint8(_in[0]) < 128) { - encoded = _in; - } else { - encoded = abi.encodePacked(_writeLength(_in.length, 128), _in); - } - - return encoded; - } - - /** - * RLP encodes a list of RLP encoded byte byte strings. - * @param _in The list of RLP encoded byte strings. - * @return The RLP encoded list of items in bytes. - */ - function writeList( - bytes[] memory _in - ) internal pure returns (bytes memory) { - bytes memory list = _flatten(_in); - return abi.encodePacked(_writeLength(list.length, 192), list); - } - - /** - * RLP encodes a string. - * @param _in The string to encode. - * @return The RLP encoded string in bytes. - */ - function writeString( - string memory _in - ) internal pure returns (bytes memory) { - return writeBytes(bytes(_in)); - } - - /** - * RLP encodes an address. - * @param _in The address to encode. - * @return The RLP encoded address in bytes. - */ - function writeAddress(address _in) internal pure returns (bytes memory) { - return writeBytes(abi.encodePacked(_in)); - } - - /** - * RLP encodes a uint. - * @param _in The uint256 to encode. - * @return The RLP encoded uint256 in bytes. - */ - function writeUint(uint256 _in) internal pure returns (bytes memory) { - return writeBytes(_toBinary(_in)); - } - - function writeBytes32(bytes32 _in) internal pure returns (bytes memory) { - return writeBytes(_toBinary(uint256(_in))); - } - - /** - * RLP encodes a hash, we should use this function but not writeBytes32 to - * encode a hash, since writeBytes32 will remove the leading zeros of the - * given bytes. - * @param _in The hash to encode. - * @return The RLP encoded hash in bytes. - */ - function writeHash(bytes32 _in) internal pure returns (bytes memory) { - return writeBytes(_toBinaryWithLeadingZeros(uint256(_in))); - } - - function writeUint64(uint64 _in) internal pure returns (bytes memory) { - return writeBytes(_toBinary(_in)); - } - - /** - * RLP encodes a bool. - * @param _in The bool to encode. - * @return The RLP encoded bool in bytes. - */ - function writeBool(bool _in) internal pure returns (bytes memory) { - bytes memory encoded = new bytes(1); - encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80)); - return encoded; - } - - /********************* - * Private Functions * - *********************/ - - /** - * Encode the first byte, followed by the `len` in binary form if `length` is more than 55. - * @param _len The length of the string or the payload. - * @param _offset 128 if item is string, 192 if item is list. - * @return RLP encoded bytes. - */ - function _writeLength( - uint256 _len, - uint256 _offset - ) private pure returns (bytes memory) { - bytes memory encoded; - - if (_len < 56) { - encoded = new bytes(1); - encoded[0] = bytes1(uint8(_len) + uint8(_offset)); - } else { - uint256 lenLen; - uint256 i = 1; - while (_len / i != 0) { - ++lenLen; - i *= 256; - } - - encoded = new bytes(lenLen + 1); - encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); - for (i = 1; i <= lenLen; ++i) { - encoded[i] = bytes1( - uint8((_len / (256 ** (lenLen - i))) % 256) - ); - } - } - - return encoded; - } - - /** - * Encode integer in big endian binary form with no leading zeroes. - * @notice TODO: This should be optimized with assembly to save gas costs. - * @param _x The integer to encode. - * @return RLP encoded bytes. - */ - function _toBinary(uint256 _x) private pure returns (bytes memory) { - bytes memory b = abi.encodePacked(_x); - - uint256 i; - for (; i < 32; ++i) { - if (b[i] != 0) { - break; - } - } - - bytes memory res = new bytes(32 - i); - for (uint256 j; j < res.length; ++j) { - res[j] = b[i++]; - } - - return res; - } - - /** - * Encode integer in big endian binary form with leading zeroes. - * @notice TODO: This should be optimized with assembly to save gas costs. - * @param _x The integer to encode. - * @return RLP encoded bytes. - */ - function _toBinaryWithLeadingZeros( - uint256 _x - ) private pure returns (bytes memory) { - bytes memory b = abi.encodePacked(_x); - - uint256 i; - - bytes memory res = new bytes(32); - for (uint256 j; j < res.length; ++j) { - res[j] = b[i++]; - } - - return res; - } - - /** - * Copies a piece of memory to another location. - * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. - * @param _dest Destination location. - * @param _src Source location. - * @param _len Length of memory to copy. - */ - function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure { - uint256 dest = _dest; - uint256 src = _src; - uint256 len = _len; - - for (; len >= 32; len -= 32) { - assembly { - mstore(dest, mload(src)) - } - dest += 32; - src += 32; - } - - uint256 mask; - unchecked { - mask = 256 ** (32 - len) - 1; - } - assembly { - let srcpart := and(mload(src), not(mask)) - let destpart := and(mload(dest), mask) - mstore(dest, or(destpart, srcpart)) - } - } - - /** - * Flattens a list of byte strings into one byte string. - * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. - * @param _list List of byte strings to flatten. - * @return The flattened byte string. - */ - function _flatten( - bytes[] memory _list - ) private pure returns (bytes memory) { - if (_list.length == 0) { - return new bytes(0); - } - - uint256 len; - uint256 i; - for (; i < _list.length; ++i) { - len += _list[i].length; - } - - bytes memory flattened = new bytes(len); - uint256 flattenedPtr; - assembly { - flattenedPtr := add(flattened, 0x20) - } - - for (i = 0; i < _list.length; ++i) { - bytes memory item = _list[i]; - - uint256 listPtr; - assembly { - listPtr := add(item, 0x20) - } - - _memcpy(flattenedPtr, listPtr, item.length); - flattenedPtr += _list[i].length; - } - - return flattened; - } -} From 9786d103d8fe49fd1e88d104ae6c26d493d77db8 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Thu, 30 Mar 2023 22:26:04 +0200 Subject: [PATCH 46/90] Remove archived test file --- .../test2/TaikoL1.t_sol_my_added_tests.bak | 220 ------------------ 1 file changed, 220 deletions(-) delete mode 100644 packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak diff --git a/packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak b/packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak deleted file mode 100644 index e3c6a8bc1b0..00000000000 --- a/packages/protocol/test2/TaikoL1.t_sol_my_added_tests.bak +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.18; - -import {Test} from "forge-std/Test.sol"; -import {console2} from "forge-std/console2.sol"; -import {AddressManager} from "../contracts/thirdparty/AddressManager.sol"; -import {TaikoConfig} from "../contracts/L1/TaikoConfig.sol"; -import {TaikoData} from "../contracts/L1/TaikoData.sol"; -import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; -import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; -import {SignalService} from "../contracts/signal/SignalService.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {TaikoL1TestBase} from "./TaikoL1TestBase.sol"; - -contract TaikoL1WithConfig is TaikoL1 { - function getConfig() - public - pure - override - returns (TaikoData.Config memory config) - { - config = TaikoConfig.getConfig(); - - config.enableTokenomics = true; - config.bootstrapDiscountHalvingPeriod = 0; - config.constantFeeRewardBlocks = 0; - config.txListCacheExpiry = 5 minutes; - config.proposerDepositPctg = 0; - config.maxVerificationsPerTx = 0; - config.enableSoloProposer = false; - config.enableOracleProver = false; - config.maxNumProposedBlocks = 10; - config.ringBufferSize = 12; - // this value must be changed if `maxNumProposedBlocks` is changed. - config.slotSmoothingFactor = 4160; - - config.proposingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, - dampingFactorBips: 5000 - }); - - config.provingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, - dampingFactorBips: 5000 - }); - } -} - -contract Verifier { - fallback(bytes calldata) external returns (bytes memory) { - return bytes.concat(keccak256("taiko")); - } -} - -contract TaikoL1Test is TaikoL1TestBase { - function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { - taikoL1 = new TaikoL1WithConfig(); - } - - function setUp() public override { - TaikoL1TestBase.setUp(); - _registerAddress( - string(abi.encodePacked("verifier_", uint16(100))), - address(new Verifier()) - ); - } - - /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' - function test_more_blocks_than_ring_buffer_size() external { - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for ( - uint256 blockId = 1; - blockId < conf.maxNumProposedBlocks * 10; - blockId++ - ) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - printVariables("after propose"); - mine(1); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - parentHash = blockHash; - } - printVariables(""); - } - - /// @dev Test more than one block can be proposed, proven, & verified in the - /// same L1 block. - function test_multiple_blocks_in_one_L1_block() external { - _depositTaikoToken(Alice, 1000 * 1E8, 1000 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for (uint256 blockId = 1; blockId <= 2; blockId++) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - printVariables("after propose"); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Alice, meta, parentHash, blockHash, signalRoot); - verifyBlock(Alice, 2); - parentHash = blockHash; - } - printVariables(""); - } - - /// @dev Test verifying multiple blocks in one transaction - function test_verifying_multiple_blocks_once() external { - _depositTaikoToken(Alice, 1E6 * 1E8, 1000 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for ( - uint256 blockId = 1; - blockId <= conf.maxNumProposedBlocks; - blockId++ - ) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - printVariables("after propose"); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Alice, meta, parentHash, blockHash, signalRoot); - parentHash = blockHash; - } - verifyBlock(Alice, conf.maxNumProposedBlocks - 1); - printVariables("after verify"); - verifyBlock(Alice, conf.maxNumProposedBlocks); - printVariables("after verify"); - } - - /// @dev Test block time increases and fee decreases. - function test_block_time_increases_and_fee_decreases() external { - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for ( - uint256 blockId = 1; - blockId < conf.maxNumProposedBlocks * 10; - blockId++ - ) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(1); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - mine(blockId); - parentHash = blockHash; - } - printVariables(""); - } - - /// @dev Test block time decreases and the fee increases - function test_block_time_decreases_but_fee_remains() external { - mine(1); - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - uint256 total = conf.maxNumProposedBlocks * 10; - - for (uint256 blockId = 1; blockId < total; blockId++) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(1); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - mine(total + 1 - blockId); - parentHash = blockHash; - } - printVariables(""); - } - - /// @dev Test block time decreases and the fee increases - function test_block_time_is_bigger_than_2x_average_and_prospoing_fee_decreases() external { - mine(1); - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - uint256 total = conf.maxNumProposedBlocks * 10; - - for (uint256 blockId = 1; blockId < total; blockId++) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(1); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - mine(blockId * 3); - parentHash = blockHash; - } - printVariables(""); - } -} From 266b021b5eaefa929d625d12e47c82a3457579d8 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 31 Mar 2023 11:31:01 +0800 Subject: [PATCH 47/90] add doc --- packages/protocol/docs/L2EIP1559.md | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 packages/protocol/docs/L2EIP1559.md diff --git a/packages/protocol/docs/L2EIP1559.md b/packages/protocol/docs/L2EIP1559.md new file mode 100644 index 00000000000..4ed797f4566 --- /dev/null +++ b/packages/protocol/docs/L2EIP1559.md @@ -0,0 +1,35 @@ +# Taiko L2 EIP-1559 + +## The overall design + +The EIP-1559 base fee per gas (basefee) on Taiko L2 is calculated by Taiko L1 protocol contracts and injected into the block's metadata. The Taiko client should skip calculating the basefee value, and stop burning the basefee, and send it to a named address ("treasure") specified in Taiko L1 protocol contracts, which will be verified by Taiko ZKP. + +## Basefee Calculation + +We use Vitalik's idea proposed here: https://ethresear.ch/t/make-eip-1559-more-like-an-amm-curve/9082 (read it first!). The x-axis represents the current gas _excess_, the y-axis is the ether amount. When some gas are sold, excess goes up, and the difference of the new and the old y value is the total cost of the gas purchase, or $$cost(gasAmount) = e^{(gasExcess + gasAmount)} -e^{gasExcess}$$, and $$basefee(gasAmount) = cost(gasAmount)/gasAmount$$. + +A nice property of the $e^x$ curve is that for a chosen gas target $T$, the basefee ($basefee(T)$) for a block with $T$ gas and the basefee ($basefee(2T)$) for a block with $2T$ gas always have the fixed ratio: $$R == basefee(2T)/basefee(T)$$ regardless of the current _gas excess_ value, $T$ and $R$ together determine the shape of the curve. In Ethereum, $T$ is 15 million and $R$ is 12.5%; it's yet to be decided what value we should use in Taiko. + +![4f785d35722c2f255a448c7803d511a0bb2b148c](https://user-images.githubusercontent.com/99078276/229010491-a3fcddd5-1798-47af-bafc-5d680fbb574c.png) + +### Implementation of $e^x$ + +We steal the `exp(x)` implementation from https://github.com/recmo/experiment-solexp/blob/main/src/test/FixedPointMathLib.t.sol. This implementation has a limitation: the range is input parameter `x` is `[-42.139678854, + 135.305999369]` with 18 decimals/precision. In our case, we need to map gas excess to the range of `[0, K]` where `K` equals `135.305999369` or `135305999368893231588` in fixed point integer form. + +The $e^x$ curve can be expressed using $$py=e^{qx}$$, as you can see below: the two parameters $p$ and $q$ defines the shape/slope of the curve. We need to find the right value for them, otherwise, the basefee movement will not be as expected. + +Screenshot 2023-03-30 at 17 21 19 + +(the plot above is available at https://www.desmos.com/calculator/yncurfx3ar) + +## Scaling + +The following is how we calculate $p$ and $q$. Assuming the max gas excess $M$, a uint64. then $q = 135305999368893231588/M$ (internally we keep $q'=q <<64$ as it fits into a uint64). + +We also assuming the initial value of gasExcess is $M/2$; and the initial basefee (the fee for purchasing 1 gas) is $b_0$, or $$b_0=p e^{(M/2 + 1)} + p e^{M/2}$$, so $$p = b_0/(e^{(M/2 + 1)} + e^{M/2})$$. + +It turns out the initial value of gasExcess doesn't really matter for the above calculation due to the nature of the e-curve. But choosing $M/2$ allows price to go up and down by the same max amount. + +## Adjust the slope + +To adjust the slope of the curve to satisfy $R == basefee(2T)/basefee(T)$, we simply need to chose $M$ and $b_0$. $b_0$ is simply to decide -- if we believe the cost of a L2 transaction is $1/n$ of the same L1 transaction, we simply use the current L1 base fee divided by $n$. Then we can simply tune $M$ to make sure $R == basefee(2T)/basefee(T)$ holds. This is very simply manually a try-and-adjust approach as shown in `LibL2Tokenomics.t.sol`. The TaikoL1 contract will check if $R == basefee(2T)/basefee(T)$ holds but will not calculate $M$ for us. From 5b294a9d5b001ce1a063c34b88e210d9b98b54c8 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 31 Mar 2023 13:25:44 +0800 Subject: [PATCH 48/90] add doc --- .../contracts/L1/libs/LibL2Tokenomics.sol | 39 +++++---- .../contracts/L1/libs/LibVerifying.sol | 20 ++--- packages/protocol/test2/LibL2Tokenomics.t.sol | 84 ++++++++++++++----- 3 files changed, 98 insertions(+), 45 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 7ca8cb525ef..fa838f7ab27 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -39,13 +39,19 @@ library LibL2Tokenomics { newGasExcess = uint64(reduced.max(state.l2GasExcess) - reduced); } - basefee = calcL2Basefee({ + uint256 _basefee = calcL2Basefee({ l2GasExcess: newGasExcess, xscale: state.l2Xscale, yscale: uint256(state.l2Yscale) << 64, gasAmount: gasLimit }).toUint64(); + if (_basefee >= type(uint64).max) { + // This is a valid case when the curve slope is large. + revert L1_OUT_OF_BLOCK_SPACE(); + } + + basefee = uint64(_basefee); newGasExcess += gasLimit; } @@ -54,7 +60,11 @@ library LibL2Tokenomics { uint64 basefeeInitial, uint64 gasTarget, uint64 expected2X1XRatio - ) internal pure returns (uint64 l2GasExcess, uint64 xscale, uint64 yscale) { + ) + internal + pure + returns (uint64 l2GasExcess, uint64 xscale, uint256 yscale) + { assert(gasExcessMax != 0); l2GasExcess = gasExcessMax / 2; @@ -66,33 +76,30 @@ library LibL2Tokenomics { } xscale = uint64(_xscale); - // calculate yscale - uint256 _yscale = calcL2Basefee( - l2GasExcess, - xscale, - basefeeInitial, - gasTarget + require( + uint(xscale) * gasExcessMax < LibFixedPointMath.MAX_EXP_INPUT, + "FFF" ); - if ((_yscale >> 64) >= type(uint64).max) { + + // calculate yscale + yscale = calcL2Basefee(l2GasExcess, xscale, basefeeInitial, gasTarget); + if ((yscale >> 64) >= type(uint64).max) { revert L1_1559_Y_SCALE_TOO_LARGE(); } - yscale = uint64(_yscale >> 64); - // Verify the gas price ratio between two blocks, one has // 2*gasTarget gas and the other one has gasTarget gas. { - _yscale = uint256(yscale) << 64; uint256 price1x = calcL2Basefee( l2GasExcess, xscale, - _yscale, + yscale, gasTarget ); uint256 price2x = calcL2Basefee( l2GasExcess, xscale, - _yscale, + yscale, gasTarget * 2 ); @@ -121,7 +128,9 @@ library LibL2Tokenomics { uint256 xscale ) private pure returns (uint256) { uint256 x = l2GasExcess * xscale; - if (x > LibFixedPointMath.MAX_EXP_INPUT) revert L1_OUT_OF_BLOCK_SPACE(); + if (x > LibFixedPointMath.MAX_EXP_INPUT) { + revert L1_OUT_OF_BLOCK_SPACE(); + } return uint256(LibFixedPointMath.exp(int256(x))); } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 404112941e4..9062717b2cd 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -66,16 +66,16 @@ library LibVerifying { l2Expected2X1XRatio == 0 ) revert L1_INVALID_L21559_PARAMS(); - ( - state.l2GasExcess, - state.l2Xscale, - state.l2Yscale - ) = LibL2Tokenomics.calcL2BasefeeParams( - l2GasExcessMax, - l2BasefeeInitial, - l2GasTarget, - l2Expected2X1XRatio - ); + uint256 yscale; + (state.l2GasExcess, state.l2Xscale, yscale) = LibL2Tokenomics + .calcL2BasefeeParams( + l2GasExcessMax, + l2BasefeeInitial, + l2GasTarget, + l2Expected2X1XRatio + ); + + state.l2Yscale = uint64(yscale >> 64); } emit BlockVerified(0, l2GenesisBlockHash); diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 2eb27615e8d..de8807922d9 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -15,32 +15,76 @@ import { contract TestLibL2Tokenomics is Test { using SafeCastUpgradeable for uint256; - function test1559PurchaseMaxSizeGasWontOverflow() public view { - uint64 basefeeInitial = 5000000000; - uint64 l2GasExcessMax = 15000000 * 256; + function test1559_basefeeMayBeZero() public { + uint64 gasExcessMax = 15000000 * 256; uint64 gasTarget = 6000000; - uint64 expected2X1XRatio = 111; // 11 %% + uint64 basefeeInitial = 5000000000; + uint64 expected2X1XRatio = 111; (uint64 l2GasExcess, uint64 xscale, uint256 yscale) = T - .calcL2BasefeeParams( - l2GasExcessMax, - basefeeInitial, - gasTarget, - expected2X1XRatio + .calcL2BasefeeParams({ + gasExcessMax: gasExcessMax, + basefeeInitial: basefeeInitial, + gasTarget: gasTarget, + expected2X1XRatio: expected2X1XRatio + }); + + // basefee should be 0 when gasExcess is 0 + assertEq(T.calcL2Basefee(0, xscale, yscale, gasTarget), 0); + + // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] + // gas range, the expected2X1XRatio holds. + l2GasExcess = gasExcessMax / 2 - 50 * gasTarget; + + while (l2GasExcess <= gasExcessMax / 2 + 50 * gasTarget) { + uint256 basefee1 = T.calcL2Basefee( + l2GasExcess, + xscale, + yscale, + gasTarget + ); + uint256 basefee2 = T.calcL2Basefee( + l2GasExcess, + xscale, + yscale, + 2 * gasTarget ); - uint64 _basefee = basefeeInitial; - console2.log("basefee", _basefee); - l2GasExcess += gasTarget; - - for (uint i = 0; i < 10; ++i) { - uint64 newBasefee = T - .calcL2Basefee(l2GasExcess, xscale, yscale << 64, gasTarget) - .toUint64(); - uint ratio = (newBasefee * 100) / _basefee - 100; - console2.log("basefee", newBasefee, "+%", ratio); - _basefee = newBasefee; + if (basefee1 != 0 && basefee2 != 0) { + assertEq((basefee2 * 100) / basefee1, expected2X1XRatio); + } + l2GasExcess += gasTarget; } } + + // function test1559PurchaseMaxSizeGasWontOverflow() public view { + // uint64 basefeeInitial = 5000000000; + // uint64 gasTarget = 6000000; + + // (uint64 _l2GasExcess, uint64 xscale, uint256 yscale) = T + // .calcL2BasefeeParams({ + // gasExcessMax: 15000000 * 256, + // basefeeInitial: basefeeInitial, + // gasTarget: gasTarget, + // expected2X1XRatio: 111 + // }); + + // uint64 l2GasExcess = 0; + // uint64 basefee = 0; + + // for (uint i = 0; i < 1 ; ++i) { + + // uint64 basefee = T + // .calcL2Basefee(l2GasExcess, xscale, yscale, gasTarget) + // .toUint64(); + + // // uint ratio = (newBasefee * 100) / _basefee; + // console2.log(i,l2GasExcess, "basefee", basefee);//, "+%", ratio); + // // basefee = newBasefee; + + // // if (uint256(l2GasExcess) + gasTarget > type(uint64).max) break; + // // l2GasExcess += gasTarget; + // } + // } } From fcc1facd1d334d8a4c2369c3f6f633bb5d609963 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 31 Mar 2023 13:31:27 +0800 Subject: [PATCH 49/90] +deploy_on_l1.sh --- packages/protocol/test2/LibL2Tokenomics.t.sol | 45 ++++--------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index de8807922d9..55a6f59eb62 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -15,7 +15,7 @@ import { contract TestLibL2Tokenomics is Test { using SafeCastUpgradeable for uint256; - function test1559_basefeeMayBeZero() public { + function test1559_2X1XRatio() public { uint64 gasExcessMax = 15000000 * 256; uint64 gasTarget = 6000000; uint64 basefeeInitial = 5000000000; @@ -32,11 +32,14 @@ contract TestLibL2Tokenomics is Test { // basefee should be 0 when gasExcess is 0 assertEq(T.calcL2Basefee(0, xscale, yscale, gasTarget), 0); + uint64 N = 50; // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] // gas range, the expected2X1XRatio holds. - l2GasExcess = gasExcessMax / 2 - 50 * gasTarget; - - while (l2GasExcess <= gasExcessMax / 2 + 50 * gasTarget) { + for ( + l2GasExcess = gasExcessMax / 2 - N * gasTarget; + l2GasExcess <= gasExcessMax / 2 + N * gasTarget; + l2GasExcess += gasTarget + ) { uint256 basefee1 = T.calcL2Basefee( l2GasExcess, xscale, @@ -50,41 +53,9 @@ contract TestLibL2Tokenomics is Test { 2 * gasTarget ); - if (basefee1 != 0 && basefee2 != 0) { + if (basefee1 != 0) { assertEq((basefee2 * 100) / basefee1, expected2X1XRatio); } - - l2GasExcess += gasTarget; } } - - // function test1559PurchaseMaxSizeGasWontOverflow() public view { - // uint64 basefeeInitial = 5000000000; - // uint64 gasTarget = 6000000; - - // (uint64 _l2GasExcess, uint64 xscale, uint256 yscale) = T - // .calcL2BasefeeParams({ - // gasExcessMax: 15000000 * 256, - // basefeeInitial: basefeeInitial, - // gasTarget: gasTarget, - // expected2X1XRatio: 111 - // }); - - // uint64 l2GasExcess = 0; - // uint64 basefee = 0; - - // for (uint i = 0; i < 1 ; ++i) { - - // uint64 basefee = T - // .calcL2Basefee(l2GasExcess, xscale, yscale, gasTarget) - // .toUint64(); - - // // uint ratio = (newBasefee * 100) / _basefee; - // console2.log(i,l2GasExcess, "basefee", basefee);//, "+%", ratio); - // // basefee = newBasefee; - - // // if (uint256(l2GasExcess) + gasTarget > type(uint64).max) break; - // // l2GasExcess += gasTarget; - // } - // } } From d9d4940f364699e216cdf48756f597b19ea2c91a Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 31 Mar 2023 15:40:20 +0800 Subject: [PATCH 50/90] +deploy_on_l1.sh --- .../contracts/L1/libs/LibL2Tokenomics.sol | 20 ++---- .../contracts/L1/libs/LibVerifying.sol | 14 ++-- packages/protocol/test2/LibL2Tokenomics.t.sol | 65 +++++++++++++++---- 3 files changed, 65 insertions(+), 34 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index fa838f7ab27..3edd2a6cb57 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -60,14 +60,10 @@ library LibL2Tokenomics { uint64 basefeeInitial, uint64 gasTarget, uint64 expected2X1XRatio - ) - internal - pure - returns (uint64 l2GasExcess, uint64 xscale, uint256 yscale) - { + ) internal pure returns (uint64 xscale, uint256 yscale) { assert(gasExcessMax != 0); - l2GasExcess = gasExcessMax / 2; + uint64 l2GasExcess = gasExcessMax / 2; // calculate xscale uint256 _xscale = LibFixedPointMath.MAX_EXP_INPUT / gasExcessMax; @@ -76,11 +72,6 @@ library LibL2Tokenomics { } xscale = uint64(_xscale); - require( - uint(xscale) * gasExcessMax < LibFixedPointMath.MAX_EXP_INPUT, - "FFF" - ); - // calculate yscale yscale = calcL2Basefee(l2GasExcess, xscale, basefeeInitial, gasTarget); if ((yscale >> 64) >= type(uint64).max) { @@ -117,10 +108,11 @@ library LibL2Tokenomics { uint256 yscale, uint64 gasAmount ) internal pure returns (uint256) { - assert(gasAmount != 0 && xscale != 0 && yscale != 0); + uint64 _gasAmount = gasAmount == 0 ? 1 : gasAmount; + assert(xscale != 0 && yscale != 0); uint256 _before = _ethqty(l2GasExcess, xscale); - uint256 _after = _ethqty(l2GasExcess + gasAmount, xscale); - return (_after - _before) / gasAmount / yscale; + uint256 _after = _ethqty(l2GasExcess + _gasAmount, xscale); + return (_after - _before) / _gasAmount / yscale; } function _ethqty( diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 9062717b2cd..4648e624015 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -67,15 +67,15 @@ library LibVerifying { ) revert L1_INVALID_L21559_PARAMS(); uint256 yscale; - (state.l2GasExcess, state.l2Xscale, yscale) = LibL2Tokenomics - .calcL2BasefeeParams( - l2GasExcessMax, - l2BasefeeInitial, - l2GasTarget, - l2Expected2X1XRatio - ); + (state.l2Xscale, yscale) = LibL2Tokenomics.calcL2BasefeeParams( + l2GasExcessMax, + l2BasefeeInitial, + l2GasTarget, + l2Expected2X1XRatio + ); state.l2Yscale = uint64(yscale >> 64); + state.l2GasExcess = l2GasExcessMax / 2; } emit BlockVerified(0, l2GenesisBlockHash); diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 55a6f59eb62..c8e62be3b15 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -15,19 +15,19 @@ import { contract TestLibL2Tokenomics is Test { using SafeCastUpgradeable for uint256; - function test1559_2X1XRatio() public { - uint64 gasExcessMax = 15000000 * 256; - uint64 gasTarget = 6000000; - uint64 basefeeInitial = 5000000000; - uint64 expected2X1XRatio = 111; + function test1559_2X1XRatio(uint16 rand) public { + vm.assume(rand != 0); - (uint64 l2GasExcess, uint64 xscale, uint256 yscale) = T - .calcL2BasefeeParams({ - gasExcessMax: gasExcessMax, - basefeeInitial: basefeeInitial, - gasTarget: gasTarget, - expected2X1XRatio: expected2X1XRatio - }); + uint64 gasExcessMax = (uint(15000000) * 256 * rand).toUint64(); + uint64 gasTarget = (uint(6000000) * rand).toUint64(); + uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); + uint64 expected2X1XRatio = 111; + (uint64 xscale, uint256 yscale) = T.calcL2BasefeeParams({ + gasExcessMax: gasExcessMax, + basefeeInitial: basefeeInitial, + gasTarget: gasTarget, + expected2X1XRatio: expected2X1XRatio + }); // basefee should be 0 when gasExcess is 0 assertEq(T.calcL2Basefee(0, xscale, yscale, gasTarget), 0); @@ -36,7 +36,7 @@ contract TestLibL2Tokenomics is Test { // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] // gas range, the expected2X1XRatio holds. for ( - l2GasExcess = gasExcessMax / 2 - N * gasTarget; + uint64 l2GasExcess = gasExcessMax / 2 - N * gasTarget; l2GasExcess <= gasExcessMax / 2 + N * gasTarget; l2GasExcess += gasTarget ) { @@ -58,4 +58,43 @@ contract TestLibL2Tokenomics is Test { } } } + + function test1559_SpecalCases(uint16 rand) public { + vm.assume(rand != 0); + + uint64 gasExcessMax = (uint(15000000) * 256 * rand).toUint64(); + uint64 gasTarget = (uint(6000000) * rand).toUint64(); + uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); + uint64 expected2X1XRatio = 111; + + (uint64 xscale, uint256 yscale) = T.calcL2BasefeeParams({ + gasExcessMax: gasExcessMax, + basefeeInitial: basefeeInitial, + gasTarget: gasTarget, + expected2X1XRatio: expected2X1XRatio + }); + + assertEq(T.calcL2Basefee(0, xscale, yscale, 0), 0); + assertEq(T.calcL2Basefee(0, xscale, yscale, 1), 0); + + assertGt( + T.calcL2Basefee( + gasExcessMax - gasTarget, + xscale, + yscale, + gasTarget + ), + type(uint64).max + ); + + assertGt( + T.calcL2Basefee(0, xscale, yscale, gasExcessMax), + type(uint64).max + ); + + assertGt( + T.calcL2Basefee(gasExcessMax / 2, xscale, yscale, gasExcessMax / 2), + type(uint64).max + ); + } } From 45a94fa790632d3f14db0c14e24c5ed69ddbdfac Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 31 Mar 2023 15:42:39 +0800 Subject: [PATCH 51/90] +deploy_on_l1.sh --- packages/protocol/test2/LibL2Tokenomics.t.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index c8e62be3b15..16ed210d053 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -34,7 +34,8 @@ contract TestLibL2Tokenomics is Test { uint64 N = 50; // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] - // gas range, the expected2X1XRatio holds. + // gas range, the expected2X1XRatio holds, and the gas price is still smaller + // than uint64.max for ( uint64 l2GasExcess = gasExcessMax / 2 - N * gasTarget; l2GasExcess <= gasExcessMax / 2 + N * gasTarget; @@ -46,6 +47,8 @@ contract TestLibL2Tokenomics is Test { yscale, gasTarget ); + assertLt(basefee1, type(uint64).max); + uint256 basefee2 = T.calcL2Basefee( l2GasExcess, xscale, @@ -53,6 +56,8 @@ contract TestLibL2Tokenomics is Test { 2 * gasTarget ); + assertLt(basefee2, type(uint64).max); + if (basefee1 != 0) { assertEq((basefee2 * 100) / basefee1, expected2X1XRatio); } From ccebb17467b8c401524e1ada2daf5ab7b01bbd46 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Fri, 31 Mar 2023 20:41:57 +0200 Subject: [PATCH 52/90] Changes: - changed proofTimeTarget config - New tests (LibL1Tokenomics.t.sol completely changed) - remove proposerDepositPctg; since the proposing time is decoupled from the fee calculation --- .../protocol/contracts/L1/TaikoConfig.sol | 3 +- packages/protocol/contracts/L1/TaikoData.sol | 1 - .../contracts/L1/libs/LibL1Tokenomics.sol | 2 +- .../contracts/L1/libs/LibVerifying.sol | 1 + packages/protocol/test2/LibL1Tokenomics.t.sol | 666 +++++++++++------- packages/protocol/test2/TaikoL1.t.sol | 86 +-- packages/protocol/test2/TaikoL1TestBase.sol | 11 + 7 files changed, 438 insertions(+), 332 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 2c5df8fb81b..16f252d7c49 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -36,12 +36,11 @@ library TaikoConfig { slotSmoothingFactor: 946649, // 100 basis points or 1% rewardBurnBips: 100, - proposerDepositPctg: 0, // - 25% // Moving average factors feeBaseMAF: 1024, constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, - proofTimeTarget: 60, // 60sec general + proofTimeTarget: 90, // 90sec general adjustmentQuotient: 16, enableSoloProposer: false, enableOracleProver: true, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 29515f194f0..59e08cff2e1 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -28,7 +28,6 @@ library TaikoData { uint256 minTxGasLimit; uint256 slotSmoothingFactor; uint256 rewardBurnBips; - uint256 proposerDepositPctg; // Moving average factors uint256 feeBaseMAF; uint64 constantFeeRewardBlocks; diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index a9579ee375f..a65751ba35d 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -64,7 +64,7 @@ library LibL1Tokenomics { TaikoData.State storage state, uint32 gasUsed ) internal view returns (uint64 newFeeBase, uint64 fee) { - newFeeBase = fee = state.baseFeeProof; + newFeeBase = state.baseFeeProof; fee = newFeeBase * gasUsed; } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index d9e81160e07..c28e7dea448 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -40,6 +40,7 @@ library LibVerifying { state.genesisHeight = timeNow; state.genesisTimestamp = timeNow; state.feeBase = feeBase; + state.baseFeeProof = 1; // Symbolic fee (TKO) for 1st proposal state.numBlocks = 1; state.gasAccumulated = gasAccumulated; diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index e7da16095b8..7e921172bea 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -3,281 +3,459 @@ pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; +import {AddressManager} from "../contracts/thirdparty/AddressManager.sol"; +import {TaikoConfig} from "../contracts/L1/TaikoConfig.sol"; import {TaikoData} from "../contracts/L1/TaikoData.sol"; -import {LibL1Tokenomics} from "../contracts/L1/libs/LibL1Tokenomics.sol"; -import { - SafeCastUpgradeable -} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; - -contract TestLibL1Tokenomics is Test { - using SafeCastUpgradeable for uint256; - - struct FeeConfig { - uint64 avgTimeMAF; - uint64 avgTimeCap; - uint64 gracePeriodPctg; - uint64 maxPeriodPctg; - // extra fee/reward on top of baseFee - uint64 multiplerPctg; - } +import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; +import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; +import {SignalService} from "../contracts/signal/SignalService.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {TaikoL1TestBase} from "./TaikoL1TestBase.sol"; - function xtestTokenomicsFeeCalcWithNonZeroStartBips() public { - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 0 seconds, - isProposal: true, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 * 1E8, - expectedPreimumRate: 0 - }); +contract TaikoL1WithConfig is TaikoL1 { + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config = TaikoConfig.getConfig(); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 20 seconds, - isProposal: true, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 120 * 1E8, - expectedPreimumRate: 0 - }); + config.enableTokenomics = true; + config.constantFeeRewardBlocks = 0; + config.txListCacheExpiry = 5 minutes; + config.maxVerificationsPerTx = 0; + config.enableSoloProposer = false; + config.enableOracleProver = false; + config.maxNumProposedBlocks = 10; + config.ringBufferSize = 12; + // this value must be changed if `maxNumProposedBlocks` is changed. + config.slotSmoothingFactor = 4160; - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 40 seconds, - isProposal: true, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 + config.proposingConfig = TaikoData.FeeConfig({ + avgTimeMAF: 1024, + dampingFactorBips: 5000 }); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 60 seconds, - isProposal: true, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 80 * 1E8, - expectedPreimumRate: 0 + config.provingConfig = TaikoData.FeeConfig({ + avgTimeMAF: 1024, + dampingFactorBips: 5000 }); + } +} - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 80 seconds, - isProposal: true, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 * 1E8, - expectedPreimumRate: 0 - }); +// Since the fee/reward calculation heavily depends on the baseFeeProof and the proofTime +// we need to simulate proposing/proving so that can calculate them. +contract LibL1TokenomicsTest is TaikoL1TestBase { + function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { + taikoL1 = new TaikoL1WithConfig(); + } - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 81 seconds, - isProposal: true, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 * 1E8, - expectedPreimumRate: 0 - }); + function setUp() public override { + TaikoL1TestBase.setUp(); + + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); } - function xtestTokenomicsFeeCalcWithZeroStartBips() public { - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 0 seconds, - isProposal: true, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + /// @dev Test blockFee for first block + function test_getBlockFee() external { + uint32 gasLimit = 10000000; + uint256 fee = L1.getBlockFee(gasLimit); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 20 seconds, - isProposal: true, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + // First block propoal has a symbolic 1 unit of baseFeeProof so here gasLimit and fee shall be equal + assertEq(gasLimit, fee); + } - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 40 seconds, - isProposal: true, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + /// @dev Test what happens when proof time increases + function test_reward_and_fee_if_proof_time_increases() external { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 60 seconds, - isProposal: true, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + bytes32 parentHash = GENESIS_BLOCK_HASH; - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 80 seconds, - isProposal: true, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + printVariables("after propose"); + mine(blockId); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 81 seconds, - isProposal: true, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + printVariables(""); } - function xtestTokenomicsRewardCalcWithNonZeroStartBips() public { - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 0 seconds, - isProposal: false, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 60 * 1E8, - expectedPreimumRate: 0 - }); + /// @dev Test what happens when proof time decreases + function test_reward_and_fee_if_proof_time_decreases() external { + mine(1); + _depositTaikoToken(Alice, 1E7 * 1E8, 1 ether); + _depositTaikoToken(Bob, 1E7 * 1E8, 1 ether); + _depositTaikoToken(Carol, 1E7 * 1E8, 1 ether); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 20 seconds, - isProposal: false, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 80 * 1E8, - expectedPreimumRate: 0 - }); + bytes32 parentHash = GENESIS_BLOCK_HASH; - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 40 seconds, - isProposal: false, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(11 - blockId); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 60 seconds, - isProposal: false, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 120 * 1E8, - expectedPreimumRate: 5000 - }); + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + printVariables("after proved"); + parentHash = blockHash; + } + printVariables(""); + } - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 80 seconds, - isProposal: false, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 * 1E8, - expectedPreimumRate: 10000 - }); + /// @dev Test what happens when proof time stable + function test_reward_and_fee_if_proof_time_stable_and_below_time_target() + external + { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 81 seconds, - isProposal: false, - dampingFactorBips: 4000, // 40% - expectedFeeBase: 140 * 1E8, - expectedPreimumRate: 10000 - }); + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + printVariables(""); } - function xtestTokenomicsRewardCalcWithZeroStartBips() public { - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 0 seconds, - isProposal: false, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + /// @dev Test blockFee when proof target is stable but slightly above the target + function test_reward_and_fee_if_proof_time_stable_but_above_time_target() + external + { + uint32 gasLimit = 10000000; + uint256 fee = L1.getBlockFee(gasLimit); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 20 seconds, - isProposal: false, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 40 seconds, - isProposal: false, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + bytes32 parentHash = GENESIS_BLOCK_HASH; - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 60 seconds, - isProposal: false, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + for (uint256 blockId = 1; blockId < 50; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + //Constant 5 means = 100 sec (90 sec is the target) + mine(5); - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 80 seconds, - isProposal: false, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } - xtestTimeAdjustedFee({ - feeBase: 100 * 1E8, - timeAverageSec: 40 seconds, - timeUsedSec: 81 seconds, - isProposal: false, - dampingFactorBips: 0, // 0% - expectedFeeBase: 100 * 1E8, - expectedPreimumRate: 0 - }); + // First block propoal has a symbolic 1 unit of baseFeeProof so here gasLimit and fee shall be equal + assertEq(gasLimit, fee); + } + + /// @dev Test what happens when proof time decreasing then stabilizes + function test_reward_and_fee_if_proof_time_decreasing_then_stabilizes() + external + { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + console2.log("Decreasing"); + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(21 - blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + console2.log("Stable"); + for (uint256 blockId = 1; blockId < 40; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + uint256 Alice_end_balance = L1.getBalance(Alice); + uint256 Bob_end_balance = L1.getBalance(Bob); + + console2.log("Alice balance:", Alice_end_balance); + console2.log("Bob balance:", Bob_end_balance); + + // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount + // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test + uint256 aliceChange = Alice_start_balance - Alice_end_balance; + uint256 bobChange = Bob_end_balance - Bob_start_balance; + + console2.log("Alice change:", aliceChange); + console2.log("Bob change:", bobChange); + + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(aliceChange, bobChange, 1e17); } - function xtestTimeAdjustedFee( - uint256 feeBase, - uint256 timeAverageSec, - uint256 timeUsedSec, - bool isProposal, - uint16 dampingFactorBips, - uint256 expectedFeeBase, - uint256 expectedPreimumRate - ) private { - // Todo:Implement the verification tests + /// @dev Test what happens when proof time increasing then stabilizes + function test_reward_and_fee_if_proof_time_increasing_then_stabilizes_below_the_proof_time_target() + external + { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + console2.log("Increasing"); + for (uint256 blockId = 1; blockId < 20; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + console2.log("Stable"); + for (uint256 blockId = 1; blockId < 40; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + uint256 Alice_end_balance = L1.getBalance(Alice); + uint256 Bob_end_balance = L1.getBalance(Bob); + + console2.log("Alice balance:", Alice_end_balance); + console2.log("Bob balance:", Bob_end_balance); + + // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount + // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test + uint256 aliceChange = Alice_start_balance - Alice_end_balance; + uint256 bobChange = Bob_end_balance - Bob_start_balance; + + console2.log("Alice change:", aliceChange); + console2.log("Bob change:", bobChange); + + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(aliceChange, bobChange, 1e17); + } + + /// @dev Test what happens when proof time fluctuates then stabilizes + function test_reward_and_fee_if_proof_time_fluctuates_then_stabilizes() + external + { + mine(1); + _depositTaikoToken(Alice, 1E7 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E7 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + console2.log("Increasing"); + for (uint256 blockId = 1; blockId < 20; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + console2.log("Decreasing"); + for (uint256 blockId = 1; blockId < 20; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(21 - blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + console2.log("Stable"); + for (uint256 blockId = 1; blockId < 40; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + uint256 Alice_end_balance = L1.getBalance(Alice); + uint256 Bob_end_balance = L1.getBalance(Bob); + + console2.log("Alice balance:", Alice_end_balance); + console2.log("Bob balance:", Bob_end_balance); + + // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount + // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test + uint256 aliceChange = Alice_start_balance - Alice_end_balance; + uint256 bobChange = Bob_end_balance - Bob_start_balance; + + console2.log("Alice change:", aliceChange); + console2.log("Bob change:", bobChange); + + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(aliceChange, bobChange, 1e17); + } + + /// @dev Test blockFee start decreasing when the proof time goes below proof target (given gas used is the same) + function test_getBlockFee_is_higher_when_increasing_proof_time() external { + uint32 gasLimit = 10000000; + uint256 previousfee = L1.getBlockFee(gasLimit); + bytes32 parentHash = GENESIS_BLOCK_HASH; + + for (uint256 blockId = 1; blockId < 10; blockId++) { + uint256 previousFee = L1.getBlockFee(gasLimit); + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + uint256 actualFee = L1.getBlockFee(gasLimit); + // Check that fee always increasing in this scenario + assertGt(actualFee, previousFee); + } + printVariables(""); + } + + /// @dev Test blockFee starts decreasing when the proof time goes below proof target + function test_getBlockFee_starts_decreasing_when_proof_time_falls_below_the_average() + external + { + uint32 gasLimit = 10000000; + uint256 fee = L1.getBlockFee(gasLimit); + bytes32 parentHash = GENESIS_BLOCK_HASH; + uint256 blockId; + uint256 previousFee; + uint256 actualFee; + + for (blockId = 1; blockId < 10; blockId++) { + previousFee = L1.getBlockFee(gasLimit); + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(50 - blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + + actualFee = L1.getBlockFee(gasLimit); + // Check that fee always increasing in this scenario + assertGt(actualFee, previousFee); + } + + // Start proving below proof time - will affect the next proposal only after + mine(1); + previousFee = L1.getBlockFee(gasLimit); + printVariables("See still higher"); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(1); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + //After this verification - the proof time falls below the target average, so it will start decreasing + verifyBlock(Carol, 1); + + actualFee = L1.getBlockFee(gasLimit); + assertGt(previousFee, actualFee); } } diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index d309c286717..324c5367365 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -44,12 +44,6 @@ contract TaikoL1WithConfig is TaikoL1 { } } -contract Verifier { - fallback(bytes calldata) external returns (bytes memory) { - return bytes.concat(keccak256("taiko")); - } -} - contract TaikoL1Test is TaikoL1TestBase { function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { taikoL1 = new TaikoL1WithConfig(); @@ -57,14 +51,10 @@ contract TaikoL1Test is TaikoL1TestBase { function setUp() public override { TaikoL1TestBase.setUp(); - _registerAddress( - string(abi.encodePacked("verifier_", uint16(100))), - address(new Verifier()) - ); } /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' - function xtest_more_blocks_than_ring_buffer_size() external { + function test_more_blocks_than_ring_buffer_size() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); @@ -92,7 +82,7 @@ contract TaikoL1Test is TaikoL1TestBase { /// @dev Test more than one block can be proposed, proven, & verified in the /// same L1 block. - function xtest_multiple_blocks_in_one_L1_block() external { + function test_multiple_blocks_in_one_L1_block() external { _depositTaikoToken(Alice, 1000 * 1E8, 1000 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; @@ -188,76 +178,4 @@ contract TaikoL1Test is TaikoL1TestBase { } printVariables(""); } - - /// @dev Test what happens when proof time increases - function test_reward_and_fee_if_proof_time_increases() external { - mine(1); - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for (uint256 blockId = 1; blockId < 10; blockId++) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - printVariables("after propose"); - mine(blockId); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - parentHash = blockHash; - } - printVariables(""); - } - - /// @dev Test what happens when proof time decreases - function test_reward_and_fee_if_proof_time_decreases() external { - mine(1); - _depositTaikoToken(Alice, 1E7 * 1E8, 1 ether); - _depositTaikoToken(Bob, 1E7 * 1E8, 1 ether); - _depositTaikoToken(Carol, 1E7 * 1E8, 1 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for (uint256 blockId = 1; blockId < 10; blockId++) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(11 - blockId); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - printVariables("after proved"); - parentHash = blockHash; - } - printVariables(""); - } - - /// @dev Test what happens when proof time stable - function test_reward_and_fee_if_proof_time_stable() external { - mine(1); - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); - - bytes32 parentHash = GENESIS_BLOCK_HASH; - - for (uint256 blockId = 1; blockId < 10; blockId++) { - printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - printVariables("after propose"); - mine(1); - - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - parentHash = blockHash; - } - printVariables(""); - } } diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.sol index 2d6aadcbc53..e17b2de5aad 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.sol @@ -11,6 +11,12 @@ import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; import {SignalService} from "../contracts/signal/SignalService.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +contract Verifier { + fallback(bytes calldata) external returns (bytes memory) { + return bytes.concat(keccak256("taiko")); + } +} + abstract contract TaikoL1TestBase is Test { AddressManager public addressManager; TaikoToken public tko; @@ -78,6 +84,11 @@ abstract contract TaikoL1TestBase is Test { _registerL2Address("signal_service", address(L2SS)); _registerL2Address("taiko_l2", address(L2TaikoL2)); + _registerAddress( + string(abi.encodePacked("verifier_", uint16(100))), + address(new Verifier()) + ); + printVariables("init "); } From 867f384a1f02f043ccacc5ff400d14cbdde4042c Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Fri, 31 Mar 2023 20:58:38 +0200 Subject: [PATCH 53/90] Remove python algo --- packages/protocol/scripts/eip1559.py | 129 --------------------------- 1 file changed, 129 deletions(-) delete mode 100755 packages/protocol/scripts/eip1559.py diff --git a/packages/protocol/scripts/eip1559.py b/packages/protocol/scripts/eip1559.py deleted file mode 100755 index 1f196b0ebe6..00000000000 --- a/packages/protocol/scripts/eip1559.py +++ /dev/null @@ -1,129 +0,0 @@ -import math - -ETH_BLOCK_TIME = 12 -GAS_TARGET = 5000000 # target L2 gas (per ETH_BLOCK_TIME seconds) -ADJUSTMENT_QUOTIENT = 32 - -PROVER_REWARD_TARGET_PER_GAS = 0.1 # TAI/GAS in block rewards to prover -PROVER_TARGET_DELAY_PER_GAS = 0.001 # TODO: change to something dynamic probably - -time = 0 - -# network fee -gas_issued = 0 -last_time = time - -# prover fee -basefee_proof = 0 -blockreward_issued = 0 - - -# This calculates the amount of 'Ether' (or token) based on the inputs, using an exponential function. -# Question: This one we need -def eth_amount(value, target): - return math.exp(value / target / ADJUSTMENT_QUOTIENT) - -# This function calculates the network fee for a given amount of gas in a block -# Question: This shall be "de-coupled" from the prover - proposer TKO distribution (!?) -def network_fee(gas_in_block): - global gas_issued - global last_time - - gas_issued = max(0, gas_issued - GAS_TARGET * ((time - last_time)/ETH_BLOCK_TIME)) - # # # # print('Debug prints:') - # # # # print(gas_issued) - # # # # print(gas_in_block) - - # Will change based on simply elapsed time -> which will increase the gas_issued accumulation - cost = eth_amount(gas_issued + gas_in_block, GAS_TARGET) - eth_amount(gas_issued, GAS_TARGET) - gas_issued = gas_issued + gas_in_block - - last_time = time - - return cost - -# This function updates the base fee for proving a block, based on the amount of gas in the block and the block reward. -# Question: This shall be updated in the verifyBlock(), right ? OK -def update_basefee_proof(gas_in_block, block_reward): - global blockreward_issued - global basefee_proof - - # # # # print('UpdateBaseFee proof') - # # # # print(blockreward_issued + block_reward - PROVER_REWARD_TARGET_PER_GAS * gas_in_block) - - blockreward_issued = max(0, blockreward_issued + block_reward - PROVER_REWARD_TARGET_PER_GAS * gas_in_block) - basefee_proof = eth_amount(blockreward_issued/gas_in_block, PROVER_REWARD_TARGET_PER_GAS) / (PROVER_REWARD_TARGET_PER_GAS * ADJUSTMENT_QUOTIENT) - - # # # # print('Blockreward issued:') - # # # # print(blockreward_issued) - - return basefee_proof - -# This function calculates the prover fee for a given amount of gas in a block. -# It simply multiplies the gas in the block by the current base fee for proving. -def prover_fee(gas_in_block): - # # # # print('WHat is basefee_proof') - # # # # print(basefee_proof) - return gas_in_block * basefee_proof - -# This function calculates the block reward based on the amount of gas in a block and the delay. -# This is placed in the verifyBlock() function -def calc_block_reward(gas_in_block, delay): - # TODO: probably something else than this - - # Some notes: - # Bigger the delay, the higher the reward --> BUT actually is it ? I mean, if we calculate something on proposeBlock() it might differ (be more) when slower proof is submitted ? - - # # # # # Ez 'csak' a delay-en es a gas-on fugg, semmi mason. - # # # # print("a calc block rewardban vagyok") - # # # # print(PROVER_TARGET_DELAY_PER_GAS) - # # # # print(gas_in_block) - # # # # print(delay) - # # # # print(PROVER_REWARD_TARGET_PER_GAS) - - return PROVER_REWARD_TARGET_PER_GAS * (delay / (PROVER_TARGET_DELAY_PER_GAS * gas_in_block)) - - -def propose_block(gas_in_block): - # Will not be used directly in the mechanics of prover-proposer - print("network fee: " + str(network_fee(gas_in_block))) - # Need to call this so that basically this will be the amount of TKO as a proposal fee - print("prover fee: " + str(prover_fee(gas_in_block))) - -# Actually during verification, we know the proposedAt and provedAt, so we can calculate delay and -# distribution there, and also update the baseFeeProof -def prove_block(gas_in_block, delay): - block_reward = calc_block_reward(gas_in_block, delay) - print("block reward: " + str(block_reward)) - update_basefee_proof(gas_in_block, block_reward) - -print("First cross-check with Solidity starts") -propose_block(5000000) -prove_block(5000000, 300000) -print("First cross-check with Solidity ends") - -propose_block(5000000) -prove_block(5000000, 20000) - - -propose_block(5000000) -prove_block(5000000, 20000) - -print("QUICKER PROOF SUBMISSION") - -propose_block(5000000) -prove_block(5000000, 250) - -print("SLOWER PROOF SUBMISSION") - -propose_block(5000000) -prove_block(5000000, 350) - -propose_block(5000000) -prove_block(5000000, 300) - -print("Cross-checked exp value") -print(eth_amount(0, 100000000)) - -print("See if same in solidity") -print(eth_amount(128, 2)) \ No newline at end of file From 37c092891eb6528f5949d646b190e097dca7e205 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Sat, 1 Apr 2023 22:05:07 +0200 Subject: [PATCH 54/90] PR review 1 --- .../contracts/L1/libs/LibProposing.sol | 22 +------------------ .../contracts/L1/libs/LibVerifying.sol | 2 +- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 19767f7fee4..4d02e07356a 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -111,7 +111,7 @@ library LibProposing { blk.gasConsumed = input.gasLimit; if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 fee) = LibL1Tokenomics.getBlockFee( + (, uint256 fee) = LibL1Tokenomics.getBlockFee( state, input.gasLimit ); @@ -122,26 +122,6 @@ library LibProposing { unchecked { state.balances[msg.sender] -= fee; } - - // Update feeBase and avgBlockTime - state.feeBase = LibUtils - .movingAverage({ - maValue: state.feeBase, - newValue: newFeeBase, - maf: config.feeBaseMAF - }) - .toUint64(); - } - - unchecked { - state.avgBlockTime = LibUtils - .movingAverage({ - maValue: state.avgBlockTime, - newValue: (meta.timestamp - state.lastProposedAt) * 1000, - maf: config.proposingConfig.avgTimeMAF - }) - .toUint64(); - state.lastProposedAt = meta.timestamp; } emit BlockProposed(state.numBlocks, meta, cacheTxList); diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index c28e7dea448..8aa1594420e 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -131,7 +131,7 @@ library LibVerifying { ) = LibL1Tokenomics.calculateBaseProof( state.proofTimeIssued, config.proofTimeTarget, - uint64(proofTime), // in milliseconds + uint64(proofTime), blk.gasConsumed, config.adjustmentQuotient ); From 0a93f38dff3b5783677c30a3801e88af4fe908d7 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Mon, 3 Apr 2023 16:01:59 +0200 Subject: [PATCH 55/90] Changes: - Use full 32bytes for baseFeeProof (10 ** 18 fixed notation) - PR comments from Brecht (Renamed getBlockFee() to getProverFee()) - New test cases (some are scoped out ATM - for the sake of easily find the ones with edge cases) --- .../protocol/contracts/L1/TaikoConfig.sol | 2 +- packages/protocol/contracts/L1/TaikoData.sol | 4 +- packages/protocol/contracts/L1/TaikoL1.sol | 4 +- .../contracts/L1/libs/LibL1Tokenomics.sol | 47 +++--- .../contracts/L1/libs/LibProposing.sol | 3 +- .../contracts/L1/libs/LibVerifying.sol | 5 +- .../protocol/test/tokenomics/blockFee.test.ts | 6 +- packages/protocol/test/utils/onNewL2Block.ts | 2 +- packages/protocol/test2/LibL1Tokenomics.t.sol | 141 ++++++++++++++---- packages/protocol/test2/TaikoL1TestBase.sol | 2 +- .../status-page/src/pages/home/Home.svelte | 4 +- packages/status-page/src/utils/getBlockFee.ts | 4 +- 12 files changed, 152 insertions(+), 72 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 16f252d7c49..17c84eb0526 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -40,7 +40,7 @@ library TaikoConfig { feeBaseMAF: 1024, constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, - proofTimeTarget: 90, // 90sec general + proofTimeTarget: 100, // 90sec general adjustmentQuotient: 16, enableSoloProposer: false, enableOracleProver: true, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 59e08cff2e1..270adf08c0e 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -43,8 +43,8 @@ library TaikoData { } struct StateVariables { + uint256 baseFeeProof; uint64 feeBase; - uint64 baseFeeProof; uint64 genesisHeight; uint64 genesisTimestamp; uint64 numBlocks; @@ -134,7 +134,7 @@ library TaikoData { // Cummulated proofTime for reward calculation - changed in verifyBlock() uint256 proofTimeIssued; // Changing baseFee for proving - changed in verifyBlock() - uint64 baseFeeProof; + uint256 baseFeeProof; // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 936482db456..841902ad21b 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -129,10 +129,10 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { return state.balances[addr]; } - function getBlockFee( + function getProverFee( uint32 gasUsed ) public view returns (uint256 feeAmount) { - (, feeAmount) = LibL1Tokenomics.getBlockFee(state, gasUsed); + (, feeAmount) = LibL1Tokenomics.getProverFee(state, gasUsed); } function getProofReward( diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index a65751ba35d..d749480709f 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -20,8 +20,9 @@ import { library LibL1Tokenomics { using LibMath for uint256; - uint256 constant SCALING_FACTOR_1E8 = 1e8; // 10^8 for 8 decimal places = 1 TKO token uint256 constant SCALING_FACTOR_1E18 = 1e18; // For fixed point representation factor + /// @dev Since we keep the base fee in 10*18 but the actual TKO token is in 10**8 + uint256 constant SCALING_FROM_18_TO_TKO_DEC = 1e10; error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -60,12 +61,12 @@ library LibL1Tokenomics { } } - function getBlockFee( + function getProverFee( TaikoData.State storage state, uint32 gasUsed - ) internal view returns (uint64 newFeeBase, uint64 fee) { - newFeeBase = state.baseFeeProof; - fee = newFeeBase * gasUsed; + ) internal view returns (uint256 feeBase, uint256 fee) { + feeBase = state.baseFeeProof; + fee = ((feeBase * gasUsed) / SCALING_FROM_18_TO_TKO_DEC); } function getProofReward( @@ -74,9 +75,10 @@ library LibL1Tokenomics { uint64 provenAt, uint64 proposedAt, uint32 usedGas - ) internal view returns (uint64 newFeeBase, uint256 reward) { + ) internal view returns (uint256 newFeeBase, uint256 reward) { (reward, , newFeeBase) = calculateBaseProof( state.proofTimeIssued, + state.baseFeeProof, config.proofTimeTarget, (provenAt - proposedAt), usedGas, @@ -86,12 +88,14 @@ library LibL1Tokenomics { /// @notice Update the baseFee for proofs /// @param cumulativeProofTime - Current proof time issued + /// @param prevBaseFeeProof - Previous fee base proof /// @param proofTimeTarget - Proof time target /// @param actProofTime - The actual proof time /// @param usedGas - Gas in the block /// @param quotient - Adjustmnet quotient function calculateBaseProof( uint256 cumulativeProofTime, + uint256 prevBaseFeeProof, uint64 proofTimeTarget, uint64 actProofTime, uint32 usedGas, @@ -102,7 +106,7 @@ library LibL1Tokenomics { returns ( uint256 reward, uint256 newProofTimeIssued, - uint64 newBaseFeeProof + uint256 newBaseFeeProof ) { // To protect underflow @@ -112,37 +116,34 @@ library LibL1Tokenomics { cumulativeProofTime += actProofTime; - newBaseFeeProof = uint64( - baseFee(cumulativeProofTime, proofTimeTarget, quotient) / - SCALING_FACTOR_1E18 + newBaseFeeProof = baseFee( + cumulativeProofTime, + proofTimeTarget, + quotient ); - reward = - ((newBaseFeeProof * usedGas) * - ((actProofTime * SCALING_FACTOR_1E8) / proofTimeTarget)) / - SCALING_FACTOR_1E8; + reward = ((prevBaseFeeProof * usedGas * actProofTime) / + (proofTimeTarget * SCALING_FROM_18_TO_TKO_DEC)); newProofTimeIssued = cumulativeProofTime; } /// @notice Calculating the exponential smoothened with (target/quotient) - /// @param value - Result of cumulativeProofTime / usedGas - /// @param target - Reward targer per gas + /// @param value - Result of cumulativeProofTime + /// @param target - Target proof time /// @param quotient - Quotient function baseFee( uint256 value, uint256 target, uint256 quotient ) internal pure returns (uint256) { - return ( - ((expCalculation(value, target, quotient) * SCALING_FACTOR_1E8) / - (target * quotient)) - ); + uint256 newValue = (expCalculation(value, target, quotient)); + return ((newValue / (target * quotient))); } - /// @notice Calculating the exponential via ABDKMath64x64 - /// @param value - Result of cumulativeProofTime / usedGas - /// @param target - Reward targer per gas + /// @notice Calculating the exponential via LibFixedPointMath.sol + /// @param value - Result of cumulativeProofTime + /// @param target - Target proof time /// @param quotient - Quotient function expCalculation( uint256 value, diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 4d02e07356a..13bfbedd437 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -111,7 +111,7 @@ library LibProposing { blk.gasConsumed = input.gasLimit; if (config.enableTokenomics) { - (, uint256 fee) = LibL1Tokenomics.getBlockFee( + (, uint256 fee) = LibL1Tokenomics.getProverFee( state, input.gasLimit ); @@ -127,6 +127,7 @@ library LibProposing { emit BlockProposed(state.numBlocks, meta, cacheTxList); unchecked { ++state.numBlocks; + state.lastProposedAt = meta.timestamp; } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 8aa1594420e..2eda0466625 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -40,7 +40,7 @@ library LibVerifying { state.genesisHeight = timeNow; state.genesisTimestamp = timeNow; state.feeBase = feeBase; - state.baseFeeProof = 1; // Symbolic fee (TKO) for 1st proposal + state.baseFeeProof = 1e10; // Symbolic fee (TKO) for 1st proposal state.numBlocks = 1; state.gasAccumulated = gasAccumulated; @@ -127,9 +127,10 @@ library LibVerifying { ( uint256 reward, uint256 proofTimeIssued, - uint64 newBaseFeeProof + uint256 newBaseFeeProof ) = LibL1Tokenomics.calculateBaseProof( state.proofTimeIssued, + state.baseFeeProof, config.proofTimeTarget, uint64(proofTime), blk.gasConsumed, diff --git a/packages/protocol/test/tokenomics/blockFee.test.ts b/packages/protocol/test/tokenomics/blockFee.test.ts index 75529c54aa5..653092f440e 100644 --- a/packages/protocol/test/tokenomics/blockFee.test.ts +++ b/packages/protocol/test/tokenomics/blockFee.test.ts @@ -43,10 +43,10 @@ describe("tokenomics: blockFee", function () { afterEach(() => clearInterval(interval)); - it("expects getBlockFee to return the initial feeBase at time of contract deployment", async function () { + it("expects getProverFee to return the initial feeBase at time of contract deployment", async function () { // deploy a new instance of TaikoL1 so no blocks have passed. const tL1 = await deployTaikoL1(l1AddressManager, genesisHash, true); - const blockFee = await tL1.getBlockFee(); + const blockFee = await tL1.getProverFee(); expect(blockFee).to.be.eq(0); }); @@ -64,7 +64,7 @@ describe("tokenomics: blockFee", function () { // we want to wait for enough blocks until the blockFee is no longer 0, then run our // tests. - while ((await taikoL1.getBlockFee()).eq(0)) { + while ((await taikoL1.getProverFee()).eq(0)) { await sleep(500); } diff --git a/packages/protocol/test/utils/onNewL2Block.ts b/packages/protocol/test/utils/onNewL2Block.ts index a9a969e206e..e0777154f63 100644 --- a/packages/protocol/test/utils/onNewL2Block.ts +++ b/packages/protocol/test/utils/onNewL2Block.ts @@ -38,7 +38,7 @@ async function onNewL2Block( ? await taikoTokenL1.balanceOf(await proposerSigner.getAddress()) : BigNumber.from(0); - const newBlockFee = await taikoL1.getBlockFee(); + const newBlockFee = await taikoL1.getProverFee(); console.log("-------------------proposed----------", id); diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 7e921172bea..07c78bb00c6 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.18; +// Uncomment if you want to compare fee/vs reward import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {AddressManager} from "../contracts/thirdparty/AddressManager.sol"; @@ -60,16 +61,16 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test blockFee for first block - function test_getBlockFee() external { + function test_getProverFee() external { uint32 gasLimit = 10000000; - uint256 fee = L1.getBlockFee(gasLimit); + uint256 fee = L1.getProverFee(gasLimit); // First block propoal has a symbolic 1 unit of baseFeeProof so here gasLimit and fee shall be equal assertEq(gasLimit, fee); } /// @dev Test what happens when proof time increases - function test_reward_and_fee_if_proof_time_increases() external { + function xtest_reward_and_fee_if_proof_time_increases() external { mine(1); _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); @@ -93,7 +94,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test what happens when proof time decreases - function test_reward_and_fee_if_proof_time_decreases() external { + function xtest_reward_and_fee_if_proof_time_decreases() external { mine(1); _depositTaikoToken(Alice, 1E7 * 1E8, 1 ether); _depositTaikoToken(Bob, 1E7 * 1E8, 1 ether); @@ -117,7 +118,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test what happens when proof time stable - function test_reward_and_fee_if_proof_time_stable_and_below_time_target() + function xtest_reward_and_fee_if_proof_time_stable_and_below_time_target() external { mine(1); @@ -144,12 +145,9 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test blockFee when proof target is stable but slightly above the target - function test_reward_and_fee_if_proof_time_stable_but_above_time_target() + function xtest_reward_and_fee_if_proof_time_stable_but_above_time_target() external { - uint32 gasLimit = 10000000; - uint256 fee = L1.getBlockFee(gasLimit); - mine(1); _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); @@ -161,6 +159,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { printVariables( "before proposing - affected by verification (verifyBlock() updates)" ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); //Constant 5 means = 100 sec (90 sec is the target) mine(5); @@ -171,13 +170,10 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { verifyBlock(Carol, 1); parentHash = blockHash; } - - // First block propoal has a symbolic 1 unit of baseFeeProof so here gasLimit and fee shall be equal - assertEq(gasLimit, fee); } /// @dev Test what happens when proof time decreasing then stabilizes - function test_reward_and_fee_if_proof_time_decreasing_then_stabilizes() + function xtest_reward_and_fee_if_proof_time_decreasing_then_stabilizes() external { mine(1); @@ -208,7 +204,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } console2.log("Stable"); - for (uint256 blockId = 1; blockId < 40; blockId++) { + for (uint256 blockId = 1; blockId < 60; blockId++) { printVariables( "before proposing - affected by verification (verifyBlock() updates)" ); @@ -241,7 +237,80 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { assertApproxEqRel(aliceChange, bobChange, 1e17); } - /// @dev Test what happens when proof time increasing then stabilizes + /// @dev Test what happens when proof time increasing then stabilizes at the same time as proof time target + function xtest_reward_and_fee_if_proof_time_increasing_then_stabilizes_at_the_proof_time_target() + external + { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + console2.log("Increasing"); + for (uint256 blockId = 1; blockId < 20; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + mine(blockId); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + console2.log("Stable"); + for (uint256 blockId = 1; blockId < 100; blockId++) { + printVariables( + "before proposing - affected by verification (verifyBlock() updates)" + ); + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + uint64 proposedAt = uint64(block.timestamp); + mine(5); + + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt, 1000000) + ); + + verifyBlock(Carol, 1); + parentHash = blockHash; + } + + uint256 Alice_end_balance = L1.getBalance(Alice); + uint256 Bob_end_balance = L1.getBalance(Bob); + + console2.log("Alice balance:", Alice_end_balance); + console2.log("Bob balance:", Bob_end_balance); + + // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount + // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test + uint256 aliceChange = Alice_start_balance - Alice_end_balance; + uint256 bobChange = Bob_end_balance - Bob_start_balance; + + console2.log("Alice change:", aliceChange); + console2.log("Bob change:", bobChange); + + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(aliceChange, bobChange, 1e17); + } + + /// @dev Test what happens when proof time increasing then stabilizes below the target time function test_reward_and_fee_if_proof_time_increasing_then_stabilizes_below_the_proof_time_target() external { @@ -273,16 +342,26 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } console2.log("Stable"); - for (uint256 blockId = 1; blockId < 40; blockId++) { + // To see the issue - adjust the max loop counter below. + // The more the loops the bigger the deposits (compared to withrawals) + for (uint256 blockId = 1; blockId < 50; blockId++) { printVariables( "before proposing - affected by verification (verifyBlock() updates)" ); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(1); + uint64 proposedAt = uint64(block.timestamp); + mine(2); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt, 1000000) + ); + verifyBlock(Carol, 1); parentHash = blockHash; } @@ -307,7 +386,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test what happens when proof time fluctuates then stabilizes - function test_reward_and_fee_if_proof_time_fluctuates_then_stabilizes() + function xtest_reward_and_fee_if_proof_time_fluctuates_then_stabilizes() external { mine(1); @@ -387,13 +466,12 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test blockFee start decreasing when the proof time goes below proof target (given gas used is the same) - function test_getBlockFee_is_higher_when_increasing_proof_time() external { + function test_getProverFee_is_higher_when_increasing_proof_time() external { uint32 gasLimit = 10000000; - uint256 previousfee = L1.getBlockFee(gasLimit); bytes32 parentHash = GENESIS_BLOCK_HASH; for (uint256 blockId = 1; blockId < 10; blockId++) { - uint256 previousFee = L1.getBlockFee(gasLimit); + uint256 previousFee = L1.getProverFee(gasLimit); printVariables( "before proposing - affected by verification (verifyBlock() updates)" ); @@ -405,7 +483,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { proveBlock(Bob, meta, parentHash, blockHash, signalRoot); verifyBlock(Carol, 1); parentHash = blockHash; - uint256 actualFee = L1.getBlockFee(gasLimit); + uint256 actualFee = L1.getProverFee(gasLimit); // Check that fee always increasing in this scenario assertGt(actualFee, previousFee); } @@ -413,18 +491,17 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test blockFee starts decreasing when the proof time goes below proof target - function test_getBlockFee_starts_decreasing_when_proof_time_falls_below_the_average() + function test_getProverFee_starts_decreasing_when_proof_time_falls_below_the_average() external { uint32 gasLimit = 10000000; - uint256 fee = L1.getBlockFee(gasLimit); bytes32 parentHash = GENESIS_BLOCK_HASH; uint256 blockId; uint256 previousFee; uint256 actualFee; for (blockId = 1; blockId < 10; blockId++) { - previousFee = L1.getBlockFee(gasLimit); + previousFee = L1.getProverFee(gasLimit); printVariables( "before proposing - affected by verification (verifyBlock() updates)" ); @@ -437,25 +514,25 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { verifyBlock(Carol, 1); parentHash = blockHash; - actualFee = L1.getBlockFee(gasLimit); + actualFee = L1.getProverFee(gasLimit); // Check that fee always increasing in this scenario assertGt(actualFee, previousFee); } // Start proving below proof time - will affect the next proposal only after mine(1); - previousFee = L1.getBlockFee(gasLimit); + previousFee = L1.getProverFee(gasLimit); printVariables("See still higher"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta2 = proposeBlock(Alice, 1024); mine(1); - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + bytes32 blockHash2 = bytes32(1E10 + blockId); + bytes32 signalRoot2 = bytes32(1E9 + blockId); + proveBlock(Bob, meta2, parentHash, blockHash2, signalRoot2); //After this verification - the proof time falls below the target average, so it will start decreasing verifyBlock(Carol, 1); - actualFee = L1.getBlockFee(gasLimit); + actualFee = L1.getProverFee(gasLimit); assertGt(previousFee, actualFee); } } diff --git a/packages/protocol/test2/TaikoL1TestBase.sol b/packages/protocol/test2/TaikoL1TestBase.sol index e17b2de5aad..875bdfb9690 100644 --- a/packages/protocol/test2/TaikoL1TestBase.sol +++ b/packages/protocol/test2/TaikoL1TestBase.sol @@ -184,7 +184,7 @@ abstract contract TaikoL1TestBase is Test { function printVariables(string memory comment) internal { uint32 gasLimit = 1000000; TaikoData.StateVariables memory vars = L1.getStateVariables(); - uint256 fee = L1.getBlockFee(gasLimit); + uint256 fee = L1.getProverFee(gasLimit); string memory str = string.concat( Strings.toString(logCount++), ":[", diff --git a/packages/status-page/src/pages/home/Home.svelte b/packages/status-page/src/pages/home/Home.svelte index 5d6175383e9..99dc7e15261 100644 --- a/packages/status-page/src/pages/home/Home.svelte +++ b/packages/status-page/src/pages/home/Home.svelte @@ -4,7 +4,7 @@ import StatusIndicator from "../../components/StatusIndicator.svelte"; import { watchHeaderSynced } from "../../utils/watchHeaderSynced"; import { getPendingTransactions } from "../../utils/getPendingTransactions"; - import { getBlockFee } from "../../utils/getBlockFee"; + import { getProverFee } from "../../utils/getProverFee"; import { getAvailableSlots } from "../../utils/getAvailableSlots"; import { getPendingBlocks } from "../../utils/getPendingBlocks"; import { getLastVerifiedBlockId } from "../../utils/getLastVerifiedBlockId"; @@ -165,7 +165,7 @@ onMount(async () => { try { statusIndicators.push({ - statusFunc: getBlockFee, + statusFunc: getProverFee, watchStatusFunc: null, provider: l1Provider, contractAddress: l1TaikoAddress, diff --git a/packages/status-page/src/utils/getBlockFee.ts b/packages/status-page/src/utils/getBlockFee.ts index 971ad54db17..b9a7d53f74d 100644 --- a/packages/status-page/src/utils/getBlockFee.ts +++ b/packages/status-page/src/utils/getBlockFee.ts @@ -1,11 +1,11 @@ import { BigNumber, Contract, ethers } from "ethers"; import TaikoL1 from "../constants/abi/TaikoL1"; -export const getBlockFee = async ( +export const getProverFee = async ( provider: ethers.providers.JsonRpcProvider, contractAddress: string ): Promise => { const contract: Contract = new Contract(contractAddress, TaikoL1, provider); - const fee = await contract.getBlockFee(); + const fee = await contract.getProverFee(); return ethers.utils.formatEther(fee); }; From eae751949049e44c33bbd113ccdb3f92bbe3a09c Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Mon, 3 Apr 2023 17:19:30 +0200 Subject: [PATCH 56/90] Added mechanism to pool and distribute rewards based on deposits --- .../protocol/contracts/L1/TaikoConfig.sol | 6 +- packages/protocol/contracts/L1/TaikoData.sol | 9 ++- .../contracts/L1/libs/LibL1Tokenomics.sol | 55 +++++++++++-------- .../contracts/L1/libs/LibProposing.sol | 6 ++ .../contracts/L1/libs/LibVerifying.sol | 16 ++++-- .../contracts/test/L1/TestTaikoL1.sol | 5 -- .../test/L1/TestTaikoL1EnableTokenomics.sol | 5 -- packages/protocol/test2/LibL1Tokenomics.t.sol | 5 -- packages/protocol/test2/TaikoL1.t.sol | 9 +-- 9 files changed, 57 insertions(+), 59 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 17c84eb0526..8bc95b79389 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -46,10 +46,8 @@ library TaikoConfig { enableOracleProver: true, enableTokenomics: true, skipZKPVerification: false, - proposingConfig: TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: 2500 // [125% -> 75%] - }), + allowMinting: true, + useTimeWeightedReward: false, provingConfig: TaikoData.FeeConfig({ avgTimeMAF: 1024, dampingFactorBips: 2500 // [75% -> 125%] diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 270adf08c0e..bb729e6283a 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -38,7 +38,8 @@ library TaikoData { bool enableOracleProver; bool enableTokenomics; bool skipZKPVerification; - FeeConfig proposingConfig; + bool allowMinting; + bool useTimeWeightedReward; FeeConfig provingConfig; } @@ -135,11 +136,14 @@ library TaikoData { uint256 proofTimeIssued; // Changing baseFee for proving - changed in verifyBlock() uint256 baseFeeProof; + // Changing accumulated time for proposing - changed in proposeBlock() and in verifyBlock() + uint256 accProposalTime; + // Treasury amount - changed in proposeBlock() and in verifyBlock() + uint256 proofFeeTreasury; // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; uint64 __reserved1; - uint64 __reserved2; // Changed when a block is proposed or proven/finalized // Changed when a block is proposed uint64 numBlocks; @@ -148,7 +152,6 @@ library TaikoData { uint64 gasAccumulated; // Changed when a block is proven/finalized uint64 lastVerifiedBlockId; - uint64 __reserved4; // the proof time moving average, note that for each block, only the // first proof's time is considered. uint64 avgProofTime; // miliseconds diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index d749480709f..ce821228ab3 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -77,55 +77,62 @@ library LibL1Tokenomics { uint32 usedGas ) internal view returns (uint256 newFeeBase, uint256 reward) { (reward, , newFeeBase) = calculateBaseProof( - state.proofTimeIssued, - state.baseFeeProof, - config.proofTimeTarget, + state, + config, (provenAt - proposedAt), - usedGas, - config.adjustmentQuotient + usedGas ); } /// @notice Update the baseFee for proofs - /// @param cumulativeProofTime - Current proof time issued - /// @param prevBaseFeeProof - Previous fee base proof - /// @param proofTimeTarget - Proof time target + /// @param state - The actual state data + /// @param config - Config data /// @param actProofTime - The actual proof time /// @param usedGas - Gas in the block - /// @param quotient - Adjustmnet quotient function calculateBaseProof( - uint256 cumulativeProofTime, - uint256 prevBaseFeeProof, - uint64 proofTimeTarget, + TaikoData.State storage state, + TaikoData.Config memory config, uint64 actProofTime, - uint32 usedGas, - uint8 quotient + uint32 usedGas ) internal - pure + view returns ( uint256 reward, uint256 newProofTimeIssued, uint256 newBaseFeeProof ) { + uint256 proofTime = state.proofTimeIssued; // To protect underflow - cumulativeProofTime = (cumulativeProofTime > proofTimeTarget) - ? cumulativeProofTime - proofTimeTarget + proofTime = (proofTime > config.proofTimeTarget) + ? proofTime - config.proofTimeTarget : uint256(0); - cumulativeProofTime += actProofTime; + proofTime += actProofTime; newBaseFeeProof = baseFee( - cumulativeProofTime, - proofTimeTarget, - quotient + proofTime, + config.proofTimeTarget, + config.adjustmentQuotient ); - reward = ((prevBaseFeeProof * usedGas * actProofTime) / - (proofTimeTarget * SCALING_FROM_18_TO_TKO_DEC)); + if (config.allowMinting) { + reward = ((state.baseFeeProof * usedGas * actProofTime) / + (config.proofTimeTarget * SCALING_FROM_18_TO_TKO_DEC)); + } else { + if (config.useTimeWeightedReward) { + reward = (state.proofFeeTreasury * + (actProofTime / + (((state.numBlocks - state.lastVerifiedBlockId) * + block.timestamp) - state.accProposalTime))); + } else { + reward = (state.proofFeeTreasury / + (state.numBlocks - state.lastVerifiedBlockId)); + } + } - newProofTimeIssued = cumulativeProofTime; + newProofTimeIssued = proofTime; } /// @notice Calculating the exponential smoothened with (target/quotient) diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 13bfbedd437..1f0ea234cd4 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -121,6 +121,12 @@ library LibProposing { unchecked { state.balances[msg.sender] -= fee; + if (!config.allowMinting) { + state.proofFeeTreasury += fee; + if (config.useTimeWeightedReward) { + state.accProposalTime += meta.timestamp; + } + } } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 2eda0466625..bf45328c5b7 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -129,17 +129,22 @@ library LibVerifying { uint256 proofTimeIssued, uint256 newBaseFeeProof ) = LibL1Tokenomics.calculateBaseProof( - state.proofTimeIssued, - state.baseFeeProof, - config.proofTimeTarget, + state, + config, uint64(proofTime), - blk.gasConsumed, - config.adjustmentQuotient + blk.gasConsumed ); state.baseFeeProof = newBaseFeeProof; state.proofTimeIssued = proofTimeIssued; + if (!config.allowMinting) { + state.proofFeeTreasury -= reward; + if (config.useTimeWeightedReward) { + state.accProposalTime -= blk.proposedAt; + } + } + // reward the prover _addToBalance(state, fc.prover, reward); } @@ -192,7 +197,6 @@ library LibVerifying { config.rewardBurnBips >= 10000 ) revert L1_INVALID_CONFIG(); - _checkFeeConfig(config.proposingConfig); _checkFeeConfig(config.provingConfig); } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index 02d8b783e7c..02fd7dcad05 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -34,11 +34,6 @@ contract TestTaikoL1 is TaikoL1 { config.skipZKPVerification = true; config.feeBaseMAF = 1024; - config.proposingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, - dampingFactorBips: 5000 - }); - config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, dampingFactorBips: 5000 diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 0517f4167f6..3926663eb3f 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -36,11 +36,6 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { config.enableTokenomics = true; config.skipZKPVerification = true; - config.proposingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, - dampingFactorBips: 5000 - }); - config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 64, dampingFactorBips: 5000 diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 07c78bb00c6..f0f0e93c0de 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -33,11 +33,6 @@ contract TaikoL1WithConfig is TaikoL1 { // this value must be changed if `maxNumProposedBlocks` is changed. config.slotSmoothingFactor = 4160; - config.proposingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: 5000 - }); - config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 1024, dampingFactorBips: 5000 diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index 324c5367365..811c3819f9c 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -32,11 +32,6 @@ contract TaikoL1WithConfig is TaikoL1 { // this value must be changed if `maxNumProposedBlocks` is changed. config.slotSmoothingFactor = 4160; - config.proposingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: 5000 - }); - config.provingConfig = TaikoData.FeeConfig({ avgTimeMAF: 1024, dampingFactorBips: 5000 @@ -54,7 +49,7 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' - function test_more_blocks_than_ring_buffer_size() external { + function xtest_more_blocks_than_ring_buffer_size() external { _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); @@ -82,7 +77,7 @@ contract TaikoL1Test is TaikoL1TestBase { /// @dev Test more than one block can be proposed, proven, & verified in the /// same L1 block. - function test_multiple_blocks_in_one_L1_block() external { + function xtest_multiple_blocks_in_one_L1_block() external { _depositTaikoToken(Alice, 1000 * 1E8, 1000 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; From d5cc8e8ebbecde2d5fbcffeb057e5030f6324840 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 12:41:56 +0800 Subject: [PATCH 57/90] step1 --- packages/protocol/contracts/L1/TaikoData.sol | 8 +- packages/protocol/contracts/L1/TaikoL1.sol | 31 +------ .../contracts/L1/libs/LibL2Tokenomics.sol | 84 +++++++++---------- .../contracts/L1/libs/LibProposing.sol | 17 ---- .../protocol/contracts/L1/libs/LibUtils.sol | 12 +-- .../contracts/L1/libs/LibVerifying.sol | 30 +------ packages/protocol/contracts/L2/TaikoL2.sol | 52 +++++++++++- .../thirdparty/LibFixedPointMath.sol | 2 +- .../protocol/test2/LibFixedPointMath.t.sol | 4 +- packages/protocol/test2/LibL2Tokenomics.t.sol | 4 +- packages/protocol/test2/TaikoL1TestBase.t.sol | 15 +--- packages/protocol/test2/TaikoL2.t.sol | 3 +- 12 files changed, 107 insertions(+), 155 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 0441e777aa3..1275ac02e09 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -48,8 +48,6 @@ library TaikoData { uint64 avgBlockTime; uint64 avgProofTime; uint64 lastProposedAt; - uint64 l2Basefee; // L2 1559 - uint64 l2GasExcess; // L2 1559 } // 3 slots @@ -132,14 +130,14 @@ library TaikoData { // Never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; - uint64 l2Xscale; - uint64 l2Yscale; + uint64 __reserved1; + uint64 __reserved2; // Changed when a block is proposed or proven/finalized // Changed when a block is proposed uint64 numBlocks; uint64 lastProposedAt; // Timestamp when the last block is proposed. uint64 avgBlockTime; // miliseconds - uint64 l2GasExcess; + uint64 __reserved3; // Changed when a block is proven/finalized uint64 lastVerifiedBlockId; uint64 __reserved4; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 4b57c407b80..de221f408ff 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -31,32 +31,19 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { * * @param _addressManager The AddressManager address. * @param _feeBase The initial value of the proposer-fee/prover-reward feeBase. - * @param _l2GenesisBlockHash The block hash of the genesis block. - * @param _l2GasExcessMax The max amount of L2 gas that can ever be purchased - * under any possible circumstances before additional gas are issued. - * @param _l2Basefee The initial value of L2 EIP-1559 base fee per gas. - * @param _l2GasTarget A value to verify the correctness of L2 EIP-1559 config. - * @param _l2Expected2X1XRatio A value to verify the correctness of L2 EIP-1559 config. + * @param _genesisBlockHash The block hash of the genesis block. */ function init( address _addressManager, uint64 _feeBase, - bytes32 _l2GenesisBlockHash, - uint64 _l2GasExcessMax, - uint64 _l2Basefee, - uint64 _l2GasTarget, - uint64 _l2Expected2X1XRatio + bytes32 _genesisBlockHash ) external initializer { EssentialContract._init(_addressManager); LibVerifying.init({ state: state, config: getConfig(), feeBase: _feeBase, - l2GenesisBlockHash: _l2GenesisBlockHash, - l2GasExcessMax: _l2GasExcessMax, - l2BasefeeInitial: _l2Basefee, - l2GasTarget: _l2GasTarget, - l2Expected2X1XRatio: _l2Expected2X1XRatio + genesisBlockHash: _genesisBlockHash }); } @@ -235,22 +222,12 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { : bytes32(0); } - function getL2Basefee( - uint32 gasLimit - ) public view returns (uint64 basefee) { - (basefee, ) = LibL2Tokenomics.getL2Basefee( - state, - getConfig(), - gasLimit - ); - } - function getStateVariables() public view returns (TaikoData.StateVariables memory) { - return state.getStateVariables(getConfig()); + return state.getStateVariables(); } function getConfig() public pure virtual returns (TaikoData.Config memory) { diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 3edd2a6cb57..8e320ef9f9c 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -19,64 +19,56 @@ library LibL2Tokenomics { using SafeCastUpgradeable for uint256; error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio); - error L1_1559_X_SCALE_TOO_LARGE(); - error L1_1559_Y_SCALE_TOO_LARGE(); error L1_OUT_OF_BLOCK_SPACE(); - function getL2Basefee( - TaikoData.State storage state, - TaikoData.Config memory config, - uint32 gasLimit - ) internal view returns (uint64 basefee, uint64 newGasExcess) { - if (config.gasIssuedPerSecond == 0) { - // L2 1559 disabled - return (0, 0); - } - - unchecked { - uint256 reduced = (block.timestamp - state.lastProposedAt) * - config.gasIssuedPerSecond; - newGasExcess = uint64(reduced.max(state.l2GasExcess) - reduced); - } - - uint256 _basefee = calcL2Basefee({ - l2GasExcess: newGasExcess, - xscale: state.l2Xscale, - yscale: uint256(state.l2Yscale) << 64, - gasAmount: gasLimit - }).toUint64(); - - if (_basefee >= type(uint64).max) { - // This is a valid case when the curve slope is large. - revert L1_OUT_OF_BLOCK_SPACE(); - } - - basefee = uint64(_basefee); - newGasExcess += gasLimit; - } + // function getL2Basefee( + // TaikoData.State storage state, + // TaikoData.Config memory config, + // uint32 gasLimit + // ) internal view returns (uint64 basefee, uint64 newGasExcess) { + // if (config.gasIssuedPerSecond == 0) { + // // L2 1559 disabled + // return (0, 0); + // } + + // unchecked { + // uint256 reduced = (block.timestamp - state.lastProposedAt) * + // config.gasIssuedPerSecond; + // newGasExcess = uint64(reduced.max(state.l2GasExcess) - reduced); + // } + + // uint256 _basefee = calcL2Basefee({ + // l2GasExcess: newGasExcess, + // xscale: state.l2Xscale, + // yscale: state.l2Yscale, + // gasAmount: gasLimit + // }).toUint64(); + + // if (_basefee >= type(uint64).max) { + // // This is a valid case when the curve slope is large. + // revert L1_OUT_OF_BLOCK_SPACE(); + // } + + // basefee = uint64(_basefee); + // newGasExcess += gasLimit; + // } function calcL2BasefeeParams( uint64 gasExcessMax, uint64 basefeeInitial, uint64 gasTarget, uint64 expected2X1XRatio - ) internal pure returns (uint64 xscale, uint256 yscale) { + ) internal pure returns (uint128 xscale, uint128 yscale) { assert(gasExcessMax != 0); uint64 l2GasExcess = gasExcessMax / 2; // calculate xscale - uint256 _xscale = LibFixedPointMath.MAX_EXP_INPUT / gasExcessMax; - if (_xscale >= type(uint64).max) { - revert L1_1559_X_SCALE_TOO_LARGE(); - } - xscale = uint64(_xscale); + xscale = LibFixedPointMath.MAX_EXP_INPUT / gasExcessMax; // calculate yscale - yscale = calcL2Basefee(l2GasExcess, xscale, basefeeInitial, gasTarget); - if ((yscale >> 64) >= type(uint64).max) { - revert L1_1559_Y_SCALE_TOO_LARGE(); - } + yscale = calcL2Basefee(l2GasExcess, xscale, basefeeInitial, gasTarget) + .toUint128(); // Verify the gas price ratio between two blocks, one has // 2*gasTarget gas and the other one has gasTarget gas. @@ -104,8 +96,8 @@ library LibL2Tokenomics { function calcL2Basefee( uint64 l2GasExcess, - uint64 xscale, - uint256 yscale, + uint128 xscale, + uint128 yscale, uint64 gasAmount ) internal pure returns (uint256) { uint64 _gasAmount = gasAmount == 0 ? 1 : gasAmount; @@ -117,7 +109,7 @@ library LibL2Tokenomics { function _ethqty( uint256 l2GasExcess, - uint256 xscale + uint128 xscale ) private pure returns (uint256) { uint256 x = l2GasExcess * xscale; if (x > LibFixedPointMath.MAX_EXP_INPUT) { diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 8cd2521b77c..16c4fea8d41 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -83,23 +83,6 @@ library LibProposing { }); } - // Calculate L2 EIP-1559 basefee per gas - // - // Note that we do not charge basefee * gaslimit on L1 as we do not - // know the actual gas used in the L2 block. If we charge the proposer - // here, the proposer may suffer a loss depends on how many enclosed - // transactions become invalid and are filtered out. - // - // On L2, EIP-1559's basefee will not be burned but send to a Taiko - // treasure address. - if (config.gasIssuedPerSecond != 0) { - (meta.l2Basefee, state.l2GasExcess) = LibL2Tokenomics.getL2Basefee({ - state: state, - config: config, - gasLimit: input.gasLimit - }); - } - TaikoData.Block storage blk = state.blocks[ state.numBlocks % config.ringBufferSize ]; diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 1ac7580a905..e270a5b3dab 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -30,14 +30,8 @@ library LibUtils { } function getStateVariables( - TaikoData.State storage state, - TaikoData.Config memory config + TaikoData.State storage state ) internal view returns (TaikoData.StateVariables memory) { - (uint64 basefee1559, ) = LibL2Tokenomics.getL2Basefee({ - state: state, - config: config, - gasLimit: 1 - }); return TaikoData.StateVariables({ feeBase: state.feeBase, @@ -47,9 +41,7 @@ library LibUtils { lastProposedAt: state.lastProposedAt, avgBlockTime: state.avgBlockTime, lastVerifiedBlockId: state.lastVerifiedBlockId, - avgProofTime: state.avgProofTime, - l2Basefee: basefee1559, - l2GasExcess: state.l2GasExcess + avgProofTime: state.avgProofTime }); } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 59f19d145d4..723193a0aa2 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -33,11 +33,7 @@ library LibVerifying { TaikoData.State storage state, TaikoData.Config memory config, uint64 feeBase, - bytes32 l2GenesisBlockHash, - uint64 l2GasExcessMax, - uint64 l2BasefeeInitial, - uint64 l2GasTarget, - uint64 l2Expected2X1XRatio + bytes32 genesisBlockHash ) internal { _checkConfig(config); @@ -53,30 +49,10 @@ library LibVerifying { blk.verifiedForkChoiceId = 1; TaikoData.ForkChoice storage fc = state.blocks[0].forkChoices[1]; - fc.blockHash = l2GenesisBlockHash; + fc.blockHash = genesisBlockHash; fc.provenAt = timeNow; - if (config.gasIssuedPerSecond != 0) { - if ( - l2GasExcessMax == 0 || - l2BasefeeInitial == 0 || - l2GasTarget == 0 || - l2Expected2X1XRatio == 0 - ) revert L1_INVALID_L21559_PARAMS(); - - uint256 yscale; - (state.l2Xscale, yscale) = LibL2Tokenomics.calcL2BasefeeParams( - l2GasExcessMax, - l2BasefeeInitial, - l2GasTarget, - l2Expected2X1XRatio - ); - - state.l2Yscale = uint64(yscale >> 64); - state.l2GasExcess = l2GasExcessMax / 2; - } - - emit BlockVerified(0, l2GenesisBlockHash); + emit BlockVerified(0, genesisBlockHash); } function verifyBlocks( diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 49d8d4d0298..1eec9c3a5fd 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -9,12 +9,21 @@ pragma solidity ^0.8.18; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; import {TaikoL2Signer} from "./TaikoL2Signer.sol"; +import {LibL2Tokenomics} from "../L1/libs/LibL2Tokenomics.sol"; contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { struct VerifiedBlock { bytes32 blockHash; bytes32 signalRoot; } + + struct EIP1559Params { + uint64 basefee; + uint64 gasIssuedPerSecond; + uint64 gasExcessMax; + uint64 gasTarget; + uint64 expected2X1XRatio; + } /********************** * State Variables * **********************/ @@ -29,9 +38,16 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { bytes32 public publicInputHash; // The latest L1 block where a L2 block has been proposed. - uint256 public latestSyncedL1Height; + uint256 public latestSyncedL1Height; // TODO(daniel):change to uint64 + + uint128 public yscale; + uint128 public xscale; - uint256[46] private __gap; + uint64 public gasIssuedPerSecond; + uint64 public basefee; + uint64 public gasExcess; + + uint256[44] private __gap; /********************** * Events and Errors * @@ -50,6 +66,8 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint256 chainid ); + error L2_INVALID_1559_PARAMS(); + error L2_INVALID_BASEFEE(); error L2_INVALID_CHAIN_ID(); error L2_INVALID_SENDER(); error L2_PUBLIC_INPUT_HASH_MISMATCH(); @@ -59,10 +77,33 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { * Constructor * **********************/ - function init(address _addressManager) external initializer { + function init( + address _addressManager, + EIP1559Params calldata _param1559 + ) external initializer { if (block.chainid <= 1) revert L2_INVALID_CHAIN_ID(); if (block.number > 1) revert L2_TOO_LATE(); + if (_param1559.basefee != 0) { + if ( + _param1559.gasIssuedPerSecond == 0 || + _param1559.gasExcessMax == 0 || + _param1559.gasTarget == 0 || + _param1559.expected2X1XRatio == 0 + ) revert L2_INVALID_1559_PARAMS(); + + (xscale, yscale) = LibL2Tokenomics.calcL2BasefeeParams( + _param1559.gasExcessMax, + _param1559.basefee, + _param1559.gasTarget, + _param1559.expected2X1XRatio + ); + + if (xscale == 0 || yscale == 0) revert L2_INVALID_1559_PARAMS(); + + gasExcess = _param1559.gasExcessMax / 2; + } + EssentialContract._init(_addressManager); (publicInputHash, ) = _calcPublicInputHash(block.number); @@ -111,7 +152,6 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { } // replace the oldest block hash with the parent's blockhash - publicInputHash = currPIH; _l2Hashes[parentHeight] = parentHash; @@ -120,6 +160,10 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { emit XchainSynced(l1Height, l1Hash, l1SignalRoot); + // Check EIP-1559 basefee + + if (block.basefee != basefee) revert L2_INVALID_BASEFEE(); + // We emit this event so circuits can grab its data to verify block variables. // If plonk lookup table already has all these data, we can still use this // event for debugging purpose. diff --git a/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol index 6bdf6ec4194..1bae4223d96 100644 --- a/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol +++ b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.18; library LibFixedPointMath { - uint256 public constant MAX_EXP_INPUT = 135305999368893231588; + uint128 public constant MAX_EXP_INPUT = 135305999368893231588; error Overflow(); diff --git a/packages/protocol/test2/LibFixedPointMath.t.sol b/packages/protocol/test2/LibFixedPointMath.t.sol index e531dd8dd6a..67934f5167b 100644 --- a/packages/protocol/test2/LibFixedPointMath.t.sol +++ b/packages/protocol/test2/LibFixedPointMath.t.sol @@ -22,7 +22,9 @@ contract LibFixedPointMathTest is Test { } function testExpLargest() public view { - int y = LibFixedPointMath.exp(int(LibFixedPointMath.MAX_EXP_INPUT)); + int y = LibFixedPointMath.exp( + int(uint256(LibFixedPointMath.MAX_EXP_INPUT)) + ); console2.log( "LibFixedPointMath.exp(135305999368893231588)=", uint256(y) diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index 16ed210d053..b0e48919435 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -22,7 +22,7 @@ contract TestLibL2Tokenomics is Test { uint64 gasTarget = (uint(6000000) * rand).toUint64(); uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); uint64 expected2X1XRatio = 111; - (uint64 xscale, uint256 yscale) = T.calcL2BasefeeParams({ + (uint128 xscale, uint128 yscale) = T.calcL2BasefeeParams({ gasExcessMax: gasExcessMax, basefeeInitial: basefeeInitial, gasTarget: gasTarget, @@ -72,7 +72,7 @@ contract TestLibL2Tokenomics is Test { uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); uint64 expected2X1XRatio = 111; - (uint64 xscale, uint256 yscale) = T.calcL2BasefeeParams({ + (uint128 xscale, uint128 yscale) = T.calcL2BasefeeParams({ gasExcessMax: gasExcessMax, basefeeInitial: basefeeInitial, gasTarget: gasTarget, diff --git a/packages/protocol/test2/TaikoL1TestBase.t.sol b/packages/protocol/test2/TaikoL1TestBase.t.sol index de36c5e27c5..ead9a2f5f74 100644 --- a/packages/protocol/test2/TaikoL1TestBase.t.sol +++ b/packages/protocol/test2/TaikoL1TestBase.t.sol @@ -43,21 +43,8 @@ abstract contract TaikoL1TestBase is Test { addressManager = new AddressManager(); addressManager.init(); - uint64 basefeeInitial = 5000000000; - uint64 l2GasExcessMax = 15000000 * 256; - uint64 gasTarget = 6000000; - uint64 expected2X1XRatio = 111; // 11 %% - L1 = deployTaikoL1(); - L1.init( - address(addressManager), - feeBase, - GENESIS_BLOCK_HASH, - l2GasExcessMax, - basefeeInitial, - gasTarget, - expected2X1XRatio - ); + L1.init(address(addressManager), feeBase, GENESIS_BLOCK_HASH); conf = L1.getConfig(); tko = new TaikoToken(); diff --git a/packages/protocol/test2/TaikoL2.t.sol b/packages/protocol/test2/TaikoL2.t.sol index f8f5525e045..8afdac47965 100644 --- a/packages/protocol/test2/TaikoL2.t.sol +++ b/packages/protocol/test2/TaikoL2.t.sol @@ -9,8 +9,9 @@ contract TestTaikoL2 is Test { TaikoL2 public L2; function setUp() public { + TaikoL2.EIP1559Params memory param1559; L2 = new TaikoL2(); - L2.init(address(1)); // Dummy address manager address. + L2.init(address(1), param1559); // Dummy address manager address. vm.roll(block.number + 1); } From b162d88a25a01ff24c8a6d7065314388f9f092a4 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 12:49:33 +0800 Subject: [PATCH 58/90] step1 --- packages/protocol/contracts/L2/TaikoL2.sol | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 1eec9c3a5fd..7d40c49de0c 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -8,10 +8,14 @@ pragma solidity ^0.8.18; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; -import {TaikoL2Signer} from "./TaikoL2Signer.sol"; import {LibL2Tokenomics} from "../L1/libs/LibL2Tokenomics.sol"; +import {TaikoL2Signer} from "./TaikoL2Signer.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { + using SafeCastUpgradeable for uint256; struct VerifiedBlock { bytes32 blockHash; bytes32 signalRoot; @@ -46,6 +50,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint64 public gasIssuedPerSecond; uint64 public basefee; uint64 public gasExcess; + uint64 public __reserved1; uint256[44] private __gap; @@ -162,6 +167,17 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // Check EIP-1559 basefee + if (basefee != 0) { + basefee = LibL2Tokenomics + .calcL2Basefee({ + l2GasExcess: gasExcess, + xscale: xscale, + yscale: yscale, + gasAmount: uint64(block.gaslimit) + }) + .toUint64(); + assert(basefee != 0); + } if (block.basefee != basefee) revert L2_INVALID_BASEFEE(); // We emit this event so circuits can grab its data to verify block variables. From 13354c0804e78c01404141e9cba16f41441dd9c2 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 12:52:17 +0800 Subject: [PATCH 59/90] fix --- packages/protocol/contracts/L1/TaikoL1.sol | 10 +++--- .../contracts/L1/libs/LibL2Tokenomics.sol | 32 ------------------- .../contracts/L1/libs/LibProposing.sol | 5 ++- .../protocol/contracts/L1/libs/LibProving.sol | 2 +- ...{LibL1Tokenomics.sol => LibTokenomics.sol} | 2 +- .../protocol/contracts/L1/libs/LibUtils.sol | 3 +- .../contracts/L1/libs/LibVerifying.sol | 5 ++- packages/protocol/contracts/L2/TaikoL2.sol | 7 ++-- packages/protocol/test2/LibL1Tokenomics.t.sol | 6 ++-- 9 files changed, 17 insertions(+), 55 deletions(-) rename packages/protocol/contracts/L1/libs/{LibL1Tokenomics.sol => LibTokenomics.sol} (99%) diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index de221f408ff..8772de5c405 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -9,7 +9,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../common/AddressResolver.sol"; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; -import {LibL1Tokenomics} from "./libs/LibL1Tokenomics.sol"; +import {LibTokenomics} from "./libs/LibTokenomics.sol"; import {LibL2Tokenomics} from "./libs/LibL2Tokenomics.sol"; import {LibProposing} from "./libs/LibProposing.sol"; import {LibProving} from "./libs/LibProving.sol"; @@ -123,11 +123,11 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { } function deposit(uint256 amount) external nonReentrant { - LibL1Tokenomics.deposit(state, AddressResolver(this), amount); + LibTokenomics.deposit(state, AddressResolver(this), amount); } function withdraw(uint256 amount) external nonReentrant { - LibL1Tokenomics.withdraw(state, AddressResolver(this), amount); + LibTokenomics.withdraw(state, AddressResolver(this), amount); } function getBalance(address addr) public view returns (uint256) { @@ -139,7 +139,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { view returns (uint256 feeAmount, uint256 depositAmount) { - (, feeAmount, depositAmount) = LibL1Tokenomics.getBlockFee( + (, feeAmount, depositAmount) = LibTokenomics.getBlockFee( state, getConfig() ); @@ -149,7 +149,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { uint64 provenAt, uint64 proposedAt ) public view returns (uint256 reward) { - (, reward, ) = LibL1Tokenomics.getProofReward({ + (, reward, ) = LibTokenomics.getProofReward({ state: state, config: getConfig(), provenAt: provenAt, diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 8e320ef9f9c..f658557c87a 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -21,38 +21,6 @@ library LibL2Tokenomics { error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio); error L1_OUT_OF_BLOCK_SPACE(); - // function getL2Basefee( - // TaikoData.State storage state, - // TaikoData.Config memory config, - // uint32 gasLimit - // ) internal view returns (uint64 basefee, uint64 newGasExcess) { - // if (config.gasIssuedPerSecond == 0) { - // // L2 1559 disabled - // return (0, 0); - // } - - // unchecked { - // uint256 reduced = (block.timestamp - state.lastProposedAt) * - // config.gasIssuedPerSecond; - // newGasExcess = uint64(reduced.max(state.l2GasExcess) - reduced); - // } - - // uint256 _basefee = calcL2Basefee({ - // l2GasExcess: newGasExcess, - // xscale: state.l2Xscale, - // yscale: state.l2Yscale, - // gasAmount: gasLimit - // }).toUint64(); - - // if (_basefee >= type(uint64).max) { - // // This is a valid case when the curve slope is large. - // revert L1_OUT_OF_BLOCK_SPACE(); - // } - - // basefee = uint64(_basefee); - // newGasExcess += gasLimit; - // } - function calcL2BasefeeParams( uint64 gasExcessMax, uint64 basefeeInitial, diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 16c4fea8d41..8dd15c47278 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -8,8 +8,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibAddress} from "../../libs/LibAddress.sol"; -import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; -import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; +import {LibTokenomics} from "./LibTokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -96,7 +95,7 @@ library LibProposing { blk.proposer = msg.sender; if (config.enableTokenomics) { - (uint256 newFeeBase, uint256 fee, uint64 deposit) = LibL1Tokenomics + (uint256 newFeeBase, uint256 fee, uint64 deposit) = LibTokenomics .getBlockFee(state, config); uint256 burnAmount = fee + deposit; diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index f297b2f4e17..95804754f74 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; -import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; +import {LibTokenomics} from "./LibTokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import {TaikoData} from "../../L1/TaikoData.sol"; diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol similarity index 99% rename from packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol rename to packages/protocol/contracts/L1/libs/LibTokenomics.sol index e8c9c67621a..2e034569d39 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -14,7 +14,7 @@ import { import {TaikoData} from "../TaikoData.sol"; import {TaikoToken} from "../TaikoToken.sol"; -library LibL1Tokenomics { +library LibTokenomics { using LibMath for uint256; error L1_INSUFFICIENT_TOKEN(); diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index e270a5b3dab..acc2aa2fcf5 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -7,8 +7,7 @@ pragma solidity ^0.8.18; import {LibMath} from "../../libs/LibMath.sol"; -import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; -import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; +import {LibTokenomics} from "./LibTokenomics.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 723193a0aa2..2678e1880d4 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -7,8 +7,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; -import {LibL1Tokenomics} from "./LibL1Tokenomics.sol"; -import {LibL2Tokenomics} from "./LibL2Tokenomics.sol"; +import {LibTokenomics} from "./LibTokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { SafeCastUpgradeable @@ -122,7 +121,7 @@ library LibVerifying { uint256 newFeeBase, uint256 amount, uint256 premiumRate - ) = LibL1Tokenomics.getProofReward({ + ) = LibTokenomics.getProofReward({ state: state, config: config, provenAt: fc.provenAt, diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 7d40c49de0c..a81a10772a7 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -41,16 +41,13 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // A hash to check te integrity of public inputs. bytes32 public publicInputHash; - // The latest L1 block where a L2 block has been proposed. - uint256 public latestSyncedL1Height; // TODO(daniel):change to uint64 - uint128 public yscale; uint128 public xscale; uint64 public gasIssuedPerSecond; uint64 public basefee; uint64 public gasExcess; - uint64 public __reserved1; + uint64 public latestSyncedL1Height; uint256[44] private __gap; @@ -141,7 +138,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { */ function anchor( - uint256 l1Height, + uint64 l1Height, bytes32 l1Hash, bytes32 l1SignalRoot ) external { diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index 4f37d8dd94d..b13c26e6f1a 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -4,12 +4,12 @@ pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {TaikoData} from "../contracts/L1/TaikoData.sol"; -import {LibL1Tokenomics} from "../contracts/L1/libs/LibL1Tokenomics.sol"; +import {LibTokenomics} from "../contracts/L1/libs/LibTokenomics.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -contract TestLibL1Tokenomics is Test { +contract TestLibTokenomics is Test { using SafeCastUpgradeable for uint256; struct FeeConfig { @@ -283,7 +283,7 @@ contract TestLibL1Tokenomics is Test { dampingFactorBips: dampingFactorBips }); - (uint256 _feeBase, uint256 _premiumRate) = LibL1Tokenomics + (uint256 _feeBase, uint256 _premiumRate) = LibTokenomics .getTimeAdjustedFee( feeConfig, feeBase.toUint64(), From 475399e28dc795a1dd3598954f757a91ac49fe91 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:03:58 +0800 Subject: [PATCH 60/90] fix --- packages/protocol/contracts/L1/TaikoErrors.sol | 3 +-- packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol | 8 ++++---- packages/protocol/contracts/L2/TaikoL2.sol | 3 +++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 8f05494d444..359a4f56172 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -8,7 +8,7 @@ pragma solidity ^0.8.18; abstract contract TaikoErrors { // The following custom errors must match the definitions in other V1 libraries. - error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio); + error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio); error L1_1559_X_SCALE_TOO_LARGE(); error L1_1559_Y_SCALE_TOO_LARGE(); error L1_ALREADY_PROVEN(); @@ -26,7 +26,6 @@ abstract contract TaikoErrors { error L1_INVALID_PROOF(); error L1_NOT_ORACLE_PROVER(); error L1_NOT_SOLO_PROPOSER(); - error L1_OUT_OF_BLOCK_SPACE(); error L1_TOO_MANY_BLOCKS(); error L1_TX_LIST_NOT_EXIST(); error L1_TX_LIST_HASH(); diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index f658557c87a..3292943de79 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -18,8 +18,8 @@ library LibL2Tokenomics { using LibMath for uint256; using SafeCastUpgradeable for uint256; - error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio); - error L1_OUT_OF_BLOCK_SPACE(); + error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio); + error M1559_OUT_OF_STOCK(); function calcL2BasefeeParams( uint64 gasExcessMax, @@ -57,7 +57,7 @@ library LibL2Tokenomics { uint64 ratio = uint64((price2x * 100) / price1x); if (expected2X1XRatio != ratio) { - revert L1_1559_GAS_CHANGE_MISMATCH(expected2X1XRatio, ratio); + revert M1559_UNEXPECTED_CHANGE(expected2X1XRatio, ratio); } } } @@ -81,7 +81,7 @@ library LibL2Tokenomics { ) private pure returns (uint256) { uint256 x = l2GasExcess * xscale; if (x > LibFixedPointMath.MAX_EXP_INPUT) { - revert L1_OUT_OF_BLOCK_SPACE(); + revert M1559_OUT_OF_STOCK(); } return uint256(LibFixedPointMath.exp(int256(x))); } diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index a81a10772a7..a856608f45f 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -75,6 +75,9 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { error L2_PUBLIC_INPUT_HASH_MISMATCH(); error L2_TOO_LATE(); + error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio); + error M1559_OUT_OF_STOCK(); + /********************** * Constructor * **********************/ From f5ed9c24bbfcf432435cab2397b534a7cbba14e1 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:05:31 +0800 Subject: [PATCH 61/90] fix --- .../contracts/L1/libs/LibL2Tokenomics.sol | 8 ++++---- packages/protocol/test2/LibL2Tokenomics.t.sol | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 3292943de79..352187bb87a 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -35,22 +35,22 @@ library LibL2Tokenomics { xscale = LibFixedPointMath.MAX_EXP_INPUT / gasExcessMax; // calculate yscale - yscale = calcL2Basefee(l2GasExcess, xscale, basefeeInitial, gasTarget) + yscale = calcL2Basefee(xscale, basefeeInitial, l2GasExcess, gasTarget) .toUint128(); // Verify the gas price ratio between two blocks, one has // 2*gasTarget gas and the other one has gasTarget gas. { uint256 price1x = calcL2Basefee( - l2GasExcess, xscale, yscale, + l2GasExcess, gasTarget ); uint256 price2x = calcL2Basefee( - l2GasExcess, xscale, yscale, + l2GasExcess, gasTarget * 2 ); @@ -63,9 +63,9 @@ library LibL2Tokenomics { } function calcL2Basefee( - uint64 l2GasExcess, uint128 xscale, uint128 yscale, + uint64 l2GasExcess, uint64 gasAmount ) internal pure returns (uint256) { uint64 _gasAmount = gasAmount == 0 ? 1 : gasAmount; diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index b0e48919435..afeec8f74ea 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -30,7 +30,7 @@ contract TestLibL2Tokenomics is Test { }); // basefee should be 0 when gasExcess is 0 - assertEq(T.calcL2Basefee(0, xscale, yscale, gasTarget), 0); + assertEq(T.calcL2Basefee(xscale, yscale, 0, gasTarget), 0); uint64 N = 50; // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] @@ -42,17 +42,17 @@ contract TestLibL2Tokenomics is Test { l2GasExcess += gasTarget ) { uint256 basefee1 = T.calcL2Basefee( - l2GasExcess, xscale, yscale, + l2GasExcess, gasTarget ); assertLt(basefee1, type(uint64).max); uint256 basefee2 = T.calcL2Basefee( - l2GasExcess, xscale, yscale, + l2GasExcess, 2 * gasTarget ); @@ -79,26 +79,26 @@ contract TestLibL2Tokenomics is Test { expected2X1XRatio: expected2X1XRatio }); - assertEq(T.calcL2Basefee(0, xscale, yscale, 0), 0); - assertEq(T.calcL2Basefee(0, xscale, yscale, 1), 0); + assertEq(T.calcL2Basefee(xscale, yscale, 0, 0), 0); + assertEq(T.calcL2Basefee(xscale, yscale, 0, 1), 0); assertGt( T.calcL2Basefee( - gasExcessMax - gasTarget, xscale, yscale, + gasExcessMax - gasTarget, gasTarget ), type(uint64).max ); assertGt( - T.calcL2Basefee(0, xscale, yscale, gasExcessMax), + T.calcL2Basefee(xscale, yscale, 0, gasExcessMax), type(uint64).max ); assertGt( - T.calcL2Basefee(gasExcessMax / 2, xscale, yscale, gasExcessMax / 2), + T.calcL2Basefee(xscale, yscale, gasExcessMax / 2, gasExcessMax / 2), type(uint64).max ); } From be2d91ade5bad121a806cfcce9a0b9d54d78a4b8 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:17:28 +0800 Subject: [PATCH 62/90] fix --- .../contracts/L1/libs/LibL2Tokenomics.sol | 66 ++++++++----------- packages/protocol/contracts/L2/TaikoL2.sol | 24 +++---- packages/protocol/test2/LibL2Tokenomics.t.sol | 49 +++++++------- 3 files changed, 65 insertions(+), 74 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 352187bb87a..34d5363e517 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -18,71 +18,57 @@ library LibL2Tokenomics { using LibMath for uint256; using SafeCastUpgradeable for uint256; - error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio); + error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual); error M1559_OUT_OF_STOCK(); - function calcL2BasefeeParams( - uint64 gasExcessMax, - uint64 basefeeInitial, - uint64 gasTarget, - uint64 expected2X1XRatio + function calculateScales( + uint64 xMax, + uint64 price, + uint64 target, + uint64 ratio2x1x ) internal pure returns (uint128 xscale, uint128 yscale) { - assert(gasExcessMax != 0); + assert(xMax != 0); - uint64 l2GasExcess = gasExcessMax / 2; + uint64 x = xMax / 2; // calculate xscale - xscale = LibFixedPointMath.MAX_EXP_INPUT / gasExcessMax; + xscale = LibFixedPointMath.MAX_EXP_INPUT / xMax; // calculate yscale - yscale = calcL2Basefee(xscale, basefeeInitial, l2GasExcess, gasTarget) - .toUint128(); + yscale = calculatePrice(xscale, price, x, target).toUint128(); // Verify the gas price ratio between two blocks, one has - // 2*gasTarget gas and the other one has gasTarget gas. + // 2*target gas and the other one has target gas. { - uint256 price1x = calcL2Basefee( - xscale, - yscale, - l2GasExcess, - gasTarget - ); - uint256 price2x = calcL2Basefee( - xscale, - yscale, - l2GasExcess, - gasTarget * 2 - ); + uint256 price1x = calculatePrice(xscale, yscale, x, target); + uint256 price2x = calculatePrice(xscale, yscale, x, target * 2); uint64 ratio = uint64((price2x * 100) / price1x); - if (expected2X1XRatio != ratio) { - revert M1559_UNEXPECTED_CHANGE(expected2X1XRatio, ratio); + if (ratio2x1x != ratio) { + revert M1559_UNEXPECTED_CHANGE(ratio2x1x, ratio); } } } - function calcL2Basefee( + function calculatePrice( uint128 xscale, uint128 yscale, - uint64 l2GasExcess, - uint64 gasAmount + uint64 x, + uint64 xPurchase ) internal pure returns (uint256) { - uint64 _gasAmount = gasAmount == 0 ? 1 : gasAmount; assert(xscale != 0 && yscale != 0); - uint256 _before = _ethqty(l2GasExcess, xscale); - uint256 _after = _ethqty(l2GasExcess + _gasAmount, xscale); - return (_after - _before) / _gasAmount / yscale; + uint64 _xPurchase = xPurchase == 0 ? 1 : xPurchase; + uint256 _before = _calcY(x, xscale); + uint256 _after = _calcY(x + _xPurchase, xscale); + return (_after - _before) / _xPurchase / yscale; } - function _ethqty( - uint256 l2GasExcess, - uint128 xscale - ) private pure returns (uint256) { - uint256 x = l2GasExcess * xscale; - if (x > LibFixedPointMath.MAX_EXP_INPUT) { + function _calcY(uint256 x, uint128 xscale) private pure returns (uint256) { + uint256 _x = x * xscale; + if (_x > LibFixedPointMath.MAX_EXP_INPUT) { revert M1559_OUT_OF_STOCK(); } - return uint256(LibFixedPointMath.exp(int256(x))); + return uint256(LibFixedPointMath.exp(int256(_x))); } } diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index a856608f45f..1e4a3da32fe 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -26,7 +26,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint64 gasIssuedPerSecond; uint64 gasExcessMax; uint64 gasTarget; - uint64 expected2X1XRatio; + uint64 ratio2x1x; } /********************** * State Variables * @@ -75,7 +75,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { error L2_PUBLIC_INPUT_HASH_MISMATCH(); error L2_TOO_LATE(); - error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio); + error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual); error M1559_OUT_OF_STOCK(); /********************** @@ -94,15 +94,15 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { _param1559.gasIssuedPerSecond == 0 || _param1559.gasExcessMax == 0 || _param1559.gasTarget == 0 || - _param1559.expected2X1XRatio == 0 + _param1559.ratio2x1x == 0 ) revert L2_INVALID_1559_PARAMS(); - (xscale, yscale) = LibL2Tokenomics.calcL2BasefeeParams( - _param1559.gasExcessMax, - _param1559.basefee, - _param1559.gasTarget, - _param1559.expected2X1XRatio - ); + (xscale, yscale) = LibL2Tokenomics.calculateScales({ + xMax: _param1559.gasExcessMax, + price: _param1559.basefee, + target: _param1559.gasTarget, + ratio2x1x: _param1559.ratio2x1x + }); if (xscale == 0 || yscale == 0) revert L2_INVALID_1559_PARAMS(); @@ -169,11 +169,11 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { if (basefee != 0) { basefee = LibL2Tokenomics - .calcL2Basefee({ - l2GasExcess: gasExcess, + .calculatePrice({ xscale: xscale, yscale: yscale, - gasAmount: uint64(block.gaslimit) + x: gasExcess, + xPurchase: uint64(block.gaslimit) }) .toUint64(); assert(basefee != 0); diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/LibL2Tokenomics.t.sol index afeec8f74ea..8837179c2f7 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/LibL2Tokenomics.t.sol @@ -21,27 +21,27 @@ contract TestLibL2Tokenomics is Test { uint64 gasExcessMax = (uint(15000000) * 256 * rand).toUint64(); uint64 gasTarget = (uint(6000000) * rand).toUint64(); uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); - uint64 expected2X1XRatio = 111; - (uint128 xscale, uint128 yscale) = T.calcL2BasefeeParams({ - gasExcessMax: gasExcessMax, - basefeeInitial: basefeeInitial, - gasTarget: gasTarget, - expected2X1XRatio: expected2X1XRatio + uint64 ratio2x1x = 111; + (uint128 xscale, uint128 yscale) = T.calculateScales({ + xMax: gasExcessMax, + price: basefeeInitial, + target: gasTarget, + ratio2x1x: ratio2x1x }); // basefee should be 0 when gasExcess is 0 - assertEq(T.calcL2Basefee(xscale, yscale, 0, gasTarget), 0); + assertEq(T.calculatePrice(xscale, yscale, 0, gasTarget), 0); uint64 N = 50; // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] - // gas range, the expected2X1XRatio holds, and the gas price is still smaller + // gas range, the ratio2x1x holds, and the gas price is still smaller // than uint64.max for ( uint64 l2GasExcess = gasExcessMax / 2 - N * gasTarget; l2GasExcess <= gasExcessMax / 2 + N * gasTarget; l2GasExcess += gasTarget ) { - uint256 basefee1 = T.calcL2Basefee( + uint256 basefee1 = T.calculatePrice( xscale, yscale, l2GasExcess, @@ -49,7 +49,7 @@ contract TestLibL2Tokenomics is Test { ); assertLt(basefee1, type(uint64).max); - uint256 basefee2 = T.calcL2Basefee( + uint256 basefee2 = T.calculatePrice( xscale, yscale, l2GasExcess, @@ -59,7 +59,7 @@ contract TestLibL2Tokenomics is Test { assertLt(basefee2, type(uint64).max); if (basefee1 != 0) { - assertEq((basefee2 * 100) / basefee1, expected2X1XRatio); + assertEq((basefee2 * 100) / basefee1, ratio2x1x); } } } @@ -70,20 +70,20 @@ contract TestLibL2Tokenomics is Test { uint64 gasExcessMax = (uint(15000000) * 256 * rand).toUint64(); uint64 gasTarget = (uint(6000000) * rand).toUint64(); uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); - uint64 expected2X1XRatio = 111; + uint64 ratio2x1x = 111; - (uint128 xscale, uint128 yscale) = T.calcL2BasefeeParams({ - gasExcessMax: gasExcessMax, - basefeeInitial: basefeeInitial, - gasTarget: gasTarget, - expected2X1XRatio: expected2X1XRatio + (uint128 xscale, uint128 yscale) = T.calculateScales({ + xMax: gasExcessMax, + price: basefeeInitial, + target: gasTarget, + ratio2x1x: ratio2x1x }); - assertEq(T.calcL2Basefee(xscale, yscale, 0, 0), 0); - assertEq(T.calcL2Basefee(xscale, yscale, 0, 1), 0); + assertEq(T.calculatePrice(xscale, yscale, 0, 0), 0); + assertEq(T.calculatePrice(xscale, yscale, 0, 1), 0); assertGt( - T.calcL2Basefee( + T.calculatePrice( xscale, yscale, gasExcessMax - gasTarget, @@ -93,12 +93,17 @@ contract TestLibL2Tokenomics is Test { ); assertGt( - T.calcL2Basefee(xscale, yscale, 0, gasExcessMax), + T.calculatePrice(xscale, yscale, 0, gasExcessMax), type(uint64).max ); assertGt( - T.calcL2Basefee(xscale, yscale, gasExcessMax / 2, gasExcessMax / 2), + T.calculatePrice( + xscale, + yscale, + gasExcessMax / 2, + gasExcessMax / 2 + ), type(uint64).max ); } From 709c895aef90300b52f512c9ce3a14866d23f0ec Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:19:10 +0800 Subject: [PATCH 63/90] fix --- .../protocol/contracts/L1/libs/LibL2Tokenomics.sol | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol index 34d5363e517..f60d10c4c82 100644 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol @@ -39,16 +39,12 @@ library LibL2Tokenomics { // Verify the gas price ratio between two blocks, one has // 2*target gas and the other one has target gas. - { - uint256 price1x = calculatePrice(xscale, yscale, x, target); - uint256 price2x = calculatePrice(xscale, yscale, x, target * 2); + uint256 price1x = calculatePrice(xscale, yscale, x, target); + uint256 price2x = calculatePrice(xscale, yscale, x, target * 2); + uint64 ratio = uint64((price2x * 100) / price1x); - uint64 ratio = uint64((price2x * 100) / price1x); - - if (ratio2x1x != ratio) { - revert M1559_UNEXPECTED_CHANGE(ratio2x1x, ratio); - } - } + if (ratio2x1x != ratio) + revert M1559_UNEXPECTED_CHANGE(ratio2x1x, ratio); } function calculatePrice( From 1f73d9730a8840d62ce5ff96559869cd9db87a03 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:23:30 +0800 Subject: [PATCH 64/90] fix --- packages/protocol/contracts/L1/TaikoL1.sol | 1 - .../contracts/L1/libs/LibL2Tokenomics.sol | 70 ------------ packages/protocol/contracts/L2/TaikoL2.sol | 6 +- .../protocol/contracts/libs/Lib1559Math.sol | 107 +++++++++--------- .../contracts/test/libs/TestLib1559Math.sol | 39 ------- packages/protocol/docs/L2EIP1559.md | 2 +- packages/protocol/script/deploy_on_l1.sh | 2 +- ...{LibL2Tokenomics.t.sol => Lib1559Math.sol} | 10 +- ...L1Tokenomics.t.sol => LibTokenomics.t.sol} | 0 9 files changed, 61 insertions(+), 176 deletions(-) delete mode 100644 packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol delete mode 100644 packages/protocol/contracts/test/libs/TestLib1559Math.sol rename packages/protocol/test2/{LibL2Tokenomics.t.sol => Lib1559Math.sol} (93%) rename packages/protocol/test2/{LibL1Tokenomics.t.sol => LibTokenomics.t.sol} (100%) diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 8772de5c405..bdf8d9f2da9 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -10,7 +10,6 @@ import {AddressResolver} from "../common/AddressResolver.sol"; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; import {LibTokenomics} from "./libs/LibTokenomics.sol"; -import {LibL2Tokenomics} from "./libs/LibL2Tokenomics.sol"; import {LibProposing} from "./libs/LibProposing.sol"; import {LibProving} from "./libs/LibProving.sol"; import {LibUtils} from "./libs/LibUtils.sol"; diff --git a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol deleted file mode 100644 index f60d10c4c82..00000000000 --- a/packages/protocol/contracts/L1/libs/LibL2Tokenomics.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// _____ _ _ _ _ -// |_ _|_ _(_) |_____ | | __ _| |__ ___ -// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< -// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ - -pragma solidity ^0.8.18; - -import {AddressResolver} from "../../common/AddressResolver.sol"; -import {LibMath} from "../../libs/LibMath.sol"; -import {LibFixedPointMath} from "../../thirdparty/LibFixedPointMath.sol"; -import { - SafeCastUpgradeable -} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -import {TaikoData} from "../TaikoData.sol"; - -library LibL2Tokenomics { - using LibMath for uint256; - using SafeCastUpgradeable for uint256; - - error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual); - error M1559_OUT_OF_STOCK(); - - function calculateScales( - uint64 xMax, - uint64 price, - uint64 target, - uint64 ratio2x1x - ) internal pure returns (uint128 xscale, uint128 yscale) { - assert(xMax != 0); - - uint64 x = xMax / 2; - - // calculate xscale - xscale = LibFixedPointMath.MAX_EXP_INPUT / xMax; - - // calculate yscale - yscale = calculatePrice(xscale, price, x, target).toUint128(); - - // Verify the gas price ratio between two blocks, one has - // 2*target gas and the other one has target gas. - uint256 price1x = calculatePrice(xscale, yscale, x, target); - uint256 price2x = calculatePrice(xscale, yscale, x, target * 2); - uint64 ratio = uint64((price2x * 100) / price1x); - - if (ratio2x1x != ratio) - revert M1559_UNEXPECTED_CHANGE(ratio2x1x, ratio); - } - - function calculatePrice( - uint128 xscale, - uint128 yscale, - uint64 x, - uint64 xPurchase - ) internal pure returns (uint256) { - assert(xscale != 0 && yscale != 0); - uint64 _xPurchase = xPurchase == 0 ? 1 : xPurchase; - uint256 _before = _calcY(x, xscale); - uint256 _after = _calcY(x + _xPurchase, xscale); - return (_after - _before) / _xPurchase / yscale; - } - - function _calcY(uint256 x, uint128 xscale) private pure returns (uint256) { - uint256 _x = x * xscale; - if (_x > LibFixedPointMath.MAX_EXP_INPUT) { - revert M1559_OUT_OF_STOCK(); - } - return uint256(LibFixedPointMath.exp(int256(_x))); - } -} diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 1e4a3da32fe..b0c5cf5ccea 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -8,7 +8,7 @@ pragma solidity ^0.8.18; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; -import {LibL2Tokenomics} from "../L1/libs/LibL2Tokenomics.sol"; +import {Lib1559Math} from "../libs/Lib1559Math.sol"; import {TaikoL2Signer} from "./TaikoL2Signer.sol"; import { SafeCastUpgradeable @@ -97,7 +97,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { _param1559.ratio2x1x == 0 ) revert L2_INVALID_1559_PARAMS(); - (xscale, yscale) = LibL2Tokenomics.calculateScales({ + (xscale, yscale) = Lib1559Math.calculateScales({ xMax: _param1559.gasExcessMax, price: _param1559.basefee, target: _param1559.gasTarget, @@ -168,7 +168,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // Check EIP-1559 basefee if (basefee != 0) { - basefee = LibL2Tokenomics + basefee = Lib1559Math .calculatePrice({ xscale: xscale, yscale: yscale, diff --git a/packages/protocol/contracts/libs/Lib1559Math.sol b/packages/protocol/contracts/libs/Lib1559Math.sol index c86196d9e83..4fec75b581b 100644 --- a/packages/protocol/contracts/libs/Lib1559Math.sol +++ b/packages/protocol/contracts/libs/Lib1559Math.sol @@ -6,68 +6,63 @@ pragma solidity ^0.8.18; -/** - * @notice This library offers two functions for EIP-1559-style math. - * See more at https://dankradfeist.de/ethereum/2022/03/16/exponential-eip1559.html - */ +import {LibMath} from "./LibMath.sol"; +import {LibFixedPointMath} from "../thirdparty/LibFixedPointMath.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; + library Lib1559Math { - /** - * @notice Calculates and returns the next round's target value using the equation below: - * - * `nextTarget = prevTarget * ((A-1) * T + prevMeasured / (A * T)` - * which implies if `prevMeasured` is larger than `T`, `nextTarget` will - * become larger than `prevTarget`. - * - * @param prevTarget The previous round's target value. - * @param prevMeasured The previous round's measured value. It must be in the same unit as `T`. - * @param t The base target value. It must be in the same unit as `prevMeasured`. - * @param a The adjustment factor. Bigger values change the next round's target more slowly. - * @return nextTarget The next round's target value. - */ - function adjustTarget( - uint256 prevTarget, - uint256 prevMeasured, - uint256 t, - uint256 a - ) internal pure returns (uint256 nextTarget) { - assert(prevTarget != 0 && t != 0 && a > 1); + using LibMath for uint256; // TODO(daniel) + using SafeCastUpgradeable for uint256; - uint256 x = prevTarget * ((a - 1) * t + prevMeasured); - uint256 y = a * t; - nextTarget = x / y; + error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual); + error M1559_OUT_OF_STOCK(); - if (nextTarget == 0) { - nextTarget = prevTarget; - } - } + function calculateScales( + uint64 xMax, + uint64 price, + uint64 target, + uint64 ratio2x1x + ) internal pure returns (uint128 xscale, uint128 yscale) { + assert(xMax != 0); + + uint64 x = xMax / 2; + + // calculate xscale + xscale = LibFixedPointMath.MAX_EXP_INPUT / xMax; - /** - * @notice Calculates and returns the next round's target value using the equation below: - * - * `nextTarget = prevTarget * A * T / ((A-1) * T + prevMeasured)` - * which implies if `prevMeasured` is larger than `T`, `nextTarget` will - * become smaller than `prevTarget`. - * - * @param prevTarget The previous round's target value. - * @param prevMeasured The previous round's measured value. It must be in the same unit as `T`. - * @param t The base target value. It must be in the same unit as `prevMeasured`. - * @param a The adjustment factor. Bigger values change the next round's target more slowly. - * @return nextTarget The next round's target value. - */ - function adjustTargetReverse( - uint256 prevTarget, - uint256 prevMeasured, - uint256 t, - uint256 a - ) internal pure returns (uint256 nextTarget) { - assert(prevTarget != 0 && t != 0 && a > 1); + // calculate yscale + yscale = calculatePrice(xscale, price, x, target).toUint128(); - uint256 x = prevTarget * a * t; - uint256 y = (a - 1) * t + prevMeasured; - nextTarget = x / y; + // Verify the gas price ratio between two blocks, one has + // 2*target gas and the other one has target gas. + uint256 price1x = calculatePrice(xscale, yscale, x, target); + uint256 price2x = calculatePrice(xscale, yscale, x, target * 2); + uint64 ratio = uint64((price2x * 100) / price1x); + + if (ratio2x1x != ratio) + revert M1559_UNEXPECTED_CHANGE(ratio2x1x, ratio); + } + + function calculatePrice( + uint128 xscale, + uint128 yscale, + uint64 x, + uint64 xPurchase + ) internal pure returns (uint256) { + assert(xscale != 0 && yscale != 0); + uint64 _xPurchase = xPurchase == 0 ? 1 : xPurchase; + uint256 _before = _calcY(x, xscale); + uint256 _after = _calcY(x + _xPurchase, xscale); + return (_after - _before) / _xPurchase / yscale; + } - if (nextTarget == 0) { - nextTarget = prevTarget; + function _calcY(uint256 x, uint128 xscale) private pure returns (uint256) { + uint256 _x = x * xscale; + if (_x > LibFixedPointMath.MAX_EXP_INPUT) { + revert M1559_OUT_OF_STOCK(); } + return uint256(LibFixedPointMath.exp(int256(_x))); } } diff --git a/packages/protocol/contracts/test/libs/TestLib1559Math.sol b/packages/protocol/contracts/test/libs/TestLib1559Math.sol deleted file mode 100644 index 0bbb57b9a05..00000000000 --- a/packages/protocol/contracts/test/libs/TestLib1559Math.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -// _____ _ _ _ _ -// |_ _|_ _(_) |_____ | | __ _| |__ ___ -// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< -// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ - -pragma solidity ^0.8.18; - -import {Lib1559Math} from "../../libs/Lib1559Math.sol"; - -contract TestLib1559Math { - function adjustTarget( - uint256 firstTarget, - uint256 startingMeasurement, - uint256 baseTargetVal, - uint256 adjustmentFactor - ) public pure returns (uint256 nextTarget) { - nextTarget = Lib1559Math.adjustTarget( - firstTarget, - startingMeasurement, - baseTargetVal, - adjustmentFactor - ); - } - - function adjustTargetReverse( - uint256 firstTarget, - uint256 startingMeasurement, - uint256 baseTargetVal, - uint256 adjustmentFactor - ) public pure returns (uint256 nextTarget) { - nextTarget = Lib1559Math.adjustTargetReverse( - firstTarget, - startingMeasurement, - baseTargetVal, - adjustmentFactor - ); - } -} diff --git a/packages/protocol/docs/L2EIP1559.md b/packages/protocol/docs/L2EIP1559.md index 4ed797f4566..3ff6502a02c 100644 --- a/packages/protocol/docs/L2EIP1559.md +++ b/packages/protocol/docs/L2EIP1559.md @@ -32,4 +32,4 @@ It turns out the initial value of gasExcess doesn't really matter for the above ## Adjust the slope -To adjust the slope of the curve to satisfy $R == basefee(2T)/basefee(T)$, we simply need to chose $M$ and $b_0$. $b_0$ is simply to decide -- if we believe the cost of a L2 transaction is $1/n$ of the same L1 transaction, we simply use the current L1 base fee divided by $n$. Then we can simply tune $M$ to make sure $R == basefee(2T)/basefee(T)$ holds. This is very simply manually a try-and-adjust approach as shown in `LibL2Tokenomics.t.sol`. The TaikoL1 contract will check if $R == basefee(2T)/basefee(T)$ holds but will not calculate $M$ for us. +To adjust the slope of the curve to satisfy $R == basefee(2T)/basefee(T)$, we simply need to chose $M$ and $b_0$. $b_0$ is simply to decide -- if we believe the cost of a L2 transaction is $1/n$ of the same L1 transaction, we simply use the current L1 base fee divided by $n$. Then we can simply tune $M$ to make sure $R == basefee(2T)/basefee(T)$ holds. This is very simply manually a try-and-adjust approach as shown in `Lib1559Math.t.sol`. The TaikoL1 contract will check if $R == basefee(2T)/basefee(T)$ holds but will not calculate $M$ for us. diff --git a/packages/protocol/script/deploy_on_l1.sh b/packages/protocol/script/deploy_on_l1.sh index 554b0f3842b..2ec0baa644b 100755 --- a/packages/protocol/script/deploy_on_l1.sh +++ b/packages/protocol/script/deploy_on_l1.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Please reference LibL2Tokenomics.t.sol for L2 EIP-1559 related variables. +# Please reference Lib1559Math.t.sol for L2 EIP-1559 related variables. set -e PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ diff --git a/packages/protocol/test2/LibL2Tokenomics.t.sol b/packages/protocol/test2/Lib1559Math.sol similarity index 93% rename from packages/protocol/test2/LibL2Tokenomics.t.sol rename to packages/protocol/test2/Lib1559Math.sol index 8837179c2f7..ec6fb4042a5 100644 --- a/packages/protocol/test2/LibL2Tokenomics.t.sol +++ b/packages/protocol/test2/Lib1559Math.sol @@ -3,16 +3,16 @@ pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {LibL2Tokenomics as T} from "../contracts/L1/libs/LibL2Tokenomics.sol"; +import {Lib1559Math as T} from "../contracts/libs/Lib1559Math.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -import { - LibFixedPointMath as M -} from "../contracts/thirdparty/LibFixedPointMath.sol"; +// import { +// LibFixedPointMath as M +// } from "../contracts/thirdparty/LibFixedPointMath.sol"; -contract TestLibL2Tokenomics is Test { +contract TestLib1559Math is Test { using SafeCastUpgradeable for uint256; function test1559_2X1XRatio(uint16 rand) public { diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol similarity index 100% rename from packages/protocol/test2/LibL1Tokenomics.t.sol rename to packages/protocol/test2/LibTokenomics.t.sol From e3648b84ac55abb4491635af75558d277ffee870 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:23:50 +0800 Subject: [PATCH 65/90] fix --- .../contract-documentation/L1/TaikoData.md | 8 +- .../contract-documentation/L1/TaikoErrors.md | 10 +-- .../contract-documentation/L1/TaikoL1.md | 22 ++---- .../contract-documentation/L2/TaikoL2.md | 74 ++++++++++++++++++- 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 724f1a8e400..63e25fb4aa4 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -53,8 +53,6 @@ struct StateVariables { uint64 avgBlockTime; uint64 avgProofTime; uint64 lastProposedAt; - uint64 l2Basefee; - uint64 l2GasExcess; } ``` @@ -158,12 +156,12 @@ struct State { mapping(bytes32 => struct TaikoData.TxListInfo) txListInfo; uint64 genesisHeight; uint64 genesisTimestamp; - uint64 l2Xscale; - uint64 l2Yscale; + uint64 __reserved1; + uint64 __reserved2; uint64 numBlocks; uint64 lastProposedAt; uint64 avgBlockTime; - uint64 l2GasExcess; + uint64 __reserved3; uint64 lastVerifiedBlockId; uint64 __reserved4; uint64 avgProofTime; diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index 8f3226b832e..a8fb6a8d59c 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -4,10 +4,10 @@ title: TaikoErrors ## TaikoErrors -### L1_1559_GAS_CHANGE_MISMATCH +### M1559_UNEXPECTED_CHANGE ```solidity -error L1_1559_GAS_CHANGE_MISMATCH(uint64 expectedRatio, uint64 actualRatio) +error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio) ``` ### L1_1559_X_SCALE_TOO_LARGE @@ -112,12 +112,6 @@ error L1_NOT_ORACLE_PROVER() error L1_NOT_SOLO_PROPOSER() ``` -### L1_OUT_OF_BLOCK_SPACE - -```solidity -error L1_OUT_OF_BLOCK_SPACE() -``` - ### L1_TOO_MANY_BLOCKS ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index e6b23503eb8..5c14d920a4d 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -13,22 +13,18 @@ struct TaikoData.State state ### init ```solidity -function init(address _addressManager, uint64 _feeBase, bytes32 _l2GenesisBlockHash, uint64 _l2GasExcessMax, uint64 _l2Basefee, uint64 _l2GasTarget, uint64 _l2Expected2X1XRatio) external +function init(address _addressManager, uint64 _feeBase, bytes32 _genesisBlockHash) external ``` Initialize the rollup. #### Parameters -| Name | Type | Description | -| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- | -| \_addressManager | address | The AddressManager address. | -| \_feeBase | uint64 | The initial value of the proposer-fee/prover-reward feeBase. | -| \_l2GenesisBlockHash | bytes32 | The block hash of the genesis block. | -| \_l2GasExcessMax | uint64 | The max amount of L2 gas that can ever be purchased under any possible circumstances before additional gas are issued. | -| \_l2Basefee | uint64 | The initial value of L2 EIP-1559 base fee per gas. | -| \_l2GasTarget | uint64 | A value to verify the correctness of L2 EIP-1559 config. | -| \_l2Expected2X1XRatio | uint64 | A value to verify the correctness of L2 EIP-1559 config. | +| Name | Type | Description | +| ------------------ | ------- | ------------------------------------------------------------ | +| \_addressManager | address | The AddressManager address. | +| \_feeBase | uint64 | The initial value of the proposer-fee/prover-reward feeBase. | +| \_genesisBlockHash | bytes32 | The block hash of the genesis block. | ### proposeBlock @@ -129,12 +125,6 @@ function getXchainBlockHash(uint256 blockId) public view returns (bytes32) function getXchainSignalRoot(uint256 blockId) public view returns (bytes32) ``` -### getL2Basefee - -```solidity -function getL2Basefee(uint32 gasLimit) public view returns (uint64 basefee) -``` - ### getStateVariables ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md index 5138d4743dd..dec5caeb1b6 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md +++ b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md @@ -13,16 +13,58 @@ struct VerifiedBlock { } ``` +### EIP1559Params + +```solidity +struct EIP1559Params { + uint64 basefee; + uint64 gasIssuedPerSecond; + uint64 gasExcessMax; + uint64 gasTarget; + uint64 ratio2x1x; +} +``` + ### publicInputHash ```solidity bytes32 publicInputHash ``` +### yscale + +```solidity +uint128 yscale +``` + +### xscale + +```solidity +uint128 xscale +``` + +### gasIssuedPerSecond + +```solidity +uint64 gasIssuedPerSecond +``` + +### basefee + +```solidity +uint64 basefee +``` + +### gasExcess + +```solidity +uint64 gasExcess +``` + ### latestSyncedL1Height ```solidity -uint256 latestSyncedL1Height +uint64 latestSyncedL1Height ``` ### BlockVars @@ -31,6 +73,18 @@ uint256 latestSyncedL1Height event BlockVars(uint256 number, bytes32 parentHash, uint256 timestamp, uint256 basefee, uint256 prevrandao, address coinbase, uint256 gaslimit, uint256 chainid) ``` +### L2_INVALID_1559_PARAMS + +```solidity +error L2_INVALID_1559_PARAMS() +``` + +### L2_INVALID_BASEFEE + +```solidity +error L2_INVALID_BASEFEE() +``` + ### L2_INVALID_CHAIN_ID ```solidity @@ -55,16 +109,28 @@ error L2_PUBLIC_INPUT_HASH_MISMATCH() error L2_TOO_LATE() ``` +### M1559_UNEXPECTED_CHANGE + +```solidity +error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual) +``` + +### M1559_OUT_OF_STOCK + +```solidity +error M1559_OUT_OF_STOCK() +``` + ### init ```solidity -function init(address _addressManager) external +function init(address _addressManager, struct TaikoL2.EIP1559Params _param1559) external ``` ### anchor ```solidity -function anchor(uint256 l1Height, bytes32 l1Hash, bytes32 l1SignalRoot) external +function anchor(uint64 l1Height, bytes32 l1Hash, bytes32 l1SignalRoot) external ``` Persist the latest L1 block height and hash to L2 for cross-layer @@ -84,7 +150,7 @@ This transaction shall be the first transaction in every L2 block. | Name | Type | Description | | ------------ | ------- | --------------------------------------------------------- | -| l1Height | uint256 | The latest L1 block height when this block was proposed. | +| l1Height | uint64 | The latest L1 block height when this block was proposed. | | l1Hash | bytes32 | The latest L1 block hash when this block was proposed. | | l1SignalRoot | bytes32 | The latest value of the L1 "signal service storage root". | From c02042d403ab37c57aa73408f4331f8b822e62d9 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:25:13 +0800 Subject: [PATCH 66/90] fix --- packages/protocol/contracts/L1/TaikoErrors.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 359a4f56172..927d10632f6 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -8,7 +8,6 @@ pragma solidity ^0.8.18; abstract contract TaikoErrors { // The following custom errors must match the definitions in other V1 libraries. - error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio); error L1_1559_X_SCALE_TOO_LARGE(); error L1_1559_Y_SCALE_TOO_LARGE(); error L1_ALREADY_PROVEN(); From e1b099d8c8d4b27cb6968011038f4884dd22de00 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:27:45 +0800 Subject: [PATCH 67/90] fix --- packages/protocol/contracts/L2/TaikoL2.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index b0c5cf5ccea..7be2bf799be 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -166,14 +166,13 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { emit XchainSynced(l1Height, l1Hash, l1SignalRoot); // Check EIP-1559 basefee - if (basefee != 0) { basefee = Lib1559Math .calculatePrice({ xscale: xscale, yscale: yscale, x: gasExcess, - xPurchase: uint64(block.gaslimit) + xPurchase: block.gaslimit.toUint64() }) .toUint64(); assert(basefee != 0); From 0a8f01df675e0bd9c94d5168c1cb88b5d331d076 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 13:37:54 +0800 Subject: [PATCH 68/90] fix --- packages/protocol/contracts/L2/TaikoL2.sol | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 7be2bf799be..1796b055994 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -44,10 +44,11 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint128 public yscale; uint128 public xscale; + uint64 public parentTimestamp; + uint64 public latestSyncedL1Height; uint64 public gasIssuedPerSecond; uint64 public basefee; uint64 public gasExcess; - uint64 public latestSyncedL1Height; uint256[44] private __gap; @@ -109,6 +110,8 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { gasExcess = _param1559.gasExcessMax / 2; } + parentTimestamp = uint64(block.timestamp); + EssentialContract._init(_addressManager); (publicInputHash, ) = _calcPublicInputHash(block.number); @@ -167,18 +170,27 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // Check EIP-1559 basefee if (basefee != 0) { + uint64 gasIssued = (gasIssuedPerSecond * + (block.timestamp - parentTimestamp)).toUint64(); + gasExcess = gasExcess > gasIssued ? gasExcess - gasIssued : 0; + + uint64 gasPurchase = block.gaslimit.toUint64(); basefee = Lib1559Math .calculatePrice({ xscale: xscale, yscale: yscale, x: gasExcess, - xPurchase: block.gaslimit.toUint64() + xPurchase: gasPurchase }) .toUint64(); assert(basefee != 0); + + gasExcess += gasPurchase; } if (block.basefee != basefee) revert L2_INVALID_BASEFEE(); + parentTimestamp = uint64(block.timestamp); + // We emit this event so circuits can grab its data to verify block variables. // If plonk lookup table already has all these data, we can still use this // event for debugging purpose. From 00ff4dce68433bedac4344ad99524d59ab1fd838 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 15:31:27 +0800 Subject: [PATCH 69/90] fix --- .../protocol/contracts/L1/TaikoConfig.sol | 1 - packages/protocol/contracts/L1/TaikoData.sol | 1 - packages/protocol/contracts/L2/TaikoL2.sol | 1 + .../protocol/contracts/libs/Lib1559Math.sol | 4 +-- packages/protocol/script/DeployOnL1.s.sol | 25 ++----------------- packages/protocol/test2/TaikoL1.t.sol | 1 - 6 files changed, 4 insertions(+), 29 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 8198b9fe956..92209a52902 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,7 +23,6 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasIssuedPerSecond: 30000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 1275ac02e09..2286e5e6fbe 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -21,7 +21,6 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint256 gasIssuedPerSecond; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 1796b055994..132ab44c414 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -16,6 +16,7 @@ import { contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { using SafeCastUpgradeable for uint256; + struct VerifiedBlock { bytes32 blockHash; bytes32 signalRoot; diff --git a/packages/protocol/contracts/libs/Lib1559Math.sol b/packages/protocol/contracts/libs/Lib1559Math.sol index 4fec75b581b..473768f032c 100644 --- a/packages/protocol/contracts/libs/Lib1559Math.sol +++ b/packages/protocol/contracts/libs/Lib1559Math.sol @@ -6,14 +6,12 @@ pragma solidity ^0.8.18; -import {LibMath} from "./LibMath.sol"; import {LibFixedPointMath} from "../thirdparty/LibFixedPointMath.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; library Lib1559Math { - using LibMath for uint256; // TODO(daniel) using SafeCastUpgradeable for uint256; error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual); @@ -60,7 +58,7 @@ library Lib1559Math { function _calcY(uint256 x, uint128 xscale) private pure returns (uint256) { uint256 _x = x * xscale; - if (_x > LibFixedPointMath.MAX_EXP_INPUT) { + if (_x >= LibFixedPointMath.MAX_EXP_INPUT) { revert M1559_OUT_OF_STOCK(); } return uint256(LibFixedPointMath.exp(int256(_x))); diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index 71ac8d46f0d..d3ef4e57a3f 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -24,7 +24,7 @@ contract DeployOnL1 is Script, AddressResolver { using SafeCastUpgradeable for uint256; uint256 public l2ChainId = vm.envUint("L2_CHAIN_ID"); - bytes32 public l2GensisHash = vm.envBytes32("L2_GENESIS_HASH"); + bytes32 public gensisHash = vm.envBytes32("L2_GENESIS_HASH"); uint256 public deployerPrivateKey = vm.envUint("PRIVATE_KEY"); @@ -107,19 +107,6 @@ contract DeployOnL1 is Script, AddressResolver { // TaikoL1 TaikoL1 taikoL1 = new TaikoL1(); - uint64 l2GasExcessMax; - uint64 l2Basefee; - uint64 l2GasTarget; - uint64 l2Expected2X1XRatio; - - if (taikoL1.getConfig().gasIssuedPerSecond != 0) { - l2GasExcessMax = vm.envUint("L2_GAS_EXCESS_MAX").toUint64(); - l2Basefee = vm.envUint("L2_BASE_FEE").toUint64(); - l2GasTarget = vm.envUint("L2_GAS_TARGET").toUint64(); - l2Expected2X1XRatio = vm - .envUint("L2_EXPECTED_2X1X_RATIO") - .toUint64(); - } uint64 feeBase = 1 ** 8; // Taiko Token's decimals is 8, not 18 address taikoL1Proxy = deployProxy( @@ -127,15 +114,7 @@ contract DeployOnL1 is Script, AddressResolver { address(taikoL1), bytes.concat( taikoL1.init.selector, - abi.encode( - addressManagerProxy, - feeBase, - l2GensisHash, - l2GasExcessMax, - l2Basefee, - l2GasTarget, - l2Expected2X1XRatio - ) + abi.encode(addressManagerProxy, feeBase, gensisHash) ) ); setAddress("proto_broker", taikoL1Proxy); diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index 1018e7d1043..96334e76212 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -28,7 +28,6 @@ contract TaikoL1WithConfig is TaikoL1 { config.enableSoloProposer = false; config.enableOracleProver = false; config.maxNumProposedBlocks = 10; - config.gasIssuedPerSecond = 0; config.ringBufferSize = 12; // this value must be changed if `maxNumProposedBlocks` is changed. config.slotSmoothingFactor = 4160; From 48f950c0f0fa65616b34b1d24aecbafa57d4174a Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 15:31:59 +0800 Subject: [PATCH 70/90] fix --- packages/protocol/contracts/common/AddressResolver.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/protocol/contracts/common/AddressResolver.sol b/packages/protocol/contracts/common/AddressResolver.sol index c690759cca5..ccb40687e34 100644 --- a/packages/protocol/contracts/common/AddressResolver.sol +++ b/packages/protocol/contracts/common/AddressResolver.sol @@ -73,10 +73,7 @@ abstract contract AddressResolver { uint256 chainId, string memory name ) public pure returns (string memory key) { - key = string.concat(Strings.toString(chainId), ".", name); - // TODO: the next line is cheaper in gas but will break - // many Hardhat tests. - // key = string(bytes.concat(bytes32(chainId), bytes(name))); + key = string(bytes.concat(bytes32(chainId), bytes(name))); } function _init(address addressManager_) internal virtual { From 58936ebde4dad93c02fc20c956240d33c69f7e97 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 15:36:11 +0800 Subject: [PATCH 71/90] fix --- packages/protocol/contracts/L2/TaikoL2.sol | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 132ab44c414..2cc790f1f4d 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -60,13 +60,13 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // Captures all block variables mentioned in // https://docs.soliditylang.org/en/v0.8.18/units-and-global-variables.html event BlockVars( - uint256 number, + uint64 number, + uint64 basefee, + uint64 gaslimit, + uint64 timestamp, bytes32 parentHash, - uint256 timestamp, - uint256 basefee, uint256 prevrandao, address coinbase, - uint256 gaslimit, uint256 chainid ); @@ -188,6 +188,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { gasExcess += gasPurchase; } + if (block.basefee != basefee) revert L2_INVALID_BASEFEE(); parentTimestamp = uint64(block.timestamp); @@ -195,14 +196,15 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // We emit this event so circuits can grab its data to verify block variables. // If plonk lookup table already has all these data, we can still use this // event for debugging purpose. + emit BlockVars({ - number: block.number, + number: uint64(block.number), + basefee: basefee, + gaslimit: uint64(block.gaslimit), + timestamp: uint64(block.timestamp), parentHash: parentHash, - timestamp: block.timestamp, - basefee: block.basefee, prevrandao: block.prevrandao, coinbase: block.coinbase, - gaslimit: block.gaslimit, chainid: block.chainid }); } From 0945a2d9db0aa44b2a96c06d0d90082ff18131ea Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 15:51:37 +0800 Subject: [PATCH 72/90] fix --- packages/protocol/contracts/L2/TaikoL2.sol | 58 ++++++++++++------- .../protocol/contracts/libs/Lib1559Math.sol | 14 ++--- packages/protocol/test2/Lib1559Math.sol | 4 +- .../contract-documentation/L1/TaikoData.md | 1 - .../contract-documentation/L1/TaikoErrors.md | 6 -- .../contract-documentation/L2/TaikoL2.md | 20 ++++--- 6 files changed, 60 insertions(+), 43 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 2cc790f1f4d..6cd57eeb4b5 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -67,7 +67,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { bytes32 parentHash, uint256 prevrandao, address coinbase, - uint256 chainid + uint32 chainid ); error L2_INVALID_1559_PARAMS(); @@ -88,7 +88,8 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { address _addressManager, EIP1559Params calldata _param1559 ) external initializer { - if (block.chainid <= 1) revert L2_INVALID_CHAIN_ID(); + if (block.chainid <= 1 || block.chainid >= type(uint32).max) + revert L2_INVALID_CHAIN_ID(); if (block.number > 1) revert L2_TOO_LATE(); if (_param1559.basefee != 0) { @@ -100,7 +101,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { ) revert L2_INVALID_1559_PARAMS(); (xscale, yscale) = Lib1559Math.calculateScales({ - xMax: _param1559.gasExcessMax, + xExcessMax: _param1559.gasExcessMax, price: _param1559.basefee, target: _param1559.gasTarget, ratio2x1x: _param1559.ratio2x1x @@ -171,22 +172,10 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // Check EIP-1559 basefee if (basefee != 0) { - uint64 gasIssued = (gasIssuedPerSecond * - (block.timestamp - parentTimestamp)).toUint64(); - gasExcess = gasExcess > gasIssued ? gasExcess - gasIssued : 0; - - uint64 gasPurchase = block.gaslimit.toUint64(); - basefee = Lib1559Math - .calculatePrice({ - xscale: xscale, - yscale: yscale, - x: gasExcess, - xPurchase: gasPurchase - }) - .toUint64(); - assert(basefee != 0); - - gasExcess += gasPurchase; + (basefee, gasExcess) = _calcBasefee( + block.timestamp - parentTimestamp, + uint64(block.gaslimit) + ); } if (block.basefee != basefee) revert L2_INVALID_BASEFEE(); @@ -205,7 +194,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { parentHash: parentHash, prevrandao: block.prevrandao, coinbase: block.coinbase, - chainid: block.chainid + chainid: uint32(block.chainid) }); } @@ -213,6 +202,16 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { * Public Functions * **********************/ + function getBasefee( + uint32 timeSinceNow, + uint64 gasLimit + ) private view returns (uint64 _basefee) { + (_basefee, ) = _calcBasefee( + timeSinceNow + block.timestamp - parentTimestamp, + gasLimit + ); + } + function getXchainBlockHash( uint256 number ) public view override returns (bytes32) { @@ -265,4 +264,23 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { currPIH := keccak256(inputs, mul(256, 32)) } } + + function _calcBasefee( + uint256 timeSinceParent, + uint64 gasLimit + ) private view returns (uint64 _basefee, uint64 _gasExcess) { + uint64 gasIssued = (gasIssuedPerSecond * timeSinceParent).toUint64(); + _gasExcess = gasExcess > gasIssued ? gasExcess - gasIssued : 0; + + _basefee = Lib1559Math + .calculatePrice({ + xscale: xscale, + yscale: yscale, + xExcess: _gasExcess, + xPurchase: gasLimit + }) + .toUint64(); + assert(_basefee != 0); + _gasExcess += gasLimit; + } } diff --git a/packages/protocol/contracts/libs/Lib1559Math.sol b/packages/protocol/contracts/libs/Lib1559Math.sol index 473768f032c..ac7a6aee07c 100644 --- a/packages/protocol/contracts/libs/Lib1559Math.sol +++ b/packages/protocol/contracts/libs/Lib1559Math.sol @@ -18,17 +18,17 @@ library Lib1559Math { error M1559_OUT_OF_STOCK(); function calculateScales( - uint64 xMax, + uint64 xExcessMax, uint64 price, uint64 target, uint64 ratio2x1x ) internal pure returns (uint128 xscale, uint128 yscale) { - assert(xMax != 0); + assert(xExcessMax != 0); - uint64 x = xMax / 2; + uint64 x = xExcessMax / 2; // calculate xscale - xscale = LibFixedPointMath.MAX_EXP_INPUT / xMax; + xscale = LibFixedPointMath.MAX_EXP_INPUT / xExcessMax; // calculate yscale yscale = calculatePrice(xscale, price, x, target).toUint128(); @@ -46,13 +46,13 @@ library Lib1559Math { function calculatePrice( uint128 xscale, uint128 yscale, - uint64 x, + uint64 xExcess, uint64 xPurchase ) internal pure returns (uint256) { assert(xscale != 0 && yscale != 0); uint64 _xPurchase = xPurchase == 0 ? 1 : xPurchase; - uint256 _before = _calcY(x, xscale); - uint256 _after = _calcY(x + _xPurchase, xscale); + uint256 _before = _calcY(xExcess, xscale); + uint256 _after = _calcY(xExcess + _xPurchase, xscale); return (_after - _before) / _xPurchase / yscale; } diff --git a/packages/protocol/test2/Lib1559Math.sol b/packages/protocol/test2/Lib1559Math.sol index ec6fb4042a5..fd78ac4a51c 100644 --- a/packages/protocol/test2/Lib1559Math.sol +++ b/packages/protocol/test2/Lib1559Math.sol @@ -23,7 +23,7 @@ contract TestLib1559Math is Test { uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); uint64 ratio2x1x = 111; (uint128 xscale, uint128 yscale) = T.calculateScales({ - xMax: gasExcessMax, + xExcessMax: gasExcessMax, price: basefeeInitial, target: gasTarget, ratio2x1x: ratio2x1x @@ -73,7 +73,7 @@ contract TestLib1559Math is Test { uint64 ratio2x1x = 111; (uint128 xscale, uint128 yscale) = T.calculateScales({ - xMax: gasExcessMax, + xExcessMax: gasExcessMax, price: basefeeInitial, target: gasTarget, ratio2x1x: ratio2x1x diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 63e25fb4aa4..f76cdbecc5a 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -23,7 +23,6 @@ struct Config { uint256 maxNumVerifiedBlocks; uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint256 gasIssuedPerSecond; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index a8fb6a8d59c..5ef4ffc90c1 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -4,12 +4,6 @@ title: TaikoErrors ## TaikoErrors -### M1559_UNEXPECTED_CHANGE - -```solidity -error M1559_UNEXPECTED_CHANGE(uint64 expectedRatio, uint64 actualRatio) -``` - ### L1_1559_X_SCALE_TOO_LARGE ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md index dec5caeb1b6..cfc01684c73 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md +++ b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md @@ -43,6 +43,18 @@ uint128 yscale uint128 xscale ``` +### parentTimestamp + +```solidity +uint64 parentTimestamp +``` + +### latestSyncedL1Height + +```solidity +uint64 latestSyncedL1Height +``` + ### gasIssuedPerSecond ```solidity @@ -61,16 +73,10 @@ uint64 basefee uint64 gasExcess ``` -### latestSyncedL1Height - -```solidity -uint64 latestSyncedL1Height -``` - ### BlockVars ```solidity -event BlockVars(uint256 number, bytes32 parentHash, uint256 timestamp, uint256 basefee, uint256 prevrandao, address coinbase, uint256 gaslimit, uint256 chainid) +event BlockVars(uint64 number, uint64 basefee, uint64 gaslimit, uint64 timestamp, bytes32 parentHash, uint256 prevrandao, address coinbase, uint32 chainid) ``` ### L2_INVALID_1559_PARAMS From 1994877ce3852198281a1d4de96b60b27352f834 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 15:57:01 +0800 Subject: [PATCH 73/90] fix --- packages/protocol/contracts/L2/TaikoL2.sol | 13 +++-- packages/protocol/test2/Lib1559Math.sol | 62 +++++++++------------- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 6cd57eeb4b5..fe438d7be0f 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -43,15 +43,15 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { bytes32 public publicInputHash; uint128 public yscale; - uint128 public xscale; + uint64 public xscale; + uint64 public gasIssuedPerSecond; uint64 public parentTimestamp; uint64 public latestSyncedL1Height; - uint64 public gasIssuedPerSecond; uint64 public basefee; uint64 public gasExcess; - uint256[44] private __gap; + uint256[45] private __gap; /********************** * Events and Errors * @@ -100,14 +100,17 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { _param1559.ratio2x1x == 0 ) revert L2_INVALID_1559_PARAMS(); - (xscale, yscale) = Lib1559Math.calculateScales({ + uint128 _xscale; + (_xscale, yscale) = Lib1559Math.calculateScales({ xExcessMax: _param1559.gasExcessMax, price: _param1559.basefee, target: _param1559.gasTarget, ratio2x1x: _param1559.ratio2x1x }); - if (xscale == 0 || yscale == 0) revert L2_INVALID_1559_PARAMS(); + if (_xscale == 0 || _xscale >= type(uint64).max || yscale == 0) + revert L2_INVALID_1559_PARAMS(); + xscale = uint64(_xscale); gasExcess = _param1559.gasExcessMax / 2; } diff --git a/packages/protocol/test2/Lib1559Math.sol b/packages/protocol/test2/Lib1559Math.sol index fd78ac4a51c..78d018de449 100644 --- a/packages/protocol/test2/Lib1559Math.sol +++ b/packages/protocol/test2/Lib1559Math.sol @@ -18,42 +18,42 @@ contract TestLib1559Math is Test { function test1559_2X1XRatio(uint16 rand) public { vm.assume(rand != 0); - uint64 gasExcessMax = (uint(15000000) * 256 * rand).toUint64(); - uint64 gasTarget = (uint(6000000) * rand).toUint64(); - uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); + uint64 xExcessMax = (uint(15000000) * 256 * rand).toUint64(); + uint64 xTarget = (uint(6000000) * rand).toUint64(); + uint64 price0 = (uint(5000000000) * rand).toUint64(); uint64 ratio2x1x = 111; (uint128 xscale, uint128 yscale) = T.calculateScales({ - xExcessMax: gasExcessMax, - price: basefeeInitial, - target: gasTarget, + xExcessMax: xExcessMax, + price: price0, + target: xTarget, ratio2x1x: ratio2x1x }); - // basefee should be 0 when gasExcess is 0 - assertEq(T.calculatePrice(xscale, yscale, 0, gasTarget), 0); + // basefee should be 0 when xExcess is 0 + assertEq(T.calculatePrice(xscale, yscale, 0, xTarget), 0); uint64 N = 50; - // In the [gasExcessMax/2 - 50 * gasTarget, gasExcessMax/2 + 50 * gasTarget] - // gas range, the ratio2x1x holds, and the gas price is still smaller + // In the [xExcessMax/2 - 50 * xTarget, xExcessMax/2 + 50 * xTarget] + // x range, the ratio2x1x holds, and the price is still smaller // than uint64.max for ( - uint64 l2GasExcess = gasExcessMax / 2 - N * gasTarget; - l2GasExcess <= gasExcessMax / 2 + N * gasTarget; - l2GasExcess += gasTarget + uint64 xExcess = xExcessMax / 2 - N * xTarget; + xExcess <= xExcessMax / 2 + N * xTarget; + xExcess += xTarget ) { uint256 basefee1 = T.calculatePrice( xscale, yscale, - l2GasExcess, - gasTarget + xExcess, + xTarget ); assertLt(basefee1, type(uint64).max); uint256 basefee2 = T.calculatePrice( xscale, yscale, - l2GasExcess, - 2 * gasTarget + xExcess, + 2 * xTarget ); assertLt(basefee2, type(uint64).max); @@ -67,15 +67,15 @@ contract TestLib1559Math is Test { function test1559_SpecalCases(uint16 rand) public { vm.assume(rand != 0); - uint64 gasExcessMax = (uint(15000000) * 256 * rand).toUint64(); - uint64 gasTarget = (uint(6000000) * rand).toUint64(); - uint64 basefeeInitial = (uint(5000000000) * rand).toUint64(); + uint64 xExcessMax = (uint(15000000) * 256 * rand).toUint64(); + uint64 xTarget = (uint(6000000) * rand).toUint64(); + uint64 price0 = (uint(5000000000) * rand).toUint64(); uint64 ratio2x1x = 111; (uint128 xscale, uint128 yscale) = T.calculateScales({ - xExcessMax: gasExcessMax, - price: basefeeInitial, - target: gasTarget, + xExcessMax: xExcessMax, + price: price0, + target: xTarget, ratio2x1x: ratio2x1x }); @@ -83,27 +83,17 @@ contract TestLib1559Math is Test { assertEq(T.calculatePrice(xscale, yscale, 0, 1), 0); assertGt( - T.calculatePrice( - xscale, - yscale, - gasExcessMax - gasTarget, - gasTarget - ), + T.calculatePrice(xscale, yscale, xExcessMax - xTarget, xTarget), type(uint64).max ); assertGt( - T.calculatePrice(xscale, yscale, 0, gasExcessMax), + T.calculatePrice(xscale, yscale, 0, xExcessMax), type(uint64).max ); assertGt( - T.calculatePrice( - xscale, - yscale, - gasExcessMax / 2, - gasExcessMax / 2 - ), + T.calculatePrice(xscale, yscale, xExcessMax / 2, xExcessMax / 2), type(uint64).max ); } From 14e4fb7930b9a45a7db68729b40b6cf55e4c83e4 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 15:57:12 +0800 Subject: [PATCH 74/90] fix --- packages/protocol/test2/Lib1559Math.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/protocol/test2/Lib1559Math.sol b/packages/protocol/test2/Lib1559Math.sol index 78d018de449..528447b3fa2 100644 --- a/packages/protocol/test2/Lib1559Math.sol +++ b/packages/protocol/test2/Lib1559Math.sol @@ -8,10 +8,6 @@ import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -// import { -// LibFixedPointMath as M -// } from "../contracts/thirdparty/LibFixedPointMath.sol"; - contract TestLib1559Math is Test { using SafeCastUpgradeable for uint256; From 97330afa2d98c06a71b8cb9f0d036b470ac9592b Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 4 Apr 2023 16:04:31 +0800 Subject: [PATCH 75/90] fix --- .../protocol/contracts/common/AddressResolver.sol | 5 ++++- .../reference/contract-documentation/L2/TaikoL2.md | 14 +++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/protocol/contracts/common/AddressResolver.sol b/packages/protocol/contracts/common/AddressResolver.sol index ccb40687e34..c690759cca5 100644 --- a/packages/protocol/contracts/common/AddressResolver.sol +++ b/packages/protocol/contracts/common/AddressResolver.sol @@ -73,7 +73,10 @@ abstract contract AddressResolver { uint256 chainId, string memory name ) public pure returns (string memory key) { - key = string(bytes.concat(bytes32(chainId), bytes(name))); + key = string.concat(Strings.toString(chainId), ".", name); + // TODO: the next line is cheaper in gas but will break + // many Hardhat tests. + // key = string(bytes.concat(bytes32(chainId), bytes(name))); } function _init(address addressManager_) internal virtual { diff --git a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md index cfc01684c73..ab7a8c9311b 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md +++ b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md @@ -40,25 +40,25 @@ uint128 yscale ### xscale ```solidity -uint128 xscale +uint64 xscale ``` -### parentTimestamp +### gasIssuedPerSecond ```solidity -uint64 parentTimestamp +uint64 gasIssuedPerSecond ``` -### latestSyncedL1Height +### parentTimestamp ```solidity -uint64 latestSyncedL1Height +uint64 parentTimestamp ``` -### gasIssuedPerSecond +### latestSyncedL1Height ```solidity -uint64 gasIssuedPerSecond +uint64 latestSyncedL1Height ``` ### basefee From 24f1df9cd198f090700c7fe60296c8ded5474999 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 4 Apr 2023 10:53:34 +0200 Subject: [PATCH 76/90] PR review findings II. --- .../protocol/contracts/L1/TaikoConfig.sol | 2 +- .../contracts/L1/libs/LibL1Tokenomics.sol | 46 +++++++++++-------- .../contracts/L1/libs/LibVerifying.sol | 2 +- packages/protocol/test2/LibL1Tokenomics.t.sol | 12 +++-- .../contract-documentation/L1/TaikoL1.md | 4 +- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 8bc95b79389..6bd9b304450 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -40,7 +40,7 @@ library TaikoConfig { feeBaseMAF: 1024, constantFeeRewardBlocks: 1024, txListCacheExpiry: 0, - proofTimeTarget: 100, // 90sec general + proofTimeTarget: 85, // 85s based on A2 testnet status adjustmentQuotient: 16, enableSoloProposer: false, enableOracleProver: true, diff --git a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol index ce821228ab3..8a8d85718cf 100644 --- a/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibL1Tokenomics.sol @@ -76,7 +76,7 @@ library LibL1Tokenomics { uint64 proposedAt, uint32 usedGas ) internal view returns (uint256 newFeeBase, uint256 reward) { - (reward, , newFeeBase) = calculateBaseProof( + (reward, , newFeeBase) = calculateBaseFeeProof( state, config, (provenAt - proposedAt), @@ -87,12 +87,12 @@ library LibL1Tokenomics { /// @notice Update the baseFee for proofs /// @param state - The actual state data /// @param config - Config data - /// @param actProofTime - The actual proof time + /// @param proofTime - The actual proof time /// @param usedGas - Gas in the block - function calculateBaseProof( + function calculateBaseFeeProof( TaikoData.State storage state, TaikoData.Config memory config, - uint64 actProofTime, + uint64 proofTime, uint32 usedGas ) internal @@ -103,36 +103,43 @@ library LibL1Tokenomics { uint256 newBaseFeeProof ) { - uint256 proofTime = state.proofTimeIssued; + uint256 proofTimeIssued = state.proofTimeIssued; // To protect underflow - proofTime = (proofTime > config.proofTimeTarget) - ? proofTime - config.proofTimeTarget + proofTimeIssued = (proofTimeIssued > config.proofTimeTarget) + ? proofTimeIssued - config.proofTimeTarget : uint256(0); - proofTime += actProofTime; + proofTimeIssued += proofTime; newBaseFeeProof = baseFee( - proofTime, + proofTimeIssued, config.proofTimeTarget, config.adjustmentQuotient ); if (config.allowMinting) { - reward = ((state.baseFeeProof * usedGas * actProofTime) / + reward = ((state.baseFeeProof * usedGas * proofTime) / (config.proofTimeTarget * SCALING_FROM_18_TO_TKO_DEC)); } else { + /// TODO: Verify with functional tests + uint256 numBlocksBeingProven = state.numBlocks - + state.lastVerifiedBlockId; if (config.useTimeWeightedReward) { - reward = (state.proofFeeTreasury * - (actProofTime / - (((state.numBlocks - state.lastVerifiedBlockId) * - block.timestamp) - state.accProposalTime))); + /// TODO: Theroetically there can be no underflow (in case numBlocksBeingProven == 0 then + /// state.accProposalTime is also 0) - but verify with unit tests ! + uint256 totalNumProvingSeconds = numBlocksBeingProven * + block.timestamp - + state.accProposalTime; + reward = + (state.proofFeeTreasury * proofTime) / + totalNumProvingSeconds; } else { - reward = (state.proofFeeTreasury / - (state.numBlocks - state.lastVerifiedBlockId)); + /// TODO: Verify with functional tests + reward = state.proofFeeTreasury / numBlocksBeingProven; } } - newProofTimeIssued = proofTime; + newProofTimeIssued = proofTimeIssued; } /// @notice Calculating the exponential smoothened with (target/quotient) @@ -144,8 +151,9 @@ library LibL1Tokenomics { uint256 target, uint256 quotient ) internal pure returns (uint256) { - uint256 newValue = (expCalculation(value, target, quotient)); - return ((newValue / (target * quotient))); + return ( + (expCalculation(value, target, quotient) / (target * quotient)) + ); } /// @notice Calculating the exponential via LibFixedPointMath.sol diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index bf45328c5b7..dd5014ef301 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -128,7 +128,7 @@ library LibVerifying { uint256 reward, uint256 proofTimeIssued, uint256 newBaseFeeProof - ) = LibL1Tokenomics.calculateBaseProof( + ) = LibL1Tokenomics.calculateBaseFeeProof( state, config, uint64(proofTime), diff --git a/packages/protocol/test2/LibL1Tokenomics.t.sol b/packages/protocol/test2/LibL1Tokenomics.t.sol index f0f0e93c0de..7f7bc3f0721 100644 --- a/packages/protocol/test2/LibL1Tokenomics.t.sol +++ b/packages/protocol/test2/LibL1Tokenomics.t.sol @@ -270,7 +270,8 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { ); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); uint64 proposedAt = uint64(block.timestamp); - mine(5); + + mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); @@ -339,7 +340,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { console2.log("Stable"); // To see the issue - adjust the max loop counter below. // The more the loops the bigger the deposits (compared to withrawals) - for (uint256 blockId = 1; blockId < 50; blockId++) { + for (uint256 blockId = 1; blockId < 100; blockId++) { printVariables( "before proposing - affected by verification (verifyBlock() updates)" ); @@ -432,7 +433,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { "before proposing - affected by verification (verifyBlock() updates)" ); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(1); + mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); @@ -530,4 +531,9 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { actualFee = L1.getProverFee(gasLimit); assertGt(previousFee, actualFee); } + + function mine_proofTime() internal { + vm.warp(block.timestamp + 85); + vm.roll(block.number + 4); + } } diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index 1cee555e2f3..7c3ee9491c5 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -79,10 +79,10 @@ function withdraw(uint256 amount) external function getBalance(address addr) public view returns (uint256) ``` -### getBlockFee +### getProverFee ```solidity -function getBlockFee() public view returns (uint256 feeAmount, uint256 depositAmount) +function getProverFee() public view returns (uint256 feeAmount, uint256 depositAmount) ``` ### getProofReward From f72110bbb3ae7c3724ac32e81018dbbb36170583 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Tue, 4 Apr 2023 20:43:10 +0800 Subject: [PATCH 77/90] test(protocol): test 1559 calculation (#13534) --- packages/protocol/contracts/L2/TaikoL2.sol | 53 ++++--- .../protocol/contracts/libs/Lib1559Math.sol | 1 - packages/protocol/foundry.toml | 7 +- .../protocol/test2/LibFixedPointMath.t.sol | 8 +- packages/protocol/test2/TaikoL2.t.sol | 140 +++++++++++++++++- .../contract-documentation/L2/TaikoL2.md | 24 +-- 6 files changed, 191 insertions(+), 42 deletions(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index fe438d7be0f..a7413a4f26a 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; +import {LibMath} from "../libs/LibMath.sol"; import {Lib1559Math} from "../libs/Lib1559Math.sol"; import {TaikoL2Signer} from "./TaikoL2Signer.sol"; import { @@ -16,6 +17,7 @@ import { contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { using SafeCastUpgradeable for uint256; + using LibMath for uint256; struct VerifiedBlock { bytes32 blockHash; @@ -48,8 +50,8 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint64 public parentTimestamp; uint64 public latestSyncedL1Height; - uint64 public basefee; uint64 public gasExcess; + uint64 public __reserved1; uint256[45] private __gap; @@ -70,11 +72,11 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint32 chainid ); + error L2_BASEFEE_MISMATCH(uint64 expected, uint64 actual); error L2_INVALID_1559_PARAMS(); - error L2_INVALID_BASEFEE(); error L2_INVALID_CHAIN_ID(); error L2_INVALID_SENDER(); - error L2_PUBLIC_INPUT_HASH_MISMATCH(); + error L2_PUBLIC_INPUT_HASH_MISMATCH(bytes32 expected, bytes32 actual); error L2_TOO_LATE(); error M1559_UNEXPECTED_CHANGE(uint64 expected, uint64 actual); @@ -92,7 +94,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { revert L2_INVALID_CHAIN_ID(); if (block.number > 1) revert L2_TOO_LATE(); - if (_param1559.basefee != 0) { + if (_param1559.gasIssuedPerSecond != 0) { if ( _param1559.gasIssuedPerSecond == 0 || _param1559.gasExcessMax == 0 || @@ -112,6 +114,8 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { revert L2_INVALID_1559_PARAMS(); xscale = uint64(_xscale); + // basefee = _param1559.basefee; + gasIssuedPerSecond = _param1559.gasIssuedPerSecond; gasExcess = _param1559.gasExcessMax / 2; } @@ -161,7 +165,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { (bytes32 prevPIH, bytes32 currPIH) = _calcPublicInputHash(parentHeight); if (publicInputHash != prevPIH) { - revert L2_PUBLIC_INPUT_HASH_MISMATCH(); + revert L2_PUBLIC_INPUT_HASH_MISMATCH(publicInputHash, prevPIH); } // replace the oldest block hash with the parent's blockhash @@ -174,14 +178,16 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { emit XchainSynced(l1Height, l1Hash, l1SignalRoot); // Check EIP-1559 basefee - if (basefee != 0) { + uint64 basefee; + if (gasIssuedPerSecond != 0) { (basefee, gasExcess) = _calcBasefee( block.timestamp - parentTimestamp, uint64(block.gaslimit) ); } - if (block.basefee != basefee) revert L2_INVALID_BASEFEE(); + if (block.basefee != basefee) + revert L2_BASEFEE_MISMATCH(basefee, uint64(block.basefee)); parentTimestamp = uint64(block.timestamp); @@ -208,7 +214,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { function getBasefee( uint32 timeSinceNow, uint64 gasLimit - ) private view returns (uint64 _basefee) { + ) public view returns (uint64 _basefee) { (_basefee, ) = _calcBasefee( timeSinceNow + block.timestamp - parentTimestamp, gasLimit @@ -272,18 +278,23 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { uint256 timeSinceParent, uint64 gasLimit ) private view returns (uint64 _basefee, uint64 _gasExcess) { - uint64 gasIssued = (gasIssuedPerSecond * timeSinceParent).toUint64(); - _gasExcess = gasExcess > gasIssued ? gasExcess - gasIssued : 0; - - _basefee = Lib1559Math - .calculatePrice({ - xscale: xscale, - yscale: yscale, - xExcess: _gasExcess, - xPurchase: gasLimit - }) - .toUint64(); - assert(_basefee != 0); - _gasExcess += gasLimit; + uint256 gasIssued = gasIssuedPerSecond * timeSinceParent; + + _gasExcess = gasExcess > gasIssued ? uint64(gasExcess - gasIssued) : 0; + + uint256 __basefee = Lib1559Math.calculatePrice({ + xscale: xscale, + yscale: yscale, + xExcess: _gasExcess, + xPurchase: gasLimit + }); + + // Very important to cap basefee uint64 + _basefee = uint64(__basefee.min(type(uint64).max)); + + // Very important to cap _gasExcess uint64 + _gasExcess = uint64( + (uint256(_gasExcess) + gasLimit).min(type(uint64).max) + ); } } diff --git a/packages/protocol/contracts/libs/Lib1559Math.sol b/packages/protocol/contracts/libs/Lib1559Math.sol index ac7a6aee07c..7d3a3b26215 100644 --- a/packages/protocol/contracts/libs/Lib1559Math.sol +++ b/packages/protocol/contracts/libs/Lib1559Math.sol @@ -24,7 +24,6 @@ library Lib1559Math { uint64 ratio2x1x ) internal pure returns (uint128 xscale, uint128 yscale) { assert(xExcessMax != 0); - uint64 x = xExcessMax / 2; // calculate xscale diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index 93f7f39da5c..270d8ff083d 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -1,3 +1,6 @@ +# See more config options https://github.com/foundry-rs/foundry/tree/master/config + + [profile.default] src = 'contracts' out = 'out' @@ -5,4 +8,6 @@ test = 'test2' libs = ['lib'] optimizer = true optimizer_runs = 200 -# See more config options https://github.com/foundry-rs/foundry/tree/master/config + +# Do not change the block_gas_limit value, TaikoL2.t.sol depends on it. +block_gas_limit = 30000000 #30M diff --git a/packages/protocol/test2/LibFixedPointMath.t.sol b/packages/protocol/test2/LibFixedPointMath.t.sol index 67934f5167b..8846efaeaf7 100644 --- a/packages/protocol/test2/LibFixedPointMath.t.sol +++ b/packages/protocol/test2/LibFixedPointMath.t.sol @@ -37,13 +37,13 @@ contract LibFixedPointMathTest is Test { } function testExpGas() public view { - uint g0 = gasleft(); + uint256 g0 = gasleft(); LibFixedPointMath.exp(133e18); - uint g1 = gasleft(); + uint256 g1 = gasleft(); LibFixedPointMath.exp(-23e18); - uint g2 = gasleft(); + uint256 g2 = gasleft(); LibFixedPointMath.exp(5e18); - uint g3 = gasleft(); + uint256 g3 = gasleft(); console2.logUint(g0 - g1); console2.logUint(g1 - g2); console2.logUint(g2 - g3); diff --git a/packages/protocol/test2/TaikoL2.t.sol b/packages/protocol/test2/TaikoL2.t.sol index 8afdac47965..1f4f84d632f 100644 --- a/packages/protocol/test2/TaikoL2.t.sol +++ b/packages/protocol/test2/TaikoL2.t.sol @@ -1,30 +1,107 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "forge-std/Test.sol"; -import "forge-std/console2.sol"; -import "../contracts/L2/TaikoL2.sol"; +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {TaikoL2} from "../contracts/L2/TaikoL2.sol"; +import { + SafeCastUpgradeable +} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; contract TestTaikoL2 is Test { + using SafeCastUpgradeable for uint256; + uint32 public constant BLOCK_GAS_LIMIT = 30000000; // same as `block_gas_limit` in foundry.toml + TaikoL2 public L2; + uint private logIndex; function setUp() public { - TaikoL2.EIP1559Params memory param1559; + uint16 rand = 2; + TaikoL2.EIP1559Params memory param1559 = TaikoL2.EIP1559Params({ + basefee: (uint(BLOCK_GAS_LIMIT * 10) * rand).toUint64(), + gasIssuedPerSecond: 1000000, + gasExcessMax: (uint(15000000) * 256 * rand).toUint64(), + gasTarget: (uint(6000000) * rand).toUint64(), + ratio2x1x: 111 + }); + L2 = new TaikoL2(); L2.init(address(1), param1559); // Dummy address manager address. + vm.roll(block.number + 1); + vm.warp(block.timestamp + 30); + + // console2.log("basefee =", uint256(L2.basefee())); + // console2.log("xscale =", uint256(L2.xscale())); + // console2.log("yscale =", uint256(L2.yscale())); + // console2.log("gasExcess =", uint256(L2.gasExcess())); } - function testAnchorTxs() external { - for (uint256 i = 0; i < 1000; i++) { + function testAnchorTxsBlocktimeConstant() external { + uint64 firstBasefee; + for (uint256 i = 0; i < 100; i++) { + uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + vm.fee(basefee); + + if (firstBasefee == 0) { + firstBasefee = basefee; + } else { + assertEq(firstBasefee, basefee); + } + vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); L2.anchor(12345, keccak256("a"), keccak256("b")); + vm.roll(block.number + 1); + vm.warp(block.timestamp + 30); + } + } + + function testAnchorTxsBlocktimeDecreasing() external { + uint64 prevBasefee; + + for (uint256 i = 0; i < 32; i++) { + uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + vm.fee(basefee); + + assertGe(basefee, prevBasefee); + prevBasefee = basefee; + + vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); + L2.anchor(12345, keccak256("a"), keccak256("b")); + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 30 - i); + } + } + + function testAnchorTxsBlocktimeIncreasing() external { + uint64 prevBasefee; + + for (uint256 i = 0; i < 30; i++) { + uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + vm.fee(basefee); + + if (prevBasefee != 0) { + assertLe(basefee, prevBasefee); + } + prevBasefee = basefee; + + vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); + L2.anchor(12345, keccak256("a"), keccak256("b")); + + vm.roll(block.number + 1); + + vm.warp(block.timestamp + 30 + i); } } // calling anchor in the same block more than once should fail function testAnchorTxsFailInTheSameBlock() external { + uint64 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + vm.fee(expectedBasefee); + vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); L2.anchor(12345, keccak256("a"), keccak256("b")); @@ -35,6 +112,8 @@ contract TestTaikoL2 is Test { // calling anchor in the same block more than once should fail function testAnchorTxsFailByNonTaikoL2Signer() external { + uint64 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + vm.fee(expectedBasefee); vm.expectRevert(); L2.anchor(12345, keccak256("a"), keccak256("b")); } @@ -54,4 +133,53 @@ contract TestTaikoL2 is Test { vm.expectRevert(); L2.signAnchor(digest, uint8(3)); } + + function testGetBasefee1() external { + assertEq(_getBasefeeAndPrint(0, 0), 317609019); + assertEq(_getBasefeeAndPrint(0, 1), 317609019); + assertEq(_getBasefeeAndPrint(0, 1000000), 320423332); + assertEq(_getBasefeeAndPrint(0, 5000000), 332018053); + assertEq(_getBasefeeAndPrint(0, 10000000), 347305199); + } + + function testGetBasefee2() external { + assertEq(_getBasefeeAndPrint(100, 0), 54544902); + assertEq(_getBasefeeAndPrint(100, 1), 54544902); + assertEq(_getBasefeeAndPrint(100, 1000000), 55028221); + assertEq(_getBasefeeAndPrint(100, 5000000), 57019452); + assertEq(_getBasefeeAndPrint(100, 10000000), 59644805); + } + + function _getBasefeeAndPrint( + uint32 timeSinceNow, + uint64 gasLimit + ) private returns (uint64 _basefee) { + uint256 timeSinceParent = timeSinceNow + + block.timestamp - + L2.parentTimestamp(); + uint256 gasIssued = L2.gasIssuedPerSecond() * timeSinceParent; + string memory msg = string.concat( + "#", + Strings.toString(logIndex++), + ": gasExcess=", + Strings.toString(L2.gasExcess()), + ", timeSinceParent=", + Strings.toString(timeSinceParent), + ", gasIssued=", + Strings.toString(gasIssued), + ", gasLimit=", + Strings.toString(gasLimit) + ); + _basefee = L2.getBasefee(timeSinceNow, gasLimit); + + msg = string.concat( + msg, + ", gasExcess(changed)=", + Strings.toString(L2.gasExcess()), + ", basefee=", + Strings.toString(_basefee) + ); + + console2.log(msg); + } } diff --git a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md index ab7a8c9311b..b099622d53b 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md +++ b/packages/website/pages/docs/reference/contract-documentation/L2/TaikoL2.md @@ -61,16 +61,16 @@ uint64 parentTimestamp uint64 latestSyncedL1Height ``` -### basefee +### gasExcess ```solidity -uint64 basefee +uint64 gasExcess ``` -### gasExcess +### \_\_reserved1 ```solidity -uint64 gasExcess +uint64 __reserved1 ``` ### BlockVars @@ -79,16 +79,16 @@ uint64 gasExcess event BlockVars(uint64 number, uint64 basefee, uint64 gaslimit, uint64 timestamp, bytes32 parentHash, uint256 prevrandao, address coinbase, uint32 chainid) ``` -### L2_INVALID_1559_PARAMS +### L2_BASEFEE_MISMATCH ```solidity -error L2_INVALID_1559_PARAMS() +error L2_BASEFEE_MISMATCH(uint64 expected, uint64 actual) ``` -### L2_INVALID_BASEFEE +### L2_INVALID_1559_PARAMS ```solidity -error L2_INVALID_BASEFEE() +error L2_INVALID_1559_PARAMS() ``` ### L2_INVALID_CHAIN_ID @@ -106,7 +106,7 @@ error L2_INVALID_SENDER() ### L2_PUBLIC_INPUT_HASH_MISMATCH ```solidity -error L2_PUBLIC_INPUT_HASH_MISMATCH() +error L2_PUBLIC_INPUT_HASH_MISMATCH(bytes32 expected, bytes32 actual) ``` ### L2_TOO_LATE @@ -160,6 +160,12 @@ This transaction shall be the first transaction in every L2 block. | l1Hash | bytes32 | The latest L1 block hash when this block was proposed. | | l1SignalRoot | bytes32 | The latest value of the L1 "signal service storage root". | +### getBasefee + +```solidity +function getBasefee(uint32 timeSinceNow, uint64 gasLimit) public view returns (uint64 _basefee) +``` + ### getXchainBlockHash ```solidity From dd627c48d232d81a63d227c966e4797f50801653 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 4 Apr 2023 14:58:08 +0200 Subject: [PATCH 78/90] Remove unused libs --- .../protocol/contracts/libs/LibRealMath.sol | 13 ---- .../contracts/libs/LibTrieProof_explained.bak | 65 ------------------- 2 files changed, 78 deletions(-) delete mode 100644 packages/protocol/contracts/libs/LibRealMath.sol delete mode 100644 packages/protocol/contracts/libs/LibTrieProof_explained.bak diff --git a/packages/protocol/contracts/libs/LibRealMath.sol b/packages/protocol/contracts/libs/LibRealMath.sol deleted file mode 100644 index 1c6678b7275..00000000000 --- a/packages/protocol/contracts/libs/LibRealMath.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -// _____ _ _ _ _ -// |_ _|_ _(_) |_____ | | __ _| |__ ___ -// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< -// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ - -pragma solidity ^0.8.18; - -library LibRealMath { - function exp(uint128 b) internal pure returns (uint256) { - // https://github.com/NovakDistributed/macroverse/blob/master/contracts/RealMath.sol - } -} diff --git a/packages/protocol/contracts/libs/LibTrieProof_explained.bak b/packages/protocol/contracts/libs/LibTrieProof_explained.bak deleted file mode 100644 index 8f046cde2ae..00000000000 --- a/packages/protocol/contracts/libs/LibTrieProof_explained.bak +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Verifies that the value of a slot in the storage tree of `addr` - * is `value`. - * - * @param stateRoot The merkle root of state tree. - * @param addr The contract address. - * @param slot The slot in the contract. - * @param value The value to be verified. - * @param mkproof The proof obtained by encoding state proof and storage - * proof. - * @return verified The verification result. - */ -function verify( - bytes32 stateRoot, - address addr, - bytes32 slot, - bytes32 value, - bytes calldata mkproof -) public pure returns (bool verified) { - // Decode the `mkproof` parameter into separate `accountProof` and `storageProof` byte arrays. - (bytes memory accountProof, bytes memory storageProof) = abi.decode( - mkproof, - (bytes, bytes) - ); - - // Retrieve the RLP-encoded account state for the specified address using the `get` function from the `LibSecureMerkleTrie` library. - //Megnézi hogy a 'world' stateRoot-ból ki tudja-e szedni az RLP-encoded account-t. - (bool exists, bytes memory rlpAccount) = LibSecureMerkleTrie.get( - abi.encodePacked(addr), - accountProof, - stateRoot - ); - - - // Example contract accountProof: - // { - // nonce: 1, - // balance: 1000, - // codeHash: 0x123456789abcdef, - // storageHash: 0x987654321fedcba - // } - // Example of a contracts storageProof: - // [ "0x", [ { "key": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "value": { "nonce": "0x1", "balance": "0x0", "storageHash": "0xc0a49e08e1c8f3b3de04ce4fa4eeb8071594da124f92da6da4fa5b5ce5ddc8cc", "codeHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } } ], - // [ { "key": "0x1234", "value": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" } ] - // ] - - // Check that the account exists in the state tree. - require(exists, "LTP:invalid account proof"); - - // Parse the RLP-encoded account state to extract the storage root hash. - LibRLPReader.RLPItem[] memory accountState = LibRLPReader.readList( - rlpAccount - ); - bytes32 storageRoot = LibRLPReader.readBytes32( - accountState[ACCOUNT_FIELD_INDEX_STORAGE_HASH] - ); - - // Verify the inclusion of the specified slot and value in the storage tree using the `verifyInclusionProof` function from the `LibSecureMerkleTrie` library. - verified = LibSecureMerkleTrie.verifyInclusionProof( - abi.encodePacked(slot), - LibRLPWriter.writeBytes32(value), - storageProof, - storageRoot - ); -} From a7ec002761263f5cb9a49a47a3052bc53da2e67f Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 4 Apr 2023 14:59:50 +0200 Subject: [PATCH 79/90] Drop typescript deployment task --- packages/protocol/tasks/deploy_L1.ts | 363 --------------------------- 1 file changed, 363 deletions(-) delete mode 100644 packages/protocol/tasks/deploy_L1.ts diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts deleted file mode 100644 index 45ed452b1ad..00000000000 --- a/packages/protocol/tasks/deploy_L1.ts +++ /dev/null @@ -1,363 +0,0 @@ -import * as ethers from "ethers"; -import { task } from "hardhat/config"; -import * as types from "hardhat/internal/core/params/argumentTypes"; -import * as config from "./config"; -import * as log from "./log"; -import * as utils from "./utils"; - -task("deploy_L1") - .addParam("daoVault", "The DAO vault address") - .addParam("teamVault", "The team vault address") - .addOptionalParam( - "taikoL2", - "The TaikoL2 address", - ethers.constants.AddressZero - ) - .addOptionalParam( - "l2GenesisBlockHash", - "L2 genesis block hash", - ethers.constants.HashZero - ) - .addOptionalParam("l2ChainId", "L2 chain id", config.K_CHAIN_ID, types.int) - .addOptionalParam( - "bridgeFunderPrivateKey", - "Private key of the L1 bridge funder", - "", - types.string - ) - .addOptionalParam( - "bridgeFund", - "L1 bridge's initial fund in hex", - "", - types.string - ) - .addOptionalParam( - "oracleProver", - "Address of the oracle prover", - "", - types.string - ) - .addOptionalParam( - "soloProposer", - "Address of the solo proposer", - "", - types.string - ) - .addOptionalParam( - "confirmations", - "Number of confirmations to wait for deploy transaction.", - config.K_DEPLOY_CONFIRMATIONS, - types.int - ) - .setAction(async (args, hre: any) => { - if ( - hre.network.name === "localhost" || - hre.network.name === "hardhat" - ) { - args.confirmations = 1; - } else if ( - hre.network.name === "ropsten" || - hre.network.name === "goerli" - ) { - args.confirmations = 6; - } - - hre.args = args; - await deployContracts(hre); - }); - -export async function deployContracts(hre: any) { - const network = hre.network.name; - const { chainId } = await hre.ethers.provider.getNetwork(); - const deployer = await utils.getDeployer(hre); - const daoVault = hre.args.daoVault; - const teamVault = hre.args.teamVault; - const l2GenesisBlockHash = hre.args.l2GenesisBlockHash; - const taikoL2Address = hre.args.taikoL2; - const l2ChainId = hre.args.l2ChainId; - const bridgeFunderPrivateKey = hre.args.bridgeFunderPrivateKey; - const bridgeFund = hre.args.bridgeFund; - const oracleProver = hre.args.oracleProver; - const soloProposer = hre.args.soloProposer; - - log.debug(`network: ${network}`); - log.debug(`chainId: ${chainId}`); - log.debug(`deployer: ${deployer}`); - log.debug(`daoVault: ${daoVault}`); - log.debug(`l2GenesisBlockHash: ${l2GenesisBlockHash}`); - log.debug(`taikoL2Address: ${taikoL2Address}`); - log.debug(`l2ChainId: ${l2ChainId}`); - log.debug(`bridgeFunderPrivateKey: ${bridgeFunderPrivateKey}`); - log.debug(`bridgeFund: ${bridgeFund}`); - log.debug(`oracleProver: ${oracleProver}`); - log.debug(`soloProposer: ${soloProposer}`); - log.debug(`confirmations: ${hre.args.confirmations}`); - log.debug(); - - // AddressManager - const AddressManager = await utils.deployContract(hre, "AddressManager"); - await utils.waitTx(hre, await AddressManager.init()); - - await utils.waitTx( - hre, - await AddressManager.setAddress(`${chainId}.dao_vault`, daoVault) - ); - await utils.waitTx( - hre, - await AddressManager.setAddress(`${chainId}.team_vault`, teamVault) - ); - // Used by LibProving - await utils.waitTx( - hre, - await AddressManager.setAddress(`${l2ChainId}.taiko`, taikoL2Address) - ); - - // TaikoToken - const TaikoToken = await utils.deployContract(hre, "TaikoToken"); - await utils.waitTx( - hre, - await TaikoToken.init( - AddressManager.address, - "Test Taiko Token", - "TTKO", - [], - [] - ) - ); - await utils.waitTx( - hre, - await AddressManager.setAddress( - `${chainId}.taiko_token`, - TaikoToken.address - ) - ); - - // HorseToken - const HorseToken = await utils.deployContract(hre, "FreeMintERC20", {}, [ - "Horse Token", - "HORSE", - ]); - - // BullToken - const BullToken = await utils.deployContract( - hre, - "MayFailFreeMintERC20", - {}, - ["Bull Token", "BLL"] - ); - - // TaikoL1 - const TaikoL1 = await utils.deployContract( - hre, - "TaikoL1" - // await deployBaseLibs(hre) - ); - - const feeBase = hre.ethers.BigNumber.from(10).pow(18); - const gasAccumulated = hre.ethers.BigNumber.from(100000000); - - await utils.waitTx( - hre, - await TaikoL1.init( - AddressManager.address, - l2GenesisBlockHash, - feeBase, - gasAccumulated - ) - ); - - // Used by LibBridgeRead - await utils.waitTx( - hre, - await AddressManager.setAddress(`${chainId}.taiko`, TaikoL1.address) - ); - - // Used by TaikoToken - await utils.waitTx( - hre, - await AddressManager.setAddress( - `${chainId}.proto_broker`, - TaikoL1.address - ) - ); - - // Bridge - const Bridge = await deployBridge(hre, AddressManager.address); - - // TokenVault - const TokenVault = await deployTokenVault(hre, AddressManager.address); - - // Used by TokenVault - await utils.waitTx( - hre, - await AddressManager.setAddress(`${chainId}.bridge`, Bridge.address) - ); - - // Fund L1 bridge, which is necessary when there is a L2 faucet - if ( - bridgeFunderPrivateKey.length && - hre.ethers.utils.isHexString(bridgeFund) - ) { - const funder = new hre.ethers.Wallet( - bridgeFunderPrivateKey, - hre.ethers.provider - ); - - await utils.waitTx( - hre, - await funder.sendTransaction({ - to: Bridge.address, - value: hre.ethers.BigNumber.from(bridgeFund), - }) - ); - - log.debug( - `L1 bridge balance: ${hre.ethers.utils.hexlify( - await hre.ethers.provider.getBalance(Bridge.address) - )}` - ); - } - - // SignalService - const SignalService = await deploySignalService( - hre, - AddressManager.address - ); - - // Used by Bridge - await utils.waitTx( - hre, - await AddressManager.setAddress( - `${chainId}.signal_service`, - SignalService.address - ) - ); - - // PlonkVerifier - const PlonkVerifiers = await deployPlonkVerifiers(hre); - - // Used by ProofVerifier - for (let i = 0; i < PlonkVerifiers.length; i++) { - await utils.waitTx( - hre, - await AddressManager.setAddress( - // string(abi.encodePacked("plonk_verifier_", i)) - `${chainId}.${Buffer.from( - ethers.utils.arrayify( - ethers.utils.solidityPack( - ["string", "uint16"], - ["verifier_", i] - ) - ) - ).toString()}`, - PlonkVerifiers[i].address - ) - ); - } - - if (ethers.utils.isAddress(oracleProver)) { - await utils.waitTx( - hre, - await AddressManager.setAddress( - `${chainId}.oracle_prover`, - oracleProver - ) - ); - } - - if (ethers.utils.isAddress(soloProposer)) { - await utils.waitTx( - hre, - await AddressManager.setAddress( - `${chainId}.solo_proposer`, - soloProposer - ) - ); - } - - // save deployments - const deployments = { - network, - chainId, - deployer, - l2GenesisBlockHash, - contracts: Object.assign( - { AddressManager: AddressManager.address }, - { TaikoToken: TaikoToken.address }, - { TaikoL1: TaikoL1.address }, - { Bridge: Bridge.address }, - { SignalService: SignalService.address }, - { TokenVault: TokenVault.address }, - { BullToken: BullToken.address }, - { HorseToken: HorseToken.address } - ), - }; - - utils.saveDeployments(`${network}_L1`, deployments); - - return deployments; -} - -async function deployBridge(hre: any, addressManager: string): Promise { - const libTrieProof = await utils.deployContract(hre, "LibTrieProof"); - - const Bridge = await utils.deployContract(hre, "Bridge", { - LibTrieProof: libTrieProof.address, - }); - - await utils.waitTx(hre, await Bridge.init(addressManager)); - - return Bridge; -} - -async function deployTokenVault( - hre: any, - addressManager: string -): Promise { - const TokenVault = await utils.deployContract(hre, "TokenVault"); - - await utils.waitTx(hre, await TokenVault.init(addressManager)); - - return TokenVault; -} - -async function deploySignalService( - hre: any, - addressManager: string -): Promise { - const libTrieProof = await utils.deployContract(hre, "LibTrieProof"); - - const SignalService = await utils.deployContract(hre, "SignalService", { - LibTrieProof: libTrieProof.address, - }); - - await utils.waitTx(hre, await SignalService.init(addressManager)); - - return SignalService; -} - -async function deployPlonkVerifiers(hre: any): Promise { - const PlonkVerifier10TxsByteCode = utils.compileYulContract( - "../contracts/libs/yul/PlonkVerifier_10_txs.yulp" - ); - const PlonkVerifier80TxsByteCode = utils.compileYulContract( - "../contracts/libs/yul/PlonkVerifier_80_txs.yulp" - ); - - return [ - { - address: await utils.deployBytecode( - hre, - PlonkVerifier10TxsByteCode, - "PlonkVerifier_10_txs" - ), - }, - { - address: await utils.deployBytecode( - hre, - PlonkVerifier80TxsByteCode, - "PlonkVerifier_80_txs" - ), - }, - ]; -} From e293932eb1024cb42f301cf1732c609963a5d66b Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 4 Apr 2023 15:07:16 +0200 Subject: [PATCH 80/90] Disable one (inentionally) failing tesst and remove unused gasAccumulated --- packages/protocol/contracts/L1/TaikoData.sol | 2 +- packages/protocol/test2/LibTokenomics.t.sol | 3 ++- .../docs/reference/contract-documentation/L1/TaikoData.md | 5 ----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index f27b7c09f84..ccc09f61849 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -149,7 +149,7 @@ library TaikoData { uint64 numBlocks; uint64 lastProposedAt; // Timestamp when the last block is proposed. uint64 avgBlockTime; // miliseconds - uint64 gasAccumulated; + uint64 __reserved3; // Changed when a block is proven/finalized uint64 lastVerifiedBlockId; // the proof time moving average, note that for each block, only the diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index 8daca7fefca..994c065a746 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -306,7 +306,8 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { } /// @dev Test what happens when proof time increasing then stabilizes below the target time - function test_reward_and_fee_if_proof_time_increasing_then_stabilizes_below_the_proof_time_target() + /// @notice This test is failing - and disabled, but it is meant to demonstrate the behaviour + function xtest_reward_and_fee_if_proof_time_increasing_then_stabilizes_below_the_proof_time_target() external { mine(1); diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 975e7088716..15461d88607 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -23,7 +23,6 @@ struct Config { uint256 maxNumVerifiedBlocks; uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint64 gasAccumulatedPerSecond; uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; @@ -167,11 +166,7 @@ struct State { uint64 numBlocks; uint64 lastProposedAt; uint64 avgBlockTime; -<<<<<<< HEAD - uint64 gasAccumulated; -======= uint64 __reserved3; ->>>>>>> origin/L2_1559_rebase_in_anchor uint64 lastVerifiedBlockId; uint64 __reserved4; uint64 avgProofTime; From a05fb76f7049b7c7d3b96b8982d9d2e1aac184a6 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 4 Apr 2023 19:28:15 +0200 Subject: [PATCH 81/90] Deduct 1 from lastVerifiedBlock --- packages/protocol/contracts/L1/libs/LibTokenomics.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol index 322209dcd90..93071e89452 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -121,7 +121,8 @@ library LibTokenomics { } else { /// TODO: Verify with functional tests uint256 numBlocksBeingProven = state.numBlocks - - state.lastVerifiedBlockId; + state.lastVerifiedBlockId - + 1; if (config.useTimeWeightedReward) { /// TODO: Theroetically there can be no underflow (in case numBlocksBeingProven == 0 then /// state.accProposalTime is also 0) - but verify with unit tests ! From cffbf2196f2afe77c41367097fbe3bbedac0ba54 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:58:47 +0800 Subject: [PATCH 82/90] refactor(protocol): recommend changes to Dani's PR (part1) (#13540) Co-authored-by: Dani <51912515+adaki2004@users.noreply.github.com> --- packages/protocol/.solhintignore | 3 +- .../protocol/contracts/L1/TaikoConfig.sol | 16 +- packages/protocol/contracts/L1/TaikoData.sol | 62 +- .../protocol/contracts/L1/TaikoErrors.sol | 5 - packages/protocol/contracts/L1/TaikoL1.sol | 30 +- .../contracts/L1/libs/LibProposing.sol | 22 +- .../protocol/contracts/L1/libs/LibProving.sol | 10 +- .../contracts/L1/libs/LibTokenomics.sol | 124 ++- .../protocol/contracts/L1/libs/LibUtils.sol | 22 +- .../contracts/L1/libs/LibVerifying.sol | 63 +- .../protocol/contracts/L2/LibL2Consts.sol | 12 + .../contracts/test/L1/TestTaikoL1.sol | 9 - .../test/L1/TestTaikoL1EnableTokenomics.sol | 9 - .../thirdparty/LibFixedPointMath.sol | 1 + packages/protocol/foundry.toml | 4 +- packages/protocol/script/DeployOnL1.s.sol | 4 +- .../protocol/test/tokenomics/blockFee.test.ts | 2 +- packages/protocol/test/utils/fixture.ts | 4 +- packages/protocol/test/utils/taikoL1.ts | 8 +- packages/protocol/test2/GasComparison.t.sol | 4 - packages/protocol/test2/LibTokenomics.t.sol | 722 +++++++++++------- .../test2/LibTokenomicsMainnetMock.t.sol | 291 +++++++ packages/protocol/test2/TaikoL1.t.sol | 7 - packages/protocol/test2/TaikoL1TestBase.t.sol | 44 +- packages/protocol/test2/TaikoL2.t.sol | 8 +- packages/protocol/test2/TestLn.sol | 207 +++++ .../protocol/utils/generate_config/main.py | 1 - packages/relayer/TaikoL1.json | 13 +- packages/relayer/contracts/taikol1/TaikoL1.go | 40 +- packages/relayer/contracts/taikol2/TaikoL2.go | 6 +- .../status-page/src/constants/abi/TaikoL1.ts | 8 +- .../status-page/src/pages/home/Home.svelte | 2 +- packages/tokenomics/main.py | 6 +- .../contract-documentation/L1/TaikoData.md | 46 +- .../contract-documentation/L1/TaikoErrors.md | 30 - .../contract-documentation/L1/TaikoL1.md | 15 +- .../contract-documentation/L2/LibL2Consts.md | 11 + 37 files changed, 1225 insertions(+), 646 deletions(-) create mode 100644 packages/protocol/contracts/L2/LibL2Consts.sol create mode 100644 packages/protocol/test2/LibTokenomicsMainnetMock.t.sol create mode 100644 packages/protocol/test2/TestLn.sol create mode 100644 packages/website/pages/docs/reference/contract-documentation/L2/LibL2Consts.md diff --git a/packages/protocol/.solhintignore b/packages/protocol/.solhintignore index 35e56e7003c..7b6a5611295 100644 --- a/packages/protocol/.solhintignore +++ b/packages/protocol/.solhintignore @@ -3,4 +3,5 @@ lib/ contracts/test/TestLibRLPReader.sol contracts/test/TestLibRLPWriter.sol **/contracts/thirdparty/**/*.sol -test2/GasComparison.t.sol \ No newline at end of file +test2/GasComparison.t.sol +test2/TestLn.sol \ No newline at end of file diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index d6f6bd9a7d1..707c866b957 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -23,8 +23,6 @@ library TaikoConfig { // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - gasTargetPerSecond: 30000000, - gasAdjustmentFactor: (30000000 * 200) ** 2 * 5000000000, // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, @@ -33,24 +31,14 @@ library TaikoConfig { // transactions list calldata, 8K for the remaining tx fields. maxBytesPerTxList: 120000, minTxGasLimit: 21000, - slotSmoothingFactor: 946649, - // 100 basis points or 1% - rewardBurnBips: 100, // Moving average factors - feeBaseMAF: 1024, txListCacheExpiry: 0, - proofTimeTarget: 85, // 85s based on A2 testnet status + proofTimeTarget: 1800, // 85s based on A2 testnet status, or set to 1800 for 30mins (mainnet mock) adjustmentQuotient: 16, enableSoloProposer: false, enableOracleProver: true, enableTokenomics: true, - skipZKPVerification: false, - allowMinting: true, - useTimeWeightedReward: false, - provingConfig: TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: 2500 // [75% -> 125%] - }) + skipZKPVerification: false }); } } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index ccc09f61849..ae6cbd309dd 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -7,11 +7,6 @@ pragma solidity ^0.8.18; library TaikoData { - struct FeeConfig { - uint16 avgTimeMAF; - uint16 dampingFactorBips; - } - struct Config { uint256 chainId; uint256 maxNumProposedBlocks; @@ -21,15 +16,10 @@ library TaikoData { // the 'the maximum value of the multiplier' close to 20.0 uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint64 gasTargetPerSecond; - uint256 gasAdjustmentFactor; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; - uint256 slotSmoothingFactor; - uint256 rewardBurnBips; // Moving average factors - uint256 feeBaseMAF; uint256 txListCacheExpiry; uint64 proofTimeTarget; uint8 adjustmentQuotient; @@ -37,20 +27,15 @@ library TaikoData { bool enableOracleProver; bool enableTokenomics; bool skipZKPVerification; - bool allowMinting; - bool useTimeWeightedReward; - FeeConfig provingConfig; } struct StateVariables { - uint256 baseFeeProof; - uint64 feeBase; + uint64 basefee; + uint64 rewardPool; uint64 genesisHeight; uint64 genesisTimestamp; uint64 numBlocks; uint64 lastVerifiedBlockId; - uint64 avgBlockTime; - uint64 avgProofTime; uint64 lastProposedAt; } @@ -70,7 +55,6 @@ library TaikoData { uint64 id; uint64 timestamp; uint64 l1Height; - uint64 l2Basefee; bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; @@ -78,7 +62,6 @@ library TaikoData { uint24 txListByteEnd; uint32 gasLimit; address beneficiary; - address treasure; } struct ZKProof { @@ -94,6 +77,7 @@ library TaikoData { bytes32 signalRoot; bytes32 graffiti; address prover; + uint32 gasUsed; } // 3 slots @@ -101,6 +85,7 @@ library TaikoData { bytes32 blockHash; bytes32 signalRoot; uint64 provenAt; + uint32 gasUsed; address prover; } @@ -111,7 +96,6 @@ library TaikoData { uint64 blockId; uint64 proposedAt; uint64 deposit; - uint32 gasConsumed; uint24 nextForkChoiceId; uint24 verifiedForkChoiceId; bytes32 metaHash; @@ -130,32 +114,30 @@ library TaikoData { // A mapping from (blockId, parentHash) to a reusable ForkChoice storage pointer. // solhint-disable-next-line max-line-length mapping(uint256 blockId => mapping(bytes32 parentHash => uint256 forkChoiceId)) forkChoiceIds; + // TODO(dani): change to: + // mapping(address account => uint64 balance) balances; mapping(address account => uint256 balance) balances; mapping(bytes32 txListHash => TxListInfo) txListInfo; - // Cummulated proofTime for reward calculation - changed in verifyBlock() - uint256 proofTimeIssued; - // Changing baseFee for proving - changed in verifyBlock() - uint256 baseFeeProof; - // Changing accumulated time for proposing - changed in proposeBlock() and in verifyBlock() - uint256 accProposalTime; - // Treasury amount - changed in proposeBlock() and in verifyBlock() - uint256 proofFeeTreasury; - // Never or rarely changed + // Slot 5: never or rarely changed uint64 genesisHeight; uint64 genesisTimestamp; - uint64 __reserved1; - // Changed when a block is proposed or proven/finalized - // Changed when a block is proposed + uint64 __reserved51; + uint64 __reserved52; + // Slot 6: changed by proposeBlock + uint64 lastProposedAt; uint64 numBlocks; - uint64 lastProposedAt; // Timestamp when the last block is proposed. - uint64 avgBlockTime; // miliseconds - uint64 __reserved3; - // Changed when a block is proven/finalized + uint64 accProposedAt; // also by verifyBlocks + uint64 rewardPool; // also by verifyBlocks + // Slot 7: changed by proveBlock + // uint64 __reserved71; + // uint64 __reserved72; + // uint64 __reserved73; + // uint64 __reserved74; + // Slot 8: changed by verifyBlocks + uint64 basefee; + uint64 proofTimeIssued; uint64 lastVerifiedBlockId; - // the proof time moving average, note that for each block, only the - // first proof's time is considered. - uint64 avgProofTime; // miliseconds - uint64 feeBase; + uint64 __reserved81; // Reserved uint256[43] __gap; } diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index c437f5ea208..4d0a74135b6 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -8,24 +8,19 @@ pragma solidity ^0.8.18; abstract contract TaikoErrors { // The following custom errors must match the definitions in other V1 libraries. - error L1_1559_X_SCALE_TOO_LARGE(); - error L1_1559_Y_SCALE_TOO_LARGE(); error L1_ALREADY_PROVEN(); error L1_BLOCK_ID(); error L1_CONTRACT_NOT_ALLOWED(); error L1_EVIDENCE_MISMATCH(); error L1_FORK_CHOICE_NOT_FOUND(); - error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_CONFIG(); error L1_INVALID_EVIDENCE(); - error L1_INVALID_L21559_PARAMS(); error L1_INVALID_METADATA(); error L1_INVALID_PARAM(); error L1_INVALID_PROOF(); error L1_NOT_ORACLE_PROVER(); error L1_NOT_SOLO_PROPOSER(); - error L1_OUT_OF_BLOCK_SPACE(); error L1_TOO_MANY_BLOCKS(); error L1_TX_LIST_NOT_EXIST(); error L1_TX_LIST_HASH(); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 7c55eeee47d..9ce277ef5c5 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -29,20 +29,22 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { * Initialize the rollup. * * @param _addressManager The AddressManager address. - * @param _feeBase The initial value of the proposer-fee/prover-reward feeBase. * @param _genesisBlockHash The block hash of the genesis block. + * @param _initBasefee Initial (reasonable) basefee value. */ function init( address _addressManager, - uint64 _feeBase, - bytes32 _genesisBlockHash + bytes32 _genesisBlockHash, + uint64 _initBasefee, + uint64 _initProofTimeIssued ) external initializer { EssentialContract._init(_addressManager); LibVerifying.init({ state: state, config: getConfig(), - feeBase: _feeBase, - genesisBlockHash: _genesisBlockHash + genesisBlockHash: _genesisBlockHash, + initBasefee: _initBasefee, + initProofTimeIssued: _initProofTimeIssued }); } @@ -133,23 +135,19 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { return state.balances[addr]; } - function getProverFee( - uint32 gasUsed - ) public view returns (uint256 feeAmount) { - (, feeAmount) = LibTokenomics.getProverFee(state, gasUsed); + function getProverFee() public view returns (uint64 fee) { + fee = LibTokenomics.getProverFee(state); } function getProofReward( uint64 provenAt, - uint64 proposedAt, - uint32 usedGas - ) public view returns (uint256 reward) { - (, reward) = LibTokenomics.getProofReward({ + uint64 proposedAt + ) public view returns (uint64 reward) { + reward = LibTokenomics.getProofReward({ state: state, config: getConfig(), provenAt: provenAt, - proposedAt: proposedAt, - usedGas: usedGas + proposedAt: proposedAt }); } @@ -223,7 +221,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { view returns (TaikoData.StateVariables memory) { - return state.getStateVariables(getConfig()); + return state.getStateVariables(); } function getConfig() public pure virtual returns (TaikoData.Config memory) { diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 19ab4d9d6cb..f8b634f6cc8 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibAddress} from "../../libs/LibAddress.sol"; +import {LibL2Consts} from "../../L2/LibL2Consts.sol"; import {LibTokenomics} from "./LibTokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { @@ -28,7 +29,6 @@ library LibProposing { ); error L1_BLOCK_ID(); - error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_METADATA(); error L1_NOT_SOLO_PROPOSER(); @@ -70,15 +70,13 @@ library LibProposing { id: state.numBlocks, timestamp: uint64(block.timestamp), l1Height: uint64(block.number - 1), - l2Basefee: 0, // will be set later l1Hash: blockhash(block.number - 1), mixHash: bytes32(block.prevrandao * state.numBlocks), txListHash: input.txListHash, txListByteStart: input.txListByteStart, txListByteEnd: input.txListByteEnd, gasLimit: input.gasLimit, - beneficiary: input.beneficiary, - treasure: resolver.resolve(config.chainId, "treasure", false) + beneficiary: input.beneficiary }); } @@ -93,23 +91,17 @@ library LibProposing { blk.verifiedForkChoiceId = 0; blk.metaHash = LibUtils.hashMetadata(meta); blk.proposer = msg.sender; - // Later on we might need to have actual gas consumed in that L2 block - blk.gasConsumed = input.gasLimit; - if (config.enableTokenomics) { - (, uint256 fee) = LibTokenomics.getProverFee(state, input.gasLimit); + if (config.proofTimeTarget != 0) { + uint64 fee = LibTokenomics.getProverFee(state); if (state.balances[msg.sender] < fee) revert L1_INSUFFICIENT_TOKEN(); unchecked { state.balances[msg.sender] -= fee; - if (!config.allowMinting) { - state.proofFeeTreasury += fee; - if (config.useTimeWeightedReward) { - state.accProposalTime += meta.timestamp; - } - } + state.rewardPool += fee; + state.accProposedAt += meta.timestamp; } } @@ -146,7 +138,7 @@ library LibProposing { if ( input.beneficiary == address(0) || - input.gasLimit == 0 || + input.gasLimit < LibL2Consts.ANCHOR_GAS_COST || input.gasLimit > config.blockMaxGasLimit ) revert L1_INVALID_METADATA(); diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 95804754f74..fd30f4000e9 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -61,7 +61,8 @@ library LibProving { evidence.blockHash == evidence.parentHash || evidence.signalRoot == 0 || // prover must not be zero - evidence.prover == address(0) + evidence.prover == address(0) || + evidence.gasUsed == 0 ) revert L1_INVALID_EVIDENCE(); TaikoData.Block storage blk = state.blocks[ @@ -96,9 +97,11 @@ library LibProving { // [provenAt+prover] slot. fc.provenAt = uint64(1); fc.prover = address(0); + fc.gasUsed = 0; } else { fc.provenAt = uint64(block.timestamp); fc.prover = evidence.prover; + fc.gasUsed = evidence.gasUsed; } } else { assert(fcId < blk.nextForkChoiceId); @@ -123,6 +126,7 @@ library LibProving { fc.provenAt = uint64(block.timestamp); fc.prover = evidence.prover; + fc.gasUsed = evidence.gasUsed; } if (!oracleProving && !config.skipZKPVerification) { @@ -152,7 +156,9 @@ library LibProving { inputs[4] = uint256(evidence.blockHash); inputs[5] = uint256(evidence.signalRoot); inputs[6] = uint256(evidence.graffiti); - inputs[7] = uint160(evidence.prover); + inputs[7] = + (uint256(uint160(evidence.prover)) << 96) | + (uint256(evidence.gasUsed) << 64); inputs[8] = uint256(blk.metaHash); assembly { diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol index 93071e89452..34171c0974b 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -20,10 +20,6 @@ import { library LibTokenomics { using LibMath for uint256; - uint256 constant SCALING_FACTOR_1E18 = 1e18; // For fixed point representation factor - /// @dev Since we keep the base fee in 10*18 but the actual TKO token is in 10**8 - uint256 constant SCALING_FROM_18_TO_TKO_DEC = 1e10; - error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_PARAM(); @@ -60,82 +56,85 @@ library LibTokenomics { } function getProverFee( - TaikoData.State storage state, - uint32 gasUsed - ) internal view returns (uint256 feeBase, uint256 fee) { - feeBase = state.baseFeeProof; - fee = ((feeBase * gasUsed) / SCALING_FROM_18_TO_TKO_DEC); + TaikoData.State storage state + ) internal view returns (uint64 fee) { + return state.basefee; } function getProofReward( TaikoData.State storage state, TaikoData.Config memory config, uint64 provenAt, - uint64 proposedAt, - uint32 usedGas - ) internal view returns (uint256 newFeeBase, uint256 reward) { - (reward, , newFeeBase) = calculateBaseFeeProof( - state, - config, - (provenAt - proposedAt), - usedGas - ); + uint64 proposedAt + ) internal view returns (uint64 reward) { + (reward, , ) = calculateBasefee(state, config, (provenAt - proposedAt)); } /// @notice Update the baseFee for proofs /// @param state - The actual state data /// @param config - Config data /// @param proofTime - The actual proof time - /// @param usedGas - Gas in the block - function calculateBaseFeeProof( + function calculateBasefee( TaikoData.State storage state, TaikoData.Config memory config, - uint64 proofTime, - uint32 usedGas + uint64 proofTime ) internal view - returns ( - uint256 reward, - uint256 newProofTimeIssued, - uint256 newBaseFeeProof - ) + returns (uint64 reward, uint64 newProofTimeIssued, uint64 newBasefee) { - uint256 proofTimeIssued = state.proofTimeIssued; + uint64 proofTimeIssued = state.proofTimeIssued; // To protect underflow proofTimeIssued = (proofTimeIssued > config.proofTimeTarget) ? proofTimeIssued - config.proofTimeTarget - : uint256(0); + : uint64(0); proofTimeIssued += proofTime; - newBaseFeeProof = baseFee( + newBasefee = _calcBasefee( proofTimeIssued, config.proofTimeTarget, config.adjustmentQuotient ); - if (config.allowMinting) { - reward = ((state.baseFeeProof * usedGas * proofTime) / - (config.proofTimeTarget * SCALING_FROM_18_TO_TKO_DEC)); + uint64 numBlocksBeingProven = state.numBlocks - + state.lastVerifiedBlockId - + 1; + if (numBlocksBeingProven == 0) { + reward = uint64(0); } else { - /// TODO: Verify with functional tests - uint256 numBlocksBeingProven = state.numBlocks - - state.lastVerifiedBlockId - - 1; - if (config.useTimeWeightedReward) { - /// TODO: Theroetically there can be no underflow (in case numBlocksBeingProven == 0 then - /// state.accProposalTime is also 0) - but verify with unit tests ! - uint256 totalNumProvingSeconds = numBlocksBeingProven * + uint64 totalNumProvingSeconds = uint64( + uint256(numBlocksBeingProven) * block.timestamp - - state.accProposalTime; - reward = - (state.proofFeeTreasury * proofTime) / - totalNumProvingSeconds; - } else { - /// TODO: Verify with functional tests - reward = state.proofFeeTreasury / numBlocksBeingProven; - } + state.accProposedAt + ); + + reward = uint64( + ( + uint256( + (state.rewardPool * proofTime) / totalNumProvingSeconds + ) + ) + ); + + // // todo:(dani) Validate algo and check which seems best among the 3 + // Can stay as is for now - until simulation validates which might be better! + // reward_opt2 = uint64( + // ( + // uint256( + // (state.rewardPool * proofTime) / (totalNumProvingSeconds * 2) + // ) + // ) + // ); + + // reward_opt3 = uint64( + // ( + // uint256( + // (state.rewardPool * proofTime) / + // (numBlocksBeingProven - 1) * config.proofTimeTarget + proofTime + // ) + // ) + // ); } newProofTimeIssued = proofTimeIssued; @@ -145,31 +144,30 @@ library LibTokenomics { /// @param value - Result of cumulativeProofTime /// @param target - Target proof time /// @param quotient - Quotient - function baseFee( + function _calcBasefee( uint256 value, uint256 target, uint256 quotient - ) internal pure returns (uint256) { - return ( - (expCalculation(value, target, quotient) / (target * quotient)) - ); + ) private view returns (uint64) { + uint256 result = _expCalculation(value, target, quotient) / + (target * quotient); + + if (result > type(uint64).max) return type(uint64).max; + + return uint64(result); } /// @notice Calculating the exponential via LibFixedPointMath.sol /// @param value - Result of cumulativeProofTime /// @param target - Target proof time /// @param quotient - Quotient - function expCalculation( + function _expCalculation( uint256 value, uint256 target, uint256 quotient - ) internal pure returns (uint256 retVal) { - // Overflow handled by the code - return - uint256( - Math.exp( - int256((value * SCALING_FACTOR_1E18) / (target * quotient)) - ) - ); + ) private view returns (uint256 retVal) { + uint256 x = (value * Math.SCALING_FACTOR_1E18) / (target * quotient); + + return (uint256(Math.exp(int256(x))) / Math.SCALING_FACTOR_1E18); } } diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index b52b55ffc3d..7f6c8959cf6 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -29,20 +29,18 @@ library LibUtils { } function getStateVariables( - TaikoData.State storage state, - TaikoData.Config memory config + TaikoData.State storage state ) internal view returns (TaikoData.StateVariables memory) { + // TODO(dani): expose new state variables. return TaikoData.StateVariables({ - feeBase: state.feeBase, - baseFeeProof: state.baseFeeProof, + basefee: state.basefee, + rewardPool: state.rewardPool, genesisHeight: state.genesisHeight, genesisTimestamp: state.genesisTimestamp, numBlocks: state.numBlocks, lastProposedAt: state.lastProposedAt, - avgBlockTime: state.avgBlockTime, - lastVerifiedBlockId: state.lastVerifiedBlockId, - avgProofTime: state.avgProofTime + lastVerifiedBlockId: state.lastVerifiedBlockId }); } @@ -70,19 +68,17 @@ library LibUtils { uint24 txListByteEnd; uint32 gasLimit; address beneficiary; - address treasure; } function hashMetadata( TaikoData.BlockMetadata memory meta ) internal pure returns (bytes32 hash) { - uint256[6] memory inputs; + uint256[5] memory inputs; inputs[0] = (uint256(meta.id) << 192) | (uint256(meta.timestamp) << 128) | - (uint256(meta.l1Height) << 64) | - uint256(meta.l2Basefee); + (uint256(meta.l1Height) << 64); inputs[1] = uint256(meta.l1Hash); inputs[2] = uint256(meta.mixHash); @@ -94,10 +90,8 @@ library LibUtils { (uint256(meta.gasLimit) << 176) | (uint256(uint160(meta.beneficiary)) << 16); - inputs[5] = (uint256(uint160(meta.treasure)) << 96); - assembly { - hash := keccak256(inputs, mul(6, 32)) + hash := keccak256(inputs, mul(5, 32)) } } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 85efb1cb94e..c8cc32a03cb 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -19,7 +19,6 @@ library LibVerifying { using LibUtils for TaikoData.State; error L1_INVALID_CONFIG(); - error L1_INVALID_L21559_PARAMS(); event BlockVerified(uint256 indexed id, bytes32 blockHash); event XchainSynced( @@ -31,16 +30,18 @@ library LibVerifying { function init( TaikoData.State storage state, TaikoData.Config memory config, - uint64 feeBase, - bytes32 genesisBlockHash + bytes32 genesisBlockHash, + uint64 initBasefee, + uint64 initProofTimeIssued ) internal { _checkConfig(config); uint64 timeNow = uint64(block.timestamp); state.genesisHeight = uint64(block.number); state.genesisTimestamp = timeNow; - state.feeBase = feeBase; - state.baseFeeProof = 1e10; // Symbolic fee (TKO) for 1st proposal + + state.basefee = initBasefee; + state.proofTimeIssued = initProofTimeIssued; state.numBlocks = 1; TaikoData.Block storage blk = state.blocks[0]; @@ -117,45 +118,34 @@ library LibVerifying { TaikoData.ForkChoice storage fc, uint24 fcId ) private returns (bytes32 blockHash, bytes32 signalRoot) { - uint256 proofTime; - unchecked { - proofTime = (fc.provenAt - blk.proposedAt); - } + if (config.proofTimeTarget != 0) { + uint256 proofTime; + unchecked { + proofTime = (fc.provenAt - blk.proposedAt); + } - if (config.enableTokenomics) { ( - uint256 reward, - uint256 proofTimeIssued, - uint256 newBaseFeeProof - ) = LibTokenomics.calculateBaseFeeProof( + uint64 reward, + uint64 proofTimeIssued, + uint64 newBasefee + ) = LibTokenomics.calculateBasefee( state, config, - uint64(proofTime), - blk.gasConsumed + uint64(proofTime) ); - state.baseFeeProof = newBaseFeeProof; + state.basefee = newBasefee; state.proofTimeIssued = proofTimeIssued; - if (!config.allowMinting) { - state.proofFeeTreasury -= reward; - if (config.useTimeWeightedReward) { - state.accProposalTime -= blk.proposedAt; - } + unchecked { + state.rewardPool -= reward; + state.accProposedAt -= blk.proposedAt; } // reward the prover _addToBalance(state, fc.prover, reward); } - state.avgProofTime = LibUtils - .movingAverage({ - maValue: state.avgProofTime, - newValue: proofTime * 1000, - maf: config.provingConfig.avgTimeMAF - }) - .toUint64(); - blockHash = fc.blockHash; signalRoot = fc.signalRoot; @@ -190,19 +180,8 @@ library LibVerifying { // EIP-4844 blob size up to 128K config.maxBytesPerTxList > 128 * 1024 || config.minTxGasLimit == 0 || - config.slotSmoothingFactor == 0 || // EIP-4844 blob deleted after 30 days - config.txListCacheExpiry > 30 * 24 hours || - config.rewardBurnBips >= 10000 + config.txListCacheExpiry > 30 * 24 hours ) revert L1_INVALID_CONFIG(); - - _checkFeeConfig(config.provingConfig); - } - - function _checkFeeConfig( - TaikoData.FeeConfig memory feeConfig - ) private pure { - if (feeConfig.avgTimeMAF <= 1 || feeConfig.dampingFactorBips > 10000) - revert L1_INVALID_CONFIG(); } } diff --git a/packages/protocol/contracts/L2/LibL2Consts.sol b/packages/protocol/contracts/L2/LibL2Consts.sol new file mode 100644 index 00000000000..86ba938ac61 --- /dev/null +++ b/packages/protocol/contracts/L2/LibL2Consts.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.18; + +library LibL2Consts { + // TODO(david): need you to confirm this value. + uint32 public constant ANCHOR_GAS_COST = 47000; +} diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index 02fd7dcad05..3b515f25d6f 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -27,16 +27,7 @@ contract TestTaikoL1 is TaikoL1 { config.maxTransactionsPerBlock = 20; config.maxBytesPerTxList = 120000; config.minTxGasLimit = 21000; - config.slotSmoothingFactor = 590000; - config.rewardBurnBips = 100; // 100 basis points or 1% - config.enableTokenomics = false; config.skipZKPVerification = true; - config.feeBaseMAF = 1024; - - config.provingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, - dampingFactorBips: 5000 - }); } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 3926663eb3f..2f161ebc9ef 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -27,18 +27,9 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { config.maxTransactionsPerBlock = 20; config.maxBytesPerTxList = 120000; config.minTxGasLimit = 21000; - config.slotSmoothingFactor = 590000; - config.rewardBurnBips = 100; // 100 basis points or 1% // Moving average factors - config.feeBaseMAF = 1024; - config.enableTokenomics = true; config.skipZKPVerification = true; - - config.provingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 64, - dampingFactorBips: 5000 - }); } } diff --git a/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol index 1bae4223d96..a056e09f44f 100644 --- a/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol +++ b/packages/protocol/contracts/thirdparty/LibFixedPointMath.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.18; library LibFixedPointMath { uint128 public constant MAX_EXP_INPUT = 135305999368893231588; + uint256 public constant SCALING_FACTOR_1E18 = 1e18; // For fixed point representation factor error Overflow(); diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index 270d8ff083d..ad67d6726aa 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -10,4 +10,6 @@ optimizer = true optimizer_runs = 200 # Do not change the block_gas_limit value, TaikoL2.t.sol depends on it. -block_gas_limit = 30000000 #30M +# block_gas_limit = 30000000 #30M +# For mainnet_mock tokenomics test we need a huge value to run lots of iteration. Need to be revoked afterwards +block_gas_limit = 3000000000 #30M diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index d3ef4e57a3f..a2401277c3b 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -108,13 +108,13 @@ contract DeployOnL1 is Script, AddressResolver { // TaikoL1 TaikoL1 taikoL1 = new TaikoL1(); - uint64 feeBase = 1 ** 8; // Taiko Token's decimals is 8, not 18 + uint64 basefee = 1 ** 8; // Taiko Token's decimals is 8, not 18 address taikoL1Proxy = deployProxy( "taiko", address(taikoL1), bytes.concat( taikoL1.init.selector, - abi.encode(addressManagerProxy, feeBase, gensisHash) + abi.encode(addressManagerProxy, basefee, gensisHash) ) ); setAddress("proto_broker", taikoL1Proxy); diff --git a/packages/protocol/test/tokenomics/blockFee.test.ts b/packages/protocol/test/tokenomics/blockFee.test.ts index 653092f440e..5c825527a20 100644 --- a/packages/protocol/test/tokenomics/blockFee.test.ts +++ b/packages/protocol/test/tokenomics/blockFee.test.ts @@ -43,7 +43,7 @@ describe("tokenomics: blockFee", function () { afterEach(() => clearInterval(interval)); - it("expects getProverFee to return the initial feeBase at time of contract deployment", async function () { + it("expects getProverFee to return the initial basefee at time of contract deployment", async function () { // deploy a new instance of TaikoL1 so no blocks have passed. const tL1 = await deployTaikoL1(l1AddressManager, genesisHash, true); const blockFee = await tL1.getProverFee(); diff --git a/packages/protocol/test/utils/fixture.ts b/packages/protocol/test/utils/fixture.ts index ea9ca26d145..2b924eec910 100644 --- a/packages/protocol/test/utils/fixture.ts +++ b/packages/protocol/test/utils/fixture.ts @@ -10,7 +10,7 @@ import { getL2Provider, } from "./provider"; import { createAndSeedWallets, sendTinyEtherToZeroAddress } from "./seed"; -import { defaultFeeBase, deployTaikoL1 } from "./taikoL1"; +import { defaultBasefee, deployTaikoL1 } from "./taikoL1"; import { deployTaikoL2 } from "./taikoL2"; import deployTaikoToken from "./taikoToken"; @@ -51,7 +51,7 @@ async function initIntegrationFixture( l1AddressManager, genesisHash, enableTokenomics, - defaultFeeBase + defaultBasefee ); const { chainId } = await l1Provider.getNetwork(); diff --git a/packages/protocol/test/utils/taikoL1.ts b/packages/protocol/test/utils/taikoL1.ts index 71974305b20..3d205800320 100644 --- a/packages/protocol/test/utils/taikoL1.ts +++ b/packages/protocol/test/utils/taikoL1.ts @@ -2,13 +2,13 @@ import { BigNumber } from "ethers"; import { ethers } from "hardhat"; import { AddressManager, TaikoL1 } from "../../typechain"; -const defaultFeeBase = BigNumber.from(10).pow(18); +const defaultBasefee = BigNumber.from(10).pow(18); async function deployTaikoL1( addressManager: AddressManager, genesisHash: string, enableTokenomics: boolean, - feeBase?: BigNumber + basefee?: BigNumber ): Promise { // const libProposing = await ( // await ethers.getContractFactory("LibProposing") @@ -39,11 +39,11 @@ async function deployTaikoL1( await taikoL1.init( addressManager.address, genesisHash, - feeBase ?? defaultFeeBase + basefee ?? defaultBasefee ) ).wait(1); return taikoL1 as TaikoL1; } -export { deployTaikoL1, defaultFeeBase }; +export { deployTaikoL1, defaultBasefee }; diff --git a/packages/protocol/test2/GasComparison.t.sol b/packages/protocol/test2/GasComparison.t.sol index c9b6547ff6f..a948536d32f 100644 --- a/packages/protocol/test2/GasComparison.t.sol +++ b/packages/protocol/test2/GasComparison.t.sol @@ -81,12 +81,10 @@ contract FooBar { l1Height: 1, l1Hash: bytes32(uint256(1)), beneficiary: address(this), - treasure: address(this), txListHash: bytes32(uint256(1)), txListByteStart: 0, txListByteEnd: 1000, gasLimit: 1, - l2Basefee: 1000000, mixHash: bytes32(uint256(1)), timestamp: 1 }); @@ -98,12 +96,10 @@ contract FooBar { l1Height: 1, l1Hash: bytes32(uint256(1)), beneficiary: address(this), - treasure: address(this), txListHash: bytes32(uint256(1)), txListByteStart: 0, txListByteEnd: 1000, gasLimit: 1, - l2Basefee: 1000000, mixHash: bytes32(uint256(1)), timestamp: 1 }); diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index 994c065a746..5ddafa04b0e 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -13,7 +13,7 @@ import {SignalService} from "../contracts/signal/SignalService.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {TaikoL1TestBase} from "./TaikoL1TestBase.t.sol"; -contract TaikoL1WithConfig is TaikoL1 { +contract TaikoL1WithNonMintingConfig is TaikoL1 { function getConfig() public pure @@ -22,20 +22,12 @@ contract TaikoL1WithConfig is TaikoL1 { { config = TaikoConfig.getConfig(); - config.enableTokenomics = true; config.txListCacheExpiry = 5 minutes; config.maxVerificationsPerTx = 0; config.enableSoloProposer = false; config.enableOracleProver = false; - config.maxNumProposedBlocks = 10; - config.ringBufferSize = 12; - // this value must be changed if `maxNumProposedBlocks` is changed. - config.slotSmoothingFactor = 4160; - - config.provingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: 5000 - }); + config.maxNumProposedBlocks = 40; + config.ringBufferSize = 48; } } @@ -43,7 +35,7 @@ contract TaikoL1WithConfig is TaikoL1 { // we need to simulate proposing/proving so that can calculate them. contract LibL1TokenomicsTest is TaikoL1TestBase { function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { - taikoL1 = new TaikoL1WithConfig(); + taikoL1 = new TaikoL1WithNonMintingConfig(); } function setUp() public override { @@ -54,17 +46,10 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); } - /// @dev Test blockFee for first block - function test_getProverFee() external { - uint32 gasLimit = 10000000; - uint256 fee = L1.getProverFee(gasLimit); - - // First block propoal has a symbolic 1 unit of baseFeeProof so here gasLimit and fee shall be equal - assertEq(gasLimit, fee); - } - /// @dev Test what happens when proof time increases - function test_reward_and_fee_if_proof_time_increases() external { + function test_balanced_state_reward_and_fee_if_proof_time_increases_slowly_then_drastically() + external + { mine(1); _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); @@ -72,74 +57,178 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { bytes32 parentHash = GENESIS_BLOCK_HASH; + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + //parentHash = prove_with_increasing_time(parentHash, 10); for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine(blockId); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) + ); + verifyBlock(Carol, 1); + parentHash = blockHash; } - printVariables(""); - } - /// @dev Test what happens when proof time decreases - function test_reward_and_fee_if_proof_time_decreases() external { - mine(1); - _depositTaikoToken(Alice, 1E7 * 1E8, 1 ether); - _depositTaikoToken(Bob, 1E7 * 1E8, 1 ether); - _depositTaikoToken(Carol, 1E7 * 1E8, 1 ether); + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; - bytes32 parentHash = GENESIS_BLOCK_HASH; + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); + // Run another session with huge times for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(11 - blockId); + uint64 proposedAt = uint64(block.timestamp); + printVariables("after propose"); + mine_huge(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) + ); verifyBlock(Carol, 1); - printVariables("after proved"); + parentHash = blockHash; } - printVariables(""); + + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + // console2.log("Deposits:", deposits); + // console2.log("withdrawals:", withdrawals); + assertEq(deposits, withdrawals); } - /// @dev Test what happens when proof time stable - function test_reward_and_fee_if_proof_time_stable_and_below_time_target() + /// @dev Test what happens when proof time hectic couple of proposes, without prove, then some proofs + function test_balanced_state_reward_and_fee_if_proof_time_hectic() external { mine(1); - _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); - _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + //Needs lot of token here - because there is lots of time elapsed between 2 'propose' blocks, which will raise the fee + _depositTaikoToken(Alice, 1E8 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E8 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E8 * 1E8, 100 ether); + + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + 20 + ); + uint64[] memory proposedAt = new uint64[](20); bytes32 parentHash = GENESIS_BLOCK_HASH; - for (uint256 blockId = 1; blockId < 10; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + // Propose blocks + for (uint256 blockId = 1; blockId < 20; blockId++) { + //printVariables("before propose"); + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); + mine(blockId); + } + + // Wait random X + mine(6); + // Prove and verify + for (uint256 blockId = 1; blockId < 20; blockId++) { + bytes32 blockHash = bytes32(1E10 + blockId); + bytes32 signalRoot = bytes32(1E9 + blockId); + proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId]) ); + + verifyBlock(Carol, 1); + mine(blockId); + parentHash = blockHash; + } + + /// @dev Long term the sum of deposits / withdrawals converge towards the balance + /// @dev The best way to assert this is to observ: the higher the loop counter + /// @dev the smaller the difference between deposits / withrawals + + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + // console2.log("Deposits:", deposits); + // console2.log("withdrawals:", withdrawals); + assertEq(deposits, withdrawals); + + // Run another sessioins + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(1); + uint64 proposedAt = uint64(block.timestamp); + printVariables("after propose"); + mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) + ); verifyBlock(Carol, 1); + parentHash = blockHash; } - printVariables(""); + + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + // console2.log("Deposits:", deposits); + // console2.log("withdrawals:", withdrawals); + assertEq(deposits, withdrawals); } - /// @dev Test blockFee when proof target is stable but slightly above the target - function test_reward_and_fee_if_proof_time_stable_but_above_time_target() + /// @dev Test and see what happens when proof time is stable below the target and proving consecutive + function test_balanced_state_reward_and_fee_if_proof_time_stable_below_target_prooving_consecutive() external { mine(1); @@ -149,25 +238,54 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { bytes32 parentHash = GENESIS_BLOCK_HASH; - for (uint256 blockId = 1; blockId < 50; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + //parentHash = prove_with_increasing_time(parentHash, 10); + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - //Constant 5 means = 100 sec (90 sec is the target) - mine(5); + uint64 proposedAt = uint64(block.timestamp); + printVariables("after propose"); + mine(2); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) + ); + verifyBlock(Carol, 1); + parentHash = blockHash; } + + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); } - /// @dev Test what happens when proof time decreasing then stabilizes - function test_reward_and_fee_if_proof_time_decreasing_then_stabilizes() + /// @dev Test and see what happens when proof time is stable below the target and proving non consecutive + function test_balanced_state_reward_and_fee_if_proof_time_stable_below_target_proving_non_consecutive() external { mine(1); @@ -175,64 +293,68 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + 30 + ); + uint64[] memory proposedAt = new uint64[](30); + bytes32 parentHash = GENESIS_BLOCK_HASH; + // Check balances uint256 Alice_start_balance = L1.getBalance(Alice); uint256 Bob_start_balance = L1.getBalance(Bob); console2.log("Alice balance:", Alice_start_balance); console2.log("Bob balance:", Bob_start_balance); - console2.log("Decreasing"); - for (uint256 blockId = 1; blockId < 10; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(21 - blockId); + // Propose blocks + for (uint256 blockId = 1; blockId < 30; blockId++) { + //printVariables("before propose"); + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); + mine(blockId); + } + // Wait random X + mine(6); + //Prove and verify + for (uint256 blockId = 1; blockId < 30; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - parentHash = blockHash; - } - console2.log("Stable"); - for (uint256 blockId = 1; blockId < 100; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" + proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId]) ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine_proofTime(); - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); verifyBlock(Carol, 1); + mine(3); parentHash = blockHash; } - uint256 Alice_end_balance = L1.getBalance(Alice); - uint256 Bob_end_balance = L1.getBalance(Bob); - - console2.log("Alice balance:", Alice_end_balance); - console2.log("Bob balance:", Bob_end_balance); - - // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount - // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test - uint256 aliceChange = Alice_start_balance - Alice_end_balance; - uint256 bobChange = Bob_end_balance - Bob_start_balance; - - console2.log("Alice change:", aliceChange); - console2.log("Bob change:", bobChange); - - // Assert their balance changed relatively the same way - // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range - assertApproxEqRel(aliceChange, bobChange, 1e17); + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); } - /// @dev Test what happens when proof time increasing then stabilizes at the same time as proof time target - function test_reward_and_fee_if_proof_time_increasing_then_stabilizes_at_the_proof_time_target() + /// @dev Test what happens when proof time decreases + function test_balanced_state_reward_and_fee_if_proof_time_decreases() external { mine(1); @@ -240,74 +362,118 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + 20 + ); + uint64[] memory proposedAt = new uint64[](20); + bytes32 parentHash = GENESIS_BLOCK_HASH; + // Check balances uint256 Alice_start_balance = L1.getBalance(Alice); uint256 Bob_start_balance = L1.getBalance(Bob); console2.log("Alice balance:", Alice_start_balance); console2.log("Bob balance:", Bob_start_balance); - console2.log("Increasing"); + // Propose blocks for (uint256 blockId = 1; blockId < 20; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + //printVariables("before propose"); + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); mine(blockId); + } + // Wait random X + mine(6); + // Prove and verify + for (uint256 blockId = 1; blockId < 20; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId]) + ); + verifyBlock(Carol, 1); + mine(21 - blockId); parentHash = blockHash; } - console2.log("Stable"); - for (uint256 blockId = 1; blockId < 110; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); + /// @dev Long term the sum of deposits / withdrawals converge towards the balance + /// @dev The best way to assert this is to observ: the higher the loop counter + /// @dev the smaller the difference between deposits / withrawals + + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + // console2.log("Deposits:", deposits); + // console2.log("withdrawals:", withdrawals); + assertEq(deposits, withdrawals); + } + + /// @dev Test and see what happens when proof time is stable above the target and proving consecutive + function test_balanced_state_reward_and_fee_if_proof_time_stable_above_target_prooving_consecutive() + external + { + mine(1); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + //parentHash = prove_with_increasing_time(parentHash, 10); + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); uint64 proposedAt = uint64(block.timestamp); - - mine_proofTime(); + printVariables("after propose"); + mine(5); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); - console2.log( "Proof reward is:", - L1.getProofReward(provenAt, proposedAt, 1000000) + L1.getProofReward(provenAt, proposedAt) ); verifyBlock(Carol, 1); + parentHash = blockHash; } - uint256 Alice_end_balance = L1.getBalance(Alice); - uint256 Bob_end_balance = L1.getBalance(Bob); - - console2.log("Alice balance:", Alice_end_balance); - console2.log("Bob balance:", Bob_end_balance); - - // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount - // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test - uint256 aliceChange = Alice_start_balance - Alice_end_balance; - uint256 bobChange = Bob_end_balance - Bob_start_balance; - - console2.log("Alice change:", aliceChange); - console2.log("Bob change:", bobChange); - - // Assert their balance changed relatively the same way - // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range - assertApproxEqRel(aliceChange, bobChange, 1e17); + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); } - /// @dev Test what happens when proof time increasing then stabilizes below the target time - /// @notice This test is failing - and disabled, but it is meant to demonstrate the behaviour - function xtest_reward_and_fee_if_proof_time_increasing_then_stabilizes_below_the_proof_time_target() + /// @dev Test and see what happens when proof time is stable above the target and proving non consecutive + function test_balanced_state_reward_and_fee_if_proof_time_stable_above_target_proving_non_consecutive() external { mine(1); @@ -315,225 +481,255 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + 30 + ); + uint64[] memory proposedAt = new uint64[](30); + bytes32 parentHash = GENESIS_BLOCK_HASH; + // Check balances uint256 Alice_start_balance = L1.getBalance(Alice); uint256 Bob_start_balance = L1.getBalance(Bob); console2.log("Alice balance:", Alice_start_balance); console2.log("Bob balance:", Bob_start_balance); - console2.log("Increasing"); - for (uint256 blockId = 1; blockId < 20; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + // Propose blocks + for (uint256 blockId = 1; blockId < 30; blockId++) { + //printVariables("before propose"); + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); mine(blockId); + } + // Wait random X + mine(6); + // Prove and verify + for (uint256 blockId = 1; blockId < 30; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - parentHash = blockHash; - } - console2.log("Stable - but under proof time"); - // To see the issue - adjust the max loop counter below. - // The more the loops the bigger the deposits (compared to withrawals) - for (uint256 blockId = 1; blockId < 100; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - uint64 proposedAt = uint64(block.timestamp); - mine(2); + proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); - console2.log( "Proof reward is:", - L1.getProofReward(provenAt, proposedAt, 1000000) + L1.getProofReward(provenAt, proposedAt[blockId]) ); verifyBlock(Carol, 1); + mine(5); parentHash = blockHash; } - uint256 Alice_end_balance = L1.getBalance(Alice); - uint256 Bob_end_balance = L1.getBalance(Bob); - - console2.log("Alice balance:", Alice_end_balance); - console2.log("Bob balance:", Bob_end_balance); - - // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount - // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test - uint256 aliceChange = Alice_start_balance - Alice_end_balance; - uint256 bobChange = Bob_end_balance - Bob_start_balance; - - console2.log("Alice change:", aliceChange); - console2.log("Bob change:", bobChange); - - // Assert their balance changed relatively the same way - // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range - // Unfortunately it is not ! The longer we run the algo with stable but under proof time - // the more unequal it will get - assertApproxEqRel(aliceChange, bobChange, 1e17); + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); } - /// @dev Test what happens when proof time fluctuates then stabilizes - function test_reward_and_fee_if_proof_time_fluctuates_then_stabilizes() + /// @dev Test what happens when proof time decreases + function test_balanced_state_reward_and_fee_if_proof_time_decreasses_then_stabilizes_consecutive() external { mine(1); - _depositTaikoToken(Alice, 1E7 * 1E8, 100 ether); - _depositTaikoToken(Bob, 1E7 * 1E8, 100 ether); + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; + // Check balances uint256 Alice_start_balance = L1.getBalance(Alice); uint256 Bob_start_balance = L1.getBalance(Bob); console2.log("Alice balance:", Alice_start_balance); console2.log("Bob balance:", Bob_start_balance); - console2.log("Increasing"); - for (uint256 blockId = 1; blockId < 20; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); + //parentHash = prove_with_increasing_time(parentHash, 10); + for (uint256 blockId = 1; blockId < 10; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(blockId); + printVariables("after propose"); + uint64 proposedAt = uint64(block.timestamp); + mine(11 - blockId); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); - verifyBlock(Carol, 1); - parentHash = blockHash; - } - - console2.log("Decreasing"); - for (uint256 blockId = 1; blockId < 20; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(21 - blockId); - bytes32 blockHash = bytes32(1E10 + blockId); - bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); verifyBlock(Carol, 1); + parentHash = blockHash; } - console2.log("Stable"); - for (uint256 blockId = 1; blockId < 150; blockId++) { - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); + + // Run another session with huge times + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + uint64 proposedAt = uint64(block.timestamp); + printVariables("after propose"); mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) + ); verifyBlock(Carol, 1); + parentHash = blockHash; } - uint256 Alice_end_balance = L1.getBalance(Alice); - uint256 Bob_end_balance = L1.getBalance(Bob); - - console2.log("Alice balance:", Alice_end_balance); - console2.log("Bob balance:", Bob_end_balance); + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; - // Now we need to check if Alice's balance changed (not 100% same but approx.) with the same amount - // We know that Alice spent while Bob gets the rewards so no need to check for underflow for the sake of this test - uint256 aliceChange = Alice_start_balance - Alice_end_balance; - uint256 bobChange = Bob_end_balance - Bob_start_balance; + // console2.log("Deposits:", deposits); + // console2.log("withdrawals:", withdrawals); + assertEq(deposits, withdrawals); + } - console2.log("Alice change:", aliceChange); - console2.log("Bob change:", bobChange); + /// @dev Test what happens when proof time decreases + function test_balanced_state_reward_and_fee_if_proof_time_decreasses_then_stabilizes_non_consecutive() + external + { + mine(1); + // Requires a bit more tokens + _depositTaikoToken(Alice, 1E8 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E8 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E8 * 1E8, 100 ether); - // Assert their balance changed relatively the same way - // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range - assertApproxEqRel(aliceChange, bobChange, 1e17); - } + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + 20 + ); + uint64[] memory proposedAt = new uint64[](20); - /// @dev Test blockFee start decreasing when the proof time goes below proof target (given gas used is the same) - function test_getProverFee_is_higher_when_increasing_proof_time() external { - uint32 gasLimit = 10000000; bytes32 parentHash = GENESIS_BLOCK_HASH; - for (uint256 blockId = 1; blockId < 10; blockId++) { - uint256 previousFee = L1.getProverFee(gasLimit); - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + console2.log("Alice balance:", Alice_start_balance); + console2.log("Bob balance:", Bob_start_balance); + + // Propose blocks + for (uint256 blockId = 1; blockId < 20; blockId++) { + //printVariables("before propose"); + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); mine(blockId); + } + // Wait random X + mine(6); + // Prove and verify + for (uint256 blockId = 1; blockId < 20; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId]) + ); + verifyBlock(Carol, 1); + mine(21 - blockId); parentHash = blockHash; - uint256 actualFee = L1.getProverFee(gasLimit); - // Check that fee always increasing in this scenario - assertGt(actualFee, previousFee); } - printVariables(""); - } - /// @dev Test blockFee starts decreasing when the proof time goes below proof target - function test_getProverFee_starts_decreasing_when_proof_time_falls_below_the_average() - external - { - uint32 gasLimit = 10000000; - bytes32 parentHash = GENESIS_BLOCK_HASH; - uint256 blockId; - uint256 previousFee; - uint256 actualFee; - - for (blockId = 1; blockId < 10; blockId++) { - previousFee = L1.getProverFee(gasLimit); - printVariables( - "before proposing - affected by verification (verifyBlock() updates)" - ); + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log( + "Alice current balance after first iteration:", + L1.getBalance(Alice) + ); + console2.log( + "Bob current balance after first iteration:", + L1.getBalance(Bob) + ); + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + + assertEq(deposits, withdrawals); + + // Run another session with huge times + for (uint256 blockId = 1; blockId < 10; blockId++) { + printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); - mine(50 - blockId); + uint64 proposedAt = uint64(block.timestamp); + printVariables("after propose"); + mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt) + ); verifyBlock(Carol, 1); - parentHash = blockHash; - actualFee = L1.getProverFee(gasLimit); - // Check that fee always increasing in this scenario - assertGt(actualFee, previousFee); + parentHash = blockHash; } - // Start proving below proof time - will affect the next proposal only after - mine(1); - previousFee = L1.getProverFee(gasLimit); - printVariables("See still higher"); - TaikoData.BlockMetadata memory meta2 = proposeBlock(Alice, 1024); - mine(1); + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; - bytes32 blockHash2 = bytes32(1E10 + blockId); - bytes32 signalRoot2 = bytes32(1E9 + blockId); - proveBlock(Bob, meta2, parentHash, blockHash2, signalRoot2); - //After this verification - the proof time falls below the target average, so it will start decreasing - verifyBlock(Carol, 1); + // console2.log("Deposits:", deposits); + // console2.log("withdrawals:", withdrawals); + assertEq(deposits, withdrawals); + } - actualFee = L1.getProverFee(gasLimit); - assertGt(previousFee, actualFee); + function mine_huge() internal { + vm.warp(block.timestamp + 1200); + vm.roll(block.number + 300); } + // Currently set to 85s proofTimeTarget function mine_proofTime() internal { vm.warp(block.timestamp + 85); vm.roll(block.number + 4); diff --git a/packages/protocol/test2/LibTokenomicsMainnetMock.t.sol b/packages/protocol/test2/LibTokenomicsMainnetMock.t.sol new file mode 100644 index 00000000000..502df5f65f0 --- /dev/null +++ b/packages/protocol/test2/LibTokenomicsMainnetMock.t.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +// Uncomment if you want to compare fee/vs reward +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {AddressManager} from "../contracts/thirdparty/AddressManager.sol"; +import {TaikoConfig} from "../contracts/L1/TaikoConfig.sol"; +import {TaikoData} from "../contracts/L1/TaikoData.sol"; +import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; +import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; +import {SignalService} from "../contracts/signal/SignalService.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {TaikoL1TestBase} from "./TaikoL1TestBase.t.sol"; + +contract TaikoL1WithNonMintingConfig is TaikoL1 { + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config = TaikoConfig.getConfig(); + + config.txListCacheExpiry = 5 minutes; + config.maxVerificationsPerTx = 0; + config.enableSoloProposer = false; + config.enableOracleProver = false; + config.maxNumProposedBlocks = 200; + config.ringBufferSize = 240; + } +} + +// Since the fee/reward calculation heavily depends on the baseFeeProof and the proofTime +// we need to simulate proposing/proving so that can calculate them. +contract LibL1TokenomicsTest is TaikoL1TestBase { + function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { + taikoL1 = new TaikoL1WithNonMintingConfig(); + } + + function setUp() public override { + TaikoL1TestBase.setUp(); + + _depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); + } + + /// @dev To mock a mainnet: set proofTImeTarget in TaikoConfig.sol to 30 mins and run this test + + /// @dev A possible (close to) mainnet scenarios is the following: + //// - Blocks ever 10 seconds proposed + //// - Proofs coming shifted slightly below 30 min / proposed block afterwards + //// Expected result: Withdrawals and deposits are in balance but keep shrinking since quicker proofTime + function xtest_possible_mainnet_scenario_proof_time_below_target() + external + { + vm.pauseGasMetering(); + mine(1); + + _depositTaikoToken(Alice, 1E8 * 1E8, 1000 ether); + _depositTaikoToken(Bob, 1E8 * 1E8, 1000 ether); + _depositTaikoToken(Carol, 1E8 * 1E8, 1000 ether); + + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + + // Can play to adjust + uint32 iterationCnt = 5000; + uint8 proofTime = 179; // When proofs are coming, 179 means 1790 sec + + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + iterationCnt + ); + uint64[] memory proposedAt = new uint64[](iterationCnt); + bytes32[] memory parentHashes = new bytes32[](iterationCnt); + bytes32[] memory blockHashes = new bytes32[](iterationCnt); + bytes32[] memory signalRoots = new bytes32[](iterationCnt); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + console2.logBytes32(parentHash); + + // Run another session with huge times + for (uint256 blockId = 1; blockId < iterationCnt; blockId++) { + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); + blockHashes[blockId] = bytes32(1E10 + blockId); + signalRoots[blockId] = bytes32(1E9 + blockId); + + if (blockId > proofTime) { + //Start proving with an offset + proveBlock( + Bob, + meta[blockId - proofTime], + parentHashes[blockId - proofTime], + blockHashes[blockId - proofTime], + signalRoots[blockId - proofTime] + ); + + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId - proofTime]) + ); + verifyBlock(Carol, 1); + } + + mine_every_10_sec(); + + parentHashes[blockId] = parentHash; + parentHash = blockHashes[blockId]; + } + + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(deposits, withdrawals, 1e17); + } + + /// @dev A possible (close to) mainnet scenarios is the following: + //// - Blocks ever 10 seconds proposed + //// - Proofs coming shifted 30 min / proposed block afterwards + //// Expected result: Withdrawals and deposits are in balance and since it is coming at proofTimeTarget stays the same + function xtest_possible_mainnet_scenario_proof_time_at_target() external { + vm.pauseGasMetering(); + mine(1); + + _depositTaikoToken(Alice, 1E8 * 1E8, 1000 ether); + _depositTaikoToken(Bob, 1E8 * 1E8, 1000 ether); + _depositTaikoToken(Carol, 1E8 * 1E8, 1000 ether); + + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + + // Can play to adjust + uint32 iterationCnt = 5000; + uint32 proofTime = 180; // When proofs are coming, 180 means 1800 sec + + /// 1.step: mine 180 blocks without proofs - then start prooving with a -180 offset + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + iterationCnt + ); + uint64[] memory proposedAt = new uint64[](iterationCnt); + bytes32[] memory parentHashes = new bytes32[](iterationCnt); + bytes32[] memory blockHashes = new bytes32[](iterationCnt); + bytes32[] memory signalRoots = new bytes32[](iterationCnt); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + console2.logBytes32(parentHash); + + for (uint256 blockId = 1; blockId < iterationCnt; blockId++) { + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); + blockHashes[blockId] = bytes32(1E10 + blockId); + signalRoots[blockId] = bytes32(1E9 + blockId); + + if (blockId > proofTime) { + //Start proving with an offset + proveBlock( + Bob, + meta[blockId - proofTime], + parentHashes[blockId - proofTime], + blockHashes[blockId - proofTime], + signalRoots[blockId - proofTime] + ); + + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId - proofTime]) + ); + verifyBlock(Carol, 1); + } + + mine_every_10_sec(); + + parentHashes[blockId] = parentHash; + parentHash = blockHashes[blockId]; + } + + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(deposits, withdrawals, 1e17); + } + + /// @dev A possible (close to) mainnet scenarios is the following: + //// - Blocks ever 10 seconds proposed + //// - Proofs coming shifted slightly above 30 min / proposed block afterwards + //// Expected result: Withdrawals and deposits are in balance but fees keep growing + function test_possible_mainnet_scenario_proof_time_above_target() external { + vm.pauseGasMetering(); + mine(1); + + _depositTaikoToken(Alice, 1E8 * 1E8, 1000 ether); + _depositTaikoToken(Bob, 1E8 * 1E8, 1000 ether); + _depositTaikoToken(Carol, 1E8 * 1E8, 1000 ether); + + // Check balances + uint256 Alice_start_balance = L1.getBalance(Alice); + uint256 Bob_start_balance = L1.getBalance(Bob); + + // Can play to adjust + uint32 iterationCnt = 5000; + uint8 proofTime = 181; // When proofs are coming, 181 means 1810 sec + + TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + iterationCnt + ); + uint64[] memory proposedAt = new uint64[](iterationCnt); + bytes32[] memory parentHashes = new bytes32[](iterationCnt); + bytes32[] memory blockHashes = new bytes32[](iterationCnt); + bytes32[] memory signalRoots = new bytes32[](iterationCnt); + + bytes32 parentHash = GENESIS_BLOCK_HASH; + console2.logBytes32(parentHash); + + for (uint256 blockId = 1; blockId < iterationCnt; blockId++) { + meta[blockId] = proposeBlock(Alice, 1024); + proposedAt[blockId] = (uint64(block.timestamp)); + printVariables("after propose"); + blockHashes[blockId] = bytes32(1E10 + blockId); + signalRoots[blockId] = bytes32(1E9 + blockId); + + if (blockId > proofTime) { + //Start proving with an offset + proveBlock( + Bob, + meta[blockId - proofTime], + parentHashes[blockId - proofTime], + blockHashes[blockId - proofTime], + signalRoots[blockId - proofTime] + ); + + uint64 provenAt = uint64(block.timestamp); + console2.log( + "Proof reward is:", + L1.getProofReward(provenAt, proposedAt[blockId - proofTime]) + ); + verifyBlock(Carol, 1); + } + + mine_every_10_sec(); + + parentHashes[blockId] = parentHash; + parentHash = blockHashes[blockId]; + } + + //Check end balances + uint256 deposits = Alice_start_balance - L1.getBalance(Alice); + uint256 withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + //Check end balances + deposits = Alice_start_balance - L1.getBalance(Alice); + withdrawals = L1.getBalance(Bob) - Bob_start_balance; + + console2.log("Deposits:", deposits); + console2.log("withdrawals:", withdrawals); + // Assert their balance changed relatively the same way + // 1e18 == within 100 % delta -> 1e17 10%, let's see if this is within that range + assertApproxEqRel(deposits, withdrawals, 1e17); + } + + // Currently set to 85s proofTimeTarget + function mine_every_10_sec() internal { + vm.warp(block.timestamp + 10); + vm.roll(block.number + 1); + } +} diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index 916a8cdb561..e6c6969344a 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -21,7 +21,6 @@ contract TaikoL1WithConfig is TaikoL1 { { config = TaikoConfig.getConfig(); - config.enableTokenomics = true; config.txListCacheExpiry = 5 minutes; config.maxVerificationsPerTx = 0; config.enableSoloProposer = false; @@ -29,12 +28,6 @@ contract TaikoL1WithConfig is TaikoL1 { config.maxNumProposedBlocks = 10; config.ringBufferSize = 12; // this value must be changed if `maxNumProposedBlocks` is changed. - config.slotSmoothingFactor = 4160; - - config.provingConfig = TaikoData.FeeConfig({ - avgTimeMAF: 1024, - dampingFactorBips: 5000 - }); } } diff --git a/packages/protocol/test2/TaikoL1TestBase.t.sol b/packages/protocol/test2/TaikoL1TestBase.t.sol index 2b278d763a8..6cd936b8a58 100644 --- a/packages/protocol/test2/TaikoL1TestBase.t.sol +++ b/packages/protocol/test2/TaikoL1TestBase.t.sol @@ -10,6 +10,7 @@ import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; import {SignalService} from "../contracts/signal/SignalService.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {TestLn as TestMath} from "./TestLn.sol"; contract Verifier { fallback(bytes calldata) external returns (bytes memory) { @@ -25,13 +26,12 @@ abstract contract TaikoL1TestBase is Test { TaikoData.Config conf; uint256 internal logCount; + uint256 private constant SCALING_E18 = 1e18; + bytes32 public constant GENESIS_BLOCK_HASH = keccak256("GENESIS_BLOCK_HASH"); - uint64 feeBase = 1E8; // 1 TKO uint64 l2GasExcess = 1E18; - address public constant L2Treasure = - 0x859d74b52762d9ed07D1b2B8d7F93d26B1EA78Bb; address public constant L2SS = 0xa008AE5Ba00656a3Cc384de589579e3E52aC030C; address public constant L2TaikoL2 = 0x0082D90249342980d011C58105a03b35cCb4A315; @@ -42,15 +42,35 @@ abstract contract TaikoL1TestBase is Test { address public constant Dave = 0x400147C0Eb43D8D71b2B03037bB7B31f8f78EF5F; address public constant Eve = 0x50081b12838240B1bA02b3177153Bca678a86078; + uint32 private constant INIT_FEE = 1e9; // 10 TKO : Only relevant for the first proposing + uint16 private constant PROOF_TIME_TARGET = 1800; + uint8 private constant ADJUSTMENT_QUOTIENT = 16; + function deployTaikoL1() internal virtual returns (TaikoL1 taikoL1); function setUp() public virtual { // vm.warp(1000000); addressManager = new AddressManager(); addressManager.init(); + uint64 initBasefee = INIT_FEE; + + // Calculating it for our needs based on testnet/mainnet proof vars. + // See Brecht's comment https://github.com/taikoxyz/taiko-mono/pull/13564 + uint256 scale = uint256(PROOF_TIME_TARGET * ADJUSTMENT_QUOTIENT); + // ln_pub() expects 1e18 fixed format + int256 logInput = int256((scale * initBasefee) * SCALING_E18); + int256 log_result = TestMath.ln_pub(logInput); + uint64 initProofTimeIssued = uint64( + ((scale * (uint256(log_result))) / (SCALING_E18)) + ); L1 = deployTaikoL1(); - L1.init(address(addressManager), feeBase, GENESIS_BLOCK_HASH); + L1.init( + address(addressManager), + GENESIS_BLOCK_HASH, + initBasefee, + initProofTimeIssued + ); conf = L1.getConfig(); tko = new TaikoToken(); @@ -75,7 +95,6 @@ abstract contract TaikoL1TestBase is Test { _registerAddress("taiko_token", address(tko)); _registerAddress("proto_broker", address(L1)); _registerAddress("signal_service", address(ss)); - _registerL2Address("treasure", L2Treasure); _registerL2Address("signal_service", address(L2SS)); _registerL2Address("taiko_l2", address(L2TaikoL2)); @@ -113,7 +132,6 @@ abstract contract TaikoL1TestBase is Test { meta.id = variables.numBlocks; meta.timestamp = uint64(block.timestamp); meta.l1Height = uint64(block.number - 1); - meta.l2Basefee = 0; meta.l1Hash = blockhash(block.number - 1); meta.mixHash = bytes32(_mixHash); meta.txListHash = keccak256(txList); @@ -121,7 +139,6 @@ abstract contract TaikoL1TestBase is Test { meta.txListByteEnd = txListSize; meta.gasLimit = gasLimit; meta.beneficiary = proposer; - meta.treasure = L2Treasure; vm.prank(proposer, proposer); L1.proposeBlock(abi.encode(input), txList); @@ -146,7 +163,8 @@ abstract contract TaikoL1TestBase is Test { blockHash: blockHash, signalRoot: signalRoot, graffiti: 0x0, - prover: prover + prover: prover, + gasUsed: 100000 }); vm.prank(prover, prover); @@ -182,23 +200,17 @@ abstract contract TaikoL1TestBase is Test { } function printVariables(string memory comment) internal { - uint32 gasLimit = 1000000; TaikoData.StateVariables memory vars = L1.getStateVariables(); - uint256 fee = L1.getProverFee(gasLimit); + uint256 fee = L1.getProverFee(); string memory str = string.concat( Strings.toString(logCount++), ":[", Strings.toString(vars.lastVerifiedBlockId), unicode"→", Strings.toString(vars.numBlocks), - "] feeBase:", - Strings.toString(vars.baseFeeProof), + "]", " fee:", Strings.toString(fee), - " avgBlockTime:", - Strings.toString(vars.avgBlockTime), - " avgProofTime:", - Strings.toString(vars.avgProofTime), " lastProposedAt:", Strings.toString(vars.lastProposedAt), " // ", diff --git a/packages/protocol/test2/TaikoL2.t.sol b/packages/protocol/test2/TaikoL2.t.sol index 1f4f84d632f..e8dfea7c330 100644 --- a/packages/protocol/test2/TaikoL2.t.sol +++ b/packages/protocol/test2/TaikoL2.t.sol @@ -38,7 +38,7 @@ contract TestTaikoL2 is Test { // console2.log("gasExcess =", uint256(L2.gasExcess())); } - function testAnchorTxsBlocktimeConstant() external { + function xtestAnchorTxsBlocktimeConstant() external { uint64 firstBasefee; for (uint256 i = 0; i < 100; i++) { uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); @@ -58,7 +58,7 @@ contract TestTaikoL2 is Test { } } - function testAnchorTxsBlocktimeDecreasing() external { + function xtestAnchorTxsBlocktimeDecreasing() external { uint64 prevBasefee; for (uint256 i = 0; i < 32; i++) { @@ -76,7 +76,7 @@ contract TestTaikoL2 is Test { } } - function testAnchorTxsBlocktimeIncreasing() external { + function xtestAnchorTxsBlocktimeIncreasing() external { uint64 prevBasefee; for (uint256 i = 0; i < 30; i++) { @@ -98,7 +98,7 @@ contract TestTaikoL2 is Test { } // calling anchor in the same block more than once should fail - function testAnchorTxsFailInTheSameBlock() external { + function xtestAnchorTxsFailInTheSameBlock() external { uint64 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); vm.fee(expectedBasefee); diff --git a/packages/protocol/test2/TestLn.sol b/packages/protocol/test2/TestLn.sol new file mode 100644 index 00000000000..e7d9d267ce3 --- /dev/null +++ b/packages/protocol/test2/TestLn.sol @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +library TestLn { + error Overflow(); + error LnNegativeUndefined(); + + // Integer log2 (alternative implementation) + // @returns floor(log2(x)) if x is nonzero, otherwise 0. + // Consumes 317 gas. This could have been an 3 gas EVM opcode though. + function ilog2_alt(uint256 x) internal pure returns (uint256 r) { + unchecked { + // Repeat first zero all the way to the right + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + x |= x >> 64; + x |= x >> 128; + + // Count 32 bit chunks + r = x & 0x100000001000000010000000100000001000000010000000100000001; + r *= 0x20000000200000002000000020000000200000002000000020; + r >>= 224; + + // Extract highest bit + x ^= x >> 1; + + // Copy to lowest 32 bit chunk + x |= x >> 32; + x |= x >> 64; + x |= x >> 128; + // No need to clear the other chunks + + // Map to 0-31 using the B(2, 5) de Bruijn sequence 0x077CB531. + // See + x = ((x * 0x077CB531) >> 27) & 0x1f; + + // Use a bytes32 32 entry lookup table + assembly { + // Need assembly here because solidity introduces an uncessary bounds + // check. + r := add( + r, + byte( + x, + 0x11c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a09 + ) + ) + } + } + } + + // Integer log2 + // @returns floor(log2(x)) if x is nonzero, otherwise 0. This is the same + // as the location of the highest set bit. + // Consumes 232 gas. This could have been an 3 gas EVM opcode though. + function ilog2(uint256 x) internal pure returns (uint256 r) { + assembly { + r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) + r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) + r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) + r := or(r, shl(4, lt(0xffff, shr(r, x)))) + r := or(r, shl(3, lt(0xff, shr(r, x)))) + r := or(r, shl(2, lt(0xf, shr(r, x)))) + r := or(r, shl(1, lt(0x3, shr(r, x)))) + r := or(r, lt(0x1, shr(r, x))) + } + } + + function ln_pub(int256 x) public pure returns (int256 r) { + return ln(x); + } + + // Computes ln(x) in 1e18 fixed point. + // Reverts if x is negative or zero. + // Consumes 670 gas. + function ln(int256 x) internal pure returns (int256 r) { + unchecked { + if (x < 1) { + if (x < 0) revert LnNegativeUndefined(); + revert Overflow(); + } + + // We want to convert x from 10**18 fixed point to 2**96 fixed point. + // We do this by multiplying by 2**96 / 10**18. + // But since ln(x * C) = ln(x) + ln(C), we can simply do nothing here + // and add ln(2**96 / 10**18) at the end. + + // Reduce range of x to (1, 2) * 2**96 + // ln(2^k * x) = k * ln(2) + ln(x) + // Note: inlining ilog2 saves 8 gas. + int256 k = int256(ilog2(uint256(x))) - 96; + x <<= uint256(159 - k); + x = int256(uint256(x) >> 159); + + // Evaluate using a (8, 8)-term rational approximation + // p is made monic, we will multiply by a scale factor later + int256 p = x + 3273285459638523848632254066296; + p = ((p * x) >> 96) + 24828157081833163892658089445524; + p = ((p * x) >> 96) + 43456485725739037958740375743393; + p = ((p * x) >> 96) - 11111509109440967052023855526967; + p = ((p * x) >> 96) - 45023709667254063763336534515857; + p = ((p * x) >> 96) - 14706773417378608786704636184526; + p = p * x - (795164235651350426258249787498 << 96); + //emit log_named_int("p", p); + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + // q is monic by convention + int256 q = x + 5573035233440673466300451813936; + q = ((q * x) >> 96) + 71694874799317883764090561454958; + q = ((q * x) >> 96) + 283447036172924575727196451306956; + q = ((q * x) >> 96) + 401686690394027663651624208769553; + q = ((q * x) >> 96) + 204048457590392012362485061816622; + q = ((q * x) >> 96) + 31853899698501571402653359427138; + q = ((q * x) >> 96) + 909429971244387300277376558375; + assembly { + // Div in assembly because solidity adds a zero check despite the `unchecked`. + // The q polynomial is known not to have zeros in the domain. (All roots are complex) + // No scaling required because p is already 2**96 too large. + r := sdiv(p, q) + } + // r is in the range (0, 0.125) * 2**96 + + // Finalization, we need to + // * multiply by the scale factor s = 5.549… + // * add ln(2**96 / 10**18) + // * add k * ln(2) + // * multiply by 10**18 / 2**96 = 5**18 >> 78 + // mul s * 5e18 * 2**96, base is now 5**18 * 2**192 + r *= 1677202110996718588342820967067443963516166; + // add ln(2) * k * 5e18 * 2**192 + r += + 16597577552685614221487285958193947469193820559219878177908093499208371 * + k; + // add ln(2**96 / 10**18) * 5e18 * 2**192 + r += 600920179829731861736702779321621459595472258049074101567377883020018308; + // base conversion: mul 2**18 / 2**192 + r >>= 174; + } + } + + // Computes e^x in 1e18 fixed point. + function exp(int256 x) internal pure returns (int256 r) { + unchecked { + // Input x is in fixed point format, with scale factor 1/1e18. + + // When the result is < 0.5 we return zero. This happens when + // x <= floor(log(0.5e18) * 1e18) ~ -42e18 + if (x <= -42139678854452767551) { + return 0; + } + + // When the result is > (2**255 - 1) / 1e18 we can not represent it + // as an int256. This happens when x >= floor(log((2**255 -1) / 1e18) * 1e18) ~ 135. + if (x >= 135305999368893231589) revert Overflow(); + + // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 + // for more intermediate precision and a binary basis. This base conversion + // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. + x = (x << 78) / 5 ** 18; + + // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers of two + // such that exp(x) = exp(x') * 2**k, where k is an integer. + // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). + int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> + 96; + x = x - k * 54916777467707473351141471128; + // k is in the range [-61, 195]. + + // Evaluate using a (6, 7)-term rational approximation + // p is made monic, we will multiply by a scale factor later + int256 p = x + 2772001395605857295435445496992; + p = ((p * x) >> 96) + 44335888930127919016834873520032; + p = ((p * x) >> 96) + 398888492587501845352592340339721; + p = ((p * x) >> 96) + 1993839819670624470859228494792842; + p = p * x + (4385272521454847904632057985693276 << 96); + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + // Evaluate using using Knuth's scheme from p. 491. + int256 z = x + 750530180792738023273180420736; + z = ((z * x) >> 96) + 32788456221302202726307501949080; + int256 w = x - 2218138959503481824038194425854; + w = ((w * z) >> 96) + 892943633302991980437332862907700; + int256 q = z + w - 78174809823045304726920794422040; + q = ((q * w) >> 96) + 4203224763890128580604056984195872; + assembly { + // Div in assembly because solidity adds a zero check despite the `unchecked`. + // The q polynomial is known not to have zeros in the domain. (All roots are complex) + // No scaling required because p is already 2**96 too large. + r := sdiv(p, q) + } + // r should be in the range (0.09, 0.25) * 2**96. + + // We now need to multiply r by + // * the scale factor s = ~6.031367120..., + // * the 2**k factor from the range reduction, and + // * the 1e18 / 2**96 factor for base converison. + // We do all of this at once, with an intermediate result in 2**213 basis + // so the final right shift is always by a positive amount. + r = int( + (uint(r) * 3822833074963236453042738258902158003155416615667) >> + uint256(195 - k) + ); + } + } +} diff --git a/packages/protocol/utils/generate_config/main.py b/packages/protocol/utils/generate_config/main.py index 160dba63e12..ecff83f11c6 100644 --- a/packages/protocol/utils/generate_config/main.py +++ b/packages/protocol/utils/generate_config/main.py @@ -39,4 +39,3 @@ print("---------") print("maxNumProposedBlocks:", max_num_slots) - print("slotSmoothingFactor:", fee_smoothing_factor) diff --git a/packages/relayer/TaikoL1.json b/packages/relayer/TaikoL1.json index f9410f57423..49d131b5910 100644 --- a/packages/relayer/TaikoL1.json +++ b/packages/relayer/TaikoL1.json @@ -516,11 +516,6 @@ "name": "slotSmoothingFactor", "type": "uint256" }, - { - "internalType": "uint256", - "name": "rewardBurnBips", - "type": "uint256" - }, { "internalType": "uint256", "name": "proposerDepositPctg", @@ -528,7 +523,7 @@ }, { "internalType": "uint256", - "name": "feeBaseMAF", + "name": "basefeeMAF", "type": "uint256" }, { @@ -741,7 +736,7 @@ "components": [ { "internalType": "uint256", - "name": "feeBase", + "name": "basefee", "type": "uint256" }, { @@ -826,7 +821,7 @@ }, { "internalType": "uint256", - "name": "_feeBase", + "name": "_basefee", "type": "uint256" } ], @@ -1046,7 +1041,7 @@ }, { "internalType": "uint256", - "name": "feeBase", + "name": "basefee", "type": "uint256" }, { diff --git a/packages/relayer/contracts/taikol1/TaikoL1.go b/packages/relayer/contracts/taikol1/TaikoL1.go index e90754ac72d..f1ae8e258aa 100644 --- a/packages/relayer/contracts/taikol1/TaikoL1.go +++ b/packages/relayer/contracts/taikol1/TaikoL1.go @@ -31,7 +31,7 @@ var ( // LibUtilsStateVariables is an auto generated low-level Go binding around an user-defined struct. type LibUtilsStateVariables struct { - FeeBase *big.Int + Basefee *big.Int GenesisHeight uint64 GenesisTimestamp uint64 NextBlockId uint64 @@ -72,7 +72,7 @@ type TaikoDataConfig struct { SlotSmoothingFactor *big.Int RewardBurnBips *big.Int ProposerDepositPctg *big.Int - FeeBaseMAF *big.Int + BasefeeMAF *big.Int BlockTimeMAF *big.Int ProofTimeMAF *big.Int RewardMultiplierPctg uint64 @@ -103,7 +103,7 @@ type TaikoDataProposedBlock struct { // TaikoL1MetaData contains all meta data concerning the TaikoL1 contract. var TaikoL1MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"L1_0_FEE_BASE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ALREADY_PROVEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_CALLDATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_DEST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_GAS_LIMIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_ADDR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_DATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_LOGS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_STATUS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_TOPICS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_SIG_R\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_SIG_S\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_TX_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_TYPE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_NUMBER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_NUMBER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_CANNOT_BE_FIRST_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_COMMITTED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_CONFLICT_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_CONTRACT_NOT_ALLOWED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_DUP_PROVERS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_EXTRA_DATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_GAS_LIMIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INPUT_SIZE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PARAM\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_METADATA_FIELD\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_META_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_COMMITTED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_ORACLE_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_PROOF_LENGTH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_SOLO_PROPOSER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TOO_MANY_BLOCKS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ZKP\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_ADDR\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"BlockCommitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"commitBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumProposedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumVerifiedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxVerificationsPerTx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitConfirmations\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxTransactionsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBytesPerTxList\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"anchorTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"slotSmoothingFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardBurnBips\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerDepositPctg\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeBaseMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"feeMultiplierPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeGracePeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeMaxPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proofTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"bootstrapDiscountHalvingPeriod\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enableTokenomics\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enablePublicInputsCheck\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enableAnchorValidation\",\"type\":\"bool\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"}],\"name\":\"getForkChoice\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.ForkChoice\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"}],\"name\":\"getProofReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getProposedBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"deposit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.ProposedBlock\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStateVariables\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"feeBase\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastProposedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgBlockTime\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgProofTime\",\"type\":\"uint64\"}],\"internalType\":\"structLibUtils.StateVariables\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisBlockHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_feeBase\",\"type\":\"uint256\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"commitSlot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitHeight\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"isCommitValid\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proposeBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlockInvalid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"k\",\"type\":\"uint8\"}],\"name\":\"signWithGoldenTouch\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"r\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"state\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reservedA1\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reservedA2\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"feeBase\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nextBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastProposedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgBlockTime\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__avgGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgProofTime\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reservedC1\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxBlocks\",\"type\":\"uint256\"}],\"name\":\"verifyBlocks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawBalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"L1_0_FEE_BASE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ALREADY_PROVEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_CALLDATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_DEST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_GAS_LIMIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_ADDR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_DATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_LOGS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_STATUS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_RECEIPT_TOPICS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_SIG_R\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_SIG_S\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_TX_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ANCHOR_TYPE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_NUMBER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_NUMBER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_CANNOT_BE_FIRST_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_COMMITTED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_CONFLICT_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_CONTRACT_NOT_ALLOWED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_DUP_PROVERS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_EXTRA_DATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_GAS_LIMIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INPUT_SIZE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PARAM\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_METADATA_FIELD\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_META_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_COMMITTED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_ORACLE_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_PROOF_LENGTH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_SOLO_PROPOSER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TOO_MANY_BLOCKS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ZKP\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_ADDR\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"BlockCommitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"commitBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumProposedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumVerifiedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxVerificationsPerTx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitConfirmations\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxTransactionsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBytesPerTxList\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"anchorTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"slotSmoothingFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardBurnBips\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerDepositPctg\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"basefeeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"feeMultiplierPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeGracePeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeMaxPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proofTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"bootstrapDiscountHalvingPeriod\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enableTokenomics\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enablePublicInputsCheck\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enableAnchorValidation\",\"type\":\"bool\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"}],\"name\":\"getForkChoice\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.ForkChoice\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"}],\"name\":\"getProofReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getProposedBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"deposit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.ProposedBlock\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStateVariables\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastProposedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgBlockTime\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgProofTime\",\"type\":\"uint64\"}],\"internalType\":\"structLibUtils.StateVariables\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisBlockHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_basefee\",\"type\":\"uint256\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"commitSlot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitHeight\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"isCommitValid\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proposeBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlockInvalid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"k\",\"type\":\"uint8\"}],\"name\":\"signWithGoldenTouch\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"r\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"state\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reservedA1\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reservedA2\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nextBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastProposedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgBlockTime\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__avgGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"avgProofTime\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reservedC1\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxBlocks\",\"type\":\"uint256\"}],\"name\":\"verifyBlocks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawBalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // TaikoL1ABI is the input ABI used to generate the binding from. @@ -738,13 +738,13 @@ func (_TaikoL1 *TaikoL1CallerSession) SignWithGoldenTouch(hash [32]byte, k uint8 // State is a free data retrieval call binding the contract method 0xc19d93fb. // -// Solidity: function state() view returns(uint64 genesisHeight, uint64 genesisTimestamp, uint64 __reservedA1, uint64 __reservedA2, uint256 feeBase, uint64 nextBlockId, uint64 lastProposedAt, uint64 avgBlockTime, uint64 __avgGasLimit, uint64 latestVerifiedHeight, uint64 lastBlockId, uint64 avgProofTime, uint64 __reservedC1) +// Solidity: function state() view returns(uint64 genesisHeight, uint64 genesisTimestamp, uint64 __reservedA1, uint64 __reservedA2, uint256 basefee, uint64 nextBlockId, uint64 lastProposedAt, uint64 avgBlockTime, uint64 __avgGasLimit, uint64 latestVerifiedHeight, uint64 lastBlockId, uint64 avgProofTime, uint64 __reservedC1) func (_TaikoL1 *TaikoL1Caller) State(opts *bind.CallOpts) (struct { GenesisHeight uint64 GenesisTimestamp uint64 ReservedA1 uint64 ReservedA2 uint64 - FeeBase *big.Int + Basefee *big.Int NextBlockId uint64 LastProposedAt uint64 AvgBlockTime uint64 @@ -762,7 +762,7 @@ func (_TaikoL1 *TaikoL1Caller) State(opts *bind.CallOpts) (struct { GenesisTimestamp uint64 ReservedA1 uint64 ReservedA2 uint64 - FeeBase *big.Int + Basefee *big.Int NextBlockId uint64 LastProposedAt uint64 AvgBlockTime uint64 @@ -780,7 +780,7 @@ func (_TaikoL1 *TaikoL1Caller) State(opts *bind.CallOpts) (struct { outstruct.GenesisTimestamp = *abi.ConvertType(out[1], new(uint64)).(*uint64) outstruct.ReservedA1 = *abi.ConvertType(out[2], new(uint64)).(*uint64) outstruct.ReservedA2 = *abi.ConvertType(out[3], new(uint64)).(*uint64) - outstruct.FeeBase = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + outstruct.Basefee = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) outstruct.NextBlockId = *abi.ConvertType(out[5], new(uint64)).(*uint64) outstruct.LastProposedAt = *abi.ConvertType(out[6], new(uint64)).(*uint64) outstruct.AvgBlockTime = *abi.ConvertType(out[7], new(uint64)).(*uint64) @@ -796,13 +796,13 @@ func (_TaikoL1 *TaikoL1Caller) State(opts *bind.CallOpts) (struct { // State is a free data retrieval call binding the contract method 0xc19d93fb. // -// Solidity: function state() view returns(uint64 genesisHeight, uint64 genesisTimestamp, uint64 __reservedA1, uint64 __reservedA2, uint256 feeBase, uint64 nextBlockId, uint64 lastProposedAt, uint64 avgBlockTime, uint64 __avgGasLimit, uint64 latestVerifiedHeight, uint64 lastBlockId, uint64 avgProofTime, uint64 __reservedC1) +// Solidity: function state() view returns(uint64 genesisHeight, uint64 genesisTimestamp, uint64 __reservedA1, uint64 __reservedA2, uint256 basefee, uint64 nextBlockId, uint64 lastProposedAt, uint64 avgBlockTime, uint64 __avgGasLimit, uint64 latestVerifiedHeight, uint64 lastBlockId, uint64 avgProofTime, uint64 __reservedC1) func (_TaikoL1 *TaikoL1Session) State() (struct { GenesisHeight uint64 GenesisTimestamp uint64 ReservedA1 uint64 ReservedA2 uint64 - FeeBase *big.Int + Basefee *big.Int NextBlockId uint64 LastProposedAt uint64 AvgBlockTime uint64 @@ -817,13 +817,13 @@ func (_TaikoL1 *TaikoL1Session) State() (struct { // State is a free data retrieval call binding the contract method 0xc19d93fb. // -// Solidity: function state() view returns(uint64 genesisHeight, uint64 genesisTimestamp, uint64 __reservedA1, uint64 __reservedA2, uint256 feeBase, uint64 nextBlockId, uint64 lastProposedAt, uint64 avgBlockTime, uint64 __avgGasLimit, uint64 latestVerifiedHeight, uint64 lastBlockId, uint64 avgProofTime, uint64 __reservedC1) +// Solidity: function state() view returns(uint64 genesisHeight, uint64 genesisTimestamp, uint64 __reservedA1, uint64 __reservedA2, uint256 basefee, uint64 nextBlockId, uint64 lastProposedAt, uint64 avgBlockTime, uint64 __avgGasLimit, uint64 latestVerifiedHeight, uint64 lastBlockId, uint64 avgProofTime, uint64 __reservedC1) func (_TaikoL1 *TaikoL1CallerSession) State() (struct { GenesisHeight uint64 GenesisTimestamp uint64 ReservedA1 uint64 ReservedA2 uint64 - FeeBase *big.Int + Basefee *big.Int NextBlockId uint64 LastProposedAt uint64 AvgBlockTime uint64 @@ -859,23 +859,23 @@ func (_TaikoL1 *TaikoL1TransactorSession) CommitBlock(commitSlot uint64, commitH // Init is a paid mutator transaction binding the contract method 0x9c5e9f06. // -// Solidity: function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _feeBase) returns() -func (_TaikoL1 *TaikoL1Transactor) Init(opts *bind.TransactOpts, _addressManager common.Address, _genesisBlockHash [32]byte, _feeBase *big.Int) (*types.Transaction, error) { - return _TaikoL1.contract.Transact(opts, "init", _addressManager, _genesisBlockHash, _feeBase) +// Solidity: function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _basefee) returns() +func (_TaikoL1 *TaikoL1Transactor) Init(opts *bind.TransactOpts, _addressManager common.Address, _genesisBlockHash [32]byte, _basefee *big.Int) (*types.Transaction, error) { + return _TaikoL1.contract.Transact(opts, "init", _addressManager, _genesisBlockHash, _basefee) } // Init is a paid mutator transaction binding the contract method 0x9c5e9f06. // -// Solidity: function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _feeBase) returns() -func (_TaikoL1 *TaikoL1Session) Init(_addressManager common.Address, _genesisBlockHash [32]byte, _feeBase *big.Int) (*types.Transaction, error) { - return _TaikoL1.Contract.Init(&_TaikoL1.TransactOpts, _addressManager, _genesisBlockHash, _feeBase) +// Solidity: function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _basefee) returns() +func (_TaikoL1 *TaikoL1Session) Init(_addressManager common.Address, _genesisBlockHash [32]byte, _basefee *big.Int) (*types.Transaction, error) { + return _TaikoL1.Contract.Init(&_TaikoL1.TransactOpts, _addressManager, _genesisBlockHash, _basefee) } // Init is a paid mutator transaction binding the contract method 0x9c5e9f06. // -// Solidity: function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _feeBase) returns() -func (_TaikoL1 *TaikoL1TransactorSession) Init(_addressManager common.Address, _genesisBlockHash [32]byte, _feeBase *big.Int) (*types.Transaction, error) { - return _TaikoL1.Contract.Init(&_TaikoL1.TransactOpts, _addressManager, _genesisBlockHash, _feeBase) +// Solidity: function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _basefee) returns() +func (_TaikoL1 *TaikoL1TransactorSession) Init(_addressManager common.Address, _genesisBlockHash [32]byte, _basefee *big.Int) (*types.Transaction, error) { + return _TaikoL1.Contract.Init(&_TaikoL1.TransactOpts, _addressManager, _genesisBlockHash, _basefee) } // ProposeBlock is a paid mutator transaction binding the contract method 0xa043dbdf. diff --git a/packages/relayer/contracts/taikol2/TaikoL2.go b/packages/relayer/contracts/taikol2/TaikoL2.go index efd19a7c899..041a57d5296 100644 --- a/packages/relayer/contracts/taikol2/TaikoL2.go +++ b/packages/relayer/contracts/taikol2/TaikoL2.go @@ -44,7 +44,7 @@ type TaikoDataConfig struct { SlotSmoothingFactor *big.Int RewardBurnBips *big.Int ProposerDepositPctg *big.Int - FeeBaseMAF *big.Int + BasefeeMAF *big.Int BlockTimeMAF *big.Int ProofTimeMAF *big.Int RewardMultiplierPctg uint64 @@ -60,11 +60,11 @@ type TaikoDataConfig struct { // TaikoL2MetaData contains all meta data concerning the TaikoL2 contract. var TaikoL2MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ERR_INVALID_HINT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERR_INVALID_TX_IDX\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERR_PARAMS_NOT_DEFAULTS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERR_VERIFICAITON_FAILURE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_INVALID_CHAIN_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_INVALID_GAS_PRICE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_INVALID_SENDER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_PUBLIC_INPUT_HASH_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_ADDR\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"}],\"name\":\"BlockInvalidated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"}],\"name\":\"anchor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumProposedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumVerifiedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxVerificationsPerTx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitConfirmations\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxTransactionsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBytesPerTxList\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"anchorTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"slotSmoothingFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardBurnBips\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerDepositPctg\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeBaseMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"rewardMultiplierPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeGracePeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeMaxPeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proofTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"bootstrapDiscountHalvingPeriod\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enableTokenomics\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enablePublicInputsCheck\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enableAnchorValidation\",\"type\":\"bool\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"},{\"internalType\":\"enumLibInvalidTxList.Hint\",\"name\":\"hint\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"txIdx\",\"type\":\"uint256\"}],\"name\":\"invalidateBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestSyncedL1Height\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ERR_INVALID_HINT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERR_INVALID_TX_IDX\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERR_PARAMS_NOT_DEFAULTS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERR_VERIFICAITON_FAILURE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_INVALID_CHAIN_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_INVALID_GAS_PRICE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_INVALID_SENDER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L2_PUBLIC_INPUT_HASH_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_ADDR\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"}],\"name\":\"BlockInvalidated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"}],\"name\":\"anchor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumProposedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumVerifiedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxVerificationsPerTx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitConfirmations\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxTransactionsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBytesPerTxList\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"anchorTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"slotSmoothingFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardBurnBips\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerDepositPctg\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"basefeeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"rewardMultiplierPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeGracePeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeMaxPeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proofTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"bootstrapDiscountHalvingPeriod\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enableTokenomics\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enablePublicInputsCheck\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enableAnchorValidation\",\"type\":\"bool\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"},{\"internalType\":\"enumLibInvalidTxList.Hint\",\"name\":\"hint\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"txIdx\",\"type\":\"uint256\"}],\"name\":\"invalidateBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestSyncedL1Height\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // TaikoL2ABI is the input ABI used to generate the binding from. -const TaikoL2ABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"}],\"name\":\"BlockInvalidated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"height\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"}],\"name\":\"anchor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumProposedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumVerifiedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"zkProofsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxVerificationsPerTx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitConfirmations\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxProofsPerForkChoice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxTransactionsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBytesPerTxList\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"anchorTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feePremiumLamda\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardBurnBips\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerDepositPctg\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeBaseMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"feeMultiplierPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeGracePeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeMaxPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proofTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"bootstrapDiscountHalvingPeriod\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initialUncleDelay\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enableTokenomics\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enablePublicInputsCheck\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enableAnchorValidation\",\"type\":\"bool\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"},{\"internalType\":\"enumLibInvalidTxList.Reason\",\"name\":\"hint\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"txIdx\",\"type\":\"uint256\"}],\"name\":\"invalidateBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestSyncedL1Height\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"publicInputHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" +const TaikoL2ABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"}],\"name\":\"BlockInvalidated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"height\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"}],\"name\":\"anchor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumProposedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxNumVerifiedBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"zkProofsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxVerificationsPerTx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitConfirmations\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxProofsPerForkChoice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxTransactionsPerBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBytesPerTxList\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"anchorTxGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feePremiumLamda\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardBurnBips\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerDepositPctg\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"basefeeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimeMAF\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"feeMultiplierPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeGracePeriodPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"feeMaxPctg\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proofTimeCap\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"bootstrapDiscountHalvingPeriod\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initialUncleDelay\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enableTokenomics\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enablePublicInputsCheck\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enableAnchorValidation\",\"type\":\"bool\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"},{\"internalType\":\"enumLibInvalidTxList.Reason\",\"name\":\"hint\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"txIdx\",\"type\":\"uint256\"}],\"name\":\"invalidateBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestSyncedL1Height\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"publicInputHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" // TaikoL2 is an auto generated Go binding around an Ethereum contract. type TaikoL2 struct { diff --git a/packages/status-page/src/constants/abi/TaikoL1.ts b/packages/status-page/src/constants/abi/TaikoL1.ts index 555ddec6060..2220bc4637f 100644 --- a/packages/status-page/src/constants/abi/TaikoL1.ts +++ b/packages/status-page/src/constants/abi/TaikoL1.ts @@ -533,7 +533,7 @@ export default [ }, { internalType: "uint256", - name: "feeBaseMAF", + name: "basefeeMAF", type: "uint256", }, { @@ -746,7 +746,7 @@ export default [ components: [ { internalType: "uint256", - name: "feeBase", + name: "basefee", type: "uint256", }, { @@ -831,7 +831,7 @@ export default [ }, { internalType: "uint256", - name: "_feeBase", + name: "_basefee", type: "uint256", }, ], @@ -1051,7 +1051,7 @@ export default [ }, { internalType: "uint256", - name: "feeBase", + name: "basefee", type: "uint256", }, { diff --git a/packages/status-page/src/pages/home/Home.svelte b/packages/status-page/src/pages/home/Home.svelte index 99dc7e15261..26e1d5e78f5 100644 --- a/packages/status-page/src/pages/home/Home.svelte +++ b/packages/status-page/src/pages/home/Home.svelte @@ -293,7 +293,7 @@ ) => { const stateVars = await getStateVariables(provider, address); return `${truncateString( - ethers.utils.formatEther(stateVars.feeBase), + ethers.utils.formatEther(stateVars.basefee), 6 )} ${feeTokenSymbol}`; }, diff --git a/packages/tokenomics/main.py b/packages/tokenomics/main.py index f637c36bf84..4715352c779 100644 --- a/packages/tokenomics/main.py +++ b/packages/tokenomics/main.py @@ -96,7 +96,7 @@ def setup(self, config): def get_time_adjusted_fee(self, is_proposal, t_now, t_last, t_avg, t_cap): # if (tAvg == 0) { - # return s.feeBase; + # return s.basefee; # } # uint256 _tAvg = tAvg > tCap ? tCap : tAvg; # uint256 tGrace = (LibConstants.K_FEE_GRACE_PERIOD * _tAvg) / 100; @@ -108,9 +108,9 @@ def get_time_adjusted_fee(self, is_proposal, t_now, t_last, t_avg, t_cap): # ((LibConstants.K_REWARD_MULTIPLIER - 100) * tRel) / # 100; # if (isProposal) { - # return (s.feeBase * 10000) / alpha; // fee + # return (s.basefee * 10000) / alpha; // fee # } else { - # return (s.feeBase * alpha) / 10000; // reward + # return (s.basefee * alpha) / 10000; // reward # } if t_avg == 0: diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 15461d88607..a50853b0f08 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -4,15 +4,6 @@ title: TaikoData ## TaikoData -### FeeConfig - -```solidity -struct FeeConfig { - uint16 avgTimeMAF; - uint16 dampingFactorBips; -} -``` - ### Config ```solidity @@ -23,25 +14,16 @@ struct Config { uint256 maxNumVerifiedBlocks; uint256 maxVerificationsPerTx; uint256 blockMaxGasLimit; - uint256 gasPoolProduct; uint256 maxTransactionsPerBlock; uint256 maxBytesPerTxList; uint256 minTxGasLimit; - uint256 slotSmoothingFactor; - uint256 rewardBurnBips; - uint256 proposerDepositPctg; - uint256 feeBaseMAF; - uint64 constantFeeRewardBlocks; - uint64 txListCacheExpiry; + uint256 txListCacheExpiry; uint64 proofTimeTarget; uint8 adjustmentQuotient; bool enableSoloProposer; bool enableOracleProver; bool enableTokenomics; bool skipZKPVerification; - bool allowMinting; - bool useTimeWeightedReward; - struct TaikoData.FeeConfig provingConfig; } ``` @@ -49,13 +31,12 @@ struct Config { ```solidity struct StateVariables { - uint64 feeBase; + uint64 basefee; + uint64 rewardPool; uint64 genesisHeight; uint64 genesisTimestamp; uint64 numBlocks; uint64 lastVerifiedBlockId; - uint64 avgBlockTime; - uint64 avgProofTime; uint64 lastProposedAt; } ``` @@ -80,7 +61,6 @@ struct BlockMetadata { uint64 id; uint64 timestamp; uint64 l1Height; - uint64 l2Basefee; bytes32 l1Hash; bytes32 mixHash; bytes32 txListHash; @@ -88,7 +68,6 @@ struct BlockMetadata { uint24 txListByteEnd; uint32 gasLimit; address beneficiary; - address treasure; } ``` @@ -112,6 +91,7 @@ struct BlockEvidence { bytes32 signalRoot; bytes32 graffiti; address prover; + uint32 gasUsed; } ``` @@ -122,6 +102,7 @@ struct ForkChoice { bytes32 blockHash; bytes32 signalRoot; uint64 provenAt; + uint32 gasUsed; address prover; } ``` @@ -134,7 +115,6 @@ struct Block { uint64 blockId; uint64 proposedAt; uint64 deposit; - uint32 gasConsumed; uint24 nextForkChoiceId; uint24 verifiedForkChoiceId; bytes32 metaHash; @@ -161,16 +141,16 @@ struct State { mapping(bytes32 => struct TaikoData.TxListInfo) txListInfo; uint64 genesisHeight; uint64 genesisTimestamp; - uint64 __reserved1; - uint64 __reserved2; - uint64 numBlocks; + uint64 __reserved51; + uint64 __reserved52; uint64 lastProposedAt; - uint64 avgBlockTime; - uint64 __reserved3; + uint64 numBlocks; + uint64 accProposedAt; + uint64 rewardPool; + uint64 basefee; + uint64 proofTimeIssued; uint64 lastVerifiedBlockId; - uint64 __reserved4; - uint64 avgProofTime; - uint64 feeBase; + uint64 __reserved81; uint256[43] __gap; } ``` diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index 1883a036f17..4028d4ecf94 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -4,18 +4,6 @@ title: TaikoErrors ## TaikoErrors -### L1_1559_X_SCALE_TOO_LARGE - -```solidity -error L1_1559_X_SCALE_TOO_LARGE() -``` - -### L1_1559_Y_SCALE_TOO_LARGE - -```solidity -error L1_1559_Y_SCALE_TOO_LARGE() -``` - ### L1_ALREADY_PROVEN ```solidity @@ -46,12 +34,6 @@ error L1_EVIDENCE_MISMATCH() error L1_FORK_CHOICE_NOT_FOUND() ``` -### L1_INSUFFICIENT_ETHER - -```solidity -error L1_INSUFFICIENT_ETHER() -``` - ### L1_INSUFFICIENT_TOKEN ```solidity @@ -70,12 +52,6 @@ error L1_INVALID_CONFIG() error L1_INVALID_EVIDENCE() ``` -### L1_INVALID_L21559_PARAMS - -```solidity -error L1_INVALID_L21559_PARAMS() -``` - ### L1_INVALID_METADATA ```solidity @@ -106,12 +82,6 @@ error L1_NOT_ORACLE_PROVER() error L1_NOT_SOLO_PROPOSER() ``` -### L1_OUT_OF_BLOCK_SPACE - -```solidity -error L1_OUT_OF_BLOCK_SPACE() -``` - ### L1_TOO_MANY_BLOCKS ```solidity diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index a946fe8962c..643679a7800 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -13,18 +13,17 @@ struct TaikoData.State state ### init ```solidity -function init(address _addressManager, uint64 _feeBase, bytes32 _genesisBlockHash) external +function init(address _addressManager, bytes32 _genesisBlockHash) external ``` Initialize the rollup. #### Parameters -| Name | Type | Description | -| ------------------ | ------- | ------------------------------------------------------------ | -| \_addressManager | address | The AddressManager address. | -| \_feeBase | uint64 | The initial value of the proposer-fee/prover-reward feeBase. | -| \_genesisBlockHash | bytes32 | The block hash of the genesis block. | +| Name | Type | Description | +| ------------------ | ------- | ------------------------------------ | +| \_addressManager | address | The AddressManager address. | +| \_genesisBlockHash | bytes32 | The block hash of the genesis block. | ### proposeBlock @@ -92,13 +91,13 @@ function getBalance(address addr) public view returns (uint256) ### getProverFee ```solidity -function getProverFee() public view returns (uint256 feeAmount, uint256 depositAmount) +function getProverFee() public view returns (uint64 feeAmount) ``` ### getProofReward ```solidity -function getProofReward(uint64 provenAt, uint64 proposedAt) public view returns (uint256 reward) +function getProofReward(uint64 provenAt, uint64 proposedAt, uint32 gasUsed) public view returns (uint256 reward) ``` ### getBlock diff --git a/packages/website/pages/docs/reference/contract-documentation/L2/LibL2Consts.md b/packages/website/pages/docs/reference/contract-documentation/L2/LibL2Consts.md new file mode 100644 index 00000000000..6b5bb2b750c --- /dev/null +++ b/packages/website/pages/docs/reference/contract-documentation/L2/LibL2Consts.md @@ -0,0 +1,11 @@ +--- +title: LibL2Consts +--- + +## LibL2Consts + +### ANCHOR_GAS_COST + +```solidity +uint32 ANCHOR_GAS_COST +``` From 10aa351826c0074c5304e03b003e6253050de1a8 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Mon, 17 Apr 2023 12:49:19 +0200 Subject: [PATCH 83/90] Function modifier and shadow declaration in test fix --- .../contracts/L1/libs/LibTokenomics.sol | 6 ++-- packages/protocol/test2/LibTokenomics.t.sol | 33 +++++++++++-------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol index 34171c0974b..a7918733777 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -84,7 +84,7 @@ library LibTokenomics { returns (uint64 reward, uint64 newProofTimeIssued, uint64 newBasefee) { uint64 proofTimeIssued = state.proofTimeIssued; - // To protect underflow + proofTimeIssued = (proofTimeIssued > config.proofTimeTarget) ? proofTimeIssued - config.proofTimeTarget : uint64(0); @@ -148,7 +148,7 @@ library LibTokenomics { uint256 value, uint256 target, uint256 quotient - ) private view returns (uint64) { + ) private pure returns (uint64) { uint256 result = _expCalculation(value, target, quotient) / (target * quotient); @@ -165,7 +165,7 @@ library LibTokenomics { uint256 value, uint256 target, uint256 quotient - ) private view returns (uint256 retVal) { + ) private pure returns (uint256 retVal) { uint256 x = (value * Math.SCALING_FACTOR_1E18) / (target * quotient); return (uint256(Math.exp(int256(x))) / Math.SCALING_FACTOR_1E18); diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index 5ddafa04b0e..cc617768f1b 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -142,10 +142,10 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { _depositTaikoToken(Bob, 1E8 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E8 * 1E8, 100 ether); - TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( + TaikoData.BlockMetadata[] memory metas = new TaikoData.BlockMetadata[]( 20 ); - uint64[] memory proposedAt = new uint64[](20); + uint64[] memory proposedAtArr = new uint64[](20); bytes32 parentHash = GENESIS_BLOCK_HASH; @@ -158,8 +158,8 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 20; blockId++) { //printVariables("before propose"); - meta[blockId] = proposeBlock(Alice, 1024); - proposedAt[blockId] = (uint64(block.timestamp)); + metas[blockId] = proposeBlock(Alice, 1024); + proposedAtArr[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); } @@ -170,11 +170,11 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < 20; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + proveBlock(Bob, metas[blockId], parentHash, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", - L1.getProofReward(provenAt, proposedAt[blockId]) + L1.getProofReward(provenAt, proposedAtArr[blockId]) ); verifyBlock(Carol, 1); @@ -636,10 +636,9 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { _depositTaikoToken(Bob, 1E8 * 1E8, 100 ether); _depositTaikoToken(Carol, 1E8 * 1E8, 100 ether); - TaikoData.BlockMetadata[] memory meta = new TaikoData.BlockMetadata[]( - 20 - ); - uint64[] memory proposedAt = new uint64[](20); + TaikoData.BlockMetadata[] + memory metaArr = new TaikoData.BlockMetadata[](20); + uint64[] memory proposedAtArr = new uint64[](20); bytes32 parentHash = GENESIS_BLOCK_HASH; @@ -652,8 +651,8 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 20; blockId++) { //printVariables("before propose"); - meta[blockId] = proposeBlock(Alice, 1024); - proposedAt[blockId] = (uint64(block.timestamp)); + metaArr[blockId] = proposeBlock(Alice, 1024); + proposedAtArr[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); } @@ -664,12 +663,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < 20; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + proveBlock( + Bob, + metaArr[blockId], + parentHash, + blockHash, + signalRoot + ); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", - L1.getProofReward(provenAt, proposedAt[blockId]) + L1.getProofReward(provenAt, proposedAtArr[blockId]) ); verifyBlock(Carol, 1); From 5a4b51b70e9c847ac1d6e9f235662e47f2c90810 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 18 Apr 2023 07:13:01 +0200 Subject: [PATCH 84/90] Resolve merge conflicts with base --- .../protocol/contracts/L1/TaikoConfig.sol | 5 +- packages/protocol/contracts/L1/TaikoData.sol | 28 +- packages/protocol/contracts/L1/TaikoL1.sol | 30 +- .../contracts/L1/libs/LibProposing.sol | 24 +- .../protocol/contracts/L1/libs/LibProving.sol | 258 ++++++++++++------ .../contracts/L1/libs/LibTokenomics.sol | 4 +- .../protocol/contracts/L1/libs/LibUtils.sol | 42 ++- .../contracts/L1/libs/LibVerifying.sol | 29 +- .../protocol/contracts/L2/LibL2Consts.sol | 3 +- packages/protocol/contracts/L2/TaikoL2.sol | 66 ++--- .../protocol/contracts/libs/Lib1559Math.sol | 2 +- .../contracts/test/L1/TestTaikoL1.sol | 10 + .../test/L1/TestTaikoL1EnableTokenomics.sol | 11 +- packages/protocol/foundry.toml | 20 +- packages/protocol/script/DeployOnL1.s.sol | 64 ++++- packages/protocol/script/deploy_on_l1.sh | 8 +- packages/protocol/test2/Lib1559Math.sol | 8 +- packages/protocol/test2/LibTokenomics.t.sol | 118 ++++++-- packages/protocol/test2/TaikoL1.t.sol | 108 +++++++- packages/protocol/test2/TaikoL1TestBase.t.sol | 49 +++- packages/protocol/test2/TaikoL2.t.sol | 120 ++++---- .../status-page/src/pages/home/Home.svelte | 149 ++++++---- .../contract-documentation/L1/TaikoData.md | 53 +++- 23 files changed, 875 insertions(+), 334 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 707c866b957..fde54c2de58 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -17,13 +17,13 @@ library TaikoConfig { maxNumProposedBlocks: 120960, ringBufferSize: 120960 + 10, maxNumVerifiedBlocks: 4096, - //Each time one more block is verified, there will be ~20k + // Each time one more block is verified, there will be ~20k // more gas cost. maxVerificationsPerTx: 10, // Set it to 6M, since its the upper limit of the Alpha-2 // testnet's circuits. blockMaxGasLimit: 6000000, - // Set it to 79 (+1 TaikoL2.anchor transaction = 80), + // Set it to 79 (+1 TaikoL2.anchor transaction = 80), // and 80 is the upper limit of the Alpha-2 testnet's circuits. maxTransactionsPerBlock: 79, // Set it to 120KB, since 128KB is the upper size limit @@ -35,6 +35,7 @@ library TaikoConfig { txListCacheExpiry: 0, proofTimeTarget: 1800, // 85s based on A2 testnet status, or set to 1800 for 30mins (mainnet mock) adjustmentQuotient: 16, + relaySignalRoot: false, enableSoloProposer: false, enableOracleProver: true, enableTokenomics: true, diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index ae6cbd309dd..4d466968298 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -23,6 +23,7 @@ library TaikoData { uint256 txListCacheExpiry; uint64 proofTimeTarget; uint8 adjustmentQuotient; + bool relaySignalRoot; bool enableSoloProposer; bool enableOracleProver; bool enableTokenomics; @@ -50,7 +51,7 @@ library TaikoData { } // 6 slots - // Changing this struct requires chaing LibUtils.hashMetadata accordingly. + // Changing this struct requires changing LibUtils.hashMetadata accordingly. struct BlockMetadata { uint64 id; uint64 timestamp; @@ -62,6 +63,8 @@ library TaikoData { uint24 txListByteEnd; uint32 gasLimit; address beneficiary; + uint8 cacheTxListInfo; + address treasure; } struct ZKProof { @@ -77,16 +80,30 @@ library TaikoData { bytes32 signalRoot; bytes32 graffiti; address prover; + uint32 parentGasUsed; uint32 gasUsed; } - // 3 slots + struct BlockOracle { + bytes32 blockHash; + uint32 gasUsed; + bytes32 signalRoot; + } + + struct BlockOracles { + bytes32 parentHash; + uint32 parentGasUsed; + BlockOracle[] blks; + } + + // 4 slots struct ForkChoice { + bytes32 key; // only written/read for the 1st fork choice. bytes32 blockHash; bytes32 signalRoot; uint64 provenAt; - uint32 gasUsed; address prover; + uint32 gasUsed; } // 4 slots @@ -111,11 +128,8 @@ library TaikoData { struct State { // Ring buffer for proposed blocks and a some recent verified blocks. mapping(uint256 blockId_mode_ringBufferSize => Block) blocks; - // A mapping from (blockId, parentHash) to a reusable ForkChoice storage pointer. // solhint-disable-next-line max-line-length - mapping(uint256 blockId => mapping(bytes32 parentHash => uint256 forkChoiceId)) forkChoiceIds; - // TODO(dani): change to: - // mapping(address account => uint64 balance) balances; + mapping(uint256 blockId => mapping(bytes32 parentHash => mapping(uint32 parentGasUsed => uint256 forkChoiceId))) forkChoiceIds; mapping(address account => uint256 balance) balances; mapping(bytes32 txListHash => TxListInfo) txListInfo; // Slot 5: never or rarely changed diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 9ce277ef5c5..7e13431b9c5 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -75,11 +75,32 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { LibVerifying.verifyBlocks({ state: state, config: config, + resolver: AddressResolver(this), maxBlocks: config.maxVerificationsPerTx }); } } + /** + * Oracle prove mutliple blocks in a row. + * + * @param blockId The index of the first block to prove. This is also used + * to select the right implementation version. + * @param input An abi-encoded TaikoData.BlockOracle[] object. + */ + function oracleProveBlocks( + uint256 blockId, + bytes calldata input + ) external nonReentrant { + LibProving.oracleProveBlocks({ + state: state, + config: getConfig(), + blockId: blockId, + resolver: AddressResolver(this), + oracles: abi.decode(input, (TaikoData.BlockOracles)) + }); + } + /** * Prove a block is valid with a zero-knowledge proof, a transaction * merkel proof, and a receipt merkel proof. @@ -88,7 +109,6 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { * to select the right implementation version. * @param input An abi-encoded TaikoData.ValidBlockEvidence object. */ - function proveBlock( uint256 blockId, bytes calldata input @@ -105,6 +125,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { LibVerifying.verifyBlocks({ state: state, config: config, + resolver: AddressResolver(this), maxBlocks: config.maxVerificationsPerTx }); } @@ -119,6 +140,7 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { LibVerifying.verifyBlocks({ state: state, config: getConfig(), + resolver: AddressResolver(this), maxBlocks: maxBlocks }); } @@ -176,14 +198,16 @@ contract TaikoL1 is EssentialContract, IXchainSync, TaikoEvents, TaikoErrors { function getForkChoice( uint256 blockId, - bytes32 parentHash + bytes32 parentHash, + uint32 parentGasUsed ) public view returns (TaikoData.ForkChoice memory) { return LibProving.getForkChoice({ state: state, config: getConfig(), blockId: blockId, - parentHash: parentHash + parentHash: parentHash, + parentGasUsed: parentGasUsed }); } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index f8b634f6cc8..6027326dc01 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -8,7 +8,6 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibAddress} from "../../libs/LibAddress.sol"; -import {LibL2Consts} from "../../L2/LibL2Consts.sol"; import {LibTokenomics} from "./LibTokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { @@ -22,13 +21,10 @@ library LibProposing { using LibAddress for address payable; using LibUtils for TaikoData.State; - event BlockProposed( - uint256 indexed id, - TaikoData.BlockMetadata meta, - bool txListCached - ); + event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta); error L1_BLOCK_ID(); + error L1_INSUFFICIENT_ETHER(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_METADATA(); error L1_NOT_SOLO_PROPOSER(); @@ -45,7 +41,7 @@ library LibProposing { TaikoData.BlockMetadataInput memory input, bytes calldata txList ) internal { - bool cacheTxList = _validateBlock({ + uint8 cacheTxListInfo = _validateBlock({ state: state, config: config, resolver: resolver, @@ -53,7 +49,7 @@ library LibProposing { txList: txList }); - if (cacheTxList) { + if (cacheTxListInfo != 0) { state.txListInfo[input.txListHash] = TaikoData.TxListInfo({ validSince: uint64(block.timestamp), size: uint24(txList.length) @@ -76,7 +72,9 @@ library LibProposing { txListByteStart: input.txListByteStart, txListByteEnd: input.txListByteEnd, gasLimit: input.gasLimit, - beneficiary: input.beneficiary + beneficiary: input.beneficiary, + treasure: resolver.resolve(config.chainId, "treasure", false), + cacheTxListInfo: cacheTxListInfo }); } @@ -105,7 +103,7 @@ library LibProposing { } } - emit BlockProposed(state.numBlocks, meta, cacheTxList); + emit BlockProposed(state.numBlocks, meta); unchecked { ++state.numBlocks; state.lastProposedAt = meta.timestamp; @@ -127,7 +125,7 @@ library LibProposing { AddressResolver resolver, TaikoData.BlockMetadataInput memory input, bytes calldata txList - ) private view returns (bool cacheTxList) { + ) private view returns (uint8 cacheTxListInfo) { // For alpha-2 testnet, the network only allows an special address // to propose but anyone to prove. This is the first step of testing // the tokenomics. @@ -138,7 +136,7 @@ library LibProposing { if ( input.beneficiary == address(0) || - input.gasLimit < LibL2Consts.ANCHOR_GAS_COST || + input.gasLimit == 0 || input.gasLimit > config.blockMaxGasLimit ) revert L1_INVALID_METADATA(); @@ -180,7 +178,7 @@ library LibProposing { if (input.txListHash != keccak256(txList)) revert L1_TX_LIST_HASH(); - cacheTxList = (input.cacheTxListInfo != 0); + cacheTxListInfo = input.cacheTxListInfo; } } } diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index fd30f4000e9..f262842dfbf 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -22,23 +22,90 @@ library LibProving { address prover ); - event ConflictingProof( - uint64 blockId, - bytes32 parentHash, - bytes32 conflictingBlockHash, - bytes32 conflictingSignalRoot, - bytes32 blockHash, - bytes32 signalRoot - ); - error L1_ALREADY_PROVEN(); error L1_BLOCK_ID(); - error L1_EVIDENCE_MISMATCH(); + error L1_EVIDENCE_MISMATCH(bytes32 expected, bytes32 actual); error L1_FORK_CHOICE_NOT_FOUND(); error L1_INVALID_PROOF(); error L1_INVALID_EVIDENCE(); + error L1_INVALID_ORACLE(); + error L1_ORACLE_DISABLED(); + error L1_NOT_ORACLE_PROVEN(); error L1_NOT_ORACLE_PROVER(); error L1_UNEXPECTED_FORK_CHOICE_ID(); + error L1_CONFLICTING_PROOF( + uint64 id, + uint32 parentGasUsed, + bytes32 parentHash, + bytes32 conflictingBlockHash, + bytes32 conflictingSignalRoot, + bytes32 blockHash, + bytes32 signalRoot + ); + + function oracleProveBlocks( + TaikoData.State storage state, + TaikoData.Config memory config, + AddressResolver resolver, + uint256 blockId, + TaikoData.BlockOracles memory oracles + ) internal { + if (!config.enableOracleProver) revert L1_ORACLE_DISABLED(); + if (msg.sender != resolver.resolve("oracle_prover", false)) + revert L1_NOT_ORACLE_PROVER(); + + bytes32 parentHash = oracles.parentHash; + uint32 parentGasUsed = oracles.parentGasUsed; + + for (uint i = 0; i < oracles.blks.length; ) { + uint256 id = blockId + i; + + if (id <= state.lastVerifiedBlockId || id >= state.numBlocks) + revert L1_BLOCK_ID(); + + TaikoData.BlockOracle memory oracle = oracles.blks[i]; + if ( + oracle.blockHash == 0 || + oracle.blockHash == parentHash || + oracle.signalRoot == 0 || + oracle.gasUsed == 0 + ) revert L1_INVALID_ORACLE(); + + TaikoData.Block storage blk = state.blocks[ + id % config.ringBufferSize + ]; + + uint256 fcId = LibUtils.getForkChoiceId( + state, + blk, + parentHash, + parentGasUsed + ); + + if (fcId == 0) { + fcId = _getNextForkChoiceId(blk); + } + + _saveForkChoice({ + state: state, + config: config, + blk: blk, + fcId: fcId, + parentHash: parentHash, + parentGasUsed: parentGasUsed, + blockHash: oracle.blockHash, + signalRoot: oracle.signalRoot, + gasUsed: oracle.gasUsed, + prover: address(0) + }); + + unchecked { + ++i; + parentHash = oracle.blockHash; + parentGasUsed = oracle.gasUsed; + } + } + } function proveBlock( TaikoData.State storage state, @@ -57,7 +124,6 @@ library LibProving { if ( evidence.parentHash == 0 || evidence.blockHash == 0 || - // cannot be the same hash evidence.blockHash == evidence.parentHash || evidence.signalRoot == 0 || // prover must not be zero @@ -69,67 +135,35 @@ library LibProving { meta.id % config.ringBufferSize ]; - if (blk.metaHash != LibUtils.hashMetadata(meta)) - revert L1_EVIDENCE_MISMATCH(); + bytes32 _metaHash = LibUtils.hashMetadata(meta); + if (blk.metaHash != _metaHash) + revert L1_EVIDENCE_MISMATCH(blk.metaHash, _metaHash); - TaikoData.ForkChoice storage fc; - bool oracleProving; + uint256 fcId = LibUtils.getForkChoiceId( + state, + blk, + evidence.parentHash, + evidence.parentGasUsed + ); - uint256 fcId = state.forkChoiceIds[blockId][evidence.parentHash]; if (fcId == 0) { - fcId = blk.nextForkChoiceId; - unchecked { - ++blk.nextForkChoiceId; - } - - assert(fcId > 0); - state.forkChoiceIds[blockId][evidence.parentHash] = fcId; - fc = blk.forkChoices[fcId]; - fc.blockHash = evidence.blockHash; - fc.signalRoot = evidence.signalRoot; - - if (config.enableOracleProver) { - if (msg.sender != resolver.resolve("oracle_prover", false)) - revert L1_NOT_ORACLE_PROVER(); - - oracleProving = true; - // we are reusing storage slots, still need to reset the - // [provenAt+prover] slot. - fc.provenAt = uint64(1); - fc.prover = address(0); - fc.gasUsed = 0; - } else { - fc.provenAt = uint64(block.timestamp); - fc.prover = evidence.prover; - fc.gasUsed = evidence.gasUsed; - } - } else { - assert(fcId < blk.nextForkChoiceId); - fc = blk.forkChoices[fcId]; - - if ( - fc.blockHash != evidence.blockHash || - fc.signalRoot != evidence.signalRoot - ) { - emit ConflictingProof({ - blockId: meta.id, - parentHash: evidence.parentHash, - conflictingBlockHash: evidence.blockHash, - conflictingSignalRoot: evidence.signalRoot, - blockHash: fc.blockHash, - signalRoot: fc.signalRoot - }); - return; - } - - if (fc.prover != address(0)) revert L1_ALREADY_PROVEN(); - - fc.provenAt = uint64(block.timestamp); - fc.prover = evidence.prover; - fc.gasUsed = evidence.gasUsed; + if (config.enableOracleProver) revert L1_NOT_ORACLE_PROVEN(); + fcId = _getNextForkChoiceId(blk); } + _saveForkChoice({ + state: state, + config: config, + blk: blk, + fcId: fcId, + parentHash: evidence.parentHash, + parentGasUsed: evidence.parentGasUsed, + blockHash: evidence.blockHash, + signalRoot: evidence.signalRoot, + gasUsed: evidence.gasUsed, + prover: evidence.prover + }); - if (!oracleProving && !config.skipZKPVerification) { + if (!config.skipZKPVerification) { bytes32 instance; { // otherwise: stack too deep @@ -158,7 +192,8 @@ library LibProving { inputs[6] = uint256(evidence.graffiti); inputs[7] = (uint256(uint160(evidence.prover)) << 96) | - (uint256(evidence.gasUsed) << 64); + (uint256(evidence.parentGasUsed) << 64) | + (uint256(evidence.gasUsed) << 32); inputs[8] = uint256(blk.metaHash); assembly { @@ -181,32 +216,95 @@ library LibProving { bytes32(ret) != keccak256("taiko") ) revert L1_INVALID_PROOF(); } - - emit BlockProven({ - id: blockId, - parentHash: evidence.parentHash, - blockHash: evidence.blockHash, - signalRoot: evidence.signalRoot, - prover: evidence.prover - }); } function getForkChoice( TaikoData.State storage state, TaikoData.Config memory config, uint256 blockId, - bytes32 parentHash + bytes32 parentHash, + uint32 parentGasUsed ) internal view returns (TaikoData.ForkChoice storage) { TaikoData.Block storage blk = state.blocks[ blockId % config.ringBufferSize ]; if (blk.blockId != blockId) revert L1_BLOCK_ID(); - uint256 fcId = state.forkChoiceIds[blockId][parentHash]; + uint256 fcId = LibUtils.getForkChoiceId( + state, + blk, + parentHash, + parentGasUsed + ); if (fcId == 0) revert L1_FORK_CHOICE_NOT_FOUND(); - assert(fcId < blk.nextForkChoiceId); - return blk.forkChoices[fcId]; } + + function _saveForkChoice( + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.Block storage blk, + uint256 fcId, + bytes32 parentHash, + uint32 parentGasUsed, + bytes32 blockHash, + bytes32 signalRoot, + uint32 gasUsed, + address prover + ) private { + TaikoData.ForkChoice storage fc = blk.forkChoices[fcId]; + if (fcId == 1) { + // We only write the key when fcId is 1. + fc.key = LibUtils.keyForForkChoice(parentHash, parentGasUsed); + state.forkChoiceIds[blk.blockId][parentHash][parentGasUsed] = 0; + } else { + state.forkChoiceIds[blk.blockId][parentHash][parentGasUsed] = fcId; + } + + if (prover != address(0) && config.enableOracleProver) { + // This is a regular proof after the oracle proof + if (fc.prover != address(0)) revert L1_ALREADY_PROVEN(); + + if ( + fc.blockHash != blockHash || + fc.signalRoot != signalRoot || + fc.gasUsed != gasUsed + ) + revert L1_CONFLICTING_PROOF({ + id: blk.blockId, + parentGasUsed: parentGasUsed, + parentHash: parentHash, + conflictingBlockHash: blockHash, + conflictingSignalRoot: signalRoot, + blockHash: fc.blockHash, + signalRoot: fc.signalRoot + }); + } else { + // oracle proof or enableOracleProver is disabled + fc.blockHash = blockHash; + fc.signalRoot = signalRoot; + fc.gasUsed = gasUsed; + } + + fc.provenAt = uint64(block.timestamp); + fc.prover = prover; + + emit BlockProven({ + id: blk.blockId, + parentHash: parentHash, + blockHash: blockHash, + signalRoot: signalRoot, + prover: prover + }); + } + + function _getNextForkChoiceId( + TaikoData.Block storage blk + ) private returns (uint256 fcId) { + fcId = blk.nextForkChoiceId; + unchecked { + ++blk.nextForkChoiceId; + } + } } diff --git a/packages/protocol/contracts/L1/libs/LibTokenomics.sol b/packages/protocol/contracts/L1/libs/LibTokenomics.sol index a7918733777..4eb7afaf2b4 100644 --- a/packages/protocol/contracts/L1/libs/LibTokenomics.sol +++ b/packages/protocol/contracts/L1/libs/LibTokenomics.sol @@ -117,8 +117,8 @@ library LibTokenomics { ) ); - // // todo:(dani) Validate algo and check which seems best among the 3 - // Can stay as is for now - until simulation validates which might be better! + // // todo:(dani) + // Can stay as is for now - until simulation validates which might be better long term. // reward_opt2 = uint64( // ( // uint256( diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 7f6c8959cf6..4813896ce07 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -28,6 +28,25 @@ library LibUtils { found = (blk.blockId == id && blk.verifiedForkChoiceId != 0); } + function getForkChoiceId( + TaikoData.State storage state, + TaikoData.Block storage blk, + bytes32 parentHash, + uint32 parentGasUsed + ) internal view returns (uint256) { + if ( + blk.forkChoices[1].key == + keyForForkChoice(parentHash, parentGasUsed) + ) return 1; + + uint256 fcId = state.forkChoiceIds[blk.blockId][parentHash][ + parentGasUsed + ]; + if (fcId >= blk.nextForkChoiceId) return 0; + + return fcId; + } + function getStateVariables( TaikoData.State storage state ) internal view returns (TaikoData.StateVariables memory) { @@ -68,12 +87,13 @@ library LibUtils { uint24 txListByteEnd; uint32 gasLimit; address beneficiary; + address treasure; } function hashMetadata( TaikoData.BlockMetadata memory meta ) internal pure returns (bytes32 hash) { - uint256[5] memory inputs; + uint256[6] memory inputs; inputs[0] = (uint256(meta.id) << 192) | @@ -88,10 +108,26 @@ library LibUtils { (uint256(meta.txListByteStart) << 232) | (uint256(meta.txListByteEnd) << 208) | (uint256(meta.gasLimit) << 176) | - (uint256(uint160(meta.beneficiary)) << 16); + (uint256(uint160(meta.beneficiary)) << 16) | + (uint256(meta.cacheTxListInfo) << 8); + + inputs[5] = (uint256(uint160(meta.treasure)) << 96); + + assembly { + hash := keccak256(inputs, mul(6, 32)) + } + } + function keyForForkChoice( + bytes32 parentHash, + uint32 parentGasUsed + ) internal pure returns (bytes32 key) { assembly { - hash := keccak256(inputs, mul(5, 32)) + let ptr := mload(0x40) + mstore(ptr, parentGasUsed) + mstore(add(ptr, 32), parentHash) + key := keccak256(add(ptr, 28), 36) + mstore(0x40, add(ptr, 64)) } } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index c8cc32a03cb..b6daef6df62 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -7,6 +7,7 @@ pragma solidity ^0.8.18; import {AddressResolver} from "../../common/AddressResolver.sol"; +import {ISignalService} from "../../signal/ISignalService.sol"; import {LibTokenomics} from "./LibTokenomics.sol"; import {LibUtils} from "./LibUtils.sol"; import { @@ -19,6 +20,7 @@ library LibVerifying { using LibUtils for TaikoData.State; error L1_INVALID_CONFIG(); + error L1_INVALID_L21559_PARAMS(); event BlockVerified(uint256 indexed id, bytes32 blockHash); event XchainSynced( @@ -59,6 +61,7 @@ library LibVerifying { function verifyBlocks( TaikoData.State storage state, TaikoData.Config memory config, + AddressResolver resolver, uint256 maxBlocks ) internal { uint256 i = state.lastVerifiedBlockId; @@ -68,9 +71,9 @@ library LibVerifying { assert(fcId > 0); bytes32 blockHash = blk.forkChoices[fcId].blockHash; - assert(blockHash != bytes32(0)); - + uint32 gasUsed = blk.forkChoices[fcId].gasUsed; bytes32 signalRoot; + uint64 processed; unchecked { ++i; @@ -78,14 +81,16 @@ library LibVerifying { while (i < state.numBlocks && processed < maxBlocks) { blk = state.blocks[i % config.ringBufferSize]; + assert(blk.blockId == i); + + fcId = LibUtils.getForkChoiceId(state, blk, blockHash, gasUsed); - fcId = state.forkChoiceIds[i][blockHash]; if (fcId == 0) break; TaikoData.ForkChoice storage fc = blk.forkChoices[fcId]; if (fc.prover == address(0)) break; - (blockHash, signalRoot) = _markBlockVerified({ + _markBlockVerified({ state: state, config: config, blk: blk, @@ -93,9 +98,9 @@ library LibVerifying { fc: fc }); - assert(blockHash != bytes32(0)); - - emit BlockVerified(i, blockHash); + blockHash = fc.blockHash; + gasUsed = fc.gasUsed; + signalRoot = fc.signalRoot; unchecked { ++i; @@ -107,6 +112,14 @@ library LibVerifying { unchecked { state.lastVerifiedBlockId += processed; } + + if (config.relaySignalRoot) { + // Send the L2's signal root to the signal service so other TaikoL1 + // deployments, if they share the same signal service, can relay the + // signal to their corresponding TaikoL2 contract. + ISignalService(resolver.resolve("signal_service", false)) + .sendSignal(signalRoot); + } emit XchainSynced(state.lastVerifiedBlockId, blockHash, signalRoot); } } @@ -151,6 +164,8 @@ library LibVerifying { blk.nextForkChoiceId = 1; blk.verifiedForkChoiceId = fcId; + + emit BlockVerified(blk.blockId, fc.blockHash); } function _addToBalance( diff --git a/packages/protocol/contracts/L2/LibL2Consts.sol b/packages/protocol/contracts/L2/LibL2Consts.sol index 86ba938ac61..b24997851c4 100644 --- a/packages/protocol/contracts/L2/LibL2Consts.sol +++ b/packages/protocol/contracts/L2/LibL2Consts.sol @@ -7,6 +7,5 @@ pragma solidity ^0.8.18; library LibL2Consts { - // TODO(david): need you to confirm this value. - uint32 public constant ANCHOR_GAS_COST = 47000; + uint64 public constant ANCHOR_GAS_COST = 150000; // owner:david } diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index a7413a4f26a..9730f61dd19 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {EssentialContract} from "../common/EssentialContract.sol"; import {IXchainSync} from "../common/IXchainSync.sol"; +import {LibL2Consts} from "./LibL2Consts.sol"; import {LibMath} from "../libs/LibMath.sol"; import {Lib1559Math} from "../libs/Lib1559Math.sol"; import {TaikoL2Signer} from "./TaikoL2Signer.sol"; @@ -61,7 +62,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { // Captures all block variables mentioned in // https://docs.soliditylang.org/en/v0.8.18/units-and-global-variables.html - event BlockVars( + event Anchored( uint64 number, uint64 basefee, uint64 gaslimit, @@ -96,7 +97,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { if (_param1559.gasIssuedPerSecond != 0) { if ( - _param1559.gasIssuedPerSecond == 0 || + _param1559.basefee == 0 || _param1559.gasExcessMax == 0 || _param1559.gasTarget == 0 || _param1559.ratio2x1x == 0 @@ -147,15 +148,17 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { * * This transaction shall be the first transaction in every L2 block. * - * @param l1Height The latest L1 block height when this block was proposed. * @param l1Hash The latest L1 block hash when this block was proposed. * @param l1SignalRoot The latest value of the L1 "signal service storage root". + * @param l1Height The latest L1 block height when this block was proposed. + * @param parentGasUsed the gas used in the parent block. */ function anchor( - uint64 l1Height, bytes32 l1Hash, - bytes32 l1SignalRoot + bytes32 l1SignalRoot, + uint64 l1Height, + uint64 parentGasUsed ) external { if (msg.sender != GOLDEN_TOUCH_ADDRESS) revert L2_INVALID_SENDER(); @@ -178,26 +181,26 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { emit XchainSynced(l1Height, l1Hash, l1SignalRoot); // Check EIP-1559 basefee - uint64 basefee; + uint256 basefee; if (gasIssuedPerSecond != 0) { (basefee, gasExcess) = _calcBasefee( block.timestamp - parentTimestamp, - uint64(block.gaslimit) + uint64(block.gaslimit), + parentGasUsed ); } if (block.basefee != basefee) - revert L2_BASEFEE_MISMATCH(basefee, uint64(block.basefee)); + revert L2_BASEFEE_MISMATCH(uint64(basefee), uint64(block.basefee)); parentTimestamp = uint64(block.timestamp); // We emit this event so circuits can grab its data to verify block variables. // If plonk lookup table already has all these data, we can still use this // event for debugging purpose. - - emit BlockVars({ + emit Anchored({ number: uint64(block.number), - basefee: basefee, + basefee: uint64(basefee), gaslimit: uint64(block.gaslimit), timestamp: uint64(block.timestamp), parentHash: parentHash, @@ -212,13 +215,11 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { **********************/ function getBasefee( - uint32 timeSinceNow, - uint64 gasLimit - ) public view returns (uint64 _basefee) { - (_basefee, ) = _calcBasefee( - timeSinceNow + block.timestamp - parentTimestamp, - gasLimit - ); + uint32 timeSinceParent, + uint64 gasLimit, + uint64 parentGasUsed + ) public view returns (uint256 _basefee) { + (_basefee, ) = _calcBasefee(timeSinceParent, gasLimit, parentGasUsed); } function getXchainBlockHash( @@ -276,25 +277,26 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, IXchainSync { function _calcBasefee( uint256 timeSinceParent, - uint64 gasLimit - ) private view returns (uint64 _basefee, uint64 _gasExcess) { - uint256 gasIssued = gasIssuedPerSecond * timeSinceParent; - - _gasExcess = gasExcess > gasIssued ? uint64(gasExcess - gasIssued) : 0; + uint64 gasLimit, + uint64 parentGasUsed + ) private view returns (uint256 _basefee, uint64 _gasExcess) { + // Very important to cap _gasExcess uint64 + unchecked { + uint64 parentGasUsedNet = parentGasUsed > + LibL2Consts.ANCHOR_GAS_COST + ? parentGasUsed - LibL2Consts.ANCHOR_GAS_COST + : 0; + + uint256 a = uint256(gasExcess) + parentGasUsedNet; + uint256 b = gasIssuedPerSecond * timeSinceParent; + _gasExcess = uint64((a.max(b) - b).min(type(uint64).max)); + } - uint256 __basefee = Lib1559Math.calculatePrice({ + _basefee = Lib1559Math.calculatePrice({ xscale: xscale, yscale: yscale, xExcess: _gasExcess, xPurchase: gasLimit }); - - // Very important to cap basefee uint64 - _basefee = uint64(__basefee.min(type(uint64).max)); - - // Very important to cap _gasExcess uint64 - _gasExcess = uint64( - (uint256(_gasExcess) + gasLimit).min(type(uint64).max) - ); } } diff --git a/packages/protocol/contracts/libs/Lib1559Math.sol b/packages/protocol/contracts/libs/Lib1559Math.sol index 7d3a3b26215..e36c838a91d 100644 --- a/packages/protocol/contracts/libs/Lib1559Math.sol +++ b/packages/protocol/contracts/libs/Lib1559Math.sol @@ -36,7 +36,7 @@ library Lib1559Math { // 2*target gas and the other one has target gas. uint256 price1x = calculatePrice(xscale, yscale, x, target); uint256 price2x = calculatePrice(xscale, yscale, x, target * 2); - uint64 ratio = uint64((price2x * 100) / price1x); + uint64 ratio = uint64((price2x * 10000) / price1x); if (ratio2x1x != ratio) revert M1559_UNEXPECTED_CHANGE(ratio2x1x, ratio); diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index 3b515f25d6f..5c1111b1c65 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {TaikoL1} from "../../L1/TaikoL1.sol"; import {TaikoData} from "../../L1/TaikoData.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; contract TestTaikoL1 is TaikoL1 { function getConfig() @@ -28,6 +29,15 @@ contract TestTaikoL1 is TaikoL1 { config.maxBytesPerTxList = 120000; config.minTxGasLimit = 21000; + config.enableTokenomics = false; config.skipZKPVerification = true; } + + // The old implementation that is also used in hardhat tests. + function keyForName( + uint256 chainId, + string memory name + ) public pure override returns (string memory key) { + key = string.concat(Strings.toString(chainId), ".", name); + } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 2f161ebc9ef..d876250b2df 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.18; import {TaikoL1} from "../../L1/TaikoL1.sol"; import {TaikoData} from "../../L1/TaikoData.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; contract TestTaikoL1EnableTokenomics is TaikoL1 { function getConfig() @@ -29,7 +30,15 @@ contract TestTaikoL1EnableTokenomics is TaikoL1 { config.minTxGasLimit = 21000; // Moving average factors - + config.enableTokenomics = true; config.skipZKPVerification = true; } + + // The old implementation that is also used in hardhat tests. + function keyForName( + uint256 chainId, + string memory name + ) public pure override returns (string memory key) { + key = string.concat(Strings.toString(chainId), ".", name); + } } diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index ad67d6726aa..45727ed8600 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -1,15 +1,25 @@ # See more config options https://github.com/foundry-rs/foundry/tree/master/config - - [profile.default] +solc-version = "0.8.18" src = 'contracts' out = 'out' test = 'test2' libs = ['lib'] optimizer = true optimizer_runs = 200 +ffi = true +gas_limit = '18446744073709551615' # Do not change the block_gas_limit value, TaikoL2.t.sol depends on it. -# block_gas_limit = 30000000 #30M -# For mainnet_mock tokenomics test we need a huge value to run lots of iteration. Need to be revoked afterwards -block_gas_limit = 3000000000 #30M + block_gas_limit = 30000000 #30M +# For mainnet_mock tokenomics test we need a huge value to run lots of iterations. +# Use the above 30M for TaikoL2.t.sol related tests, only use this number with mainnet simulation. +#block_gas_limit = 3000000000 #3000M + +fs_permissions = [ + { access = "read", path = "./out"}, + { access = "read", path = "./deployments"}, + { access = "read", path = "./test2"} +] + +fuzz = { runs = 256 } diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index a2401277c3b..6ffbbd477c4 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -19,12 +19,14 @@ import "../contracts/signal/SignalService.sol"; import "../contracts/thirdparty/AddressManager.sol"; import "../contracts/test/erc20/FreeMintERC20.sol"; import "../contracts/test/erc20/MayFailFreeMintERC20.sol"; +import "../test2/Ln.sol"; contract DeployOnL1 is Script, AddressResolver { using SafeCastUpgradeable for uint256; + uint256 public l2ChainId = vm.envUint("L2_CHAIN_ID"); - bytes32 public gensisHash = vm.envBytes32("L2_GENESIS_HASH"); + bytes32 public genesisHash = vm.envBytes32("L2_GENESIS_HASH"); uint256 public deployerPrivateKey = vm.envUint("PRIVATE_KEY"); @@ -32,10 +34,14 @@ contract DeployOnL1 is Script, AddressResolver { address public owner = vm.envAddress("OWNER"); - address public oracleProver = vm.envAddress("ORACLE_PROVER_ADDRESS"); + address public oracleProver = vm.envAddress("ORACLE_PROVER"); address public soloProposer = vm.envAddress("SOLO_PROPOSER"); + address public sharedSignalService = vm.envAddress("SHARED_SIGNAL_SERVICE"); + + address public treasure = vm.envAddress("TREASURE"); + address public taikoTokenPremintRecipient = vm.envAddress("TAIKO_TOKEN_PREMINT_RECIPIENT"); @@ -44,12 +50,18 @@ contract DeployOnL1 is Script, AddressResolver { address public addressManagerProxy; + // New fee/reward related variables + uint256 public constant SCALING_E18 = 1e18; + uint16 public constant PROOF_TIME_TARGET = 1800; // Mainnet it is 30 mins, choose carefully ! + uint8 public constant ADJUSTMENT_QUOTIENT = 16; + error FAILED_TO_DEPLOY_PLONK_VERIFIER(string contractPath); function run() external { require(l2ChainId != block.chainid, "same chainid"); require(owner != address(0), "owner is zero"); require(taikoL2Address != address(0), "taikoL2Address is zero"); + require(treasure != address(0), "treasure is zero"); require( taikoTokenPremintRecipient != address(0), "taikoTokenPremintRecipient is zero" @@ -72,6 +84,7 @@ contract DeployOnL1 is Script, AddressResolver { setAddress(l2ChainId, "taiko", taikoL2Address); setAddress("oracle_prover", oracleProver); setAddress("solo_proposer", soloProposer); + setAddress(l2ChainId, "treasure", treasure); // TaikoToken TaikoToken taikoToken = new TaikoToken(); @@ -108,13 +121,30 @@ contract DeployOnL1 is Script, AddressResolver { // TaikoL1 TaikoL1 taikoL1 = new TaikoL1(); - uint64 basefee = 1 ** 8; // Taiko Token's decimals is 8, not 18 + uint64 feeBase = 1 ** 8; // Taiko Token's decimals is 8, not 18 + + // Calculating it for our needs based on testnet/mainnet. We need it in + // order to make the fees on the same level - in ideal circumstences. + // See Brecht's comment https://github.com/taikoxyz/taiko-mono/pull/13564 + uint256 scale = uint256(PROOF_TIME_TARGET * ADJUSTMENT_QUOTIENT); + // ln_pub() expects 1e18 fixed format + int256 logInput = int256((scale * feeBase) * SCALING_E18); + int256 log_result = Ln.ln_pub(logInput); + uint64 initProofTimeIssued = uint64( + ((scale * (uint256(log_result))) / (SCALING_E18)) + ); + address taikoL1Proxy = deployProxy( "taiko", address(taikoL1), bytes.concat( taikoL1.init.selector, - abi.encode(addressManagerProxy, basefee, gensisHash) + abi.encode( + addressManagerProxy, + genesisHash, + feeBase, + initProofTimeIssued + ) ) ); setAddress("proto_broker", taikoL1Proxy); @@ -139,15 +169,23 @@ contract DeployOnL1 is Script, AddressResolver { ); // SignalService - SignalService signalService = new SignalService(); - deployProxy( - "signal_service", - address(signalService), - bytes.concat( - signalService.init.selector, - abi.encode(addressManagerProxy) - ) - ); + if (sharedSignalService == address(0)) { + SignalService signalService = new SignalService(); + deployProxy( + "signal_service", + address(signalService), + bytes.concat( + signalService.init.selector, + abi.encode(addressManagerProxy) + ) + ); + } else { + console.log( + "Warining: using shared signal service: ", + sharedSignalService + ); + setAddress("signal_service", sharedSignalService); + } // PlonkVerifier deployPlonkVerifiers(); diff --git a/packages/protocol/script/deploy_on_l1.sh b/packages/protocol/script/deploy_on_l1.sh index 2ec0baa644b..b7b5f871b73 100755 --- a/packages/protocol/script/deploy_on_l1.sh +++ b/packages/protocol/script/deploy_on_l1.sh @@ -4,18 +4,16 @@ set -e PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ -ORACLE_PROVER_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ +SHARED_SIGNAL_SERVICE=0x0000000000000000000000000000000000000000 \ +ORACLE_PROVER=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ SOLO_PROPOSER=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ OWNER=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ TAIKO_L2_ADDRESS=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ TAIKO_TOKEN_PREMINT_RECIPIENT=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ TAIKO_TOKEN_PREMINT_AMOUNT=0xffff \ +TREASURE=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 \ L2_GENESIS_HASH=0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 \ L2_CHAIN_ID=167001 \ -L2_GAS_EXCESS_MAX=3840000000 \ -L2_BASE_FEE=5000000000 \ -L2_GAS_TARGET=6000000 \ -L2_EXPECTED_2X1X_RATIO=111 \ forge script script/DeployOnL1.s.sol:DeployOnL1 \ --fork-url http://localhost:8545 \ --broadcast \ diff --git a/packages/protocol/test2/Lib1559Math.sol b/packages/protocol/test2/Lib1559Math.sol index 528447b3fa2..9af6d90cacb 100644 --- a/packages/protocol/test2/Lib1559Math.sol +++ b/packages/protocol/test2/Lib1559Math.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; @@ -17,7 +17,7 @@ contract TestLib1559Math is Test { uint64 xExcessMax = (uint(15000000) * 256 * rand).toUint64(); uint64 xTarget = (uint(6000000) * rand).toUint64(); uint64 price0 = (uint(5000000000) * rand).toUint64(); - uint64 ratio2x1x = 111; + uint64 ratio2x1x = 11177; (uint128 xscale, uint128 yscale) = T.calculateScales({ xExcessMax: xExcessMax, price: price0, @@ -55,7 +55,7 @@ contract TestLib1559Math is Test { assertLt(basefee2, type(uint64).max); if (basefee1 != 0) { - assertEq((basefee2 * 100) / basefee1, ratio2x1x); + assertEq((basefee2 * 10000) / basefee1, ratio2x1x); } } } @@ -66,7 +66,7 @@ contract TestLib1559Math is Test { uint64 xExcessMax = (uint(15000000) * 256 * rand).toUint64(); uint64 xTarget = (uint(6000000) * rand).toUint64(); uint64 price0 = (uint(5000000000) * rand).toUint64(); - uint64 ratio2x1x = 111; + uint64 ratio2x1x = 11177; (uint128 xscale, uint128 yscale) = T.calculateScales({ xExcessMax: xExcessMax, diff --git a/packages/protocol/test2/LibTokenomics.t.sol b/packages/protocol/test2/LibTokenomics.t.sol index cc617768f1b..b02fd515a82 100644 --- a/packages/protocol/test2/LibTokenomics.t.sol +++ b/packages/protocol/test2/LibTokenomics.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; // Uncomment if you want to compare fee/vs reward @@ -66,14 +66,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { //parentHash = prove_with_increasing_time(parentHash, 10); for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine(blockId); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -105,14 +109,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Run another session with huge times for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine_huge(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -158,7 +166,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 20; blockId++) { //printVariables("before propose"); - metas[blockId] = proposeBlock(Alice, 1024); + metas[blockId] = proposeBlock(Alice, 1000000, 1024); proposedAtArr[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); @@ -170,7 +178,15 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < 20; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, metas[blockId], parentHash, blockHash, signalRoot); + proveBlock( + Bob, + metas[blockId], + parentHash, + 123, + 456, + blockHash, + signalRoot + ); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -196,14 +212,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Run another sessioins for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -247,14 +267,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { //parentHash = prove_with_increasing_time(parentHash, 10); for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine(2); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -309,7 +333,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 30; blockId++) { //printVariables("before propose"); - meta[blockId] = proposeBlock(Alice, 1024); + meta[blockId] = proposeBlock(Alice, 1000000, 1024); proposedAt[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); @@ -322,7 +346,15 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + proveBlock( + Bob, + meta[blockId], + parentHash, + 123, + 456, + blockHash, + signalRoot + ); uint64 provenAt = uint64(block.timestamp); console2.log( @@ -378,7 +410,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 20; blockId++) { //printVariables("before propose"); - meta[blockId] = proposeBlock(Alice, 1024); + meta[blockId] = proposeBlock(Alice, 1000000, 1024); proposedAt[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); @@ -390,7 +422,15 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < 20; blockId++) { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + proveBlock( + Bob, + meta[blockId], + parentHash, + 123, + 456, + blockHash, + signalRoot + ); uint64 provenAt = uint64(block.timestamp); console2.log( @@ -435,14 +475,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { //parentHash = prove_with_increasing_time(parentHash, 10); for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine(5); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -497,7 +541,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 30; blockId++) { //printVariables("before propose"); - meta[blockId] = proposeBlock(Alice, 1024); + meta[blockId] = proposeBlock(Alice, 1000000, 1024); proposedAt[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); @@ -510,7 +554,15 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta[blockId], parentHash, blockHash, signalRoot); + proveBlock( + Bob, + meta[blockId], + parentHash, + 123, + 456, + blockHash, + signalRoot + ); uint64 provenAt = uint64(block.timestamp); console2.log( @@ -560,14 +612,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { //parentHash = prove_with_increasing_time(parentHash, 10); for (uint256 blockId = 1; blockId < 10; blockId++) { - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); printVariables("after propose"); uint64 proposedAt = uint64(block.timestamp); mine(11 - blockId); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -599,14 +655,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Run another session with huge times for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", @@ -651,7 +711,7 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Propose blocks for (uint256 blockId = 1; blockId < 20; blockId++) { //printVariables("before propose"); - metaArr[blockId] = proposeBlock(Alice, 1024); + metaArr[blockId] = proposeBlock(Alice, 1000000, 1024); proposedAtArr[blockId] = (uint64(block.timestamp)); printVariables("after propose"); mine(blockId); @@ -667,6 +727,8 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { Bob, metaArr[blockId], parentHash, + 123, + 456, blockHash, signalRoot ); @@ -702,14 +764,18 @@ contract LibL1TokenomicsTest is TaikoL1TestBase { // Run another session with huge times for (uint256 blockId = 1; blockId < 10; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); uint64 proposedAt = uint64(block.timestamp); printVariables("after propose"); mine_proofTime(); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock(Bob, meta, parentHash, 123, 456, blockHash, signalRoot); uint64 provenAt = uint64(block.timestamp); console2.log( "Proof reward is:", diff --git a/packages/protocol/test2/TaikoL1.t.sol b/packages/protocol/test2/TaikoL1.t.sol index e6c6969344a..87ecb497a5d 100644 --- a/packages/protocol/test2/TaikoL1.t.sol +++ b/packages/protocol/test2/TaikoL1.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; @@ -12,7 +12,7 @@ import {SignalService} from "../contracts/signal/SignalService.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {TaikoL1TestBase} from "./TaikoL1TestBase.t.sol"; -contract TaikoL1WithConfig is TaikoL1 { +contract TaikoL1_a is TaikoL1 { function getConfig() public pure @@ -21,6 +21,7 @@ contract TaikoL1WithConfig is TaikoL1 { { config = TaikoConfig.getConfig(); + config.enableTokenomics = true; config.txListCacheExpiry = 5 minutes; config.maxVerificationsPerTx = 0; config.enableSoloProposer = false; @@ -33,7 +34,7 @@ contract TaikoL1WithConfig is TaikoL1 { contract TaikoL1Test is TaikoL1TestBase { function deployTaikoL1() internal override returns (TaikoL1 taikoL1) { - taikoL1 = new TaikoL1WithConfig(); + taikoL1 = new TaikoL1_a(); } function setUp() public override { @@ -47,6 +48,8 @@ contract TaikoL1Test is TaikoL1TestBase { _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; + uint32 parentGasUsed = 0; + uint32 gasUsed = 1000000; for ( uint256 blockId = 1; @@ -54,15 +57,29 @@ contract TaikoL1Test is TaikoL1TestBase { blockId++ ) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock( + Bob, + meta, + parentHash, + parentGasUsed, + gasUsed, + blockHash, + signalRoot + ); + verifyBlock(Carol, 1); parentHash = blockHash; + parentGasUsed = gasUsed; } printVariables(""); } @@ -73,17 +90,32 @@ contract TaikoL1Test is TaikoL1TestBase { _depositTaikoToken(Alice, 1000 * 1E8, 1000 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; + uint32 parentGasUsed = 0; + uint32 gasUsed = 1000000; for (uint256 blockId = 1; blockId <= 2; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); printVariables("after propose"); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Alice, meta, parentHash, blockHash, signalRoot); + proveBlock( + Alice, + meta, + parentHash, + parentGasUsed, + gasUsed, + blockHash, + signalRoot + ); verifyBlock(Alice, 2); parentHash = blockHash; + parentGasUsed = gasUsed; } printVariables(""); } @@ -93,6 +125,8 @@ contract TaikoL1Test is TaikoL1TestBase { _depositTaikoToken(Alice, 1E6 * 1E8, 1000 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; + uint32 parentGasUsed = 0; + uint32 gasUsed = 1000000; for ( uint256 blockId = 1; @@ -100,14 +134,28 @@ contract TaikoL1Test is TaikoL1TestBase { blockId++ ) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); printVariables("after propose"); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Alice, meta, parentHash, blockHash, signalRoot); + proveBlock( + Alice, + meta, + parentHash, + parentGasUsed, + gasUsed, + blockHash, + signalRoot + ); parentHash = blockHash; + parentGasUsed = gasUsed; } + verifyBlock(Alice, conf.maxNumProposedBlocks - 1); printVariables("after verify"); verifyBlock(Alice, conf.maxNumProposedBlocks); @@ -121,6 +169,8 @@ contract TaikoL1Test is TaikoL1TestBase { _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; + uint32 parentGasUsed = 0; + uint32 gasUsed = 1000000; for ( uint256 blockId = 1; @@ -128,12 +178,27 @@ contract TaikoL1Test is TaikoL1TestBase { blockId++ ) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); mine(1); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock( + Bob, + meta, + parentHash, + parentGasUsed, + gasUsed, + blockHash, + signalRoot + ); + parentHash = blockHash; + parentGasUsed = gasUsed; + verifyBlock(Carol, 1); mine(blockId); parentHash = blockHash; @@ -148,17 +213,34 @@ contract TaikoL1Test is TaikoL1TestBase { _depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; + uint32 parentGasUsed = 0; + uint32 gasUsed = 1000000; uint256 total = conf.maxNumProposedBlocks * 10; for (uint256 blockId = 1; blockId < total; blockId++) { printVariables("before propose"); - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1024); + TaikoData.BlockMetadata memory meta = proposeBlock( + Alice, + 1000000, + 1024 + ); mine(1); bytes32 blockHash = bytes32(1E10 + blockId); bytes32 signalRoot = bytes32(1E9 + blockId); - proveBlock(Bob, meta, parentHash, blockHash, signalRoot); + proveBlock( + Bob, + meta, + parentHash, + parentGasUsed, + gasUsed, + blockHash, + signalRoot + ); + parentHash = blockHash; + parentGasUsed = gasUsed; + verifyBlock(Carol, 1); mine(total + 1 - blockId); parentHash = blockHash; diff --git a/packages/protocol/test2/TaikoL1TestBase.t.sol b/packages/protocol/test2/TaikoL1TestBase.t.sol index 6cd936b8a58..72248929c54 100644 --- a/packages/protocol/test2/TaikoL1TestBase.t.sol +++ b/packages/protocol/test2/TaikoL1TestBase.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; @@ -10,7 +10,7 @@ import {TaikoL1} from "../contracts/L1/TaikoL1.sol"; import {TaikoToken} from "../contracts/L1/TaikoToken.sol"; import {SignalService} from "../contracts/signal/SignalService.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {TestLn as TestMath} from "./TestLn.sol"; +import "./Ln.sol"; contract Verifier { fallback(bytes calldata) external returns (bytes memory) { @@ -30,8 +30,11 @@ abstract contract TaikoL1TestBase is Test { bytes32 public constant GENESIS_BLOCK_HASH = keccak256("GENESIS_BLOCK_HASH"); + uint64 feeBase = 1E8; // 1 TKO uint64 l2GasExcess = 1E18; + address public constant L2Treasure = + 0x859d74b52762d9ed07D1b2B8d7F93d26B1EA78Bb; address public constant L2SS = 0xa008AE5Ba00656a3Cc384de589579e3E52aC030C; address public constant L2TaikoL2 = 0x0082D90249342980d011C58105a03b35cCb4A315; @@ -42,7 +45,6 @@ abstract contract TaikoL1TestBase is Test { address public constant Dave = 0x400147C0Eb43D8D71b2B03037bB7B31f8f78EF5F; address public constant Eve = 0x50081b12838240B1bA02b3177153Bca678a86078; - uint32 private constant INIT_FEE = 1e9; // 10 TKO : Only relevant for the first proposing uint16 private constant PROOF_TIME_TARGET = 1800; uint8 private constant ADJUSTMENT_QUOTIENT = 16; @@ -52,14 +54,13 @@ abstract contract TaikoL1TestBase is Test { // vm.warp(1000000); addressManager = new AddressManager(); addressManager.init(); - uint64 initBasefee = INIT_FEE; // Calculating it for our needs based on testnet/mainnet proof vars. // See Brecht's comment https://github.com/taikoxyz/taiko-mono/pull/13564 uint256 scale = uint256(PROOF_TIME_TARGET * ADJUSTMENT_QUOTIENT); // ln_pub() expects 1e18 fixed format - int256 logInput = int256((scale * initBasefee) * SCALING_E18); - int256 log_result = TestMath.ln_pub(logInput); + int256 logInput = int256((scale * feeBase) * SCALING_E18); + int256 log_result = Ln.ln_pub(logInput); uint64 initProofTimeIssued = uint64( ((scale * (uint256(log_result))) / (SCALING_E18)) ); @@ -68,7 +69,7 @@ abstract contract TaikoL1TestBase is Test { L1.init( address(addressManager), GENESIS_BLOCK_HASH, - initBasefee, + feeBase, initProofTimeIssued ); conf = L1.getConfig(); @@ -95,6 +96,7 @@ abstract contract TaikoL1TestBase is Test { _registerAddress("taiko_token", address(tko)); _registerAddress("proto_broker", address(L1)); _registerAddress("signal_service", address(ss)); + _registerL2Address("treasure", L2Treasure); _registerL2Address("signal_service", address(L2SS)); _registerL2Address("taiko_l2", address(L2TaikoL2)); @@ -108,9 +110,9 @@ abstract contract TaikoL1TestBase is Test { function proposeBlock( address proposer, + uint32 gasLimit, uint24 txListSize ) internal returns (TaikoData.BlockMetadata memory meta) { - uint32 gasLimit = 1000000; bytes memory txList = new bytes(txListSize); TaikoData.BlockMetadataInput memory input = TaikoData .BlockMetadataInput({ @@ -139,15 +141,43 @@ abstract contract TaikoL1TestBase is Test { meta.txListByteEnd = txListSize; meta.gasLimit = gasLimit; meta.beneficiary = proposer; + meta.treasure = L2Treasure; vm.prank(proposer, proposer); L1.proposeBlock(abi.encode(input), txList); } + function oracleProveBlock( + address prover, + uint256 blockId, + bytes32 parentHash, + uint32 parentGasUsed, + uint32 gasUsed, + bytes32 blockHash, + bytes32 signalRoot + ) internal { + TaikoData.BlockOracle[] memory blks = new TaikoData.BlockOracle[](1); + + blks[0].blockHash = blockHash; + blks[0].signalRoot = signalRoot; + blks[0].gasUsed = gasUsed; + + TaikoData.BlockOracles memory oracles = TaikoData.BlockOracles( + parentHash, + parentGasUsed, + blks + ); + + vm.prank(prover, prover); + L1.oracleProveBlocks(blockId, abi.encode(oracles)); + } + function proveBlock( address prover, TaikoData.BlockMetadata memory meta, bytes32 parentHash, + uint32 parentGasUsed, + uint32 gasUsed, bytes32 blockHash, bytes32 signalRoot ) internal { @@ -164,7 +194,8 @@ abstract contract TaikoL1TestBase is Test { signalRoot: signalRoot, graffiti: 0x0, prover: prover, - gasUsed: 100000 + parentGasUsed: parentGasUsed, + gasUsed: gasUsed }); vm.prank(prover, prover); diff --git a/packages/protocol/test2/TaikoL2.t.sol b/packages/protocol/test2/TaikoL2.t.sol index e8dfea7c330..b2c5070896f 100644 --- a/packages/protocol/test2/TaikoL2.t.sol +++ b/packages/protocol/test2/TaikoL2.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.18; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; +import {LibL2Consts} from "../contracts/L2/LibL2Consts.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {TaikoL2} from "../contracts/L2/TaikoL2.sol"; import { @@ -11,10 +12,11 @@ import { contract TestTaikoL2 is Test { using SafeCastUpgradeable for uint256; - uint32 public constant BLOCK_GAS_LIMIT = 30000000; // same as `block_gas_limit` in foundry.toml + uint64 public constant BLOCK_GAS_LIMIT = 30000000; // same as `block_gas_limit` in foundry.toml TaikoL2 public L2; uint private logIndex; + uint64 private ANCHOR_GAS_COST = LibL2Consts.ANCHOR_GAS_COST; function setUp() public { uint16 rand = 2; @@ -23,7 +25,7 @@ contract TestTaikoL2 is Test { gasIssuedPerSecond: 1000000, gasExcessMax: (uint(15000000) * 256 * rand).toUint64(), gasTarget: (uint(6000000) * rand).toUint64(), - ratio2x1x: 111 + ratio2x1x: 11177 }); L2 = new TaikoL2(); @@ -31,17 +33,12 @@ contract TestTaikoL2 is Test { vm.roll(block.number + 1); vm.warp(block.timestamp + 30); - - // console2.log("basefee =", uint256(L2.basefee())); - // console2.log("xscale =", uint256(L2.xscale())); - // console2.log("yscale =", uint256(L2.yscale())); - // console2.log("gasExcess =", uint256(L2.gasExcess())); } - function xtestAnchorTxsBlocktimeConstant() external { - uint64 firstBasefee; + function testAnchorTxsBlocktimeConstant() external { + uint256 firstBasefee; for (uint256 i = 0; i < 100; i++) { - uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); vm.fee(basefee); if (firstBasefee == 0) { @@ -51,36 +48,36 @@ contract TestTaikoL2 is Test { } vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - L2.anchor(12345, keccak256("a"), keccak256("b")); + _anchor(BLOCK_GAS_LIMIT); vm.roll(block.number + 1); vm.warp(block.timestamp + 30); } } - function xtestAnchorTxsBlocktimeDecreasing() external { - uint64 prevBasefee; + function testAnchorTxsBlocktimeDecreasing() external { + uint256 prevBasefee; for (uint256 i = 0; i < 32; i++) { - uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); vm.fee(basefee); assertGe(basefee, prevBasefee); prevBasefee = basefee; vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - L2.anchor(12345, keccak256("a"), keccak256("b")); + _anchor(BLOCK_GAS_LIMIT); vm.roll(block.number + 1); vm.warp(block.timestamp + 30 - i); } } - function xtestAnchorTxsBlocktimeIncreasing() external { - uint64 prevBasefee; + function testAnchorTxsBlocktimeIncreasing() external { + uint256 prevBasefee; for (uint256 i = 0; i < 30; i++) { - uint64 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 basefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); vm.fee(basefee); if (prevBasefee != 0) { @@ -89,7 +86,7 @@ contract TestTaikoL2 is Test { prevBasefee = basefee; vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - L2.anchor(12345, keccak256("a"), keccak256("b")); + _anchor(BLOCK_GAS_LIMIT); vm.roll(block.number + 1); @@ -98,24 +95,24 @@ contract TestTaikoL2 is Test { } // calling anchor in the same block more than once should fail - function xtestAnchorTxsFailInTheSameBlock() external { - uint64 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + function testAnchorTxsFailInTheSameBlock() external { + uint256 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); vm.fee(expectedBasefee); vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - L2.anchor(12345, keccak256("a"), keccak256("b")); + _anchor(BLOCK_GAS_LIMIT); vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); vm.expectRevert(); - L2.anchor(12345, keccak256("a"), keccak256("b")); + _anchor(BLOCK_GAS_LIMIT); } // calling anchor in the same block more than once should fail function testAnchorTxsFailByNonTaikoL2Signer() external { - uint64 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); + uint256 expectedBasefee = _getBasefeeAndPrint(0, BLOCK_GAS_LIMIT); vm.fee(expectedBasefee); vm.expectRevert(); - L2.anchor(12345, keccak256("a"), keccak256("b")); + _anchor(BLOCK_GAS_LIMIT); } function testAnchorSigning(bytes32 digest) external { @@ -134,31 +131,29 @@ contract TestTaikoL2 is Test { L2.signAnchor(digest, uint8(3)); } - function testGetBasefee1() external { - assertEq(_getBasefeeAndPrint(0, 0), 317609019); - assertEq(_getBasefeeAndPrint(0, 1), 317609019); - assertEq(_getBasefeeAndPrint(0, 1000000), 320423332); - assertEq(_getBasefeeAndPrint(0, 5000000), 332018053); - assertEq(_getBasefeeAndPrint(0, 10000000), 347305199); - } - - function testGetBasefee2() external { - assertEq(_getBasefeeAndPrint(100, 0), 54544902); - assertEq(_getBasefeeAndPrint(100, 1), 54544902); - assertEq(_getBasefeeAndPrint(100, 1000000), 55028221); - assertEq(_getBasefeeAndPrint(100, 5000000), 57019452); - assertEq(_getBasefeeAndPrint(100, 10000000), 59644805); + function testGetBasefee() external { + uint32 timeSinceParent = uint32(block.timestamp - L2.parentTimestamp()); + assertEq(_getBasefeeAndPrint(timeSinceParent, 0, 0), 317609019); + assertEq(_getBasefeeAndPrint(timeSinceParent, 1, 0), 317609019); + assertEq(_getBasefeeAndPrint(timeSinceParent, 1000000, 0), 320423332); + assertEq(_getBasefeeAndPrint(timeSinceParent, 5000000, 0), 332018053); + assertEq(_getBasefeeAndPrint(timeSinceParent, 10000000, 0), 347305199); + + timeSinceParent = uint32(100 + block.timestamp - L2.parentTimestamp()); + assertEq(_getBasefeeAndPrint(timeSinceParent, 0, 0), 54544902); + assertEq(_getBasefeeAndPrint(timeSinceParent, 1, 0), 54544902); + assertEq(_getBasefeeAndPrint(timeSinceParent, 1000000, 0), 55028221); + assertEq(_getBasefeeAndPrint(timeSinceParent, 5000000, 0), 57019452); + assertEq(_getBasefeeAndPrint(timeSinceParent, 10000000, 0), 59644805); } function _getBasefeeAndPrint( - uint32 timeSinceNow, - uint64 gasLimit - ) private returns (uint64 _basefee) { - uint256 timeSinceParent = timeSinceNow + - block.timestamp - - L2.parentTimestamp(); + uint32 timeSinceParent, + uint64 gasLimit, + uint64 parentGasUsed + ) private returns (uint256 _basefee) { uint256 gasIssued = L2.gasIssuedPerSecond() * timeSinceParent; - string memory msg = string.concat( + string memory _msg = string.concat( "#", Strings.toString(logIndex++), ": gasExcess=", @@ -168,18 +163,41 @@ contract TestTaikoL2 is Test { ", gasIssued=", Strings.toString(gasIssued), ", gasLimit=", - Strings.toString(gasLimit) + Strings.toString(gasLimit), + ", parentGasUsed=", + Strings.toString(parentGasUsed) ); - _basefee = L2.getBasefee(timeSinceNow, gasLimit); + _basefee = L2.getBasefee(timeSinceParent, gasLimit, parentGasUsed); - msg = string.concat( - msg, + _msg = string.concat( + _msg, ", gasExcess(changed)=", Strings.toString(L2.gasExcess()), ", basefee=", Strings.toString(_basefee) ); - console2.log(msg); + console2.log(_msg); + } + + function _getBasefeeAndPrint( + uint32 timeSinceNow, + uint64 gasLimit + ) private returns (uint256 _basefee) { + return + _getBasefeeAndPrint( + uint32(timeSinceNow + block.timestamp - L2.parentTimestamp()), + gasLimit, + gasLimit + ANCHOR_GAS_COST + ); + } + + function _anchor(uint64 parentGasLimit) private { + L2.anchor( + keccak256("a"), + keccak256("b"), + 12345, + parentGasLimit + ANCHOR_GAS_COST + ); } } diff --git a/packages/status-page/src/pages/home/Home.svelte b/packages/status-page/src/pages/home/Home.svelte index 26e1d5e78f5..93f02005d61 100644 --- a/packages/status-page/src/pages/home/Home.svelte +++ b/packages/status-page/src/pages/home/Home.svelte @@ -1,5 +1,5 @@