From 19119053ee4dbd1b573f78b78ee8cbf2aa59a62b Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 12:13:14 +0200 Subject: [PATCH 01/11] Optimization of staking flow nr1 --- packages/protocol/contracts/L1/TaikoData.sol | 1 + .../protocol/contracts/L1/TaikoEvents.sol | 2 +- packages/protocol/contracts/L1/TaikoL1.sol | 14 +++++ .../contracts/L1/libs/LibProposing.sol | 14 ++--- .../contracts/L1/libs/LibTkoDistribution.sol | 54 +++++++++++++++++++ .../contracts/L1/libs/LibVerifying.sol | 12 ++--- packages/protocol/test/TaikoL1TestBase.t.sol | 5 +- 7 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 packages/protocol/contracts/L1/libs/LibTkoDistribution.sol diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 049bc0b4065..725ee1a0174 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -147,6 +147,7 @@ library TaikoData { mapping(bytes32 txListHash => TxListInfo) txListInfo; mapping(uint256 depositId_mode_ethDepositRingBufferSize => uint256) ethDeposits; + mapping(address account => uint256 balance) taikoTokenBalances; // Never or rarely changed // Slot 7: never or rarely changed uint64 genesisHeight; diff --git a/packages/protocol/contracts/L1/TaikoEvents.sol b/packages/protocol/contracts/L1/TaikoEvents.sol index a2067839286..ec2a23d5d18 100644 --- a/packages/protocol/contracts/L1/TaikoEvents.sol +++ b/packages/protocol/contracts/L1/TaikoEvents.sol @@ -11,7 +11,7 @@ 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, diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 12aa6e5c5d0..d50fd86e77a 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -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"; @@ -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, diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index d3788b93cbe..5684e66675f 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -25,7 +25,7 @@ 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(); @@ -137,15 +137,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; } } diff --git a/packages/protocol/contracts/L1/libs/LibTkoDistribution.sol b/packages/protocol/contracts/L1/libs/LibTkoDistribution.sol new file mode 100644 index 00000000000..7bf1766d7b6 --- /dev/null +++ b/packages/protocol/contracts/L1/libs/LibTkoDistribution.sol @@ -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; + } + } +} diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index a2cfc780160..ba61781d923 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -236,16 +236,10 @@ 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); } diff --git a/packages/protocol/test/TaikoL1TestBase.t.sol b/packages/protocol/test/TaikoL1TestBase.t.sol index d4c67f7a706..de39060efdb 100644 --- a/packages/protocol/test/TaikoL1TestBase.t.sol +++ b/packages/protocol/test/TaikoL1TestBase.t.sol @@ -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 { From 2cec9221a9d9dc6f086c2d3bf44f485022ec1042 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 10:20:23 +0000 Subject: [PATCH 02/11] Add auto-generated contract documentation --- .../reference/contract-documentation/L1/TaikoData.md | 1 + .../contract-documentation/L1/TaikoEvents.md | 2 +- .../reference/contract-documentation/L1/TaikoL1.md | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index d0622de70c2..6340fe2218a 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -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; diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md index a48d893897b..bb83edac2b2 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md @@ -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 diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md index 94043ce136e..92726bc4481 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoL1.md @@ -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 From 449f8bf96a7e6084808fd22c8a3fe1ba6d3a5536 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 14:57:41 +0200 Subject: [PATCH 03/11] Change read-only var to memory --- packages/protocol/contracts/L1/libs/LibVerifying.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index ba61781d923..cb0e6794dde 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -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) @@ -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 From 9345180ac0ba5ec070a58f5c80227359c69fbcc9 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 16:56:02 +0200 Subject: [PATCH 04/11] Additional optimization on calcWeight --- packages/protocol/contracts/L1/ProverPool.sol | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/L1/ProverPool.sol b/packages/protocol/contracts/L1/ProverPool.sol index ec9da7aaa85..363fb44ad1d 100644 --- a/packages/protocol/contracts/L1/ProverPool.sol +++ b/packages/protocol/contracts/L1/ProverPool.sol @@ -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; @@ -153,6 +155,9 @@ 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); } @@ -224,8 +229,8 @@ 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 @@ -233,7 +238,12 @@ contract ProverPool is EssentialContract, IProverPool { { 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]; } } @@ -296,6 +306,9 @@ 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; @@ -319,6 +332,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 @@ -373,6 +387,27 @@ 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; + } + } + function _loadProver(uint256 proverId) private view From 95dde9ad7f33cf09af1850408edcc8708a287749 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 16:59:46 +0200 Subject: [PATCH 05/11] linting --- packages/protocol/contracts/L1/ProverPool.sol | 24 +++++++++---------- .../protocol/contracts/L1/TaikoEvents.sol | 4 +++- .../contracts/L1/libs/LibProposing.sol | 4 +++- .../contracts/L1/libs/LibVerifying.sol | 3 ++- packages/protocol/test/TaikoL1TestBase.t.sol | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/protocol/contracts/L1/ProverPool.sol b/packages/protocol/contracts/L1/ProverPool.sol index 363fb44ad1d..0674a0a248f 100644 --- a/packages/protocol/contracts/L1/ProverPool.sol +++ b/packages/protocol/contracts/L1/ProverPool.sol @@ -155,7 +155,11 @@ contract ProverPool is EssentialContract, IProverPool { _saveProver(staker.proverId, prover); } - uint256 proverWeight = _calcWeight2(staker.maxCapacity, prover.stakedAmount * ONE_TKO, prover.rewardPerGas); + uint256 proverWeight = _calcWeight2( + staker.maxCapacity, + prover.stakedAmount * ONE_TKO, + prover.rewardPerGas + ); idToWeights[staker.proverId] = proverWeight; emit Slashed(addr, amountToSlash); @@ -238,11 +242,10 @@ contract ProverPool is EssentialContract, IProverPool { { for (uint8 i; i < MAX_NUM_PROVERS; ++i) { Prover memory prover = _loadProver(i + 1); - if(prover.currentCapacity == 0) { + if (prover.currentCapacity == 0) { weights[i] = 0; - } - else { - weights[i] = idToWeights[i+1]; + } else { + weights[i] = idToWeights[i + 1]; } totalWeight += weights[i]; } @@ -307,7 +310,8 @@ contract ProverPool is EssentialContract, IProverPool { // } idToProver[proverId] = addr; // Keep track of weights when changes () - uint256 proverWeight = _calcWeight2(maxCapacity, amount * ONE_TKO, rewardPerGas); + uint256 proverWeight = + _calcWeight2(maxCapacity, amount * ONE_TKO, rewardPerGas); idToWeights[proverId] = proverWeight; // Assign the staker this proverId @@ -397,14 +401,10 @@ contract ProverPool is EssentialContract, IProverPool { pure returns (uint256) { - if ( - currentCapacity == 0 || stakedAmount == 0 - || rewardPerGas == 0 - ) { + if (currentCapacity == 0 || stakedAmount == 0 || rewardPerGas == 0) { return 0; } else { - return uint256 (stakedAmount) - / rewardPerGas / rewardPerGas; + return uint256(stakedAmount) / rewardPerGas / rewardPerGas; } } diff --git a/packages/protocol/contracts/L1/TaikoEvents.sol b/packages/protocol/contracts/L1/TaikoEvents.sol index ec2a23d5d18..7b704165555 100644 --- a/packages/protocol/contracts/L1/TaikoEvents.sol +++ b/packages/protocol/contracts/L1/TaikoEvents.sol @@ -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, uint64 blockFee); + event BlockProposed( + uint256 indexed id, TaikoData.BlockMetadata meta, uint64 blockFee + ); event BlockProven( uint256 indexed id, diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 5684e66675f..1225011e014 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -25,7 +25,9 @@ library LibProposing { using LibUtils for TaikoData.State; using SafeCastUpgradeable for uint256; - event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta, uint64 blockFee); + event BlockProposed( + uint256 indexed id, TaikoData.BlockMetadata meta, uint64 blockFee + ); error L1_BLOCK_ID(); error L1_INSUFFICIENT_TOKEN(); diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index cb0e6794dde..d3a46de0298 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -239,7 +239,8 @@ library LibVerifying { // Reward the prover state.taikoTokenBalances[fc.prover] += proofReward; - state.taikoTokenBalances[blk.proposer] += (_gasLimit - fc.gasUsed) * blk.feePerGas; + state.taikoTokenBalances[blk.proposer] += + (_gasLimit - fc.gasUsed) * blk.feePerGas; emit BlockVerified(blk.blockId, fc.blockHash, proofReward); } diff --git a/packages/protocol/test/TaikoL1TestBase.t.sol b/packages/protocol/test/TaikoL1TestBase.t.sol index de39060efdb..33d9b2858a5 100644 --- a/packages/protocol/test/TaikoL1TestBase.t.sol +++ b/packages/protocol/test/TaikoL1TestBase.t.sol @@ -229,7 +229,7 @@ abstract contract TaikoL1TestBase is Test { console2.log("balance:", tko.balanceOf(who)); vm.prank(who, who); // Keep half for proving and deposit half for proposing fee - L1.depositTaikoToken(amountTko/2); + L1.depositTaikoToken(amountTko / 2); } function printVariables(string memory comment) internal { From e6f68f59cdd900347aa322a904d7b2f3268005ed Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 15:04:42 +0000 Subject: [PATCH 06/11] Add auto-generated contract documentation --- .../docs/reference/contract-documentation/L1/ProverPool.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool.md b/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool.md index f5816cd97fb..c16d174e3c6 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool.md @@ -67,6 +67,12 @@ uint256 MAX_NUM_PROVERS mapping(uint256 => address) idToProver ``` +### idToWeights + +```solidity +mapping(uint256 => uint256) idToWeights +``` + ### stakers ```solidity From 0a36e17fe830bc93a82ec9925b6c50d6f303403f Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 17:09:59 +0200 Subject: [PATCH 07/11] Changed gap --- packages/protocol/contracts/L1/TaikoData.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 725ee1a0174..2074bc9090b 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -166,6 +166,6 @@ library TaikoData { uint32 feePerGas; uint16 avgProofDelay; // Reserved - uint256[43] __gap; // TODO: update this + uint256[42] __gap; // TODO: update this } } From cdb79bcee2eb68fd576e1053196172605bfbf986 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 15:14:57 +0000 Subject: [PATCH 08/11] Add auto-generated contract documentation --- .../pages/docs/reference/contract-documentation/L1/TaikoData.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 6340fe2218a..bd2c625a422 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -172,6 +172,6 @@ struct State { uint64 __reserved90; uint32 feePerGas; uint16 avgProofDelay; - uint256[43] __gap; + uint256[42] __gap; } ``` From 83f28f8b2a3d3bfcfd16415770dc2fc76456be2c Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 22:46:01 +0200 Subject: [PATCH 09/11] Develop further ProverPool2 --- .../protocol/contracts/L1/ProverPool2.sol | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/packages/protocol/contracts/L1/ProverPool2.sol b/packages/protocol/contracts/L1/ProverPool2.sol index 8741d4f2486..b8024b83e85 100644 --- a/packages/protocol/contracts/L1/ProverPool2.sol +++ b/packages/protocol/contracts/L1/ProverPool2.sol @@ -15,24 +15,32 @@ import { Proxied } from "../common/Proxied.sol"; contract ProverPool2 is EssentialContract { uint256 public constant NUM_SLOTS = 128; uint256 public constant EXIT_PERIOD = 1 weeks; - uint32 public constant SLASH_POINTS = 500; // basis points + uint32 public constant SLASH_POINTS = 9500; // basis points uint256 public totalStaked; uint256 public totalWeight; - error CAPACITY_TOO_HIGH(); + error CAPACITY_INCORRECT(); error NOT_ENOUGH_BALANCE(); + error CANNOT_BE_PREFERRED(); struct Staker { uint256 amount; uint256 numSlots; - uint256 maxNumSlots; //Max capacity if someone else's unstake would + // If type(uint256).max = signals prover can prove all of the blocks + // Then gets into the preferredProver (if he is also the max prover) + uint256 maxNumSlots; // Max capacity if someone else's unstake would // increase a prover's slot count uint256 unstakedAt; - uint256 unstakedAmount; uint16 rewardPerGas; } + // Temporary staker who could jump in as a new prover + // when someone unstakes and we need to fill their slots + // - until the 'weight-based-owner' claims them (!) + // So we basically don't increase anyone's slots unintentionally + address preferredProver; + mapping(uint256 slot => address) slots; mapping(address staker => Staker) stakers; @@ -70,8 +78,8 @@ contract ProverPool2 is EssentialContract { ) external { - if (maxCapacity > NUM_SLOTS) { - revert CAPACITY_TOO_HIGH(); + if (maxCapacity > NUM_SLOTS && (maxCapacity != type(uint256).max)) { + revert CAPACITY_INCORRECT(); } address staker = msg.sender; // If the staker was unstaking, first revert the unstaking @@ -96,18 +104,26 @@ contract ProverPool2 is EssentialContract { } } - function unstake(uint256 unstakedAmount) external { - if (stakers[msg.sender].amount < unstakedAmount) { - revert NOT_ENOUGH_BALANCE(); - } + function unstake() external { address staker = msg.sender; totalWeight -= getWeight(staker); stakers[staker].unstakedAt = block.timestamp; - stakers[staker].unstakedAmount += unstakedAmount; - stakers[staker].amount -= unstakedAmount; - totalStaked -= unstakedAmount; - totalWeight += getWeight(staker); + totalStaked -= stakers[staker].amount; + + // Exchange unstaked slots with the preferredProver + // Auto-claim adjustment + uint256 replacedSlots; + for (uint256 slotIdx = 0; slotIdx < NUM_SLOTS; slotIdx++) { + address current = slots[slotIdx]; + if (current == staker) { + slots[slotIdx] = preferredProver; + replacedSlots++; + } + } + // Someone (later) who's weight allows to actually claim + // the slots will do that later from preferredProver. + stakers[preferredProver].numSlots += replacedSlots; } function setRewardPerGas(uint16 rewardPerGas) external { @@ -145,43 +161,27 @@ contract ProverPool2 is EssentialContract { } } - function slashProver(address slashed) external { - Staker memory staker = stakers[slashed]; - - uint256 slashableAmount = staker.unstakedAt > 0 - && block.timestamp <= staker.unstakedAt + EXIT_PERIOD - ? staker.amount + staker.unstakedAmount - : staker.amount; - - uint256 amountToSlash; - - if (slashableAmount > 0) { - amountToSlash = slashableAmount * SLASH_POINTS / 10_000; - // make sure we can slash even if totalAmount is as small as 1 - if (amountToSlash == 0) amountToSlash = 1; + // preferredProver is the one who can (theoretically) prove all + // the blocks and also the most staked TKO. He will be assigned + // with the slots which will have no 'owner' (until claimed) + // when someone unstakes + function claimPreferredProverStatus(address staker) external { + if ( + stakers[staker].maxNumSlots != type(uint256).max + || stakers[preferredProver].amount >= stakers[staker].amount + ) { + revert CANNOT_BE_PREFERRED(); } + preferredProver = staker; + } - if (amountToSlash == 0) { - // do nothing - } else if (amountToSlash <= staker.unstakedAmount) { - staker.unstakedAmount -= amountToSlash; - } else { - uint256 _additional = amountToSlash - staker.unstakedAmount; - staker.unstakedAmount = 0; - - if (staker.amount > _additional) { - staker.amount -= _additional; - } else { - staker.amount = 0; - } - } - //Write back memory var to storage - stakers[slashed] = staker; + function slashProver(address slashed) external { + stakers[slashed].amount = + stakers[slashed].amount * SLASH_POINTS / 10_000; } function withdraw(address staker) public { require(stakers[staker].unstakedAt + EXIT_PERIOD >= block.timestamp); - stakers[staker].unstakedAmount = 0; stakers[staker].unstakedAt = 0; } From 54c5fb561988183856f9d69d665f990e04d9d15e Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 20:52:27 +0000 Subject: [PATCH 10/11] Add auto-generated contract documentation --- .../contract-documentation/L1/ProverPool2.md | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool2.md b/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool2.md index c68fcc1ac61..d68027149eb 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool2.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/ProverPool2.md @@ -34,10 +34,10 @@ uint256 totalStaked uint256 totalWeight ``` -### CAPACITY_TOO_HIGH +### CAPACITY_INCORRECT ```solidity -error CAPACITY_TOO_HIGH() +error CAPACITY_INCORRECT() ``` ### NOT_ENOUGH_BALANCE @@ -46,6 +46,12 @@ error CAPACITY_TOO_HIGH() error NOT_ENOUGH_BALANCE() ``` +### CANNOT_BE_PREFERRED + +```solidity +error CANNOT_BE_PREFERRED() +``` + ### Staker ```solidity @@ -54,11 +60,16 @@ struct Staker { uint256 numSlots; uint256 maxNumSlots; uint256 unstakedAt; - uint256 unstakedAmount; uint16 rewardPerGas; } ``` +### preferredProver + +```solidity +address preferredProver +``` + ### slots ```solidity @@ -92,7 +103,7 @@ function stake(uint256 amount, uint16 rewardPerGas, uint16 maxCapacity) external ### unstake ```solidity -function unstake(uint256 unstakedAmount) external +function unstake() external ``` ### setRewardPerGas @@ -113,6 +124,12 @@ function setMaxNumSlots(address staker, uint16 maxNumSlots) external function claimSlot(address staker, uint256 slotIdx) public ``` +### claimPreferredProverStatus + +```solidity +function claimPreferredProverStatus(address staker) external +``` + ### slashProver ```solidity From 32ccebdea945f4e01ad677ce7385ecdb6fe985c8 Mon Sep 17 00:00:00 2001 From: adaki2004 Date: Tue, 27 Jun 2023 23:45:12 +0200 Subject: [PATCH 11/11] Do not allow to set preferredProver if he is unstaking --- packages/protocol/contracts/L1/ProverPool2.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/protocol/contracts/L1/ProverPool2.sol b/packages/protocol/contracts/L1/ProverPool2.sol index b8024b83e85..cd67507a8d6 100644 --- a/packages/protocol/contracts/L1/ProverPool2.sol +++ b/packages/protocol/contracts/L1/ProverPool2.sol @@ -169,6 +169,7 @@ contract ProverPool2 is EssentialContract { if ( stakers[staker].maxNumSlots != type(uint256).max || stakers[preferredProver].amount >= stakers[staker].amount + || stakers[staker].unstakedAt != 0 ) { revert CANNOT_BE_PREFERRED(); }