Merge PR #4255: Supply Module

This commit is contained in:
Federico Kunze 2019-06-28 22:11:27 +02:00 committed by Alexander Bezobchuk
parent 4a0fbb3d6e
commit 352678438c
138 changed files with 3510 additions and 1889 deletions

View File

@ -0,0 +1,9 @@
#4255 Add supply module that passively tracks the supplies of a chain
- Introduce `ModuleAccount` type, which tracks the flow of coins held within a module
- Replaced `FeeCollectorKeeper` for a `ModuleAccount`
- Replaced the staking `Pool`, which coins are now held by the `BondedPool` and `NotBonded` module accounts
- The `NotBonded` module account now only keeps track of the not bonded tokens within staking, instead of the whole chain
- #3628 Replaced governance's burn and deposit accounts for a `ModuleAccount`
- Added a `ModuleAccount` for the distribution module
- Added a `ModuleAccount` for the mint module
#4472 validation for crisis genesis

View File

@ -15,11 +15,11 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
@ -27,6 +27,7 @@ import (
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
const appName = "SimApp"
@ -53,6 +54,7 @@ var (
params.AppModuleBasic{},
crisis.AppModuleBasic{},
slashing.AppModuleBasic{},
supply.AppModuleBasic{},
)
)
@ -73,30 +75,30 @@ type SimApp struct {
invCheckPeriod uint
// keys to access the substores
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyStaking *sdk.KVStoreKey
tkeyStaking *sdk.TransientStoreKey
keySlashing *sdk.KVStoreKey
keyMint *sdk.KVStoreKey
keyDistr *sdk.KVStoreKey
tkeyDistr *sdk.TransientStoreKey
keyGov *sdk.KVStoreKey
keyFeeCollection *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
tkeyParams *sdk.TransientStoreKey
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keySupply *sdk.KVStoreKey
keyStaking *sdk.KVStoreKey
tkeyStaking *sdk.TransientStoreKey
keySlashing *sdk.KVStoreKey
keyMint *sdk.KVStoreKey
keyDistr *sdk.KVStoreKey
tkeyDistr *sdk.TransientStoreKey
keyGov *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
tkeyParams *sdk.TransientStoreKey
// keepers
accountKeeper auth.AccountKeeper
feeCollectionKeeper auth.FeeCollectionKeeper
bankKeeper bank.Keeper
stakingKeeper staking.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
crisisKeeper crisis.Keeper
paramsKeeper params.Keeper
accountKeeper auth.AccountKeeper
bankKeeper bank.Keeper
supplyKeeper supply.Keeper
stakingKeeper staking.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
crisisKeeper crisis.Keeper
paramsKeeper params.Keeper
// the module manager
mm *module.Manager
@ -113,21 +115,21 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
bApp.SetAppVersion(version.Version)
var app = &SimApp{
BaseApp: bApp,
cdc: cdc,
invCheckPeriod: invCheckPeriod,
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
keyMint: sdk.NewKVStoreKey(mint.StoreKey),
keyDistr: sdk.NewKVStoreKey(distr.StoreKey),
tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey),
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
keyGov: sdk.NewKVStoreKey(gov.StoreKey),
keyFeeCollection: sdk.NewKVStoreKey(auth.FeeStoreKey),
keyParams: sdk.NewKVStoreKey(params.StoreKey),
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
BaseApp: bApp,
cdc: cdc,
invCheckPeriod: invCheckPeriod,
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
keySupply: sdk.NewKVStoreKey(supply.StoreKey),
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
keyMint: sdk.NewKVStoreKey(mint.StoreKey),
keyDistr: sdk.NewKVStoreKey(distr.StoreKey),
tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey),
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
keyGov: sdk.NewKVStoreKey(gov.StoreKey),
keyParams: sdk.NewKVStoreKey(params.StoreKey),
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
}
// init params keeper and subspaces
@ -141,19 +143,24 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace)
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
// account permissions
basicModuleAccs := []string{auth.FeeCollectorName, distr.ModuleName}
minterModuleAccs := []string{mint.ModuleName}
burnerModuleAccs := []string{staking.BondedPoolName, staking.NotBondedPoolName, gov.ModuleName}
// add keepers
app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.keyAccount, authSubspace, auth.ProtoBaseAccount)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace)
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
stakingKeeper := staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking, app.bankKeeper,
stakingSubspace, staking.DefaultCodespace)
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, mintSubspace, &stakingKeeper, app.feeCollectionKeeper)
app.distrKeeper = distr.NewKeeper(app.cdc, app.keyDistr, distrSubspace, app.bankKeeper, &stakingKeeper,
app.feeCollectionKeeper, distr.DefaultCodespace)
app.supplyKeeper = supply.NewKeeper(app.cdc, app.keySupply, app.accountKeeper,
app.bankKeeper, supply.DefaultCodespace, basicModuleAccs, minterModuleAccs, burnerModuleAccs)
stakingKeeper := staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking,
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, mintSubspace, &stakingKeeper, app.supplyKeeper, auth.FeeCollectorName)
app.distrKeeper = distr.NewKeeper(app.cdc, app.keyDistr, distrSubspace, &stakingKeeper,
app.supplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName)
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, &stakingKeeper,
slashingSubspace, slashing.DefaultCodespace)
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.distrKeeper,
app.bankKeeper, app.feeCollectionKeeper)
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
// register the proposal types
govRouter := gov.NewRouter()
@ -161,7 +168,7 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper, govSubspace,
app.bankKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
app.supplyKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
@ -171,14 +178,15 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
app.mm = module.NewManager(
genaccounts.NewAppModule(app.accountKeeper),
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.accountKeeper, app.feeCollectionKeeper),
auth.NewAppModule(app.accountKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
crisis.NewAppModule(app.crisisKeeper),
distr.NewAppModule(app.distrKeeper),
gov.NewAppModule(app.govKeeper),
supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
distr.NewAppModule(app.distrKeeper, app.supplyKeeper),
gov.NewAppModule(app.govKeeper, app.supplyKeeper),
mint.NewAppModule(app.mintKeeper),
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
staking.NewAppModule(app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper),
)
// During begin block slashing happens after distr.BeginBlocker so that
@ -190,7 +198,7 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
// genutils must occur after staking so that pools are properly
// initialized with tokens from genesis accounts.
app.mm.SetOrderInitGenesis(genaccounts.ModuleName, distr.ModuleName,
app.mm.SetOrderInitGenesis(genaccounts.ModuleName, supply.ModuleName, distr.ModuleName,
staking.ModuleName, auth.ModuleName, bank.ModuleName, slashing.ModuleName,
gov.ModuleName, mint.ModuleName, crisis.ModuleName, genutil.ModuleName)
@ -198,14 +206,14 @@ func NewSimApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bo
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
// initialize stores
app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keyMint,
app.keyDistr, app.keySlashing, app.keyGov, app.keyFeeCollection,
app.keyParams, app.tkeyParams, app.tkeyStaking, app.tkeyDistr)
app.MountStores(app.keyMain, app.keyAccount, app.keySupply, app.keyStaking,
app.keyMint, app.keyDistr, app.keySlashing, app.keyGov, app.keyParams,
app.tkeyParams, app.tkeyStaking, app.tkeyDistr)
// initialize BaseApp
app.SetInitChainer(app.InitChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper, auth.DefaultSigVerificationGasConsumer))
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.supplyKeeper, auth.DefaultSigVerificationGasConsumer))
app.SetEndBlocker(app.EndBlocker)
if loadLatest {

View File

@ -23,11 +23,11 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
authsim "github.com/cosmos/cosmos-sdk/x/auth/simulation"
"github.com/cosmos/cosmos-sdk/x/bank"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/gov"
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/mint"
@ -37,6 +37,7 @@ import (
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation"
"github.com/cosmos/cosmos-sdk/x/supply"
)
var (
@ -145,6 +146,7 @@ func appStateRandomizedFn(
genGenesisAccounts(cdc, r, accs, genesisTimestamp, amount, numInitiallyBonded, genesisState)
genAuthGenesisState(cdc, r, appParams, genesisState)
genBankGenesisState(cdc, r, appParams, genesisState)
genSupplyGenesisState(cdc, amount, numInitiallyBonded, int64(len(accs)), genesisState)
genGovGenesisState(cdc, r, appParams, genesisState)
genMintGenesisState(cdc, r, appParams, genesisState)
genDistrGenesisState(cdc, r, appParams, genesisState)
@ -192,7 +194,6 @@ func appStateFn(
func genAuthGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams, genesisState map[string]json.RawMessage) {
authGenesis := auth.NewGenesisState(
nil,
auth.NewParams(
func(r *rand.Rand) uint64 {
var v uint64
@ -257,6 +258,16 @@ func genBankGenesisState(cdc *codec.Codec, r *rand.Rand, ap simulation.AppParams
genesisState[bank.ModuleName] = cdc.MustMarshalJSON(bankGenesis)
}
func genSupplyGenesisState(cdc *codec.Codec, amount, numInitiallyBonded, numAccs int64, genesisState map[string]json.RawMessage) {
totalSupply := sdk.NewInt(amount * (numAccs + numInitiallyBonded))
supplyGenesis := supply.NewGenesisState(
supply.NewSupply(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply))),
)
fmt.Printf("Generated supply parameters:\n%s\n", codec.MustMarshalJSONIndent(cdc, supplyGenesis))
genesisState[supply.ModuleName] = cdc.MustMarshalJSON(supplyGenesis)
}
func genGenesisAccounts(
cdc *codec.Codec, r *rand.Rand, accs []simulation.Account,
genesisTimestamp time.Time, amount, numInitiallyBonded int64,
@ -517,7 +528,6 @@ func genStakingGenesisState(
) staking.GenesisState {
stakingGenesis := staking.NewGenesisState(
staking.InitialPool(),
staking.NewParams(
func(r *rand.Rand) time.Duration {
var v time.Duration
@ -560,7 +570,6 @@ func genStakingGenesisState(
delegations = append(delegations, delegation)
}
stakingGenesis.Pool.NotBondedTokens = sdk.NewInt((amount * numAccs) + (numInitiallyBonded * amount))
stakingGenesis.Validators = validators
stakingGenesis.Delegations = delegations
@ -593,7 +602,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation {
})
return v
}(nil),
authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper),
authsim.SimulateDeductFee(app.accountKeeper, app.supplyKeeper),
},
{
func(_ *rand.Rand) int {
@ -764,6 +773,11 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation {
}
func invariants(app *SimApp) []sdk.Invariant {
// TODO: fix PeriodicInvariants, it doesn't seem to call individual invariants for a period of 1
// Ref: https://github.com/cosmos/cosmos-sdk/issues/4631
if period == 1 {
return app.crisisKeeper.Invariants()
}
return simulation.PeriodicInvariants(app.crisisKeeper.Invariants(), period, 0)
}
@ -896,6 +910,7 @@ func TestAppImportExport(t *testing.T) {
if err != nil {
panic(err)
}
fmt.Printf("debug genesisState: %s\n", genesisState)
ctxB := newApp.NewContext(true, abci.Header{})
newApp.mm.InitGenesis(ctxB, genesisState)
@ -917,7 +932,7 @@ func TestAppImportExport(t *testing.T) {
{app.keySlashing, newApp.keySlashing, [][]byte{}},
{app.keyMint, newApp.keyMint, [][]byte{}},
{app.keyDistr, newApp.keyDistr, [][]byte{}},
{app.keyFeeCollection, newApp.keyFeeCollection, [][]byte{}},
{app.keySupply, newApp.keySupply, [][]byte{}},
{app.keyParams, newApp.keyParams, [][]byte{}},
{app.keyGov, newApp.keyGov, [][]byte{}},
}
@ -1074,7 +1089,7 @@ func BenchmarkInvariants(b *testing.B) {
for _, cr := range app.crisisKeeper.Routes() {
b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) {
if err := cr.Invar(ctx); err != nil {
fmt.Println(err)
fmt.Printf("broken invariant at block %d of %d\n%s", ctx.BlockHeight()-1, numBlocks, err)
b.FailNow()
}
})

View File

@ -22,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
// NewSimAppUNSAFE is used for debugging purposes only.
@ -57,6 +58,8 @@ func getSimulationLog(storeName string, cdcA, cdcB *codec.Codec, kvA, kvB cmn.KV
return decodeGovStore(cdcA, cdcB, kvA, kvB)
case distribution.StoreKey:
return decodeDistributionStore(cdcA, cdcB, kvA, kvB)
case supply.StoreKey:
return decodeSupplyStore(cdcA, cdcB, kvA, kvB)
default:
return
}
@ -254,3 +257,15 @@ func decodeGovStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string {
panic(fmt.Sprintf("invalid governance key prefix %X", kvA.Key[:1]))
}
}
func decodeSupplyStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string {
switch {
case bytes.Equal(kvA.Key[:1], supply.SupplyKey):
var supplyA, supplyB supply.Supply
cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA)
cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB)
return fmt.Sprintf("%v\n%v", supplyB, supplyB)
default:
panic(fmt.Sprintf("invalid supply key %X", kvA.Key))
}
}

View File

@ -20,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -55,6 +56,7 @@ func TestGetSimulationLog(t *testing.T) {
{gov.StoreKey, cmn.KVPair{Key: gov.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(gov.Vote{})}},
{distribution.StoreKey, cmn.KVPair{Key: distr.ProposerKey, Value: consAddr1.Bytes()}},
{slashing.StoreKey, cmn.KVPair{Key: slashing.GetValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryLengthPrefixed(true)}},
{supply.StoreKey, cmn.KVPair{Key: supply.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(supply.NewSupply(sdk.Coins{}))}},
{"Empty", cmn.KVPair{}},
{"OtherStore", cmn.KVPair{Key: []byte("key"), Value: []byte("value")}},
}
@ -183,14 +185,12 @@ func TestDecodeStakingStore(t *testing.T) {
bondTime := time.Now().UTC()
pool := staking.InitialPool()
val := staking.NewValidator(valAddr1, delPk1, staking.NewDescription("test", "test", "test", "test"))
del := staking.NewDelegation(delAddr1, valAddr1, sdk.OneDec())
ubd := staking.NewUnbondingDelegation(delAddr1, valAddr1, 15, bondTime, sdk.OneInt())
red := staking.NewRedelegation(delAddr1, valAddr1, valAddr1, 12, bondTime, sdk.OneInt(), sdk.OneDec())
kvPairs := cmn.KVPairs{
cmn.KVPair{Key: staking.PoolKey, Value: cdc.MustMarshalBinaryLengthPrefixed(pool)},
cmn.KVPair{Key: staking.LastTotalPowerKey, Value: cdc.MustMarshalBinaryLengthPrefixed(sdk.OneInt())},
cmn.KVPair{Key: staking.GetValidatorKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(val)},
cmn.KVPair{Key: staking.LastValidatorPowerKey, Value: valAddr1.Bytes()},
@ -204,7 +204,6 @@ func TestDecodeStakingStore(t *testing.T) {
name string
expectedLog string
}{
{"Pool", fmt.Sprintf("%v\n%v", pool, pool)},
{"LastTotalPower", fmt.Sprintf("%v\n%v", sdk.OneInt(), sdk.OneInt())},
{"Validator", fmt.Sprintf("%v\n%v", val, val)},
{"LastValidatorPower/ValidatorsByConsAddr/ValidatorsByPowerIndex", fmt.Sprintf("%v\n%v", valAddr1, valAddr1)},
@ -302,3 +301,33 @@ func TestDecodeGovStore(t *testing.T) {
})
}
}
func TestDecodeSupplyStore(t *testing.T) {
cdc := makeTestCodec()
totalSupply := supply.NewSupply(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000)))
kvPairs := cmn.KVPairs{
cmn.KVPair{Key: supply.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(totalSupply)},
cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}},
}
tests := []struct {
name string
expectedLog string
}{
{"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply)},
{"other", ""},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
switch i {
case len(tests) - 1:
require.Panics(t, func() { decodeSupplyStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name)
default:
require.Equal(t, tt.expectedLog, decodeSupplyStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name)
}
})
}
}

View File

@ -12,7 +12,7 @@ import (
const (
ModuleName = types.ModuleName
StoreKey = types.StoreKey
FeeStoreKey = types.FeeStoreKey
FeeCollectorName = types.FeeCollectorName
QuerierRoute = types.QuerierRoute
DefaultParamspace = types.DefaultParamspace
DefaultMaxMemoCharacters = types.DefaultMaxMemoCharacters
@ -34,8 +34,6 @@ var (
NewDelayedVestingAccountRaw = types.NewDelayedVestingAccountRaw
NewDelayedVestingAccount = types.NewDelayedVestingAccount
RegisterCodec = types.RegisterCodec
RegisterBaseAccount = types.RegisterBaseAccount
NewFeeCollectionKeeper = types.NewFeeCollectionKeeper
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
ValidateGenesis = types.ValidateGenesis
@ -43,6 +41,7 @@ var (
NewParams = types.NewParams
ParamKeyTable = types.ParamKeyTable
DefaultParams = types.DefaultParams
NewQueryAccountParams = types.NewQueryAccountParams
NewStdTx = types.NewStdTx
CountSubKeys = types.CountSubKeys
NewStdFee = types.NewStdFee
@ -72,9 +71,9 @@ type (
BaseVestingAccount = types.BaseVestingAccount
ContinuousVestingAccount = types.ContinuousVestingAccount
DelayedVestingAccount = types.DelayedVestingAccount
FeeCollectionKeeper = types.FeeCollectionKeeper
GenesisState = types.GenesisState
Params = types.Params
QueryAccountParams = types.QueryAccountParams
StdSignMsg = types.StdSignMsg
StdTx = types.StdTx
StdFee = types.StdFee

View File

@ -4,7 +4,6 @@ import (
"bytes"
"encoding/hex"
"fmt"
"time"
"github.com/tendermint/tendermint/crypto/ed25519"
@ -14,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
var (
@ -35,11 +35,15 @@ type SignatureVerificationGasConsumer = func(meter sdk.GasMeter, sig []byte, pub
// NewAnteHandler returns an AnteHandler that checks and increments sequence
// numbers, checks signatures & account numbers, and deducts fees from the first
// signer.
func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler {
func NewAnteHandler(ak AccountKeeper, supplyKeeper types.SupplyKeeper, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler {
return func(
ctx sdk.Context, tx sdk.Tx, simulate bool,
) (newCtx sdk.Context, res sdk.Result, abort bool) {
if addr := supplyKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.FeeCollectorName))
}
// all transactions must be of type auth.StdTx
stdTx, ok := tx.(StdTx)
if !ok {
@ -112,13 +116,15 @@ func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper, sigGasConsumer Si
return newCtx, res, true
}
// deduct the fees
if !stdTx.Fee.Amount.IsZero() {
signerAccs[0], res = DeductFees(ctx.BlockHeader().Time, signerAccs[0], stdTx.Fee)
res = DeductFees(supplyKeeper, newCtx, signerAccs[0], stdTx.Fee.Amount)
if !res.IsOK() {
return newCtx, res, true
}
fck.AddCollectedFees(newCtx, stdTx.Fee.Amount)
// reload the account as fees have been deducted
signerAccs[0] = ak.GetAccount(newCtx, signerAccs[0].GetAddress())
}
// stdSigs contains the sequence number, account number, and signatures.
@ -327,36 +333,37 @@ func consumeMultisignatureVerificationGas(meter sdk.GasMeter,
//
// NOTE: We could use the CoinKeeper (in addition to the AccountKeeper, because
// the CoinKeeper doesn't give us accounts), but it seems easier to do this.
func DeductFees(blockTime time.Time, acc Account, fee StdFee) (Account, sdk.Result) {
func DeductFees(supplyKeeper types.SupplyKeeper, ctx sdk.Context, acc Account, fees sdk.Coins) sdk.Result {
blockTime := ctx.BlockHeader().Time
coins := acc.GetCoins()
feeAmount := fee.Amount
if !feeAmount.IsValid() {
return nil, sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", feeAmount)).Result()
if !fees.IsValid() {
return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", fees)).Result()
}
// get the resulting coins deducting the fees
newCoins, ok := coins.SafeSub(feeAmount)
if ok {
return nil, sdk.ErrInsufficientFunds(
fmt.Sprintf("insufficient funds to pay for fees; %s < %s", coins, feeAmount),
// verify the account has enough funds to pay for fees
_, hasNeg := coins.SafeSub(fees)
if hasNeg {
return sdk.ErrInsufficientFunds(
fmt.Sprintf("insufficient funds to pay for fees; %s < %s", coins, fees),
).Result()
}
// Validate the account has enough "spendable" coins as this will cover cases
// such as vesting accounts.
spendableCoins := acc.SpendableCoins(blockTime)
if _, hasNeg := spendableCoins.SafeSub(feeAmount); hasNeg {
return nil, sdk.ErrInsufficientFunds(
fmt.Sprintf("insufficient funds to pay for fees; %s < %s", spendableCoins, feeAmount),
if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg {
return sdk.ErrInsufficientFunds(
fmt.Sprintf("insufficient funds to pay for fees; %s < %s", spendableCoins, fees),
).Result()
}
if err := acc.SetCoins(newCoins); err != nil {
return nil, sdk.ErrInternal(err.Error()).Result()
err := supplyKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
if err != nil {
return err.Result()
}
return acc, sdk.Result{}
return sdk.Result{}
}
// EnsureSufficientMempoolFees verifies that the given transaction has supplied

View File

@ -19,6 +19,7 @@ import (
// run the tx through the anteHandler and ensure its valid
func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) {
_, result, abort := anteHandler(ctx, tx, simulate)
require.Equal(t, "", result.Log)
require.False(t, abort)
require.Equal(t, sdk.CodeOK, result.Code)
require.True(t, result.IsOK())
@ -48,7 +49,7 @@ func TestAnteHandlerSigErrors(t *testing.T) {
// setup
input := setupTestInput()
ctx := input.ctx
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
// keys and addresses
priv1, _, addr1 := types.KeyTestPubAddr()
@ -96,7 +97,7 @@ func TestAnteHandlerSigErrors(t *testing.T) {
func TestAnteHandlerAccountNumbers(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -106,9 +107,11 @@ func TestAnteHandlerAccountNumbers(t *testing.T) {
// set the accounts
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(types.NewTestCoins())
require.NoError(t, acc1.SetAccountNumber(0))
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
// msg and signatures
@ -151,19 +154,20 @@ func TestAnteHandlerAccountNumbers(t *testing.T) {
func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(0)
// keys and addresses
priv1, _, addr1 := types.KeyTestPubAddr()
priv2, _, addr2 := types.KeyTestPubAddr()
// set the accounts
// set the accounts, we don't need the acc numbers as it is in the genesis block
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(types.NewTestCoins())
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
// msg and signatures
@ -206,7 +210,7 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) {
func TestAnteHandlerSequences(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -217,12 +221,15 @@ func TestAnteHandlerSequences(t *testing.T) {
// set the accounts
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(types.NewTestCoins())
require.NoError(t, acc1.SetAccountNumber(0))
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
acc3 := input.ak.NewAccountWithAddress(ctx, addr3)
acc3.SetCoins(types.NewTestCoins())
require.NoError(t, acc3.SetAccountNumber(2))
input.ak.SetAccount(ctx, acc3)
// msg and signatures
@ -281,7 +288,7 @@ func TestAnteHandlerFees(t *testing.T) {
// setup
input := setupTestInput()
ctx := input.ctx
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
// keys and addresses
priv1, _, addr1 := types.KeyTestPubAddr()
@ -305,23 +312,22 @@ func TestAnteHandlerFees(t *testing.T) {
input.ak.SetAccount(ctx, acc1)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInsufficientFunds)
emptyCoins := sdk.NewCoins()
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(emptyCoins))
require.True(t, input.ak.GetAccount(ctx, addr1).GetCoins().AmountOf("atom").Equal(sdk.NewInt(149)))
require.True(t, input.sk.GetModuleAccount(ctx, types.FeeCollectorName).GetCoins().Empty())
require.True(sdk.IntEq(t, input.ak.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), sdk.NewInt(149)))
acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150)))
input.ak.SetAccount(ctx, acc1)
checkValidTx(t, anteHandler, ctx, tx, false)
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("atom", 150))))
require.True(t, input.ak.GetAccount(ctx, addr1).GetCoins().AmountOf("atom").Equal(sdk.NewInt(0)))
require.True(sdk.IntEq(t, input.sk.GetModuleAccount(ctx, types.FeeCollectorName).GetCoins().AmountOf("atom"), sdk.NewInt(150)))
require.True(sdk.IntEq(t, input.ak.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), sdk.NewInt(0)))
}
// Test logic around memo gas consumption.
func TestAnteHandlerMemoGas(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -329,6 +335,7 @@ func TestAnteHandlerMemoGas(t *testing.T) {
// set the accounts
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
require.NoError(t, acc1.SetAccountNumber(0))
input.ak.SetAccount(ctx, acc1)
// msg and signatures
@ -360,7 +367,7 @@ func TestAnteHandlerMemoGas(t *testing.T) {
func TestAnteHandlerMultiSigner(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -371,12 +378,15 @@ func TestAnteHandlerMultiSigner(t *testing.T) {
// set the accounts
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(types.NewTestCoins())
require.NoError(t, acc1.SetAccountNumber(0))
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
acc3 := input.ak.NewAccountWithAddress(ctx, addr3)
acc3.SetCoins(types.NewTestCoins())
require.NoError(t, acc3.SetAccountNumber(2))
input.ak.SetAccount(ctx, acc3)
// set up msgs and fee
@ -407,7 +417,7 @@ func TestAnteHandlerMultiSigner(t *testing.T) {
func TestAnteHandlerBadSignBytes(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -417,9 +427,11 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
// set the accounts
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(types.NewTestCoins())
require.NoError(t, acc1.SetAccountNumber(0))
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
var tx sdk.Tx
@ -482,7 +494,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
func TestAnteHandlerSetPubKey(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -492,9 +504,11 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
// set the accounts
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(types.NewTestCoins())
require.NoError(t, acc1.SetAccountNumber(0))
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
var tx sdk.Tx
@ -676,7 +690,7 @@ func TestCountSubkeys(t *testing.T) {
func TestAnteHandlerSigLimitExceeded(t *testing.T) {
// setup
input := setupTestInput()
anteHandler := NewAnteHandler(input.ak, input.fck, DefaultSigVerificationGasConsumer)
anteHandler := NewAnteHandler(input.ak, input.sk, DefaultSigVerificationGasConsumer)
ctx := input.ctx.WithBlockHeight(1)
// keys and addresses
@ -695,6 +709,7 @@ func TestAnteHandlerSigLimitExceeded(t *testing.T) {
input.ak.SetAccount(ctx, acc1)
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(types.NewTestCoins())
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
var tx sdk.Tx
@ -765,7 +780,7 @@ func TestCustomSignatureVerificationGasConsumer(t *testing.T) {
// setup
input := setupTestInput()
// setup an ante handler that only accepts PubKeyEd25519
anteHandler := NewAnteHandler(input.ak, input.fck, func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result {
anteHandler := NewAnteHandler(input.ak, input.sk, func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result {
switch pubkey := pubkey.(type) {
case ed25519.PubKeyEd25519:
meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
@ -781,6 +796,7 @@ func TestCustomSignatureVerificationGasConsumer(t *testing.T) {
acc1 := input.ak.NewAccountWithAddress(ctx, addr1)
_ = acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150)))
input.ak.SetAccount(ctx, acc1)
var tx sdk.Tx
msg := types.NewTestMsg(addr1)
privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0}
@ -794,7 +810,8 @@ func TestCustomSignatureVerificationGasConsumer(t *testing.T) {
pub2 := priv2.PubKey()
addr2 := sdk.AccAddress(pub2.Address())
acc2 := input.ak.NewAccountWithAddress(ctx, addr2)
_ = acc2.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150)))
require.NoError(t, acc2.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150))))
require.NoError(t, acc2.SetAccountNumber(1))
input.ak.SetAccount(ctx, acc2)
msg = types.NewTestMsg(addr2)
privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{1}, []uint64{0}

View File

@ -2,19 +2,18 @@ package auth
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
// InitGenesis - Init store state from genesis data
func InitGenesis(ctx sdk.Context, ak AccountKeeper, fck types.FeeCollectionKeeper, data types.GenesisState) {
//
// CONTRACT: old coins from the FeeCollectionKeeper need to be transferred through
// a genesis port script to the new fee collector account
func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) {
ak.SetParams(ctx, data.Params)
fck.AddCollectedFees(ctx, data.CollectedFees)
}
// ExportGenesis returns a GenesisState for a given context and keeper
func ExportGenesis(ctx sdk.Context, ak AccountKeeper, fck types.FeeCollectionKeeper) types.GenesisState {
collectedFees := fck.GetCollectedFees(ctx)
func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState {
params := ak.GetParams(ctx)
return types.NewGenesisState(collectedFees, params)
return NewGenesisState(params)
}

View File

@ -68,17 +68,14 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
// app module object
type AppModule struct {
AppModuleBasic
accountKeeper AccountKeeper
feeCollectionKeeper types.FeeCollectionKeeper
accountKeeper AccountKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(accountKeeper AccountKeeper,
feeCollectionKeeper types.FeeCollectionKeeper) AppModule {
func NewAppModule(accountKeeper AccountKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
accountKeeper: accountKeeper,
feeCollectionKeeper: feeCollectionKeeper,
AppModuleBasic: AppModuleBasic{},
accountKeeper: accountKeeper,
}
}
@ -108,15 +105,15 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
// module init-genesis
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState types.GenesisState
var genesisState GenesisState
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.accountKeeper, am.feeCollectionKeeper, genesisState)
InitGenesis(ctx, am.accountKeeper, genesisState)
return []abci.ValidatorUpdate{}
}
// module export genesis
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
gs := ExportGenesis(ctx, am.accountKeeper, am.feeCollectionKeeper)
gs := ExportGenesis(ctx, am.accountKeeper)
return types.ModuleCdc.MustMarshalJSON(gs)
}

View File

@ -2,25 +2,32 @@ 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/auth/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
// SimulateDeductFee
func SimulateDeductFee(m auth.AccountKeeper, f auth.FeeCollectionKeeper) simulation.Operation {
func SimulateDeductFee(ak auth.AccountKeeper, supplyKeeper types.SupplyKeeper) simulation.Operation {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simulation.Account) (
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
account := simulation.RandomAcc(r, accs)
stored := m.GetAccount(ctx, account.Address)
stored := ak.GetAccount(ctx, account.Address)
initCoins := stored.GetCoins()
opMsg = simulation.NewOperationMsgBasic(auth.ModuleName, "deduct_fee", "", false, nil)
opMsg = simulation.NewOperationMsgBasic(types.ModuleName, "deduct_fee", "", false, nil)
feeCollector := ak.GetAccount(ctx, supplyKeeper.GetModuleAddress(types.FeeCollectorName))
if feeCollector == nil {
panic(fmt.Errorf("fee collector account hasn't been set"))
}
if len(initCoins) == 0 {
return opMsg, nil, nil
@ -36,25 +43,23 @@ func SimulateDeductFee(m auth.AccountKeeper, f auth.FeeCollectionKeeper) simulat
// Create a random fee and verify the fees are within the account's spendable
// balance.
fees := sdk.Coins{sdk.NewCoin(randCoin.Denom, amt)}
fees := sdk.NewCoins(sdk.NewCoin(randCoin.Denom, amt))
spendableCoins := stored.SpendableCoins(ctx.BlockHeader().Time)
if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg {
return opMsg, nil, nil
}
// get the new account balance
newCoins, hasNeg := initCoins.SafeSub(fees)
_, hasNeg := initCoins.SafeSub(fees)
if hasNeg {
return opMsg, nil, nil
}
if err := stored.SetCoins(newCoins); err != nil {
err = supplyKeeper.SendCoinsFromAccountToModule(ctx, stored.GetAddress(), types.FeeCollectorName, fees)
if err != nil {
panic(err)
}
m.SetAccount(ctx, stored)
f.AddCollectedFees(ctx, fees)
opMsg.OK = true
return opMsg, nil, nil
}

View File

@ -11,39 +11,104 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/params/subspace"
"github.com/cosmos/cosmos-sdk/x/supply/exported"
supplytypes "github.com/cosmos/cosmos-sdk/x/supply/types"
)
type testInput struct {
cdc *codec.Codec
ctx sdk.Context
ak AccountKeeper
fck types.FeeCollectionKeeper
sk types.SupplyKeeper
}
func setupTestInput() testInput {
db := dbm.NewMemDB()
cdc := codec.New()
types.RegisterBaseAccount(cdc)
types.RegisterCodec(cdc)
supplytypes.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
authCapKey := sdk.NewKVStoreKey("authCapKey")
fckCapKey := sdk.NewKVStoreKey("fckCapKey")
keyParams := sdk.NewKVStoreKey("subspace")
tkeyParams := sdk.NewTransientStoreKey("transient_subspace")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(fckCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
ms.LoadLatestVersion()
ps := subspace.NewSubspace(cdc, keyParams, tkeyParams, types.DefaultParamspace)
ak := NewAccountKeeper(cdc, authCapKey, ps, types.ProtoBaseAccount)
fck := types.NewFeeCollectionKeeper(cdc, fckCapKey)
sk := NewDummySupplyKeeper(ak)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id"}, false, log.NewNopLogger())
ak.SetParams(ctx, types.DefaultParams())
return testInput{cdc: cdc, ctx: ctx, ak: ak, fck: fck}
return testInput{cdc: cdc, ctx: ctx, ak: ak, sk: sk}
}
// DummySupplyKeeper defines a supply keeper used only for testing to avoid
// circle dependencies
type DummySupplyKeeper struct {
ak AccountKeeper
}
// NewDummySupplyKeeper creates a DummySupplyKeeper instance
func NewDummySupplyKeeper(ak AccountKeeper) DummySupplyKeeper {
return DummySupplyKeeper{ak}
}
// SendCoinsFromAccountToModule for the dummy supply keeper
func (sk DummySupplyKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, fromAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error {
fromAcc := sk.ak.GetAccount(ctx, fromAddr)
moduleAcc := sk.GetModuleAccount(ctx, recipientModule)
newFromCoins, hasNeg := fromAcc.GetCoins().SafeSub(amt)
if hasNeg {
return sdk.ErrInsufficientCoins(fromAcc.GetCoins().String())
}
newToCoins := moduleAcc.GetCoins().Add(amt)
if err := fromAcc.SetCoins(newFromCoins); err != nil {
return sdk.ErrInternal(err.Error())
}
if err := moduleAcc.SetCoins(newToCoins); err != nil {
return sdk.ErrInternal(err.Error())
}
sk.ak.SetAccount(ctx, fromAcc)
sk.ak.SetAccount(ctx, moduleAcc)
return nil
}
// GetModuleAccount for dummy supply keeper
func (sk DummySupplyKeeper) GetModuleAccount(ctx sdk.Context, moduleName string) exported.ModuleAccountI {
addr := sk.GetModuleAddress(moduleName)
acc := sk.ak.GetAccount(ctx, addr)
if acc != nil {
macc, ok := acc.(exported.ModuleAccountI)
if ok {
return macc
}
}
// create a new module account
macc := supplytypes.NewEmptyModuleAccount(moduleName, "basic")
maccI := (sk.ak.NewAccount(ctx, macc)).(exported.ModuleAccountI)
sk.ak.SetAccount(ctx, maccI)
return maccI
}
// GetModuleAddress for dummy supply keeper
func (sk DummySupplyKeeper) GetModuleAddress(moduleName string) sdk.AccAddress {
return supplytypes.NewModuleAddress(moduleName)
}

View File

@ -8,23 +8,12 @@ import (
// RegisterCodec registers concrete types on the codec
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterInterface((*exported.Account)(nil), nil)
cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil)
cdc.RegisterInterface((*exported.VestingAccount)(nil), nil)
cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", nil)
cdc.RegisterConcrete(&BaseVestingAccount{}, "auth/BaseVestingAccount", nil)
cdc.RegisterConcrete(&ContinuousVestingAccount{}, "auth/ContinuousVestingAccount", nil)
cdc.RegisterConcrete(&DelayedVestingAccount{}, "auth/DelayedVestingAccount", nil)
cdc.RegisterConcrete(StdTx{}, "auth/StdTx", nil)
}
// RegisterBaseAccount most users shouldn't use this, but this comes in handy for tests.
func RegisterBaseAccount(cdc *codec.Codec) {
cdc.RegisterInterface((*exported.Account)(nil), nil)
cdc.RegisterInterface((*exported.VestingAccount)(nil), nil)
cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/BaseAccount", nil)
cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil)
cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil)
cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil)
codec.RegisterCrypto(cdc)
cdc.RegisterConcrete(StdTx{}, "cosmos-sdk/StdTx", nil)
}
// module wide codec

View File

@ -0,0 +1,13 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// SupplyKeeper defines the expected supply Keeper (noalias)
type SupplyKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error
GetModuleAccount(ctx sdk.Context, moduleName string) exported.ModuleAccountI
GetModuleAddress(moduleName string) sdk.AccAddress
}

View File

@ -1,62 +0,0 @@
package types
import (
codec "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
collectedFeesKey = []byte("collectedFees")
)
// FeeCollectionKeeper handles collection of fees in the anteHandler
// and setting of MinFees for different fee tokens
type FeeCollectionKeeper struct {
// The (unexposed) key used to access the fee store from the Context.
key sdk.StoreKey
// The codec codec for binary encoding/decoding of accounts.
cdc *codec.Codec
}
// NewFeeCollectionKeeper returns a new FeeCollectionKeeper
func NewFeeCollectionKeeper(cdc *codec.Codec, key sdk.StoreKey) FeeCollectionKeeper {
return FeeCollectionKeeper{
key: key,
cdc: cdc,
}
}
// GetCollectedFees - retrieves the collected fee pool
func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins {
store := ctx.KVStore(fck.key)
bz := store.Get(collectedFeesKey)
if bz == nil {
return sdk.NewCoins()
}
emptyFees := sdk.NewCoins()
feePool := &emptyFees
fck.cdc.MustUnmarshalBinaryLengthPrefixed(bz, feePool)
return *feePool
}
func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) {
bz := fck.cdc.MustMarshalBinaryLengthPrefixed(coins)
store := ctx.KVStore(fck.key)
store.Set(collectedFeesKey, bz)
}
// AddCollectedFees - add to the fee pool
func (fck FeeCollectionKeeper) AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins {
newCoins := fck.GetCollectedFees(ctx).Add(coins)
fck.setCollectedFees(ctx, newCoins)
return newCoins
}
// ClearCollectedFees - clear the fee pool
func (fck FeeCollectionKeeper) ClearCollectedFees(ctx sdk.Context) {
fck.setCollectedFees(ctx, sdk.NewCoins())
}

View File

@ -1,59 +0,0 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
emptyCoins = sdk.NewCoins()
oneCoin = sdk.NewCoins(sdk.NewInt64Coin("foocoin", 1))
twoCoins = sdk.NewCoins(sdk.NewInt64Coin("foocoin", 2))
)
func TestFeeCollectionKeeperGetSet(t *testing.T) {
input := setupTestInput()
ctx := input.ctx
// no coins initially
currFees := input.fck.GetCollectedFees(ctx)
require.True(t, currFees.IsEqual(emptyCoins))
// set feeCollection to oneCoin
input.fck.setCollectedFees(ctx, oneCoin)
// check that it is equal to oneCoin
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(oneCoin))
}
func TestFeeCollectionKeeperAdd(t *testing.T) {
input := setupTestInput()
ctx := input.ctx
// no coins initially
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(emptyCoins))
// add oneCoin and check that pool is now oneCoin
input.fck.AddCollectedFees(ctx, oneCoin)
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(oneCoin))
// add oneCoin again and check that pool is now twoCoins
input.fck.AddCollectedFees(ctx, oneCoin)
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(twoCoins))
}
func TestFeeCollectionKeeperClear(t *testing.T) {
input := setupTestInput()
ctx := input.ctx
// set coins initially
input.fck.setCollectedFees(ctx, twoCoins)
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(twoCoins))
// clear fees and see that pool is now empty
input.fck.ClearCollectedFees(ctx)
require.True(t, input.fck.GetCollectedFees(ctx).IsEqual(emptyCoins))
}

View File

@ -2,27 +2,21 @@ package types
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - all auth state that must be provided at genesis
type GenesisState struct {
CollectedFees sdk.Coins `json:"collected_fees"`
Params Params `json:"params"`
Params Params `json:"params"`
}
// NewGenesisState - Create a new genesis state
func NewGenesisState(collectedFees sdk.Coins, params Params) GenesisState {
return GenesisState{
CollectedFees: collectedFees,
Params: params,
}
func NewGenesisState(params Params) GenesisState {
return GenesisState{params}
}
// DefaultGenesisState - Return a default genesis state
func DefaultGenesisState() GenesisState {
return NewGenesisState(sdk.NewCoins(), DefaultParams())
return NewGenesisState(DefaultParams())
}
// ValidateGenesis performs basic validation of auth genesis data returning an

View File

@ -1,6 +1,8 @@
package types
import sdk "github.com/cosmos/cosmos-sdk/types"
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
// module name
@ -9,8 +11,8 @@ const (
// StoreKey is string representation of the store key for auth
StoreKey = "acc"
// FeeStoreKey is a string representation of the store key for fees
FeeStoreKey = "fee"
// FeeCollectorName the root string for the fee collector account address
FeeCollectorName = "FeeCollector"
// QuerierRoute is the querier route for acc
QuerierRoute = StoreKey

View File

@ -2,49 +2,12 @@
package types
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/secp256k1"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
)
//DONTCOVER
type testInput struct {
cdc *codec.Codec
ctx sdk.Context
fck FeeCollectionKeeper
}
func setupTestInput() testInput {
db := dbm.NewMemDB()
cdc := codec.New()
RegisterBaseAccount(cdc)
authCapKey := sdk.NewKVStoreKey("authCapKey")
fckCapKey := sdk.NewKVStoreKey("fckCapKey")
keyParams := sdk.NewKVStoreKey("subspace")
tkeyParams := sdk.NewTransientStoreKey("transient_subspace")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(fckCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
ms.LoadLatestVersion()
fck := NewFeeCollectionKeeper(cdc, fckCapKey)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id"}, false, log.NewNopLogger())
return testInput{cdc: cdc, ctx: ctx, fck: fck}
}
func NewTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg {
return sdk.NewTestMsg(addrs...)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank/internal/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
"github.com/cosmos/cosmos-sdk/x/mock"
supplytypes "github.com/cosmos/cosmos-sdk/x/supply/types"
"github.com/stretchr/testify/require"
@ -92,6 +93,7 @@ var (
// initialize the mock application for this module
func getMockApp(t *testing.T) *mock.App {
mapp, err := getBenchmarkMockApp()
supplytypes.RegisterCodec(mapp.Cdc)
require.NoError(t, err)
return mapp
}
@ -274,12 +276,14 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) {
func TestMsgMultiSendDependent(t *testing.T) {
mapp := getMockApp(t)
acc1 := &auth.BaseAccount{
Address: addr1,
Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)},
}
acc1 := auth.NewBaseAccountWithAddress(addr1)
acc2 := auth.NewBaseAccountWithAddress(addr2)
err := acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))
require.NoError(t, err)
err = acc2.SetAccountNumber(1)
require.NoError(t, err)
mock.SetGenesis(mapp, []auth.Account{acc1})
mock.SetGenesis(mapp, []auth.Account{&acc1, &acc2})
testCases := []appTestCase{
{

View File

@ -24,8 +24,8 @@ type Keeper interface {
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error)
InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) sdk.Error
DelegateCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error
UndelegateCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error
DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
}
// BaseKeeper manages transfers between accounts. It implements the Keeper interface.
@ -53,10 +53,6 @@ func NewBaseKeeper(ak types.AccountKeeper,
func (keeper BaseKeeper) SetCoins(
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
) sdk.Error {
if !amt.IsValid() {
return sdk.ErrInvalidCoins(amt.String())
}
return setCoins(ctx, keeper.ak, addr, amt)
}
@ -64,10 +60,6 @@ func (keeper BaseKeeper) SetCoins(
func (keeper BaseKeeper) SubtractCoins(
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
) (sdk.Coins, sdk.Error) {
if !amt.IsValid() {
return nil, sdk.ErrInvalidCoins(amt.String())
}
return subtractCoins(ctx, keeper.ak, addr, amt)
}
@ -75,10 +67,6 @@ func (keeper BaseKeeper) SubtractCoins(
func (keeper BaseKeeper) AddCoins(
ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins,
) (sdk.Coins, sdk.Error) {
if !amt.IsValid() {
return nil, sdk.ErrInvalidCoins(amt.String())
}
return addCoins(ctx, keeper.ak, addr, amt)
}
@ -90,22 +78,95 @@ func (keeper BaseKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input,
// DelegateCoins performs delegation by deducting amt coins from an account with
// address addr. For vesting accounts, delegations amounts are tracked for both
// vesting and vested coins.
func (keeper BaseKeeper) DelegateCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
// The coins are then transferred from the delegator address to a ModuleAccount address.
// If any of the delegation amounts are negative, an error is returned.
func (keeper BaseKeeper) DelegateCoins(
ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins,
) sdk.Error {
delegatorAcc := getAccount(ctx, keeper.ak, delegatorAddr)
if delegatorAcc == nil {
return sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", delegatorAddr))
}
moduleAcc := getAccount(ctx, keeper.ak, moduleAccAddr)
if moduleAcc == nil {
return sdk.ErrUnknownAddress(fmt.Sprintf("module account %s does not exist", moduleAccAddr))
}
if !amt.IsValid() {
return sdk.ErrInvalidCoins(amt.String())
}
return delegateCoins(ctx, keeper.ak, addr, amt)
oldCoins := delegatorAcc.GetCoins()
_, hasNeg := oldCoins.SafeSub(amt)
if hasNeg {
return sdk.ErrInsufficientCoins(
fmt.Sprintf("insufficient account funds; %s < %s", oldCoins, amt),
)
}
// TODO: return error on account.TrackDelegation
if err := trackDelegation(delegatorAcc, ctx.BlockHeader().Time, amt); err != nil {
return sdk.ErrInternal(fmt.Sprintf("failed to track delegation: %v", err))
}
setAccount(ctx, keeper.ak, delegatorAcc)
_, err := addCoins(ctx, keeper.ak, moduleAccAddr, amt)
if err != nil {
return err
}
return nil
}
// UndelegateCoins performs undelegation by crediting amt coins to an account with
// address addr. For vesting accounts, undelegation amounts are tracked for both
// vesting and vested coins.
// The coins are then transferred from a ModuleAccount address to the delegator address.
// If any of the undelegation amounts are negative, an error is returned.
func (keeper BaseKeeper) UndelegateCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
func (keeper BaseKeeper) UndelegateCoins(
ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins,
) sdk.Error {
delegatorAcc := getAccount(ctx, keeper.ak, delegatorAddr)
if delegatorAcc == nil {
return sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", delegatorAddr))
}
moduleAcc := getAccount(ctx, keeper.ak, moduleAccAddr)
if moduleAcc == nil {
return sdk.ErrUnknownAddress(fmt.Sprintf("module account %s does not exist", moduleAccAddr))
}
if !amt.IsValid() {
return sdk.ErrInvalidCoins(amt.String())
}
return undelegateCoins(ctx, keeper.ak, addr, amt)
oldCoins := moduleAcc.GetCoins()
newCoins, hasNeg := oldCoins.SafeSub(amt)
if hasNeg {
return sdk.ErrInsufficientCoins(
fmt.Sprintf("insufficient account funds; %s < %s", oldCoins, amt),
)
}
err := setCoins(ctx, keeper.ak, moduleAccAddr, newCoins)
if err != nil {
return err
}
// TODO: return error on account.TrackUndelegation
if err := trackUndelegation(delegatorAcc, amt); err != nil {
return sdk.ErrInternal(fmt.Sprintf("failed to track undelegation: %v", err))
}
setAccount(ctx, keeper.ak, delegatorAcc)
return nil
}
// SendKeeper defines a module interface that facilitates the transfer of coins
@ -141,6 +202,7 @@ func NewBaseSendKeeper(ak types.AccountKeeper,
}
}
// TODO combine with sendCoins
// SendCoins moves coins from one account to another
func (keeper BaseSendKeeper) SendCoins(
ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins,
@ -199,6 +261,11 @@ func NewBaseViewKeeper(ak types.AccountKeeper, codespace sdk.CodespaceType) Base
return BaseViewKeeper{ak: ak, codespace: codespace}
}
// Logger returns a module-specific logger.
func (keeper BaseViewKeeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// GetCoins returns the coins at the addr.
func (keeper BaseViewKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
return getCoins(ctx, keeper.ak, addr)
@ -214,13 +281,9 @@ func (keeper BaseViewKeeper) Codespace() sdk.CodespaceType {
return keeper.codespace
}
// Logger returns a module-specific logger.
func (keeper BaseViewKeeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
func getCoins(ctx sdk.Context, ak types.AccountKeeper, addr sdk.AccAddress) sdk.Coins {
acc := ak.GetAccount(ctx, addr)
// getCoins returns the account coins
func getCoins(ctx sdk.Context, am types.AccountKeeper, addr sdk.AccAddress) sdk.Coins {
acc := am.GetAccount(ctx, addr)
if acc == nil {
return sdk.NewCoins()
}
@ -252,10 +315,12 @@ func hasCoins(ctx sdk.Context, ak types.AccountKeeper, addr sdk.AccAddress, amt
return getCoins(ctx, ak, addr).IsAllGTE(amt)
}
// getAccount implements AccountKeeper
func getAccount(ctx sdk.Context, ak types.AccountKeeper, addr sdk.AccAddress) exported.Account {
return ak.GetAccount(ctx, addr)
}
// setAccount implements AccountKeeper
func setAccount(ctx sdk.Context, ak types.AccountKeeper, acc exported.Account) {
ak.SetAccount(ctx, acc)
}
@ -313,11 +378,7 @@ func addCoins(ctx sdk.Context, ak types.AccountKeeper, addr sdk.AccAddress, amt
// SendCoins moves coins from one account to another
// Returns ErrInvalidCoins if amt is invalid.
func sendCoins(ctx sdk.Context, ak types.AccountKeeper, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error {
// Safety check ensuring that when sending coins the keeper must maintain the
if !amt.IsValid() {
return sdk.ErrInvalidCoins(amt.String())
}
func sendCoins(ctx sdk.Context, ak types.AccountKeeper, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error {
_, err := subtractCoins(ctx, ak, fromAddr, amt)
if err != nil {
@ -372,51 +433,6 @@ func inputOutputCoins(ctx sdk.Context, ak types.AccountKeeper, inputs []types.In
return nil
}
func delegateCoins(ctx sdk.Context, ak types.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
if !amt.IsValid() {
return sdk.ErrInvalidCoins(amt.String())
}
acc := getAccount(ctx, ak, addr)
if acc == nil {
return sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", addr))
}
oldCoins := acc.GetCoins()
_, hasNeg := oldCoins.SafeSub(amt)
if hasNeg {
return sdk.ErrInsufficientCoins(
fmt.Sprintf("insufficient account funds; %s < %s", oldCoins, amt),
)
}
if err := trackDelegation(acc, ctx.BlockHeader().Time, amt); err != nil {
return sdk.ErrInternal(fmt.Sprintf("failed to track delegation: %v", err))
}
setAccount(ctx, ak, acc)
return nil
}
func undelegateCoins(ctx sdk.Context, ak types.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) sdk.Error {
if !amt.IsValid() {
return sdk.ErrInvalidCoins(amt.String())
}
acc := getAccount(ctx, ak, addr)
if acc == nil {
return sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", addr))
}
if err := trackUndelegation(acc, amt); err != nil {
return sdk.ErrInternal(fmt.Sprintf("failed to track undelegation: %v", err))
}
setAccount(ctx, ak, acc)
return nil
}
// CONTRACT: assumes that amt is valid.
func trackDelegation(acc exported.Account, blockTime time.Time, amt sdk.Coins) error {
vacc, ok := acc.(exported.VestingAccount)

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
@ -30,16 +31,15 @@ func setupTestInput() testInput {
db := dbm.NewMemDB()
cdc := codec.New()
auth.RegisterBaseAccount(cdc)
auth.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
authCapKey := sdk.NewKVStoreKey("authCapKey")
fckCapKey := sdk.NewKVStoreKey("fckCapKey")
keyParams := sdk.NewKVStoreKey("params")
tkeyParams := sdk.NewTransientStoreKey("transient_params")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(fckCapKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
ms.LoadLatestVersion()
@ -283,25 +283,30 @@ func TestDelegateCoins(t *testing.T) {
addr1 := sdk.AccAddress([]byte("addr1"))
addr2 := sdk.AccAddress([]byte("addr2"))
addrModule := sdk.AccAddress([]byte("moduleAcc"))
bacc := auth.NewBaseAccountWithAddress(addr1)
bacc.SetCoins(origCoins)
macc := input.ak.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing
vacc := auth.NewContinuousVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), endTime.Unix())
acc := input.ak.NewAccountWithAddress(ctx, addr2)
input.ak.SetAccount(ctx, vacc)
input.ak.SetAccount(ctx, acc)
input.ak.SetAccount(ctx, macc)
input.k.SetCoins(ctx, addr2, origCoins)
ctx = ctx.WithBlockTime(now.Add(12 * time.Hour))
// require the ability for a non-vesting account to delegate
err := input.k.DelegateCoins(ctx, addr2, delCoins)
err := input.k.DelegateCoins(ctx, addr2, addrModule, delCoins)
acc = input.ak.GetAccount(ctx, addr2)
macc = input.ak.GetAccount(ctx, addrModule)
require.NoError(t, err)
require.Equal(t, delCoins, acc.GetCoins())
require.Equal(t, origCoins.Sub(delCoins), acc.GetCoins())
require.Equal(t, delCoins, macc.GetCoins())
// require the ability for a vesting account to delegate
err = input.k.DelegateCoins(ctx, addr1, delCoins)
err = input.k.DelegateCoins(ctx, addr1, addrModule, delCoins)
vacc = input.ak.GetAccount(ctx, addr1).(*auth.ContinuousVestingAccount)
require.NoError(t, err)
require.Equal(t, delCoins, vacc.GetCoins())
@ -318,36 +323,53 @@ func TestUndelegateCoins(t *testing.T) {
addr1 := sdk.AccAddress([]byte("addr1"))
addr2 := sdk.AccAddress([]byte("addr2"))
addrModule := sdk.AccAddress([]byte("moduleAcc"))
bacc := auth.NewBaseAccountWithAddress(addr1)
bacc.SetCoins(origCoins)
macc := input.ak.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing
vacc := auth.NewContinuousVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), endTime.Unix())
acc := input.ak.NewAccountWithAddress(ctx, addr2)
input.ak.SetAccount(ctx, vacc)
input.ak.SetAccount(ctx, acc)
input.ak.SetAccount(ctx, macc)
input.k.SetCoins(ctx, addr2, origCoins)
ctx = ctx.WithBlockTime(now.Add(12 * time.Hour))
// require the ability for a non-vesting account to delegate
err := input.k.DelegateCoins(ctx, addr2, delCoins)
require.NoError(t, err)
// require the ability for a non-vesting account to undelegate
err = input.k.UndelegateCoins(ctx, addr2, delCoins)
err := input.k.DelegateCoins(ctx, addr2, addrModule, delCoins)
require.NoError(t, err)
acc = input.ak.GetAccount(ctx, addr2)
require.Equal(t, origCoins, acc.GetCoins())
macc = input.ak.GetAccount(ctx, addrModule)
require.Equal(t, origCoins.Sub(delCoins), acc.GetCoins())
require.Equal(t, delCoins, macc.GetCoins())
// require the ability for a vesting account to delegate
err = input.k.DelegateCoins(ctx, addr1, delCoins)
// require the ability for a non-vesting account to undelegate
err = input.k.UndelegateCoins(ctx, addrModule, addr2, delCoins)
require.NoError(t, err)
// require the ability for a vesting account to undelegate
err = input.k.UndelegateCoins(ctx, addr1, delCoins)
acc = input.ak.GetAccount(ctx, addr2)
macc = input.ak.GetAccount(ctx, addrModule)
require.Equal(t, origCoins, acc.GetCoins())
require.True(t, macc.GetCoins().Empty())
// require the ability for a vesting account to delegate
err = input.k.DelegateCoins(ctx, addr1, addrModule, delCoins)
require.NoError(t, err)
vacc = input.ak.GetAccount(ctx, addr1).(*auth.ContinuousVestingAccount)
macc = input.ak.GetAccount(ctx, addrModule)
require.Equal(t, origCoins.Sub(delCoins), vacc.GetCoins())
require.Equal(t, delCoins, macc.GetCoins())
// require the ability for a vesting account to undelegate
err = input.k.UndelegateCoins(ctx, addrModule, addr1, delCoins)
require.NoError(t, err)
vacc = input.ak.GetAccount(ctx, addr1).(*auth.ContinuousVestingAccount)
macc = input.ak.GetAccount(ctx, addrModule)
require.Equal(t, origCoins, vacc.GetCoins())
require.True(t, macc.GetCoins().Empty())
}

View File

@ -11,6 +11,7 @@ import (
)
const (
// query balance path
QueryBalance = "balances"
)

View File

@ -1,20 +0,0 @@
package crisis
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// expected bank keeper
type DistrKeeper interface {
DistributeFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) sdk.Error
}
// expected fee collection keeper
type FeeCollectionKeeper interface {
AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins
}
// expected bank keeper
type BankKeeper interface {
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error)
}

View File

@ -29,13 +29,11 @@ func handleMsgVerifyInvariant(ctx sdk.Context, msg types.MsgVerifyInvariant, k K
// remove the constant fee
constantFee := sdk.NewCoins(k.GetConstantFee(ctx))
_, err := k.bankKeeper.SubtractCoins(ctx, msg.Sender, constantFee)
err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, msg.Sender, k.feeCollectorName, constantFee)
if err != nil {
return err.Result()
}
_ = k.feeCollectionKeeper.AddCollectedFees(ctx, constantFee)
// use a cached context to avoid gas costs during invariants
cacheCtx, _ := ctx.CacheContext()
@ -62,7 +60,7 @@ func handleMsgVerifyInvariant(ctx sdk.Context, msg types.MsgVerifyInvariant, k K
// TODO uncomment the following code block with implementation of the circuit breaker
//// refund constant fee
//err := k.distrKeeper.DistributeFeePool(ctx, constantFee, msg.Sender)
//err := k.distrKeeper.DistributeFromFeePool(ctx, constantFee, msg.Sender)
//if err != nil {
//// if there are insufficient coins to refund, log the error,
//// but still halt the chain.

View File

@ -24,11 +24,11 @@ var (
func CreateTestInput(t *testing.T) (sdk.Context, Keeper, auth.AccountKeeper, distr.Keeper) {
communityTax := sdk.NewDecWithPrec(2, 2)
ctx, accKeeper, bankKeeper, distrKeeper, _, feeCollectionKeeper, paramsKeeper :=
ctx, accKeeper, _, distrKeeper, _, paramsKeeper, supplyKeeper :=
distr.CreateTestInputAdvanced(t, false, 10, communityTax)
paramSpace := paramsKeeper.Subspace(DefaultParamspace)
crisisKeeper := NewKeeper(paramSpace, 1, distrKeeper, bankKeeper, feeCollectionKeeper)
crisisKeeper := NewKeeper(paramSpace, 1, supplyKeeper, auth.FeeCollectorName)
constantFee := sdk.NewInt64Coin("stake", 10000000)
crisisKeeper.SetConstantFee(ctx, constantFee)

View File

@ -17,23 +17,21 @@ type Keeper struct {
paramSpace params.Subspace
invCheckPeriod uint
distrKeeper DistrKeeper
bankKeeper BankKeeper
feeCollectionKeeper FeeCollectionKeeper
supplyKeeper types.SupplyKeeper
feeCollectorName string // name of the FeeCollector ModuleAccount
}
// NewKeeper creates a new Keeper object
func NewKeeper(paramSpace params.Subspace, invCheckPeriod uint,
distrKeeper DistrKeeper, bankKeeper BankKeeper,
feeCollectionKeeper FeeCollectionKeeper) Keeper {
supplyKeeper types.SupplyKeeper, feeCollectorName string) Keeper {
return Keeper{
routes: []types.InvarRoute{},
paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()),
invCheckPeriod: invCheckPeriod,
distrKeeper: distrKeeper,
bankKeeper: bankKeeper,
feeCollectionKeeper: feeCollectionKeeper,
routes: []types.InvarRoute{},
paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()),
invCheckPeriod: invCheckPeriod,
supplyKeeper: supplyKeeper,
feeCollectorName: feeCollectorName,
}
}
@ -42,7 +40,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// register routes for the
// RegisterRoute register the routes for each of the invariants
func (k *Keeper) RegisterRoute(moduleName, route string, invar sdk.Invariant) {
invarRoute := types.NewInvarRoute(moduleName, route, invar)
k.routes = append(k.routes, invarRoute)

View File

@ -13,6 +13,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/crisis/client/cli"
"github.com/cosmos/cosmos-sdk/x/crisis/types"
)
var (
@ -37,13 +38,16 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
// default genesis state
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
return ModuleCdc.MustMarshalJSON(DefaultGenesisState())
return types.ModuleCdc.MustMarshalJSON(DefaultGenesisState())
}
// module validate genesis
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
// TODO
return nil
var data types.GenesisState
if err := types.ModuleCdc.UnmarshalJSON(bz, &data); err != nil {
return err
}
return types.ValidateGenesis(data)
}
// register rest routes
@ -99,7 +103,7 @@ func (AppModule) NewQuerierHandler() sdk.Querier { return nil }
// module init-genesis
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState GenesisState
ModuleCdc.MustUnmarshalJSON(data, &genesisState)
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, genesisState)
am.keeper.AssertInvariants(ctx)
@ -109,7 +113,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va
// module export genesis
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return ModuleCdc.MustMarshalJSON(gs)
return types.ModuleCdc.MustMarshalJSON(gs)
}
// module begin-block

View File

@ -0,0 +1,10 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// SupplyKeeper defines the expected supply keeper (noalias)
type SupplyKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error
}

View File

@ -1,6 +1,8 @@
package types
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -22,3 +24,11 @@ func DefaultGenesisState() GenesisState {
ConstantFee: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)),
}
}
// ValidateGenesis - validate crisis genesis data
func ValidateGenesis(data GenesisState) error {
if !data.ConstantFee.IsPositive() {
return fmt.Errorf("constant fee must be positive: %s", data.ConstantFee)
}
return nil
}

View File

@ -46,6 +46,7 @@ var (
NonNegativeOutstandingInvariant = keeper.NonNegativeOutstandingInvariant
CanWithdrawInvariant = keeper.CanWithdrawInvariant
ReferenceCountInvariant = keeper.ReferenceCountInvariant
ModuleAccountInvariant = keeper.ModuleAccountInvariant
NewKeeper = keeper.NewKeeper
GetValidatorOutstandingRewardsAddress = keeper.GetValidatorOutstandingRewardsAddress
GetDelegatorWithdrawInfoAddress = keeper.GetDelegatorWithdrawInfoAddress
@ -127,13 +128,8 @@ var (
type (
Hooks = keeper.Hooks
Keeper = keeper.Keeper
DummyFeeCollectionKeeper = keeper.DummyFeeCollectionKeeper
DelegatorStartingInfo = types.DelegatorStartingInfo
CodeType = types.CodeType
StakingKeeper = types.StakingKeeper
StakingHooks = types.StakingHooks
BankKeeper = types.BankKeeper
FeeCollectionKeeper = types.FeeCollectionKeeper
FeePool = types.FeePool
DelegatorWithdrawInfo = types.DelegatorWithdrawInfo
ValidatorOutstandingRewardsRecord = types.ValidatorOutstandingRewardsRecord

View File

@ -1,23 +1,29 @@
package distribution
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// InitGenesis sets distribution information for genesis
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper, data types.GenesisState) {
var moduleHoldings sdk.DecCoins
keeper.SetFeePool(ctx, data.FeePool)
keeper.SetCommunityTax(ctx, data.CommunityTax)
keeper.SetBaseProposerReward(ctx, data.BaseProposerReward)
keeper.SetBonusProposerReward(ctx, data.BonusProposerReward)
keeper.SetWithdrawAddrEnabled(ctx, data.WithdrawAddrEnabled)
for _, dwi := range data.DelegatorWithdrawInfos {
keeper.SetDelegatorWithdrawAddr(ctx, dwi.DelegatorAddress, dwi.WithdrawAddress)
}
keeper.SetPreviousProposerConsAddr(ctx, data.PreviousProposer)
for _, rew := range data.OutstandingRewards {
keeper.SetValidatorOutstandingRewards(ctx, rew.ValidatorAddress, rew.OutstandingRewards)
moduleHoldings = moduleHoldings.Add(rew.OutstandingRewards)
}
for _, acc := range data.ValidatorAccumulatedCommissions {
keeper.SetValidatorAccumulatedCommission(ctx, acc.ValidatorAddress, acc.Accumulated)
@ -34,6 +40,22 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
for _, evt := range data.ValidatorSlashEvents {
keeper.SetValidatorSlashEvent(ctx, evt.ValidatorAddress, evt.Height, evt.Event)
}
moduleHoldings = moduleHoldings.Add(data.FeePool.CommunityPool)
moduleHoldingsInt, _ := moduleHoldings.TruncateDecimal()
// check if the module account exists
moduleAcc := keeper.GetDistributionAccount(ctx)
if moduleAcc == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
if moduleAcc.GetCoins().IsZero() {
if err := moduleAcc.SetCoins(moduleHoldingsInt); err != nil {
panic(err)
}
supplyKeeper.SetModuleAccount(ctx, moduleAcc)
}
}
// ExportGenesis returns a GenesisState for a given context and keeper.

View File

@ -2,6 +2,8 @@ package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// get outstanding rewards
@ -13,3 +15,8 @@ func (k Keeper) GetValidatorOutstandingRewardsCoins(ctx sdk.Context, val sdk.Val
func (k Keeper) GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins {
return k.GetFeePool(ctx).CommunityPool
}
// GetDistributionAccount returns the distribution ModuleAccount
func (k Keeper) GetDistributionAccount(ctx sdk.Context) exported.ModuleAccountI {
return k.supplyKeeper.GetModuleAccount(ctx, types.ModuleName)
}

View File

@ -10,7 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking/exported"
)
// allocate fees handles distribution of the collected fees
// AllocateTokens handles distribution of the collected fees
func (k Keeper) AllocateTokens(
ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64,
previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo,
@ -21,9 +21,15 @@ func (k Keeper) AllocateTokens(
// fetch and clear the collected fees for distribution, since this is
// called in BeginBlock, collected fees will be from the previous block
// (and distributed to the previous proposer)
feesCollectedInt := k.feeCollectionKeeper.GetCollectedFees(ctx)
feeCollector := k.supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName)
feesCollectedInt := feeCollector.GetCoins()
feesCollected := sdk.NewDecCoins(feesCollectedInt)
k.feeCollectionKeeper.ClearCollectedFees(ctx)
// transfer collected fees to the distribution module account
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt)
if err != nil {
panic(err)
}
// temporary workaround to keep CanWithdrawInvariant happy
// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634

View File

@ -38,7 +38,7 @@ func TestAllocateTokensToValidatorWithCommission(t *testing.T) {
}
func TestAllocateTokensToManyValidators(t *testing.T) {
ctx, _, k, sk, fck := CreateTestInputDefault(t, false, 1000)
ctx, ak, k, sk, supplyKeeper := CreateTestInputDefault(t, false, 1000)
sh := staking.NewHandler(sk)
// create validator with 50% commission
@ -72,10 +72,14 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
require.True(t, k.GetValidatorCurrentRewards(ctx, valOpAddr2).Rewards.IsZero())
// allocate tokens as if both had voted and second was proposer
fees := sdk.Coins{
{sdk.DefaultBondDenom, sdk.NewInt(100)},
}
fck.SetCollectedFees(fees)
fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)))
feeCollector := supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName)
require.NotNil(t, feeCollector)
err := feeCollector.SetCoins(fees)
require.NoError(t, err)
ak.SetAccount(ctx, feeCollector)
votes := []abci.VoteInfo{
{
Validator: abciValA,
@ -105,7 +109,7 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
func TestAllocateTokensTruncation(t *testing.T) {
communityTax := sdk.NewDec(0)
ctx, _, _, k, sk, fck, _ := CreateTestInputAdvanced(t, false, 1000000, communityTax)
ctx, ak, _, k, sk, _, supplyKeeper := CreateTestInputAdvanced(t, false, 1000000, communityTax)
sh := staking.NewHandler(sk)
// create validator with 10% commission
@ -150,10 +154,16 @@ func TestAllocateTokensTruncation(t *testing.T) {
require.True(t, k.GetValidatorCurrentRewards(ctx, valOpAddr2).Rewards.IsZero())
// allocate tokens as if both had voted and second was proposer
fees := sdk.Coins{
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(634195840)),
}
fck.SetCollectedFees(fees)
fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(634195840)))
feeCollector := supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName)
require.NotNil(t, feeCollector)
err := feeCollector.SetCoins(fees)
require.NoError(t, err)
ak.SetAccount(ctx, feeCollector)
votes := []abci.VoteInfo{
{
Validator: abciValA,

View File

@ -173,7 +173,8 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val exported.Validato
// add coins to user account
if !coins.IsZero() {
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr())
if _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil {
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins)
if err != nil {
return nil, err
}
}

View File

@ -266,6 +266,11 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
ctx, ak, k, sk, _ := CreateTestInputDefault(t, false, balancePower)
sh := staking.NewHandler(sk)
// set module account coins
distrAcc := k.GetDistributionAccount(ctx)
distrAcc.SetCoins(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))
k.supplyKeeper.SetModuleAccount(ctx, distrAcc)
// create validator with 50% commission
power := int64(100)
valTokens := sdk.TokensFromConsensusPower(power)
@ -473,8 +478,13 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000)
sh := staking.NewHandler(sk)
initial := int64(20)
// set module account coins
distrAcc := k.GetDistributionAccount(ctx)
distrAcc.SetCoins(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))
k.supplyKeeper.SetModuleAccount(ctx, distrAcc)
totalRewards := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDec(initial*2))}
tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDec(initial))}

View File

@ -5,17 +5,21 @@ import (
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// DistributeFeePool distributes funds from the the community pool to a receiver address
func (k Keeper) DistributeFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) sdk.Error {
// DistributeFromFeePool distributes funds from the distribution module account to
// a receiver address while updating the community pool
func (k Keeper) DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) sdk.Error {
feePool := k.GetFeePool(ctx)
poolTruncated, _ := feePool.CommunityPool.TruncateDecimal()
if !poolTruncated.IsAllGTE(amount) {
// NOTE the community pool isn't a module account, however its coins
// are held in the distribution module account. Thus the community pool
// must be reduced separately from the SendCoinsFromModuleToAccount call
newPool, negative := feePool.CommunityPool.SafeSub(sdk.NewDecCoins(amount))
if negative {
return types.ErrBadDistribution(k.codespace)
}
feePool.CommunityPool = newPool
feePool.CommunityPool.Sub(sdk.NewDecCoins(amount))
_, err := k.bankKeeper.AddCoins(ctx, receiveAddr, amount)
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiveAddr, amount)
if err != nil {
return err
}

View File

@ -2,7 +2,8 @@ package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// Wrapper struct
@ -10,7 +11,7 @@ type Hooks struct {
k Keeper
}
var _ types.StakingHooks = Hooks{}
var _ stakingtypes.StakingHooks = Hooks{}
// Create new distribution hooks
func (k Keeper) Hooks() Hooks { return Hooks{k} }
@ -46,8 +47,8 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr
accAddr := sdk.AccAddress(valAddr)
withdrawAddr := h.k.GetDelegatorWithdrawAddr(ctx, accAddr)
if _, err := h.k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil {
err := h.k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins)
if err != nil {
panic(err)
}
}

View File

@ -16,6 +16,8 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
CanWithdrawInvariant(k))
ir.RegisterRoute(types.ModuleName, "reference-count",
ReferenceCountInvariant(k))
ir.RegisterRoute(types.ModuleName, "module-account",
ModuleAccountInvariant(k))
}
// AllInvariants runs all invariants of the distribution module
@ -33,7 +35,7 @@ func AllInvariants(k Keeper) sdk.Invariant {
if err != nil {
return err
}
return nil
return ModuleAccountInvariant(k)(ctx)
}
}
@ -135,3 +137,29 @@ func ReferenceCountInvariant(k Keeper) sdk.Invariant {
return nil
}
}
// ModuleAccountInvariant checks that the coins held by the distr ModuleAccount
// is consistent with the sum of validator outstanding rewards
func ModuleAccountInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
var expectedCoins sdk.DecCoins
k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
expectedCoins = expectedCoins.Add(rewards)
return false
})
communityPool := k.GetFeePoolCommunityCoins(ctx)
expectedInt, _ := expectedCoins.Add(communityPool).TruncateDecimal()
macc := k.GetDistributionAccount(ctx)
if !macc.GetCoins().IsEqual(expectedInt) {
return fmt.Errorf("distribution ModuleAccount coins invariance:\n"+
"\texpected ModuleAccount coins: %s\n"+
"\tdistribution ModuleAccount coins : %s", expectedInt, macc.GetCoins())
}
return nil
}
}

View File

@ -11,32 +11,39 @@ import (
"github.com/tendermint/tendermint/libs/log"
)
// keeper of the staking store
// Keeper of the distribution store
type Keeper struct {
storeKey sdk.StoreKey
cdc *codec.Codec
paramSpace params.Subspace
bankKeeper types.BankKeeper
stakingKeeper types.StakingKeeper
feeCollectionKeeper types.FeeCollectionKeeper
storeKey sdk.StoreKey
cdc *codec.Codec
paramSpace params.Subspace
stakingKeeper types.StakingKeeper
supplyKeeper types.SupplyKeeper
// codespace
codespace sdk.CodespaceType
feeCollectorName string // name of the FeeCollector ModuleAccount
}
// create a new keeper
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, ck types.BankKeeper,
sk types.StakingKeeper, fck types.FeeCollectionKeeper, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
bankKeeper: ck,
stakingKeeper: sk,
feeCollectionKeeper: fck,
codespace: codespace,
// NewKeeper creates a new distribution Keeper instance
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace,
sk types.StakingKeeper, supplyKeeper types.SupplyKeeper, codespace sdk.CodespaceType,
feeCollectorName string) Keeper {
// ensure distribution module account is set
if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
return Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
stakingKeeper: sk,
supplyKeeper: supplyKeeper,
codespace: codespace,
feeCollectorName: feeCollectorName,
}
return keeper
}
// Logger returns a module-specific logger.
@ -110,8 +117,8 @@ func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddr
if !commission.IsZero() {
accAddr := sdk.AccAddress(valAddr)
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr)
if _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, commission); err != nil {
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission)
if err != nil {
return nil, err
}
}
@ -125,3 +132,14 @@ func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddr
return commission, nil
}
// GetTotalRewards returns the total amount of fee distribution rewards held in the store
func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) {
k.IterateValidatorOutstandingRewards(ctx,
func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
totalRewards = totalRewards.Add(rewards)
return false
},
)
return totalRewards
}

View File

@ -30,12 +30,19 @@ func TestWithdrawValidatorCommission(t *testing.T) {
sdk.NewDecCoinFromDec("stake", sdk.NewDec(3).Quo(sdk.NewDec(2))),
}
// set module account coins
distrAcc := keeper.GetDistributionAccount(ctx)
distrAcc.SetCoins(sdk.NewCoins(
sdk.NewCoin("mytoken", sdk.NewInt(2)),
sdk.NewCoin("stake", sdk.NewInt(2)),
))
keeper.supplyKeeper.SetModuleAccount(ctx, distrAcc)
// check initial balance
balance := ak.GetAccount(ctx, sdk.AccAddress(valOpAddr3)).GetCoins()
expTokens := sdk.TokensFromConsensusPower(1000)
require.Equal(t, sdk.Coins{
sdk.NewCoin("stake", sdk.TokensFromConsensusPower(1000)),
}, balance)
expCoins := sdk.NewCoins(sdk.NewCoin("stake", expTokens))
require.Equal(t, expCoins, balance)
// set outstanding rewards
keeper.SetValidatorOutstandingRewards(ctx, valOpAddr3, valCommission)
@ -48,10 +55,10 @@ func TestWithdrawValidatorCommission(t *testing.T) {
// check balance increase
balance = ak.GetAccount(ctx, sdk.AccAddress(valOpAddr3)).GetCoins()
require.Equal(t, sdk.Coins{
require.Equal(t, sdk.NewCoins(
sdk.NewCoin("mytoken", sdk.NewInt(1)),
sdk.NewCoin("stake", expTokens.AddRaw(1)),
}, balance)
), balance)
// check remainder
remainder := keeper.GetValidatorAccumulatedCommission(ctx, valOpAddr3)
@ -62,3 +69,20 @@ func TestWithdrawValidatorCommission(t *testing.T) {
require.True(t, true)
}
func TestGetTotalRewards(t *testing.T) {
ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 1000)
valCommission := sdk.DecCoins{
sdk.NewDecCoinFromDec("mytoken", sdk.NewDec(5).Quo(sdk.NewDec(4))),
sdk.NewDecCoinFromDec("stake", sdk.NewDec(3).Quo(sdk.NewDec(2))),
}
keeper.SetValidatorOutstandingRewards(ctx, valOpAddr1, valCommission)
keeper.SetValidatorOutstandingRewards(ctx, valOpAddr2, valCommission)
expectedRewards := valCommission.MulDec(sdk.NewDec(2))
totalRewards := keeper.GetTotalRewards(ctx)
require.Equal(t, expectedRewards, totalRewards)
}

View File

@ -7,20 +7,14 @@ import (
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// Handler for executing a passed community spend proposal
// HandleCommunityPoolSpendProposal is a handler for executing a passed community spend proposal
func HandleCommunityPoolSpendProposal(ctx sdk.Context, k Keeper, p types.CommunityPoolSpendProposal) sdk.Error {
feePool := k.GetFeePool(ctx)
newPool, negative := feePool.CommunityPool.SafeSub(sdk.NewDecCoins(p.Amount))
if negative {
return types.ErrBadDistribution(k.codespace)
}
feePool.CommunityPool = newPool
k.SetFeePool(ctx, feePool)
_, err := k.bankKeeper.AddCoins(ctx, p.Recipient, p.Amount)
err := k.DistributeFromFeePool(ctx, p.Amount, p.Recipient)
if err != nil {
return err
}
logger := k.Logger(ctx)
logger.Info(fmt.Sprintf("Spent %s coins from the community pool to recipient %s", p.Amount, p.Recipient))
logger.Info(fmt.Sprintf("transferred %s from the community pool to recipient %s", p.Amount, p.Recipient))
return nil
}

View File

@ -12,6 +12,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
const custom = "custom"
@ -138,6 +139,7 @@ func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, qu
func TestQueries(t *testing.T) {
cdc := codec.New()
types.RegisterCodec(cdc)
supply.RegisterCodec(cdc)
ctx, _, keeper, sk, _ := CreateTestInputDefault(t, false, 100)
querier := NewQuerier(keeper)

View File

@ -18,6 +18,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
@ -66,6 +67,7 @@ func MakeTestCodec() *codec.Codec {
bank.RegisterCodec(cdc)
staking.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
supply.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
@ -75,26 +77,26 @@ func MakeTestCodec() *codec.Codec {
// test input with default values
func CreateTestInputDefault(t *testing.T, isCheckTx bool, initPower int64) (
sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, DummyFeeCollectionKeeper) {
sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, types.SupplyKeeper) {
communityTax := sdk.NewDecWithPrec(2, 2)
ctx, ak, _, dk, sk, fck, _ := CreateTestInputAdvanced(t, isCheckTx, initPower, communityTax)
return ctx, ak, dk, sk, fck
ctx, ak, _, dk, sk, _, supplyKeeper := CreateTestInputAdvanced(t, isCheckTx, initPower, communityTax)
return ctx, ak, dk, sk, supplyKeeper
}
// hogpodge of all sorts of input required for testing
func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64,
communityTax sdk.Dec) (sdk.Context, auth.AccountKeeper, bank.Keeper,
Keeper, staking.Keeper, DummyFeeCollectionKeeper, params.Keeper) {
Keeper, staking.Keeper, params.Keeper, types.SupplyKeeper) {
initCoins := sdk.TokensFromConsensusPower(initPower)
initTokens := sdk.TokensFromConsensusPower(initPower)
keyDistr := sdk.NewKVStoreKey(types.StoreKey)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
@ -104,8 +106,8 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64,
ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyStaking, sdk.StoreTypeTransient, nil)
ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
@ -118,23 +120,34 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64,
ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger())
accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, bankKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
sk.SetPool(ctx, staking.InitialPool())
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, supply.DefaultCodespace,
[]string{auth.FeeCollectorName, types.ModuleName}, []string{}, []string{staking.NotBondedPoolName, staking.BondedPoolName})
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
sk.SetParams(ctx, staking.DefaultParams())
keeper := NewKeeper(cdc, keyDistr, pk.Subspace(DefaultParamspace), sk, supplyKeeper, types.DefaultCodespace, auth.FeeCollectorName)
initCoins := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens))
totalSupply := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens.MulRaw(int64(len(TestAddrs)))))
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
// fill all the addresses with some coins, set the loose pool tokens simultaneously
for _, addr := range TestAddrs {
pool := sk.GetPool(ctx)
_, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{
sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins),
})
_, err := bankKeeper.AddCoins(ctx, addr, initCoins)
require.Nil(t, err)
pool.NotBondedTokens = pool.NotBondedTokens.Add(initCoins)
sk.SetPool(ctx, pool)
}
fck := DummyFeeCollectionKeeper{}
keeper := NewKeeper(cdc, keyDistr, pk.Subspace(DefaultParamspace), bankKeeper, sk, fck, types.DefaultCodespace)
// create module accounts
feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName, supply.Basic)
notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner)
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner)
distrAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Basic)
keeper.supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
keeper.supplyKeeper.SetModuleAccount(ctx, bondPool)
keeper.supplyKeeper.SetModuleAccount(ctx, distrAcc)
// set the distribution hooks on staking
sk.SetHooks(keeper.Hooks())
@ -145,27 +158,5 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64,
keeper.SetBaseProposerReward(ctx, sdk.NewDecWithPrec(1, 2))
keeper.SetBonusProposerReward(ctx, sdk.NewDecWithPrec(4, 2))
return ctx, accountKeeper, bankKeeper, keeper, sk, fck, pk
}
//__________________________________________________________________________________
// fee collection keeper used only for testing
type DummyFeeCollectionKeeper struct{}
var heldFees sdk.Coins
var _ types.FeeCollectionKeeper = DummyFeeCollectionKeeper{}
// nolint
func (fck DummyFeeCollectionKeeper) AddCollectedFees(_ sdk.Context, in sdk.Coins) sdk.Coins {
fck.SetCollectedFees(heldFees.Add(in))
return heldFees
}
func (fck DummyFeeCollectionKeeper) GetCollectedFees(_ sdk.Context) sdk.Coins {
return heldFees
}
func (fck DummyFeeCollectionKeeper) SetCollectedFees(in sdk.Coins) {
heldFees = in
}
func (fck DummyFeeCollectionKeeper) ClearCollectedFees(_ sdk.Context) {
heldFees = sdk.NewCoins()
return ctx, accountKeeper, bankKeeper, keeper, sk, pk, supplyKeeper
}

