cosmos-sdk详解---委托、解除委托、重新委托

gaia/app.go中NewGaiaApp方法

// NewGaiaApp returns a reference to an initialized GaiaApp.

func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, invCheckPeriod uint,  baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {

cdc := MakeCodec()

bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)

bApp.SetCommitMultiStoreTracer(traceStore)

var app = &GaiaApp{

    BaseApp:          bApp,

    cdc:              cdc,

    invCheckPeriod:  invCheckPeriod,

    keyMain:          sdk.NewKVStoreKey(bam.MainStoreKey),

    ......

}

......

// register message routes

app.Router().

    AddRoute(bank.RouterKey, bank.NewHandler(app.bankKeeper)).

    AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)).

    AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)).

    AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)).

    AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper)).

    AddRoute(crisis.RouterKey, crisis.NewHandler(app.crisisKeeper))

app.QueryRouter().

    AddRoute(auth.QuerierRoute, auth.NewQuerier(app.accountKeeper)).

    AddRoute(distr.QuerierRoute, distr.NewQuerier(app.distrKeeper)).

    AddRoute(gov.QuerierRoute, gov.NewQuerier(app.govKeeper)).

    AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)).

    AddRoute(staking.QuerierRoute, staking.NewQuerier(app.stakingKeeper, app.cdc)).

    AddRoute(mint.QuerierRoute, mint.NewQuerier(app.mintKeeper))

......

app.SetInitChainer(app.initChainer)  // 加载genesis.json文件默认配置

app.SetBeginBlocker(app.BeginBlocker)

app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))  // 手续费处理

app.SetEndBlocker(app.EndBlocker)

......

return app

staking.NewHandler中包含了创建Validator、编辑Validator、处理委托、重新委托、取消委托操作。

cosmos-sdk源码解析之-------------Delegate

=> handleMsgDelegate方法中

func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result {

validator, found := k.GetValidator(ctx, msg.ValidatorAddress)

if !found {

return ErrNoValidatorFound(k.Codespace()).Result()

}

if msg.Amount.Denom != k.GetParams(ctx).BondDenom {

    return ErrBadDenom(k.Codespace()).Result()

}

_, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, validator, true)

if err != nil {

    return err.Result()

}

tags := sdk.NewTags(

    tags.Delegator, msg.DelegatorAddress.String(),

    tags.DstValidator, msg.ValidatorAddress.String(),

)

return sdk.Result{

    Tags: tags,

}

}

校验validator是否存在,委托的币是否是应该绑定的币。接着绑定委托,若该委托者和validator存在委托关系,则修改委托信息,否则创建委托信息。

// Perform a delegation, set/update everything necessary within the store.

func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int,

validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) {

// In some situations, the exchange rate becomes invalid, e.g. if

// Validator loses all tokens due to slashing. In this case,

// make all future delegations invalid.

if validator.InvalidExRate() {

    return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid(k.Codespace())

}

// Get or create the delegation object

delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddress)

if !found {

    delegation = types.NewDelegation(delAddr, validator.OperatorAddress, sdk.ZeroDec())

}

// call the appropriate hook if present

if found {

    k.BeforeDelegationSharesModified(ctx, delAddr, validator.OperatorAddress)

} else {

    k.BeforeDelegationCreated(ctx, delAddr, validator.OperatorAddress)

}

if subtractAccount {

    _, err := k.bankKeeper.DelegateCoins(ctx, delegation.DelegatorAddress, sdk.Coins{sdk.NewCoin(k.GetParams(ctx).BondDenom, bondAmt)})

    if err != nil {

        return sdk.Dec{}, err

    }

}

validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt)

// Update delegation

delegation.Shares = delegation.Shares.Add(newShares)

k.SetDelegation(ctx, delegation)

// Call the after-modification hook

k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)

return newShares, nil

}

  Delegate方法主要校验了validator的数量是否为负或者0,委托关系关系是否存在,若不存在,则新建委托关系。否则调用BeforeDelegationSharesModified,将该委托者之前的委托收益退回给该委托者,清除之前委托开始信息。  AddValidatorTokensAndShares方法删除之前的validator权重索引,该validator委托的总数量加上新委托的数量(amount个),pool中将amount个未委托的数量转为amount个委托的数量。委托者的数量加上新委托的数量,调用AfterDelegationModified,重新生成该委托者的委托开始信息。生成委托Tag,将委托结果返回。

cosmos-sdk源码解析之---------Unbond

=> handleMsgUndelegate方法中

cosmos-sdk/x/staking/handler.go

