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 11 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
73 changes: 56 additions & 17 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,48 @@ library LibVerifying {
reward = (reward * (10000 - config.rewardBurnBips)) / 10000;
}

function getProverRewardBips(
TaikoData.Config memory config,
uint256 numProvers
) public view returns (uint256[] memory weights) {
uint256 randomized = config.proverRewardRandomizedPercentage;
if (randomized > 100) {
randomized = 100;
}

uint256 sum;
uint256 i;

if (randomized > 0) {
unchecked {
// Calculate the randomized weight
uint256 seed = block.prevrandao;
for (i = 0; i < numProvers; ++i) {
weights[i] = uint16(seed * (1 + i));
cyberhorsey marked this conversation as resolved.
Show resolved Hide resolved
sum += weights[i];
}
for (i = 0; i < numProvers; ++i) {
weights[i] = (weights[i] * 100 * randomized) / sum;
}
}
}

if (randomized != 100) {
unchecked {
// 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
sum = (1 << numProvers) - 1;
uint256 fix = 100 - randomized;
uint256 weight = 1 << (numProvers - 1);
for (i = 0; i < numProvers; ++i) {
weights[i] += (weight * 100 * fix) / sum;
weight >>= 1;
}
}
}
}

function _refundProposerDeposit(
TaikoData.ProposedBlock storage target,
uint256 tRelBp,
Expand All @@ -166,30 +208,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 weights = 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 = 0; i < count; ++i) {
uint256 proverReward = (reward * weights[i]) / 10000;
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -249,7 +288,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 = 0; i < fc.provers.length; ++i) {
fc.provers[i] = address(0);
}
delete fc.provers;
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
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 = 20;
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 = 20;
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 = 20;
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
114 changes: 57 additions & 57 deletions packages/protocol/test/tokenomics/proofReward.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,61 +215,61 @@ describe("tokenomics: proofReward", function () {
}
});

it(`asserts that with N provers, where N is config.maxProofsPerForkChoice all provers who submit proofs are paid with decreasing weight from the first prover to the Nth`, async function () {
const provers = (
await createAndSeedWallets(
config.maxProofsPerForkChoice.toNumber(),
l1Signer
)
).map((p: ethers.Wallet) => new Prover(taikoL1, l2Provider, p));

await seedTko(provers, tkoTokenL1.connect(l1Signer));

const blockNumber = genesisHeight + 1;

const block = await l2Provider.getBlock(blockNumber);

// commit and propose block, so our provers can prove it.
const { proposedEvent } = await proposer.commitThenProposeBlock(block);

// submit a proof for each prover
for (const prover of provers) {
await prover.prove(
proposedEvent.args.id.toNumber(),
blockNumber,
proposedEvent.args.meta as any as BlockMetadata
);
}

const forkChoice = await taikoL1.getForkChoice(
proposedEvent.args.id.toNumber(),
block.parentHash
);
expect(forkChoice).not.to.be.undefined;
expect(forkChoice.provers.length).to.be.eq(
config.maxProofsPerForkChoice.toNumber()
);

await sleepUntilBlockIsVerifiable(
taikoL1,
proposedEvent.args.id.toNumber(),
0
);
await verifyBlocks(taikoL1, 1);

// all provers had same initial TKO balance.
// each prover in order should have less balance than the previous.
for (let i = 0; i < forkChoice.provers.length; i++) {
if (i !== 0) {
const proverBalance = await tkoTokenL1.balanceOf(
forkChoice.provers[i]
);
const previousProverBalance = await tkoTokenL1.balanceOf(
forkChoice.provers[i - 1]
);

expect(previousProverBalance.gt(proverBalance)).to.be.eq(true);
}
}
});
// it(`asserts that with N provers, where N is config.maxProofsPerForkChoice all provers who submit proofs are paid with decreasing weight from the first prover to the Nth`, async function () {
dantaik marked this conversation as resolved.
Show resolved Hide resolved
// const provers = (
// await createAndSeedWallets(
// config.maxProofsPerForkChoice.toNumber(),
// l1Signer
// )
// ).map((p: ethers.Wallet) => new Prover(taikoL1, l2Provider, p));

// await seedTko(provers, tkoTokenL1.connect(l1Signer));

// const blockNumber = genesisHeight + 1;

// const block = await l2Provider.getBlock(blockNumber);

// // commit and propose block, so our provers can prove it.
// const { proposedEvent } = await proposer.commitThenProposeBlock(block);

// // submit a proof for each prover
// for (const prover of provers) {
// await prover.prove(
// proposedEvent.args.id.toNumber(),
// blockNumber,
// proposedEvent.args.meta as any as BlockMetadata
// );
// }

// const forkChoice = await taikoL1.getForkChoice(
// proposedEvent.args.id.toNumber(),
// block.parentHash
// );
// expect(forkChoice).not.to.be.undefined;
// expect(forkChoice.provers.length).to.be.eq(
// config.maxProofsPerForkChoice.toNumber()
// );

// await sleepUntilBlockIsVerifiable(
// taikoL1,
// proposedEvent.args.id.toNumber(),
// 0
// );
// await verifyBlocks(taikoL1, 1);

// // all provers had same initial TKO balance.
// // each prover in order should have less balance than the previous.
// for (let i = 0; i < forkChoice.provers.length; i++) {
// if (i !== 0) {
// const proverBalance = await tkoTokenL1.balanceOf(
// forkChoice.provers[i]
// );
// const previousProverBalance = await tkoTokenL1.balanceOf(
// forkChoice.provers[i - 1]
// );

// expect(previousProverBalance.gt(proverBalance)).to.be.eq(true);
// }
// }
// });
});