View File

@ -14,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/distribution/client/cli"
"github.com/cosmos/cosmos-sdk/x/distribution/client/rest"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
var (
@ -67,14 +68,16 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
// app module
type AppModule struct {
AppModuleBasic
keeper Keeper
keeper Keeper
supplyKeeper types.SupplyKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(keeper Keeper) AppModule {
func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: keeper,
supplyKeeper: supplyKeeper,
}
}
@ -112,7 +115,7 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState GenesisState
ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, genesisState)
InitGenesis(ctx, am.keeper, am.supplyKeeper, genesisState)
return []abci.ValidatorUpdate{}
}

View File

@ -13,6 +13,8 @@ import (
var (
delPk1 = ed25519.GenPrivKey().PubKey()
delAddr1 = sdk.AccAddress(delPk1.Address())
amount = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1)))
)
func testProposal(recipient sdk.AccAddress, amount sdk.Coins) types.CommunityPoolSpendProposal {
@ -25,34 +27,39 @@ func testProposal(recipient sdk.AccAddress, amount sdk.Coins) types.CommunityPoo
}
func TestProposalHandlerPassed(t *testing.T) {
ctx, accountKeeper, keeper, _, _ := CreateTestInputDefault(t, false, 10)
ctx, accountKeeper, keeper, _, supplyKeeper := CreateTestInputDefault(t, false, 10)
recipient := delAddr1
amount := sdk.NewCoin("stake", sdk.NewInt(1))
// add coins to the module account
macc := keeper.GetDistributionAccount(ctx)
err := macc.SetCoins(macc.GetCoins().Add(amount))
require.NoError(t, err)
supplyKeeper.SetModuleAccount(ctx, macc)
account := accountKeeper.NewAccountWithAddress(ctx, recipient)
require.True(t, account.GetCoins().IsZero())
accountKeeper.SetAccount(ctx, account)
feePool := keeper.GetFeePool(ctx)
feePool.CommunityPool = sdk.DecCoins{sdk.NewDecCoinFromCoin(amount)}
feePool.CommunityPool = sdk.NewDecCoins(amount)
keeper.SetFeePool(ctx, feePool)
tp := testProposal(recipient, sdk.NewCoins(amount))
tp := testProposal(recipient, amount)
hdlr := NewCommunityPoolSpendProposalHandler(keeper)
require.NoError(t, hdlr(ctx, tp))
require.Equal(t, accountKeeper.GetAccount(ctx, recipient).GetCoins(), sdk.NewCoins(amount))
require.Equal(t, accountKeeper.GetAccount(ctx, recipient).GetCoins(), amount)
}
func TestProposalHandlerFailed(t *testing.T) {
ctx, accountKeeper, keeper, _, _ := CreateTestInputDefault(t, false, 10)
recipient := delAddr1
amount := sdk.NewCoin("stake", sdk.NewInt(1))
account := accountKeeper.NewAccountWithAddress(ctx, recipient)
require.True(t, account.GetCoins().IsZero())
accountKeeper.SetAccount(ctx, account)
tp := testProposal(recipient, sdk.NewCoins(amount))
tp := testProposal(recipient, amount)
hdlr := NewCommunityPoolSpendProposalHandler(keeper)
require.Error(t, hdlr(ctx, tp))
require.True(t, accountKeeper.GetAccount(ctx, recipient).GetCoins().IsZero())

View File

@ -3,27 +3,26 @@ package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/staking/exported"
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// StakingKeeper expected staking keeper
// StakingKeeper expected staking keeper (noalias)
type StakingKeeper interface {
// iterate through validators by operator address, execute func for each validator
IterateValidators(sdk.Context,
func(index int64, validator exported.ValidatorI) (stop bool))
func(index int64, validator stakingexported.ValidatorI) (stop bool))
// iterate through bonded validators by operator address, execute func for each validator
IterateBondedValidatorsByPower(sdk.Context,
func(index int64, validator exported.ValidatorI) (stop bool))
func(index int64, validator stakingexported.ValidatorI) (stop bool))
// iterate through the consensus validator set of the last block by operator address, execute func for each validator
IterateLastValidators(sdk.Context,
func(index int64, validator exported.ValidatorI) (stop bool))
func(index int64, validator stakingexported.ValidatorI) (stop bool))
Validator(sdk.Context, sdk.ValAddress) exported.ValidatorI // get a particular validator by operator address
ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) exported.ValidatorI // get a particular validator by consensus address
TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set
TotalTokens(sdk.Context) sdk.Int // total token supply
Validator(sdk.Context, sdk.ValAddress) stakingexported.ValidatorI // get a particular validator by operator address
ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI // get a particular validator by consensus address
// slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction
Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec)
@ -32,13 +31,13 @@ type StakingKeeper interface {
// Delegation allows for getting a particular delegation for a given validator
// and delegator outside the scope of the staking module.
Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) exported.DelegationI
Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingexported.DelegationI
// MaxValidators returns the maximum amount of bonded validators
MaxValidators(sdk.Context) uint16
IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress,
fn func(index int64, delegation exported.DelegationI) (stop bool))
fn func(index int64, delegation stakingexported.DelegationI) (stop bool))
GetLastTotalPower(ctx sdk.Context) sdk.Int
GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) int64
@ -46,7 +45,7 @@ type StakingKeeper interface {
GetAllSDKDelegations(ctx sdk.Context) []staking.Delegation
}
// StakingHooks event hooks for staking validator object
// StakingHooks event hooks for staking validator object (noalias)
type StakingHooks interface {
AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created
AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is deleted
@ -57,13 +56,15 @@ type StakingHooks interface {
BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec)
}
// expected coin keeper
type BankKeeper interface {
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error)
}
// SupplyKeeper defines the expected supply Keeper (noalias)
type SupplyKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI
// expected fee collection keeper
type FeeCollectionKeeper interface {
GetCollectedFees(ctx sdk.Context) sdk.Coins
ClearCollectedFees(ctx sdk.Context)
// TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862
SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI)
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) sdk.Error
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error
}

