Merge PR #2501: Simulate transactions for fee distribution, simulate inflation, and fix a multitude of bugs discovered in the process of doing so
This commit is contained in:
commit
60d188ded0
18
PENDING.md
18
PENDING.md
@ -47,6 +47,24 @@ 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] \#2501 Simulate transactions & invariants for fee distribution, and fix bugs discovered in the process
|
||||
* [x/auth] Simulate random fee payments
|
||||
* [cmd/gaia/app] Simulate non-zero inflation
|
||||
* [x/stake] Call hooks correctly in several cases related to delegation/validator updates
|
||||
* [x/stake] Check full supply invariants, including yet-to-be-withdrawn fees
|
||||
* [x/stake] Remove no-longer-in-use store key
|
||||
* [x/slashing] Call hooks correctly when a validator is slashed
|
||||
* [x/slashing] Truncate withdrawals (unbonding, redelegation) and burn change
|
||||
* [x/mock/simulation] Ensure the simulation cannot set a proposer address of nil
|
||||
* [x/mock/simulation] Add more event logs on begin block / end block for clarity
|
||||
* [x/mock/simulation] Correctly set validator power in abci.RequestBeginBlock
|
||||
* [x/minting] Correctly call stake keeper to track inflated supply
|
||||
* [x/distribution] Sanity check for nonexistent rewards
|
||||
* [x/distribution] Truncate withdrawals and return change to the community pool
|
||||
* [x/distribution] Add sanity checks for incorrect accum / total accum relations
|
||||
* [x/distribution] Correctly calculate total power using Tendermint updates
|
||||
* [x/distribution] Simulate withdrawal transactions
|
||||
* [x/distribution] Fix a bug where the fee pool was not correctly tracked on WithdrawDelegatorRewardsAll
|
||||
* [x/stake] \#1673 Validators are no longer deleted until they can no longer possibly be slashed
|
||||
* [\#1890](https://github.com/cosmos/cosmos-sdk/issues/1890) Start chain with initial state + sequence of transactions
|
||||
* [cli] Rename `gaiad init gentx` to `gaiad gentx`.
|
||||
|
||||
@ -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,17 +335,19 @@ 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)
|
||||
}
|
||||
func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) {
|
||||
h.sh.OnValidatorBonded(ctx, addr)
|
||||
func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress, operator sdk.ValAddress) {
|
||||
h.dh.OnValidatorBonded(ctx, addr, operator)
|
||||
h.sh.OnValidatorBonded(ctx, addr, operator)
|
||||
}
|
||||
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.dh.OnValidatorBeginUnbonding(ctx, addr, operator)
|
||||
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)
|
||||
|
||||
@ -14,8 +14,10 @@ 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"
|
||||
distrsim "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/mint"
|
||||
@ -47,9 +49,11 @@ func init() {
|
||||
func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
var genesisAccounts []GenesisAccount
|
||||
|
||||
amt := int64(10000)
|
||||
|
||||
// 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(amt)}}
|
||||
genesisAccounts = append(genesisAccounts, GenesisAccount{
|
||||
Address: acc.Address,
|
||||
Coins: coins,
|
||||
@ -71,20 +75,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(amt)
|
||||
validator.DelegatorShares = sdk.NewDec(amt)
|
||||
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amt), 0}
|
||||
validators = append(validators, validator)
|
||||
delegations = append(delegations, delegation)
|
||||
}
|
||||
stakeGenesis.Pool.LooseTokens = sdk.NewDec(int64(100*250) + (numInitiallyBonded * 100))
|
||||
stakeGenesis.Pool.LooseTokens = sdk.NewDec(amt*250 + (numInitiallyBonded * amt))
|
||||
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,
|
||||
@ -106,7 +106,12 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
|
||||
func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
|
||||
return []simulation.WeightedOperation{
|
||||
{5, authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper)},
|
||||
{100, banksim.SingleInputSendMsg(app.accountKeeper, app.bankKeeper)},
|
||||
{50, distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper)},
|
||||
{50, distrsim.SimulateMsgWithdrawDelegatorRewardsAll(app.accountKeeper, app.distrKeeper)},
|
||||
{50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)},
|
||||
{50, distrsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountKeeper, app.distrKeeper)},
|
||||
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)},
|
||||
{100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgCreateValidator(app.accountKeeper, app.stakeKeeper)},
|
||||
@ -122,7 +127,7 @@ func invariants(app *GaiaApp) []simulation.Invariant {
|
||||
return []simulation.Invariant{
|
||||
banksim.NonnegativeBalanceInvariant(app.accountKeeper),
|
||||
govsim.AllInvariants(),
|
||||
stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.accountKeeper),
|
||||
stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
|
||||
slashingsim.AllInvariants(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,12 +5,12 @@ 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) // 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
|
||||
|
||||
@ -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 {
|
||||
bz, err := d.Int.MarshalText()
|
||||
if err != nil {
|
||||
|
||||
@ -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
|
||||
@ -111,12 +111,12 @@ 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) // Must be called when a validator begins unbonding
|
||||
OnValidatorBonded(ctx Context, address ConsAddress, operator ValAddress) // 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
|
||||
|
||||
62
x/auth/simulation/fake.go
Normal file
62
x/auth/simulation/fake.go
Normal file
@ -0,0 +1,62 @@
|
||||
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.AccountKeeper, 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)}
|
||||
err = stored.SetCoins(initCoins.Minus(coins))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.SetAccount(ctx, stored)
|
||||
if !coins.IsNotNegative() {
|
||||
panic("setting negative fees")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -49,7 +49,7 @@ var (
|
||||
|
||||
NewMsgSetWithdrawAddress = types.NewMsgSetWithdrawAddress
|
||||
NewMsgWithdrawDelegatorRewardsAll = types.NewMsgWithdrawDelegatorRewardsAll
|
||||
NewMsgWithdrawDelegationReward = types.NewMsgWithdrawDelegatorReward
|
||||
NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward
|
||||
NewMsgWithdrawValidatorRewardsAll = types.NewMsgWithdrawValidatorRewardsAll
|
||||
)
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -13,6 +13,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, percentVotes sdk.Dec, proposer s
|
||||
|
||||
// get the proposer of this block
|
||||
proposerValidator := k.stakeKeeper.ValidatorByConsAddr(ctx, proposer)
|
||||
|
||||
proposerDist := k.GetValidatorDistInfo(ctx, proposerValidator.GetOperator())
|
||||
|
||||
// get the fees which have been getting collected through all the
|
||||
|
||||
@ -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) {
|
||||
@ -62,29 +69,40 @@ 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) {
|
||||
valAddr sdk.ValAddress) sdk.Error {
|
||||
|
||||
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 := sdk.NewDecFromInt(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.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission())
|
||||
delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, lastTotalPower,
|
||||
lastValPower, 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 +111,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)
|
||||
}
|
||||
@ -104,19 +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)
|
||||
feePool := k.GetFeePool(ctx)
|
||||
lastTotalPower := sdk.NewDecFromInt(k.stakeKeeper.GetLastTotalPower(ctx))
|
||||
|
||||
// iterate over all the delegations
|
||||
// TODO: Reconcile with duplicate code in WithdrawDelegationReward.
|
||||
operationAtDelegation := func(_ int64, del sdk.Delegation) (stop bool) {
|
||||
valAddr := del.GetValidator()
|
||||
feePool := k.GetFeePool(ctx)
|
||||
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.GetTokens(), 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)
|
||||
@ -124,7 +148,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
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ func TestWithdrawDelegationRewardBasic(t *testing.T) {
|
||||
|
||||
// withdraw delegation
|
||||
ctx = ctx.WithBlockHeight(1)
|
||||
sk.SetLastTotalPower(ctx, sdk.NewInt(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())
|
||||
|
||||
@ -22,8 +22,13 @@ func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
|
||||
}
|
||||
|
||||
// Withdrawal all validator rewards
|
||||
func (k Keeper) onValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) {
|
||||
k.WithdrawValidatorRewardsAll(ctx, addr)
|
||||
func (k Keeper) onValidatorModified(ctx sdk.Context, addr sdk.ValAddress) {
|
||||
// This doesn't need to be run at genesis
|
||||
if ctx.BlockHeight() > 0 {
|
||||
if err := k.WithdrawValidatorRewardsAll(ctx, addr); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Withdrawal all validator distribution rewards and cleanup the distribution record
|
||||
@ -50,7 +55,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
|
||||
@ -76,22 +83,26 @@ 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) {
|
||||
h.k.onValidatorCommissionChange(ctx, addr)
|
||||
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) {
|
||||
h.k.onValidatorRemoved(ctx, addr)
|
||||
}
|
||||
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||
h.k.onValidatorModified(ctx, valAddr)
|
||||
h.k.onDelegationCreated(ctx, delAddr, valAddr)
|
||||
}
|
||||
func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||
h.k.onValidatorModified(ctx, valAddr)
|
||||
h.k.onDelegationSharesModified(ctx, delAddr, valAddr)
|
||||
}
|
||||
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||
h.k.onDelegationRemoved(ctx, delAddr, valAddr)
|
||||
}
|
||||
|
||||
// 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) OnValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) {
|
||||
h.k.onValidatorModified(ctx, addr)
|
||||
}
|
||||
func (h Hooks) OnValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, addr sdk.ValAddress) {
|
||||
h.k.onValidatorModified(ctx, addr)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,27 +41,52 @@ 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()
|
||||
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 := sdk.NewDecFromInt(k.stakeKeeper.GetLastTotalPower(ctx))
|
||||
valInfo := k.GetValidatorDistInfo(ctx, operatorAddr)
|
||||
feePool := k.GetFeePool(ctx)
|
||||
valInfo, feePool, commission := valInfo.WithdrawCommission(feePool, height, bondedTokens,
|
||||
validator.GetTokens(), validator.GetCommission())
|
||||
valInfo, feePool, commission := valInfo.WithdrawCommission(feePool, height, lastTotalPower,
|
||||
lastValPower, 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
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++
|
||||
}
|
||||
}
|
||||
|
||||
122
x/distribution/simulation/msgs.go
Normal file
122
x/distribution/simulation/msgs.go
Normal file
@ -0,0 +1,122 @@
|
||||
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/simulation"
|
||||
)
|
||||
|
||||
// SimulateMsgSetWithdrawAddress
|
||||
func SimulateMsgSetWithdrawAddress(m auth.AccountKeeper, 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.AccountKeeper, 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.AccountKeeper, 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.AccountKeeper, 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
|
||||
}
|
||||
}
|
||||
@ -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 // binary search
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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{},
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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.Int
|
||||
GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) sdk.Dec
|
||||
}
|
||||
|
||||
// expected coin keeper
|
||||
|
||||
@ -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,13 +53,17 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo
|
||||
blocks := height - vi.FeePoolWithdrawalHeight
|
||||
vi.FeePoolWithdrawalHeight = height
|
||||
accum := vdTokens.MulInt(sdk.NewInt(blocks))
|
||||
withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(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.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)
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -21,5 +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})
|
||||
k.sk.InflateSupply(ctx, sdk.NewDecFromInt(mintedCoin.Amount))
|
||||
k.SetMinter(ctx, minter)
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package mint
|
||||
|
||||
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// expected stake keeper
|
||||
type StakeKeeper interface {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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,12 +170,18 @@ 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()
|
||||
}
|
||||
|
||||
if header.ProposerAddress == nil {
|
||||
fmt.Printf("\nSimulation stopped early as all validators have been unbonded, there is nobody left propose a block!\n")
|
||||
stopEarly = true
|
||||
break
|
||||
}
|
||||
|
||||
// Generate a random RequestBeginBlock with the current validator set for the next block
|
||||
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
|
||||
|
||||
@ -210,10 +230,10 @@ 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)
|
||||
fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize)
|
||||
}
|
||||
}
|
||||
opCount++
|
||||
@ -370,6 +390,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,
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -6,7 +6,7 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) {
|
||||
// Update the signing info start height or create a new signing info
|
||||
_, found := k.getValidatorSigningInfo(ctx, address)
|
||||
if !found {
|
||||
@ -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)
|
||||
@ -51,18 +51,18 @@ func (k Keeper) Hooks() Hooks {
|
||||
}
|
||||
|
||||
// Implements sdk.ValidatorHooks
|
||||
func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
h.k.onValidatorBonded(ctx, address)
|
||||
func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) {
|
||||
h.k.onValidatorBonded(ctx, address, operator)
|
||||
}
|
||||
|
||||
// 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
|
||||
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) {}
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
func TestHookOnValidatorBonded(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
|
||||
addr := sdk.ConsAddress(addrs[0])
|
||||
keeper.onValidatorBonded(ctx, addr)
|
||||
keeper.onValidatorBonded(ctx, addr, nil)
|
||||
period := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, ctx.BlockHeight())
|
||||
require.Equal(t, ValidatorSlashingPeriod{addr, ctx.BlockHeight(), 0, sdk.ZeroDec()}, period)
|
||||
}
|
||||
@ -19,8 +19,8 @@ func TestHookOnValidatorBonded(t *testing.T) {
|
||||
func TestHookOnValidatorBeginUnbonding(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
|
||||
addr := sdk.ConsAddress(addrs[0])
|
||||
keeper.onValidatorBonded(ctx, addr)
|
||||
keeper.onValidatorBeginUnbonding(ctx, addr)
|
||||
keeper.onValidatorBonded(ctx, addr, nil)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, paramspa
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -71,6 +72,11 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
|
||||
logger.Info(fmt.Sprintf("Fraction slashed capped by slashing period from %v to %v", fraction, revisedFraction))
|
||||
|
||||
// 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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -42,7 +41,6 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
|
||||
// Manually set indices for the first time
|
||||
keeper.SetValidatorByConsAddr(ctx, validator)
|
||||
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||
|
||||
keeper.OnValidatorCreated(ctx, validator.OperatorAddr)
|
||||
}
|
||||
|
||||
|
||||
@ -106,6 +106,8 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
||||
k.SetValidatorByConsAddr(ctx, validator)
|
||||
k.SetNewValidatorByPowerIndex(ctx, validator)
|
||||
|
||||
k.OnValidatorCreated(ctx, validator.OperatorAddr)
|
||||
|
||||
// move coins from the msg.Address account to a (self-delegation) delegator account
|
||||
// the validator account and global shares are updated within here
|
||||
_, err = k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
|
||||
@ -113,10 +115,6 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
k.OnValidatorCreated(ctx, validator.OperatorAddr)
|
||||
accAddr := sdk.AccAddress(validator.OperatorAddr)
|
||||
k.OnDelegationCreated(ctx, accAddr, validator.OperatorAddr)
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionCreateValidator,
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
@ -150,6 +148,7 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
|
||||
return err.Result()
|
||||
}
|
||||
validator.Commission = commission
|
||||
k.OnValidatorModified(ctx, msg.ValidatorAddr)
|
||||
}
|
||||
|
||||
k.SetValidator(ctx, validator)
|
||||
@ -185,9 +184,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()),
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -480,7 +488,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 +558,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())
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,15 +23,15 @@ func (k Keeper) OnValidatorRemoved(ctx sdk.Context, address sdk.ValAddress) {
|
||||
}
|
||||
}
|
||||
|
||||
func (k Keeper) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
func (k Keeper) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, operator sdk.ValAddress) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.OnValidatorBonded(ctx, address)
|
||||
k.hooks.OnValidatorBonded(ctx, address, operator)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,65 @@ 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.Int) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
b := store.Get(LastTotalPowerKey)
|
||||
if b == nil {
|
||||
k.SetIntraTxCounter(ctx, 0)
|
||||
return sdk.ZeroInt()
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &power)
|
||||
return
|
||||
}
|
||||
|
||||
// Set the last total validator power.
|
||||
func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) {
|
||||
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
|
||||
}
|
||||
|
||||
// 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.cdc.MustMarshalBinary(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
|
||||
|
||||
@ -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 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
|
||||
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 GetBondedValidatorIndexKey(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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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))
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -97,10 +98,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)
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -11,7 +11,14 @@ 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 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.
|
||||
// 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
|
||||
@ -20,11 +27,14 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
totalPower := sdk.ZeroInt()
|
||||
|
||||
// retrieve last validator set
|
||||
last := k.retrieveLastValidatorSet(ctx)
|
||||
// Retrieve the last validator set.
|
||||
// The persistent set is updated later in this function.
|
||||
// (see LastValidatorPowerKey).
|
||||
last := k.getLastValidatorsByAddr(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() {
|
||||
@ -62,22 +72,22 @@ 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.cdc.MustMarshalBinary(sdk.NewDec(newPower))
|
||||
// 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 the bonded validator index
|
||||
store.Set(GetBondedValidatorIndexKey(operator), newPowerBytes)
|
||||
|
||||
// keep count
|
||||
count++
|
||||
|
||||
totalPower = totalPower.Add(sdk.NewInt(newPower))
|
||||
}
|
||||
|
||||
// sort the no-longer-bonded validators
|
||||
@ -98,11 +108,15 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
|
||||
}
|
||||
|
||||
// delete from the bonded validator index
|
||||
store.Delete(GetBondedValidatorIndexKey(operator))
|
||||
k.DeleteLastValidatorPower(ctx, sdk.ValAddress(operator))
|
||||
|
||||
// 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, totalPower)
|
||||
}
|
||||
|
||||
return updates
|
||||
@ -183,7 +197,7 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
|
||||
|
||||
// call the bond hook if present
|
||||
if k.hooks != nil {
|
||||
k.hooks.OnValidatorBonded(ctx, validator.ConsAddress())
|
||||
k.hooks.OnValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr)
|
||||
}
|
||||
|
||||
return validator
|
||||
@ -219,7 +233,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
|
||||
@ -237,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, ValidatorsBondedIndexKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var operator [sdk.AddrLen]byte
|
||||
copy(operator[:], iterator.Key()[1:])
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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]))
|
||||
|
||||
@ -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.AccountKeeper) simulation.Invariant {
|
||||
func AllInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
err := SupplyInvariants(ck, k, am)(app)
|
||||
err := SupplyInvariants(ck, k, f, d, am)(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -31,19 +32,19 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountKeeper) 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.AccountKeeper) simulation.Invariant {
|
||||
func SupplyInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) 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,24 +52,41 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountKeeper) 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 outstanding fees
|
||||
loose = loose.Add(sdk.NewDecFromInt(f.GetCollectedFees(ctx).AmountOf("steak")))
|
||||
|
||||
// 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
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.AccountKeeper
|
||||
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.AccountKeeper),
|
||||
AllInvariants(bankKeeper, stakeKeeper, feeCollectionKeeper, distrKeeper, mapp.AccountKeeper),
|
||||
}, 10, 100,
|
||||
false,
|
||||
)
|
||||
|
||||
@ -38,12 +38,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
|
||||
|
||||
@ -97,9 +97,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
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user