本文主要介绍Matic(现为Polygon) 验证者质押、质押代理、奖励分配以及惩罚处理流程。
验证者质押
直接质押
直接质押要求目前验证者人数小于 validatorThreshold
, validatorThreshold
初始值为7, 可通过治理更新,目前值为:100。
质押数量需要大于minDeposit
, minDeposit
初始值为1 matic, 可通过治理更新, 目前为1 Matic。
质押需要支付手续费用 hemidallFee
, 需要大于minHeimdallFee
, 其初始值为1matic, 可通过治理更新, 目前为1 matic。
function stake(
uint256 amount,
uint256 heimdallFee,
bool acceptDelegation,
bytes calldata signerPubkey
) external {
stakeFor(msg.sender, amount, heimdallFee, acceptDelegation, signerPubkey);
}
质押过程通过NFTContract为验证者铸造validatorId
, 验证者从当前周期epoch
生效。
若接受代理,则为验证者部署ValidateShare
合约。
竞拍成为验证者
竞拍合约函数:
function startAuction(
uint256 validatorId,
uint256 amount,
bool _acceptDelegation,
bytes calldata _signerPubkey
) external onlyWhenUnlocked
-
validiatorId
为要挑战的验证者,必须要求其是有效的;
require(
validators[validatorId].deactivationEpoch == 0 && currentValidatorAmount != 0,
"Invalid validator for an auction"
);
-
验证者不能挑战另外一个验证者。
-
要求过了冷却期:
replacementCoolDown
; 每次更新dynasty
时冷静期才会更新, 目前值为:2018083。 目前currentEpoch
值为 18106, 所有还不能参与竞拍。replacementCoolDown
可以用来控制不是开启竞拍功能。 -
要求在竞拍期,
autionPeriod
目前值为20。
// (auctionPeriod--dynasty)--(auctionPeriod--dynasty)--(auctionPeriod--dynasty)
currentEpoch.sub(validators[validatorId].activationEpoch) % dynasty.add(auctionPeriod)) < auctionPeriod
-
Dynasty
为两次竞拍的周期间隔,初始值为886, 可通过治理更新, 目前值为:80
function updateDynastyValue(uint256 newDynasty) public onlyGovernance {
require(newDynasty > 0);
logger.logDynastyValueChange(newDynasty, dynasty);
dynasty = newDynasty;
WITHDRAWAL_DELAY = newDynasty;
auctionPeriod = newDynasty.div(4);
// set cooldown period
replacementCoolDown = currentEpoch.add(auctionPeriod);
}
- 保证竞标质押金额同时大于 被挑战的验证者的总质押(加上代理给验证者的质押)和上一个竞标者的质押金额;
增加质押
验证者可以调用restake
增加质押量,或者将奖励转为质押。
function restake(
uint256 validatorId,
uint256 amount,
bool stakeRewards
) public onlyWhenUnlocked onlyStaker(validatorId)
竞拍确认函数:
function confirmAuctionBid(
uint256 validatorId,
uint256 heimdallFee /** for new validator */
) external onlyWhenUnlocked
-
要求竞标者或当前验证者触发该函数;
-
要求必须过了竞拍期:
_currentEpoch.sub(auction.startEpoch) % auctionPeriod.add(dynasty) >= auctionPeriod
-
若验证者最终质押总量大于竞标者的质押量,则退还竞标者的质押金额;
-
若竞标者最终胜出,竞标者需要支付手费用:
heimdalFee
, 从当前周期成为新的验证者。原先验证者开始解除质押。
Heimdall fee
无论验证者经过质押或竞拍成为验证者,都需要支付一定的手续费用Heimdall fee
,
function _transferAndTopUp(
address user,
uint256 fee,
uint256 additionalAmount
) private {
require(fee >= minHeimdallFee, "Not enough heimdall fee");
require(token.transferFrom(msg.sender, address(this), fee.add(additionalAmount)), "Fee transfer failed");
totalHeimdallFee = totalHeimdallFee.add(fee);
logger.logTopUpFee(user, fee);
}
minHeimdallFee
初始值为1 Matic, 可通过治理更新, 目前值为1Matic, 推荐大于10。
Heimdall Fee
可以由验证者自己赎回,需要提供Merkle 路径证明,其中accountStateRoot
由每次提交checkpoint时更新。
function claimFee(
uint256 accumFeeAmount,
uint256 index,
bytes memory proof
) public {}
质押退出
function unstake(uint256 validatorId) external onlyStaker(validatorId)
-
必须在非竞拍期才能调用;
-
标记开始退出的周期:
validators[validatorId].deactivationEpoch = exitEpoch;
-
立即返还验证者的奖励;
-
若存在代理合约的,锁定代理合约,代理者无法再质押。
质押赎回
function unstakeClaim(uint256 validatorId) public onlyStaker(validatorId)
- 要求必须经过
WITHDRAW_DELAY
个周期,其初始值为2^13(8192), 随后可通过dynasty
值设定, 目前值为80。
require(
deactivationEpoch > 0 &&
deactivationEpoch.add(WITHDRAWAL_DELAY) <= currentEpoch &&
validators[validatorId].status != Status.Unstaked
);
-
销毁验证者的NFT:
NFTContract.burn(validatorId);
-
signerToValidator[validators[validatorId].signer] = INCORRECT_VALIDATOR_ID;
通过了设置`INCORRECT_VALIDATOR_ID` 确保该验证者不能再参与竞拍成为验证者,但可以通过直接参与质押成为验证者。
-
设置验证者的状态为:
validators[validatorId].status = Status.Unstaked;
-
立即退还验证者的质押。
代理者质押
验证者给代理者奖励分配采用类似AMM模型的机制,由验证者部署ValidatorShare
合约,可以看作发行一定数量的代币,总量为10^10个:
function totalSupply() public view returns (uint256) {
return 10000000000 * 10**uint256(DECIMALS);
}
然后由代理者根据当前的汇率认购:
ExchangeRate = (totalDelegatedPower + delegatorRewardPool) / totalDelegatorShares
汇率是不断变化的,当验证者不断获取奖励的时候,汇率上升;反之若受到惩罚,奖励汇率下降。
质押
当代理者代理质押matic时候,将matic通过汇率转换为可申购的份额:
function _buyShares(uint256 _amount, uint256 _minSharesToMint) private onlyWhenUnlocked returns(uint256) {}
代理者也可以通过调用restake()
将所得到的奖励进行再质押。
退出质押
当代理者退出质押的时候,根据当前汇率售出所拥有的份额:
function sellVoucher(uint256 _minClaimAmount) public {}
注:每次提交checkpoint是一个周期。
质押赎回
质押的退出需要等待 WITHDRAWL_DELAY
个周期,即目前为80。
function unstakeClaimTokens() public {
...
require(delegator.withdrawEpoch.add(stakeManager.withdrawalDelay()) <= stakeManager.epoch() && shares > 0, "Incomplete withdrawal period");
...
}
奖励
Checkpoint 奖励
CHECKPOINT_REWARD
checkpoint奖励的上限,初始为20188个matic, 可以通过治理更新, 目前值为:107163。
checkPointBlockInterval
初始值设为1024,可通过治理更新, 目前值为:5120。
proposerBouns
初始值为10,可通过治理进行更新,值小于等于100,目前值为10:
maxRewardsCheckpoints
: 可通过治理更新, 目前值为:3
checkpointRewardDelta
: 可通过治理更新,目前值为:10
rewardDecreasePerCheckpoint
: 可通过治理更新,目前值为:30
CHK_REWARD_PRECISION
: 值为100
计算过程:
计算 :
其中 为当前是checkpoint
的包括的块数,
初始值:
然后计算 :
更新 :
//
if (prevBlockInterval > fullIntervals) { // 若checkpoint比上次提交的更快
// checkpoint is faster
ckpReward += delta;
} else {
ckpReward -= delta;
}
若 , 执行下面的更新:
最终该checkPoint
得到 reward
为:
提交checkPoint的Bonus分配
其中 proposerBonus
为通过治理更新的参数,目前值为10. MAX_PROPOSER_BOUNS
值为100
proposer.reward = proposer.reward.add(proposerBonus);
proposerBonus
奖励全部给予 proposer
剩余奖励分配
主要分为以下三个步骤:
(1) 计算每份质押的奖励
其中 为上一个checkpoint执行后得到每份质押的奖励,signedStakePower
为此次checkpoint所有签名者的质押。 为固定值, 值为, 即 matic.
(2) 分发非签名验证者的奖励,并更新initialRewardPerStake
// evaluate rewards for validator who did't sign and set latest reward per stake to new value to avoid them from getting new rewards.
_updateValidatorsRewards(unsignedValidators, totalUnsignedValidators, newRewardPerStake);
(3) 分发签名验证者的奖励
// distribute rewards between signed validators
rewardPerStake = newRewardPerStake;
(4) 分发已经在退出质押的签名验证者的奖励
// evaluate rewards for unstaked validators to avoid getting new rewards until they claim their stake
_updateValidatorsRewards(deactivatedValidators, totalDeactivatedValidators, newRewardPerStake);
代理者奖励
分给完Bonus后,剩余奖励根据质押比例,分别对每个签名验证者根据质押总量比例进行奖励分配;然后每个验证者然后根据其代理者质押比例再进行分配。
function checkSignature(
uint256 checkpointStakePower,
uint256 reward,
bytes32 voteHash,
bytes memory sigs
) internal returns (uint256)
在手续费commisionRate
大于0的情况下,给委托者的奖励需要先扣除手续费,然后再根据质押比较比例分配给代理者。
惩罚机制
惩罚包括两个过程: (1)惩罚作恶验证者;(2)奖励提出者
function updateSlashedAmounts(bytes memory data, bytes memory sigs) public {}
slash
- 通过
slashNonce
防止重复提交。 - 惩罚需要经过2/3 + 1 质押验证者签名验证;
- 提交的数据中包括: (1)待惩罚的验证者; (2)惩罚金额;(3)确定是否要关禁闭:
jail
- 惩罚金额直接从验证者的质押金额中扣除, 若存在代理者,则根据质押比例分担惩罚金额,即使有代理正在赎回期,仍要进行处罚。
- 禁闭时间目前为1个周期,期间无法对checkpoint验证,然后由验证者主动解除禁闭。
function unjail(uint256 validatorId) public onlyStaker(validatorId)
Bounty
奖励的金额为总的惩罚金额的乘以 reportRate
比例,reportRate
初始值为5, 可由管理员更新,其余到的转到合约中。合约的金额可以由管理员取款。
uint256 bounty = (slashedAmount.mul(reportRate)).div(100);
若调用合约者 和 proposer
为不同的地址,则两者均分,否则都转给合约调用者。
参考
https://github.com/maticnetwork/contracts
https://docs.matic.network/docs/validate/orientation/
https://docs.matic.network/docs/develop/network-details/genesis-contracts/
https://etherscan.io/address/0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908#readProxyContract
网友评论