View File

@ -2,7 +2,7 @@ package types
const (
// ModuleName is the module name constant used in many places
ModuleName = "distr"
ModuleName = "distribution"
// StoreKey is the store key string for distribution
StoreKey = ModuleName

View File

@ -11,7 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genutil"
)
@ -60,7 +60,7 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec,
return err
}
genAcc := genaccounts.NewGenesisAccountRaw(addr, coins, vestingAmt, vestingStart, vestingEnd)
genAcc := genaccounts.NewGenesisAccountRaw(addr, coins, vestingAmt, vestingStart, vestingEnd, "", "")
if err := genAcc.Validate(); err != nil {
return err
}

View File

@ -3,9 +3,13 @@ package genaccounts
import (
"errors"
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/supply"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// GenesisAccount is a struct for account initialization used exclusively during genesis
@ -21,9 +25,13 @@ type GenesisAccount struct {
DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation
StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time)
EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time)
// module account fields
ModuleName string `json:"module_name"` // name of the module account
ModulePermission string `json:"module_permission"` // permission of module account
}
// validate the the VestingAccount parameters are possible
// Validate checks for errors on the vesting and module account parameters
func (ga GenesisAccount) Validate() error {
if !ga.OriginalVesting.IsZero() {
if ga.OriginalVesting.IsAnyGT(ga.Coins) {
@ -33,12 +41,19 @@ func (ga GenesisAccount) Validate() error {
return errors.New("vesting start-time cannot be before end-time")
}
}
// don't allow blank (i.e just whitespaces) on the module name
if ga.ModuleName != "" && strings.TrimSpace(ga.ModuleName) == "" {
return errors.New("module account name cannot be blank")
}
return nil
}
// NewGenesisAccount creates a new GenesisAccount object
// NewGenesisAccountRaw creates a new GenesisAccount object
func NewGenesisAccountRaw(address sdk.AccAddress, coins,
vestingAmount sdk.Coins, vestingStartTime, vestingEndTime int64) GenesisAccount {
vestingAmount sdk.Coins, vestingStartTime, vestingEndTime int64,
module, permission string) GenesisAccount {
return GenesisAccount{
Address: address,
@ -50,9 +65,12 @@ func NewGenesisAccountRaw(address sdk.AccAddress, coins,
DelegatedVesting: sdk.Coins{}, // ignored
StartTime: vestingStartTime,
EndTime: vestingEndTime,
ModuleName: module,
ModulePermission: permission,
}
}
// NewGenesisAccount creates a GenesisAccount instance from a BaseAccount.
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
return GenesisAccount{
Address: acc.Address,
@ -62,7 +80,8 @@ func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
}
}
func NewGenesisAccountI(acc auth.Account) (GenesisAccount, error) {
// NewGenesisAccountI creates a GenesisAccount instance from an Account interface.
func NewGenesisAccountI(acc authexported.Account) (GenesisAccount, error) {
gacc := GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
@ -74,22 +93,26 @@ func NewGenesisAccountI(acc auth.Account) (GenesisAccount, error) {
return gacc, err
}
vacc, ok := acc.(auth.VestingAccount)
if ok {
gacc.OriginalVesting = vacc.GetOriginalVesting()
gacc.DelegatedFree = vacc.GetDelegatedFree()
gacc.DelegatedVesting = vacc.GetDelegatedVesting()
gacc.StartTime = vacc.GetStartTime()
gacc.EndTime = vacc.GetEndTime()
switch acc := acc.(type) {
case authexported.VestingAccount:
gacc.OriginalVesting = acc.GetOriginalVesting()
gacc.DelegatedFree = acc.GetDelegatedFree()
gacc.DelegatedVesting = acc.GetDelegatedVesting()
gacc.StartTime = acc.GetStartTime()
gacc.EndTime = acc.GetEndTime()
case supplyexported.ModuleAccountI:
gacc.ModuleName = acc.GetName()
gacc.ModulePermission = acc.GetPermission()
}
return gacc, nil
}
// convert GenesisAccount to auth.Account
// ToAccount converts a GenesisAccount to an Account interface
func (ga *GenesisAccount) ToAccount() auth.Account {
bacc := auth.NewBaseAccount(ga.Address, ga.Coins.Sort(), nil, ga.AccountNumber, ga.Sequence)
// vesting accounts
if !ga.OriginalVesting.IsZero() {
baseVestingAcc := auth.NewBaseVestingAccount(
bacc, ga.OriginalVesting, ga.DelegatedFree,
@ -106,6 +129,11 @@ func (ga *GenesisAccount) ToAccount() auth.Account {
}
}
// module accounts
if ga.ModuleName != "" {
return supply.NewModuleAccount(bacc, ga.ModuleName, ga.ModulePermission)
}
return bacc
}

View File

@ -11,6 +11,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
supplytypes "github.com/cosmos/cosmos-sdk/x/supply/types"
)
func TestGenesisAccountValidate(t *testing.T) {
@ -22,13 +23,18 @@ func TestGenesisAccountValidate(t *testing.T) {
}{
{
"valid account",
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0),
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, "", ""),
nil,
},
{
"valid module account",
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, "mint", supplytypes.Minter),
nil,
},
{
"invalid vesting amount",
NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
sdk.NewCoins(sdk.NewInt64Coin("stake", 100)), 0, 0),
sdk.NewCoins(sdk.NewInt64Coin("stake", 100)), 0, 0, "", ""),
errors.New("vesting amount cannot be greater than total amount"),
},
{
@ -36,15 +42,20 @@ func TestGenesisAccountValidate(t *testing.T) {
NewGenesisAccountRaw(addr,
sdk.NewCoins(sdk.NewInt64Coin("uatom", 50), sdk.NewInt64Coin("eth", 50)),
sdk.NewCoins(sdk.NewInt64Coin("uatom", 100), sdk.NewInt64Coin("eth", 20)),
0, 0),
0, 0, "", ""),
errors.New("vesting amount cannot be greater than total amount"),
},
{
"invalid vesting times",
NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), 1654668078, 1554668078),
sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), 1654668078, 1554668078, "", ""),
errors.New("vesting start-time cannot be before end-time"),
},
{
"invalid module account name",
NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, " ", ""),
errors.New("module account name cannot be blank"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -57,6 +68,8 @@ func TestGenesisAccountValidate(t *testing.T) {
func TestToAccount(t *testing.T) {
priv := ed25519.GenPrivKey()
addr := sdk.AccAddress(priv.PubKey().Address())
// base account
authAcc := auth.NewBaseAccountWithAddress(addr)
authAcc.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)))
genAcc := NewGenesisAccount(&authAcc)
@ -64,6 +77,7 @@ func TestToAccount(t *testing.T) {
require.IsType(t, &auth.BaseAccount{}, acc)
require.Equal(t, &authAcc, acc.(*auth.BaseAccount))
// vesting account
vacc := auth.NewContinuousVestingAccount(
&authAcc, time.Now().Unix(), time.Now().Add(24*time.Hour).Unix(),
)
@ -72,4 +86,12 @@ func TestToAccount(t *testing.T) {
acc = genAcc.ToAccount()
require.IsType(t, &auth.ContinuousVestingAccount{}, acc)
require.Equal(t, vacc, acc.(*auth.ContinuousVestingAccount))
// module account
macc := supplytypes.NewEmptyModuleAccount("mint", supplytypes.Minter)
genAcc, err = NewGenesisAccountI(macc)
require.NoError(t, err)
acc = genAcc.ToAccount()
require.IsType(t, &supplytypes.ModuleAccount{}, acc)
require.Equal(t, macc, acc.(*supplytypes.ModuleAccount))
}

View File

@ -39,9 +39,8 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd
return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false
}
// Send coins from depositor's account to DepositedCoinsAccAddr account
// TODO: Don't use an account for this purpose; it's clumsy and prone to misuse.
err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount)
// update the governance module's account coins pool
err := keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, depositorAddr, types.ModuleName, depositAmount)
if err != nil {
return err, false
}
@ -105,11 +104,12 @@ func (keeper Keeper) GetDepositsIterator(ctx sdk.Context, proposalID uint64) sdk
func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
keeper.IterateDeposits(ctx, proposalID, func(deposit Deposit) bool {
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount)
keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool {
err := keeper.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, deposit.Depositor, deposit.Amount)
if err != nil {
panic(err)
}
store.Delete(DepositKey(proposalID, deposit.Depositor))
return false
})
@ -119,8 +119,8 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
keeper.IterateDeposits(ctx, proposalID, func(deposit Deposit) bool {
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount)
keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool {
err := keeper.supplyKeeper.BurnCoins(ctx, types.ModuleName, deposit.Amount)
if err != nil {
panic(err)
}

View File

@ -19,7 +19,6 @@ func TestTickExpiredDepositPeriod(t *testing.T) {
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
input.keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(input.keeper)
inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
@ -69,7 +68,6 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) {
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
input.keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(input.keeper)
inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
@ -138,7 +136,6 @@ func TestTickPassedDepositPeriod(t *testing.T) {
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
input.keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(input.keeper)
inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
@ -188,7 +185,6 @@ func TestTickPassedVotingPeriod(t *testing.T) {
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
input.keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(input.keeper)
inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
@ -256,10 +252,13 @@ func TestProposalPassedEndblocker(t *testing.T) {
valAddr := sdk.ValAddress(input.addrs[0])
input.keeper.ck.SetSendEnabled(ctx, true)
createValidators(t, stakingHandler, ctx, []sdk.ValAddress{valAddr}, []int64{10})
staking.EndBlocker(ctx, input.sk)
macc := input.keeper.GetGovernanceAccount(ctx)
require.NotNil(t, macc)
initialModuleAccCoins := macc.GetCoins()
proposal, err := input.keeper.SubmitProposal(ctx, testProposal())
require.NoError(t, err)
@ -268,6 +267,13 @@ func TestProposalPassedEndblocker(t *testing.T) {
res := handler(ctx, newDepositMsg)
require.True(t, res.IsOK())
macc = input.keeper.GetGovernanceAccount(ctx)
require.NotNil(t, macc)
moduleAccCoins := macc.GetCoins()
deposits := initialModuleAccCoins.Add(proposal.TotalDeposit).Add(proposalCoins)
require.True(t, moduleAccCoins.IsEqual(deposits))
err = input.keeper.AddVote(ctx, proposal.ProposalID, input.addrs[0], OptionYes)
require.NoError(t, err)
@ -276,6 +282,10 @@ func TestProposalPassedEndblocker(t *testing.T) {
ctx = ctx.WithBlockHeader(newHeader)
EndBlocker(ctx, input.keeper)
macc = input.keeper.GetGovernanceAccount(ctx)
require.NotNil(t, macc)
require.True(t, macc.GetCoins().IsEqual(initialModuleAccCoins))
}
func TestEndBlockerProposalHandlerFailed(t *testing.T) {
@ -294,7 +304,6 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) {
valAddr := sdk.ValAddress(input.addrs[0])
input.keeper.ck.SetSendEnabled(ctx, true)
createValidators(t, stakingHandler, ctx, []sdk.ValAddress{valAddr}, []int64{10})
staking.EndBlocker(ctx, input.sk)

View File

@ -2,26 +2,31 @@ package gov
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/exported"
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// expected bank keeper
type BankKeeper interface {
GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
// SupplyKeeper defines the supply Keeper for module accounts
type SupplyKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI
// TODO remove once governance doesn't require use of accounts
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
SetSendEnabled(ctx sdk.Context, enabled bool)
// TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862
SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI)
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error
BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error
}
// StakingKeeper expected staking keeper (Validator and Delegator sets)
type StakingKeeper interface {
// iterate through bonded validators by operator address, execute func for each validator
IterateBondedValidatorsByPower(sdk.Context,
func(index int64, validator exported.ValidatorI) (stop bool))
func(index int64, validator stakingexported.ValidatorI) (stop bool))
TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set
IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress,
fn func(index int64, delegation exported.DelegationI) (stop bool))
fn func(index int64, delegation stakingexported.DelegationI) (stop bool))
}

View File

@ -91,18 +91,29 @@ func ValidateGenesis(data GenesisState) error {
}
// InitGenesis - store genesis parameters
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper SupplyKeeper, data GenesisState) {
k.setProposalID(ctx, data.StartingProposalID)
k.setDepositParams(ctx, data.DepositParams)
k.setVotingParams(ctx, data.VotingParams)
k.setTallyParams(ctx, data.TallyParams)
// check if the deposits pool account exists
moduleAcc := k.GetGovernanceAccount(ctx)
if moduleAcc == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
var totalDeposits sdk.Coins
for _, deposit := range data.Deposits {
k.setDeposit(ctx, deposit.ProposalID, deposit.Depositor, deposit)
totalDeposits = totalDeposits.Add(deposit.Amount)
}
for _, vote := range data.Votes {
k.setVote(ctx, vote.ProposalID, vote.Voter, vote)
}
for _, proposal := range data.Proposals {
switch proposal.Status {
case StatusDepositPeriod:
@ -112,6 +123,14 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
}
k.SetProposal(ctx, proposal)
}
// add coins if not provided on genesis
if moduleAcc.GetCoins().IsZero() {
if err := moduleAcc.SetCoins(totalDeposits); err != nil {
panic(err)
}
supplyKeeper.SetModuleAccount(ctx, moduleAcc)
}
}
// ExportGenesis - output genesis parameters

View File

@ -85,7 +85,8 @@ func TestImportExportQueues(t *testing.T) {
require.NoError(t, err)
proposalID2 := proposal2.ProposalID
_, votingStarted := input.keeper.AddDeposit(ctx, proposalID2, input.addrs[0], input.keeper.GetDepositParams(ctx).MinDeposit)
err, votingStarted := input.keeper.AddDeposit(ctx, proposalID2, input.addrs[0], input.keeper.GetDepositParams(ctx).MinDeposit)
require.NoError(t, err)
require.True(t, votingStarted)
proposal1, ok := input.keeper.GetProposal(ctx, proposalID1)
@ -117,7 +118,9 @@ func TestImportExportQueues(t *testing.T) {
require.True(t, proposal1.Status == StatusDepositPeriod)
require.True(t, proposal2.Status == StatusVotingPeriod)
// Run the endblocker. Check to make sure that proposal1 is removed from state, and proposal2 is finished VotingPeriod.
require.Equal(t, input2.keeper.GetDepositParams(ctx2).MinDeposit, input2.keeper.GetGovernanceAccount(ctx2).GetCoins())
// Run the endblocker. Check to make sure that proposal1 is removed from state, and proposal2 is finished VotingPeriod.
EndBlocker(ctx2, input2.keeper)
proposal1, ok = input2.keeper.GetProposal(ctx2, proposalID1)

42
x/gov/invariants.go Normal file
View File

@ -0,0 +1,42 @@
package gov
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// RegisterInvariants registers all governance invariants
func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper) {
ir.RegisterRoute(types.ModuleName, "module-account", ModuleAccountInvariant(keeper))
}
// AllInvariants runs all invariants of the governance module
func AllInvariants(keeper Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
return ModuleAccountInvariant(keeper)(ctx)
}
}
// ModuleAccountInvariant checks that the module account coins reflects the sum of
// deposit amounts held on store
func ModuleAccountInvariant(keeper Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
var expectedDeposits sdk.Coins
keeper.IterateAllDeposits(ctx, func(deposit types.Deposit) bool {
expectedDeposits = expectedDeposits.Add(deposit.Amount)
return false
})
macc := keeper.GetGovernanceAccount(ctx)
if !macc.GetCoins().IsEqual(expectedDeposits) {
return fmt.Errorf("deposits invariance:\n"+
"\tgov ModuleAccount coins: %s\n"+
"\tsum of deposit amounts: %s", macc.GetCoins(), expectedDeposits)
}
return nil
}
}

View File

@ -8,18 +8,11 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/supply/exported"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/log"
)
// special governance addresses
var (
// TODO: Find another way to implement this without using accounts, or find a cleaner way to implement it using accounts.
DepositedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins")))
BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins")))
)
// Governance Keeper
type Keeper struct {
// The reference to the Param Keeper to get and set Global Params
@ -28,8 +21,8 @@ type Keeper struct {
// The reference to the Paramstore to get and set gov specific params
paramSpace params.Subspace
// The reference to the CoinKeeper to modify balances
ck BankKeeper
// The SupplyKeeper to reduce the supply of the network
supplyKeeper SupplyKeeper
// The reference to the DelegationSet and ValidatorSet to get information about validators and delegators
sk StakingKeeper
@ -54,9 +47,14 @@ type Keeper struct {
// - and tallying the result of the vote.
func NewKeeper(
cdc *codec.Codec, key sdk.StoreKey, paramsKeeper params.Keeper, paramSpace params.Subspace,
ck BankKeeper, sk StakingKeeper, codespace sdk.CodespaceType, rtr Router,
supplyKeeper SupplyKeeper, sk StakingKeeper, codespace sdk.CodespaceType, rtr Router,
) Keeper {
// ensure governance module account is set
if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
// It is vital to seal the governance proposal router here as to not allow
// further handlers to be registered after the keeper is created since this
// could create invalid or non-deterministic behavior.
@ -66,7 +64,7 @@ func NewKeeper(
storeKey: key,
paramsKeeper: paramsKeeper,
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
ck: ck,
supplyKeeper: supplyKeeper,
sk: sk,
cdc: cdc,
codespace: codespace,
@ -79,6 +77,11 @@ func (keeper Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// GetGovernanceAccount returns the governance ModuleAccount
func (keeper Keeper) GetGovernanceAccount(ctx sdk.Context) exported.ModuleAccountI {
return keeper.supplyKeeper.GetModuleAccount(ctx, types.ModuleName)
}
// Params
// Returns the current DepositParams from the global param store

View File

@ -83,6 +83,7 @@ func TestActivateVotingPeriod(t *testing.T) {
func TestDeposits(t *testing.T) {
input := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
@ -98,8 +99,8 @@ func TestDeposits(t *testing.T) {
fourStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(4)))
fiveStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(5)))
addr0Initial := input.keeper.ck.GetCoins(ctx, input.addrs[0])
addr1Initial := input.keeper.ck.GetCoins(ctx, input.addrs[1])
addr0Initial := input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins()
addr1Initial := input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[1]).GetCoins()
expTokens := sdk.TokensFromConsensusPower(42)
require.Equal(t, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, expTokens)), addr0Initial)
@ -123,7 +124,7 @@ func TestDeposits(t *testing.T) {
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake, proposal.TotalDeposit)
require.Equal(t, addr0Initial.Sub(fourStake), input.keeper.ck.GetCoins(ctx, input.addrs[0]))
require.Equal(t, addr0Initial.Sub(fourStake), input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins())
// Check a second deposit from same address
err, votingStarted = input.keeper.AddDeposit(ctx, proposalID, input.addrs[0], fiveStake)
@ -136,7 +137,7 @@ func TestDeposits(t *testing.T) {
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake.Add(fiveStake), proposal.TotalDeposit)
require.Equal(t, addr0Initial.Sub(fourStake).Sub(fiveStake), input.keeper.ck.GetCoins(ctx, input.addrs[0]))
require.Equal(t, addr0Initial.Sub(fourStake).Sub(fiveStake), input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins())
// Check third deposit from a new address
err, votingStarted = input.keeper.AddDeposit(ctx, proposalID, input.addrs[1], fourStake)
@ -149,7 +150,7 @@ func TestDeposits(t *testing.T) {
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake.Add(fiveStake).Add(fourStake), proposal.TotalDeposit)
require.Equal(t, addr1Initial.Sub(fourStake), input.keeper.ck.GetCoins(ctx, input.addrs[1]))
require.Equal(t, addr1Initial.Sub(fourStake), input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[1]).GetCoins())
// Check that proposal moved to voting period
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
@ -177,8 +178,8 @@ func TestDeposits(t *testing.T) {
input.keeper.RefundDeposits(ctx, proposalID)
deposit, found = input.keeper.GetDeposit(ctx, proposalID, input.addrs[1])
require.False(t, found)
require.Equal(t, addr0Initial, input.keeper.ck.GetCoins(ctx, input.addrs[0]))
require.Equal(t, addr1Initial, input.keeper.ck.GetCoins(ctx, input.addrs[1]))
require.Equal(t, addr0Initial, input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins())
require.Equal(t, addr1Initial, input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[1]).GetCoins())
}

View File

@ -92,14 +92,16 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
// app module
type AppModule struct {
AppModuleBasic
keeper Keeper
keeper Keeper
supplyKeeper SupplyKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(keeper Keeper) AppModule {
func NewAppModule(keeper Keeper, supplyKeeper SupplyKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: keeper,
supplyKeeper: supplyKeeper,
}
}
@ -109,7 +111,9 @@ func (AppModule) Name() string {
}
// register invariants
func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
RegisterInvariants(ir, am.keeper)
}
// module message route name
func (AppModule) Route() string {
@ -135,7 +139,7 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState GenesisState
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, genesisState)
InitGenesis(ctx, am.keeper, am.supplyKeeper, genesisState)
return []abci.ValidatorUpdate{}
}

