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): ProverPool gas optimizations #14062

Merged
merged 12 commits into from
Jun 28, 2023
39 changes: 37 additions & 2 deletions packages/protocol/contracts/L1/ProverPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ contract ProverPool is EssentialContract, IProverPool {
// reserve more slots than necessary
uint256[10_000] private proverData;
mapping(uint256 id => address prover) public idToProver;
// Save the weights only when: stake / unstaked / slashed
mapping(uint256 id => uint256 weights) public idToWeights;
mapping(address staker => Staker) public stakers;

uint256[48] private __gap;
Expand Down Expand Up @@ -153,6 +155,13 @@ contract ProverPool is EssentialContract, IProverPool {
_saveProver(staker.proverId, prover);
}

uint256 proverWeight = _calcWeight2(
staker.maxCapacity,
prover.stakedAmount * ONE_TKO,
prover.rewardPerGas
);
idToWeights[staker.proverId] = proverWeight;

emit Slashed(addr, amountToSlash);
}

Expand Down Expand Up @@ -224,16 +233,20 @@ contract ProverPool is EssentialContract, IProverPool {
_stakers[i] = idToProver[i + 1];
}
}
//Returns each prover's weight dynamically based on feePerGas.

//Returns each prover's weight dynamically based on feePerGas.
function getWeights(uint32 feePerGas)
public
view
returns (uint256[MAX_NUM_PROVERS] memory weights, uint256 totalWeight)
{
for (uint8 i; i < MAX_NUM_PROVERS; ++i) {
Prover memory prover = _loadProver(i + 1);
weights[i] = _calcWeight(prover, feePerGas);
if (prover.currentCapacity == 0) {
weights[i] = 0;
} else {
weights[i] = idToWeights[i + 1];
}
totalWeight += weights[i];
}
}
Expand Down Expand Up @@ -296,6 +309,10 @@ contract ProverPool is EssentialContract, IProverPool {
_exit(replaced);
// }
idToProver[proverId] = addr;
// Keep track of weights when changes ()
uint256 proverWeight =
_calcWeight2(maxCapacity, amount * ONE_TKO, rewardPerGas);
idToWeights[proverId] = proverWeight;

// Assign the staker this proverId
staker.proverId = proverId;
Expand All @@ -319,6 +336,7 @@ contract ProverPool is EssentialContract, IProverPool {
if (staker.proverId == 0) return;

delete idToProver[staker.proverId];
delete idToWeights[staker.proverId];

// Delete the prover but make it non-zero for cheaper rewrites
// by keep rewardPerGas = 1
Expand Down Expand Up @@ -373,6 +391,23 @@ contract ProverPool is EssentialContract, IProverPool {
}
}

// Calculates the user weight's when it stakes/unstakes/slashed
function _calcWeight2(
uint16 currentCapacity,
uint64 stakedAmount,
uint16 rewardPerGas
)
private
pure
returns (uint256)
{
if (currentCapacity == 0 || stakedAmount == 0 || rewardPerGas == 0) {
return 0;
} else {
return uint256(stakedAmount) / rewardPerGas / rewardPerGas;
dantaik marked this conversation as resolved.
Show resolved Hide resolved
}
}

function _loadProver(uint256 proverId)
private
view
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ library TaikoData {
mapping(bytes32 txListHash => TxListInfo) txListInfo;
mapping(uint256 depositId_mode_ethDepositRingBufferSize => uint256)
ethDeposits;
mapping(address account => uint256 balance) taikoTokenBalances;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__gap should be changed to 42

// Never or rarely changed
// Slot 7: never or rarely changed
uint64 genesisHeight;
Expand Down
4 changes: 3 additions & 1 deletion packages/protocol/contracts/L1/TaikoEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { TaikoData } from "./TaikoData.sol";
abstract contract TaikoEvents {
// The following events must match the definitions in corresponding L1
// libraries.
event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta);
event BlockProposed(
uint256 indexed id, TaikoData.BlockMetadata meta, uint64 blockFee
);

event BlockProven(
uint256 indexed id,
Expand Down
14 changes: 14 additions & 0 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Proxied } from "../common/Proxied.sol";
import { LibEthDepositing } from "./libs/LibEthDepositing.sol";
import { LibProposing } from "./libs/LibProposing.sol";
import { LibProving } from "./libs/LibProving.sol";
import { LibTkoDistribution } from "./libs/LibTkoDistribution.sol";
import { LibUtils } from "./libs/LibUtils.sol";
import { LibVerifying } from "./libs/LibVerifying.sol";
import { TaikoConfig } from "./TaikoConfig.sol";
Expand Down Expand Up @@ -155,6 +156,19 @@ contract TaikoL1 is
});
}

// From proposer side - same way paying the fees - and saving gas.
function depositTaikoToken(uint256 amount) external nonReentrant {
LibTkoDistribution.depositTaikoToken(
state, AddressResolver(this), amount
);
}

function withdrawTaikoToken(uint256 amount) external nonReentrant {
LibTkoDistribution.withdrawTaikoToken(
state, AddressResolver(this), amount
);
}