func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) sdk.Result {

  shares, err := k.ValidateUnbondAmount(

  ctx, msg.DelegatorAddress, msg.ValidatorAddress, msg.Amount.Amount,

  ) 

  if err != nil {

    return err.Result()

  }

  completionTime, err := k.Undelegate(ctx, msg.DelegatorAddress, msg.ValidatorAddress, shares)

  if err != nil {

      return err.Result()

  }

  finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime)

  tags := sdk.NewTags(

    tags.Delegator, msg.DelegatorAddress.String(),

    tags.SrcValidator, msg.ValidatorAddress.String(),

    tags.EndTime, completionTime.Format(time.RFC3339),

  )

  return sdk.Result{Data: finishTime, Tags: tags}

}

ValidateUnbondAmount: 校验解除委托数量,主要判断validator、delegator是否存在,解除委托数量是否大于该委托者的委托数量。

在委托解除完后记录完成时间,委托人地址,validator地址,生成相应的Tag。返回解除委托结果。Undelegate中主要完成了解除委托、判断validator的委托实体是否达到最大,记录委托完成时间,将委托实体加入实体队列。

cosmos-sdk/x/staking/keeper/delegation.go

func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares sdk.Dec) (amount sdk.Int, err sdk.Error) {

// check if a delegation object exists in the store

delegation, found := k.GetDelegation(ctx, delAddr, valAddr)

if !found {

    return amount, types.ErrNoDelegatorForAddress(k.Codespace())

}

// call the before-delegation-modified hook

k.BeforeDelegationSharesModified(ctx, delAddr, valAddr)

// ensure that we have enough shares to remove

if delegation.Shares.LT(shares) {

    return amount, types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String())

}

// get validator

validator, found := k.GetValidator(ctx, valAddr)

if !found {

    return amount, types.ErrNoValidatorFound(k.Codespace())

}

// subtract shares from delegation

delegation.Shares = delegation.Shares.Sub(shares)

isValidatorOperator := bytes.Equal(delegation.DelegatorAddress, validator.OperatorAddress)

// if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum

// trigger a jail validator

if isValidatorOperator && !validator.Jailed &&

    validator.TokensFromShares(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) {

    k.jailValidator(ctx, validator)

    validator = k.mustGetValidator(ctx, validator.OperatorAddress)

}

// remove the delegation

if delegation.Shares.IsZero() {

    k.RemoveDelegation(ctx, delegation)

} else {

    k.SetDelegation(ctx, delegation)

    // call the after delegation modification hook

    k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)

}

// remove the shares and coins from the validator

validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)

if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded {

    // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period

    k.RemoveValidator(ctx, validator.OperatorAddress)

}

return amount, nil

}

  当委托关系发生变动时,委托者的委托奖励就会退回给委托者,清除委托starting info。BeforeDelegationSharesModified主要在hooks.go(cosmos-sdk/x/distribution/keeper/hooks.go)中withdrawDelegationRewards处理。withdrawDelegationRewards中主要根据(endingPeriod时每个委托Token的累积奖励-startingPeriod时每个委托Token的累积奖励)* 持有的Token计算该委托者的委托奖励,然后将outstanding(委托未退回的收益)池中减去该委托者的收益。同时将委托者收益的整数部分退回给委托者,小数部分放到CommunityPool中,删除委托starting信息。 接着委托者委托数量减去解除委托的数量,,若剩下委托数量为0,则删除出委托,否则触发AfterDelegationModified钩子,获取当前委托数量,生成新的委托开始信息。(类似于一张新的委托记录信息) RemoveValidatorTokensAndShares:先删除validator的权重索引,然后重新生成委托权重。将validator的Token数量减去解除委托的数量,委托池子中绑定数量减去解除绑定数量,未绑定数量加上解除绑定数量。

cosmos-sdk源码解析之---------Rebond

=> handleMsgBeginRedelegate方法中

cosmos-sdk/x/staking/handle.go

func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {

shares, err := k.ValidateUnbondAmount(

ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.Amount.Amount,

)

if err != nil {

return err.Result()

}

completionTime, err := k.BeginRedelegation(

    ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.ValidatorDstAddress, shares,

)

if err != nil {

    return err.Result()

}

finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime)

resTags := sdk.NewTags(

    tags.Delegator, msg.DelegatorAddress.String(),

    tags.SrcValidator, msg.ValidatorSrcAddress.String(),

    tags.DstValidator, msg.ValidatorDstAddress.String(),

    tags.EndTime, completionTime.Format(time.RFC3339),

)

return sdk.Result{Data: finishTime, Tags: resTags}

}

cosmos-sdk详解--------crisis

=> handleMsgVerifyInvariant方法