View File

@ -20,6 +20,14 @@ import (
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
var (
valTokens = sdk.TokensFromConsensusPower(42)
initTokens = sdk.TokensFromConsensusPower(100000)
valCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens))
initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens))
)
type testInput struct {
@ -36,30 +44,34 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
mApp := mock.NewApp()
staking.RegisterCodec(mApp.Cdc)
RegisterCodec(mApp.Cdc)
types.RegisterCodec(mApp.Cdc)
supply.RegisterCodec(mApp.Cdc)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keyGov := sdk.NewKVStoreKey(StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
pk := mApp.ParamsKeeper
rtr := NewRouter().
AddRoute(RouterKey, ProposalHandler)
ck := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, ck, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace, rtr)
bk := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, supply.DefaultCodespace,
[]string{}, []string{}, []string{types.ModuleName, staking.NotBondedPoolName, staking.BondedPoolName})
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace("testgov"), supplyKeeper, sk, DefaultCodespace, rtr)
mApp.Router().AddRoute(RouterKey, NewHandler(keeper))
mApp.QueryRouter().AddRoute(QuerierRoute, NewQuerier(keeper))
mApp.SetEndBlocker(getEndBlocker(keeper))
mApp.SetInitChainer(getInitChainer(mApp, keeper, sk, mApp.AccountKeeper, genState))
mApp.SetInitChainer(getInitChainer(mApp, keeper, sk, supplyKeeper, genAccs, genState))
require.NoError(t, mApp.CompleteSetup(keyStaking, tKeyStaking, keyGov))
valTokens := sdk.TokensFromConsensusPower(42)
require.NoError(t, mApp.CompleteSetup(keyStaking, tKeyStaking, keyGov, keySupply))
var (
addrs []sdk.AccAddress
@ -68,8 +80,7 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
)
if genAccs == nil || len(genAccs) == 0 {
genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs,
sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, valTokens)})
genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs, valCoins)
}
mock.SetGenesis(mApp, genAccs)
@ -86,21 +97,29 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
}
// gov and staking initchainer
func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper,
accountKeeper staking.AccountKeeper, genState GenesisState) sdk.InitChainer {
func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper, supplyKeeper supply.Keeper, accs []auth.Account, genState GenesisState) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
stakingGenesis := staking.DefaultGenesisState()
tokens := sdk.TokensFromConsensusPower(100000)
stakingGenesis.Pool.NotBondedTokens = tokens
validators := staking.InitGenesis(ctx, stakingKeeper, accountKeeper, stakingGenesis)
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(len(mapp.GenesisAccounts)))))
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
// set module accounts
govAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner)
notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner)
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner)
supplyKeeper.SetModuleAccount(ctx, govAcc)
supplyKeeper.SetModuleAccount(ctx, notBondedPool)
supplyKeeper.SetModuleAccount(ctx, bondPool)
validators := staking.InitGenesis(ctx, stakingKeeper, mapp.AccountKeeper, supplyKeeper, stakingGenesis)
if genState.IsEmpty() {
InitGenesis(ctx, keeper, DefaultGenesisState())
InitGenesis(ctx, keeper, supplyKeeper, DefaultGenesisState())
} else {
InitGenesis(ctx, keeper, genState)
InitGenesis(ctx, keeper, supplyKeeper, genState)
}
return abci.ResponseInitChain{
Validators: validators,

View File

@ -27,6 +27,9 @@ func (v Vote) String() string {
type Votes []Vote
func (v Votes) String() string {
if len(v) == 0 {
return "[]"
}
out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalID)
for _, vot := range v {
out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Option)

View File

@ -9,6 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
supplytypes "github.com/cosmos/cosmos-sdk/x/supply/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/secp256k1"
@ -19,6 +20,8 @@ func getMockApp(t *testing.T) *mock.App {
mapp := mock.NewApp()
RegisterCodec(mapp.Cdc)
supplytypes.RegisterCodec(mapp.Cdc)
keyIBC := sdk.NewKVStoreKey("ibc")
ibcMapper := NewMapper(mapp.Cdc, keyIBC, DefaultCodespace)
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper,

View File

@ -12,16 +12,26 @@ func BeginBlocker(ctx sdk.Context, k Keeper) {
params := k.GetParams(ctx)
// recalculate inflation rate
totalSupply := k.TotalTokens(ctx)
totalStakingSupply := k.StakingTokenSupply(ctx)
bondedRatio := k.BondedRatio(ctx)
minter.Inflation = minter.NextInflationRate(params, bondedRatio)
minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalSupply)
minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalStakingSupply)
k.SetMinter(ctx, minter)
// mint coins, add to collected fees, update supply
// mint coins, update supply
mintedCoin := minter.BlockProvision(params)
k.AddCollectedFees(ctx, sdk.Coins{mintedCoin})
k.InflateSupply(ctx, mintedCoin.Amount)
mintedCoins := sdk.NewCoins(mintedCoin)
err := k.MintCoins(ctx, mintedCoins)
if err != nil {
panic(err)
}
// send the minted coins to the fee collector account
err = k.AddCollectedFees(ctx, mintedCoins)
if err != nil {
panic(err)
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(

View File

@ -26,7 +26,7 @@ func DefaultGenesisState() GenesisState {
}
}
// new mint genesis
// InitGenesis new mint genesis
func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
keeper.SetMinter(ctx, data.Minter)
keeper.SetParams(ctx, data.Params)

View File

@ -11,28 +11,34 @@ import (
"github.com/cosmos/cosmos-sdk/x/params"
)
// keeper of the staking store
// Keeper of the mint store
type Keeper struct {
storeKey sdk.StoreKey
cdc *codec.Codec
paramSpace params.Subspace
sk types.StakingKeeper
fck types.FeeCollectionKeeper
cdc *codec.Codec
storeKey sdk.StoreKey
paramSpace params.Subspace
sk types.StakingKeeper
supplyKeeper types.SupplyKeeper
feeCollectorName string
}
// NewKeeper creates a new mint Keeper instance
func NewKeeper(
cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace,
sk types.StakingKeeper, fck types.FeeCollectionKeeper,
) Keeper {
sk types.StakingKeeper, supplyKeeper types.SupplyKeeper, feeCollectorName string) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()),
sk: sk,
fck: fck,
// ensure mint module account is set
if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic("the mint module account has not been set")
}
return Keeper{
cdc: cdc,
storeKey: key,
paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()),
sk: sk,
supplyKeeper: supplyKeeper,
feeCollectorName: feeCollectorName,
}
return keeper
}
//______________________________________________________________________
@ -61,30 +67,6 @@ func (k Keeper) SetMinter(ctx sdk.Context, minter types.Minter) {
store.Set(types.MinterKey, b)
}
// TotalTokens implements an alias call to the underlying staking keeper's
// TotalTokens to be used in BeginBlocker.
func (k Keeper) TotalTokens(ctx sdk.Context) sdk.Int {
return k.sk.TotalTokens(ctx)
}
// BondedRatio implements an alias call to the underlying staking keeper's
// BondedRatio to be used in BeginBlocker.
func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec {
return k.sk.BondedRatio(ctx)
}
// InflateSupply implements an alias call to the underlying staking keeper's
// InflateSupply to be used in BeginBlocker.
func (k Keeper) InflateSupply(ctx sdk.Context, newTokens sdk.Int) {
k.sk.InflateSupply(ctx, newTokens)
}
// AddCollectedFees implements an alias call to the underlying staking keeper's
// AddCollectedFees to be used in BeginBlocker.
func (k Keeper) AddCollectedFees(ctx sdk.Context, fees sdk.Coins) sdk.Coins {
return k.fck.AddCollectedFees(ctx, fees)
}
//______________________________________________________________________
// GetParams returns the total set of minting parameters.
@ -97,3 +79,29 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramSpace.SetParamSet(ctx, &params)
}
//______________________________________________________________________
// StakingTokenSupply implements an alias call to the underlying staking keeper's
// StakingTokenSupply to be used in BeginBlocker.
func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int {
return k.sk.StakingTokenSupply(ctx)
}
// BondedRatio implements an alias call to the underlying staking keeper's
// BondedRatio to be used in BeginBlocker.
func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec {
return k.sk.BondedRatio(ctx)
}
// MintCoins implements an alias call to the underlying supply keeper's
// MintCoins to be used in BeginBlocker.
func (k Keeper) MintCoins(ctx sdk.Context, newCoins sdk.Coins) sdk.Error {
return k.supplyKeeper.MintCoins(ctx, types.ModuleName, newCoins)
}
// AddCollectedFees implements an alias call to the underlying supply keeper's
// AddCollectedFees to be used in BeginBlocker.
func (k Keeper) AddCollectedFees(ctx sdk.Context, fees sdk.Coins) sdk.Error {
return k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.feeCollectorName, fees)
}

