2018-07-17 11:50:30 -07:00
package simulation
import (
2018-09-09 08:34:09 -07:00
"fmt"
2018-07-17 11:50:30 -07:00
"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/bank"
2018-10-18 12:58:57 -07:00
"github.com/cosmos/cosmos-sdk/x/distribution"
2018-07-17 11:50:30 -07:00
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/tendermint/abci/types"
)
// AllInvariants runs all invariants of the stake module.
// Currently: total supply, positive power
2018-10-18 18:08:25 -07:00
func AllInvariants ( ck bank . Keeper , k stake . Keeper , f auth . FeeCollectionKeeper , d distribution . Keeper , am auth . AccountMapper ) simulation . Invariant {
2018-09-09 08:34:09 -07:00
return func ( app * baseapp . BaseApp ) error {
2018-10-18 18:08:25 -07:00
err := SupplyInvariants ( ck , k , f , d , am ) ( app )
2018-09-09 08:34:09 -07:00
if err != nil {
return err
}
err = PositivePowerInvariant ( k ) ( app )
if err != nil {
return err
}
err = ValidatorSetInvariant ( k ) ( app )
return err
2018-07-17 11:50:30 -07:00
}
}
// SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations
2018-08-31 15:22:37 -07:00
// nolint: unparam
2018-10-18 18:08:25 -07:00
func SupplyInvariants ( ck bank . Keeper , k stake . Keeper , f auth . FeeCollectionKeeper , d distribution . Keeper , am auth . AccountMapper ) simulation . Invariant {
2018-09-09 08:34:09 -07:00
return func ( app * baseapp . BaseApp ) error {
2018-07-17 11:50:30 -07:00
ctx := app . NewContext ( false , abci . Header { } )
2018-09-27 06:52:30 -07:00
pool := k . GetPool ( ctx )
2018-07-17 11:50:30 -07:00
2018-10-18 12:58:57 -07:00
loose := sdk . ZeroDec ( )
2018-08-14 17:15:02 -07:00
bonded := sdk . ZeroDec ( )
2018-07-17 11:50:30 -07:00
am . IterateAccounts ( ctx , func ( acc auth . Account ) bool {
2018-10-18 12:58:57 -07:00
loose = loose . Add ( sdk . NewDecFromInt ( acc . GetCoins ( ) . AmountOf ( "steak" ) ) )
2018-07-17 11:50:30 -07:00
return false
} )
2018-07-17 22:37:38 -07:00
k . IterateUnbondingDelegations ( ctx , func ( _ int64 , ubd stake . UnbondingDelegation ) bool {
2018-10-18 12:58:57 -07:00
loose = loose . Add ( sdk . NewDecFromInt ( ubd . Balance . Amount ) )
2018-07-17 22:37:38 -07:00
return false
} )
2018-07-17 11:50:30 -07:00
k . IterateValidators ( ctx , func ( _ int64 , validator sdk . Validator ) bool {
switch validator . GetStatus ( ) {
case sdk . Bonded :
bonded = bonded . Add ( validator . GetPower ( ) )
2018-07-18 00:42:18 -07:00
case sdk . Unbonding :
2018-10-18 12:58:57 -07:00
loose = loose . Add ( validator . GetTokens ( ) )
2018-07-18 00:42:18 -07:00
case sdk . Unbonded :
2018-10-18 12:58:57 -07:00
loose = loose . Add ( validator . GetTokens ( ) )
2018-07-17 11:50:30 -07:00
}
return false
} )
2018-07-18 00:42:18 -07:00
2018-10-18 12:58:57 -07:00
feePool := d . GetFeePool ( ctx )
2018-10-18 18:08:25 -07:00
// add outstanding fees
loose = loose . Add ( sdk . NewDecFromInt ( f . GetCollectedFees ( ctx ) . AmountOf ( "steak" ) ) )
2018-10-18 12:58:57 -07:00
// 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
} )
2018-07-18 00:42:18 -07:00
// Loose tokens should equal coin supply plus unbonding delegations plus tokens on unbonded validators
2018-10-18 12:58:57 -07:00
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 )
2018-09-27 06:52:30 -07:00
}
2018-07-18 00:42:18 -07:00
// Bonded tokens should equal sum of tokens with bonded validators
2018-10-18 12:58:57 -07:00
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 )
2018-09-27 06:52:30 -07:00
}
2018-07-17 11:50:30 -07:00
2018-09-09 08:34:09 -07:00
return nil
2018-07-17 11:50:30 -07:00
}
}
// PositivePowerInvariant checks that all stored validators have > 0 power
func PositivePowerInvariant ( k stake . Keeper ) simulation . Invariant {
2018-09-09 08:34:09 -07:00
return func ( app * baseapp . BaseApp ) error {
2018-07-17 11:50:30 -07:00
ctx := app . NewContext ( false , abci . Header { } )
2018-09-09 08:34:09 -07:00
var err error
2018-07-17 11:50:30 -07:00
k . IterateValidatorsBonded ( ctx , func ( _ int64 , validator sdk . Validator ) bool {
2018-09-09 08:34:09 -07:00
if ! validator . GetPower ( ) . GT ( sdk . ZeroDec ( ) ) {
2018-09-24 21:09:31 -07:00
err = fmt . Errorf ( "validator with non-positive power stored. (pubkey %v)" , validator . GetConsPubKey ( ) )
2018-09-09 08:34:09 -07:00
return true
}
2018-07-17 11:50:30 -07:00
return false
} )
2018-09-09 08:34:09 -07:00
return err
2018-07-17 11:50:30 -07:00
}
}
// ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set
func ValidatorSetInvariant ( k stake . Keeper ) simulation . Invariant {
2018-09-09 08:34:09 -07:00
return func ( app * baseapp . BaseApp ) error {
2018-07-17 11:50:30 -07:00
// TODO
2018-09-09 08:34:09 -07:00
return nil
2018-07-17 11:50:30 -07:00
}
}