func handleMsgVerifyInvariant(ctx sdk.Context, msg MsgVerifyInvariant, k Keeper) sdk.Result {

// remove the constant fee

constantFee := sdk.NewCoins(k.GetConstantFee(ctx))

_, _, err := k.bankKeeper.SubtractCoins(ctx, msg.Sender, constantFee)

if err != nil {

    return err.Result()

}

_ = k.feeCollectionKeeper.AddCollectedFees(ctx, constantFee)

// use a cached context to avoid gas costs during invariants

cacheCtx, _ := ctx.CacheContext()

found := false

var invarianceErr error

msgFullRoute := msg.FullInvariantRoute()

for _, invarRoute := range k.routes {

    if invarRoute.FullRoute() == msgFullRoute {

        invarianceErr = invarRoute.Invar(cacheCtx)

        found = true

        break

    }

}

if !found {

    return ErrUnknownInvariant(DefaultCodespace).Result()

}

if invarianceErr != nil {

    // NOTE currently, because the chain halts here, this transaction will never be included

    // in the blockchain thus the constant fee will have never been deducted. Thus no

    // refund is required.

    // TODO uncomment the following code block with implementation of the circuit breaker

    //// refund constant fee

    //err := k.distrKeeper.DistributeFeePool(ctx, constantFee, msg.Sender)

    //if err != nil {

    //// if there are insufficient coins to refund, log the error,

    //// but still halt the chain.

    //logger := ctx.Logger().With("module", "x/crisis")

    //logger.Error(fmt.Sprintf(

    //"WARNING: insufficient funds to allocate to sender from fee pool, err: %s", err))

    //}

    // TODO replace with circuit breaker

    panic(invarianceErr)

}

tags := sdk.NewTags(

    "sender", msg.Sender.String(),

    "invariant", msg.InvariantRoute,

)

return sdk.Result{

    Tags: tags,

}

}

该方法主要处理ConstantFee的,将constantFee加入到收集的FeePool中。

cosmos-sdk详解之----------gov

=> handleMsgDeposit方法

func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result {

err, votingStarted := keeper.AddDeposit(ctx, msg.ProposalID, msg.Depositor, msg.Amount)

if err != nil {

return err.Result()

}

proposalIDStr := fmt.Sprintf("%d", msg.ProposalID)

resTags := sdk.NewTags(

    tags.Depositor, []byte(msg.Depositor.String()),

    tags.ProposalID, proposalIDStr,

)

if votingStarted {

    resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDStr)

}

return sdk.Result{

    Tags: resTags,

}

}

  handleMsgDeposit中调用AddDeposit方法主要validator对Proposal进行充币,若该Proposal的充币金额提案最小充币金额要求时,激活该Proposal,进入Voting阶段;若没有发现该提案时,则新建一个该提案。记录Prosal,生成相应的Tag日志。

func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, depositAmount sdk.Coins) (sdk.Error, bool) {

// Checks to see if proposal exists

proposal, ok := keeper.GetProposal(ctx, proposalID)

if !ok {

return ErrUnknownProposal(keeper.codespace, proposalID), false

}

// Check if proposal is still depositable

if (proposal.Status != StatusDepositPeriod) && (proposal.Status != StatusVotingPeriod) {

    return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false

}

// Send coins from depositor's account to DepositedCoinsAccAddr account

// TODO: Don't use an account for this purpose; it's clumsy and prone to misuse.

_, err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount)

if err != nil {

    return err, false

}

// Update proposal

proposal.TotalDeposit = proposal.TotalDeposit.Add(depositAmount)

keeper.SetProposal(ctx, proposal)

// Check if deposit has provided sufficient total funds to transition the proposal into the voting period

activatedVotingPeriod := false

if proposal.Status == StatusDepositPeriod && proposal.TotalDeposit.IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) {

    keeper.activateVotingPeriod(ctx, proposal)

    activatedVotingPeriod = true

}

// Add or update deposit object

currDeposit, found := keeper.GetDeposit(ctx, proposalID, depositorAddr)

if !found {

    newDeposit := Deposit{depositorAddr, proposalID, depositAmount}

    keeper.setDeposit(ctx, proposalID, depositorAddr, newDeposit)

} else {

    currDeposit.Amount = currDeposit.Amount.Add(depositAmount)

    keeper.setDeposit(ctx, proposalID, depositorAddr, currDeposit)

}

return nil, activatedVotingPeriod

}

  先根据proposalID获取Proposal,判断proposal的状态,然后将充的币发送到DepositedCoinsAccAddr中,然后将Proposal总充值累加上充币的数量。根据proposalID和depositorAddr获取充币的数量,将充的币加上新充值的币设置到队列中。

