diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 41886b9bcea..8843adfaada 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -38,6 +38,7 @@ library TaikoData { uint64 proofTimeCap; uint64 bootstrapDiscountHalvingPeriod; uint64 initialUncleDelay; + uint64 proverRewardRandomizedPercentage; bool enableTokenomics; bool enablePublicInputsCheck; bool enableProofValidation; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 2e199096adb..63593e53758 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -287,6 +287,12 @@ contract TaikoL1 is return LibUtils.getUncleProofDelay(state, getConfig(), blockId); } + function getProverRewardBips( + uint256 numProvers + ) public view returns (uint256[] memory) { + return LibVerifying.getProverRewardBips(getConfig(), numProvers); + } + function getConfig() public pure virtual returns (TaikoData.Config memory) { return LibSharedConfig.getConfig(); } diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 3614fa5c75d..1f99af5003a 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -284,7 +284,7 @@ library LibProving { bytes32 blockHash = evidence.header.hashBlockHeader(); if (!skipZKPVerification) { - for (uint256 i = 0; i < config.zkProofsPerBlock; ++i) { + for (uint256 i; i < config.zkProofsPerBlock; ++i) { bytes32 instance = keccak256( abi.encode( blockHash, @@ -292,6 +292,7 @@ library LibProving { evidence.meta.txListHash ) ); + if ( !proofVerifier.verifyZKP({ verifierId: string( @@ -357,7 +358,7 @@ library LibProving { }) ) revert L1_TOO_LATE(); - for (uint256 i = 0; i < fc.provers.length; ++i) { + for (uint256 i; i < fc.provers.length; ++i) { if (fc.provers[i] == prover) revert L1_DUP_PROVERS(); } diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index a5d8749093d..58073f30e9f 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -98,7 +98,7 @@ library LibUtils { ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { if (tAvg == 0) { newFeeBase = state.feeBase; - tRelBp = 0; + // tRelBp = 0; } else { uint256 _tAvg = tAvg > config.proofTimeCap ? config.proofTimeCap diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 8b18be76b55..13b4d884335 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -62,7 +62,7 @@ library LibVerifying { bytes32 latestL2Hash = state.l2Hashes[ latestL2Height % config.blockHashHistory ]; - uint64 processed = 0; + uint64 processed; for ( uint256 i = state.latestVerifiedId + 1; @@ -144,6 +144,63 @@ library LibVerifying { reward = (reward * (10000 - config.rewardBurnBips)) / 10000; } + /** + * A function that calculates the weight for each prover based on the number + * of provers and a random seed. The weight is a number between 0 and 100. + * The sum of the weights will be 100. The weight is calculated in bips, + * so the weight of 1 will be 0.01%. + * + * @param config The config of the Taiko protocol (stores the randomized percentage) + * @param numProvers The number of provers + * @return bips The weight of each prover in bips + */ + function getProverRewardBips( + TaikoData.Config memory config, + uint256 numProvers + ) public view returns (uint256[] memory bips) { + bips = new uint256[](numProvers); + + uint256 randomized = config.proverRewardRandomizedPercentage; + if (randomized > 100) { + randomized = 100; + } + + uint256 sum; + uint256 i; + + // Calculate the randomized weight + if (randomized > 0) { + unchecked { + uint256 seed = block.prevrandao; + for (i = 0; i < numProvers; ++i) { + // Get an uint16, note that smart provers may + // choose the right timing to maximize their rewards + // which helps blocks to be verified sooner. + bips[i] = uint16(seed * (1 + i)); + sum += bips[i]; + } + for (i = 0; i < numProvers; ++i) { + bips[i] = (bips[i] * 100 * randomized) / sum; + } + } + } + + // Add the fixed weight. If there are 5 provers, then their + // weight will be: + // 1<<4=16, 1<<3=8, 1<<2=4, 1<<1=2, 1<<0=1 + if (randomized != 100) { + unchecked { + sum = (1 << numProvers) - 1; + uint256 fix = 100 - randomized; + uint256 weight = 1 << (numProvers - 1); + for (i = 0; i < numProvers; ++i) { + bips[i] += (weight * 100 * fix) / sum; + weight >>= 1; + } + } + } + } + function _refundProposerDeposit( TaikoData.ProposedBlock storage target, uint256 tRelBp, @@ -162,30 +219,27 @@ library LibVerifying { uint256 reward, TkoToken tkoToken ) private { - uint256 start; + uint256 offset; uint256 count = fc.provers.length; if (config.enableOracleProver) { - start = 1; + offset = 1; count -= 1; } - uint256 sum = (1 << count) - 1; - uint256 weight = 1 << (count - 1); - for (uint i = 0; i < count; ++i) { - uint256 proverReward = (reward * weight) / sum; - if (proverReward == 0) { - break; - } + uint256[] memory bips = getProverRewardBips(config, count); - if (tkoToken.balanceOf(fc.provers[start + i]) == 0) { - // Reduce reward to 1 wei as a penalty if the prover - // has 0 TKO balance. This allows the next prover reward - // to be fully paid. - proverReward = uint256(1); + for (uint256 i; i < count; ++i) { + uint256 proverReward = (reward * bips[i]) / 10000; + if (proverReward != 0) { + if (tkoToken.balanceOf(fc.provers[offset + i]) == 0) { + // Reduce reward to 1 wei as a penalty if the prover + // has 0 TKO balance. This allows the next prover reward + // to be fully paid. + proverReward = uint256(1); + } + tkoToken.mint(fc.provers[offset + i], proverReward); } - tkoToken.mint(fc.provers[start + i], proverReward); - weight = weight >> 1; } } @@ -245,7 +299,7 @@ library LibVerifying { function _cleanUp(TaikoData.ForkChoice storage fc) private { fc.blockHash = 0; fc.provenAt = 0; - for (uint i = 0; i < fc.provers.length; ++i) { + for (uint256 i; i < fc.provers.length; ++i) { fc.provers[i] = address(0); } delete fc.provers; diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 4a61c1d33ca..20841415adf 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -64,7 +64,7 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { bytes32[255] memory ancestors; uint256 number = block.number; - for (uint256 i = 0; i < 255 && number >= i + 2; ++i) { + for (uint256 i; i < 255 && number >= i + 2; ++i) { ancestors[i] = blockhash(number - i - 2); } diff --git a/packages/protocol/contracts/libs/LibReceiptDecoder.sol b/packages/protocol/contracts/libs/LibReceiptDecoder.sol index d14e9197379..87174c9d54e 100644 --- a/packages/protocol/contracts/libs/LibReceiptDecoder.sol +++ b/packages/protocol/contracts/libs/LibReceiptDecoder.sol @@ -60,7 +60,7 @@ library LibReceiptDecoder { ) internal pure returns (Log[] memory) { Log[] memory logs = new Log[](logsRlp.length); - for (uint256 i = 0; i < logsRlp.length; ++i) { + for (uint256 i; i < logsRlp.length; ++i) { LibRLPReader.RLPItem[] memory rlpItems = LibRLPReader.readList( logsRlp[i] ); @@ -77,7 +77,7 @@ library LibReceiptDecoder { ) internal pure returns (bytes32[] memory) { bytes32[] memory topics = new bytes32[](topicsRlp.length); - for (uint256 i = 0; i < topicsRlp.length; ++i) { + for (uint256 i; i < topicsRlp.length; ++i) { topics[i] = LibRLPReader.readBytes32(topicsRlp[i]); } diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol index bfde1b6d260..9770762f539 100644 --- a/packages/protocol/contracts/libs/LibSharedConfig.sol +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -41,6 +41,7 @@ library LibSharedConfig { proofTimeCap: 60 minutes, bootstrapDiscountHalvingPeriod: 180 days, initialUncleDelay: 60 minutes, + proverRewardRandomizedPercentage: 0, enableTokenomics: false, enablePublicInputsCheck: true, enableProofValidation: false, diff --git a/packages/protocol/contracts/libs/LibTxDecoder.sol b/packages/protocol/contracts/libs/LibTxDecoder.sol index edbd1973146..c92e0160d86 100644 --- a/packages/protocol/contracts/libs/LibTxDecoder.sol +++ b/packages/protocol/contracts/libs/LibTxDecoder.sol @@ -82,7 +82,7 @@ library LibTxDecoder { LibRLPReader.RLPItem[] memory txs = LibRLPReader.readList(encoded); Tx[] memory _txList = new Tx[](txs.length); - for (uint256 i = 0; i < txs.length; ++i) { + for (uint256 i; i < txs.length; ++i) { _txList[i] = decodeTx(chainId, LibRLPReader.readBytes(txs[i])); } @@ -211,7 +211,7 @@ library LibTxDecoder { LibRLPReader.RLPItem[] memory accessListRLP ) internal pure returns (AccessItem[] memory accessList) { accessList = new AccessItem[](accessListRLP.length); - for (uint256 i = 0; i < accessListRLP.length; ++i) { + for (uint256 i; i < accessListRLP.length; ++i) { LibRLPReader.RLPItem[] memory items = LibRLPReader.readList( accessListRLP[i] ); @@ -220,7 +220,7 @@ library LibTxDecoder { items[1] ); bytes32[] memory slots = new bytes32[](slotListRLP.length); - for (uint256 j = 0; j < slotListRLP.length; ++j) { + for (uint256 j; j < slotListRLP.length; ++j) { slots[j] = LibRLPReader.readBytes32(slotListRLP[j]); } accessList[i] = AccessItem(addr, slots); @@ -231,7 +231,7 @@ library LibTxDecoder { TxList memory txList ) internal pure returns (uint256 sum) { Tx[] memory items = txList.items; - for (uint256 i = 0; i < items.length; ++i) { + for (uint256 i; i < items.length; ++i) { sum += items[i].gasLimit; } } diff --git a/packages/protocol/contracts/libs/LibTxUtils.sol b/packages/protocol/contracts/libs/LibTxUtils.sol index c7ef36419a2..d316a763bfb 100644 --- a/packages/protocol/contracts/libs/LibTxUtils.sol +++ b/packages/protocol/contracts/libs/LibTxUtils.sol @@ -55,7 +55,7 @@ library LibTxUtils { transaction.txType == 0 ? txRLPItems.length : txRLPItems.length - 3 ); - for (uint256 i = 0; i < list.length; ++i) { + for (uint256 i; i < list.length; ++i) { // For Non-legacy transactions, accessList is always the // fourth to last item. if (transaction.txType != 0 && i == list.length - 1) { diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index a4bde07d5e5..06024663730 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -47,6 +47,7 @@ contract TestTaikoL1 is TaikoL1, IProofVerifier { config.proofTimeCap = 4 seconds; config.bootstrapDiscountHalvingPeriod = 180 days; config.initialUncleDelay = 1 seconds; + config.proverRewardRandomizedPercentage = 0; config.enableTokenomics = false; config.enablePublicInputsCheck = false; config.enableOracleProver = false; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 68f634b48c9..8191bfca592 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -47,6 +47,7 @@ contract TestTaikoL1EnableTokenomics is TaikoL1, IProofVerifier { config.proofTimeCap = 5 seconds; config.bootstrapDiscountHalvingPeriod = 1 seconds; config.initialUncleDelay = 1 seconds; + config.proverRewardRandomizedPercentage = 0; config.enableTokenomics = true; config.enablePublicInputsCheck = false; config.enableProofValidation = false; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL2.sol b/packages/protocol/contracts/test/L1/TestTaikoL2.sol index ae786c018e1..648cea4b244 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL2.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL2.sol @@ -48,6 +48,7 @@ contract TestTaikoL2 is TaikoL2 { config.proofTimeCap = 60 minutes; config.bootstrapDiscountHalvingPeriod = 180 days; config.initialUncleDelay = 1 minutes; + config.proverRewardRandomizedPercentage = 0; config.enableTokenomics = true; config.enablePublicInputsCheck = false; config.enableProofValidation = false; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol b/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol index 7f479f97bbc..052e9bd38a6 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol @@ -48,6 +48,7 @@ contract TestTaikoL2EnablePublicInputsCheck is TaikoL2 { config.proofTimeCap = 60 minutes; config.bootstrapDiscountHalvingPeriod = 180 days; config.initialUncleDelay = 1 minutes; + config.proverRewardRandomizedPercentage = 0; config.enableTokenomics = true; config.enablePublicInputsCheck = true; config.enableProofValidation = true; diff --git a/packages/protocol/contracts/test/libs/TestLibProving.sol b/packages/protocol/contracts/test/libs/TestLibProving.sol index 24e5f87279d..7062c672722 100644 --- a/packages/protocol/contracts/test/libs/TestLibProving.sol +++ b/packages/protocol/contracts/test/libs/TestLibProving.sol @@ -290,7 +290,7 @@ library TestLibProving { bytes32 blockHash = evidence.header.hashBlockHeader(); if (!skipZKPVerification) { - for (uint256 i = 0; i < config.zkProofsPerBlock; ++i) { + for (uint256 i; i < config.zkProofsPerBlock; ++i) { bytes32 instance = keccak256( abi.encode( blockHash, @@ -298,6 +298,7 @@ library TestLibProving { evidence.meta.txListHash ) ); + if ( !proofVerifier.verifyZKP({ verifierId: string( @@ -363,7 +364,7 @@ library TestLibProving { }) ) revert L1_TOO_LATE(); - for (uint256 i = 0; i < fc.provers.length; ++i) { + for (uint256 i; i < fc.provers.length; ++i) { if (fc.provers[i] == prover) revert L1_DUP_PROVERS(); } diff --git a/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol b/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol index 9b665fba2f7..7de89cae7c3 100644 --- a/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol +++ b/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol @@ -11,7 +11,7 @@ contract TestLibRLPReader { function readList(bytes memory _in) public pure returns (bytes[] memory) { LibRLPReader.RLPItem[] memory decoded = LibRLPReader.readList(_in); bytes[] memory out = new bytes[](decoded.length); - for (uint256 i = 0; i < out.length; ++i) { + for (uint256 i; i < out.length; ++i) { out[i] = LibRLPReader.readRawBytes(decoded[i]); } return out; diff --git a/packages/protocol/contracts/thirdparty/LibBytesUtils.sol b/packages/protocol/contracts/thirdparty/LibBytesUtils.sol index 69f27184251..54e082a9cdc 100644 --- a/packages/protocol/contracts/thirdparty/LibBytesUtils.sol +++ b/packages/protocol/contracts/thirdparty/LibBytesUtils.sol @@ -142,7 +142,7 @@ library LibBytesUtils { ) internal pure returns (bytes memory) { bytes memory nibbles = new bytes(_bytes.length * 2); - for (uint256 i = 0; i < _bytes.length; ++i) { + for (uint256 i; i < _bytes.length; ++i) { nibbles[i * 2] = _bytes[i] >> 4; nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); } @@ -155,7 +155,7 @@ library LibBytesUtils { ) internal pure returns (bytes memory) { bytes memory ret = new bytes(_bytes.length / 2); - for (uint256 i = 0; i < ret.length; ++i) { + for (uint256 i; i < ret.length; ++i) { ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); } diff --git a/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol b/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol index 5c5446858d3..5f893543262 100644 --- a/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol +++ b/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol @@ -158,7 +158,7 @@ library LibMerkleTrie { bool _isFinalNode ) { - uint256 pathLength = 0; + uint256 pathLength; bytes memory key = LibBytesUtils.toNibbles(_key); bytes32 currentNodeID = _root; @@ -167,7 +167,7 @@ library LibMerkleTrie { TrieNode memory currentNode; // Proof is top-down, so we start at the first element (root). - for (uint256 i = 0; i < _proof.length; ++i) { + for (uint256 i; i < _proof.length; ++i) { currentNode = _proof[i]; currentKeyIndex += currentKeyIncrement; @@ -286,7 +286,7 @@ library LibMerkleTrie { LibRLPReader.RLPItem[] memory nodes = LibRLPReader.readList(_proof); TrieNode[] memory proof = new TrieNode[](nodes.length); - for (uint256 i = 0; i < nodes.length; ++i) { + for (uint256 i; i < nodes.length; ++i) { bytes memory encoded = LibRLPReader.readBytes(nodes[i]); proof[i] = TrieNode({ encoded: encoded, @@ -354,7 +354,7 @@ library LibMerkleTrie { bytes memory _a, bytes memory _b ) private pure returns (uint256 _shared) { - uint256 i = 0; + uint256 i; while (_a.length > i && _b.length > i && _a[i] == _b[i]) { ++i; } diff --git a/packages/protocol/contracts/thirdparty/LibRLPReader.sol b/packages/protocol/contracts/thirdparty/LibRLPReader.sol index 3df840372c8..09c51a04006 100644 --- a/packages/protocol/contracts/thirdparty/LibRLPReader.sol +++ b/packages/protocol/contracts/thirdparty/LibRLPReader.sol @@ -93,7 +93,7 @@ library LibRLPReader { // simply set a reasonable maximum list length and decrease the size before we finish. RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH); - uint256 itemCount = 0; + uint256 itemCount; uint256 offset = listOffset; while (offset < _in.length) { require( @@ -424,7 +424,7 @@ library LibRLPReader { } // Copy over as many complete words as we can. - for (uint256 i = 0; i < _length / 32; ++i) { + for (uint256 i; i < _length / 32; ++i) { assembly { mstore(dest, mload(src)) } diff --git a/packages/protocol/contracts/thirdparty/LibRLPWriter.sol b/packages/protocol/contracts/thirdparty/LibRLPWriter.sol index ae256de5e78..58934a53c16 100644 --- a/packages/protocol/contracts/thirdparty/LibRLPWriter.sol +++ b/packages/protocol/contracts/thirdparty/LibRLPWriter.sol @@ -172,7 +172,7 @@ library LibRLPWriter { function _toBinary(uint256 _x) private pure returns (bytes memory) { bytes memory b = abi.encodePacked(_x); - uint256 i = 0; + uint256 i; for (; i < 32; ++i) { if (b[i] != 0) { break; @@ -180,7 +180,7 @@ library LibRLPWriter { } bytes memory res = new bytes(32 - i); - for (uint256 j = 0; j < res.length; ++j) { + for (uint256 j; j < res.length; ++j) { res[j] = b[i++]; } @@ -198,10 +198,10 @@ library LibRLPWriter { ) private pure returns (bytes memory) { bytes memory b = abi.encodePacked(_x); - uint256 i = 0; + uint256 i; bytes memory res = new bytes(32); - for (uint256 j = 0; j < res.length; ++j) { + for (uint256 j; j < res.length; ++j) { res[j] = b[i++]; } @@ -253,7 +253,7 @@ library LibRLPWriter { } uint256 len; - uint256 i = 0; + uint256 i; for (; i < _list.length; ++i) { len += _list[i].length; }