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

fix(protocol): limit reward per gas in weight calculation #14098

Merged
merged 10 commits into from
Jul 5, 2023
66 changes: 38 additions & 28 deletions packages/protocol/contracts/L1/ProverPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@ contract ProverPool is EssentialContract, IProverPool {

struct Prover {
uint64 stakedAmount;
uint16 rewardPerGas;
uint16 currentCapacity;
uint64 weight;
uint32 rewardPerGas;
uint32 currentCapacity;
}

// Make sure we only use one slot
struct Staker {
uint64 exitRequestedAt;
uint64 exitAmount;
uint16 maxCapacity;
uint8 proverId; // 0 to indicate the staker is not a top prover
uint32 maxCapacity;
uint32 proverId; // 0 to indicate the staker is not a top prover
}

// Given that we only have 32 slots for the top provers, if the protocol
Expand Down Expand Up @@ -57,8 +56,8 @@ contract ProverPool is EssentialContract, IProverPool {
event Staked(
address indexed addr,
uint64 amount,
uint16 rewardPerGas,
uint16 currentCapacity
uint32 rewardPerGas,
uint32 currentCapacity
);

error CHANGE_TOO_FREQUENT();
Expand All @@ -80,22 +79,33 @@ contract ProverPool is EssentialContract, IProverPool {

function assignProver(
uint64 blockId,
uint32 /*feePerGas*/
uint32 feePerGas
)
external
onlyFromProtocol
returns (address prover, uint32 rewardPerGas)
{
unchecked {
uint32[MAX_NUM_PROVERS] memory effectiveRewardPerGas;
uint256[MAX_NUM_PROVERS] memory weights;
uint256 totalWeight;
Prover memory _prover;

for (uint8 i; i < MAX_NUM_PROVERS; ++i) {
for (uint32 i; i < MAX_NUM_PROVERS; ++i) {
_prover = provers[i + 1];
if (_prover.currentCapacity != 0) {
weights[i] = _prover.weight;
totalWeight += _prover.weight;
// Keep the effective rewardPerGas in [75-125%] of feePerGas
if (_prover.rewardPerGas > feePerGas * 125 / 100) {
effectiveRewardPerGas[i] = feePerGas * 125 / 100;
} else if (_prover.rewardPerGas < feePerGas * 75 / 100) {
effectiveRewardPerGas[i] = feePerGas * 75 / 100;
} else {
effectiveRewardPerGas[i] = _prover.rewardPerGas;
}
weights[i] = _calcWeight(
_prover.stakedAmount, effectiveRewardPerGas[i]
);
totalWeight += weights[i];
}
}

Expand All @@ -108,15 +118,16 @@ contract ProverPool is EssentialContract, IProverPool {
keccak256(abi.encode(blockhash(block.number - 1), blockId));
uint256 r = uint256(rand) % totalWeight + 1;
uint256 z;
uint8 id;
uint32 id;

while (z < r && id < MAX_NUM_PROVERS) {
z += weights[id++];
}
assert(id > 0);
provers[id].currentCapacity -= 1;

// Note that prover ID is 1 bigger than its index
return (idToProver[id], _prover.rewardPerGas);
return (idToProver[id], effectiveRewardPerGas[id - 1]);
}
}

Expand Down Expand Up @@ -160,11 +171,8 @@ contract ProverPool is EssentialContract, IProverPool {

if (prover.stakedAmount > _additional) {
provers[staker.proverId].stakedAmount -= _additional;
provers[staker.proverId].weight =
_calcWeight(prover.stakedAmount, prover.rewardPerGas);
} else {
provers[staker.proverId].stakedAmount = 0;
provers[staker.proverId].weight = 0;
}
}
emit Slashed(addr, amountToSlash);
Expand All @@ -173,8 +181,8 @@ contract ProverPool is EssentialContract, IProverPool {

function stake(
uint64 amount,
uint16 rewardPerGas,
uint16 maxCapacity
uint32 rewardPerGas,
uint32 maxCapacity
)
external
nonReentrant
Expand Down Expand Up @@ -240,8 +248,8 @@ contract ProverPool is EssentialContract, IProverPool {
function _stake(
address addr,
uint64 amount,
uint16 rewardPerGas,
uint16 maxCapacity
uint32 rewardPerGas,
uint32 maxCapacity
)
private
{
Expand Down Expand Up @@ -271,8 +279,8 @@ contract ProverPool is EssentialContract, IProverPool {
staker.maxCapacity = maxCapacity;

// Find the prover id
uint8 proverId = 1;
for (uint8 i = 2; i <= MAX_NUM_PROVERS;) {
uint32 proverId = 1;
for (uint32 i = 2; i <= MAX_NUM_PROVERS;) {
if (provers[proverId].stakedAmount > provers[i].stakedAmount) {
proverId = i;
}
Expand All @@ -298,8 +306,7 @@ contract ProverPool is EssentialContract, IProverPool {
provers[proverId] = Prover({
stakedAmount: amount,
rewardPerGas: rewardPerGas,
currentCapacity: maxCapacity,
weight: _calcWeight(amount, rewardPerGas)
currentCapacity: maxCapacity
});

emit Staked(addr, amount, rewardPerGas, maxCapacity);
Expand All @@ -326,7 +333,7 @@ contract ProverPool is EssentialContract, IProverPool {

// Delete the prover but make it non-zero for cheaper rewrites
// by keep rewardPerGas = 1
provers[staker.proverId] = Prover(0, 1, 0, 0);
provers[staker.proverId] = Prover(0, 1, 0);

delete idToProver[staker.proverId];

Expand Down Expand Up @@ -355,15 +362,18 @@ contract ProverPool is EssentialContract, IProverPool {
// Calculates the user weight's when it stakes/unstakes/slashed
function _calcWeight(
uint64 stakedAmount,
uint16 rewardPerGas
uint32 rewardPerGas
)
private
pure
returns (uint64)
returns (uint64 weight)
{
assert(rewardPerGas > 0);
unchecked {
return stakedAmount / rewardPerGas / rewardPerGas;
weight = stakedAmount / rewardPerGas / rewardPerGas;
if (weight == 0) {
weight = 1;
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/protocol/contracts/L1/TaikoConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ library TaikoConfig {
ethDepositGas: 21_000,
ethDepositMaxFee: 1 ether / 10,
// Group 5: tokenomics
rewardPerGasRange: 2000, // 20%
rewardOpenMultipler: 200, // percentage
rewardOpenMaxCount: 2000
});
Expand Down
1 change: 0 additions & 1 deletion packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ library TaikoData {
uint256 ethDepositGas;
uint256 ethDepositMaxFee;
// Group 5: tokenomics
uint32 rewardPerGasRange;
uint8 rewardOpenMultipler;
uint256 rewardOpenMaxCount;
}
Expand Down
13 changes: 2 additions & 11 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,9 @@ library LibProposing {
++state.numOpenBlocks;
} else {
blk.assignedProver = assignedProver;

// Cap the reward to a range of [80%, 120%] * blk.feePerGas, if
// rewardPerGasRange is set to 20% (2000 bp)
uint32 diff = blk.feePerGas * config.rewardPerGasRange / 10_000;
blk.rewardPerGas = uint32(
uint256(rewardPerGas).min(state.feePerGas + diff).max(
state.feePerGas - diff
)
);

blk.rewardPerGas = rewardPerGas;
blk.proofWindow = uint16(
uint256(state.avgProofDelay * 3).min(config.proofMaxWindow).max(
uint256(state.avgProofDelay * 2).min(config.proofMaxWindow).max(
config.proofMinWindow
)
);
Expand Down
2 changes: 0 additions & 2 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ library LibVerifying {
|| config.ethDepositMaxFee >= type(uint96).max
|| config.ethDepositMaxFee
>= type(uint96).max / config.ethDepositMaxCountPerBlock
|| config.rewardPerGasRange == 0
|| config.rewardPerGasRange >= 10_000
|| config.rewardOpenMultipler < 100
) revert L1_INVALID_CONFIG();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ title: ProverPool
```solidity
struct Prover {
uint64 stakedAmount;
uint16 rewardPerGas;
uint16 currentCapacity;
uint64 weight;
uint32 rewardPerGas;
uint32 currentCapacity;
}
```

Expand All @@ -21,8 +20,8 @@ struct Prover {
struct Staker {
uint64 exitRequestedAt;
uint64 exitAmount;
uint16 maxCapacity;
uint8 proverId;
uint32 maxCapacity;
uint32 proverId;
}
```

Expand Down Expand Up @@ -107,7 +106,7 @@ event Slashed(address addr, uint64 amount)
### Staked

```solidity
event Staked(address addr, uint64 amount, uint16 rewardPerGas, uint16 currentCapacity)
event Staked(address addr, uint64 amount, uint32 rewardPerGas, uint32 currentCapacity)
```

### CHANGE_TOO_FREQUENT
Expand Down Expand Up @@ -155,7 +154,7 @@ function init(address _addressManager) external
### assignProver

```solidity
function assignProver(uint64 blockId, uint32) external returns (address prover, uint32 rewardPerGas)
function assignProver(uint64 blockId, uint32 feePerGas) external returns (address prover, uint32 rewardPerGas)
```

### releaseProver
Expand All @@ -173,7 +172,7 @@ function slashProver(address addr) external
### stake

```solidity
function stake(uint64 amount, uint16 rewardPerGas, uint16 maxCapacity) external
function stake(uint64 amount, uint32 rewardPerGas, uint32 maxCapacity) external
```

### exit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ struct Config {
uint96 ethDepositMaxAmount;
uint256 ethDepositGas;
uint256 ethDepositMaxFee;
uint32 rewardPerGasRange;
uint8 rewardOpenMultipler;
uint256 rewardOpenMaxCount;
}
Expand Down