Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): partially randomize prover reward #13184

Merged
merged 24 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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