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比例的奖励分配给社区,扔进池子中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容