View File

@ -21,6 +21,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/mint/internal/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
type testInput struct {
@ -33,36 +34,48 @@ func newTestInput(t *testing.T) testInput {
db := dbm.NewMemDB()
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey)
keyMint := sdk.NewKVStoreKey(types.StoreKey)
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyStaking, sdk.StoreTypeTransient, nil)
ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
err := ms.LoadLatestVersion()
require.Nil(t, err)
ctx := sdk.NewContext(ms, abci.Header{Time: time.Unix(0, 0)}, false, log.NewTMLogger(os.Stdout))
paramsKeeper := params.NewKeeper(types.ModuleCdc, keyParams, tkeyParams, params.DefaultCodespace)
feeCollectionKeeper := auth.NewFeeCollectionKeeper(types.ModuleCdc, keyFeeCollection)
accountKeeper := auth.NewAccountKeeper(types.ModuleCdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
stakingKeeper := staking.NewKeeper(
types.ModuleCdc, keyStaking, tkeyStaking, bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace,
)
mintKeeper := NewKeeper(
types.ModuleCdc, keyMint, paramsKeeper.Subspace(types.DefaultParamspace), &stakingKeeper, feeCollectionKeeper,
)
supplyKeeper := supply.NewKeeper(types.ModuleCdc, keySupply, accountKeeper, bankKeeper, supply.DefaultCodespace,
[]string{auth.FeeCollectorName}, []string{types.ModuleName}, []string{staking.NotBondedPoolName, staking.BondedPoolName})
supplyKeeper.SetSupply(ctx, supply.NewSupply(sdk.Coins{}))
ctx := sdk.NewContext(ms, abci.Header{Time: time.Unix(0, 0)}, false, log.NewTMLogger(os.Stdout))
stakingKeeper := staking.NewKeeper(
types.ModuleCdc, keyStaking, tkeyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace,
)
mintKeeper := NewKeeper(types.ModuleCdc, keyMint, paramsKeeper.Subspace(types.DefaultParamspace), &stakingKeeper, supplyKeeper, auth.FeeCollectorName)
// set module accounts
feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName, supply.Basic)
minterAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Minter)
notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner)
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner)
supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc)
supplyKeeper.SetModuleAccount(ctx, minterAcc)
supplyKeeper.SetModuleAccount(ctx, notBondedPool)
supplyKeeper.SetModuleAccount(ctx, bondPool)
mintKeeper.SetParams(ctx, types.DefaultParams())
mintKeeper.SetMinter(ctx, types.DefaultInitialMinter())

