diff --git a/packages/protocol/.gitignore b/packages/protocol/.gitignore index 9007b6be4e..b660994e1f 100644 --- a/packages/protocol/.gitignore +++ b/packages/protocol/.gitignore @@ -16,6 +16,7 @@ npm-debug.log* # Hardhat files cache artifacts +out # Editors .vscode diff --git a/packages/protocol/contracts/L1/TaikoCustomErrors.sol b/packages/protocol/contracts/L1/TaikoCustomErrors.sol index 8b350329e3..059a924e2f 100644 --- a/packages/protocol/contracts/L1/TaikoCustomErrors.sol +++ b/packages/protocol/contracts/L1/TaikoCustomErrors.sol @@ -33,6 +33,7 @@ abstract contract TaikoCustomErrors { error L1_GAS_LIMIT(); error L1_ID(); error L1_INPUT_SIZE(); + error L1_INVALID_CONFIG(); error L1_INVALID_PARAM(); error L1_METADATA_FIELD(); error L1_META_MISMATCH(); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 901a7856d5..b862ca1fb0 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -45,6 +45,7 @@ contract TaikoL1 is LibVerifying.init({ state: state, genesisBlockHash: _genesisBlockHash, + config: getConfig(), feeBase: _feeBase }); } diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index cf5041b406..1fe7882778 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -163,7 +163,8 @@ library LibProposing { isProposal: true, tNow: uint64(block.timestamp), tLast: state.lastProposedAt, - tAvg: state.avgBlockTime + tAvg: state.avgBlockTime, + tCap: config.blockTimeCap }); fee = LibUtils.getSlotsAdjustedFee({ state: state, diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index cc6cc3d2c3..1694a299a9 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -77,20 +77,23 @@ library LibUtils { bool isProposal, uint64 tNow, uint64 tLast, - uint64 tAvg + uint64 tAvg, + uint64 tCap ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { - if (tAvg == 0) { + if ( + tCap == 0 || + tAvg == 0 || + config.feeMaxPeriodPctg <= config.feeGracePeriodPctg || + config.rewardMultiplierPctg <= 100 + ) { newFeeBase = state.feeBase; // tRelBp = 0; } else { - uint256 _tAvg = tAvg > config.proofTimeCap - ? config.proofTimeCap - : tAvg; - uint256 tGrace = (config.feeGracePeriodPctg * _tAvg) / 100; - uint256 tMax = (config.feeMaxPeriodPctg * _tAvg) / 100; - uint256 a = tLast + tGrace; - uint256 b = tNow > a ? tNow - a : 0; - tRelBp = (b.min(tMax) * 10000) / tMax; // [0 - 10000] + uint256 _tAvg = uint256(tAvg).min(tCap); + uint256 grace = (config.feeGracePeriodPctg * _tAvg) / 100; + uint256 max = (config.feeMaxPeriodPctg * _tAvg) / 100; + uint256 t = uint256(tNow - tLast).max(grace).min(max); + tRelBp = ((t - grace) * 10000) / (max - grace); // [0 - 10000] uint256 alpha = 10000 + ((config.rewardMultiplierPctg - 100) * tRelBp) / 100; diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index b5fb590ba8..a39fef8cdd 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -26,12 +26,34 @@ library LibVerifying { event HeaderSynced(uint256 indexed srcHeight, bytes32 srcHash); error L1_0_FEE_BASE(); + error L1_INVALID_CONFIG(); + error L1_INVALID_PARAM(); function init( TaikoData.State storage state, + TaikoData.Config memory config, bytes32 genesisBlockHash, - uint256 feeBase - ) public { + uint feeBase + ) internal { + if ( + config.chainId <= 1 || + config.maxNumBlocks <= 1 || + config.blockHashHistory == 0 || + config.blockMaxGasLimit == 0 || + config.maxTransactionsPerBlock == 0 || + config.maxBytesPerTxList == 0 || + config.minTxGasLimit == 0 || + config.slotSmoothingFactor == 0 || + config.rewardBurnBips >= 10000 || + config.feeBaseMAF == 0 || + config.blockTimeMAF == 0 || + config.proofTimeMAF == 0 || + config.blockTimeCap == 0 || + config.proofTimeCap == 0 || + config.feeGracePeriodPctg > config.feeMaxPeriodPctg || + config.rewardMultiplierPctg < 100 + ) revert L1_INVALID_CONFIG(); + if (feeBase == 0) revert L1_0_FEE_BASE(); state.genesisHeight = uint64(block.number); @@ -132,13 +154,15 @@ library LibVerifying { uint64 provenAt, uint64 proposedAt ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { + if (proposedAt > provenAt) revert L1_INVALID_PARAM(); (newFeeBase, tRelBp) = LibUtils.getTimeAdjustedFee({ state: state, config: config, isProposal: false, tNow: provenAt, tLast: proposedAt, - tAvg: state.avgProofTime + tAvg: state.avgProofTime, + tCap: config.proofTimeCap }); reward = LibUtils.getSlotsAdjustedFee({ state: state, diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol index f758008b7d..830553b605 100644 --- a/packages/protocol/contracts/libs/LibSharedConfig.sol +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -8,28 +8,13 @@ pragma solidity ^0.8.18; import {TaikoData} from "../L1/TaikoData.sol"; -/* -> cd taiko-mono/packages/protocol/utils/generate_config -> python3 main.py -Expected block time (seconds): 20 -Expected proof time (minutes): 10 -Slot availability multiplier: 20 -Number of ZKPs required per block before verificaiton: 1 -Extra slots (e.g, 50 means 50% more slots): 100 ---------- -min num slots: 30 ---------- -maxNumBlocks: 61 -slotSmoothingFactor: 16789 -*/ - library LibSharedConfig { /// Returns shared configs for both TaikoL1 and TaikoL2 for production. function getConfig() internal pure returns (TaikoData.Config memory) { return TaikoData.Config({ chainId: 167, - maxNumBlocks: 2048, // owner:daniel + maxNumBlocks: 2049, // owner:daniel blockHashHistory: 40, // owner:daniel maxVerificationsPerTx: 10, //owner:david. Each time one more block is verified, there will be ~20k more gas cost. commitConfirmations: 0, // owner:daniel @@ -38,7 +23,7 @@ library LibSharedConfig { maxBytesPerTxList: 120000, // owner:david. Set it to 120KB, since 128KB is the upper size limit of a geth transaction, so using 120KB for the proposed transactions list calldata, 8K for the remaining tx fields. minTxGasLimit: 21000, // owner:david anchorTxGasLimit: 250000, // owner:david - slotSmoothingFactor: 16789, // owner:daniel + slotSmoothingFactor: 946649, // owner:daniel rewardBurnBips: 100, // owner:daniel. 100 basis points or 1% proposerDepositPctg: 25, // owner:daniel - 25% // Moving average factors diff --git a/packages/protocol/test/tokenomics/blockFee.test.ts b/packages/protocol/test/tokenomics/blockFee.test.ts index 6232a23e52..e9b1451fe4 100644 --- a/packages/protocol/test/tokenomics/blockFee.test.ts +++ b/packages/protocol/test/tokenomics/blockFee.test.ts @@ -67,8 +67,7 @@ describe("tokenomics: blockFee", function () { }); it( - "proposes blocks on interval, blockFee should increase, " + - "proposer's balance for TkoToken should decrease as it pays proposer fee, " + + "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 " + "no proofs have been submitted", async function () { @@ -77,19 +76,14 @@ describe("tokenomics: blockFee", function () { await proposerSigner.getAddress() ); - // do the same for the blockFee, which should increase every block proposal - // with proofs not being submitted. + let lastProofReward = BigNumber.from(0); + // we want to wait for enough blocks until the blockFee is no longer 0, then run our // tests. - let lastBlockFee = await taikoL1.getBlockFee(); - - while (lastBlockFee.eq(0)) { + while ((await taikoL1.getBlockFee()).eq(0)) { await sleep(500); - lastBlockFee = await taikoL1.getBlockFee(); } - let lastProofReward = BigNumber.from(0); - l2Provider.on("block", blockListener(chan, genesisHeight)); /* eslint-disable-next-line */ for await (const blockNumber of chan) { @@ -99,7 +93,7 @@ describe("tokenomics: blockFee", function () { ) { break; } - const { newProposerBalance, newBlockFee, newProofReward } = + const { newProposerBalance, newProofReward } = await onNewL2Block( l2Provider, blockNumber, @@ -114,16 +108,10 @@ describe("tokenomics: blockFee", function () { expect(newProposerBalance).to.be.lt(lastProposerBalance); - console.log("lastBlockFee", lastBlockFee); - console.log("newBlockFee", newBlockFee); - - expect(newBlockFee).to.be.gt(lastBlockFee); - console.log("lastProofReward", lastProofReward); console.log("newProofReward", newProofReward); expect(newProofReward).to.be.gt(lastProofReward); - lastBlockFee = newBlockFee; lastProofReward = newProofReward; lastProposerBalance = newProposerBalance; } diff --git a/packages/protocol/test/utils/onNewL2Block.ts b/packages/protocol/test/utils/onNewL2Block.ts index 6cfde5cc28..eed86f7377 100644 --- a/packages/protocol/test/utils/onNewL2Block.ts +++ b/packages/protocol/test/utils/onNewL2Block.ts @@ -31,7 +31,7 @@ async function onNewL2Block( const { enableTokenomics } = await taikoL1.getConfig(); const newProofReward = await taikoL1.getProofReward( - new Date().getMilliseconds(), + new Date().getTime(), meta.timestamp ); diff --git a/packages/protocol/utils/generate_config/main.py b/packages/protocol/utils/generate_config/main.py index fc47fac218..e0449f0fc4 100644 --- a/packages/protocol/utils/generate_config/main.py +++ b/packages/protocol/utils/generate_config/main.py @@ -8,10 +8,10 @@ print("Expected proof time (minutes)", end=": ") proof_time = int(input()) * 60 - print("Slot availability multiplier", end=": ") - slot_availability_multiplier = int(input()) - if slot_availability_multiplier <= 5: - print("error: Slot availability multiplier must be greater than 5") + print("Max baseFee upside (5 = 5x)", end=": ") + max_basefee_upside = int(input()) + if max_basefee_upside < 5: + print("error: Max baseFee upside < 5") exit(1) min_num_slots = math.ceil(1.0 * proof_time / block_time) @@ -24,7 +24,7 @@ print("min num slots:", min_num_slots) max_num_slots = min_num_slots + math.ceil(min_num_slots * extra_slots / 100) + 1 - k = slot_availability_multiplier + k = max_basefee_upside n = max_num_slots # https://www.wolframalpha.com/input?i=solve++%28n%2Bx%29%28n%2Bx-1%29%3Dk*%281%2Bx%29x+for+x