function canDepositEthToL2(uint256 amount) public view returns (bool) {
return LibEthDepositing.canDepositEthToL2({
state: state,
Expand Down
16 changes: 10 additions & 6 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ library LibProposing {
using LibUtils for TaikoData.State;
using SafeCastUpgradeable for uint256;

event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta);
event BlockProposed(
uint256 indexed id, TaikoData.BlockMetadata meta, uint64 blockFee
);

error L1_BLOCK_ID();
error L1_INSUFFICIENT_TOKEN();
Expand Down Expand Up @@ -137,15 +139,17 @@ library LibProposing {
);
}

IMintableERC20(resolver.resolve("taiko_token", false)).burn({
from: msg.sender,
amount: getBlockFee(state, config, meta.gasLimit)
});
uint64 blockFee = getBlockFee(state, config, meta.gasLimit);

emit BlockProposed(state.numBlocks, meta);
if (state.taikoTokenBalances[msg.sender] < blockFee) {
revert L1_INSUFFICIENT_TOKEN();
}

emit BlockProposed(state.numBlocks, meta, blockFee);

unchecked {
++state.numBlocks;
state.taikoTokenBalances[msg.sender] -= blockFee;
}
}

Expand Down
54 changes: 54 additions & 0 deletions packages/protocol/contracts/L1/libs/LibTkoDistribution.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
// _____ _ _ _ _
// |_ _|_ _(_) |_____ | | __ _| |__ ___
// | |/ _` | | / / _ \ | |__/ _` | '_ (_-<
// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/

pragma solidity ^0.8.20;

import { AddressResolver } from "../../common/AddressResolver.sol";
import { LibMath } from "../../libs/LibMath.sol";
import { SafeCastUpgradeable } from
"@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import { TaikoData } from "../TaikoData.sol";
import { TaikoToken } from "../TaikoToken.sol";
import { LibFixedPointMath as Math } from
"../../thirdparty/LibFixedPointMath.sol";

library LibTkoDistribution {
error L1_INSUFFICIENT_TOKEN();

function withdrawTaikoToken(
TaikoData.State storage state,
AddressResolver resolver,
uint256 amount
)
internal
{
uint256 balance = state.taikoTokenBalances[msg.sender];
if (balance < amount) revert L1_INSUFFICIENT_TOKEN();

unchecked {
state.taikoTokenBalances[msg.sender] -= amount;
}

TaikoToken(resolver.resolve("taiko_token", false)).mint(
msg.sender, amount
);
}

function depositTaikoToken(
TaikoData.State storage state,
AddressResolver resolver,
uint256 amount
)
internal
{
if (amount > 0) {
TaikoToken(resolver.resolve("taiko_token", false)).burn(
msg.sender, amount
);
state.taikoTokenBalances[msg.sender] += amount;
}
}
}
17 changes: 6 additions & 11 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ library LibVerifying {
fcId = LibUtils.getForkChoiceId(state, blk, blockHash, gasUsed);
if (fcId == 0) break;

TaikoData.ForkChoice storage fc = blk.forkChoices[fcId];
TaikoData.ForkChoice memory fc = blk.forkChoices[fcId];
if (fc.prover == address(0)) break;

uint256 proofRegularCooldown = fc.prover == address(1)
Expand Down Expand Up @@ -177,7 +177,7 @@ library LibVerifying {
TaikoData.Config memory config,
AddressResolver resolver,
TaikoData.Block storage blk,
TaikoData.ForkChoice storage fc,
TaikoData.ForkChoice memory fc,
uint24 fcId
)
private
Expand Down Expand Up @@ -236,16 +236,11 @@ library LibVerifying {

blk.verifiedForkChoiceId = fcId;

IMintableERC20 taikoToken =
IMintableERC20(resolver.resolve("taiko_token", false));

// Reward the prover
taikoToken.mint(fc.prover, proofReward);
// Refund the diff to the proposer
taikoToken.mint({
to: blk.proposer,
amount: (_gasLimit - fc.gasUsed) * blk.feePerGas
});
state.taikoTokenBalances[fc.prover] += proofReward;

state.taikoTokenBalances[blk.proposer] +=
(_gasLimit - fc.gasUsed) * blk.feePerGas;

emit BlockVerified(blk.blockId, fc.blockHash, proofReward);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/test/TaikoL1TestBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,9 @@ abstract contract TaikoL1TestBase is Test {
tko.transfer(who, amountTko);
console2.log("who", who);
console2.log("balance:", tko.balanceOf(who));
// vm.prank(who, who);
// L1.depositTaikoToken(amountTko);
vm.prank(who, who);
// Keep half for proving and deposit half for proposing fee
L1.depositTaikoToken(amountTko / 2);
}

function printVariables(string memory comment) internal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ struct State {
mapping(uint256 => mapping(bytes32 => mapping(uint32 => uint24))) forkChoiceIds;
mapping(bytes32 => struct TaikoData.TxListInfo) txListInfo;
mapping(uint256 => uint256) ethDeposits;
mapping(address => uint256) taikoTokenBalances;
uint64 genesisHeight;
uint64 genesisTimestamp;
uint64 __reserved70;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: TaikoEvents
### BlockProposed

```solidity
event BlockProposed(uint256 id, struct TaikoData.BlockMetadata meta)
event BlockProposed(uint256 id, struct TaikoData.BlockMetadata meta, uint64 blockFee)
```

### BlockProven
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ Verify up to N blocks.
function depositEtherToL2(address recipient) public payable
```

### depositTaikoToken

```solidity
function depositTaikoToken(uint256 amount) external
```

### withdrawTaikoToken

```solidity
function withdrawTaikoToken(uint256 amount) external
```

### canDepositEthToL2

```solidity
Expand Down