cosmos-sdk详解之-------------------Submit Proposal

=> handleMsgSubmitProposal

func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitProposal) sdk.Result {

var content ProposalContent

switch msg.ProposalType {

case ProposalTypeText:

content = NewTextProposal(msg.Title, msg.Description)

case ProposalTypeSoftwareUpgrade:

content = NewSoftwareUpgradeProposal(msg.Title, msg.Description)

default:

return ErrInvalidProposalType(keeper.codespace, msg.ProposalType).Result()

}

proposal, err := keeper.SubmitProposal(ctx, content)

if err != nil {

return err.Result()

}

proposalID := proposal.ProposalID

proposalIDStr := fmt.Sprintf("%d", proposalID)

err, votingStarted := keeper.AddDeposit(ctx, proposalID, msg.Proposer, msg.InitialDeposit)

if err != nil {

    return err.Result()

}

resTags := sdk.NewTags(

    tags.Proposer, []byte(msg.Proposer.String()),

    tags.ProposalID, proposalIDStr,

)

if votingStarted {

    resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDStr)

}

return sdk.Result{

    Data: keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID),

    Tags: resTags,

}

}

  首先根据提案类型判断是新的Proposal还是修改Proposal,提交提案,根据提案ID和提案生成Tag日志。其中SubmitProposal方法主要获取当前区块高度的时间,Proposal留存最长时间,然后生成提案,将提案插入InactiveProposalQueue队列中。

cosmos-sdk详解之-----------------Vote

=> handleMsgVote

func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) sdk.Result {

err := keeper.AddVote(ctx, msg.ProposalID, msg.Voter, msg.Option)

if err != nil {

return err.Result()

}

return sdk.Result{

    Tags: sdk.NewTags(

        tags.Voter, msg.Voter.String(),

        tags.ProposalID, fmt.Sprintf("%d", msg.ProposalID),

    ),

}

}

AddVote方法根据proposalID获取proposal, 判断提案的状态有没有在Voting期间,投的票是不是有效的,生成一个投票记录。生成Vote的投票Tag日志。

cosmos-sdk详解之--------------minter

从BeginBlocker入手

cosmos-sdk/x/mint/abci_app.go

func BeginBlocker(ctx sdk.Context, k Keeper) {

// fetch stored minter & params

minter := k.GetMinter(ctx)

params := k.GetParams(ctx)

// recalculate inflation rate

totalSupply := k.sk.TotalTokens(ctx)

bondedRatio := k.sk.BondedRatio(ctx)

minter.Inflation = minter.NextInflationRate(params, bondedRatio)

minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalSupply)

k.SetMinter(ctx, minter)

// mint coins, add to collected fees, update supply

mintedCoin := minter.NextReduceAnnualProvisions(params, ctx.BlockHeight())

k.fck.AddCollectedFees(ctx, sdk.Coins{mintedCoin})

k.sk.InflateSupply(ctx, mintedCoin.Amount)

}

    首先获取矿工信息以及设置的通货膨胀率、每年产生的区块数等参数。然后获取当前Token总的供应量(池子中抵押的加上未抵押的),NextInflationRate获取膨胀率的,跟validator抵押率有关,bondedRatio越小,Inflation(膨胀率)越大,每块的奖励(mintedCoin)也越多。然后将挖矿奖励收集到FeePool中,总的供应量加上上一区块产生的奖励。          **重点说明一下:**  当下一个区块产生时,才会将上一个区块的奖励分配。 

cosmos-sdk/x/mint/minter.go

// NextInflationRate returns the new inflation rate for the next hour.

func (m Minter) NextInflationRate(params Params, bondedRatio sdk.Dec) sdk.Dec {

// The target annual inflation rate is recalculated for each previsions cycle. The

// inflation is also subject to a rate change (positive or negative) depending on

// the distance from the desired ratio (67%). The maximum rate change possible is

// defined to be 13% per year, however the annual inflation is capped as between

// 7% and 20%.

// (1 - bondedRatio/GoalBonded) * InflationRateChange

inflationRateChangePerYear := sdk.OneDec().

    Sub(bondedRatio.Quo(params.GoalBonded)).

    Mul(params.InflationRateChange)

inflationRateChange := inflationRateChangePerYear.Quo(sdk.NewDec(int64(params.BlocksPerYear)))

// adjust the new annual inflation for this next cycle

inflation := m.Inflation.Add(inflationRateChange) // note inflationRateChange may be negative

if inflation.GT(params.InflationMax) {

    inflation = params.InflationMax

}

if inflation.LT(params.InflationMin) {

    inflation = params.InflationMin

}

return inflation

}

