Skip to content

Commit

Permalink
feat(protocol): partially randomize prover reward (#13184)
Browse files Browse the repository at this point in the history
Co-authored-by: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com>
Co-authored-by: David <david@taiko.xyz>
  • Loading branch information
3 people committed Feb 22, 2023
1 parent 3e43c0c commit 16993cd
Show file tree
Hide file tree
Showing 20 changed files with 113 additions and 45 deletions.
1 change: 1 addition & 0 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ library TaikoData {
uint64 proofTimeCap;
uint64 bootstrapDiscountHalvingPeriod;
uint64 initialUncleDelay;
uint64 proverRewardRandomizedPercentage;
bool enableTokenomics;
bool enablePublicInputsCheck;
bool enableProofValidation;
Expand Down
6 changes: 6 additions & 0 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,15 @@ 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,
evidence.prover,
evidence.meta.txListHash
)
);

if (
!proofVerifier.verifyZKP({
verifierId: string(
Expand Down Expand Up @@ -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();
}

Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L1/libs/LibUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 72 additions & 18 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ library LibVerifying {
bytes32 latestL2Hash = state.l2Hashes[
latestL2Height % config.blockHashHistory
];
uint64 processed = 0;
uint64 processed;

for (
uint256 i = state.latestVerifiedId + 1;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L2/TaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/libs/LibReceiptDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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]
);
Expand All @@ -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]);
}

Expand Down
1 change: 1 addition & 0 deletions packages/protocol/contracts/libs/LibSharedConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ library LibSharedConfig {
proofTimeCap: 60 minutes,
bootstrapDiscountHalvingPeriod: 180 days,
initialUncleDelay: 60 minutes,
proverRewardRandomizedPercentage: 0,
enableTokenomics: false,
enablePublicInputsCheck: true,
enableProofValidation: false,
Expand Down
8 changes: 4 additions & 4 deletions packages/protocol/contracts/libs/LibTxDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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]));
}

Expand Down Expand Up @@ -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]
);
Expand All @@ -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);
Expand All @@ -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;
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/libs/LibTxUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/contracts/test/L1/TestTaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/contracts/test/L1/TestTaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/contracts/test/libs/TestLibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,15 @@ 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,
evidence.prover,
evidence.meta.txListHash
)
);

if (
!proofVerifier.verifyZKP({
verifierId: string(
Expand Down Expand Up @@ -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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/thirdparty/LibBytesUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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]);
}

Expand Down
8 changes: 4 additions & 4 deletions packages/protocol/contracts/thirdparty/LibMerkleTrie.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ library LibMerkleTrie {
bool _isFinalNode
)
{
uint256 pathLength = 0;
uint256 pathLength;
bytes memory key = LibBytesUtils.toNibbles(_key);

bytes32 currentNodeID = _root;
Expand All @@ -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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit 16993cd

Please sign in to comment.