View File

@ -2,16 +2,23 @@ package types // noalias
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// expected staking keeper
// StakingKeeper defines the expected staking keeper
type StakingKeeper interface {
TotalTokens(ctx sdk.Context) sdk.Int
StakingTokenSupply(ctx sdk.Context) sdk.Int
BondedRatio(ctx sdk.Context) sdk.Dec
InflateSupply(ctx sdk.Context, newTokens sdk.Int)
}
// expected fee collection keeper interface
type FeeCollectionKeeper interface {
AddCollectedFees(sdk.Context, sdk.Coins) sdk.Coins
// SupplyKeeper defines the expected supply keeper
type SupplyKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
// TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862
SetModuleAccount(sdk.Context, exported.ModuleAccountI)
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) sdk.Error
MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error
}

View File

@ -28,17 +28,15 @@ const chainID = ""
// capabilities aren't needed for testing.
type App struct {
*bam.BaseApp
Cdc *codec.Codec // Cdc is public since the codec is passed into the module anyways
KeyMain *sdk.KVStoreKey
KeyAccount *sdk.KVStoreKey
KeyFeeCollection *sdk.KVStoreKey
KeyParams *sdk.KVStoreKey
TKeyParams *sdk.TransientStoreKey
Cdc *codec.Codec // Cdc is public since the codec is passed into the module anyways
KeyMain *sdk.KVStoreKey
KeyAccount *sdk.KVStoreKey
KeyParams *sdk.KVStoreKey
TKeyParams *sdk.TransientStoreKey
// TODO: Abstract this out from not needing to be auth specifically
AccountKeeper auth.AccountKeeper
FeeCollectionKeeper auth.FeeCollectionKeeper
ParamsKeeper params.Keeper
AccountKeeper auth.AccountKeeper
ParamsKeeper params.Keeper
GenesisAccounts []auth.Account
TotalCoinsSupply sdk.Coins
@ -59,30 +57,27 @@ func NewApp() *App {
Cdc: cdc,
KeyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
KeyAccount: sdk.NewKVStoreKey(auth.StoreKey),
KeyFeeCollection: sdk.NewKVStoreKey("fee"),
KeyParams: sdk.NewKVStoreKey("params"),
TKeyParams: sdk.NewTransientStoreKey("transient_params"),
TotalCoinsSupply: sdk.NewCoins(),
}
// define keepers
app.ParamsKeeper = params.NewKeeper(app.Cdc, app.KeyParams, app.TKeyParams, params.DefaultCodespace)
// Define the accountKeeper
app.AccountKeeper = auth.NewAccountKeeper(
app.Cdc,
app.KeyAccount,
app.ParamsKeeper.Subspace(auth.DefaultParamspace),
auth.ProtoBaseAccount,
)
app.FeeCollectionKeeper = auth.NewFeeCollectionKeeper(
app.Cdc,
app.KeyFeeCollection,
)
supplyKeeper := auth.NewDummySupplyKeeper(app.AccountKeeper)
// Initialize the app. The chainers and blockers can be overwritten before
// calling complete setup.
app.SetInitChainer(app.InitChainer)
app.SetAnteHandler(auth.NewAnteHandler(app.AccountKeeper, app.FeeCollectionKeeper, auth.DefaultSigVerificationGasConsumer))
app.SetAnteHandler(auth.NewAnteHandler(app.AccountKeeper, supplyKeeper, auth.DefaultSigVerificationGasConsumer))
// Not sealing for custom extension
@ -94,7 +89,7 @@ func NewApp() *App {
func (app *App) CompleteSetup(newKeys ...sdk.StoreKey) error {
newKeys = append(
newKeys,
app.KeyMain, app.KeyAccount, app.KeyParams, app.TKeyParams, app.KeyFeeCollection,
app.KeyMain, app.KeyAccount, app.KeyParams, app.TKeyParams,
)
for _, key := range newKeys {
@ -116,6 +111,7 @@ func (app *App) CompleteSetup(newKeys ...sdk.StoreKey) error {
// InitChainer performs custom logic for initialization.
// nolint: errcheck
func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.ResponseInitChain {
// Load the genesis accounts
for _, genacc := range app.GenesisAccounts {
acc := app.AccountKeeper.NewAccountWithAddress(ctx, genacc.GetAddress())
@ -123,7 +119,7 @@ func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.Respo
app.AccountKeeper.SetAccount(ctx, acc)
}
auth.InitGenesis(ctx, app.AccountKeeper, app.FeeCollectionKeeper, auth.DefaultGenesisState())
auth.InitGenesis(ctx, app.AccountKeeper, auth.DefaultGenesisState())
return abci.ResponseInitChain{}
}

View File

@ -8,6 +8,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
supplytypes "github.com/cosmos/cosmos-sdk/x/supply/types"
)
const msgRoute = "testMsg"
@ -51,6 +52,7 @@ func getMockApp(t *testing.T) *App {
func TestCheckAndDeliverGenTx(t *testing.T) {
mApp := getMockApp(t)
mApp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil)
supplytypes.RegisterCodec(mApp.Cdc)
SetGenesis(mApp, accs)
ctxCheck := mApp.BaseApp.NewContext(true, abci.Header{})
@ -90,6 +92,7 @@ func TestCheckAndDeliverGenTx(t *testing.T) {
func TestCheckGenTx(t *testing.T) {
mApp := getMockApp(t)
mApp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil)
supplytypes.RegisterCodec(mApp.Cdc)
SetGenesis(mApp, accs)

View File

@ -9,6 +9,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// nolint
const (
// Minimum time per block
minTimePerBlock int64 = 10000 / 2
@ -148,6 +149,7 @@ var (
}
)
// TODO add description
type (
AppParams map[string]json.RawMessage
ParamSimulator func(r *rand.Rand)

View File

@ -36,6 +36,7 @@ func RandStringOfLength(r *rand.Rand, n int) string {
return string(b)
}
// get a rand positive sdk.Int
func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) {
if !max.GT(sdk.OneInt()) {
return sdk.Int{}, errors.New("max too small")

View File

@ -63,7 +63,7 @@ func getBlockSize(r *rand.Rand, params Params,
// PeriodicInvariants returns an array of wrapped Invariants. Where each
// invariant function is only executed periodically defined by period and offset.
func PeriodicInvariants(invariants []sdk.Invariant, period int, offset int) []sdk.Invariant {
func PeriodicInvariants(invariants []sdk.Invariant, period, offset int) []sdk.Invariant {
var outInvariants []sdk.Invariant
for _, invariant := range invariants {
outInvariant := func(ctx sdk.Context) error {

View File

@ -24,7 +24,7 @@ func TestBeginBlocker(t *testing.T) {
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())

View File

@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/cosmos/cosmos-sdk/x/supply"
)
var (
@ -27,21 +28,25 @@ func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) {
RegisterCodec(mapp.Cdc)
staking.RegisterCodec(mapp.Cdc)
supply.RegisterCodec(mapp.Cdc)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keySlashing := sdk.NewKVStoreKey(StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
stakingKeeper := staking.NewKeeper(mapp.Cdc, keyStaking, tkeyStaking, bankKeeper, mapp.ParamsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
supplyKeeper := supply.NewKeeper(mapp.Cdc, keySupply, mapp.AccountKeeper, bankKeeper, supply.DefaultCodespace,
[]string{auth.FeeCollectorName}, []string{}, []string{staking.NotBondedPoolName, staking.BondedPoolName})
stakingKeeper := staking.NewKeeper(mapp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mapp.ParamsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
keeper := NewKeeper(mapp.Cdc, keySlashing, stakingKeeper, mapp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
mapp.Router().AddRoute(staking.RouterKey, staking.NewHandler(stakingKeeper))
mapp.Router().AddRoute(RouterKey, NewHandler(keeper))
mapp.SetEndBlocker(getEndBlocker(stakingKeeper))
mapp.SetInitChainer(getInitChainer(mapp, stakingKeeper, mapp.AccountKeeper))
mapp.SetInitChainer(getInitChainer(mapp, stakingKeeper, mapp.AccountKeeper, supplyKeeper))
require.NoError(t, mapp.CompleteSetup(keyStaking, tkeyStaking, keySlashing))
require.NoError(t, mapp.CompleteSetup(keyStaking, tkeyStaking, keySupply, keySlashing))
return mapp, stakingKeeper, keeper
}
@ -57,13 +62,20 @@ func getEndBlocker(keeper staking.Keeper) sdk.EndBlocker {
}
// overwrite the mock init chainer
func getInitChainer(mapp *mock.App, keeper staking.Keeper, accountKeeper types.AccountKeeper) sdk.InitChainer {
func getInitChainer(mapp *mock.App, keeper staking.Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
// set module accounts
feeCollector := supply.NewEmptyModuleAccount(auth.FeeCollectorName, supply.Basic)
notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner)
bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner)
supplyKeeper.SetModuleAccount(ctx, feeCollector)
supplyKeeper.SetModuleAccount(ctx, bondPool)
supplyKeeper.SetModuleAccount(ctx, notBondedPool)
mapp.InitChainer(ctx, req)
stakingGenesis := staking.DefaultGenesisState()
tokens := sdk.TokensFromConsensusPower(100000)
stakingGenesis.Pool.NotBondedTokens = tokens
validators := staking.InitGenesis(ctx, keeper, accountKeeper, stakingGenesis)
validators := staking.InitGenesis(ctx, keeper, accountKeeper, supplyKeeper, stakingGenesis)
return abci.ResponseInitChain{
Validators: validators,
}

View File

@ -25,7 +25,7 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))},
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -50,7 +50,7 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))},
)
unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt())

View File

@ -69,7 +69,7 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
// Reject evidence if the double-sign is too old
if age > k.MaxEvidenceAge(ctx) {
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d",
pubkey.Address(), infractionHeight, age, k.MaxEvidenceAge(ctx)))
sdk.ConsAddress(pubkey.Address()), infractionHeight, age, k.MaxEvidenceAge(ctx)))
return
}
@ -90,12 +90,12 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
// validator is already tombstoned
if signInfo.Tombstoned {
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, validator already tombstoned", pubkey.Address(), infractionHeight))
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, validator already tombstoned", sdk.ConsAddress(pubkey.Address()), infractionHeight))
return
}
// double sign confirmed
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d", pubkey.Address(), infractionHeight, age))
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d", sdk.ConsAddress(pubkey.Address()), infractionHeight, age))
// We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height.
// Note that this *can* result in a negative "distributionHeight", up to -ValidatorUpdateDelay,
@ -151,7 +151,7 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
consAddr := sdk.ConsAddress(addr)
pubkey, err := k.getPubkey(ctx, addr)
if err != nil {
panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))
panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr.String()))
}
// fetch signing info
@ -193,7 +193,7 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
),
)
logger.Info(fmt.Sprintf("Absent validator %s (%v) at height %d, %d missed, threshold %d", addr, pubkey, height, signInfo.MissedBlocksCounter, k.MinSignedPerWindow(ctx)))
logger.Info(fmt.Sprintf("Absent validator %s (%s) at height %d, %d missed, threshold %d", addr, pubkey, height, signInfo.MissedBlocksCounter, k.MinSignedPerWindow(ctx)))
}
minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
@ -206,7 +206,7 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Downtime confirmed: slash and jail the validator
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d",
pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx)))
sdk.ConsAddress(pubkey.Address()), minHeight, k.MinSignedPerWindow(ctx)))
// We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height,
// and subtract an additional 1 since this is the LastCommit.
@ -236,7 +236,7 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
} else {
// Validator was (a) not found or (b) already jailed, don't slash
logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed",
pubkey.Address()))
sdk.ConsAddress(pubkey.Address())))
}
}
@ -254,7 +254,7 @@ func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKe
var pubkey crypto.PubKey
err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(types.GetAddrPubkeyRelationKey(address)), &pubkey)
if err != nil {
return nil, fmt.Errorf("address %v not found", address)
return nil, fmt.Errorf("address %s not found", sdk.ConsAddress(address))
}
return pubkey, nil
}

View File