inflationRateChangePerYear为每年的通货膨胀率,计算公式为:(1 - bondedRatio/GoalBonded) * InflationRateChange,其中bondedRatio为绑定利率,GoalBonded为目标绑定Token数目的百分比(一般为2/3)。inflationRateChange为每一块的通缩率, inflation为每一块实际增发利率。

cosmos-sdk详解之------------------------奖励分配 AllocateTokens

// allocate fees handles distribution of the collected fees

func (k Keeper) AllocateTokens(ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64,

previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo) {

// fetch and clear the collected fees for distribution, since this is

// called in BeginBlock, collected fees will be from the previous block

// (and distributed to the previous proposer)

feesCollectedInt := k.feeCollectionKeeper.GetCollectedFees(ctx)

feesCollected := sdk.NewDecCoins(feesCollectedInt)

k.feeCollectionKeeper.ClearCollectedFees(ctx)

// temporary workaround to keep CanWithdrawInvariant happy

// general discussions here: https://github.com/bitcv-chain/cosmos-sdk/issues/2906#issuecomment-441867634

feePool := k.GetFeePool(ctx)

if totalPreviousPower == 0 {

    feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected)

    k.SetFeePool(ctx, feePool)

    return

}

// calculate fraction votes , previousFractionVotes means the participation percentage

previousFractionVotes := sdk.NewDec(sumPreviousPrecommitPower).Quo(sdk.NewDec(totalPreviousPower))

// calculate previous proposer reward, baseProposerReward is defined in genesis.go

baseProposerReward := k.GetBaseProposerReward(ctx)

bonusProposerReward := k.GetBonusProposerReward(ctx)

proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes))

proposerReward := feesCollected.MulDecTruncate(proposerMultiplier)

fmt.Println("---", proposerMultiplier, proposerReward)

// pay previous proposer

remaining := feesCollected

proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer)

if proposerValidator != nil {

    k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward)

    remaining = remaining.Sub(proposerReward)

} else {

    // previous proposer can be unknown if say, the unbonding period is 1 block, so

    // e.g. a validator undelegates at block X, it's removed entirely by

    // block X+1's endblock, then X+2 we need to refer to the previous

    // proposer for X+1, but we've forgotten about them.

    logger.Error(fmt.Sprintf(

        "WARNING: Attempt to allocate proposer rewards to unknown proposer %s. "+

            "This should happen only if the proposer unbonded completely within a single block, "+

            "which generally should not happen except in exceptional circumstances (or fuzz testing). "+

            "We recommend you investigate immediately.",

        previousProposer.String()))

}

// calculate fraction allocated to validators

communityTax := k.GetCommunityTax(ctx)

voteMultiplier := sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax)

// allocate tokens proportionally to voting power

// TODO consider parallelizing later, ref https://github.com/bitcv-chain/cosmos-sdk/pull/3099#discussion_r246276376

for _, vote := range previousVotes {

    validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)

    // TODO consider microslashing for missing votes.

    // ref https://github.com/bitcv-chain/cosmos-sdk/issues/2525#issuecomment-430838701

    powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPreviousPower))

    reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction)

    k.AllocateTokensToValidator(ctx, validator, reward)

    remaining = remaining.Sub(reward)

}

// allocate community funding

feePool.CommunityPool = feePool.CommunityPool.Add(remaining)

k.SetFeePool(ctx, feePool)

}

GetCollectedFees方法从Fees池子中获取收集的手续费,然后将Fees池子清除。若投票权重为0,则将手续费全放到CommunityPool池子中。否则继续执行。previousFractionVotes为签名验证上一个区块投票总权重占所以validator总权重比例(it means the participation percentage)。baseProposerReward:基本的提案奖励;bonusProposerReward:绑定提案奖励;proposerMultiplier:提议者的分配奖励的比例,它是baseProposerReward加上绑定奖励比例*validator参与度。baseProposerReward:分配给提案的validator的奖励。如果proposerValidator不为空,然后将奖励分配给validator。AllocateTokensToValidator方法主要完成了将validator的佣金放到ValidatorAccumulatedCommission中,将除去佣金的奖励累加到奖励池中(ValidatorCurrentRewards);将未取回的奖励累加到ValidatorOutstandingRewards中。voteMultiplier:分配给其他签名的validator的比例为(1-proposerMultiplier-communityTax),其中communityTax为社区税。然后给其他的validator分配参与签名验证奖励。最后将communityTax比例的奖励分配给社区,扔进池子中。

你可能感兴趣的:(cosmos-sdk详解---委托、解除委托、重新委托)