From 16746254f5cef54ad86bb8cc3329b4b1019bf9ab Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 16 Oct 2018 02:18:09 +0200 Subject: [PATCH 01/30] Update PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 654e342f8c48..4fd5762417e2 100644 --- a/PENDING.md +++ b/PENDING.md @@ -44,6 +44,7 @@ BREAKING CHANGES * [simulation] \#2162 Added back correct supply invariants * [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing * [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker + * [x/mock/simulation] \#2485 Check for supply conservation (including expected inflation) in simulation * SDK * [core] \#2219 Update to Tendermint 0.24.0 From 2fff810695f3618c850c7ca2949de4192dfe72a8 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 16 Oct 2018 02:23:56 +0200 Subject: [PATCH 02/30] This PR now will do something else --- PENDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index 4fd5762417e2..7f1393bf4655 100644 --- a/PENDING.md +++ b/PENDING.md @@ -44,7 +44,7 @@ BREAKING CHANGES * [simulation] \#2162 Added back correct supply invariants * [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing * [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker - * [x/mock/simulation] \#2485 Check for supply conservation (including expected inflation) in simulation + * [x/mock/simulation] \#2501 Simulate transactions & invariants for fee distribution * SDK * [core] \#2219 Update to Tendermint 0.24.0 From b700107becd8e5e6d014032dde53700bc092f298 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Oct 2018 00:25:31 +0200 Subject: [PATCH 03/30] Add simulated distribution msgs --- cmd/gaia/app/sim_test.go | 6 ++ x/distribution/alias.go | 2 +- x/distribution/simulation/invariants.go | 17 ++++ x/distribution/simulation/msgs.go | 130 ++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 x/distribution/simulation/invariants.go create mode 100644 x/distribution/simulation/msgs.go diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 41b3da7417a1..d7dd123434c1 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -16,6 +16,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation" distr "github.com/cosmos/cosmos-sdk/x/distribution" + distributionsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" "github.com/cosmos/cosmos-sdk/x/gov" govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation" "github.com/cosmos/cosmos-sdk/x/mock/simulation" @@ -103,6 +104,10 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ {100, banksim.SingleInputSendMsg(app.accountMapper, app.bankKeeper)}, + {50, distributionsim.SimulateMsgSetWithdrawAddress(app.accountMapper, app.distrKeeper)}, + {50, distributionsim.SimulateMsgWithdrawDelegatorRewardsAll(app.accountMapper, app.distrKeeper)}, + {50, distributionsim.SimulateMsgWithdrawDelegatorReward(app.accountMapper, app.distrKeeper)}, + {50, distributionsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountMapper, app.distrKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, {100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, {100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)}, @@ -117,6 +122,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { func invariants(app *GaiaApp) []simulation.Invariant { return []simulation.Invariant{ banksim.NonnegativeBalanceInvariant(app.accountMapper), + distributionsim.AllInvariants(app.bankKeeper, app.distrKeeper, app.accountMapper), govsim.AllInvariants(), stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.accountMapper), slashingsim.AllInvariants(), diff --git a/x/distribution/alias.go b/x/distribution/alias.go index 7f14f82a45e4..5a7cbce4d53c 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -49,7 +49,7 @@ var ( NewMsgSetWithdrawAddress = types.NewMsgSetWithdrawAddress NewMsgWithdrawDelegatorRewardsAll = types.NewMsgWithdrawDelegatorRewardsAll - NewMsgWithdrawDelegationReward = types.NewMsgWithdrawDelegatorReward + NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward NewMsgWithdrawValidatorRewardsAll = types.NewMsgWithdrawValidatorRewardsAll ) diff --git a/x/distribution/simulation/invariants.go b/x/distribution/simulation/invariants.go new file mode 100644 index 000000000000..7036be46f0f9 --- /dev/null +++ b/x/distribution/simulation/invariants.go @@ -0,0 +1,17 @@ +package simulation + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/mock/simulation" +) + +// AllInvariants runs all invariants of the distribution module. +// Currently: total supply, positive power +func AllInvariants(ck bank.Keeper, k distribution.Keeper, am auth.AccountMapper) simulation.Invariant { + return func(app *baseapp.BaseApp) error { + return nil + } +} diff --git a/x/distribution/simulation/msgs.go b/x/distribution/simulation/msgs.go new file mode 100644 index 000000000000..241dccdababd --- /dev/null +++ b/x/distribution/simulation/msgs.go @@ -0,0 +1,130 @@ +package simulation + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/cosmos/cosmos-sdk/x/mock/simulation" +) + +// SimulateMsgSetWithdrawAddress +func SimulateMsgSetWithdrawAddress(m auth.AccountMapper, k distribution.Keeper) simulation.Operation { + handler := distribution.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account, event func(string)) ( + action string, fOp []simulation.FutureOperation, err error) { + + accountOrigin := simulation.RandomAcc(r, accs) + accountDestination := simulation.RandomAcc(r, accs) + msg := distribution.NewMsgSetWithdrawAddress(accountOrigin.Address, accountDestination.Address) + + if msg.ValidateBasic() != nil { + return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + result := handler(ctx, msg) + if result.IsOK() { + write() + } + + event(fmt.Sprintf("distribution/MsgSetWithdrawAddress/%v", result.IsOK())) + + action = fmt.Sprintf("TestMsgSetWithdrawAddress: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) + return action, nil, nil + } +} + +// SimulateMsgWithdrawDelegatorRewardsAll +func SimulateMsgWithdrawDelegatorRewardsAll(m auth.AccountMapper, k distribution.Keeper) simulation.Operation { + handler := distribution.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account, event func(string)) ( + action string, fOp []simulation.FutureOperation, err error) { + + account := simulation.RandomAcc(r, accs) + msg := distribution.NewMsgWithdrawDelegatorRewardsAll(account.Address) + + if msg.ValidateBasic() != nil { + return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + result := handler(ctx, msg) + if result.IsOK() { + write() + } + + event(fmt.Sprintf("distribution/MsgWithdrawDelegatorRewardsAll/%v", result.IsOK())) + + action = fmt.Sprintf("TestMsgWithdrawDelegatorRewardsAll: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) + return action, nil, nil + } +} + +// SimulateMsgWithdrawDelegatorReward +func SimulateMsgWithdrawDelegatorReward(m auth.AccountMapper, k distribution.Keeper) simulation.Operation { + handler := distribution.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account, event func(string)) ( + action string, fOp []simulation.FutureOperation, err error) { + + delegatorAccount := simulation.RandomAcc(r, accs) + validatorAccount := simulation.RandomAcc(r, accs) + msg := distribution.NewMsgWithdrawDelegatorReward(delegatorAccount.Address, sdk.ValAddress(validatorAccount.Address)) + + if msg.ValidateBasic() != nil { + return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + result := handler(ctx, msg) + if result.IsOK() { + write() + } + + event(fmt.Sprintf("distribution/MsgWithdrawDelegatorReward/%v", result.IsOK())) + + action = fmt.Sprintf("TestMsgWithdrawDelegatorReward: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) + return action, nil, nil + } +} + +// SimulateMsgWithdrawValidatorRewardsAll +func SimulateMsgWithdrawValidatorRewardsAll(m auth.AccountMapper, k distribution.Keeper) simulation.Operation { + handler := distribution.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account, event func(string)) ( + action string, fOp []simulation.FutureOperation, err error) { + + account := simulation.RandomAcc(r, accs) + msg := distribution.NewMsgWithdrawValidatorRewardsAll(sdk.ValAddress(account.Address)) + + if msg.ValidateBasic() != nil { + return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + result := handler(ctx, msg) + if result.IsOK() { + write() + } + + event(fmt.Sprintf("distribution/MsgWithdrawValidatorRewardsAll/%v", result.IsOK())) + + action = fmt.Sprintf("TestMsgWithdrawValidatorRewardsAll: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) + return action, nil, nil + } +} + +// Setup +// nolint: errcheck +func Setup(mapp *mock.App, k distribution.Keeper) simulation.RandSetup { + return func(r *rand.Rand, accs []simulation.Account) { + } +} From 1d4b595006f1775e2a3bedb91ef14ab2e890f6b2 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Oct 2018 00:34:16 +0200 Subject: [PATCH 04/30] Correctly set power in vote info --- x/mock/simulation/random_simulate_blocks.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 0af6fbbac11f..d2680bc0f4cd 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -370,6 +370,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, voteInfos[i] = abci.VoteInfo{ Validator: abci.Validator{ Address: pubkey.Address(), + Power: mVal.val.Power, }, SignedLastBlock: signed, } From e099491daa48cf9730f1843d797fe62abf972031 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 18 Oct 2018 21:58:57 +0200 Subject: [PATCH 05/30] Merge PR #2526: Distribution fixes from simulation --- cmd/gaia/app/sim_test.go | 2 +- x/distribution/handler.go | 10 +++++-- x/distribution/keeper/delegation.go | 26 ++++++++++++++--- x/distribution/keeper/validator.go | 36 ++++++++++++++++++++++-- x/distribution/types/dec_coin.go | 43 ++++++++++++++++++++++++----- x/distribution/types/errors.go | 11 ++++++-- x/stake/keeper/delegation.go | 1 + x/stake/simulation/invariants.go | 40 +++++++++++++++++++-------- 8 files changed, 138 insertions(+), 31 deletions(-) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index d7dd123434c1..bf64f24f2505 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -124,7 +124,7 @@ func invariants(app *GaiaApp) []simulation.Invariant { banksim.NonnegativeBalanceInvariant(app.accountMapper), distributionsim.AllInvariants(app.bankKeeper, app.distrKeeper, app.accountMapper), govsim.AllInvariants(), - stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.accountMapper), + stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.distrKeeper, app.accountMapper), slashingsim.AllInvariants(), } } diff --git a/x/distribution/handler.go b/x/distribution/handler.go index 661d7d32aa31..624589cead28 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -58,7 +58,10 @@ func handleMsgWithdrawDelegatorRewardsAll(ctx sdk.Context, msg types.MsgWithdraw func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDelegatorReward, k keeper.Keeper) sdk.Result { - k.WithdrawDelegationReward(ctx, msg.DelegatorAddr, msg.ValidatorAddr) + err := k.WithdrawDelegationReward(ctx, msg.DelegatorAddr, msg.ValidatorAddr) + if err != nil { + return err.Result() + } tags := sdk.NewTags( tags.Action, tags.ActionWithdrawDelegatorReward, @@ -72,7 +75,10 @@ func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDele func handleMsgWithdrawValidatorRewardsAll(ctx sdk.Context, msg types.MsgWithdrawValidatorRewardsAll, k keeper.Keeper) sdk.Result { - k.WithdrawValidatorRewardsAll(ctx, msg.ValidatorAddr) + err := k.WithdrawValidatorRewardsAll(ctx, msg.ValidatorAddr) + if err != nil { + return err.Result() + } tags := sdk.NewTags( tags.Action, tags.ActionWithdrawValidatorRewardsAll, diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index b7443a0c1151..971bd4c14378 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -5,6 +5,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/types" ) +// check whether a delegator distribution info exists +func (k Keeper) HasDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress, + valOperatorAddr sdk.ValAddress) (has bool) { + store := ctx.KVStore(k.storeKey) + return store.Has(GetDelegationDistInfoKey(delAddr, valOperatorAddr)) +} + // get the delegator distribution info func (k Keeper) GetDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress) (ddi types.DelegationDistInfo) { @@ -64,7 +71,11 @@ func (k Keeper) RemoveDelegatorWithdrawAddr(ctx sdk.Context, delAddr, withdrawAd // withdraw all the rewards for a single delegation func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccAddress, - validatorAddr sdk.ValAddress) { + validatorAddr sdk.ValAddress) sdk.Error { + + if !k.HasDelegationDistInfo(ctx, delegatorAddr, validatorAddr) { + return types.ErrNoDelegationDistInfo(k.codespace) + } height := ctx.BlockHeight() bondedTokens := k.stakeKeeper.TotalPower(ctx) @@ -77,14 +88,17 @@ func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccA delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) - k.SetFeePool(ctx, feePool) k.SetValidatorDistInfo(ctx, valInfo) k.SetDelegationDistInfo(ctx, delInfo) withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delegatorAddr) - _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal()) + coinsToAdd, change := withdraw.TruncateDecimal() + feePool.CommunityPool = feePool.CommunityPool.Plus(change) + k.SetFeePool(ctx, feePool) + _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coinsToAdd) if err != nil { panic(err) } + return nil } //___________________________________________________________________________________________ @@ -93,8 +107,12 @@ func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccA func (k Keeper) WithdrawDelegationRewardsAll(ctx sdk.Context, delegatorAddr sdk.AccAddress) { height := ctx.BlockHeight() withdraw := k.getDelegatorRewardsAll(ctx, delegatorAddr, height) + feePool := k.GetFeePool(ctx) withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delegatorAddr) - _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal()) + coinsToAdd, change := withdraw.TruncateDecimal() + feePool.CommunityPool = feePool.CommunityPool.Plus(change) + k.SetFeePool(ctx, feePool) + _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coinsToAdd) if err != nil { panic(err) } diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index ae07d8b3ffff..bbc2a732ed5d 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -5,6 +5,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/types" ) +// check whether a validator has distribution info +func (k Keeper) HasValidatorDistInfo(ctx sdk.Context, + operatorAddr sdk.ValAddress) (exists bool) { + store := ctx.KVStore(k.storeKey) + return store.Has(GetValidatorDistInfoKey(operatorAddr)) +} + // get the validator distribution info func (k Keeper) GetValidatorDistInfo(ctx sdk.Context, operatorAddr sdk.ValAddress) (vdi types.ValidatorDistInfo) { @@ -34,7 +41,11 @@ func (k Keeper) RemoveValidatorDistInfo(ctx sdk.Context, valAddr sdk.ValAddress) } // withdrawal all the validator rewards including the commission -func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.ValAddress) { +func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.ValAddress) sdk.Error { + + if !k.HasValidatorDistInfo(ctx, operatorAddr) { + return types.ErrNoValidatorDistInfo(k.codespace) + } // withdraw self-delegation height := ctx.BlockHeight() @@ -50,11 +61,30 @@ func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.Va validator.GetTokens(), validator.GetCommission()) withdraw = withdraw.Plus(commission) k.SetValidatorDistInfo(ctx, valInfo) - k.SetFeePool(ctx, feePool) withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr) - _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal()) + truncated, change := withdraw.TruncateDecimal() + feePool.CommunityPool = feePool.CommunityPool.Plus(change) + k.SetFeePool(ctx, feePool) + _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, truncated) if err != nil { panic(err) } + + return nil +} + +func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, fn func(index int64, distInfo types.ValidatorDistInfo) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey) + defer iter.Close() + index := int64(0) + for ; iter.Valid(); iter.Next() { + var vdi types.ValidatorDistInfo + k.cdc.MustUnmarshalBinary(iter.Value(), &vdi) + if fn(index, vdi) { + return + } + index++ + } } diff --git a/x/distribution/types/dec_coin.go b/x/distribution/types/dec_coin.go index 59373976de71..9e93137881b7 100644 --- a/x/distribution/types/dec_coin.go +++ b/x/distribution/types/dec_coin.go @@ -43,9 +43,11 @@ func (coin DecCoin) Minus(coinB DecCoin) DecCoin { return DecCoin{coin.Denom, coin.Amount.Sub(coinB.Amount)} } -// return the decimal coins with trunctated decimals -func (coin DecCoin) TruncateDecimal() sdk.Coin { - return sdk.NewCoin(coin.Denom, coin.Amount.TruncateInt()) +// return the decimal coins with trunctated decimals, and return the change +func (coin DecCoin) TruncateDecimal() (sdk.Coin, DecCoin) { + truncated := coin.Amount.TruncateInt() + change := coin.Amount.Sub(sdk.NewDecFromInt(truncated)) + return sdk.NewCoin(coin.Denom, truncated), DecCoin{coin.Denom, change} } //_______________________________________________________________________ @@ -61,13 +63,16 @@ func NewDecCoins(coins sdk.Coins) DecCoins { return dcs } -// return the coins with trunctated decimals -func (coins DecCoins) TruncateDecimal() sdk.Coins { +// return the coins with trunctated decimals, and return the change +func (coins DecCoins) TruncateDecimal() (sdk.Coins, DecCoins) { + changeSum := DecCoins{} out := make(sdk.Coins, len(coins)) for i, coin := range coins { - out[i] = coin.TruncateDecimal() + truncated, change := coin.TruncateDecimal() + out[i] = truncated + changeSum = changeSum.Plus(DecCoins{change}) } - return out + return out, changeSum } // Plus combines two sets of coins @@ -147,3 +152,27 @@ func (coins DecCoins) QuoDec(d sdk.Dec) DecCoins { } return res } + +// returns the amount of a denom from deccoins +func (coins DecCoins) AmountOf(denom string) sdk.Dec { + switch len(coins) { + case 0: + return sdk.ZeroDec() + case 1: + coin := coins[0] + if coin.Denom == denom { + return coin.Amount + } + return sdk.ZeroDec() + default: + midIdx := len(coins) / 2 // 2:1, 3:1, 4:2 + coin := coins[midIdx] + if denom < coin.Denom { + return coins[:midIdx].AmountOf(denom) + } else if denom == coin.Denom { + return coin.Amount + } else { + return coins[midIdx+1:].AmountOf(denom) + } + } +} diff --git a/x/distribution/types/errors.go b/x/distribution/types/errors.go index 57a3dd73e360..605c1b38db33 100644 --- a/x/distribution/types/errors.go +++ b/x/distribution/types/errors.go @@ -8,8 +8,9 @@ import ( type CodeType = sdk.CodeType const ( - DefaultCodespace sdk.CodespaceType = 6 - CodeInvalidInput CodeType = 103 + DefaultCodespace sdk.CodespaceType = 6 + CodeInvalidInput CodeType = 103 + CodeNoDistributionInfo CodeType = 104 ) func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error { @@ -21,3 +22,9 @@ func ErrNilWithdrawAddr(codespace sdk.CodespaceType) sdk.Error { func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil") } +func ErrNoDelegationDistInfo(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeNoDistributionInfo, "no delegation distribution info") +} +func ErrNoValidatorDistInfo(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeNoDistributionInfo, "no validator distribution info") +} diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 25ea3d08b855..214ae399cf93 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -551,6 +551,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, if err != nil { return types.Redelegation{}, err } + k.OnDelegationCreated(ctx, delAddr, valDstAddr) // create the unbonding delegation minTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 2866f6292fef..175338cdf83a 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" abci "github.com/tendermint/tendermint/abci/types" @@ -14,9 +15,9 @@ import ( // AllInvariants runs all invariants of the stake module. // Currently: total supply, positive power -func AllInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simulation.Invariant { +func AllInvariants(ck bank.Keeper, k stake.Keeper, d distribution.Keeper, am auth.AccountMapper) simulation.Invariant { return func(app *baseapp.BaseApp) error { - err := SupplyInvariants(ck, k, am)(app) + err := SupplyInvariants(ck, k, d, am)(app) if err != nil { return err } @@ -31,19 +32,19 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simula // SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations // nolint: unparam -func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simulation.Invariant { +func SupplyInvariants(ck bank.Keeper, k stake.Keeper, d distribution.Keeper, am auth.AccountMapper) simulation.Invariant { return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) pool := k.GetPool(ctx) - loose := sdk.ZeroInt() + loose := sdk.ZeroDec() bonded := sdk.ZeroDec() am.IterateAccounts(ctx, func(acc auth.Account) bool { - loose = loose.Add(acc.GetCoins().AmountOf("steak")) + loose = loose.Add(sdk.NewDecFromInt(acc.GetCoins().AmountOf("steak"))) return false }) k.IterateUnbondingDelegations(ctx, func(_ int64, ubd stake.UnbondingDelegation) bool { - loose = loose.Add(ubd.Balance.Amount) + loose = loose.Add(sdk.NewDecFromInt(ubd.Balance.Amount)) return false }) k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool { @@ -51,21 +52,36 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) sim case sdk.Bonded: bonded = bonded.Add(validator.GetPower()) case sdk.Unbonding: - loose = loose.Add(validator.GetTokens().RoundInt()) + loose = loose.Add(validator.GetTokens()) case sdk.Unbonded: - loose = loose.Add(validator.GetTokens().RoundInt()) + loose = loose.Add(validator.GetTokens()) } return false }) + feePool := d.GetFeePool(ctx) + + // add community pool + loose = loose.Add(feePool.CommunityPool.AmountOf("steak")) + + // add validator distribution pool + loose = loose.Add(feePool.Pool.AmountOf("steak")) + + // add validator distribution commission and yet-to-be-withdrawn-by-delegators + d.IterateValidatorDistInfos(ctx, func(_ int64, distInfo distribution.ValidatorDistInfo) (stop bool) { + loose = loose.Add(distInfo.Pool.AmountOf("steak")) + loose = loose.Add(distInfo.PoolCommission.AmountOf("steak")) + return false + }) + // Loose tokens should equal coin supply plus unbonding delegations plus tokens on unbonded validators - if pool.LooseTokens.RoundInt64() != loose.Int64() { - return fmt.Errorf("expected loose tokens to equal total steak held by accounts - pool.LooseTokens: %v, sum of account tokens: %v", pool.LooseTokens.RoundInt64(), loose.Int64()) + if !pool.LooseTokens.Equal(loose) { + return fmt.Errorf("expected loose tokens to equal total steak held by accounts - pool.LooseTokens: %v, sum of account tokens: %v", pool.LooseTokens, loose) } // Bonded tokens should equal sum of tokens with bonded validators - if pool.BondedTokens.RoundInt64() != bonded.RoundInt64() { - return fmt.Errorf("expected bonded tokens to equal total steak held by bonded validators - pool.BondedTokens: %v, sum of bonded validator tokens: %v", pool.BondedTokens.RoundInt64(), bonded.RoundInt64()) + if !pool.BondedTokens.Equal(bonded) { + return fmt.Errorf("expected bonded tokens to equal total steak held by bonded validators - pool.BondedTokens: %v, sum of bonded validator tokens: %v", pool.BondedTokens, bonded) } // TODO Inflation check on total supply From 25ce0dea00941d4996d03b443cc625b8e08fa4a2 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 18 Oct 2018 22:08:00 +0200 Subject: [PATCH 06/30] Update stake module simulation and fix linter errors --- x/distribution/keeper/hooks.go | 14 +++++++++----- x/distribution/keeper/validator.go | 1 + x/stake/simulation/sim_test.go | 12 +++++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 721a26db1def..6031d056b591 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -14,16 +14,18 @@ func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { vdi := types.ValidatorDistInfo{ OperatorAddr: addr, FeePoolWithdrawalHeight: height, - Pool: types.DecCoins{}, - PoolCommission: types.DecCoins{}, - DelAccum: types.NewTotalAccum(height), + Pool: types.DecCoins{}, + PoolCommission: types.DecCoins{}, + DelAccum: types.NewTotalAccum(height), } k.SetValidatorDistInfo(ctx, vdi) } // Withdrawal all validator rewards func (k Keeper) onValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) { - k.WithdrawValidatorRewardsAll(ctx, addr) + if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil { + panic(err) + } } // Withdrawal all validator distribution rewards and cleanup the distribution record @@ -50,7 +52,9 @@ func (k Keeper) onDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, func (k Keeper) onDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { - k.WithdrawDelegationReward(ctx, delAddr, valAddr) + if err := k.WithdrawDelegationReward(ctx, delAddr, valAddr); err != nil { + panic(err) + } } // Withdrawal all validator distribution rewards and cleanup the distribution record diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index bbc2a732ed5d..8b38b0f6a0eb 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -74,6 +74,7 @@ func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.Va return nil } +// iterate over all the validator distribution infos (inefficient, just used to check invariants) func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, fn func(index int64, distInfo types.ValidatorDistInfo) (stop bool)) { store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey) diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index 6aa780113939..207f42e61603 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -8,7 +8,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/params" @@ -22,13 +24,17 @@ func TestStakeWithRandomMessages(t *testing.T) { bank.RegisterCodec(mapp.Cdc) mapper := mapp.AccountMapper bankKeeper := bank.NewBaseKeeper(mapper) + feeKey := sdk.NewKVStoreKey("fee") stakeKey := sdk.NewKVStoreKey("stake") stakeTKey := sdk.NewTransientStoreKey("transient_stake") paramsKey := sdk.NewKVStoreKey("params") paramsTKey := sdk.NewTransientStoreKey("transient_params") + distrKey := sdk.NewKVStoreKey("distr") - paramstore := params.NewKeeper(mapp.Cdc, paramsKey, paramsTKey).Subspace(stake.DefaultParamspace) - stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, paramstore, stake.DefaultCodespace) + feeCollectionKeeper := auth.NewFeeCollectionKeeper(mapp.Cdc, feeKey) + paramstore := params.NewKeeper(mapp.Cdc, paramsKey, paramsTKey) + stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, paramstore.Subspace(stake.DefaultParamspace), stake.DefaultCodespace) + distrKeeper := distribution.NewKeeper(mapp.Cdc, distrKey, paramstore.Subspace(distribution.DefaultParamspace), bankKeeper, stakeKeeper, feeCollectionKeeper, distribution.DefaultCodespace) mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper)) mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { validatorUpdates := stake.EndBlocker(ctx, stakeKeeper) @@ -58,7 +64,7 @@ func TestStakeWithRandomMessages(t *testing.T) { }, []simulation.RandSetup{ Setup(mapp, stakeKeeper), }, []simulation.Invariant{ - AllInvariants(bankKeeper, stakeKeeper, mapp.AccountMapper), + AllInvariants(bankKeeper, stakeKeeper, distrKeeper, mapp.AccountMapper), }, 10, 100, false, ) From 42ae2671c2bf6e9da442cba49df6416da1185e01 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 03:08:25 +0200 Subject: [PATCH 07/30] Simulation fixes work-in-progress --- cmd/gaia/app/sim_test.go | 10 +++--- x/auth/ante.go | 2 +- x/auth/feekeeper.go | 2 +- x/auth/simulation/fake.go | 55 +++++++++++++++++++++++++++++ x/distribution/keeper/allocation.go | 1 + x/mock/simulation/constants.go | 2 +- x/stake/keeper/delegation.go | 19 ++++++++-- x/stake/simulation/invariants.go | 9 +++-- 8 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 x/auth/simulation/fake.go diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index bf64f24f2505..2321db835808 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" + authsim "github.com/cosmos/cosmos-sdk/x/auth/simulation" banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation" distr "github.com/cosmos/cosmos-sdk/x/distribution" distributionsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" @@ -103,11 +104,12 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ + {5, authsim.SimulateDeductFee(app.accountMapper, app.feeCollectionKeeper)}, {100, banksim.SingleInputSendMsg(app.accountMapper, app.bankKeeper)}, {50, distributionsim.SimulateMsgSetWithdrawAddress(app.accountMapper, app.distrKeeper)}, - {50, distributionsim.SimulateMsgWithdrawDelegatorRewardsAll(app.accountMapper, app.distrKeeper)}, - {50, distributionsim.SimulateMsgWithdrawDelegatorReward(app.accountMapper, app.distrKeeper)}, - {50, distributionsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountMapper, app.distrKeeper)}, + //{50, distributionsim.SimulateMsgWithdrawDelegatorRewardsAll(app.accountMapper, app.distrKeeper)}, + //{50, distributionsim.SimulateMsgWithdrawDelegatorReward(app.accountMapper, app.distrKeeper)}, + //{50, distributionsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountMapper, app.distrKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, {100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, {100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)}, @@ -124,7 +126,7 @@ func invariants(app *GaiaApp) []simulation.Invariant { banksim.NonnegativeBalanceInvariant(app.accountMapper), distributionsim.AllInvariants(app.bankKeeper, app.distrKeeper, app.accountMapper), govsim.AllInvariants(), - stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.distrKeeper, app.accountMapper), + stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountMapper), slashingsim.AllInvariants(), } } diff --git a/x/auth/ante.go b/x/auth/ante.go index b6f88025453b..98536ab7a9d2 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -93,7 +93,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { if !res.IsOK() { return newCtx, res, true } - fck.addCollectedFees(newCtx, stdTx.Fee.Amount) + fck.AddCollectedFees(newCtx, stdTx.Fee.Amount) } for i := 0; i < len(stdSigs); i++ { diff --git a/x/auth/feekeeper.go b/x/auth/feekeeper.go index 45894be1bd6b..83aa2b33bfcb 100644 --- a/x/auth/feekeeper.go +++ b/x/auth/feekeeper.go @@ -46,7 +46,7 @@ func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins store.Set(collectedFeesKey, bz) } -func (fck FeeCollectionKeeper) addCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins { +func (fck FeeCollectionKeeper) AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins { newCoins := fck.GetCollectedFees(ctx).Plus(coins) fck.setCollectedFees(ctx, newCoins) diff --git a/x/auth/simulation/fake.go b/x/auth/simulation/fake.go new file mode 100644 index 000000000000..99001617c3c0 --- /dev/null +++ b/x/auth/simulation/fake.go @@ -0,0 +1,55 @@ +package simulation + +import ( + "errors" + "fmt" + "math/big" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/mock/simulation" +) + +// SimulateDeductFee +func SimulateDeductFee(m auth.AccountMapper, f auth.FeeCollectionKeeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account, event func(string)) ( + action string, fOp []simulation.FutureOperation, err error) { + + account := simulation.RandomAcc(r, accs) + stored := m.GetAccount(ctx, account.Address) + initCoins := stored.GetCoins() + + if len(initCoins) == 0 { + event(fmt.Sprintf("auth/SimulateDeductFee/false")) + return action, nil, nil + } + + denomIndex := r.Intn(len(initCoins)) + amt, err := randPositiveInt(r, initCoins[denomIndex].Amount) + if err != nil { + event(fmt.Sprintf("auth/SimulateDeductFee/false")) + return action, nil, nil + } + + coins := sdk.Coins{sdk.NewCoin(initCoins[denomIndex].Denom, amt)} + stored.SetCoins(initCoins.Minus(coins)) + m.SetAccount(ctx, stored) + f.AddCollectedFees(ctx, coins) + + event(fmt.Sprintf("auth/SimulateDeductFee/true")) + + action = "TestDeductFee" + return action, nil, nil + } +} + +func randPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) { + if !max.GT(sdk.OneInt()) { + return sdk.Int{}, errors.New("max too small") + } + max = max.Sub(sdk.OneInt()) + return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil +} diff --git a/x/distribution/keeper/allocation.go b/x/distribution/keeper/allocation.go index e6dd1c9696bd..748fdd00c537 100644 --- a/x/distribution/keeper/allocation.go +++ b/x/distribution/keeper/allocation.go @@ -18,6 +18,7 @@ func (k Keeper) AllocateFees(ctx sdk.Context, percentVotes sdk.Dec, proposer sdk // get the fees which have been getting collected through all the // transactions in the block feesCollected := k.feeCollectionKeeper.GetCollectedFees(ctx) + fmt.Printf("fees collected: %v\n", feesCollected) feesCollectedDec := types.NewDecCoins(feesCollected) // allocated rewards to proposer diff --git a/x/mock/simulation/constants.go b/x/mock/simulation/constants.go index a96d4541f186..f0f53222f923 100644 --- a/x/mock/simulation/constants.go +++ b/x/mock/simulation/constants.go @@ -17,7 +17,7 @@ const ( evidenceFraction float64 = 0.5 // TODO Remove in favor of binary search for invariant violation - onOperation bool = false + onOperation bool = true ) var ( diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 7168bbdd655e..1afbb341ca31 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -480,7 +480,14 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, return types.UnbondingDelegation{}, err } - balance := sdk.NewCoin(k.BondDenom(ctx), returnAmount.RoundInt()) + rounded := returnAmount.TruncateInt() + balance := sdk.NewCoin(k.BondDenom(ctx), rounded) + change := returnAmount.Sub(sdk.NewDecFromInt(rounded)) + + // for now, change is just burned + pool := k.GetPool(ctx) + pool.LooseTokens = pool.LooseTokens.Sub(change) + k.SetPool(ctx, pool) // no need to create the ubd object just complete now if completeNow { @@ -543,7 +550,15 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, return types.Redelegation{}, err } - returnCoin := sdk.Coin{k.BondDenom(ctx), returnAmount.RoundInt()} + rounded := returnAmount.TruncateInt() + returnCoin := sdk.NewCoin(k.BondDenom(ctx), rounded) + change := returnAmount.Sub(sdk.NewDecFromInt(rounded)) + + // for now, change is just burned + pool := k.GetPool(ctx) + pool.LooseTokens = pool.LooseTokens.Sub(change) + k.SetPool(ctx, pool) + dstValidator, found := k.GetValidator(ctx, valDstAddr) if !found { return types.Redelegation{}, types.ErrBadRedelegationDst(k.Codespace()) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 175338cdf83a..d906cdbcca7d 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -15,9 +15,9 @@ import ( // AllInvariants runs all invariants of the stake module. // Currently: total supply, positive power -func AllInvariants(ck bank.Keeper, k stake.Keeper, d distribution.Keeper, am auth.AccountMapper) simulation.Invariant { +func AllInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountMapper) simulation.Invariant { return func(app *baseapp.BaseApp) error { - err := SupplyInvariants(ck, k, d, am)(app) + err := SupplyInvariants(ck, k, f, d, am)(app) if err != nil { return err } @@ -32,7 +32,7 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, d distribution.Keeper, am aut // SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations // nolint: unparam -func SupplyInvariants(ck bank.Keeper, k stake.Keeper, d distribution.Keeper, am auth.AccountMapper) simulation.Invariant { +func SupplyInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountMapper) simulation.Invariant { return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) pool := k.GetPool(ctx) @@ -61,6 +61,9 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, d distribution.Keeper, am feePool := d.GetFeePool(ctx) + // add outstanding fees + loose = loose.Add(sdk.NewDecFromInt(f.GetCollectedFees(ctx).AmountOf("steak"))) + // add community pool loose = loose.Add(feePool.CommunityPool.AmountOf("steak")) From 4779359e2913dadbc713d2676503a5ba01893c8f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 03:21:11 +0200 Subject: [PATCH 08/30] Linter, enable simulated distribution msgs again --- cmd/gaia/app/sim_test.go | 6 +++--- x/auth/feekeeper.go | 1 + x/auth/feekeeper_test.go | 4 ++-- x/auth/simulation/fake.go | 5 ++++- x/stake/simulation/sim_test.go | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 2321db835808..50db940234e9 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -107,9 +107,9 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { {5, authsim.SimulateDeductFee(app.accountMapper, app.feeCollectionKeeper)}, {100, banksim.SingleInputSendMsg(app.accountMapper, app.bankKeeper)}, {50, distributionsim.SimulateMsgSetWithdrawAddress(app.accountMapper, app.distrKeeper)}, - //{50, distributionsim.SimulateMsgWithdrawDelegatorRewardsAll(app.accountMapper, app.distrKeeper)}, - //{50, distributionsim.SimulateMsgWithdrawDelegatorReward(app.accountMapper, app.distrKeeper)}, - //{50, distributionsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountMapper, app.distrKeeper)}, + {50, distributionsim.SimulateMsgWithdrawDelegatorRewardsAll(app.accountMapper, app.distrKeeper)}, + {50, distributionsim.SimulateMsgWithdrawDelegatorReward(app.accountMapper, app.distrKeeper)}, + {50, distributionsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountMapper, app.distrKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, {100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, {100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)}, diff --git a/x/auth/feekeeper.go b/x/auth/feekeeper.go index 83aa2b33bfcb..dc7e91987bfa 100644 --- a/x/auth/feekeeper.go +++ b/x/auth/feekeeper.go @@ -46,6 +46,7 @@ func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins store.Set(collectedFeesKey, bz) } +// add collected fees (mostly exposed for simulation...) func (fck FeeCollectionKeeper) AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins { newCoins := fck.GetCollectedFees(ctx).Plus(coins) fck.setCollectedFees(ctx, newCoins) diff --git a/x/auth/feekeeper_test.go b/x/auth/feekeeper_test.go index 82bbe9c35da3..d481511617db 100644 --- a/x/auth/feekeeper_test.go +++ b/x/auth/feekeeper_test.go @@ -49,11 +49,11 @@ func TestFeeCollectionKeeperAdd(t *testing.T) { require.True(t, fck.GetCollectedFees(ctx).IsEqual(emptyCoins)) // add oneCoin and check that pool is now oneCoin - fck.addCollectedFees(ctx, oneCoin) + fck.AddCollectedFees(ctx, oneCoin) require.True(t, fck.GetCollectedFees(ctx).IsEqual(oneCoin)) // add oneCoin again and check that pool is now twoCoins - fck.addCollectedFees(ctx, oneCoin) + fck.AddCollectedFees(ctx, oneCoin) require.True(t, fck.GetCollectedFees(ctx).IsEqual(twoCoins)) } diff --git a/x/auth/simulation/fake.go b/x/auth/simulation/fake.go index 99001617c3c0..82f4e607bf74 100644 --- a/x/auth/simulation/fake.go +++ b/x/auth/simulation/fake.go @@ -35,7 +35,10 @@ func SimulateDeductFee(m auth.AccountMapper, f auth.FeeCollectionKeeper) simulat } coins := sdk.Coins{sdk.NewCoin(initCoins[denomIndex].Denom, amt)} - stored.SetCoins(initCoins.Minus(coins)) + err = stored.SetCoins(initCoins.Minus(coins)) + if err != nil { + panic(err) + } m.SetAccount(ctx, stored) f.AddCollectedFees(ctx, coins) diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index 207f42e61603..2ce9fa00df6a 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -64,7 +64,7 @@ func TestStakeWithRandomMessages(t *testing.T) { }, []simulation.RandSetup{ Setup(mapp, stakeKeeper), }, []simulation.Invariant{ - AllInvariants(bankKeeper, stakeKeeper, distrKeeper, mapp.AccountMapper), + AllInvariants(bankKeeper, stakeKeeper, feeCollectionKeeper, distrKeeper, mapp.AccountMapper), }, 10, 100, false, ) From d21f93afb03388106c7248df28e695f8e3dfd66a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 03:55:14 +0200 Subject: [PATCH 09/30] Simulation debugging contd. --- x/mock/simulation/random_simulate_blocks.go | 22 +++++++++++++++++---- x/mock/simulation/util.go | 3 ++- x/stake/simulation/invariants.go | 1 - x/stake/types/pool.go | 2 +- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index d2680bc0f4cd..8181bb23a52d 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -129,24 +129,38 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, pastTimes = append(pastTimes, header.Time) pastVoteInfos = append(pastVoteInfos, request.LastCommitInfo.Votes) + // Construct log writer + logWriter := addLogMessage(testingMode, blockLogBuilders, i) + // Run the BeginBlock handler + logWriter("BeginBlock") app.BeginBlock(request) if testingMode { // Make sure invariants hold at beginning of block - assertAllInvariants(t, app, invariants, displayLogs) + assertAllInvariants(t, app, invariants, "BeginBlock", displayLogs) } - logWriter := addLogMessage(testingMode, blockLogBuilders, i) ctx := app.NewContext(false, header) thisBlockSize := getBlockSize(r, blockSize) // Run queued operations. Ignores blocksize if blocksize is too small + logWriter("Queued operations") numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, accs, logWriter, displayLogs, event) numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, accs, logWriter, displayLogs, event) + if testingMode && onOperation { + // Make sure invariants hold at end of queued operations + assertAllInvariants(t, app, invariants, "QueuedOperations", displayLogs) + } + thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan + logWriter("Standard operations") operations := blockSimulator(thisBlockSize, r, app, ctx, accs, header, logWriter) opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan + if testingMode { + // Make sure invariants hold at end of block + assertAllInvariants(t, app, invariants, "StandardOperations", displayLogs) + } res := app.EndBlock(abci.RequestEndBlock{}) header.Height++ @@ -156,7 +170,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, if testingMode { // Make sure invariants hold at end of block - assertAllInvariants(t, app, invariants, displayLogs) + assertAllInvariants(t, app, invariants, "EndBlock", displayLogs) } if commit { app.Commit() @@ -210,7 +224,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f queueOperations(operationQueue, timeOperationQueue, futureOps) if testingMode { if onOperation { - assertAllInvariants(t, app, invariants, displayLogs) + assertAllInvariants(t, app, invariants, fmt.Sprintf("operation: %v", logUpdate), displayLogs) } if opCount%50 == 0 { fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize) diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index 54bfabc73f8e..93c0a5be097e 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -102,10 +102,11 @@ func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height } // assertAllInvariants asserts a list of provided invariants against application state -func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invariant, displayLogs func()) { +func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invariant, where string, displayLogs func()) { for i := 0; i < len(invariants); i++ { err := invariants[i](app) if err != nil { + fmt.Printf("Invariants broken after %s\n", where) fmt.Println(err.Error()) displayLogs() t.Fatal() diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index d906cdbcca7d..e249bac9f41d 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -87,7 +87,6 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper return fmt.Errorf("expected bonded tokens to equal total steak held by bonded validators - pool.BondedTokens: %v, sum of bonded validator tokens: %v", pool.BondedTokens, bonded) } - // TODO Inflation check on total supply return nil } } diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index c7cb6974868e..95ba0e3545aa 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -92,7 +92,7 @@ func (p Pool) ProcessProvisions(params Params) Pool { Mul(p.TokenSupply()). Quo(hrsPerYrDec) - // TODO add to the fees provisions + // TODO add to the fees provisions p.LooseTokens = p.LooseTokens.Add(provisions) return p } From d9916f99eb710d7ce3aaac4eb19cabc2f3faea44 Mon Sep 17 00:00:00 2001 From: Rigel Date: Fri, 19 Oct 2018 14:58:37 -0400 Subject: [PATCH 10/30] bugfix (#2535) --- x/distribution/keeper/delegation.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 971bd4c14378..78298157984f 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -123,10 +123,10 @@ func (k Keeper) getDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, withdraw := types.DecCoins{} bondedTokens := k.stakeKeeper.TotalPower(ctx) - feePool := k.GetFeePool(ctx) // iterate over all the delegations operationAtDelegation := func(_ int64, del sdk.Delegation) (stop bool) { + feePool := k.GetFeePool(ctx) valAddr := del.GetValidator() delInfo := k.GetDelegationDistInfo(ctx, delAddr, valAddr) valInfo := k.GetValidatorDistInfo(ctx, valAddr) @@ -142,7 +142,5 @@ func (k Keeper) getDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, return false } k.stakeKeeper.IterateDelegations(ctx, delAddr, operationAtDelegation) - - k.SetFeePool(ctx, feePool) return withdraw } From 4f719d60cdb4822f67d10f4afff1d55257fb51b2 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 20:59:51 +0200 Subject: [PATCH 11/30] Remove the print statement --- x/distribution/keeper/allocation.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/distribution/keeper/allocation.go b/x/distribution/keeper/allocation.go index 67fde398a1f9..debf0eb620bf 100644 --- a/x/distribution/keeper/allocation.go +++ b/x/distribution/keeper/allocation.go @@ -18,7 +18,6 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, percentVotes sdk.Dec, proposer s // get the fees which have been getting collected through all the // transactions in the block feesCollected := k.feeCollectionKeeper.GetCollectedFees(ctx) - fmt.Printf("fees collected: %v\n", feesCollected) feesCollectedDec := types.NewDecCoins(feesCollected) // allocated rewards to proposer From 6c9ad8c0310c5265a0e6c187695f705598b1da8e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 21:58:05 +0200 Subject: [PATCH 12/30] Add sanity checks --- x/auth/simulation/fake.go | 4 ++++ x/distribution/types/validator_info.go | 3 +++ 2 files changed, 7 insertions(+) diff --git a/x/auth/simulation/fake.go b/x/auth/simulation/fake.go index 82f4e607bf74..e89e70842268 100644 --- a/x/auth/simulation/fake.go +++ b/x/auth/simulation/fake.go @@ -40,6 +40,10 @@ func SimulateDeductFee(m auth.AccountMapper, f auth.FeeCollectionKeeper) simulat panic(err) } m.SetAccount(ctx, stored) + if !coins.IsNotNegative() { + panic("setting negative fees") + } + f.AddCollectedFees(ctx, coins) event(fmt.Sprintf("auth/SimulateDeductFee/true")) diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go index c8a02569cba6..ae80d8aa71cc 100644 --- a/x/distribution/types/validator_info.go +++ b/x/distribution/types/validator_info.go @@ -45,6 +45,9 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo blocks := height - vi.FeePoolWithdrawalHeight vi.FeePoolWithdrawalHeight = height accum := vdTokens.MulInt(sdk.NewInt(blocks)) + if accum.GT(fp.ValAccum.Accum) { + panic("individual accum should never be greater than the total") + } withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.ValAccum.Accum) remainingTokens := fp.Pool.Minus(withdrawalTokens) From 2e8f35436791e023f1b452d2be1951b2314ad345 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 22:26:28 +0200 Subject: [PATCH 13/30] Fixup stake hooks (hopefully...) --- x/stake/handler.go | 7 ++----- x/stake/keeper/delegation.go | 9 ++++++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index a75c30055ba0..81b505039809 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -114,8 +114,6 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k } k.OnValidatorCreated(ctx, validator.OperatorAddr) - accAddr := sdk.AccAddress(validator.OperatorAddr) - k.OnDelegationCreated(ctx, accAddr, validator.OperatorAddr) tags := sdk.NewTags( tags.Action, tags.ActionCreateValidator, @@ -150,6 +148,8 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe return err.Result() } validator.Commission = commission + // call the hook if present + k.OnValidatorCommissionChange(ctx, msg.ValidatorAddr) } k.SetValidator(ctx, validator) @@ -185,9 +185,6 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) return err.Result() } - // call the hook if present - k.OnDelegationCreated(ctx, msg.DelegatorAddr, validator.OperatorAddr) - tags := sdk.NewTags( tags.Action, tags.ActionDelegate, tags.Delegator, []byte(msg.DelegatorAddr.String()), diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 1afbb341ca31..a5d08d4892a9 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -359,6 +359,13 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co } } + // call the appropriate hook if present + if found { + k.OnDelegationSharesModified(ctx, delAddr, validator.OperatorAddr) + } else { + k.OnDelegationCreated(ctx, delAddr, validator.OperatorAddr) + } + if subtractAccount { // Account new shares, save _, _, err = k.bankKeeper.SubtractCoins(ctx, delegation.DelegatorAddr, sdk.Coins{bondAmt}) @@ -373,6 +380,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co delegation.Shares = delegation.Shares.Add(newShares) delegation.Height = ctx.BlockHeight() k.SetDelegation(ctx, delegation) + return newShares, nil } @@ -567,7 +575,6 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, if err != nil { return types.Redelegation{}, err } - k.OnDelegationCreated(ctx, delAddr, valDstAddr) // create the unbonding delegation minTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr) From 843ccaf615d4642f4cce71eabc5bbf87f12f7d65 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 22:33:21 +0200 Subject: [PATCH 14/30] Remove comment --- x/stake/handler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index 81b505039809..e251d2c32593 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -148,7 +148,6 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe return err.Result() } validator.Commission = commission - // call the hook if present k.OnValidatorCommissionChange(ctx, msg.ValidatorAddr) } From 1afb5bf9d2589e017da24ca52e7cc991f143bc01 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 23:01:23 +0200 Subject: [PATCH 15/30] Simulate minting, fix bug where pool was not updated --- cmd/gaia/app/sim_test.go | 14 +++++--------- x/mint/abci_app.go | 3 +++ x/mint/expected_keepers.go | 7 ++++++- x/mint/genesis.go | 2 +- x/mint/minter.go | 1 + 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 20e903b05906..630165cd0157 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -51,7 +51,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { // Randomly generate some genesis accounts for _, acc := range accs { - coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}} + coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(10000)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ Address: acc.Address, Coins: coins, @@ -73,20 +73,16 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { valAddrs[i] = valAddr validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{}) - validator.Tokens = sdk.NewDec(100) - validator.DelegatorShares = sdk.NewDec(100) - delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(100), 0} + validator.Tokens = sdk.NewDec(10000) + validator.DelegatorShares = sdk.NewDec(10000) + delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(10000), 0} validators = append(validators, validator) delegations = append(delegations, delegation) } - stakeGenesis.Pool.LooseTokens = sdk.NewDec(int64(100*250) + (numInitiallyBonded * 100)) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(int64(10000*250) + (numInitiallyBonded * 10000)) stakeGenesis.Validators = validators stakeGenesis.Bonds = delegations - - // No inflation, for now mintGenesis := mint.DefaultGenesisState() - mintGenesis.Params.InflationMax = sdk.NewDec(0) - mintGenesis.Params.InflationMin = sdk.NewDec(0) genesis := GenesisState{ Accounts: genesisAccounts, diff --git a/x/mint/abci_app.go b/x/mint/abci_app.go index cb3bd44c51bf..1e6b5a27bf31 100644 --- a/x/mint/abci_app.go +++ b/x/mint/abci_app.go @@ -21,5 +21,8 @@ func BeginBlocker(ctx sdk.Context, k Keeper) { minter.InflationLastTime = blockTime minter, mintedCoin := minter.ProcessProvisions(params, totalSupply, bondedRatio) k.fck.AddCollectedFees(ctx, sdk.Coins{mintedCoin}) + pool := k.sk.GetPool(ctx) + pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDecFromInt(mintedCoin.Amount)) + k.sk.SetPool(ctx, pool) k.SetMinter(ctx, minter) } diff --git a/x/mint/expected_keepers.go b/x/mint/expected_keepers.go index 8daaaf7ac32d..69ee9f3bd7e8 100644 --- a/x/mint/expected_keepers.go +++ b/x/mint/expected_keepers.go @@ -1,9 +1,14 @@ package mint -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + stake "github.com/cosmos/cosmos-sdk/x/stake" +) // expected stake keeper type StakeKeeper interface { + GetPool(ctx sdk.Context) stake.Pool + SetPool(ctx sdk.Context, pool stake.Pool) TotalPower(ctx sdk.Context) sdk.Dec BondedRatio(ctx sdk.Context) sdk.Dec InflateSupply(ctx sdk.Context, newTokens sdk.Dec) diff --git a/x/mint/genesis.go b/x/mint/genesis.go index 9dab64628ef3..5617685730b0 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -6,7 +6,7 @@ import ( // GenesisState - all distribution state that must be provided at genesis type GenesisState struct { - Minter Minter `json:"Minter"` // minter object + Minter Minter `json:"minter"` // minter object Params Params `json:"params"` // inflation params } diff --git a/x/mint/minter.go b/x/mint/minter.go index da2f6c5bed03..135675887bf0 100644 --- a/x/mint/minter.go +++ b/x/mint/minter.go @@ -40,6 +40,7 @@ func (m Minter) ProcessProvisions(params Params, totalSupply, bondedRatio sdk.De m.Inflation = m.NextInflation(params, bondedRatio) provisionsDec := m.Inflation.Mul(totalSupply).Quo(hrsPerYr) provisions = sdk.NewCoin(params.MintDenom, provisionsDec.TruncateInt()) + return m, provisions } From dc13a0c61d04abc11cf439f5a0fc7085160b9622 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 19 Oct 2018 23:06:18 +0200 Subject: [PATCH 16/30] Use InflateSupply instead --- x/mint/abci_app.go | 4 +--- x/mint/expected_keepers.go | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/x/mint/abci_app.go b/x/mint/abci_app.go index 1e6b5a27bf31..73491f8081e1 100644 --- a/x/mint/abci_app.go +++ b/x/mint/abci_app.go @@ -21,8 +21,6 @@ func BeginBlocker(ctx sdk.Context, k Keeper) { minter.InflationLastTime = blockTime minter, mintedCoin := minter.ProcessProvisions(params, totalSupply, bondedRatio) k.fck.AddCollectedFees(ctx, sdk.Coins{mintedCoin}) - pool := k.sk.GetPool(ctx) - pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDecFromInt(mintedCoin.Amount)) - k.sk.SetPool(ctx, pool) + k.sk.InflateSupply(ctx, sdk.NewDecFromInt(mintedCoin.Amount)) k.SetMinter(ctx, minter) } diff --git a/x/mint/expected_keepers.go b/x/mint/expected_keepers.go index 69ee9f3bd7e8..150e155cb3b5 100644 --- a/x/mint/expected_keepers.go +++ b/x/mint/expected_keepers.go @@ -2,13 +2,10 @@ package mint import ( sdk "github.com/cosmos/cosmos-sdk/types" - stake "github.com/cosmos/cosmos-sdk/x/stake" ) // expected stake keeper type StakeKeeper interface { - GetPool(ctx sdk.Context) stake.Pool - SetPool(ctx sdk.Context, pool stake.Pool) TotalPower(ctx sdk.Context) sdk.Dec BondedRatio(ctx sdk.Context) sdk.Dec InflateSupply(ctx sdk.Context, newTokens sdk.Dec) From a6ef3c42aa007f54f58e613b2511970309e1012a Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 19 Oct 2018 17:41:39 -0400 Subject: [PATCH 17/30] use power instead of total tokens --- x/distribution/keeper/delegation.go | 4 ++-- x/distribution/keeper/validator.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 78298157984f..8183d95b1a54 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -86,7 +86,7 @@ func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccA delegation := k.stakeKeeper.Delegation(ctx, delegatorAddr, validatorAddr) delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, - validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) + validator.GetPower(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) k.SetValidatorDistInfo(ctx, valInfo) k.SetDelegationDistInfo(ctx, delInfo) @@ -134,7 +134,7 @@ func (k Keeper) getDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, delegation := k.stakeKeeper.Delegation(ctx, delAddr, valAddr) delInfo, valInfo, feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, - validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) + validator.GetPower(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) withdraw = withdraw.Plus(diWithdraw) k.SetFeePool(ctx, feePool) k.SetValidatorDistInfo(ctx, valInfo) diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index 8b38b0f6a0eb..0d3453387465 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -58,7 +58,7 @@ func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.Va valInfo := k.GetValidatorDistInfo(ctx, operatorAddr) feePool := k.GetFeePool(ctx) valInfo, feePool, commission := valInfo.WithdrawCommission(feePool, height, bondedTokens, - validator.GetTokens(), validator.GetCommission()) + validator.GetPower(), validator.GetCommission()) withdraw = withdraw.Plus(commission) k.SetValidatorDistInfo(ctx, valInfo) From 7770aec306152c226eb22bbfe95652553460e746 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 20 Oct 2018 00:13:44 +0200 Subject: [PATCH 18/30] Withdraw rewards on bonded to unbonding --- cmd/gaia/app/app.go | 4 ++-- docs/spec/staking/hooks.md | 2 +- types/stake.go | 4 ++-- x/distribution/keeper/hooks.go | 14 ++++++++++++-- x/slashing/hooks.go | 6 +++--- x/slashing/hooks_test.go | 2 +- x/stake/keeper/hooks.go | 4 ++-- x/stake/keeper/val_state_change.go | 2 +- 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 8fba41f60072..6c02b7aaa6b6 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -343,8 +343,8 @@ func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) { h.sh.OnValidatorBonded(ctx, addr) } -func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress) { - h.sh.OnValidatorBeginUnbonding(ctx, addr) +func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) { + h.sh.OnValidatorBeginUnbonding(ctx, addr, operator) } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationCreated(ctx, delAddr, valAddr) diff --git a/docs/spec/staking/hooks.md b/docs/spec/staking/hooks.md index bcc496e3d6e0..60359b4312e9 100644 --- a/docs/spec/staking/hooks.md +++ b/docs/spec/staking/hooks.md @@ -10,7 +10,7 @@ type StakingHooks interface { OnValidatorRemoved(ctx Context, address ValAddress) // called when a validator is deleted OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded - OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // called when a validator begins unbonding + OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is created OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation's shares are modified diff --git a/types/stake.go b/types/stake.go index d529fa1794b7..b6e48bde7fd7 100644 --- a/types/stake.go +++ b/types/stake.go @@ -115,8 +115,8 @@ type StakingHooks interface { OnValidatorCommissionChange(ctx Context, address ValAddress) // Must be called when a validator's commission is modified OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted - OnValidatorBonded(ctx Context, address ConsAddress) // Must be called when a validator is bonded - OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // Must be called when a validator begins unbonding + OnValidatorBonded(ctx Context, address ConsAddress) // Must be called when a validator is bonded + OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator begins unbonding OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is created OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation's shares are modified diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 6031d056b591..630404607dd6 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -33,6 +33,13 @@ func (k Keeper) onValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { k.RemoveValidatorDistInfo(ctx, addr) } +// Withdraw all validator rewards +func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ValAddress) { + if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil { + panic(err) + } +} + //_________________________________________________________________________________________ // Create a new delegator distribution record @@ -96,6 +103,9 @@ func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valA h.k.onDelegationRemoved(ctx, delAddr, valAddr) } +func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) { + h.k.onValidatorBeginUnbonding(ctx, addr) +} + // nolint - unused hooks for interface -func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) {} -func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress) {} +func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) {} diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index ed07bc0c759a..d91c69579ebc 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -30,7 +30,7 @@ func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { } // Mark the slashing period as having ended when a validator begins unbonding -func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { +func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) { slashingPeriod := k.getValidatorSlashingPeriodForHeight(ctx, address, ctx.BlockHeight()) slashingPeriod.EndHeight = ctx.BlockHeight() k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) @@ -56,8 +56,8 @@ func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { } // Implements sdk.ValidatorHooks -func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { - h.k.onValidatorBeginUnbonding(ctx, address) +func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) { + h.k.onValidatorBeginUnbonding(ctx, address, operator) } // nolint - unused hooks diff --git a/x/slashing/hooks_test.go b/x/slashing/hooks_test.go index 951e3637f377..96dfab653c93 100644 --- a/x/slashing/hooks_test.go +++ b/x/slashing/hooks_test.go @@ -20,7 +20,7 @@ func TestHookOnValidatorBeginUnbonding(t *testing.T) { ctx, _, _, _, keeper := createTestInput(t, DefaultParams()) addr := sdk.ConsAddress(addrs[0]) keeper.onValidatorBonded(ctx, addr) - keeper.onValidatorBeginUnbonding(ctx, addr) + keeper.onValidatorBeginUnbonding(ctx, addr, sdk.ValAddress(addrs[0])) period := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, ctx.BlockHeight()) require.Equal(t, ValidatorSlashingPeriod{addr, ctx.BlockHeight(), ctx.BlockHeight(), sdk.ZeroDec()}, period) } diff --git a/x/stake/keeper/hooks.go b/x/stake/keeper/hooks.go index 81bf5594ba75..060c84f5128c 100644 --- a/x/stake/keeper/hooks.go +++ b/x/stake/keeper/hooks.go @@ -29,9 +29,9 @@ func (k Keeper) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { } } -func (k Keeper) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { +func (k Keeper) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) { if k.hooks != nil { - k.hooks.OnValidatorBeginUnbonding(ctx, address) + k.hooks.OnValidatorBeginUnbonding(ctx, address, operator) } } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 89d3f681faf6..e123bb2b0a8e 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -219,7 +219,7 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat // call the unbond hook if present if k.hooks != nil { - k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) + k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddr) } return validator From 255a6a5db89977b800ee0ac5ee2af2af34f6a1c4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 20 Oct 2018 00:19:10 +0200 Subject: [PATCH 19/30] Cleanup to one hook --- x/distribution/keeper/hooks.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 630404607dd6..dde631a8d613 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -22,7 +22,7 @@ func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { } // Withdrawal all validator rewards -func (k Keeper) onValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) { +func (k Keeper) onValidatorModified(ctx sdk.Context, addr sdk.ValAddress) { if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil { panic(err) } @@ -33,13 +33,6 @@ func (k Keeper) onValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { k.RemoveValidatorDistInfo(ctx, addr) } -// Withdraw all validator rewards -func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ValAddress) { - if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil { - panic(err) - } -} - //_________________________________________________________________________________________ // Create a new delegator distribution record @@ -88,7 +81,7 @@ func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { h.k.onValidatorCreated(ctx, addr) } func (h Hooks) OnValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) { - h.k.onValidatorCommissionChange(ctx, addr) + h.k.onValidatorModified(ctx, addr) } func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { h.k.onValidatorRemoved(ctx, addr) @@ -104,7 +97,7 @@ func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valA } func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) { - h.k.onValidatorBeginUnbonding(ctx, addr) + h.k.onValidatorModified(ctx, addr) } // nolint - unused hooks for interface From c88fc481d4d45c86783da32470b5b18b79ad3f44 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 20 Oct 2018 01:05:07 +0200 Subject: [PATCH 20/30] Fix linter issues --- x/distribution/keeper/hooks.go | 1 - x/slashing/hooks_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index dde631a8d613..698e729657d0 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -95,7 +95,6 @@ func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddres func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.k.onDelegationRemoved(ctx, delAddr, valAddr) } - func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) { h.k.onValidatorModified(ctx, addr) } diff --git a/x/slashing/hooks_test.go b/x/slashing/hooks_test.go index 96dfab653c93..5da7ebafbb46 100644 --- a/x/slashing/hooks_test.go +++ b/x/slashing/hooks_test.go @@ -20,7 +20,7 @@ func TestHookOnValidatorBeginUnbonding(t *testing.T) { ctx, _, _, _, keeper := createTestInput(t, DefaultParams()) addr := sdk.ConsAddress(addrs[0]) keeper.onValidatorBonded(ctx, addr) - keeper.onValidatorBeginUnbonding(ctx, addr, sdk.ValAddress(addrs[0])) + keeper.onValidatorBeginUnbonding(ctx, addr, addrs[0]) period := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, ctx.BlockHeight()) require.Equal(t, ValidatorSlashingPeriod{addr, ctx.BlockHeight(), ctx.BlockHeight(), sdk.ZeroDec()}, period) } From 3fa577892198b907b16914162533528f4ee85419 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 21 Oct 2018 12:02:45 -0700 Subject: [PATCH 21/30] Update naming (BondedValidatorIndex->ValidatorsBondedIndex) and add some comments --- x/distribution/keeper/delegation.go | 2 ++ x/stake/keeper/key.go | 2 +- x/stake/keeper/val_state_change.go | 14 ++++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 8183d95b1a54..4132976ef640 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -77,6 +77,7 @@ func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccA return types.ErrNoDelegationDistInfo(k.codespace) } + // TODO: Reconcile with duplicate code in getDelegatorRewardsAll. height := ctx.BlockHeight() bondedTokens := k.stakeKeeper.TotalPower(ctx) feePool := k.GetFeePool(ctx) @@ -125,6 +126,7 @@ func (k Keeper) getDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, bondedTokens := k.stakeKeeper.TotalPower(ctx) // iterate over all the delegations + // TODO: Reconcile with duplicate code in WithdrawDelegationReward. operationAtDelegation := func(_ int64, del sdk.Delegation) (stop bool) { feePool := k.GetFeePool(ctx) valAddr := del.GetValidator() diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index d5f2fc9d9e33..91ea7d709cde 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -61,7 +61,7 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) [] } // get the bonded validator index key for an operator address -func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte { +func GetValidatorsBondedIndexKey(operator sdk.ValAddress) []byte { return append(ValidatorsBondedIndexKey, operator...) } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index e123bb2b0a8e..2c832a8ad2b7 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -11,7 +11,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake/types" ) -// Apply and return accumulated updates to the bonded validator set +// Apply and return accumulated updates to the bonded validator set. Also, +// * Updates the active bonded valset as keyed by GetValidatorsBondedIndexKey(). +// * Updates validator status' according to updated powers. +// * Updates the fee pool bonded vs loose tokens. +// * Updates relevant indices. // // CONTRACT: Only validators with non-zero power or zero-power that were bonded // at the previous block height or were removed from the validator set entirely @@ -21,7 +25,9 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab store := ctx.KVStore(k.storeKey) maxValidators := k.GetParams(ctx).MaxValidators - // retrieve last validator set + // Retrieve the last validator set. + // This persistent set is updated later in this function. + // (see GetValidatorsBondedIndexKey()). last := k.retrieveLastValidatorSet(ctx) // iterate over validators, highest power to lowest @@ -73,7 +79,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab delete(last, operatorBytes) // set the bonded validator index - store.Set(GetBondedValidatorIndexKey(operator), newPowerBytes) + store.Set(GetValidatorsBondedIndexKey(operator), newPowerBytes) // keep count count++ @@ -98,7 +104,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab } // delete from the bonded validator index - store.Delete(GetBondedValidatorIndexKey(operator)) + store.Delete(GetValidatorsBondedIndexKey(operator)) // update the validator set updates = append(updates, validator.ABCIValidatorUpdateZero()) From 015b829a540320e4ef2d795a6fd3bba19172c31b Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 21 Oct 2018 15:26:58 -0700 Subject: [PATCH 22/30] GetValidatorsBonded -> LastValidators etc --- x/stake/handler_test.go | 8 +++--- x/stake/keeper/key.go | 46 +++++++++++++++++------------- x/stake/keeper/key_test.go | 20 ++++++------- x/stake/keeper/sdk_types.go | 4 +-- x/stake/keeper/val_state_change.go | 14 +++++---- x/stake/keeper/validator.go | 12 ++++---- x/stake/keeper/validator_test.go | 12 ++++---- x/stake/stake.go | 5 ++-- 8 files changed, 65 insertions(+), 56 deletions(-) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 3cd81202ae26..c4a558b91609 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -889,21 +889,21 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx))) + require.Equal(t, 1, len(keeper.GetLastValidators(ctx))) msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx))) + require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx))) + require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) // unbond the valdator-2 msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDec(30)) @@ -916,7 +916,7 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { // because there are extra validators waiting to get in, the queued // validator (aka. validator-1) should make it into the bonded group, thus // the total number of validators should stay the same - vals := keeper.GetValidatorsBonded(ctx) + vals := keeper.GetLastValidators(ctx) require.Equal(t, 2, len(vals), "vals %v", vals) val1, found := keeper.GetValidator(ctx, validatorAddr1) require.True(t, found) diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 91ea7d709cde..6faf36b75074 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -15,21 +15,27 @@ var ( // Keys for store prefixes // TODO DEPRECATED: delete in next release and reorder keys // ParamKey = []byte{0x00} // key for parameters relating to staking - PoolKey = []byte{0x01} // key for the staking pools - ValidatorsKey = []byte{0x02} // prefix for each key to a validator - ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey - ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators - ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power - IntraTxCounterKey = []byte{0x06} // key for intra-block tx index - DelegationKey = []byte{0x07} // key for a delegation - UnbondingDelegationKey = []byte{0x08} // key for an unbonding-delegation - UnbondingDelegationByValIndexKey = []byte{0x09} // prefix for each key for an unbonding-delegation, by validator operator - RedelegationKey = []byte{0x0A} // key for a redelegation - RedelegationByValSrcIndexKey = []byte{0x0B} // prefix for each key for an redelegation, by source validator operator - RedelegationByValDstIndexKey = []byte{0x0C} // prefix for each key for an redelegation, by destination validator operator - UnbondingQueueKey = []byte{0x0D} // prefix for the timestamps in unbonding queue - RedelegationQueueKey = []byte{0x0E} // prefix for the timestamps in redelegations queue - ValidatorQueueKey = []byte{0x0F} // prefix for the timestamps in validator queue + PoolKey = []byte{0x01} // key for the staking pools + IntraTxCounterKey = []byte{0x02} // key for intra-block tx index + + // Last* values are const during a block. + LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators + LastTotalPowerKey = []byte{0x12} // prefix for each key to a validator index, for bonded validators + + ValidatorsKey = []byte{0x21} // prefix for each key to a validator + ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey + ValidatorsByPowerIndexKey = []byte{0x23} // prefix for each key to a validator index, sorted by power + + DelegationKey = []byte{0x31} // key for a delegation + UnbondingDelegationKey = []byte{0x32} // key for an unbonding-delegation + UnbondingDelegationByValIndexKey = []byte{0x33} // prefix for each key for an unbonding-delegation, by validator operator + RedelegationKey = []byte{0x34} // key for a redelegation + RedelegationByValSrcIndexKey = []byte{0x35} // prefix for each key for an redelegation, by source validator operator + RedelegationByValDstIndexKey = []byte{0x36} // prefix for each key for an redelegation, by destination validator operator + + UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue + RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue + ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue ) const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch @@ -46,9 +52,9 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { return append(ValidatorsByConsAddrKey, addr.Bytes()...) } -// Get the validator operator address from ValBondedIndexKey -func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte { - return IndexKey[1:] // remove prefix bytes +// Get the validator operator address from LastValidatorPowerKey +func AddressFromLastValidatorPowerKey(key []byte) []byte { + return key[1:] // remove prefix bytes } // get the validator by power index. @@ -61,8 +67,8 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) [] } // get the bonded validator index key for an operator address -func GetValidatorsBondedIndexKey(operator sdk.ValAddress) []byte { - return append(ValidatorsBondedIndexKey, operator...) +func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { + return append(LastValidatorPowerKey, operator...) } // get the power ranking of a validator diff --git a/x/stake/keeper/key_test.go b/x/stake/keeper/key_test.go index dfd6a44e7990..385f313c15b5 100644 --- a/x/stake/keeper/key_test.go +++ b/x/stake/keeper/key_test.go @@ -35,10 +35,10 @@ func TestGetValidatorPowerRank(t *testing.T) { validator types.Validator wantHex string }{ - {val1, "050000000000000000ffffffffffffffffffff"}, - {val2, "050000000000000001ffffffffffffffffffff"}, - {val3, "05000000000000000affffffffffffffffffff"}, - {val4, "050000010000000000ffffffffffffffffffff"}, + {val1, "230000000000000000ffffffffffffffffffff"}, + {val2, "230000000000000001ffffffffffffffffffff"}, + {val3, "23000000000000000affffffffffffffffffff"}, + {val4, "230000010000000000ffffffffffffffffffff"}, } for i, tt := range tests { got := hex.EncodeToString(getValidatorPowerRank(tt.validator)) @@ -55,11 +55,11 @@ func TestGetREDByValDstIndexKey(t *testing.T) { wantHex string }{ {sdk.AccAddress(addr1), sdk.ValAddress(addr1), sdk.ValAddress(addr1), - "0c63d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, + "3663d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, {sdk.AccAddress(addr1), sdk.ValAddress(addr2), sdk.ValAddress(addr3), - "0c3ab62f0d93849be495e21e3e9013a517038f45bd63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f2"}, + "363ab62f0d93849be495e21e3e9013a517038f45bd63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f2"}, {sdk.AccAddress(addr2), sdk.ValAddress(addr1), sdk.ValAddress(addr3), - "0c3ab62f0d93849be495e21e3e9013a517038f45bd5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f08609"}, + "363ab62f0d93849be495e21e3e9013a517038f45bd5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f08609"}, } for i, tt := range tests { got := hex.EncodeToString(GetREDByValDstIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) @@ -76,11 +76,11 @@ func TestGetREDByValSrcIndexKey(t *testing.T) { wantHex string }{ {sdk.AccAddress(addr1), sdk.ValAddress(addr1), sdk.ValAddress(addr1), - "0b63d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, + "3563d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, {sdk.AccAddress(addr1), sdk.ValAddress(addr2), sdk.ValAddress(addr3), - "0b5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f086093ab62f0d93849be495e21e3e9013a517038f45bd"}, + "355ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f086093ab62f0d93849be495e21e3e9013a517038f45bd"}, {sdk.AccAddress(addr2), sdk.ValAddress(addr1), sdk.ValAddress(addr3), - "0b63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f23ab62f0d93849be495e21e3e9013a517038f45bd"}, + "3563d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f23ab62f0d93849be495e21e3e9013a517038f45bd"}, } for i, tt := range tests { got := hex.EncodeToString(GetREDByValSrcIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 667284356657..976dcbe65ec7 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -30,10 +30,10 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato // iterate through the active validator set and perform the provided function func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) i := int64(0) for ; iterator.Valid(); iterator.Next() { - address := GetAddressFromValBondedIndexKey(iterator.Key()) + address := AddressFromLastValidatorPowerKey(iterator.Key()) validator, found := k.GetValidator(ctx, address) if !found { panic(fmt.Sprintf("validator record not found for address: %v\n", address)) diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 2c832a8ad2b7..59f8b6deb268 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -12,10 +12,12 @@ import ( ) // Apply and return accumulated updates to the bonded validator set. Also, -// * Updates the active bonded valset as keyed by GetValidatorsBondedIndexKey(). +// * Updates the active bonded valset as keyed by LastValidatorPowerKey(). // * Updates validator status' according to updated powers. // * Updates the fee pool bonded vs loose tokens. // * Updates relevant indices. +// It gets called once after genesis, another time maybe after genesis transactions, +// then once at every EndBlock. // // CONTRACT: Only validators with non-zero power or zero-power that were bonded // at the previous block height or were removed from the validator set entirely @@ -27,10 +29,10 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // Retrieve the last validator set. // This persistent set is updated later in this function. - // (see GetValidatorsBondedIndexKey()). + // (see LastValidatorPowerKey). last := k.retrieveLastValidatorSet(ctx) - // iterate over validators, highest power to lowest + // Iterate over validators, highest power to lowest. iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) count := 0 for ; iterator.Valid() && count < int(maxValidators); iterator.Next() { @@ -79,7 +81,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab delete(last, operatorBytes) // set the bonded validator index - store.Set(GetValidatorsBondedIndexKey(operator), newPowerBytes) + store.Set(GetLastValidatorPowerKey(operator), newPowerBytes) // keep count count++ @@ -104,7 +106,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab } // delete from the bonded validator index - store.Delete(GetValidatorsBondedIndexKey(operator)) + store.Delete(GetLastValidatorPowerKey(operator)) // update the validator set updates = append(updates, validator.ABCIValidatorUpdateZero()) @@ -247,7 +249,7 @@ type validatorsByAddr map[[sdk.AddrLen]byte][]byte func (k Keeper) retrieveLastValidatorSet(ctx sdk.Context) validatorsByAddr { last := make(validatorsByAddr) store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) for ; iterator.Valid(); iterator.Next() { var operator [sdk.AddrLen]byte copy(operator[:], iterator.Key()[1:]) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 95e5f0001283..9fd7434d313a 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -235,24 +235,24 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [ } // get the group of the bonded validators -func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) { +func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) // add the actual validator power sorted store maxValidators := k.MaxValidators(ctx) validators = make([]types.Validator, maxValidators) - iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) defer iterator.Close() i := 0 for ; iterator.Valid(); iterator.Next() { // sanity check - if i > int(maxValidators-1) { - panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated") + if i >= int(maxValidators) { + panic("more validators than maxValidators found") } - address := GetAddressFromValBondedIndexKey(iterator.Key()) + address := AddressFromLastValidatorPowerKey(iterator.Key()) validator := k.mustGetValidator(ctx, address) validators[i] = validator @@ -261,7 +261,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat return validators[:i] // trim } -// get the group of bonded validators sorted by power-rank +// get the current group of bonded validators sorted by power-rank func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) maxValidators := k.MaxValidators(ctx) diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 6f14ba7a5da3..7acf1cc02dae 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -49,7 +49,7 @@ func TestSetValidator(t *testing.T) { assert.True(ValEq(t, validator, resVal)) require.True(t, found) - resVals := keeper.GetValidatorsBonded(ctx) + resVals := keeper.GetLastValidators(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validator, resVals[0])) @@ -191,7 +191,7 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { require.False(t, found) } -// This function tests UpdateValidator, GetValidator, GetValidatorsBonded, RemoveValidator +// This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator func TestValidatorBasics(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) pool := keeper.GetPool(ctx) @@ -213,7 +213,7 @@ func TestValidatorBasics(t *testing.T) { // check the empty keeper first _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) - resVals := keeper.GetValidatorsBonded(ctx) + resVals := keeper.GetLastValidators(ctx) require.Zero(t, len(resVals)) resVals = keeper.GetValidators(ctx, 2) @@ -237,7 +237,7 @@ func TestValidatorBasics(t *testing.T) { require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) - resVals = keeper.GetValidatorsBonded(ctx) + resVals = keeper.GetLastValidators(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) assert.Equal(t, sdk.Bonded, validators[0].Status) @@ -255,7 +255,7 @@ func TestValidatorBasics(t *testing.T) { require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) - resVals = keeper.GetValidatorsBonded(ctx) + resVals = keeper.GetLastValidators(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) @@ -269,7 +269,7 @@ func TestValidatorBasics(t *testing.T) { require.True(t, found) assert.True(ValEq(t, validators[2], resVal)) - resVals = keeper.GetValidatorsBonded(ctx) + resVals = keeper.GetLastValidators(ctx) require.Equal(t, 3, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here assert.True(ValEq(t, validators[1], resVals[1])) diff --git a/x/stake/stake.go b/x/stake/stake.go index c4aa547022d8..c755e352aa47 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -39,12 +39,13 @@ var ( GetDelegationKey = keeper.GetDelegationKey GetDelegationsKey = keeper.GetDelegationsKey PoolKey = keeper.PoolKey + IntraTxCounterKey = keeper.IntraTxCounterKey + LastValidatorPowerKey = keeper.LastValidatorPowerKey + LastTotalPowerKey = keeper.LastTotalPowerKey ValidatorsKey = keeper.ValidatorsKey ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey - ValidatorsBondedIndexKey = keeper.ValidatorsBondedIndexKey ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey DelegationKey = keeper.DelegationKey - IntraTxCounterKey = keeper.IntraTxCounterKey GetUBDKey = keeper.GetUBDKey GetUBDByValIndexKey = keeper.GetUBDByValIndexKey GetUBDsKey = keeper.GetUBDsKey From 5416af8a7a6bf077ac284385a1cb4373c4aac8b0 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 21 Oct 2018 15:28:14 -0700 Subject: [PATCH 23/30] LastValidatorPower -> LastValidator --- x/stake/keeper/key.go | 12 ++++++------ x/stake/keeper/sdk_types.go | 4 ++-- x/stake/keeper/val_state_change.go | 10 +++++----- x/stake/keeper/validator.go | 4 ++-- x/stake/stake.go | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 6faf36b75074..97949735d051 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -19,8 +19,8 @@ var ( IntraTxCounterKey = []byte{0x02} // key for intra-block tx index // Last* values are const during a block. - LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators - LastTotalPowerKey = []byte{0x12} // prefix for each key to a validator index, for bonded validators + LastValidatorKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators + LastTotalPowerKey = []byte{0x12} // prefix for each key to a validator index, for bonded validators ValidatorsKey = []byte{0x21} // prefix for each key to a validator ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey @@ -52,8 +52,8 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { return append(ValidatorsByConsAddrKey, addr.Bytes()...) } -// Get the validator operator address from LastValidatorPowerKey -func AddressFromLastValidatorPowerKey(key []byte) []byte { +// Get the validator operator address from LastValidatorKey +func AddressFromLastValidatorKey(key []byte) []byte { return key[1:] // remove prefix bytes } @@ -67,8 +67,8 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) [] } // get the bonded validator index key for an operator address -func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { - return append(LastValidatorPowerKey, operator...) +func GetLastValidatorKey(operator sdk.ValAddress) []byte { + return append(LastValidatorKey, operator...) } // get the power ranking of a validator diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 976dcbe65ec7..c8bebb1ea6f1 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -30,10 +30,10 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato // iterate through the active validator set and perform the provided function func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorKey) i := int64(0) for ; iterator.Valid(); iterator.Next() { - address := AddressFromLastValidatorPowerKey(iterator.Key()) + address := AddressFromLastValidatorKey(iterator.Key()) validator, found := k.GetValidator(ctx, address) if !found { panic(fmt.Sprintf("validator record not found for address: %v\n", address)) diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 59f8b6deb268..0dea23ce198b 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -12,7 +12,7 @@ import ( ) // Apply and return accumulated updates to the bonded validator set. Also, -// * Updates the active bonded valset as keyed by LastValidatorPowerKey(). +// * Updates the active bonded valset as keyed by LastValidatorKey(). // * Updates validator status' according to updated powers. // * Updates the fee pool bonded vs loose tokens. // * Updates relevant indices. @@ -29,7 +29,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // Retrieve the last validator set. // This persistent set is updated later in this function. - // (see LastValidatorPowerKey). + // (see LastValidatorKey). last := k.retrieveLastValidatorSet(ctx) // Iterate over validators, highest power to lowest. @@ -81,7 +81,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab delete(last, operatorBytes) // set the bonded validator index - store.Set(GetLastValidatorPowerKey(operator), newPowerBytes) + store.Set(GetLastValidatorKey(operator), newPowerBytes) // keep count count++ @@ -106,7 +106,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab } // delete from the bonded validator index - store.Delete(GetLastValidatorPowerKey(operator)) + store.Delete(GetLastValidatorKey(operator)) // update the validator set updates = append(updates, validator.ABCIValidatorUpdateZero()) @@ -249,7 +249,7 @@ type validatorsByAddr map[[sdk.AddrLen]byte][]byte func (k Keeper) retrieveLastValidatorSet(ctx sdk.Context) validatorsByAddr { last := make(validatorsByAddr) store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorKey) for ; iterator.Valid(); iterator.Next() { var operator [sdk.AddrLen]byte copy(operator[:], iterator.Key()[1:]) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 9fd7434d313a..2d97b5c37cbc 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -242,7 +242,7 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator maxValidators := k.MaxValidators(ctx) validators = make([]types.Validator, maxValidators) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorKey) defer iterator.Close() i := 0 @@ -252,7 +252,7 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator if i >= int(maxValidators) { panic("more validators than maxValidators found") } - address := AddressFromLastValidatorPowerKey(iterator.Key()) + address := AddressFromLastValidatorKey(iterator.Key()) validator := k.mustGetValidator(ctx, address) validators[i] = validator diff --git a/x/stake/stake.go b/x/stake/stake.go index c755e352aa47..d1e9b3a0620a 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -40,7 +40,7 @@ var ( GetDelegationsKey = keeper.GetDelegationsKey PoolKey = keeper.PoolKey IntraTxCounterKey = keeper.IntraTxCounterKey - LastValidatorPowerKey = keeper.LastValidatorPowerKey + LastValidatorKey = keeper.LastValidatorKey LastTotalPowerKey = keeper.LastTotalPowerKey ValidatorsKey = keeper.ValidatorsKey ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey From 1cc74320df5eda38c09054362928f7c29bf941a1 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 22 Oct 2018 01:18:10 -0700 Subject: [PATCH 24/30] Replicate Rigel's changes but w/ modifications as discussed + some name changes --- cmd/gaia/app/app.go | 1 + types/decimal.go | 5 ++ types/stake.go | 6 +- x/distribution/keeper/delegation.go | 32 +++++----- x/distribution/keeper/delegation_test.go | 7 ++- x/distribution/keeper/keeper_test.go | 4 +- x/distribution/keeper/test_common.go | 1 - x/distribution/keeper/validator.go | 7 ++- x/distribution/types/delegator_info.go | 8 ++- x/distribution/types/delegator_info_test.go | 4 +- x/distribution/types/fee_pool.go | 7 ++- x/distribution/types/fee_pool_test.go | 4 +- x/distribution/types/keepers.go | 2 + x/distribution/types/validator_info.go | 24 +++++--- x/distribution/types/validator_info_test.go | 10 ++-- x/gov/tally.go | 2 +- x/slashing/keeper.go | 10 +++- x/slashing/slashing_period.go | 1 + x/stake/genesis.go | 1 - x/stake/keeper/keeper.go | 66 ++++++++++++++++++--- x/stake/keeper/key.go | 12 ++-- x/stake/keeper/sdk_types.go | 6 +- x/stake/keeper/slash.go | 5 +- x/stake/keeper/test_common.go | 1 - x/stake/keeper/val_state_change.go | 30 ++++++---- x/stake/keeper/validator.go | 4 +- x/stake/stake.go | 2 +- x/stake/types/delegation.go | 6 +- x/stake/types/validator.go | 6 -- 29 files changed, 182 insertions(+), 92 deletions(-) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 6c02b7aaa6b6..7febefb5b782 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -344,6 +344,7 @@ func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) { h.sh.OnValidatorBonded(ctx, addr) } func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) { + h.dh.OnValidatorBeginUnbonding(ctx, addr, operator) h.sh.OnValidatorBeginUnbonding(ctx, addr, operator) } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { diff --git a/types/decimal.go b/types/decimal.go index e9623995f008..05dd97795a48 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -247,6 +247,11 @@ func (d Dec) QuoInt(i Int) Dec { return Dec{mul} } +// is integer, e.g. decimals are zero. +func (d Dec) IsInteger() bool { + return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 +} + func (d Dec) String() string { str := d.ToLeftPaddedWithDecimals(Precision) placement := len(str) - Precision diff --git a/types/stake.go b/types/stake.go index b6e48bde7fd7..46017746505d 100644 --- a/types/stake.go +++ b/types/stake.go @@ -85,9 +85,9 @@ type ValidatorSet interface { // delegation bond for a delegated proof of stake system type Delegation interface { - GetDelegator() AccAddress // delegator AccAddress for the bond - GetValidator() ValAddress // validator operator address - GetShares() Dec // amount of validator's shares held in this delegation + GetDelegatorAddr() AccAddress // delegator AccAddress for the bond + GetValidatorAddr() ValAddress // validator operator address + GetShares() Dec // amount of validator's shares held in this delegation } // properties for the set of all delegations for a particular diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 4132976ef640..784a75c7973a 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -69,25 +69,28 @@ func (k Keeper) RemoveDelegatorWithdrawAddr(ctx sdk.Context, delAddr, withdrawAd //___________________________________________________________________________________________ -// withdraw all the rewards for a single delegation +// Withdraw all the rewards for a single delegation. +// NOTE: This gets called "onDelegationSharesModified", +// meaning any changes to bonded coins. func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccAddress, - validatorAddr sdk.ValAddress) sdk.Error { + valAddr sdk.ValAddress) sdk.Error { - if !k.HasDelegationDistInfo(ctx, delegatorAddr, validatorAddr) { + if !k.HasDelegationDistInfo(ctx, delegatorAddr, valAddr) { return types.ErrNoDelegationDistInfo(k.codespace) } // TODO: Reconcile with duplicate code in getDelegatorRewardsAll. height := ctx.BlockHeight() - bondedTokens := k.stakeKeeper.TotalPower(ctx) + lastTotalPower := k.stakeKeeper.GetLastTotalPower(ctx) + lastValPower := k.stakeKeeper.GetLastValidatorPower(ctx, valAddr) feePool := k.GetFeePool(ctx) - delInfo := k.GetDelegationDistInfo(ctx, delegatorAddr, validatorAddr) - valInfo := k.GetValidatorDistInfo(ctx, validatorAddr) - validator := k.stakeKeeper.Validator(ctx, validatorAddr) - delegation := k.stakeKeeper.Delegation(ctx, delegatorAddr, validatorAddr) + delInfo := k.GetDelegationDistInfo(ctx, delegatorAddr, valAddr) + valInfo := k.GetValidatorDistInfo(ctx, valAddr) + validator := k.stakeKeeper.Validator(ctx, valAddr) + delegation := k.stakeKeeper.Delegation(ctx, delegatorAddr, valAddr) - delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, - validator.GetPower(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) + delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, lastTotalPower, + lastValPower, validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) k.SetValidatorDistInfo(ctx, valInfo) k.SetDelegationDistInfo(ctx, delInfo) @@ -123,20 +126,21 @@ func (k Keeper) WithdrawDelegationRewardsAll(ctx sdk.Context, delegatorAddr sdk. func (k Keeper) getDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, height int64) types.DecCoins { withdraw := types.DecCoins{} - bondedTokens := k.stakeKeeper.TotalPower(ctx) + lastTotalPower := k.stakeKeeper.GetLastTotalPower(ctx) // iterate over all the delegations // TODO: Reconcile with duplicate code in WithdrawDelegationReward. operationAtDelegation := func(_ int64, del sdk.Delegation) (stop bool) { feePool := k.GetFeePool(ctx) - valAddr := del.GetValidator() + valAddr := del.GetValidatorAddr() + lastValPower := k.stakeKeeper.GetLastValidatorPower(ctx, valAddr) delInfo := k.GetDelegationDistInfo(ctx, delAddr, valAddr) valInfo := k.GetValidatorDistInfo(ctx, valAddr) validator := k.stakeKeeper.Validator(ctx, valAddr) delegation := k.stakeKeeper.Delegation(ctx, delAddr, valAddr) - delInfo, valInfo, feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens, - validator.GetPower(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) + delInfo, valInfo, feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, lastTotalPower, + lastValPower, validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission()) withdraw = withdraw.Plus(diWithdraw) k.SetFeePool(ctx, feePool) k.SetValidatorDistInfo(ctx, valInfo) diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 3455d48c813a..8060fcdf2d14 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -34,6 +34,8 @@ func TestWithdrawDelegationRewardBasic(t *testing.T) { // withdraw delegation ctx = ctx.WithBlockHeight(1) + sk.SetLastTotalPower(ctx, sdk.NewDec(10)) + sk.SetLastValidatorPower(ctx, valOpAddr1, sdk.NewDec(10)) keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1) amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom) @@ -204,8 +206,6 @@ func TestWithdrawDelegationRewardsAll(t *testing.T) { got = stakeHandler(ctx, msgCreateValidator) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) - _ = sk.ApplyAndReturnValidatorSetUpdates(ctx) - // delegate to all the validators msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10) require.True(t, stakeHandler(ctx, msgDelegate).IsOK()) @@ -214,6 +214,9 @@ func TestWithdrawDelegationRewardsAll(t *testing.T) { msgDelegate = stake.NewTestMsgDelegate(delAddr1, valOpAddr3, 30) require.True(t, stakeHandler(ctx, msgDelegate).IsOK()) + // Update sk's LastValidatorPower/LastTotalPowers. + _ = sk.ApplyAndReturnValidatorSetUpdates(ctx) + // 40 tokens left after delegating 60 of them amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom) require.Equal(t, int64(40), amt.Int64()) diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index 824430511387..f8eb0925d538 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -29,9 +29,9 @@ func TestSetGetFeePool(t *testing.T) { ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 0) fp := types.InitialFeePool() - fp.ValAccum.UpdateHeight = 777 + fp.TotalValAccum.UpdateHeight = 777 keeper.SetFeePool(ctx, fp) res := keeper.GetFeePool(ctx) - require.Equal(t, fp.ValAccum, res.ValAccum) + require.Equal(t, fp.TotalValAccum, res.TotalValAccum) } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 59b615ec8a1a..42f3861fbe5a 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -114,7 +114,6 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64, sk := stake.NewKeeper(cdc, keyStake, tkeyStake, ck, pk.Subspace(stake.DefaultParamspace), stake.DefaultCodespace) sk.SetPool(ctx, stake.InitialPool()) sk.SetParams(ctx, stake.DefaultParams()) - sk.InitIntraTxCounter(ctx) // fill all the addresses with some coins, set the loose pool tokens simultaneously for _, addr := range addrs { diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index 0d3453387465..d8ec7ea7fdd2 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -50,15 +50,16 @@ func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.Va // withdraw self-delegation height := ctx.BlockHeight() validator := k.stakeKeeper.Validator(ctx, operatorAddr) + lastValPower := k.stakeKeeper.GetLastValidatorPower(ctx, operatorAddr) accAddr := sdk.AccAddress(operatorAddr.Bytes()) withdraw := k.getDelegatorRewardsAll(ctx, accAddr, height) // withdrawal validator commission rewards - bondedTokens := k.stakeKeeper.TotalPower(ctx) + lastTotalPower := k.stakeKeeper.GetLastTotalPower(ctx) valInfo := k.GetValidatorDistInfo(ctx, operatorAddr) feePool := k.GetFeePool(ctx) - valInfo, feePool, commission := valInfo.WithdrawCommission(feePool, height, bondedTokens, - validator.GetPower(), validator.GetCommission()) + valInfo, feePool, commission := valInfo.WithdrawCommission(feePool, height, lastTotalPower, + lastValPower, validator.GetCommission()) withdraw = withdraw.Plus(commission) k.SetValidatorDistInfo(ctx, valInfo) diff --git a/x/distribution/types/delegator_info.go b/x/distribution/types/delegator_info.go index f5b8978e5f24..4667d66e0009 100644 --- a/x/distribution/types/delegator_info.go +++ b/x/distribution/types/delegator_info.go @@ -21,7 +21,13 @@ func NewDelegationDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.Val } } -// withdraw rewards from delegator +// Withdraw rewards from delegator. +// Among many things, it does: +// * updates validator info's total del accum. +// * calls vi.TakeFeePoolRewards, which: +// * updates validator info's FeePoolWithdrawalHeight, thus setting accum to 0. +// * updates fee pool to latest height and total val accum w/ given totalBonded. +// (see comment on TakeFeePoolRewards for more info). func (di DelegationDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo, height int64, totalBonded, vdTokens, totalDelShares, delegatorShares, commissionRate sdk.Dec) (DelegationDistInfo, ValidatorDistInfo, FeePool, DecCoins) { diff --git a/x/distribution/types/delegator_info_test.go b/x/distribution/types/delegator_info_test.go index 516cbf99dfa1..4af7f0a8f122 100644 --- a/x/distribution/types/delegator_info_test.go +++ b/x/distribution/types/delegator_info_test.go @@ -33,7 +33,7 @@ func TestWithdrawRewards(t *testing.T) { validatorTokens, validatorDelShares, di1Shares, commissionRate) assert.Equal(t, height, di1.WithdrawalHeight) - assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum)) + assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount)) @@ -48,7 +48,7 @@ func TestWithdrawRewards(t *testing.T) { validatorTokens, validatorDelShares, di2Shares, commissionRate) assert.Equal(t, height, di2.WithdrawalHeight) - assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValAccum.Accum)) + assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(49), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(4), vi.PoolCommission[0].Amount)) diff --git a/x/distribution/types/fee_pool.go b/x/distribution/types/fee_pool.go index 66731cb197a2..ae1d72cc0117 100644 --- a/x/distribution/types/fee_pool.go +++ b/x/distribution/types/fee_pool.go @@ -33,21 +33,22 @@ func (ta TotalAccum) UpdateForNewHeight(height int64, accumCreatedPerBlock sdk.D // global fee pool for distribution type FeePool struct { - ValAccum TotalAccum `json:"val_accum"` // total valdator accum held by validators + TotalValAccum TotalAccum `json:"val_accum"` // total valdator accum held by validators Pool DecCoins `json:"pool"` // funds for all validators which have yet to be withdrawn CommunityPool DecCoins `json:"community_pool"` // pool for community funds yet to be spent } // update total validator accumulation factor +// NOTE: Do not call this except from ValidatorDistInfo.TakeFeePoolRewards(). func (f FeePool) UpdateTotalValAccum(height int64, totalBondedTokens sdk.Dec) FeePool { - f.ValAccum = f.ValAccum.UpdateForNewHeight(height, totalBondedTokens) + f.TotalValAccum = f.TotalValAccum.UpdateForNewHeight(height, totalBondedTokens) return f } // zero fee pool func InitialFeePool() FeePool { return FeePool{ - ValAccum: NewTotalAccum(0), + TotalValAccum: NewTotalAccum(0), Pool: DecCoins{}, CommunityPool: DecCoins{}, } diff --git a/x/distribution/types/fee_pool_test.go b/x/distribution/types/fee_pool_test.go index e39fb09c94c1..478ec7539baa 100644 --- a/x/distribution/types/fee_pool_test.go +++ b/x/distribution/types/fee_pool_test.go @@ -23,8 +23,8 @@ func TestUpdateTotalValAccum(t *testing.T) { fp := InitialFeePool() fp = fp.UpdateTotalValAccum(5, sdk.NewDec(3)) - require.True(sdk.DecEq(t, sdk.NewDec(15), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(15), fp.TotalValAccum.Accum)) fp = fp.UpdateTotalValAccum(8, sdk.NewDec(2)) - require.True(sdk.DecEq(t, sdk.NewDec(21), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(21), fp.TotalValAccum.Accum)) } diff --git a/x/distribution/types/keepers.go b/x/distribution/types/keepers.go index 31a68a5da9a6..5009c6b5af2b 100644 --- a/x/distribution/types/keepers.go +++ b/x/distribution/types/keepers.go @@ -10,6 +10,8 @@ type StakeKeeper interface { Validator(ctx sdk.Context, valAddr sdk.ValAddress) sdk.Validator ValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) sdk.Validator TotalPower(ctx sdk.Context) sdk.Dec + GetLastTotalPower(ctx sdk.Context) sdk.Dec + GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) sdk.Dec } // expected coin keeper diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go index ae80d8aa71cc..1744e3baac46 100644 --- a/x/distribution/types/validator_info.go +++ b/x/distribution/types/validator_info.go @@ -19,9 +19,9 @@ func NewValidatorDistInfo(operatorAddr sdk.ValAddress, currentHeight int64) Vali return ValidatorDistInfo{ OperatorAddr: operatorAddr, FeePoolWithdrawalHeight: currentHeight, - Pool: DecCoins{}, - PoolCommission: DecCoins{}, - DelAccum: NewTotalAccum(currentHeight), + Pool: DecCoins{}, + PoolCommission: DecCoins{}, + DelAccum: NewTotalAccum(currentHeight), } } @@ -31,13 +31,21 @@ func (vi ValidatorDistInfo) UpdateTotalDelAccum(height int64, totalDelShares sdk return vi } -// move any available accumulated fees in the FeePool to the validator's pool +// Move any available accumulated fees in the FeePool to the validator's pool. +// * updates validator info's FeePoolWithdrawalHeight, thus setting accum to 0. +// * updates fee pool to latest height and total val accum w/ given totalBonded. +// This is the only way to update the FeePool's validator TotalAccum. +// NOTE: This algorithm works as long as TakeFeePoolRewards is called after every power change. +// - called in ValidationDistInfo.WithdrawCommission. +// - called in DelegationDistInfo.WithdrawRewards. +// NOTE: When a delegator unbonds, say, onDelegationSharesModified -> +// WithdrawDelegationReward -> WithdrawRewards. func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBonded, vdTokens, commissionRate sdk.Dec) (ValidatorDistInfo, FeePool) { fp = fp.UpdateTotalValAccum(height, totalBonded) - if fp.ValAccum.Accum.IsZero() { + if fp.TotalValAccum.Accum.IsZero() { return vi, fp } @@ -45,16 +53,16 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo blocks := height - vi.FeePoolWithdrawalHeight vi.FeePoolWithdrawalHeight = height accum := vdTokens.MulInt(sdk.NewInt(blocks)) - if accum.GT(fp.ValAccum.Accum) { + if accum.GT(fp.TotalValAccum.Accum) { panic("individual accum should never be greater than the total") } - withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.ValAccum.Accum) + withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum) remainingTokens := fp.Pool.Minus(withdrawalTokens) commission := withdrawalTokens.MulDec(commissionRate) afterCommission := withdrawalTokens.Minus(commission) - fp.ValAccum.Accum = fp.ValAccum.Accum.Sub(accum) + fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum) fp.Pool = remainingTokens vi.PoolCommission = vi.PoolCommission.Plus(commission) vi.Pool = vi.Pool.Plus(afterCommission) diff --git a/x/distribution/types/validator_info_test.go b/x/distribution/types/validator_info_test.go index afa6d8c76363..9d1e39fa6a06 100644 --- a/x/distribution/types/validator_info_test.go +++ b/x/distribution/types/validator_info_test.go @@ -29,13 +29,13 @@ func TestTakeFeePoolRewards(t *testing.T) { fp.Pool = DecCoins{NewDecCoin("stake", 1000)} vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1) - require.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.PoolCommission[0].Amount)) vi2, fp = vi2.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens2, commissionRate2) - require.True(sdk.DecEq(t, sdk.NewDec(500), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(500), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(400-12), vi2.Pool[0].Amount)) assert.True(sdk.DecEq(t, vi2.PoolCommission[0].Amount, sdk.NewDec(12))) @@ -45,7 +45,7 @@ func TestTakeFeePoolRewards(t *testing.T) { fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000)) vi3, fp = vi3.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens3, commissionRate3) - require.True(sdk.DecEq(t, sdk.NewDec(500), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(500), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(1000-40), vi3.Pool[0].Amount)) assert.True(sdk.DecEq(t, vi3.PoolCommission[0].Amount, sdk.NewDec(40))) @@ -67,7 +67,7 @@ func TestWithdrawCommission(t *testing.T) { // for a more fun staring condition, have an non-withdraw update vi, fp = vi.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens, commissionRate) - require.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(900), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount)) @@ -77,7 +77,7 @@ func TestWithdrawCommission(t *testing.T) { fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000)) vi, fp, commissionRecv := vi.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens, commissionRate) - require.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValAccum.Accum)) + require.True(sdk.DecEq(t, sdk.NewDec(1800), fp.TotalValAccum.Accum)) assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount)) assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi.Pool[0].Amount)) assert.Zero(t, len(vi.PoolCommission)) diff --git a/x/gov/tally.go b/x/gov/tally.go index c5751258a00a..b6e42c4b5a02 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -50,7 +50,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall } else { keeper.ds.IterateDelegations(ctx, vote.Voter, func(index int64, delegation sdk.Delegation) (stop bool) { - valAddrStr := delegation.GetValidator().String() + valAddrStr := delegation.GetValidatorAddr().String() if val, ok := currValidators[valAddrStr]; ok { val.Minus = val.Minus.Add(delegation.GetShares()) diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 861391ac2a5f..4cd8ed91f397 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -37,7 +37,8 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, paramspa return keeper } -// handle a validator signing two blocks at the same height +// handle a validator signing two blocks at the same height. +// power: power of the double-signing validator at the height of infraction. func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractionHeight int64, timestamp time.Time, power int64) { logger := ctx.Logger().With("module", "x/slashing") time := ctx.BlockHeader().Time @@ -70,7 +71,12 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio revisedFraction := k.capBySlashingPeriod(ctx, consAddr, fraction, distributionHeight) logger.Info(fmt.Sprintf("Fraction slashed capped by slashing period from %v to %v", fraction, revisedFraction)) - // Slash validator + // Slash validator. + // `power` is the int64 power of the validator as provided to/by + // Tendermint. This value is validator.Tokens as sent to Tendermint via + // ABCI, and now received as evidence. + // The revisedFraction (which is the new fraction to be slashed) is passed + // in separately to separately slash unbonding and rebonding delegations. k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, revisedFraction) // Jail validator if not already jailed diff --git a/x/slashing/slashing_period.go b/x/slashing/slashing_period.go index 0595d5eeb25c..4b1328858882 100644 --- a/x/slashing/slashing_period.go +++ b/x/slashing/slashing_period.go @@ -37,6 +37,7 @@ func (k Keeper) capBySlashingPeriod(ctx sdk.Context, address sdk.ConsAddress, fr // This function retrieves the most recent slashing period starting // before a particular height - so the slashing period that was "in effect" // at the time of an infraction committed at that height. +// Slashing periods are created upon validator bonding. func (k Keeper) getValidatorSlashingPeriodForHeight(ctx sdk.Context, address sdk.ConsAddress, height int64) (slashingPeriod ValidatorSlashingPeriod) { store := ctx.KVStore(k.storeKey) // Get the most recent slashing period at or before the infraction height diff --git a/x/stake/genesis.go b/x/stake/genesis.go index ff8f59d44310..2fed877414ab 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -26,7 +26,6 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.SetPool(ctx, data.Pool) keeper.SetParams(ctx, data.Params) - keeper.InitIntraTxCounter(ctx) for i, validator := range data.Validators { validator.BondIntraTxCounter = int16(i) // set the intra-tx counter to the order the validators are presented diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 20927869866d..26c686bc9050 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -53,12 +53,12 @@ func (k Keeper) Codespace() sdk.CodespaceType { //_______________________________________________________________________ -// load/save the pool +// load the pool func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) { store := ctx.KVStore(k.storeKey) b := store.Get(PoolKey) if b == nil { - panic("Stored pool should not have been nil") + panic("stored pool should not have been nil") } k.cdc.MustUnmarshalBinary(b, &pool) return @@ -71,21 +71,73 @@ func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) { store.Set(PoolKey, b) } -//__________________________________________________________________________ +//_______________________________________________________________________ -// get the current in-block validator operation counter -func (k Keeper) InitIntraTxCounter(ctx sdk.Context) { +// Load the last total validator power. +func (k Keeper) GetLastTotalPower(ctx sdk.Context) (power sdk.Dec) { store := ctx.KVStore(k.storeKey) - b := store.Get(IntraTxCounterKey) + b := store.Get(LastTotalPowerKey) if b == nil { - k.SetIntraTxCounter(ctx, 0) + panic("stored last total power should not have been nil") } + k.cdc.MustUnmarshalBinary(b, &power) + return } +// Set the last total validator power. +func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Dec) { + if !power.IsInteger() { + panic("input power must be whole integer") + } + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinary(power) + store.Set(LastTotalPowerKey, b) +} + +//_______________________________________________________________________ + +// Load the last validator power. +// Returns zero if the operator was not a validator last block. +func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power sdk.Dec) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(GetLastValidatorPowerKey(operator)) + if bz == nil { + return sdk.ZeroDec() + } + k.cdc.MustUnmarshalBinary(bz, &power) + return +} + +func (k Keeper) powerToBytes(power sdk.Dec) []byte { + bz := k.cdc.MustMarshalBinary(power) + return bz +} + +// Set the last validator power. +func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Dec) { + if !power.IsInteger() { + panic("input power must be whole integer") + } + store := ctx.KVStore(k.storeKey) + bz := k.powerToBytes(power) + store.Set(GetLastValidatorPowerKey(operator), bz) +} + +// Delete the last validator power. +func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetLastValidatorPowerKey(operator)) +} + +//__________________________________________________________________________ + // get the current in-block validator operation counter func (k Keeper) GetIntraTxCounter(ctx sdk.Context) int16 { store := ctx.KVStore(k.storeKey) b := store.Get(IntraTxCounterKey) + if b == nil { + return 0 + } var counter int16 k.cdc.MustUnmarshalBinary(b, &counter) return counter diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 97949735d051..243fe34ec57c 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -19,8 +19,8 @@ var ( IntraTxCounterKey = []byte{0x02} // key for intra-block tx index // Last* values are const during a block. - LastValidatorKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators - LastTotalPowerKey = []byte{0x12} // prefix for each key to a validator index, for bonded validators + LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators + LastTotalPowerKey = []byte{0x12} // prefix for the total power ValidatorsKey = []byte{0x21} // prefix for each key to a validator ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey @@ -52,8 +52,8 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { return append(ValidatorsByConsAddrKey, addr.Bytes()...) } -// Get the validator operator address from LastValidatorKey -func AddressFromLastValidatorKey(key []byte) []byte { +// Get the validator operator address from LastValidatorPowerKey +func AddressFromLastValidatorPowerKey(key []byte) []byte { return key[1:] // remove prefix bytes } @@ -67,8 +67,8 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) [] } // get the bonded validator index key for an operator address -func GetLastValidatorKey(operator sdk.ValAddress) []byte { - return append(LastValidatorKey, operator...) +func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { + return append(LastValidatorPowerKey, operator...) } // get the power ranking of a validator diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index c8bebb1ea6f1..4e859a42a887 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -30,10 +30,10 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato // iterate through the active validator set and perform the provided function func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) i := int64(0) for ; iterator.Valid(); iterator.Next() { - address := AddressFromLastValidatorKey(iterator.Key()) + address := AddressFromLastValidatorPowerKey(iterator.Key()) validator, found := k.GetValidator(ctx, address) if !found { panic(fmt.Sprintf("validator record not found for address: %v\n", address)) @@ -66,7 +66,7 @@ func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) sdk.V return val } -// total power from the bond +// total power from the bond (not last, but current) func (k Keeper) TotalPower(ctx sdk.Context) sdk.Dec { pool := k.GetPool(ctx) return pool.BondedTokens diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index af59726aefe5..66f383179b7c 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -97,10 +97,13 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh // cannot decrease balance below zero tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens) + tokensToBurn = sdk.MaxDec(tokensToBurn, sdk.ZeroDec()) // defensive. - // burn validator's tokens and update the validator + // Deduct from validator's bonded tokens and update the validator. + // The deducted tokens are returned to pool.LooseTokens. validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn) pool := k.GetPool(ctx) + // Burn the slashed tokens, which are now loose. pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) k.SetPool(ctx, pool) diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index d0ac4a2825c0..13e2ca2c595e 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -106,7 +106,6 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, pk.Subspace(DefaultParamspace), types.DefaultCodespace) keeper.SetPool(ctx, types.InitialPool()) keeper.SetParams(ctx, types.DefaultParams()) - keeper.InitIntraTxCounter(ctx) // fill all the addresses with some coins, set the loose pool tokens simultaneously for _, addr := range Addrs { diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 0dea23ce198b..768b4c854fd2 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -12,7 +12,8 @@ import ( ) // Apply and return accumulated updates to the bonded validator set. Also, -// * Updates the active bonded valset as keyed by LastValidatorKey(). +// * Updates the active valset as keyed by LastValidatorPowerKey. +// * Updates the total power as keyed by LastTotalPowerKey. // * Updates validator status' according to updated powers. // * Updates the fee pool bonded vs loose tokens. // * Updates relevant indices. @@ -26,11 +27,12 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab store := ctx.KVStore(k.storeKey) maxValidators := k.GetParams(ctx).MaxValidators + totalPower := int64(0) // Retrieve the last validator set. - // This persistent set is updated later in this function. - // (see LastValidatorKey). - last := k.retrieveLastValidatorSet(ctx) + // The persistent set is updated later in this function. + // (see LastValidatorPowerKey). + last := k.getLastValidatorsByAddr(ctx) // Iterate over validators, highest power to lowest. iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) @@ -70,8 +72,8 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab oldPowerBytes, found := last[operatorBytes] // calculate the new power bytes - newPowerBytes := validator.ABCIValidatorPowerBytes(k.cdc) - + newPower := validator.BondedTokens().RoundInt64() + newPowerBytes := k.powerToBytes(sdk.NewDec(newPower)) // update the validator set if power has changed if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) @@ -80,14 +82,18 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // validator still in the validator set, so delete from the copy delete(last, operatorBytes) - // set the bonded validator index - store.Set(GetLastValidatorKey(operator), newPowerBytes) + // set validator power on lookup index. + k.SetLastValidatorPower(ctx, operator, sdk.NewDec(newPower)) // keep count count++ + totalPower += newPower } + // set total power on lookup index. + k.SetLastTotalPower(ctx, sdk.NewDec(totalPower)) + // sort the no-longer-bonded validators noLongerBonded := k.sortNoLongerBonded(last) @@ -106,7 +112,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab } // delete from the bonded validator index - store.Delete(GetLastValidatorKey(operator)) + k.DeleteLastValidatorPower(ctx, operator) // update the validator set updates = append(updates, validator.ABCIValidatorUpdateZero()) @@ -245,11 +251,11 @@ func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Vali // map of operator addresses to serialized power type validatorsByAddr map[[sdk.AddrLen]byte][]byte -// retrieve the last validator set -func (k Keeper) retrieveLastValidatorSet(ctx sdk.Context) validatorsByAddr { +// get the last validator set +func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr { last := make(validatorsByAddr) store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) for ; iterator.Valid(); iterator.Next() { var operator [sdk.AddrLen]byte copy(operator[:], iterator.Key()[1:]) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 2d97b5c37cbc..9fd7434d313a 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -242,7 +242,7 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator maxValidators := k.MaxValidators(ctx) validators = make([]types.Validator, maxValidators) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) defer iterator.Close() i := 0 @@ -252,7 +252,7 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator if i >= int(maxValidators) { panic("more validators than maxValidators found") } - address := AddressFromLastValidatorKey(iterator.Key()) + address := AddressFromLastValidatorPowerKey(iterator.Key()) validator := k.mustGetValidator(ctx, address) validators[i] = validator diff --git a/x/stake/stake.go b/x/stake/stake.go index d1e9b3a0620a..c755e352aa47 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -40,7 +40,7 @@ var ( GetDelegationsKey = keeper.GetDelegationsKey PoolKey = keeper.PoolKey IntraTxCounterKey = keeper.IntraTxCounterKey - LastValidatorKey = keeper.LastValidatorKey + LastValidatorPowerKey = keeper.LastValidatorPowerKey LastTotalPowerKey = keeper.LastTotalPowerKey ValidatorsKey = keeper.ValidatorsKey ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index d389115217d0..628edd45f41a 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -104,9 +104,9 @@ func (d Delegation) Equal(d2 Delegation) bool { var _ sdk.Delegation = Delegation{} // nolint - for sdk.Delegation -func (d Delegation) GetDelegator() sdk.AccAddress { return d.DelegatorAddr } -func (d Delegation) GetValidator() sdk.ValAddress { return d.ValidatorAddr } -func (d Delegation) GetShares() sdk.Dec { return d.Shares } +func (d Delegation) GetDelegatorAddr() sdk.AccAddress { return d.DelegatorAddr } +func (d Delegation) GetValidatorAddr() sdk.ValAddress { return d.ValidatorAddr } +func (d Delegation) GetShares() sdk.Dec { return d.Shares } // HumanReadableString returns a human readable string representation of a // Delegation. An error is returned if the Delegation's delegator or validator diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index d774f761bd36..52d30f0a313b 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -314,12 +314,6 @@ func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { } } -// ABCIValidatorPowerBytes -func (v Validator) ABCIValidatorPowerBytes(cdc *codec.Codec) []byte { - power := v.BondedTokens().RoundInt64() - return cdc.MustMarshalBinary(power) -} - // ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staked validator type // with zero power used for validator updates. func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { From 19225fc5d478315db8fc44a37b1cb9bfd1991708 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 22 Oct 2018 02:42:40 -0700 Subject: [PATCH 25/30] Print debugging info --- x/distribution/types/fee_pool.go | 26 +++++++++++++++++++++++++- x/distribution/types/validator_info.go | 13 +++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/x/distribution/types/fee_pool.go b/x/distribution/types/fee_pool.go index ae1d72cc0117..759519c9e2e5 100644 --- a/x/distribution/types/fee_pool.go +++ b/x/distribution/types/fee_pool.go @@ -1,7 +1,10 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + cmn "github.com/tendermint/tendermint/libs/common" ) // total accumulation tracker @@ -29,6 +32,27 @@ func (ta TotalAccum) UpdateForNewHeight(height int64, accumCreatedPerBlock sdk.D return ta } +// update total validator accumulation factor for the new height +// CONTRACT: height should be greater than the old height +func (ta TotalAccum) UpdateForNewHeight_DEBUG(height int64, accumCreatedPerBlock sdk.Dec) TotalAccum { + blocks := height - ta.UpdateHeight + if blocks < 0 { + panic("reverse updated for new height") + } + fmt.Println( + cmn.Blue( + fmt.Sprintf("FP Add %v * %v = %v +=> %v", + accumCreatedPerBlock, sdk.NewInt(blocks), + accumCreatedPerBlock.MulInt(sdk.NewInt(blocks)), + ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))), + ), + ), + ) + ta.Accum = ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))) + ta.UpdateHeight = height + return ta +} + //___________________________________________________________________________________________ // global fee pool for distribution @@ -41,7 +65,7 @@ type FeePool struct { // update total validator accumulation factor // NOTE: Do not call this except from ValidatorDistInfo.TakeFeePoolRewards(). func (f FeePool) UpdateTotalValAccum(height int64, totalBondedTokens sdk.Dec) FeePool { - f.TotalValAccum = f.TotalValAccum.UpdateForNewHeight(height, totalBondedTokens) + f.TotalValAccum = f.TotalValAccum.UpdateForNewHeight_DEBUG(height, totalBondedTokens) return f } diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go index 1744e3baac46..e082c2ee8347 100644 --- a/x/distribution/types/validator_info.go +++ b/x/distribution/types/validator_info.go @@ -1,7 +1,10 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + cmn "github.com/tendermint/tendermint/libs/common" ) // distribution info for a particular validator @@ -62,6 +65,16 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo commission := withdrawalTokens.MulDec(commissionRate) afterCommission := withdrawalTokens.Minus(commission) + fmt.Println( + cmn.Red( + fmt.Sprintf("FP Sub %v * %v = %v -=> %v", + vdTokens, sdk.NewInt(blocks), + accum, + fp.TotalValAccum.Accum.Sub(accum), + ), + ), + ) + fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum) fp.Pool = remainingTokens vi.PoolCommission = vi.PoolCommission.Plus(commission) From 471ddeef3c24acdc8c790b24f68c1e96ff266ebe Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 22 Oct 2018 09:50:17 -0700 Subject: [PATCH 26/30] tweak debug output --- x/distribution/types/fee_pool.go | 19 +++++++++++-------- x/distribution/types/validator_info.go | 19 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/x/distribution/types/fee_pool.go b/x/distribution/types/fee_pool.go index 759519c9e2e5..1d045726a13a 100644 --- a/x/distribution/types/fee_pool.go +++ b/x/distribution/types/fee_pool.go @@ -39,15 +39,18 @@ func (ta TotalAccum) UpdateForNewHeight_DEBUG(height int64, accumCreatedPerBlock if blocks < 0 { panic("reverse updated for new height") } - fmt.Println( - cmn.Blue( - fmt.Sprintf("FP Add %v * %v = %v +=> %v", - accumCreatedPerBlock, sdk.NewInt(blocks), - accumCreatedPerBlock.MulInt(sdk.NewInt(blocks)), - ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))), + if !accumCreatedPerBlock.IsZero() && blocks != 0 { + fmt.Println( + cmn.Blue( + fmt.Sprintf("FP Add %v * %v = %v, + %v (old) => %v (new)", + accumCreatedPerBlock, sdk.NewInt(blocks), + accumCreatedPerBlock.MulInt(sdk.NewInt(blocks)), + ta.Accum, + ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))), + ), ), - ), - ) + ) + } ta.Accum = ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))) ta.UpdateHeight = height return ta diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go index e082c2ee8347..01152219ec01 100644 --- a/x/distribution/types/validator_info.go +++ b/x/distribution/types/validator_info.go @@ -65,15 +65,18 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo commission := withdrawalTokens.MulDec(commissionRate) afterCommission := withdrawalTokens.Minus(commission) - fmt.Println( - cmn.Red( - fmt.Sprintf("FP Sub %v * %v = %v -=> %v", - vdTokens, sdk.NewInt(blocks), - accum, - fp.TotalValAccum.Accum.Sub(accum), + if !accum.IsZero() { + fmt.Println( + cmn.Red( + fmt.Sprintf("FP Sub %v * %v = %v, %v - _ => %v", + vdTokens, sdk.NewInt(blocks), + accum, + fp.TotalValAccum.Accum, + fp.TotalValAccum.Accum.Sub(accum), + ), ), - ), - ) + ) + } fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum) fp.Pool = remainingTokens From 278d23776bf8ddf0c1fd2ff73cff82e5caa0be86 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 22 Oct 2018 14:59:00 -0400 Subject: [PATCH 27/30] call hook on slashing --- cmd/gaia/app/app.go | 11 ++++++----- docs/spec/staking/hooks.md | 6 +++--- types/stake.go | 6 +++--- x/distribution/keeper/hooks.go | 8 ++++---- x/slashing/hooks.go | 2 +- x/stake/handler.go | 2 +- x/stake/keeper/hooks.go | 4 ++-- x/stake/keeper/slash.go | 1 + 8 files changed, 21 insertions(+), 19 deletions(-) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 6c02b7aaa6b6..5265db52bc75 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -3,6 +3,10 @@ package app import ( "encoding/json" "fmt" + "io" + "os" + "sort" + bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,9 +23,6 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" - "io" - "os" - "sort" ) const ( @@ -334,8 +335,8 @@ var _ sdk.StakingHooks = Hooks{} func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { h.dh.OnValidatorCreated(ctx, addr) } -func (h Hooks) OnValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) { - h.dh.OnValidatorCommissionChange(ctx, addr) +func (h Hooks) OnValidatorModified(ctx sdk.Context, addr sdk.ValAddress) { + h.dh.OnValidatorModified(ctx, addr) } func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { h.dh.OnValidatorRemoved(ctx, addr) diff --git a/docs/spec/staking/hooks.md b/docs/spec/staking/hooks.md index 60359b4312e9..7d24e32e6446 100644 --- a/docs/spec/staking/hooks.md +++ b/docs/spec/staking/hooks.md @@ -5,9 +5,9 @@ The staking module allow for the following hooks to be registered with staking e ``` golang // event hooks for staking validator object type StakingHooks interface { - OnValidatorCreated(ctx Context, address ValAddress) // called when a validator is created - OnValidatorCommissionChange(ctx Context, address ValAddress) // called when a validator's commission is modified - OnValidatorRemoved(ctx Context, address ValAddress) // called when a validator is deleted + OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created + OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes + OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding diff --git a/types/stake.go b/types/stake.go index b6e48bde7fd7..25b89799464e 100644 --- a/types/stake.go +++ b/types/stake.go @@ -111,9 +111,9 @@ type DelegationSet interface { // event hooks for staking validator object type StakingHooks interface { - OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created - OnValidatorCommissionChange(ctx Context, address ValAddress) // Must be called when a validator's commission is modified - OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted + OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created + OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes + OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted OnValidatorBonded(ctx Context, address ConsAddress) // Must be called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator begins unbonding diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 698e729657d0..aeb394f26e28 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -14,9 +14,9 @@ func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { vdi := types.ValidatorDistInfo{ OperatorAddr: addr, FeePoolWithdrawalHeight: height, - Pool: types.DecCoins{}, - PoolCommission: types.DecCoins{}, - DelAccum: types.NewTotalAccum(height), + Pool: types.DecCoins{}, + PoolCommission: types.DecCoins{}, + DelAccum: types.NewTotalAccum(height), } k.SetValidatorDistInfo(ctx, vdi) } @@ -80,7 +80,7 @@ func (k Keeper) Hooks() Hooks { return Hooks{k} } func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { h.k.onValidatorCreated(ctx, addr) } -func (h Hooks) OnValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) { +func (h Hooks) OnValidatorModified(ctx sdk.Context, addr sdk.ValAddress) { h.k.onValidatorModified(ctx, addr) } func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) { diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index d91c69579ebc..3ad08b864b8c 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -62,7 +62,7 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddres // nolint - unused hooks func (h Hooks) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {} -func (h Hooks) OnValidatorCommissionChange(_ sdk.Context, _ sdk.ValAddress) {} +func (h Hooks) OnValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} func (h Hooks) OnDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} diff --git a/x/stake/handler.go b/x/stake/handler.go index e251d2c32593..24cbee71750d 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -148,7 +148,7 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe return err.Result() } validator.Commission = commission - k.OnValidatorCommissionChange(ctx, msg.ValidatorAddr) + k.OnValidatorModified(ctx, msg.ValidatorAddr) } k.SetValidator(ctx, validator) diff --git a/x/stake/keeper/hooks.go b/x/stake/keeper/hooks.go index 060c84f5128c..14eedb6936fd 100644 --- a/x/stake/keeper/hooks.go +++ b/x/stake/keeper/hooks.go @@ -11,9 +11,9 @@ func (k Keeper) OnValidatorCreated(ctx sdk.Context, address sdk.ValAddress) { k.hooks.OnValidatorCreated(ctx, address) } } -func (k Keeper) OnValidatorCommissionChange(ctx sdk.Context, address sdk.ValAddress) { +func (k Keeper) OnValidatorModified(ctx sdk.Context, address sdk.ValAddress) { if k.hooks != nil { - k.hooks.OnValidatorCommissionChange(ctx, address) + k.hooks.OnValidatorModified(ctx, address) } } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index af59726aefe5..eafb3cca32d4 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -51,6 +51,7 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh } operatorAddress := validator.GetOperator() + k.OnValidatorModified(ctx, operatorAddress) // Track remaining slash amount for the validator // This will decrease when we slash unbondings and From a3447cd4804d78a781b06c23360ded576fec866f Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 22 Oct 2018 15:51:27 -0400 Subject: [PATCH 28/30] debug cleanup --- x/distribution/types/fee_pool.go | 8 ++++---- x/distribution/types/validator_info.go | 25 +++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/x/distribution/types/fee_pool.go b/x/distribution/types/fee_pool.go index 1d045726a13a..a792a888f2d9 100644 --- a/x/distribution/types/fee_pool.go +++ b/x/distribution/types/fee_pool.go @@ -43,10 +43,10 @@ func (ta TotalAccum) UpdateForNewHeight_DEBUG(height int64, accumCreatedPerBlock fmt.Println( cmn.Blue( fmt.Sprintf("FP Add %v * %v = %v, + %v (old) => %v (new)", - accumCreatedPerBlock, sdk.NewInt(blocks), - accumCreatedPerBlock.MulInt(sdk.NewInt(blocks)), - ta.Accum, - ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))), + accumCreatedPerBlock.String(), sdk.NewInt(blocks), + accumCreatedPerBlock.MulInt(sdk.NewInt(blocks)).String(), + ta.Accum.String(), + ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))).String(), ), ), ) diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go index 01152219ec01..df1062e5c4d2 100644 --- a/x/distribution/types/validator_info.go +++ b/x/distribution/types/validator_info.go @@ -56,28 +56,29 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo blocks := height - vi.FeePoolWithdrawalHeight vi.FeePoolWithdrawalHeight = height accum := vdTokens.MulInt(sdk.NewInt(blocks)) - if accum.GT(fp.TotalValAccum.Accum) { - panic("individual accum should never be greater than the total") - } - withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum) - remainingTokens := fp.Pool.Minus(withdrawalTokens) - - commission := withdrawalTokens.MulDec(commissionRate) - afterCommission := withdrawalTokens.Minus(commission) if !accum.IsZero() { fmt.Println( cmn.Red( fmt.Sprintf("FP Sub %v * %v = %v, %v - _ => %v", - vdTokens, sdk.NewInt(blocks), - accum, - fp.TotalValAccum.Accum, - fp.TotalValAccum.Accum.Sub(accum), + vdTokens.String(), sdk.NewInt(blocks), + accum.String(), + fp.TotalValAccum.Accum.String(), + fp.TotalValAccum.Accum.Sub(accum).String(), ), ), ) } + if accum.GT(fp.TotalValAccum.Accum) { + panic("individual accum should never be greater than the total") + } + withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.TotalValAccum.Accum) + remainingTokens := fp.Pool.Minus(withdrawalTokens) + + commission := withdrawalTokens.MulDec(commissionRate) + afterCommission := withdrawalTokens.Minus(commission) + fp.TotalValAccum.Accum = fp.TotalValAccum.Accum.Sub(accum) fp.Pool = remainingTokens vi.PoolCommission = vi.PoolCommission.Plus(commission) From bcc31920f1fce0ca8d754120a7a62382c48eaa37 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 22 Oct 2018 17:17:46 -0400 Subject: [PATCH 29/30] correct LastValidatorPower mistake --- x/stake/keeper/val_state_change.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 768b4c854fd2..cfa381973d30 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -77,23 +77,19 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // update the validator set if power has changed if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) + + // set validator power on lookup index. + k.SetLastValidatorPower(ctx, operator, sdk.NewDec(newPower)) } // validator still in the validator set, so delete from the copy delete(last, operatorBytes) - // set validator power on lookup index. - k.SetLastValidatorPower(ctx, operator, sdk.NewDec(newPower)) - // keep count count++ totalPower += newPower - } - // set total power on lookup index. - k.SetLastTotalPower(ctx, sdk.NewDec(totalPower)) - // sort the no-longer-bonded validators noLongerBonded := k.sortNoLongerBonded(last) @@ -116,7 +112,11 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // update the validator set updates = append(updates, validator.ABCIValidatorUpdateZero()) + } + // set total power on lookup index if there are any updates + if len(updates) > 0 { + k.SetLastTotalPower(ctx, sdk.NewDec(totalPower)) } return updates From 4df326053085cafb992a35e8fa310343b3413862 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 22 Oct 2018 22:17:52 -0400 Subject: [PATCH 30/30] add GetAccum for validator and fee pool --- x/distribution/keeper/keeper.go | 11 +++++++++++ x/distribution/keeper/validator.go | 16 ++++++++++++++++ x/distribution/types/fee_pool.go | 21 ++++++++++++++++++--- x/distribution/types/validator_info.go | 14 +++++++++----- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index 0ccf76ca63db..3919a18aedf7 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -55,6 +55,17 @@ func (k Keeper) SetFeePool(ctx sdk.Context, feePool types.FeePool) { store.Set(FeePoolKey, b) } +// get the total validator accum for the ctx height +// in the fee pool +func (k Keeper) GetFeePoolValAccum(ctx sdk.Context) sdk.Dec { + + // withdraw self-delegation + height := ctx.BlockHeight() + totalPower := k.stakeKeeper.GetLastTotalPower(ctx) + fp := k.GetFeePool(ctx) + return fp.GetTotalValAccum(height, totalPower) +} + //______________________________________________________________________ // set the proposer public key for this block diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index d8ec7ea7fdd2..1ad8bef7bd9a 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -40,6 +40,22 @@ func (k Keeper) RemoveValidatorDistInfo(ctx sdk.Context, valAddr sdk.ValAddress) store.Delete(GetValidatorDistInfoKey(valAddr)) } +// Get the calculated accum of a validator at the current block +// without affecting the state. +func (k Keeper) GetValidatorAccum(ctx sdk.Context, operatorAddr sdk.ValAddress) (sdk.Dec, sdk.Error) { + if !k.HasValidatorDistInfo(ctx, operatorAddr) { + return sdk.Dec{}, types.ErrNoValidatorDistInfo(k.codespace) + } + + // withdraw self-delegation + height := ctx.BlockHeight() + lastValPower := k.stakeKeeper.GetLastValidatorPower(ctx, operatorAddr) + valInfo := k.GetValidatorDistInfo(ctx, operatorAddr) + accum := valInfo.GetAccum(height, lastValPower) + + return accum, nil +} + // withdrawal all the validator rewards including the commission func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.ValAddress) sdk.Error { diff --git a/x/distribution/types/fee_pool.go b/x/distribution/types/fee_pool.go index a792a888f2d9..abd12f752f59 100644 --- a/x/distribution/types/fee_pool.go +++ b/x/distribution/types/fee_pool.go @@ -20,7 +20,7 @@ func NewTotalAccum(height int64) TotalAccum { } } -// update total validator accumulation factor for the new height +// update total accumulation factor for the new height // CONTRACT: height should be greater than the old height func (ta TotalAccum) UpdateForNewHeight(height int64, accumCreatedPerBlock sdk.Dec) TotalAccum { blocks := height - ta.UpdateHeight @@ -32,9 +32,19 @@ func (ta TotalAccum) UpdateForNewHeight(height int64, accumCreatedPerBlock sdk.D return ta } +// get total accumulation factor for the given height +// CONTRACT: height should be greater than the old height +func (ta TotalAccum) GetAccum(height int64, accumCreatedPerBlock sdk.Dec) sdk.Dec { + blocks := height - ta.UpdateHeight + if blocks < 0 { + panic("reverse updated for new height") + } + return ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks))) +} + // update total validator accumulation factor for the new height // CONTRACT: height should be greater than the old height -func (ta TotalAccum) UpdateForNewHeight_DEBUG(height int64, accumCreatedPerBlock sdk.Dec) TotalAccum { +func (ta TotalAccum) UpdateForNewHeightDEBUG(height int64, accumCreatedPerBlock sdk.Dec) TotalAccum { blocks := height - ta.UpdateHeight if blocks < 0 { panic("reverse updated for new height") @@ -68,10 +78,15 @@ type FeePool struct { // update total validator accumulation factor // NOTE: Do not call this except from ValidatorDistInfo.TakeFeePoolRewards(). func (f FeePool) UpdateTotalValAccum(height int64, totalBondedTokens sdk.Dec) FeePool { - f.TotalValAccum = f.TotalValAccum.UpdateForNewHeight_DEBUG(height, totalBondedTokens) + f.TotalValAccum = f.TotalValAccum.UpdateForNewHeightDEBUG(height, totalBondedTokens) return f } +// get the total validator accum for the fee pool without modifying the state +func (f FeePool) GetTotalValAccum(height int64, totalBondedTokens sdk.Dec) sdk.Dec { + return f.TotalValAccum.GetAccum(height, totalBondedTokens) +} + // zero fee pool func InitialFeePool() FeePool { return FeePool{ diff --git a/x/distribution/types/validator_info.go b/x/distribution/types/validator_info.go index df1062e5c4d2..8e7b55c29c76 100644 --- a/x/distribution/types/validator_info.go +++ b/x/distribution/types/validator_info.go @@ -34,6 +34,12 @@ func (vi ValidatorDistInfo) UpdateTotalDelAccum(height int64, totalDelShares sdk return vi } +// Get the calculated accum of this validator at the provided height +func (vi ValidatorDistInfo) GetAccum(height int64, vdTokens sdk.Dec) sdk.Dec { + blocks := height - vi.FeePoolWithdrawalHeight + return vdTokens.MulInt(sdk.NewInt(blocks)) +} + // Move any available accumulated fees in the FeePool to the validator's pool. // * updates validator info's FeePoolWithdrawalHeight, thus setting accum to 0. // * updates fee pool to latest height and total val accum w/ given totalBonded. @@ -53,17 +59,15 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo } // update the validators pool - blocks := height - vi.FeePoolWithdrawalHeight + accum := vi.GetAccum(height, vdTokens) vi.FeePoolWithdrawalHeight = height - accum := vdTokens.MulInt(sdk.NewInt(blocks)) if !accum.IsZero() { fmt.Println( cmn.Red( - fmt.Sprintf("FP Sub %v * %v = %v, %v - _ => %v", - vdTokens.String(), sdk.NewInt(blocks), - accum.String(), + fmt.Sprintf("FP Sub %v - %v => %v", fp.TotalValAccum.Accum.String(), + accum.String(), fp.TotalValAccum.Accum.Sub(accum).String(), ), ),