@ -39,7 +39,7 @@ func TestHandleDoubleSign(t *testing.T) {
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, operatorAddr).GetBondedTokens())
@ -100,7 +100,7 @@ func TestPastMaxEvidenceAge(t *testing.T) {
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, operatorAddr).GetBondedTokens())
@ -138,7 +138,7 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -174,8 +174,8 @@ func TestHandleAbsentValidator(t *testing.T) {
// validator should be bonded still
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
require.True(sdk.IntEq(t, amt, pool.BondedTokens))
bondPool := sk.GetBondedPool(ctx)
require.True(sdk.IntEq(t, amt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx))))
// 501st block missed
ctx = ctx.WithBlockHeight(height)
@ -231,8 +231,8 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(t, sdk.Bonded, validator.GetStatus())
// validator should have been slashed
pool = sk.GetPool(ctx)
require.Equal(t, amt.Int64()-slashAmt, pool.BondedTokens.Int64())
bondPool = sk.GetBondedPool(ctx)
require.Equal(t, amt.Int64()-slashAmt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx)).Int64())
// Validator start height should not have been changed
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
@ -292,7 +292,7 @@ func TestHandleNewValidator(t *testing.T) {
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -311,9 +311,9 @@ func TestHandleNewValidator(t *testing.T) {
// validator should be bonded still, should not have been jailed or slashed
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
bondPool := sk.GetBondedPool(ctx)
expTokens := sdk.TokensFromConsensusPower(100)
require.Equal(t, expTokens, pool.BondedTokens)
require.Equal(t, expTokens.Int64(), bondPool.GetCoins().AmountOf(sk.BondDenom(ctx)).Int64())
}
// Test a jailed validator being "down" twice

View File

@ -21,6 +21,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
// TODO remove dependencies on staking (should only refer to validator set type from sdk)
@ -36,13 +37,15 @@ var (
sdk.ValAddress(pks[1].Address()),
sdk.ValAddress(pks[2].Address()),
}
initCoins = sdk.TokensFromConsensusPower(200)
initTokens = sdk.TokensFromConsensusPower(200)
initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens))
)
func createTestCodec() *codec.Codec {
cdc := codec.New()
sdk.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
supply.RegisterCodec(cdc)
bank.RegisterCodec(cdc)
staking.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
@ -54,6 +57,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keySlashing := sdk.NewKVStoreKey(StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
db := dbm.NewMemDB()
@ -61,6 +65,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyStaking, sdk.StoreTypeTransient, nil)
ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
@ -71,18 +76,29 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
ck := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, ck, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
bk := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, supply.DefaultCodespace,
[]string{auth.FeeCollectorName}, []string{}, []string{staking.NotBondedPoolName, staking.BondedPoolName})
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(len(addrs)))))
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
genesis := staking.DefaultGenesisState()
genesis.Pool.NotBondedTokens = initCoins.MulRaw(int64(len(addrs)))
// set module accounts
feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName, supply.Basic)
notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner)
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner)
_ = staking.InitGenesis(ctx, sk, accountKeeper, genesis)
supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc)
supplyKeeper.SetModuleAccount(ctx, bondPool)
supplyKeeper.SetModuleAccount(ctx, notBondedPool)
_ = staking.InitGenesis(ctx, sk, accountKeeper, supplyKeeper, genesis)
for _, addr := range addrs {
_, err = ck.AddCoins(ctx, sdk.AccAddress(addr), sdk.Coins{
{sk.GetParams(ctx).BondDenom, initCoins},
})
_, err = bk.AddCoins(ctx, sdk.AccAddress(addr), initCoins)
}
require.Nil(t, err)
paramstore := paramsKeeper.Subspace(DefaultParamspace)
@ -93,7 +109,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
InitGenesis(ctx, keeper, sk, GenesisState{defaults, nil, nil})
})
return ctx, ck, sk, paramstore, keeper
return ctx, bk, sk, paramstore, keeper
}
func newPubKey(pk string) (res crypto.PubKey) {

View File

@ -31,6 +31,8 @@ const (
DefaultUnbondingTime = types.DefaultUnbondingTime
DefaultMaxValidators = types.DefaultMaxValidators
DefaultMaxEntries = types.DefaultMaxEntries
NotBondedPoolName = types.NotBondedPoolName
BondedPoolName = types.BondedPoolName
QueryValidators = types.QueryValidators
QueryValidator = types.QueryValidator
QueryDelegatorDelegations = types.QueryDelegatorDelegations
@ -56,7 +58,7 @@ var (
// functions aliases
RegisterInvariants = keeper.RegisterInvariants
AllInvariants = keeper.AllInvariants
SupplyInvariants = keeper.SupplyInvariants
ModuleAccountInvariants = keeper.ModuleAccountInvariants
NonNegativePowerInvariant = keeper.NonNegativePowerInvariant
PositiveDelegationInvariant = keeper.PositiveDelegationInvariant
DelegatorSharesInvariant = keeper.DelegatorSharesInvariant
@ -166,9 +168,7 @@ var (
DefaultParams = types.DefaultParams
MustUnmarshalParams = types.MustUnmarshalParams
UnmarshalParams = types.UnmarshalParams
InitialPool = types.InitialPool
MustUnmarshalPool = types.MustUnmarshalPool
UnmarshalPool = types.UnmarshalPool
NewPool = types.NewPool
NewQueryDelegatorParams = types.NewQueryDelegatorParams
NewQueryValidatorParams = types.NewQueryValidatorParams
NewQueryBondsParams = types.NewQueryBondsParams
@ -223,7 +223,6 @@ type (
RedelegationEntryResponse = types.RedelegationEntryResponse
RedelegationResponses = types.RedelegationResponses
CodeType = types.CodeType
AccountKeeper = types.AccountKeeper
GenesisState = types.GenesisState
LastValidatorPower = types.LastValidatorPower
MultiStakingHooks = types.MultiStakingHooks

View File

@ -11,6 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/cosmos/cosmos-sdk/x/supply"
)
// getMockApp returns an initialized mock application for this module.
@ -18,18 +19,22 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) {
mApp := mock.NewApp()
RegisterCodec(mApp.Cdc)
supply.RegisterCodec(mApp.Cdc)
keyStaking := sdk.NewKVStoreKey(StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(TStoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
keeper := NewKeeper(mApp.Cdc, keyStaking, tkeyStaking, bankKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bankKeeper, supply.DefaultCodespace,
[]string{auth.FeeCollectorName}, []string{}, []string{types.NotBondedPoolName, types.BondedPoolName})
keeper := NewKeeper(mApp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
mApp.Router().AddRoute(RouterKey, NewHandler(keeper))
mApp.SetEndBlocker(getEndBlocker(keeper))
mApp.SetInitChainer(getInitChainer(mApp, keeper, mApp.AccountKeeper))
mApp.SetInitChainer(getInitChainer(mApp, keeper, mApp.AccountKeeper, supplyKeeper))
require.NoError(t, mApp.CompleteSetup(keyStaking, tkeyStaking))
require.NoError(t, mApp.CompleteSetup(keyStaking, tkeyStaking, keySupply))
return mApp, keeper
}
@ -46,15 +51,21 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
// getInitChainer initializes the chainer of the mock app and sets the genesis
// state. It returns an empty ResponseInitChain.
func getInitChainer(mapp *mock.App, keeper Keeper, accountKeeper types.AccountKeeper) sdk.InitChainer {
func getInitChainer(mapp *mock.App, keeper Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
stakingGenesis := DefaultGenesisState()
tokens := sdk.TokensFromConsensusPower(100000)
stakingGenesis.Pool.NotBondedTokens = tokens
// set module accounts
feeCollector := supply.NewEmptyModuleAccount(auth.FeeCollectorName, supply.Basic)
notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner)
bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner)
validators := InitGenesis(ctx, keeper, accountKeeper, stakingGenesis)
supplyKeeper.SetModuleAccount(ctx, feeCollector)
supplyKeeper.SetModuleAccount(ctx, bondPool)
supplyKeeper.SetModuleAccount(ctx, notBondedPool)
stakingGenesis := DefaultGenesisState()
validators := InitGenesis(ctx, keeper, accountKeeper, supplyKeeper, stakingGenesis)
return abci.ResponseInitChain{
Validators: validators,
}

View File

@ -545,17 +545,22 @@ $ %s query staking pool
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, _, err := cliCtx.QueryStore(types.PoolKey, storeName)
bz, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/pool", storeName), nil)
if err != nil {
return err
}
return cliCtx.PrintOutput(types.MustUnmarshalPool(cdc, res))
var pool types.Pool
if err := cdc.UnmarshalJSON(bz, &pool); err != nil {
return err
}
return cliCtx.PrintOutput(pool)
},
}
}
// GetCmdQueryPool implements the params query command.
// GetCmdQueryParams implements the params query command.
func GetCmdQueryParams(storeName string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "params",

View File

@ -7,7 +7,6 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/staking/exported"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
@ -17,7 +16,11 @@ import (
// setting the indexes. In addition, it also sets any delegations found in
// data. Finally, it updates the bonded validators.
// Returns final validator set after applying all declaration and delegations
func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeeper, data types.GenesisState) (res []abci.ValidatorUpdate) {
func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeeper,
supplyKeeper types.SupplyKeeper, data types.GenesisState) (res []abci.ValidatorUpdate) {
bondedTokens := sdk.ZeroInt()
notBondedTokens := sdk.ZeroInt()
// We need to pretend to be "n blocks before genesis", where "n" is the
// validator update delay, so that e.g. slashing periods are correctly
@ -26,18 +29,6 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
// genesis.json are in block 0.
ctx = ctx.WithBlockHeight(1 - sdk.ValidatorUpdateDelay)
// manually set the total supply for staking based on accounts if not provided
if data.Pool.NotBondedTokens.IsZero() {
accountKeeper.IterateAccounts(ctx,
func(acc auth.Account) (stop bool) {
data.Pool.NotBondedTokens = data.Pool.NotBondedTokens.
Add(acc.GetCoins().AmountOf(data.Params.BondDenom))
return false
},
)
}
keeper.SetPool(ctx, data.Pool) // TODO remove pool from genesis data and always calculate?
keeper.SetParams(ctx, data.Params)
keeper.SetLastTotalPower(ctx, data.LastTotalPower)
@ -53,10 +44,19 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
keeper.AfterValidatorCreated(ctx, validator.OperatorAddress)
}
// Set timeslice if necessary
// update timeslice if necessary
if validator.IsUnbonding() {
keeper.InsertValidatorQueue(ctx, validator)
}
switch validator.GetStatus() {
case sdk.Bonded:
bondedTokens = bondedTokens.Add(validator.GetTokens())
case sdk.Unbonding, sdk.Unbonded:
notBondedTokens = notBondedTokens.Add(validator.GetTokens())
default:
panic("invalid validator status")
}
}
for _, delegation := range data.Delegations {
@ -65,6 +65,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
keeper.BeforeDelegationCreated(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
}
keeper.SetDelegation(ctx, delegation)
// Call the after-modification hook if not exported
if !data.Exported {
keeper.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
@ -75,6 +76,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
keeper.SetUnbondingDelegation(ctx, ubd)
for _, entry := range ubd.Entries {
keeper.InsertUBDQueue(ctx, ubd, entry.CompletionTime)
notBondedTokens = notBondedTokens.Add(entry.Balance)
}
}
@ -85,13 +87,43 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
}
}
bondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, bondedTokens))
notBondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, notBondedTokens))
// check if the unbonded and bonded pools accounts exists
bondedPool := keeper.GetBondedPool(ctx)
if bondedPool == nil {
panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName))
}
// TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862
// add coins if not provided on genesis
if bondedPool.GetCoins().IsZero() {
if err := bondedPool.SetCoins(bondedCoins); err != nil {
panic(err)
}
supplyKeeper.SetModuleAccount(ctx, bondedPool)
}
notBondedPool := keeper.GetNotBondedPool(ctx)
if notBondedPool == nil {
panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName))
}
if notBondedPool.GetCoins().IsZero() {
if err := notBondedPool.SetCoins(notBondedCoins); err != nil {
panic(err)
}
supplyKeeper.SetModuleAccount(ctx, notBondedPool)
}
// don't need to run Tendermint updates if we exported
if data.Exported {
for _, lv := range data.LastValidatorPowers {
keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power)
validator, found := keeper.GetValidator(ctx, lv.Address)
if !found {
panic("expected validator, not found")
panic(fmt.Sprintf("validator %s not found", lv.Address))
}
update := validator.ABCIValidatorUpdate()
update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block
@ -108,7 +140,6 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
// GenesisState will contain the pool, params, validators, and bonds found in
// the keeper.
func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
pool := keeper.GetPool(ctx)
params := keeper.GetParams(ctx)
lastTotalPower := keeper.GetLastTotalPower(ctx)
validators := keeper.GetAllValidators(ctx)
@ -130,7 +161,6 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
})
return types.GenesisState{
Pool: pool,
Params: params,
LastTotalPower: lastTotalPower,
LastValidatorPowers: lastValidatorPowers,

View File

@ -17,10 +17,8 @@ import (
)
func TestInitGenesis(t *testing.T) {
ctx, accKeeper, keeper := keep.CreateTestInput(t, false, 1000)
ctx, accKeeper, keeper, supplyKeeper := keep.CreateTestInput(t, false, 1000)
pool := keeper.GetPool(ctx)
pool.BondedTokens = sdk.TokensFromConsensusPower(2)
valTokens := sdk.TokensFromConsensusPower(1)
params := keeper.GetParams(ctx)
@ -41,11 +39,10 @@ func TestInitGenesis(t *testing.T) {
validators[1].Tokens = valTokens
validators[1].DelegatorShares = valTokens.ToDec()
genesisState := types.NewGenesisState(pool, params, validators, delegations)
vals := InitGenesis(ctx, keeper, accKeeper, genesisState)
genesisState := types.NewGenesisState(params, validators, delegations)
vals := InitGenesis(ctx, keeper, accKeeper, supplyKeeper, genesisState)
actualGenesis := ExportGenesis(ctx, keeper)
require.Equal(t, genesisState.Pool, actualGenesis.Pool)
require.Equal(t, genesisState.Params, actualGenesis.Params)
require.Equal(t, genesisState.Delegations, actualGenesis.Delegations)
require.EqualValues(t, keeper.GetAllValidators(ctx), actualGenesis.Validators)
@ -71,12 +68,7 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) {
size := 200
require.True(t, size > 100)
ctx, accKeeper, keeper := keep.CreateTestInput(t, false, 1000)
// Assigning 2 to the first 100 vals, 1 to the rest
pool := keeper.GetPool(ctx)
bondedTokens := sdk.TokensFromConsensusPower(int64(200 + (size - 100)))
pool.BondedTokens = bondedTokens
ctx, accKeeper, keeper, supplyKeeper := keep.CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
delegations := []Delegation{}
@ -96,8 +88,8 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) {
validators[i].DelegatorShares = tokens.ToDec()
}
genesisState := types.NewGenesisState(pool, params, validators, delegations)
vals := InitGenesis(ctx, keeper, accKeeper, genesisState)
genesisState := types.NewGenesisState(params, validators, delegations)
vals := InitGenesis(ctx, keeper, accKeeper, supplyKeeper, genesisState)
abcivals := make([]abci.ValidatorUpdate, 100)
for i, val := range validators[:100] {

View File

@ -146,7 +146,8 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
// 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.DelegatorAddress, msg.Value.Amount, validator, true)
// NOTE source will always be from a wallet which are unbonded
_, err = k.Delegate(ctx, msg.DelegatorAddress, msg.Value.Amount, sdk.Unbonded, validator, true)
if err != nil {
return err.Result()
}
@ -232,7 +233,8 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
return ErrBadDenom(k.Codespace()).Result()
}
_, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, validator, true)
// NOTE: source funds are always unbonded
_, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, sdk.Unbonded, validator, true)
if err != nil {
return err.Result()
}

View File

@ -34,7 +34,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
initPower := int64(1000000)
initBond := sdk.TokensFromConsensusPower(initPower)
ctx, _, keeper := keep.CreateTestInput(t, false, initPower)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower)
_ = setInstantUnbondPeriod(keeper, ctx)
// create validator
@ -114,7 +114,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
}
func TestDuplicatesMsgCreateValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
addr1, addr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
pk1, pk2 := keep.PKs[0], keep.PKs[1]
@ -166,7 +166,7 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
}
func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
addr := sdk.ValAddress(keep.Addrs[0])
invalidPk := secp256k1.GenPrivKey().PubKey()
@ -185,7 +185,7 @@ func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) {
}
func TestLegacyValidatorDelegations(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, int64(1000))
ctx, _, keeper, _ := keep.CreateTestInput(t, false, int64(1000))
setInstantUnbondPeriod(keeper, ctx)
bondAmount := sdk.TokensFromConsensusPower(10)
@ -279,7 +279,7 @@ func TestLegacyValidatorDelegations(t *testing.T) {
func TestIncrementsMsgDelegate(t *testing.T) {
initPower := int64(1000)
initBond := sdk.TokensFromConsensusPower(initPower)
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initPower)
ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower)
params := keeper.GetParams(ctx)
bondAmount := sdk.TokensFromConsensusPower(10)
@ -306,8 +306,8 @@ func TestIncrementsMsgDelegate(t *testing.T) {
require.True(t, found)
require.Equal(t, bondAmount, bond.Shares.RoundInt())
pool := keeper.GetPool(ctx)
require.Equal(t, bondAmount, pool.BondedTokens)
bondedTokens := keeper.TotalBondedTokens(ctx)
require.Equal(t, bondAmount.Int64(), bondedTokens.Int64())
// just send the same msgbond multiple times
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount)
@ -349,7 +349,7 @@ func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) {
initPower := int64(100)
initBond := sdk.TokensFromConsensusPower(100)
ctx, _, keeper := keep.CreateTestInput(t, false, initPower)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower)
_ = setInstantUnbondPeriod(keeper, ctx)
// create validator
@ -381,7 +381,7 @@ func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) {
initPower := int64(100)
initBond := sdk.TokensFromConsensusPower(100)
ctx, _, keeper := keep.CreateTestInput(t, false, initPower)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower)
_ = setInstantUnbondPeriod(keeper, ctx)
// create validator
@ -411,7 +411,7 @@ func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) {
func TestIncrementsMsgUnbond(t *testing.T) {
initPower := int64(1000)
initBond := sdk.TokensFromConsensusPower(initPower)
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initPower)
ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower)
params := setInstantUnbondPeriod(keeper, ctx)
denom := params.BondDenom
@ -469,13 +469,13 @@ func TestIncrementsMsgUnbond(t *testing.T) {
gotDelegatorShares := validator.DelegatorShares.RoundInt()
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
require.Equal(t, expBond, gotBond,
require.Equal(t, expBond.Int64(), gotBond.Int64(),
"i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n",
i, expBond, gotBond, validator, bond)
require.Equal(t, expDelegatorShares, gotDelegatorShares,
require.Equal(t, expDelegatorShares.Int64(), gotDelegatorShares.Int64(),
"i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n",
i, expDelegatorShares, gotDelegatorShares, validator, bond)
require.Equal(t, expDelegatorAcc, gotDelegatorAcc,
require.Equal(t, expDelegatorAcc.Int64(), gotDelegatorAcc.Int64(),
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n",
i, expDelegatorAcc, gotDelegatorAcc, validator, bond)
}
@ -509,7 +509,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
func TestMultipleMsgCreateValidator(t *testing.T) {
initPower := int64(1000)
initTokens := sdk.TokensFromConsensusPower(initPower)
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initPower)
ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower)
params := setInstantUnbondPeriod(keeper, ctx)
validatorAddrs := []sdk.ValAddress{
@ -576,7 +576,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
}
func TestMultipleMsgDelegate(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddrs := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1:]
_ = setInstantUnbondPeriod(keeper, ctx)
@ -618,7 +618,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
}
func TestJailValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
_ = setInstantUnbondPeriod(keeper, ctx)
@ -664,7 +664,7 @@ func TestJailValidator(t *testing.T) {
}
func TestValidatorQueue(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
// set the unbonding time
@ -722,7 +722,7 @@ func TestValidatorQueue(t *testing.T) {
}
func TestUnbondingPeriod(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr := sdk.ValAddress(keep.Addrs[0])
// set the unbonding time
@ -768,7 +768,7 @@ func TestUnbondingPeriod(t *testing.T) {
}
func TestUnbondingFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
// create the validator
@ -809,7 +809,7 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) {
}
func TestRedelegationPeriod(t *testing.T) {
ctx, AccMapper, keeper := keep.CreateTestInput(t, false, 1000)
ctx, AccMapper, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
denom := keeper.GetParams(ctx).BondDenom
@ -868,7 +868,7 @@ func TestRedelegationPeriod(t *testing.T) {
}
func TestTransitiveRedelegation(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr := sdk.ValAddress(keep.Addrs[0])
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
validatorAddr3 := sdk.ValAddress(keep.Addrs[2])
@ -911,7 +911,7 @@ func TestTransitiveRedelegation(t *testing.T) {
}
func TestMultipleRedelegationAtSameTime(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
valAddr := sdk.ValAddress(keep.Addrs[0])
valAddr2 := sdk.ValAddress(keep.Addrs[1])
@ -963,7 +963,7 @@ func TestMultipleRedelegationAtSameTime(t *testing.T) {
}
func TestMultipleRedelegationAtUniqueTimes(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
valAddr := sdk.ValAddress(keep.Addrs[0])
valAddr2 := sdk.ValAddress(keep.Addrs[1])
@ -1017,7 +1017,7 @@ func TestMultipleRedelegationAtUniqueTimes(t *testing.T) {
}
func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
valAddr := sdk.ValAddress(keep.Addrs[0])
// set the unbonding time
@ -1064,7 +1064,7 @@ func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) {
}
func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
valAddr := sdk.ValAddress(keep.Addrs[0])
// set the unbonding time
@ -1118,7 +1118,7 @@ func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) {
}
func TestUnbondingWhenExcessValidators(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
validatorAddr1 := sdk.ValAddress(keep.Addrs[0])
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
validatorAddr3 := sdk.ValAddress(keep.Addrs[2])
@ -1174,7 +1174,7 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
}
func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
consAddr0 := sdk.ConsAddress(keep.PKs[0].Address())
@ -1283,7 +1283,7 @@ func TestInvalidMsg(t *testing.T) {
}
func TestInvalidCoinDenom(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000)
valA, valB, delAddr := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
valTokens := sdk.TokensFromConsensusPower(100)

View File

@ -88,31 +88,6 @@ func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) expor
return val
}
// total staking tokens supply which is bonded
func (k Keeper) TotalBondedTokens(ctx sdk.Context) sdk.Int {
pool := k.GetPool(ctx)
return pool.BondedTokens
}
// total staking tokens supply bonded and unbonded
func (k Keeper) TotalTokens(ctx sdk.Context) sdk.Int {
pool := k.GetPool(ctx)
return pool.TokenSupply()
}
// the fraction of the staking tokens which are currently bonded
func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec {
pool := k.GetPool(ctx)
return pool.BondedRatio()
}
// when minting new tokens
func (k Keeper) InflateSupply(ctx sdk.Context, newTokens sdk.Int) {
pool := k.GetPool(ctx)
pool.NotBondedTokens = pool.NotBondedTokens.Add(newTokens)
k.SetPool(ctx, pool)
}
//_______________________________________________________________________
// Delegation Set

View File

@ -25,16 +25,26 @@ func (k Keeper) GetDelegation(ctx sdk.Context,
return delegation, true
}
// return all delegations used during genesis dump
func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
// IterateAllDelegations iterate through all of the delegations
func (k Keeper) IterateAllDelegations(ctx sdk.Context, cb func(delegation types.Delegation) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
delegations = append(delegations, delegation)
if cb(delegation) {
break
}
}
}
// GetAllDelegations returns all delegations used during genesis dump
func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
k.IterateAllDelegations(ctx, func(delegation types.Delegation) bool {
delegations = append(delegations, delegation)
return false
})
return delegations
}
@ -292,7 +302,7 @@ func (k Keeper) GetRedelegation(ctx sdk.Context,
}
// return all redelegations from a particular validator
func (k Keeper) GetRedelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) {
func (k Keeper) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.GetREDsFromValSrcIndexKey(valAddr))
defer iterator.Close()
@ -445,7 +455,8 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time
}
// Perform a delegation, set/update everything necessary within the store.
func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int,
// tokenSrc indicates the bond status of the incoming funds.
func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int, tokenSrc sdk.BondStatus,
validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) {
// In some situations, the exchange rate becomes invalid, e.g. if
@ -468,11 +479,46 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.In
k.BeforeDelegationCreated(ctx, delAddr, validator.OperatorAddress)
}
// if subtractAccount is true then we are
// performing a delegation and not a redelegation, thus the source tokens are
// all non bonded
if subtractAccount {
err := k.bankKeeper.DelegateCoins(ctx, delegation.DelegatorAddress, sdk.Coins{sdk.NewCoin(k.GetParams(ctx).BondDenom, bondAmt)})
if tokenSrc == sdk.Bonded {
panic("delegation token source cannot be bonded")
}
var sendName string
switch {
case validator.IsBonded():
sendName = types.BondedPoolName
case validator.IsUnbonding(), validator.IsUnbonded():
sendName = types.NotBondedPoolName
default:
panic("invalid validator status")
}
coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), bondAmt))
err := k.supplyKeeper.DelegateCoinsFromAccountToModule(ctx, delegation.DelegatorAddress, sendName, coins)
if err != nil {
return sdk.Dec{}, err
}
} else {
// potentially transfer tokens between pools, if
switch {
case tokenSrc == sdk.Bonded && validator.IsBonded():
// do nothing
case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && !validator.IsBonded():
// do nothing
case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && validator.IsBonded():
// transfer pools
k.notBondedTokensToBonded(ctx, bondAmt)
case tokenSrc == sdk.Bonded && !validator.IsBonded():
// transfer pools
k.bondedTokensToNotBonded(ctx, bondAmt)
default:
panic("unknown token source bond status")
}
}
validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt)
@ -514,7 +560,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
// subtract shares from delegation
delegation.Shares = delegation.Shares.Sub(shares)
isValidatorOperator := bytes.Equal(delegation.DelegatorAddress, validator.OperatorAddress)
isValidatorOperator := delegation.DelegatorAddress.Equals(validator.OperatorAddress)
// if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum
// trigger a jail validator
@ -535,6 +581,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
}
// remove the shares and coins from the validator
// NOTE that the amount is later (in keeper.Delegation) moved between staking module pools
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
if validator.DelegatorShares.IsZero() && validator.IsUnbonded() {
@ -583,6 +630,11 @@ func (k Keeper) Undelegate(
ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec,
) (time.Time, sdk.Error) {
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return time.Time{}, types.ErrNoDelegatorForAddress(k.Codespace())
}
if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) {
return time.Time{}, types.ErrMaxUnbondingDelegationEntries(k.Codespace())
}
@ -592,6 +644,11 @@ func (k Keeper) Undelegate(
return time.Time{}, err
}
// transfer the validator tokens to the not bonded pool
if validator.IsBonded() {
k.bondedTokensToNotBonded(ctx, returnAmount)
}
completionTime := ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
ubd := k.SetUnbondingDelegationEntry(ctx, delAddr, valAddr, ctx.BlockHeight(), completionTime, returnAmount)
k.InsertUBDQueue(ctx, ubd, completionTime)
@ -620,7 +677,8 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress,
// track undelegation only when remaining or truncated shares are non-zero
if !entry.Balance.IsZero() {
err := k.bankKeeper.UndelegateCoins(ctx, ubd.DelegatorAddress, sdk.Coins{sdk.NewCoin(k.GetParams(ctx).BondDenom, entry.Balance)})
amt := sdk.NewCoins(sdk.NewCoin(k.GetParams(ctx).BondDenom, entry.Balance))
err := k.supplyKeeper.UndelegateCoinsFromModuleToAccount(ctx, types.NotBondedPoolName, ubd.DelegatorAddress, amt)
if err != nil {
return err
}
@ -647,6 +705,16 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
return time.Time{}, types.ErrSelfRedelegation(k.Codespace())
}
dstValidator, found := k.GetValidator(ctx, valDstAddr)
if !found {
return time.Time{}, types.ErrBadRedelegationDst(k.Codespace())
}
srcValidator, found := k.GetValidator(ctx, valSrcAddr)
if !found {
return time.Time{}, types.ErrBadRedelegationDst(k.Codespace())
}
// check if this is a transitive redelegation
if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) {
return time.Time{}, types.ErrTransitiveRedelegation(k.Codespace())
@ -664,12 +732,8 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
if returnAmount.IsZero() {
return time.Time{}, types.ErrVerySmallRedelegation(k.Codespace())
}
dstValidator, found := k.GetValidator(ctx, valDstAddr)
if !found {
return time.Time{}, types.ErrBadRedelegationDst(k.Codespace())
}
sharesCreated, err := k.Delegate(ctx, delAddr, returnAmount, dstValidator, false)
sharesCreated, err := k.Delegate(ctx, delAddr, returnAmount, srcValidator.GetStatus(), dstValidator, false)
if err != nil {
return time.Time{}, err
}

View File

@ -13,18 +13,16 @@ import (
// tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations
func TestDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 10)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 10)
//construct the validators
amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)}
var validators [3]types.Validator
for i, amt := range amts {
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
validators[i], _ = validators[i].AddTokensFromDel(amt)
}
keeper.SetPool(ctx, pool)
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true)
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true)
validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true)
@ -130,7 +128,7 @@ func TestDelegation(t *testing.T) {
// tests Get/Set/Remove UnbondingDelegation
func TestUnbondingDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0,
time.Unix(0, 0), sdk.NewInt(5))
@ -169,21 +167,23 @@ func TestUnbondingDelegation(t *testing.T) {
}
func TestUnbondDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
startTokens := sdk.TokensFromConsensusPower(10)
pool.NotBondedTokens = startTokens
//create a validator and a delegator to that validator
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
// create a validator and a delegator to that validator
// note this validator starts not-bonded
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, startTokens)
require.Equal(t, startTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.Equal(t, startTokens, pool.BondedTokens)
require.Equal(t, startTokens, validator.BondedTokens())
validator, issuedShares := validator.AddTokensFromDel(startTokens)
require.Equal(t, startTokens, issuedShares.RoundInt())
validator = TestingUpdateValidator(keeper, ctx, validator, true)
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
@ -197,37 +197,41 @@ func TestUnbondDelegation(t *testing.T) {
require.True(t, found)
validator, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
pool = keeper.GetPool(ctx)
remainingTokens := startTokens.Sub(bondTokens)
require.Equal(t, remainingTokens, delegation.Shares.RoundInt())
require.Equal(t, remainingTokens, validator.BondedTokens())
require.Equal(t, bondTokens, pool.NotBondedTokens, "%v", pool)
require.Equal(t, remainingTokens, pool.BondedTokens)
}
func TestUnbondingDelegationsMaxEntries(t *testing.T) {
ctx, ak, keeper := CreateTestInput(t, false, 1)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 1)
startTokens := sdk.TokensFromConsensusPower(10)
pool.NotBondedTokens = startTokens
bondDenom := keeper.BondDenom(ctx)
bondedPool := keeper.GetBondedPool(ctx)
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens)))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
// create a validator and a delegator to that validator
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, startTokens)
require.Equal(t, startTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.Equal(t, startTokens, pool.BondedTokens)
require.Equal(t, startTokens, validator.BondedTokens())
validator, issuedShares := validator.AddTokensFromDel(startTokens)
require.Equal(t, startTokens, issuedShares.RoundInt())
validator = TestingUpdateValidator(keeper, ctx, validator, true)
require.True(sdk.IntEq(t, startTokens, validator.BondedTokens()))
require.True(t, validator.IsBonded())
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
maxEntries := keeper.MaxEntries(ctx)
oldBonded := keeper.GetBondedPool(ctx).GetCoins().AmountOf(bondDenom)
oldNotBonded := keeper.GetNotBondedPool(ctx).GetCoins().AmountOf(bondDenom)
// should all pass
var completionTime time.Time
for i := uint16(0); i < maxEntries; i++ {
@ -236,87 +240,97 @@ func TestUnbondingDelegationsMaxEntries(t *testing.T) {
require.NoError(t, err)
}
// delegator shares should be reduced by 7
delegator, _ := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
require.Equal(t, sdk.NewDec(9999993), delegator.GetShares())
bondedPool = keeper.GetBondedPool(ctx)
notBondedPool = keeper.GetNotBondedPool(ctx)
require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded.SubRaw(int64(maxEntries))))
require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.AddRaw(int64(maxEntries))))
acc := ak.GetAccount(ctx, addrDels[0])
require.Equal(t, int64(1000000), acc.GetCoins().AmountOf("stake").Int64())
oldBonded = bondedPool.GetCoins().AmountOf(bondDenom)
oldNotBonded = notBondedPool.GetCoins().AmountOf(bondDenom)
// an additional unbond should fail due to max entries
_, err := keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1))
_, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1))
require.Error(t, err)
// delegator shares should not reduced
delegator, _ = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
require.Equal(t, sdk.NewDec(9999993), delegator.GetShares())
bondedPool = keeper.GetBondedPool(ctx)
notBondedPool = keeper.GetNotBondedPool(ctx)
require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded))
require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded))
// mature unbonding delegations
ctx = ctx.WithBlockTime(completionTime)
err = keeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0])
require.NoError(t, err)
// delegator account balance should be increased by 7
acc = ak.GetAccount(ctx, addrDels[0])
require.Equal(t, int64(1000007), acc.GetCoins().AmountOf("stake").Int64())
bondedPool = keeper.GetBondedPool(ctx)
notBondedPool = keeper.GetNotBondedPool(ctx)
require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded))
require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.SubRaw(int64(maxEntries))))
completionTime, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(3))
require.NoError(t, err)
// delegator shares should be reduced by 3
dele2, _ := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
require.Equal(t, sdk.NewDec(9999990), dele2.GetShares())
// mature unbonding delegations
ctx = ctx.WithBlockTime(completionTime)
err = keeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0])
require.NoError(t, err)
// delegator account balance should be increased by 3
acc = ak.GetAccount(ctx, addrDels[0])
require.Equal(t, int64(1000010), acc.GetCoins().AmountOf("stake").Int64())
oldNotBonded = notBondedPool.GetCoins().AmountOf(bondDenom)
// unbonding should work again
_, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1))
require.NoError(t, err)
bondedPool = keeper.GetBondedPool(ctx)
notBondedPool = keeper.GetNotBondedPool(ctx)
require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded.SubRaw(1)))
require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.AddRaw(1)))
}
// test undelegating self delegation from a validator pushing it below MinSelfDelegation
// shift it from the bonded to unbonding state and jailed
func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
startTokens := sdk.TokensFromConsensusPower(20)
pool.NotBondedTokens = startTokens
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
delTokens := sdk.TokensFromConsensusPower(10)
delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens))
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator.MinSelfDelegation = valTokens
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
validator.MinSelfDelegation = delTokens
validator, issuedShares := validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
// add bonded tokens to pool for delegations
bondedPool := keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator)
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.True(t, validator.IsBonded())
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
// add bonded tokens to pool for delegations
bondedPool = keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.TokensFromConsensusPower(6).ToDec())
_, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.TokensFromConsensusPower(6).ToDec())
require.NoError(t, err)
// end block
@ -331,34 +345,53 @@ func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) {
}
func TestUndelegateFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
startTokens := sdk.TokensFromConsensusPower(20)
pool.NotBondedTokens = startTokens
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
delTokens := sdk.TokensFromConsensusPower(10)
delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens))
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator, issuedShares := validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
bondedPool := keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator)
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
bondedPool = keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
bondedPool = keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
header := ctx.BlockHeader()
blockHeight := int64(10)
header.Height = blockHeight
@ -368,7 +401,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
// unbond the all self-delegation to put validator in unbonding state
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec())
_, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec())
require.NoError(t, err)
// end block
@ -400,32 +433,40 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
}
func TestUndelegateFromUnbondedValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1)
pool := keeper.GetPool(ctx)
startTokens := sdk.TokensFromConsensusPower(20)
pool.NotBondedTokens = startTokens
ctx, _, keeper, _ := CreateTestInput(t, false, 1)
delTokens := sdk.TokensFromConsensusPower(10)
delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
// create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
bondedPool := keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator)
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
@ -433,7 +474,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
ctx = ctx.WithBlockTime(time.Unix(333, 0))
// unbond the all self-delegation to put validator in unbonding state
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec())
_, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec())
require.NoError(t, err)
// end block
@ -471,32 +512,43 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
}
func TestUnbondingAllDelegationFromValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
startTokens := sdk.TokensFromConsensusPower(20)
pool.NotBondedTokens = startTokens
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
delTokens := sdk.TokensFromConsensusPower(10)
delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator)
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
bondedPool := keeper.GetBondedPool(ctx)
err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
@ -504,7 +556,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) {
ctx = ctx.WithBlockTime(time.Unix(333, 0))
// unbond the all self-delegation to put validator in unbonding state
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec())
_, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec())
require.NoError(t, err)
// end block
@ -530,8 +582,8 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) {
}
// Make sure that that the retrieving the delegations doesn't affect the state
func TestGetRedelegationsFromValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
func TestGetRedelegationsFromSrcValidator(t *testing.T) {
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
time.Unix(0, 0), sdk.NewInt(5),
@ -543,19 +595,19 @@ func TestGetRedelegationsFromValidator(t *testing.T) {
require.True(t, found)
// get the redelegations one time
redelegations := keeper.GetRedelegationsFromValidator(ctx, addrVals[0])
redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0])
require.Equal(t, 1, len(redelegations))
require.True(t, redelegations[0].Equal(resBond))
// get the redelegations a second time, should be exactly the same
redelegations = keeper.GetRedelegationsFromValidator(ctx, addrVals[0])
redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0])
require.Equal(t, 1, len(redelegations))
require.True(t, redelegations[0].Equal(resBond))
}
// tests Get/Set/Remove/Has UnbondingDelegation
func TestRedelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
time.Unix(0, 0), sdk.NewInt(5),
@ -570,7 +622,7 @@ func TestRedelegation(t *testing.T) {
resRed, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.True(t, found)
redelegations := keeper.GetRedelegationsFromValidator(ctx, addrVals[0])
redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0])
require.Equal(t, 1, len(redelegations))
require.True(t, redelegations[0].Equal(resRed))
@ -594,7 +646,7 @@ func TestRedelegation(t *testing.T) {
require.True(t, found)
require.True(t, rd.Equal(resRed))
redelegations = keeper.GetRedelegationsFromValidator(ctx, addrVals[0])
redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0])
require.Equal(t, 1, len(redelegations))
require.True(t, redelegations[0].Equal(resRed))
@ -615,52 +667,58 @@ func TestRedelegation(t *testing.T) {
}
func TestRedelegateToSameValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
startTokens := sdk.TokensFromConsensusPower(30)
pool.NotBondedTokens = startTokens
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
valTokens := sdk.TokensFromConsensusPower(10)
startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), valTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
// create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
require.True(t, validator.IsBonded())
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
_, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5))
_, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5))
require.Error(t, err)
}
func TestRedelegationMaxEntries(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
startTokens := sdk.TokensFromConsensusPower(20)
pool.NotBondedTokens = startTokens
startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
// create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens)
validator2, issuedShares = validator2.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
pool.BondedTokens = pool.BondedTokens.Add(valTokens)
keeper.SetPool(ctx, pool)
validator2 = TestingUpdateValidator(keeper, ctx, validator2, true)
require.Equal(t, sdk.Bonded, validator2.Status)
@ -675,7 +733,7 @@ func TestRedelegationMaxEntries(t *testing.T) {
}
// an additional redelegation should fail due to max entries
_, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1))
_, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1))
require.Error(t, err)
// mature redelegations
@ -689,44 +747,45 @@ func TestRedelegationMaxEntries(t *testing.T) {
}
func TestRedelegateSelfDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
startTokens := sdk.TokensFromConsensusPower(30)
pool.NotBondedTokens = startTokens
startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens)
validator2, issuedShares = validator2.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
pool.BondedTokens = pool.BondedTokens.Add(valTokens)
keeper.SetPool(ctx, pool)
validator2 = TestingUpdateValidator(keeper, ctx, validator2, true)
require.Equal(t, sdk.Bonded, validator2.Status)
// create a second delegation to validator 1
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
_, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], delTokens.ToDec())
_, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], delTokens.ToDec())
require.NoError(t, err)
// end block
@ -740,20 +799,23 @@ func TestRedelegateSelfDelegation(t *testing.T) {
}
func TestRedelegateFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
startTokens := sdk.TokensFromConsensusPower(30)
pool.NotBondedTokens = startTokens
startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
@ -761,19 +823,16 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator)
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens)
validator2, issuedShares = validator2.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator2 = TestingUpdateValidator(keeper, ctx, validator2, true)
header := ctx.BlockHeader()
@ -784,7 +843,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec())
_, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec())
require.NoError(t, err)
// end block
@ -819,20 +878,23 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
}
func TestRedelegateFromUnbondedValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
ctx, _, keeper, _ := CreateTestInput(t, false, 0)
startTokens := sdk.TokensFromConsensusPower(30)
pool.NotBondedTokens = startTokens
startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens))
// add bonded tokens to pool for delegations
notBondedPool := keeper.GetNotBondedPool(ctx)
err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins))
require.NoError(t, err)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
valTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
validator, issuedShares := validator.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares)
keeper.SetDelegation(ctx, selfDelegation)
@ -840,19 +902,16 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator)
delTokens := sdk.TokensFromConsensusPower(10)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens)
validator, issuedShares = validator.AddTokensFromDel(delTokens)
require.Equal(t, delTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator, true)
pool = keeper.GetPool(ctx)
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
keeper.SetDelegation(ctx, delegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens)
validator2, issuedShares = validator2.AddTokensFromDel(valTokens)
require.Equal(t, valTokens, issuedShares.RoundInt())
keeper.SetPool(ctx, pool)
validator2 = TestingUpdateValidator(keeper, ctx, validator2, true)
require.Equal(t, sdk.Bonded, validator2.Status)
@ -860,7 +919,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
ctx = ctx.WithBlockTime(time.Unix(333, 0))
// unbond the all self-delegation to put validator in unbonding state
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec())
_, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec())
require.NoError(t, err)
// end block

View File

@ -5,17 +5,15 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/staking/exported"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// register all staking invariants
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper, f types.FeeCollectionKeeper,
d types.DistributionKeeper, am types.AccountKeeper) {
// RegisterInvariants registers all staking invariants
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
ir.RegisterRoute(types.ModuleName, "supply",
SupplyInvariants(k, f, d, am))
ir.RegisterRoute(types.ModuleName, "module-accounts",
ModuleAccountInvariants(k))
ir.RegisterRoute(types.ModuleName, "nonnegative-power",
NonNegativePowerInvariant(k))
ir.RegisterRoute(types.ModuleName, "positive-delegation",
@ -25,11 +23,10 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper, f types.FeeCollectio
}
// AllInvariants runs all invariants of the staking module.
func AllInvariants(k Keeper, f types.FeeCollectionKeeper,
d types.DistributionKeeper, am types.AccountKeeper) sdk.Invariant {
func AllInvariants(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
err := SupplyInvariants(k, f, d, am)(ctx)
err := ModuleAccountInvariants(k)(ctx)
if err != nil {
return err
}
@ -44,66 +41,57 @@ func AllInvariants(k Keeper, f types.FeeCollectionKeeper,
return err
}
err = DelegatorSharesInvariant(k)(ctx)
if err != nil {
return err
}
return nil
return DelegatorSharesInvariant(k)(ctx)
}
}
// SupplyInvariants checks that the total supply reflects all held not-bonded tokens, bonded tokens, and unbonding delegations
// nolint: unparam
func SupplyInvariants(k Keeper, f types.FeeCollectionKeeper,
d types.DistributionKeeper, am types.AccountKeeper) sdk.Invariant {
// ModuleAccountInvariants checks that the bonded and notBonded ModuleAccounts pools
// reflects the tokens actively bonded and not bonded
func ModuleAccountInvariants(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
pool := k.GetPool(ctx)
bonded := sdk.ZeroInt()
notBonded := sdk.ZeroInt()
bondedPool := k.GetBondedPool(ctx)
notBondedPool := k.GetNotBondedPool(ctx)
bondDenom := k.BondDenom(ctx)
loose := sdk.ZeroDec()
bonded := sdk.ZeroDec()
am.IterateAccounts(ctx, func(acc auth.Account) bool {
loose = loose.Add(acc.GetCoins().AmountOf(k.BondDenom(ctx)).ToDec())
return false
})
k.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) bool {
for _, entry := range ubd.Entries {
loose = loose.Add(entry.Balance.ToDec())
}
return false
})
k.IterateValidators(ctx, func(_ int64, validator exported.ValidatorI) bool {
switch validator.GetStatus() {
case sdk.Bonded:
bonded = bonded.Add(validator.GetBondedTokens().ToDec())
bonded = bonded.Add(validator.GetTokens())
case sdk.Unbonding, sdk.Unbonded:
loose = loose.Add(validator.GetTokens().ToDec())
notBonded = notBonded.Add(validator.GetTokens())
default:
panic("invalid validator status")
}
// add yet-to-be-withdrawn
loose = loose.Add(d.GetValidatorOutstandingRewardsCoins(ctx, validator.GetOperator()).AmountOf(k.BondDenom(ctx)))
return false
})
// add outstanding fees
loose = loose.Add(f.GetCollectedFees(ctx).AmountOf(k.BondDenom(ctx)).ToDec())
k.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) bool {
for _, entry := range ubd.Entries {
notBonded = notBonded.Add(entry.Balance)
}
return false
})
// add community pool
loose = loose.Add(d.GetFeePoolCommunityCoins(ctx).AmountOf(k.BondDenom(ctx)))
// Not-bonded tokens should equal coin supply plus unbonding delegations
// plus tokens on unbonded validators
if !pool.NotBondedTokens.ToDec().Equal(loose) {
return fmt.Errorf("loose token invariance:\n"+
"\tpool.NotBondedTokens: %v\n"+
"\tsum of account tokens: %v", pool.NotBondedTokens, loose)
}
poolBonded := bondedPool.GetCoins().AmountOf(bondDenom)
poolNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom)
// Bonded tokens should equal sum of tokens with bonded validators
if !pool.BondedTokens.ToDec().Equal(bonded) {
return fmt.Errorf("bonded token invariance:\n"+
"\tpool.BondedTokens: %v\n"+
"\tsum of account tokens: %v", pool.BondedTokens, bonded)
// Not-bonded tokens should equal unbonding delegations plus tokens on unbonded validators
if !poolBonded.Equal(bonded) || !poolNotBonded.Equal(notBonded) {
return fmt.Errorf(
"bonded token invariance:\n"+
"\tPool's bonded tokens: %v\n"+
"\tsum of bonded tokens: %v\n"+
"not bonded token invariance:\n"+
"\tPool's not bonded tokens: %v\n"+
"\tsum of not bonded tokens: %v\n"+
"module accounts total (bonded + not bonded):\n"+
"\tModule Accounts' tokens: %v\n"+
"\tsum tokens: %v\n",
poolBonded, bonded, poolNotBonded, notBonded, poolBonded.Add(poolNotBonded), bonded.Add(notBonded),
)
}
return nil

View File

@ -25,7 +25,7 @@ type Keeper struct {
storeKey sdk.StoreKey
storeTKey sdk.StoreKey
cdc *codec.Codec
bankKeeper types.BankKeeper
supplyKeeper types.SupplyKeeper
hooks types.StakingHooks
paramstore params.Subspace
validatorCache map[string]cachedValidator
@ -35,21 +35,30 @@ type Keeper struct {
codespace sdk.CodespaceType
}
func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, bk types.BankKeeper,
// NewKeeper creates a new staking Keeper instance
func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, supplyKeeper types.SupplyKeeper,
paramstore params.Subspace, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
// ensure bonded and not bonded module accounts are set
if addr := supplyKeeper.GetModuleAddress(types.BondedPoolName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName))
}
if addr := supplyKeeper.GetModuleAddress(types.NotBondedPoolName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName))
}
return Keeper{
storeKey: key,
storeTKey: tkey,
cdc: cdc,
bankKeeper: bk,
supplyKeeper: supplyKeeper,
paramstore: paramstore.WithKeyTable(ParamKeyTable()),
hooks: nil,
validatorCache: make(map[string]cachedValidator, aminoCacheSize),
validatorCacheList: list.New(),
codespace: codespace,
}
return keeper
}
// Logger returns a module-specific logger.
@ -71,24 +80,6 @@ func (k Keeper) Codespace() sdk.CodespaceType {
return k.codespace
}
// get the pool
func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(types.PoolKey)
if b == nil {
panic("stored pool should not have been nil")
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &pool)
return
}
// set the pool
func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinaryLengthPrefixed(pool)
store.Set(types.PoolKey, b)
}
// Load the last total validator power.
func (k Keeper) GetLastTotalPower(ctx sdk.Context) (power sdk.Int) {
store := ctx.KVStore(k.storeKey)

Some files were not shown because too many files have changed in this diff Show More