diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 31b912a9d..6d62377c2 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -88,11 +88,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) - app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) - app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) + app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams) + app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamSpace), app.RegisterCodespace(stake.DefaultCodespace)) + app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamSpace), app.RegisterCodespace(slashing.DefaultCodespace)) app.stakeKeeper = app.stakeKeeper.WithHooks(app.slashingKeeper.Hooks()) - app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) + app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamSpace), app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) // register message routes @@ -184,7 +184,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci } // load the address to pubkey map - slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) + slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) err = GaiaValidateGenesisState(genesisState) @@ -193,6 +193,11 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci panic(err) } + err = slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) + if err != nil { + panic(err) + } + return abci.ResponseInitChain{ Validators: validators, } diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index bcd16a176..fcb2fb408 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/db" @@ -21,8 +22,9 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { } genesisState := GenesisState{ - Accounts: genaccs, - StakeData: stake.DefaultGenesisState(), + Accounts: genaccs, + StakeData: stake.DefaultGenesisState(), + SlashingData: slashing.HubDefaultGenesisState(), } stateBytes, err := codec.MarshalJSONIndent(gapp.cdc, genesisState) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 84f0ea7fa..d2edd7f25 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -12,6 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" @@ -32,9 +33,10 @@ var ( // State to Unmarshal type GenesisState struct { - Accounts []GenesisAccount `json:"accounts"` - StakeData stake.GenesisState `json:"stake"` - GovData gov.GenesisState `json:"gov"` + Accounts []GenesisAccount `json:"accounts"` + StakeData stake.GenesisState `json:"stake"` + GovData gov.GenesisState `json:"gov"` + SlashingData slashing.GenesisState `json:"slashing"` } // GenesisAccount doesn't need pubkey or sequence @@ -170,6 +172,8 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat // start with the default staking genesis state stakeData := stake.DefaultGenesisState() + slashingData := slashing.HubDefaultGenesisState() + // get genesis flag account information genaccs := make([]GenesisAccount, len(appGenTxs)) for i, appGenTx := range appGenTxs { @@ -192,9 +196,10 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat // create the final app state genesisState = GenesisState{ - Accounts: genaccs, - StakeData: stakeData, - GovData: gov.DefaultGenesisState(), + Accounts: genaccs, + StakeData: stakeData, + GovData: gov.DefaultGenesisState(), + SlashingData: slashingData, } return } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index db08d872f..f6a21ba57 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation" "github.com/cosmos/cosmos-sdk/x/mock/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing" slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation" stake "github.com/cosmos/cosmos-sdk/x/stake" stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation" @@ -74,9 +75,10 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { stakeGenesis.Params.InflationMax = sdk.NewDec(0) stakeGenesis.Params.InflationMin = sdk.NewDec(0) genesis := GenesisState{ - Accounts: genesisAccounts, - StakeData: stakeGenesis, - GovData: govGenesis, + Accounts: genesisAccounts, + StakeData: stakeGenesis, + SlashingData: slashing.HubDefaultGenesisState(), + GovData: govGenesis, } // Marshal genesis diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index 182e789be..51ecac802 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -135,6 +135,7 @@ type GaiaApp struct { tkeyStake *sdk.TransientStoreKey keySlashing *sdk.KVStoreKey keyParams *sdk.KVStoreKey + tkeyParams *sdk.TransientStoreKey // Manage getting and setting accounts accountMapper auth.AccountMapper @@ -161,6 +162,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp tkeyStake: sdk.NewTransientStoreKey("transient_stake"), keySlashing: sdk.NewKVStoreKey("slashing"), keyParams: sdk.NewKVStoreKey("params"), + tkeyParams: sdk.NewTransientStoreKey("transient_params"), } // define the accountMapper @@ -172,9 +174,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) - app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) - app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) + app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams) + app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamSpace), app.RegisterCodespace(stake.DefaultCodespace)) + app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamSpace), app.RegisterCodespace(slashing.DefaultCodespace)) // register message routes app.Router(). @@ -186,7 +188,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing) + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing, app.keyParams) + app.MountStore(app.tkeyParams, sdk.StoreTypeTransient) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) @@ -252,6 +255,11 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") } + err = slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) + if err != nil { + panic(err) + } + return abci.ResponseInitChain{ Validators: validators, } diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go index eb3ae7dd4..9cdbd6495 100644 --- a/store/gaskvstore_test.go +++ b/store/gaskvstore_test.go @@ -13,13 +13,13 @@ import ( func newGasKVStore() KVStore { meter := sdk.NewGasMeter(1000) mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, sdk.DefaultGasConfig(), mem) + return NewGasKVStore(meter, sdk.DefaultKVGasConfig(), mem) } -func TestGasKVStoreBasic(t *testing.T) { +func TestKVGasKVStoreBasic(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(1000) - st := NewGasKVStore(meter, sdk.DefaultGasConfig(), mem) + st := NewGasKVStore(meter, sdk.DefaultKVGasConfig(), mem) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") st.Set(keyFmt(1), valFmt(1)) require.Equal(t, valFmt(1), st.Get(keyFmt(1))) @@ -28,10 +28,10 @@ func TestGasKVStoreBasic(t *testing.T) { require.Equal(t, meter.GasConsumed(), sdk.Gas(193)) } -func TestGasKVStoreIterator(t *testing.T) { +func TestKVGasKVStoreIterator(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(1000) - st := NewGasKVStore(meter, sdk.DefaultGasConfig(), mem) + st := NewGasKVStore(meter, sdk.DefaultKVGasConfig(), mem) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") st.Set(keyFmt(1), valFmt(1)) @@ -52,24 +52,24 @@ func TestGasKVStoreIterator(t *testing.T) { require.Equal(t, meter.GasConsumed(), sdk.Gas(384)) } -func TestGasKVStoreOutOfGasSet(t *testing.T) { +func TestKVGasKVStoreOutOfGasSet(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(0) - st := NewGasKVStore(meter, sdk.DefaultGasConfig(), mem) + st := NewGasKVStore(meter, sdk.DefaultKVGasConfig(), mem) require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") } -func TestGasKVStoreOutOfGasIterator(t *testing.T) { +func TestKVGasKVStoreOutOfGasIterator(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(200) - st := NewGasKVStore(meter, sdk.DefaultGasConfig(), mem) + st := NewGasKVStore(meter, sdk.DefaultKVGasConfig(), mem) st.Set(keyFmt(1), valFmt(1)) iterator := st.Iterator(nil, nil) iterator.Next() require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas") } -func testGasKVStoreWrap(t *testing.T, store KVStore) { +func testKVGasKVStoreWrap(t *testing.T, store KVStore) { meter := sdk.NewGasMeter(10000) store = store.Gas(meter, sdk.GasConfig{HasCost: 10}) @@ -84,22 +84,22 @@ func testGasKVStoreWrap(t *testing.T, store KVStore) { require.Equal(t, int64(40), meter.GasConsumed()) } -func TestGasKVStoreWrap(t *testing.T) { +func TestKVGasKVStoreWrap(t *testing.T) { db := dbm.NewMemDB() tree, _ := newTree(t, db) iavl := newIAVLStore(tree, numRecent, storeEvery) - testGasKVStoreWrap(t, iavl) + testKVGasKVStoreWrap(t, iavl) st := NewCacheKVStore(iavl) - testGasKVStoreWrap(t, st) + testKVGasKVStoreWrap(t, st) pref := st.Prefix([]byte("prefix")) - testGasKVStoreWrap(t, pref) + testKVGasKVStoreWrap(t, pref) dsa := dbStoreAdapter{dbm.NewMemDB()} - testGasKVStoreWrap(t, dsa) + testKVGasKVStoreWrap(t, dsa) ts := newTransientStore() - testGasKVStoreWrap(t, ts) + testKVGasKVStoreWrap(t, ts) } diff --git a/store/prefixstore.go b/store/prefixstore.go index 835a21038..8a4a87133 100644 --- a/store/prefixstore.go +++ b/store/prefixstore.go @@ -13,6 +13,18 @@ type prefixStore struct { prefix []byte } +func clone(bz []byte) (res []byte) { + res = make([]byte, len(bz)) + copy(res, bz) + return +} + +func (s prefixStore) key(key []byte) (res []byte) { + res = clone(s.prefix) + res = append(res, key...) + return +} + // Implements Store func (s prefixStore) GetStoreType() StoreType { return s.parent.GetStoreType() @@ -30,22 +42,23 @@ func (s prefixStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap // Implements KVStore func (s prefixStore) Get(key []byte) []byte { - return s.parent.Get(append(s.prefix, key...)) + res := s.parent.Get(s.key(key)) + return res } // Implements KVStore func (s prefixStore) Has(key []byte) bool { - return s.parent.Has(append(s.prefix, key...)) + return s.parent.Has(s.key(key)) } // Implements KVStore func (s prefixStore) Set(key, value []byte) { - s.parent.Set(append(s.prefix, key...), value) + s.parent.Set(s.key(key), value) } // Implements KVStore func (s prefixStore) Delete(key []byte) { - s.parent.Delete(append(s.prefix, key...)) + s.parent.Delete(s.key(key)) } // Implements KVStore @@ -60,27 +73,40 @@ func (s prefixStore) Gas(meter GasMeter, config GasConfig) KVStore { // Implements KVStore func (s prefixStore) Iterator(start, end []byte) Iterator { + newstart := clone(s.prefix) + newstart = append(newstart, start...) + + var newend []byte if end == nil { - end = sdk.PrefixEndBytes(s.prefix) + newend = sdk.PrefixEndBytes(s.prefix) } else { - end = append(s.prefix, end...) + newend = clone(s.prefix) + newend = append(newend, end...) } + return prefixIterator{ prefix: s.prefix, - iter: s.parent.Iterator(append(s.prefix, start...), end), + iter: s.parent.Iterator(newstart, newend), } } // Implements KVStore func (s prefixStore) ReverseIterator(start, end []byte) Iterator { + newstart := make([]byte, len(s.prefix), len(start)) + copy(newstart, s.prefix) + newstart = append(newstart, start...) + + newend := make([]byte, len(s.prefix)+len(end)) if end == nil { - end = sdk.PrefixEndBytes(s.prefix) + newend = sdk.PrefixEndBytes(s.prefix) } else { - end = append(s.prefix, end...) + copy(newend, s.prefix) + newend = append(newend, end...) } + return prefixIterator{ prefix: s.prefix, - iter: s.parent.ReverseIterator(start, end), + iter: s.parent.ReverseIterator(newstart, newend), } } diff --git a/store/prefixstore_test.go b/store/prefixstore_test.go index 49bc68037..7e5675d65 100644 --- a/store/prefixstore_test.go +++ b/store/prefixstore_test.go @@ -81,7 +81,7 @@ func TestCacheKVStorePrefix(t *testing.T) { func TestGasKVStorePrefix(t *testing.T) { meter := sdk.NewGasMeter(100000000) mem := dbStoreAdapter{dbm.NewMemDB()} - gasStore := NewGasKVStore(meter, sdk.DefaultGasConfig(), mem) + gasStore := NewGasKVStore(meter, sdk.DefaultKVGasConfig(), mem) testPrefixStore(t, gasStore, []byte("test")) } diff --git a/types/context.go b/types/context.go index fd2e07688..a90ec2b59 100644 --- a/types/context.go +++ b/types/context.go @@ -48,6 +48,8 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo c = c.WithVoteInfos(nil) c = c.WithGasMeter(NewInfiniteGasMeter()) c = c.WithMinimumFees(Coins{}) + c = c.WithKVGasConfig(KVGasConfig()) + c = c.WithTransientGasConfig(TransientGasConfig()) return c } @@ -73,12 +75,12 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return c.multiStore().GetKVStore(key).Gas(c.GasMeter(), cachedDefaultGasConfig) + return c.multiStore().GetKVStore(key).Gas(c.GasMeter(), c.KVGasConfig()) } // TransientStore fetches a TransientStore from the MultiStore. func (c Context) TransientStore(key StoreKey) KVStore { - return c.multiStore().GetKVStore(key).Gas(c.GasMeter(), cachedTransientGasConfig) + return c.multiStore().GetKVStore(key).Gas(c.GasMeter(), c.TransientGasConfig()) } //---------------------------------------- @@ -141,6 +143,8 @@ const ( contextKeyVoteInfos contextKeyGasMeter contextKeyMinimumFees + contextKeyKVGasConfig + contextKeyTransientGasConfig ) // NOTE: Do not expose MultiStore. @@ -176,6 +180,12 @@ func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Co func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) } +func (c Context) KVGasConfig() GasConfig { return c.Value(contextKeyKVGasConfig).(GasConfig) } + +func (c Context) TransientGasConfig() GasConfig { + return c.Value(contextKeyTransientGasConfig).(GasConfig) +} + func (c Context) WithBlockHeader(header abci.Header) Context { var _ proto.Message = &header // for cloning. return c.withValue(contextKeyBlockHeader, header) @@ -212,6 +222,12 @@ func (c Context) WithIsCheckTx(isCheckTx bool) Context { func (c Context) WithMinimumFees(minFees Coins) Context { return c.withValue(contextKeyMinimumFees, minFees) } +func (c Context) WithKVGasConfig(config GasConfig) Context { + return c.withValue(contextKeyKVGasConfig, config) +} +func (c Context) WithTransientGasConfig(config GasConfig) Context { + return c.withValue(contextKeyTransientGasConfig, config) +} // Cache the multistore and return a new cached context. The cached context is // written to the context when writeCache is called. diff --git a/types/gas.go b/types/gas.go index cce4975c3..7b0347467 100644 --- a/types/gas.go +++ b/types/gas.go @@ -13,7 +13,7 @@ const ( ) var ( - cachedDefaultGasConfig = DefaultGasConfig() + cachedKVGasConfig = KVGasConfig() cachedTransientGasConfig = TransientGasConfig() ) @@ -86,8 +86,8 @@ type GasConfig struct { IterNextCostFlat Gas } -// DefaultGasConfig returns a default gas config for KVStores. -func DefaultGasConfig() GasConfig { +// KVGasConfig returns a default gas config for KVStores. +func KVGasConfig() GasConfig { return GasConfig{ HasCost: 10, DeleteCost: 10, @@ -103,5 +103,5 @@ func DefaultGasConfig() GasConfig { // TransientGasConfig returns a default gas config for TransientStores. func TransientGasConfig() GasConfig { // TODO: define gasconfig for transient stores - return DefaultGasConfig() + return KVGasConfig() } diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 80e5a205c..313cad28d 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -9,15 +9,28 @@ import ( // nolint const ( - ParamStoreKeyDepositProcedure = "gov/depositprocedure" - ParamStoreKeyVotingProcedure = "gov/votingprocedure" - ParamStoreKeyTallyingProcedure = "gov/tallyingprocedure" + DefaultParamSpace = "gov" +) + +// nolint - Paramstore key constructor +func ParamStoreKeyDepositProcedure() params.Key { return params.NewKey("depositprocedure") } +func ParamStoreKeyVotingProcedure() params.Key { return params.NewKey("votingprocedure") } +func ParamStoreKeyTallyingProcedure() params.Key { return params.NewKey("tallyingprocedure") } + +// Cached parameter store keys +var ( + paramStoreKeyDepositProcedure = ParamStoreKeyDepositProcedure() + paramStoreKeyVotingProcedure = ParamStoreKeyVotingProcedure() + paramStoreKeyTallyingProcedure = ParamStoreKeyTallyingProcedure() ) // Governance Keeper type Keeper struct { - // The reference to the ParamSetter to get and set Global Params - ps params.Setter + // The reference to the Param Keeper to get and set Global Params + pk params.Keeper + + // The reference to the Paramstore to get and set gov specific params + ps params.Space // The reference to the CoinKeeper to modify balances ck bank.Keeper @@ -43,9 +56,10 @@ type Keeper struct { // - depositing funds into proposals, and activating upon sufficient funds being deposited // - users voting on proposals, with weight proportional to stake in the system // - and tallying the result of the vote. -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ps params.Setter, ck bank.Keeper, ds sdk.DelegationSet, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, pk params.Keeper, ps params.Space, ck bank.Keeper, ds sdk.DelegationSet, codespace sdk.CodespaceType) Keeper { return Keeper{ storeKey: key, + pk: pk, ps: ps, ck: ck, ds: ds, @@ -210,7 +224,7 @@ func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { // nolint: errcheck func (keeper Keeper) GetDepositProcedure(ctx sdk.Context) DepositProcedure { var depositProcedure DepositProcedure - keeper.ps.Get(ctx, ParamStoreKeyDepositProcedure, &depositProcedure) + keeper.ps.Get(ctx, paramStoreKeyDepositProcedure, &depositProcedure) return depositProcedure } @@ -218,7 +232,7 @@ func (keeper Keeper) GetDepositProcedure(ctx sdk.Context) DepositProcedure { // nolint: errcheck func (keeper Keeper) GetVotingProcedure(ctx sdk.Context) VotingProcedure { var votingProcedure VotingProcedure - keeper.ps.Get(ctx, ParamStoreKeyVotingProcedure, &votingProcedure) + keeper.ps.Get(ctx, paramStoreKeyVotingProcedure, &votingProcedure) return votingProcedure } @@ -226,23 +240,23 @@ func (keeper Keeper) GetVotingProcedure(ctx sdk.Context) VotingProcedure { // nolint: errcheck func (keeper Keeper) GetTallyingProcedure(ctx sdk.Context) TallyingProcedure { var tallyingProcedure TallyingProcedure - keeper.ps.Get(ctx, ParamStoreKeyTallyingProcedure, &tallyingProcedure) + keeper.ps.Get(ctx, paramStoreKeyTallyingProcedure, &tallyingProcedure) return tallyingProcedure } // nolint: errcheck func (keeper Keeper) setDepositProcedure(ctx sdk.Context, depositProcedure DepositProcedure) { - keeper.ps.Set(ctx, ParamStoreKeyDepositProcedure, &depositProcedure) + keeper.ps.Set(ctx, paramStoreKeyDepositProcedure, &depositProcedure) } // nolint: errcheck func (keeper Keeper) setVotingProcedure(ctx sdk.Context, votingProcedure VotingProcedure) { - keeper.ps.Set(ctx, ParamStoreKeyVotingProcedure, &votingProcedure) + keeper.ps.Set(ctx, paramStoreKeyVotingProcedure, &votingProcedure) } // nolint: errcheck func (keeper Keeper) setTallyingProcedure(ctx sdk.Context, tallyingProcedure TallyingProcedure) { - keeper.ps.Set(ctx, ParamStoreKeyTallyingProcedure, &tallyingProcedure) + keeper.ps.Set(ctx, paramStoreKeyTallyingProcedure, &tallyingProcedure) } // ===================================================== diff --git a/x/gov/simulation/sim_test.go b/x/gov/simulation/sim_test.go index b0317d116..cc9f710c9 100644 --- a/x/gov/simulation/sim_test.go +++ b/x/gov/simulation/sim_test.go @@ -28,16 +28,19 @@ func TestGovWithRandomMessages(t *testing.T) { stakeTKey := sdk.NewTransientStoreKey("transient_stake") stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, stake.DefaultCodespace) paramKey := sdk.NewKVStoreKey("params") - paramKeeper := params.NewKeeper(mapp.Cdc, paramKey) + paramTKey := sdk.NewTransientStoreKey("transient_params") + paramKeeper := params.NewKeeper(mapp.Cdc, paramKey, paramTKey) + stakeKey := sdk.NewKVStoreKey("stake") + stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, coinKeeper, paramKeeper.Subspace(stake.DefaultParamSpace), stake.DefaultCodespace) govKey := sdk.NewKVStoreKey("gov") - govKeeper := gov.NewKeeper(mapp.Cdc, govKey, paramKeeper.Setter(), bankKeeper, stakeKeeper, gov.DefaultCodespace) + govKeeper := gov.NewKeeper(mapp.Cdc, govKey, paramKeeper, paramKeeper.Subspace(gov.DefaultParamSpace), bankKeeper, stakeKeeper, gov.DefaultCodespace) mapp.Router().AddRoute("gov", gov.NewHandler(govKeeper)) mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { gov.EndBlocker(ctx, govKeeper) return abci.ResponseEndBlock{} }) - err := mapp.CompleteSetup(stakeKey, stakeTKey, paramKey, govKey) + err := mapp.CompleteSetup(stakeKey, stakeTKey, paramKey, paramTKey, govKey) if err != nil { panic(err) } diff --git a/x/gov/test_common.go b/x/gov/test_common.go index fdede91eb..45c885971 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -27,20 +27,22 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper, RegisterCodec(mapp.Cdc) keyGlobalParams := sdk.NewKVStoreKey("params") + tkeyGlobalParams := sdk.NewTransientStoreKey("transient_params") keyStake := sdk.NewKVStoreKey("stake") tkeyStake := sdk.NewTransientStoreKey("transient_stake") keyGov := sdk.NewKVStoreKey("gov") - pk := params.NewKeeper(mapp.Cdc, keyGlobalParams) + pk := params.NewKeeper(mapp.Cdc, keyGlobalParams, tkeyGlobalParams) ck := bank.NewBaseKeeper(mapp.AccountMapper) - sk := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, ck, mapp.RegisterCodespace(stake.DefaultCodespace)) - keeper := NewKeeper(mapp.Cdc, keyGov, pk.Setter(), ck, sk, DefaultCodespace) + sk := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, ck, pk.Subspace(stake.DefaultParamSpace), mapp.RegisterCodespace(stake.DefaultCodespace)) + keeper := NewKeeper(mapp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace) + mapp.Router().AddRoute("gov", NewHandler(keeper)) mapp.SetEndBlocker(getEndBlocker(keeper)) mapp.SetInitChainer(getInitChainer(mapp, keeper, sk)) - require.NoError(t, mapp.CompleteSetup(keyStake, keyGov, keyGlobalParams, tkeyStake)) + require.NoError(t, mapp.CompleteSetup(keyStake, tkeyStake, keyGov, keyGlobalParams, tkeyGlobalParams)) genAccs, addrs, pubKeys, privKeys := mock.CreateGenAccounts(numGenAccs, sdk.Coins{sdk.NewInt64Coin("steak", 42)}) diff --git a/x/params/client/utils/utils.go b/x/params/client/utils/utils.go new file mode 100644 index 000000000..edd6375df --- /dev/null +++ b/x/params/client/utils/utils.go @@ -0,0 +1,25 @@ +package utils + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/x/params" +) + +// TODO: remove hardcoded storename +const storeName = "params" + +// Query parameters from node with CLIContext +func QueryParams(cliCtx context.CLIContext, subStoreName string, ps params.ParamStruct) error { + m := make(map[string][]byte) + + for _, p := range ps.KeyFieldPairs() { + key := p.Key.String() + bz, err := cliCtx.QueryStore([]byte(subStoreName+"/"+key), storeName) + if err != nil { + return err + } + m[key] = bz + } + + return params.UnmarshalParamsFromMap(m, cliCtx.Codec, ps) +} diff --git a/x/params/keeper.go b/x/params/keeper.go index ca158ad42..2130fac5f 100644 --- a/x/params/keeper.go +++ b/x/params/keeper.go @@ -1,406 +1,53 @@ package params import ( - "fmt" - "reflect" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/params/space" ) -// Keeper manages global parameter store +// Keeper of the global paramstore type Keeper struct { - cdc *codec.Codec - key sdk.StoreKey + cdc *codec.Codec + key sdk.StoreKey + tkey sdk.StoreKey + + spaces map[string]*Space } -// NewKeeper constructs a new Keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey) Keeper { - return Keeper{ - cdc: cdc, - key: key, - } -} +// NewKeeper construct a params keeper +func NewKeeper(cdc *codec.Codec, key *sdk.KVStoreKey, tkey *sdk.TransientStoreKey) (k Keeper) { + k = Keeper{ + cdc: cdc, + key: key, + tkey: tkey, -// InitKeeper constructs a new Keeper with initial parameters -// nolint: errcheck -func InitKeeper(ctx sdk.Context, cdc *codec.Codec, key sdk.StoreKey, params ...interface{}) Keeper { - if len(params)%2 != 0 { - panic("Odd params list length for InitKeeper") - } - - k := NewKeeper(cdc, key) - - for i := 0; i < len(params); i += 2 { - k.set(ctx, params[i].(string), params[i+1]) + spaces: make(map[string]*Space), } return k } -// get automatically unmarshalls parameter to pointer -func (k Keeper) get(ctx sdk.Context, key string, ptr interface{}) error { - store := ctx.KVStore(k.key) - bz := store.Get([]byte(key)) - return k.cdc.UnmarshalBinary(bz, ptr) -} - -// getRaw returns raw byte slice -func (k Keeper) getRaw(ctx sdk.Context, key string) []byte { - store := ctx.KVStore(k.key) - return store.Get([]byte(key)) -} - -// set automatically marshalls and type check parameter -func (k Keeper) set(ctx sdk.Context, key string, param interface{}) error { - store := ctx.KVStore(k.key) - bz := store.Get([]byte(key)) - if bz != nil { - ptrty := reflect.PtrTo(reflect.TypeOf(param)) - ptr := reflect.New(ptrty).Interface() - - if k.cdc.UnmarshalBinary(bz, ptr) != nil { - return fmt.Errorf("Type mismatch with stored param and provided param") - } +// Allocate substore used for keepers +func (k Keeper) Subspace(space string) Space { + _, ok := k.spaces[space] + if ok { + panic("subspace already occupied") } - bz, err := k.cdc.MarshalBinary(param) - if err != nil { - return err + return k.UnsafeSubspace(space) +} + +// Get substore without checking existing allocation +func (k Keeper) UnsafeSubspace(spacename string) Space { + if spacename == "" { + panic("cannot use empty string for subspace") } - store.Set([]byte(key), bz) - return nil -} + space := space.NewSpace(k.cdc, k.key, k.tkey, spacename) -// setRaw sets raw byte slice -func (k Keeper) setRaw(ctx sdk.Context, key string, param []byte) { - store := ctx.KVStore(k.key) - store.Set([]byte(key), param) -} + k.spaces[spacename] = &space -// Getter returns readonly struct -func (k Keeper) Getter() Getter { - return Getter{k} -} - -// Setter returns read/write struct -func (k Keeper) Setter() Setter { - return Setter{Getter{k}} -} - -// Getter exposes methods related with only getting params -type Getter struct { - k Keeper -} - -// Get exposes get -func (k Getter) Get(ctx sdk.Context, key string, ptr interface{}) error { - return k.k.get(ctx, key, ptr) -} - -// GetRaw exposes getRaw -func (k Getter) GetRaw(ctx sdk.Context, key string) []byte { - return k.k.getRaw(ctx, key) -} - -// GetString is helper function for string params -func (k Getter) GetString(ctx sdk.Context, key string) (res string, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetBool is helper function for bool params -func (k Getter) GetBool(ctx sdk.Context, key string) (res bool, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetInt16 is helper function for int16 params -func (k Getter) GetInt16(ctx sdk.Context, key string) (res int16, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetInt32 is helper function for int32 params -func (k Getter) GetInt32(ctx sdk.Context, key string) (res int32, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetInt64 is helper function for int64 params -func (k Getter) GetInt64(ctx sdk.Context, key string) (res int64, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetUint16 is helper function for uint16 params -func (k Getter) GetUint16(ctx sdk.Context, key string) (res uint16, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetUint32 is helper function for uint32 params -func (k Getter) GetUint32(ctx sdk.Context, key string) (res uint32, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetUint64 is helper function for uint64 params -func (k Getter) GetUint64(ctx sdk.Context, key string) (res uint64, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetInt is helper function for sdk.Int params -func (k Getter) GetInt(ctx sdk.Context, key string) (res sdk.Int, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetUint is helper function for sdk.Uint params -func (k Getter) GetUint(ctx sdk.Context, key string) (res sdk.Uint, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetDec is helper function for decimal params -func (k Getter) GetDec(ctx sdk.Context, key string) (res sdk.Dec, err error) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - err = k.k.cdc.UnmarshalBinary(bz, &res) - return -} - -// GetStringWithDefault is helper function for string params with default value -func (k Getter) GetStringWithDefault(ctx sdk.Context, key string, def string) (res string) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetBoolWithDefault is helper function for bool params with default value -func (k Getter) GetBoolWithDefault(ctx sdk.Context, key string, def bool) (res bool) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetInt16WithDefault is helper function for int16 params with default value -func (k Getter) GetInt16WithDefault(ctx sdk.Context, key string, def int16) (res int16) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetInt32WithDefault is helper function for int32 params with default value -func (k Getter) GetInt32WithDefault(ctx sdk.Context, key string, def int32) (res int32) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetInt64WithDefault is helper function for int64 params with default value -func (k Getter) GetInt64WithDefault(ctx sdk.Context, key string, def int64) (res int64) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetUint16WithDefault is helper function for uint16 params with default value -func (k Getter) GetUint16WithDefault(ctx sdk.Context, key string, def uint16) (res uint16) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetUint32WithDefault is helper function for uint32 params with default value -func (k Getter) GetUint32WithDefault(ctx sdk.Context, key string, def uint32) (res uint32) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetUint64WithDefault is helper function for uint64 params with default value -func (k Getter) GetUint64WithDefault(ctx sdk.Context, key string, def uint64) (res uint64) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetIntWithDefault is helper function for sdk.Int params with default value -func (k Getter) GetIntWithDefault(ctx sdk.Context, key string, def sdk.Int) (res sdk.Int) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetUintWithDefault is helper function for sdk.Uint params with default value -func (k Getter) GetUintWithDefault(ctx sdk.Context, key string, def sdk.Uint) (res sdk.Uint) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// GetDecWithDefault is helper function for sdk.Dec params with default value -func (k Getter) GetDecWithDefault(ctx sdk.Context, key string, def sdk.Dec) (res sdk.Dec) { - store := ctx.KVStore(k.k.key) - bz := store.Get([]byte(key)) - if bz == nil { - return def - } - k.k.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// Setter exposes all methods including Set -type Setter struct { - Getter -} - -// Set exposes set -func (k Setter) Set(ctx sdk.Context, key string, param interface{}) error { - return k.k.set(ctx, key, param) -} - -// SetRaw exposes setRaw -func (k Setter) SetRaw(ctx sdk.Context, key string, param []byte) { - k.k.setRaw(ctx, key, param) -} - -// SetString is helper function for string params -func (k Setter) SetString(ctx sdk.Context, key string, param string) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetBool is helper function for bool params -func (k Setter) SetBool(ctx sdk.Context, key string, param bool) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetInt16 is helper function for int16 params -func (k Setter) SetInt16(ctx sdk.Context, key string, param int16) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetInt32 is helper function for int32 params -func (k Setter) SetInt32(ctx sdk.Context, key string, param int32) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetInt64 is helper function for int64 params -func (k Setter) SetInt64(ctx sdk.Context, key string, param int64) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetUint16 is helper function for uint16 params -func (k Setter) SetUint16(ctx sdk.Context, key string, param uint16) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetUint32 is helper function for uint32 params -func (k Setter) SetUint32(ctx sdk.Context, key string, param uint32) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetUint64 is helper function for uint64 params -func (k Setter) SetUint64(ctx sdk.Context, key string, param uint64) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetInt is helper function for sdk.Int params -func (k Setter) SetInt(ctx sdk.Context, key string, param sdk.Int) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetUint is helper function for sdk.Uint params -func (k Setter) SetUint(ctx sdk.Context, key string, param sdk.Uint) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } -} - -// SetDec is helper function for decimal params -func (k Setter) SetDec(ctx sdk.Context, key string, param sdk.Dec) { - if err := k.k.set(ctx, key, param); err != nil { - panic(err) - } + return space } diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go index 7eac7319f..d9dd1c108 100644 --- a/x/params/keeper_test.go +++ b/x/params/keeper_test.go @@ -1,9 +1,10 @@ package params import ( + "reflect" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" @@ -14,267 +15,123 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func defaultContext(key sdk.StoreKey) sdk.Context { +func defaultContext(key sdk.StoreKey, tkey sdk.StoreKey) sdk.Context { db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) cms.LoadLatestVersion() ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) return ctx } +type s struct{} + +func createTestCodec() *codec.Codec { + cdc := codec.NewCodec() + sdk.RegisterWire(cdc) + cdc.RegisterConcrete(s{}, "test/s", nil) + return cdc +} + func TestKeeper(t *testing.T) { kvs := []struct { - key string + key Key param int64 }{ - {"key1", 10}, - {"key2", 55}, - {"key3", 182}, - {"key4", 17582}, - {"key5", 2768554}, + {NewKey("key1"), 10}, + {NewKey("key2"), 55}, + {NewKey("key3"), 182}, + {NewKey("key4"), 17582}, + {NewKey("key5"), 2768554}, + {NewKey("space1", "key1"), 1157279}, + {NewKey("space1", "key2"), 9058701}, } skey := sdk.NewKVStoreKey("test") - ctx := defaultContext(skey) - setter := NewKeeper(codec.New(), skey).Setter() + tkey := sdk.NewTransientStoreKey("transient_test") + ctx := defaultContext(skey, tkey) + space := NewKeeper(codec.NewCodec(), skey, tkey).Subspace("test") for _, kv := range kvs { - err := setter.Set(ctx, kv.key, kv.param) - assert.Nil(t, err) + err := space.Set(ctx, kv.key, kv.param) + require.Nil(t, err) } for _, kv := range kvs { var param int64 - err := setter.Get(ctx, kv.key, ¶m) - assert.Nil(t, err) - assert.Equal(t, kv.param, param) + require.NotPanics(t, func() { space.Get(ctx, kv.key, ¶m) }) + require.Equal(t, kv.param, param) } cdc := codec.New() for _, kv := range kvs { var param int64 - bz := setter.GetRaw(ctx, kv.key) - err := cdc.UnmarshalBinary(bz, ¶m) - assert.Nil(t, err) - assert.Equal(t, kv.param, param) + bz := space.GetRaw(ctx, kv.key) + err := cdc.UnmarshalJSON(bz, ¶m) + require.Nil(t, err) + require.Equal(t, kv.param, param) } for _, kv := range kvs { var param bool - err := setter.Get(ctx, kv.key, ¶m) - assert.NotNil(t, err) + require.Panics(t, func() { space.Get(ctx, kv.key, ¶m) }) } for _, kv := range kvs { - err := setter.Set(ctx, kv.key, true) - assert.NotNil(t, err) + err := space.Set(ctx, kv.key, true) + require.NotNil(t, err) } } -func TestGetter(t *testing.T) { +func TestGet(t *testing.T) { key := sdk.NewKVStoreKey("test") - ctx := defaultContext(key) - keeper := NewKeeper(codec.New(), key) + tkey := sdk.NewTransientStoreKey("transient_test") + ctx := defaultContext(key, tkey) + keeper := NewKeeper(createTestCodec(), key, tkey) - g := keeper.Getter() - s := keeper.Setter() + space := keeper.Subspace("test") kvs := []struct { - key string + key Key param interface{} + zero interface{} + ptr interface{} }{ - {"string", "test"}, - {"bool", true}, - {"int16", int16(1)}, - {"int32", int32(1)}, - {"int64", int64(1)}, - {"uint16", uint16(1)}, - {"uint32", uint32(1)}, - {"uint64", uint64(1)}, - {"int", sdk.NewInt(1)}, - {"uint", sdk.NewUint(1)}, - {"rat", sdk.NewDec(1)}, + {NewKey("string"), "test", "", new(string)}, + {NewKey("bool"), true, false, new(bool)}, + {NewKey("int16"), int16(1), int16(0), new(int16)}, + {NewKey("int32"), int32(1), int32(0), new(int32)}, + {NewKey("int64"), int64(1), int64(0), new(int64)}, + {NewKey("uint16"), uint16(1), uint16(0), new(uint16)}, + {NewKey("uint32"), uint32(1), uint32(0), new(uint32)}, + {NewKey("uint64"), uint64(1), uint64(0), new(uint64)}, + /* + {NewKey("int"), sdk.NewInt(1), *new(sdk.Int), new(sdk.Int)}, + {NewKey("uint"), sdk.NewUint(1), *new(sdk.Uint), new(sdk.Uint)}, + {NewKey("dec"), sdk.NewDec(1), *new(sdk.Dec), new(sdk.Dec)}, + */ } - assert.NotPanics(t, func() { s.SetString(ctx, kvs[0].key, "test") }) - assert.NotPanics(t, func() { s.SetBool(ctx, kvs[1].key, true) }) - assert.NotPanics(t, func() { s.SetInt16(ctx, kvs[2].key, int16(1)) }) - assert.NotPanics(t, func() { s.SetInt32(ctx, kvs[3].key, int32(1)) }) - assert.NotPanics(t, func() { s.SetInt64(ctx, kvs[4].key, int64(1)) }) - assert.NotPanics(t, func() { s.SetUint16(ctx, kvs[5].key, uint16(1)) }) - assert.NotPanics(t, func() { s.SetUint32(ctx, kvs[6].key, uint32(1)) }) - assert.NotPanics(t, func() { s.SetUint64(ctx, kvs[7].key, uint64(1)) }) - assert.NotPanics(t, func() { s.SetInt(ctx, kvs[8].key, sdk.NewInt(1)) }) - assert.NotPanics(t, func() { s.SetUint(ctx, kvs[9].key, sdk.NewUint(1)) }) - assert.NotPanics(t, func() { s.SetDec(ctx, kvs[10].key, sdk.NewDec(1)) }) + for _, kv := range kvs { + require.NotPanics(t, func() { space.Set(ctx, kv.key, kv.param) }) + } - var res interface{} - var err error + for _, kv := range kvs { + require.NotPanics(t, func() { space.GetIfExists(ctx, NewKey("invalid"), kv.ptr) }) + require.Equal(t, kv.zero, reflect.ValueOf(kv.ptr).Elem().Interface()) + require.Panics(t, func() { space.Get(ctx, NewKey("invalid"), kv.ptr) }) + require.Equal(t, kv.zero, reflect.ValueOf(kv.ptr).Elem().Interface()) - // String - def0 := "default" - res, err = g.GetString(ctx, kvs[0].key) - assert.Nil(t, err) - assert.Equal(t, kvs[0].param, res) + require.NotPanics(t, func() { space.GetIfExists(ctx, kv.key, kv.ptr) }) + require.Equal(t, kv.param, reflect.ValueOf(kv.ptr).Elem().Interface()) + require.NotPanics(t, func() { space.Get(ctx, kv.key, kv.ptr) }) + require.Equal(t, kv.param, reflect.ValueOf(kv.ptr).Elem().Interface()) - _, err = g.GetString(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetStringWithDefault(ctx, kvs[0].key, def0) - assert.Equal(t, kvs[0].param, res) - - res = g.GetStringWithDefault(ctx, "invalid", def0) - assert.Equal(t, def0, res) - - // Bool - def1 := false - res, err = g.GetBool(ctx, kvs[1].key) - assert.Nil(t, err) - assert.Equal(t, kvs[1].param, res) - - _, err = g.GetBool(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetBoolWithDefault(ctx, kvs[1].key, def1) - assert.Equal(t, kvs[1].param, res) - - res = g.GetBoolWithDefault(ctx, "invalid", def1) - assert.Equal(t, def1, res) - - // Int16 - def2 := int16(0) - res, err = g.GetInt16(ctx, kvs[2].key) - assert.Nil(t, err) - assert.Equal(t, kvs[2].param, res) - - _, err = g.GetInt16(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetInt16WithDefault(ctx, kvs[2].key, def2) - assert.Equal(t, kvs[2].param, res) - - res = g.GetInt16WithDefault(ctx, "invalid", def2) - assert.Equal(t, def2, res) - - // Int32 - def3 := int32(0) - res, err = g.GetInt32(ctx, kvs[3].key) - assert.Nil(t, err) - assert.Equal(t, kvs[3].param, res) - - _, err = g.GetInt32(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetInt32WithDefault(ctx, kvs[3].key, def3) - assert.Equal(t, kvs[3].param, res) - - res = g.GetInt32WithDefault(ctx, "invalid", def3) - assert.Equal(t, def3, res) - - // Int64 - def4 := int64(0) - res, err = g.GetInt64(ctx, kvs[4].key) - assert.Nil(t, err) - assert.Equal(t, kvs[4].param, res) - - _, err = g.GetInt64(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetInt64WithDefault(ctx, kvs[4].key, def4) - assert.Equal(t, kvs[4].param, res) - - res = g.GetInt64WithDefault(ctx, "invalid", def4) - assert.Equal(t, def4, res) - - // Uint16 - def5 := uint16(0) - res, err = g.GetUint16(ctx, kvs[5].key) - assert.Nil(t, err) - assert.Equal(t, kvs[5].param, res) - - _, err = g.GetUint16(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetUint16WithDefault(ctx, kvs[5].key, def5) - assert.Equal(t, kvs[5].param, res) - - res = g.GetUint16WithDefault(ctx, "invalid", def5) - assert.Equal(t, def5, res) - - // Uint32 - def6 := uint32(0) - res, err = g.GetUint32(ctx, kvs[6].key) - assert.Nil(t, err) - assert.Equal(t, kvs[6].param, res) - - _, err = g.GetUint32(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetUint32WithDefault(ctx, kvs[6].key, def6) - assert.Equal(t, kvs[6].param, res) - - res = g.GetUint32WithDefault(ctx, "invalid", def6) - assert.Equal(t, def6, res) - - // Uint64 - def7 := uint64(0) - res, err = g.GetUint64(ctx, kvs[7].key) - assert.Nil(t, err) - assert.Equal(t, kvs[7].param, res) - - _, err = g.GetUint64(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetUint64WithDefault(ctx, kvs[7].key, def7) - assert.Equal(t, kvs[7].param, res) - - res = g.GetUint64WithDefault(ctx, "invalid", def7) - assert.Equal(t, def7, res) - - // Int - def8 := sdk.NewInt(0) - res, err = g.GetInt(ctx, kvs[8].key) - assert.Nil(t, err) - assert.Equal(t, kvs[8].param, res) - - _, err = g.GetInt(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetIntWithDefault(ctx, kvs[8].key, def8) - assert.Equal(t, kvs[8].param, res) - - res = g.GetIntWithDefault(ctx, "invalid", def8) - assert.Equal(t, def8, res) - - // Uint - def9 := sdk.NewUint(0) - res, err = g.GetUint(ctx, kvs[9].key) - assert.Nil(t, err) - assert.Equal(t, kvs[9].param, res) - - _, err = g.GetUint(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetUintWithDefault(ctx, kvs[9].key, def9) - assert.Equal(t, kvs[9].param, res) - - res = g.GetUintWithDefault(ctx, "invalid", def9) - assert.Equal(t, def9, res) - - // Rat - def10 := sdk.NewDec(0) - res, err = g.GetDec(ctx, kvs[10].key) - assert.Nil(t, err) - assert.Equal(t, kvs[10].param, res) - - _, err = g.GetDec(ctx, "invalid") - assert.NotNil(t, err) - - res = g.GetDecWithDefault(ctx, kvs[10].key, def10) - assert.Equal(t, kvs[10].param, res) - - res = g.GetDecWithDefault(ctx, "invalid", def10) - assert.Equal(t, def10, res) + require.Panics(t, func() { space.Get(ctx, NewKey("invalid"), kv.ptr) }) + require.Equal(t, kv.param, reflect.ValueOf(kv.ptr).Elem().Interface()) + require.Panics(t, func() { space.Get(ctx, kv.key, nil) }) + require.Panics(t, func() { space.Get(ctx, kv.key, new(s)) }) + } } diff --git a/x/params/msg_status.go b/x/params/msg_status.go deleted file mode 100644 index a8516a85b..000000000 --- a/x/params/msg_status.go +++ /dev/null @@ -1,37 +0,0 @@ -package params - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GenesisState defines initial activated msg types -type GenesisState struct { - ActivatedTypes []string `json:"activated-types"` -} - -// ActivatedParamKey - paramstore key for msg type activation -func ActivatedParamKey(ty string) string { - return "Activated/" + ty -} - -// InitGenesis stores activated type to param store -// nolint: errcheck -func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { - for _, ty := range data.ActivatedTypes { - k.set(ctx, ActivatedParamKey(ty), true) - } -} - -// NewAnteHandler returns an AnteHandler that checks -// whether msg type is activate or not -func NewAnteHandler(k Keeper) sdk.AnteHandler { - return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, sdk.Result, bool) { - for _, msg := range tx.GetMsgs() { - ok := k.Getter().GetBoolWithDefault(ctx, ActivatedParamKey(msg.Type()), false) - if !ok { - return ctx, sdk.ErrUnauthorized("deactivated msg type").Result(), true - } - } - return ctx, sdk.Result{}, false - } -} diff --git a/x/params/space.go b/x/params/space.go new file mode 100644 index 000000000..8d7f45085 --- /dev/null +++ b/x/params/space.go @@ -0,0 +1,22 @@ +package params + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/params/space" +) + +// nolint - reexport +type Space = space.Space +type ReadOnlySpace = space.ReadOnlySpace +type Key = space.Key +type KeyFieldPair = space.KeyFieldPair +type KeyFieldPairs = space.KeyFieldPairs +type ParamStruct = space.ParamStruct + +// nolint - reexport +func NewKey(keys ...string) Key { + return space.NewKey(keys...) +} +func UnmarshalParamsFromMap(m map[string][]byte, cdc *codec.Codec, ps space.ParamStruct) error { + return space.UnmarshalParamsFromMap(m, cdc, ps) +} diff --git a/x/params/space/key.go b/x/params/space/key.go new file mode 100644 index 000000000..cf96ae9b4 --- /dev/null +++ b/x/params/space/key.go @@ -0,0 +1,72 @@ +package space + +import ( + tmlibs "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// Wrapper for key string +type Key struct { + s string +} + +// Appending two keys with '/' as separator +// Checks alpanumericity +func (k Key) Append(keys ...string) (res Key) { + res = k + + for _, key := range keys { + if !tmlibs.IsASCIIText(key) { + panic("parameter key expressions can only contain alphanumeric characters") + } + res.s = res.s + "/" + key + } + return +} + +// NewKey constructs a key from a list of strings +func NewKey(keys ...string) (res Key) { + if len(keys) < 1 { + panic("length of parameter keys must not be zero") + } + res = Key{keys[0]} + + return res.Append(keys[1:]...) +} + +// KeyBytes make KVStore key bytes from Key +func (k Key) Bytes() []byte { + return []byte(k.s) +} + +// Human readable string +func (k Key) String() string { + return k.s +} + +// Used for associating paramstore key and field of param structs +type KeyFieldPair struct { + Key Key + Field interface{} +} + +// Slice of KeyFieldPair +type KeyFieldPairs []KeyFieldPair + +// Interface for structs containing parameters for a module +type ParamStruct interface { + KeyFieldPairs() KeyFieldPairs +} + +// Takes a map from key string to byte slice and +// unmarshalles it to ParamStruct +func UnmarshalParamsFromMap(m map[string][]byte, cdc *codec.Codec, ps ParamStruct) error { + for _, p := range ps.KeyFieldPairs() { + err := cdc.UnmarshalJSON(m[p.Key.String()], p.Field) + if err != nil { + return err + } + } + return nil +} diff --git a/x/params/space/space.go b/x/params/space/space.go new file mode 100644 index 000000000..e0e764c47 --- /dev/null +++ b/x/params/space/space.go @@ -0,0 +1,145 @@ +package space + +import ( + "fmt" + "reflect" + + tmlibs "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Individual parameter store for each keeper +type Space struct { + cdc *codec.Codec + key sdk.StoreKey + tkey sdk.StoreKey + + space []byte +} + +// NewSpace constructs a store with namespace +func NewSpace(cdc *codec.Codec, key sdk.StoreKey, tkey sdk.StoreKey, space string) Space { + if !tmlibs.IsASCIIText(space) { + panic("paramstore space expressions can only contain alphanumeric characters") + } + + return Space{ + cdc: cdc, + key: key, + tkey: tkey, + + space: []byte(space + "/"), + } +} + +// Get parameter from store +func (s Space) Get(ctx sdk.Context, key Key, ptr interface{}) { + store := ctx.KVStore(s.key).Prefix(s.space) + bz := store.Get(key.Bytes()) + err := s.cdc.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) + } +} + +// GetIfExists do not modify ptr if the stored parameter is nil +func (s Space) GetIfExists(ctx sdk.Context, key Key, ptr interface{}) { + store := ctx.KVStore(s.key).Prefix(s.space) + bz := store.Get(key.Bytes()) + if bz == nil { + return + } + err := s.cdc.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) + } +} + +// Get raw bytes of parameter from store +func (s Space) GetRaw(ctx sdk.Context, key Key) []byte { + store := ctx.KVStore(s.key).Prefix(s.space) + res := store.Get(key.Bytes()) + return res +} + +// Check if the parameter is set in the store +func (s Space) Has(ctx sdk.Context, key Key) bool { + store := ctx.KVStore(s.key).Prefix(s.space) + return store.Has(key.Bytes()) +} + +// Returns true if the parameter is set in the block +func (s Space) Modified(ctx sdk.Context, key Key) bool { + tstore := ctx.KVStore(s.tkey).Prefix(s.space) + return tstore.Has(key.Bytes()) +} + +// Set parameter, return error if stored parameter has different type from input +func (s Space) Set(ctx sdk.Context, key Key, param interface{}) error { + store := ctx.KVStore(s.key).Prefix(s.space) + keybz := key.Bytes() + + bz := store.Get(keybz) + if bz != nil { + ptrty := reflect.PtrTo(reflect.TypeOf(param)) + ptr := reflect.New(ptrty).Interface() + + if s.cdc.UnmarshalJSON(bz, ptr) != nil { + return fmt.Errorf("Type mismatch with stored param and provided param") + } + } + + bz, err := s.cdc.MarshalJSON(param) + if err != nil { + return err + } + store.Set(keybz, bz) + + tstore := ctx.KVStore(s.tkey).Prefix(s.space) + tstore.Set(keybz, []byte{}) + + return nil +} + +// Set raw bytes of parameter +func (s Space) SetRaw(ctx sdk.Context, key Key, param []byte) { + keybz := key.Bytes() + + store := ctx.KVStore(s.key).Prefix(s.space) + store.Set(keybz, param) + + tstore := ctx.KVStore(s.tkey).Prefix(s.space) + tstore.Set(keybz, []byte{}) +} + +// Returns a KVStore identical with the paramspace +func (s Space) KVStore(ctx sdk.Context) sdk.KVStore { + return ctx.KVStore(s.key).Prefix(s.space) +} + +// Wrapper of Space, provides immutable functions only +type ReadOnlySpace struct { + s Space +} + +// Exposes Get +func (ros ReadOnlySpace) Get(ctx sdk.Context, key Key, ptr interface{}) { + ros.s.Get(ctx, key, ptr) +} + +// Exposes GetRaw +func (ros ReadOnlySpace) GetRaw(ctx sdk.Context, key Key) []byte { + return ros.s.GetRaw(ctx, key) +} + +// Exposes Has +func (ros ReadOnlySpace) Has(ctx sdk.Context, key Key) bool { + return ros.s.Has(ctx, key) +} + +// Exposes Modified +func (ros ReadOnlySpace) Modified(ctx sdk.Context, key Key) bool { + return ros.s.Modified(ctx, key) +} diff --git a/x/params/space/test_common.go b/x/params/space/test_common.go new file mode 100644 index 000000000..c006e3f05 --- /dev/null +++ b/x/params/space/test_common.go @@ -0,0 +1,40 @@ +package space + +import ( + "os" + "testing" + + "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" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Keys for parameter access +const ( + TestParamSpace = "ParamsTest" +) + +// Returns components for testing +func DefaultTestComponents(t *testing.T) (sdk.Context, Space, func() sdk.CommitID) { + cdc := codec.New() + key := sdk.NewKVStoreKey("params") + tkey := sdk.NewTransientStoreKey("tparams") + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ms.WithTracer(os.Stdout) + ms.WithTracingContext(sdk.TraceContext{}) + ms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) + err := ms.LoadLatestVersion() + require.Nil(t, err) + ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout)) + store := NewSpace(cdc, key, tkey, TestParamSpace) + + return ctx, store, ms.Commit +} diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index a3370b1d8..14e936bdc 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -28,18 +28,20 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) { keyStake := sdk.NewKVStoreKey("stake") tkeyStake := sdk.NewTransientStoreKey("transient_stake") keySlashing := sdk.NewKVStoreKey("slashing") - keyParams := sdk.NewKVStoreKey("params") + + tkeyParams := sdk.NewTransientStoreKey("transient_params") bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper) paramsKeeper := params.NewKeeper(mapp.Cdc, keyParams) - stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, bankKeeper, mapp.RegisterCodespace(stake.DefaultCodespace)) + stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, bankKeeper, paramstore, mapp.RegisterCodespace(stake.DefaultCodespace)) + keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramstore, mapp.RegisterCodespace(DefaultCodespace)) - keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramsKeeper.Getter(), mapp.RegisterCodespace(DefaultCodespace)) mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper)) mapp.Router().AddRoute("slashing", NewHandler(keeper)) mapp.SetEndBlocker(getEndBlocker(stakeKeeper)) mapp.SetInitChainer(getInitChainer(mapp, stakeKeeper)) - require.NoError(t, mapp.CompleteSetup(keyStake, keySlashing, keyParams, tkeyStake)) + + require.NoError(t, mapp.CompleteSetup(keyStake, tkeyStake, keySlashing, keyParams, tkeyParams)) return mapp, stakeKeeper, keeper } diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 43ae6b0d0..44e61f8c7 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -5,10 +5,32 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake/types" ) -// InitGenesis initializes the keeper's address to pubkey map. -func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) { - for _, validator := range data.Validators { - keeper.addPubkey(ctx, validator.GetConsPubKey()) - } - return +// GenesisState - all slashing state that must be provided at genesis +type GenesisState struct { + Params Params +} + +// HubDefaultGenesisState - default GenesisState used by Cosmos Hub +func HubDefaultGenesisState() GenesisState { + return GenesisState{ + Params: HubDefaultParams(), + } +} + +// InitGenesis initialize default parameters +// and the keeper's address to pubkey map +func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState, sdata types.GenesisState) error { + for _, validator := range sdata.Validators { + keeper.addPubkey(ctx, validator.GetPubKey()) + } + + p := data.Params + keeper.paramstore.Set(ctx, maxEvidenceAgeKey, p.MaxEvidenceAge) + keeper.paramstore.Set(ctx, signedBlocksWindowKey, p.SignedBlocksWindow) + keeper.paramstore.Set(ctx, minSignedPerWindowKey, p.MinSignedPerWindow) + keeper.paramstore.Set(ctx, doubleSignUnbondDurationKey, p.DoubleSignUnbondDuration) + keeper.paramstore.Set(ctx, downtimeUnbondDurationKey, p.DowntimeUnbondDuration) + keeper.paramstore.Set(ctx, slashFractionDoubleSignKey, p.SlashFractionDoubleSign) + keeper.paramstore.Set(ctx, slashFractionDowntimeKey, p.SlashFractionDowntime) + return nil } diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 7aebf0d0b..90874d775 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -12,7 +12,7 @@ import ( func TestCannotUnjailUnlessJailed(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t, HubDefaultParams()) slh := NewHandler(keeper) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) @@ -30,7 +30,7 @@ func TestCannotUnjailUnlessJailed(t *testing.T) { } func TestJailedValidatorDelegations(t *testing.T) { - ctx, _, stakeKeeper, _, slashingKeeper := createTestInput(t) + ctx, _, stakeKeeper, _, slashingKeeper := createTestInput(t, HubDefaultParams()) stakeParams := stakeKeeper.GetParams(ctx) stakeParams.UnbondingTime = 0 diff --git a/x/slashing/hooks_test.go b/x/slashing/hooks_test.go index 0731fd8f2..c29718526 100644 --- a/x/slashing/hooks_test.go +++ b/x/slashing/hooks_test.go @@ -9,7 +9,7 @@ import ( ) func TestHookOnValidatorBonded(t *testing.T) { - ctx, _, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t, HubDefaultParams()) addr := sdk.ConsAddress(addrs[0]) keeper.onValidatorBonded(ctx, addr) period := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, ctx.BlockHeight()) @@ -17,7 +17,7 @@ func TestHookOnValidatorBonded(t *testing.T) { } func TestHookOnValidatorBeginUnbonding(t *testing.T) { - ctx, _, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t, HubDefaultParams()) addr := sdk.ConsAddress(addrs[0]) keeper.onValidatorBonded(ctx, addr) keeper.onValidatorBeginUnbonding(ctx, addr) diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 0c1c70b36..93bff70a2 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -18,18 +18,19 @@ type Keeper struct { storeKey sdk.StoreKey cdc *codec.Codec validatorSet sdk.ValidatorSet - params params.Getter + paramstore params.Space + // codespace codespace sdk.CodespaceType } // NewKeeper creates a slashing keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, params params.Getter, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, paramstore params.Space, codespace sdk.CodespaceType) Keeper { keeper := Keeper{ storeKey: key, cdc: cdc, validatorSet: vs, - params: params, + paramstore: paramstore, codespace: codespace, } return keeper diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index e32e7af5b..1d22eee64 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -12,10 +12,12 @@ import ( // Have to change these parameters for tests // lest the tests take forever -func init() { - defaultSignedBlocksWindow = 1000 - defaultDowntimeUnbondDuration = 60 * 60 - defaultDoubleSignUnbondDuration = 60 * 60 +func keeperTestParams() Params { + params := HubDefaultParams() + params.SignedBlocksWindow = 1000 + params.DowntimeUnbondDuration = 60 * 60 + params.DoubleSignUnbondDuration = 60 * 60 + return params } // ______________________________________________________________ @@ -26,7 +28,7 @@ func init() { func TestHandleDoubleSign(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) sk = sk.WithHooks(keeper.Hooks()) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) @@ -68,7 +70,7 @@ func TestHandleDoubleSign(t *testing.T) { func TestSlashingPeriodCap(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t, HubDefaultParams()) sk = sk.WithHooks(keeper.Hooks()) amtInt := int64(100) addr, amt := addrs[0], sdk.NewInt(amtInt) @@ -133,7 +135,7 @@ func TestSlashingPeriodCap(t *testing.T) { func TestHandleAbsentValidator(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) sk = sk.WithHooks(keeper.Hooks()) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) @@ -259,7 +261,7 @@ func TestHandleAbsentValidator(t *testing.T) { // and that they are not immediately jailed func TestHandleNewValidator(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) addr, val, amt := addrs[0], pks[0], int64(100) sh := stake.NewHandler(sk) got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) @@ -296,7 +298,7 @@ func TestHandleNewValidator(t *testing.T) { func TestHandleAlreadyJailed(t *testing.T) { // initial setup - ctx, _, sk, _, keeper := createTestInput(t) + ctx, _, sk, _, keeper := createTestInput(t, HubDefaultParams()) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) sh := stake.NewHandler(sk) diff --git a/x/slashing/params.go b/x/slashing/params.go index 6e18e5f48..94d2265ba 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -4,77 +4,111 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/params" ) -// nolint +// Default parameter namespace const ( - MaxEvidenceAgeKey = "slashing/MaxEvidenceAge" - SignedBlocksWindowKey = "slashing/SignedBlocksWindow" - MinSignedPerWindowKey = "slashing/MinSignedPerWindow" - DoubleSignUnbondDurationKey = "slashing/DoubleSignUnbondDuration" - DowntimeUnbondDurationKey = "slashing/DowntimeUnbondDuration" - SlashFractionDoubleSignKey = "slashing/SlashFractionDoubleSign" - SlashFractionDowntimeKey = "slashing/SlashFractionDowntime" + DefaultParamSpace = "slashing" ) +// nolint - Key generators for parameter access +func MaxEvidenceAgeKey() params.Key { return params.NewKey("MaxEvidenceAge") } +func SignedBlocksWindowKey() params.Key { return params.NewKey("SignedBlocksWindow") } +func MinSignedPerWindowKey() params.Key { return params.NewKey("MinSignedPerWindow") } +func DoubleSignUnbondDurationKey() params.Key { return params.NewKey("DoubleSignUnbondDuration") } +func DowntimeUnbondDurationKey() params.Key { return params.NewKey("DowntimeUnbondDuration") } +func SlashFractionDoubleSignKey() params.Key { return params.NewKey("SlashFractionDoubleSign") } +func SlashFractionDowntimeKey() params.Key { return params.NewKey("SlashFractionDowntime") } + +// Cached parameter keys +var ( + maxEvidenceAgeKey = MaxEvidenceAgeKey() + signedBlocksWindowKey = SignedBlocksWindowKey() + minSignedPerWindowKey = MinSignedPerWindowKey() + doubleSignUnbondDurationKey = DoubleSignUnbondDurationKey() + downtimeUnbondDurationKey = DowntimeUnbondDurationKey() + slashFractionDoubleSignKey = SlashFractionDoubleSignKey() + slashFractionDowntimeKey = SlashFractionDowntimeKey() +) + +// Params - used for initializing default parameter for slashing at genesis +type Params struct { + MaxEvidenceAge time.Duration `json:"max-evidence-age"` + SignedBlocksWindow int64 `json:"signed-blocks-window"` + MinSignedPerWindow sdk.Dec `json:"min-signed-per-window"` + DoubleSignUnbondDuration time.Duration `json:"doublesign-unbond-duration"` + DowntimeUnbondDuration time.Duration `json:"downtime-unbond-duration"` + SlashFractionDoubleSign sdk.Dec `json:"slash-fraction-doublesign"` + SlashFractionDowntime sdk.Dec `json:"slash-fraction-downtime"` +} + +// Default parameters used by Cosmos Hub +func HubDefaultParams() Params { + return Params{ + // defaultMaxEvidenceAge = 60 * 60 * 24 * 7 * 3 + // TODO Temporarily set to 2 minutes for testnets. + MaxEvidenceAge: 60 * 2 * time.Second, + + // TODO Temporarily set to five minutes for testnets + DoubleSignUnbondDuration: 60 * 5 * time.Second, + + // TODO Temporarily set to 100 blocks for testnets + SignedBlocksWindow: 100, + + // TODO Temporarily set to 10 minutes for testnets + DowntimeUnbondDuration: 60 * 10 * time.Second, + + MinSignedPerWindow: sdk.NewDecWithPrec(5, 1), + + SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(20)), + + SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(100)), + } +} + // MaxEvidenceAge - Max age for evidence - 21 days (3 weeks) // MaxEvidenceAge = 60 * 60 * 24 * 7 * 3 -func (k Keeper) MaxEvidenceAge(ctx sdk.Context) time.Duration { - return time.Duration(k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge)) * time.Second +func (k Keeper) MaxEvidenceAge(ctx sdk.Context) (res time.Duration) { + k.paramstore.Get(ctx, maxEvidenceAgeKey, &res) + return } // SignedBlocksWindow - sliding window for downtime slashing -func (k Keeper) SignedBlocksWindow(ctx sdk.Context) int64 { - return k.params.GetInt64WithDefault(ctx, SignedBlocksWindowKey, defaultSignedBlocksWindow) +func (k Keeper) SignedBlocksWindow(ctx sdk.Context) (res int64) { + k.paramstore.Get(ctx, signedBlocksWindowKey, &res) + return } -// Downtime slashing thershold - default 50% +// Downtime slashing thershold - default 50% of the SignedBlocksWindow func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { - minSignedPerWindow := k.params.GetDecWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow) + var minSignedPerWindow sdk.Dec + k.paramstore.Get(ctx, minSignedPerWindowKey, &minSignedPerWindow) signedBlocksWindow := k.SignedBlocksWindow(ctx) return sdk.NewDec(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() } // Double-sign unbond duration -func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) time.Duration { - return time.Duration(k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration)) * time.Second +func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) (res time.Duration) { + k.paramstore.Get(ctx, doubleSignUnbondDurationKey, &res) + return } // Downtime unbond duration -func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) time.Duration { - return time.Duration(k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration)) * time.Second +func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) (res time.Duration) { + k.paramstore.Get(ctx, downtimeUnbondDurationKey, &res) + return } // SlashFractionDoubleSign - currently default 5% -func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Dec { - return k.params.GetDecWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign) +func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) { + k.paramstore.Get(ctx, slashFractionDoubleSignKey, &res) + return } // SlashFractionDowntime - currently default 1% -func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Dec { - return k.params.GetDecWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime) +func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) { + k.paramstore.Get(ctx, slashFractionDowntimeKey, &res) + return } - -// declared as var because of keeper_test.go -// TODO: make it const or parameter of NewKeeper - -var ( - // defaultMaxEvidenceAge = 60 * 60 * 24 * 7 * 3 - // TODO Temporarily set to 2 minutes for testnets. - defaultMaxEvidenceAge int64 = 60 * 2 - - // TODO Temporarily set to five minutes for testnets - defaultDoubleSignUnbondDuration int64 = 60 * 5 - - // TODO Temporarily set to 10000 blocks for testnets - defaultSignedBlocksWindow int64 = 10000 - - // TODO Temporarily set to 10 minutes for testnets - defaultDowntimeUnbondDuration int64 = 60 * 10 - - defaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) - - defaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) - - defaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) -) diff --git a/x/slashing/signing_info_test.go b/x/slashing/signing_info_test.go index 7aff0da95..897902d4e 100644 --- a/x/slashing/signing_info_test.go +++ b/x/slashing/signing_info_test.go @@ -10,7 +10,7 @@ import ( ) func TestGetSetValidatorSigningInfo(t *testing.T) { - ctx, _, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t, HubDefaultParams()) info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0])) require.False(t, found) newInfo := ValidatorSigningInfo{ @@ -29,7 +29,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { } func TestGetSetValidatorSigningBitArray(t *testing.T) { - ctx, _, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t, HubDefaultParams()) signed := keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0) require.False(t, signed) // treat empty key as unsigned keeper.setValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true) diff --git a/x/slashing/slashing_period_test.go b/x/slashing/slashing_period_test.go index 54157bb9c..ac9c58313 100644 --- a/x/slashing/slashing_period_test.go +++ b/x/slashing/slashing_period_test.go @@ -9,7 +9,7 @@ import ( ) func TestGetSetValidatorSlashingPeriod(t *testing.T) { - ctx, _, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t, HubDefaultParams()) addr := sdk.ConsAddress(addrs[0]) height := int64(5) require.Panics(t, func() { keeper.getValidatorSlashingPeriodForHeight(ctx, addr, height) }) @@ -60,7 +60,7 @@ func TestGetSetValidatorSlashingPeriod(t *testing.T) { } func TestValidatorSlashingPeriodCap(t *testing.T) { - ctx, _, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t, HubDefaultParams()) addr := sdk.ConsAddress(addrs[0]) height := int64(5) newPeriod := ValidatorSlashingPeriod{ diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 7c97a8537..f429605cc 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -49,12 +49,13 @@ func createTestCodec() *codec.Codec { return cdc } -func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, params.Setter, Keeper) { +func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, stake.Keeper, params.Space, Keeper) { keyAcc := sdk.NewKVStoreKey("acc") keyStake := sdk.NewKVStoreKey("stake") tkeyStake := sdk.NewTransientStoreKey("transient_stake") keySlashing := sdk.NewKVStoreKey("slashing") keyParams := sdk.NewKVStoreKey("params") + tkeyParams := sdk.NewTransientStoreKey("transient_params") db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) @@ -62,14 +63,16 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keyParams, 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)) cdc := createTestCodec() accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount) + ck := bank.NewBaseKeeper(accountMapper) - params := params.NewKeeper(cdc, keyParams) - sk := stake.NewKeeper(cdc, keyStake, tkeyStake, ck, stake.DefaultCodespace) + paramstore := params.NewKeeper(cdc, keyParams, tkeyParams).Subspace(DefaultParamSpace) + sk := stake.NewKeeper(cdc, keyStake, tkeyStake, ck, paramstore, stake.DefaultCodespace) genesis := stake.DefaultGenesisState() genesis.Pool.LooseTokens = sdk.NewDec(initCoins.MulRaw(int64(len(addrs))).Int64()) @@ -83,8 +86,12 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para }) } require.Nil(t, err) - keeper := NewKeeper(cdc, keySlashing, sk, params.Getter(), DefaultCodespace) - return ctx, ck, sk, params.Setter(), keeper + keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace) + + err = InitGenesis(ctx, keeper, GenesisState{defaults}, genesis) + require.Nil(t, err) + + return ctx, ck, sk, paramstore, keeper } func newPubKey(pk string) (res crypto.PubKey) { diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index a98d97c54..7687d68cb 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -13,7 +13,7 @@ import ( ) func TestBeginBlocker(t *testing.T) { - ctx, ck, sk, _, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t, HubDefaultParams()) addr, pk, amt := addrs[2], pks[2], sdk.NewInt(100) // bond the validator diff --git a/x/stake/app_test.go b/x/stake/app_test.go index f96408c11..34c3ea91c 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" @@ -36,15 +37,20 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) { RegisterCodec(mApp.Cdc) keyStake := sdk.NewKVStoreKey("stake") + tkeyStake := sdk.NewTransientStoreKey("transient_stake") + keyParams := sdk.NewKVStoreKey("params") + tkeyParams := sdk.NewTransientStoreKey("transient_params") + bankKeeper := bank.NewBaseKeeper(mApp.AccountMapper) - keeper := NewKeeper(mApp.Cdc, keyStake, tkeyStake, bankKeeper, mApp.RegisterCodespace(DefaultCodespace)) + pk := params.NewKeeper(mApp.Cdc, keyParams, tkeyParams) + keeper := NewKeeper(mApp.Cdc, keyStake, tkeyStake, coinKeeper, pk.Subspace("stake"), mApp.RegisterCodespace(DefaultCodespace)) mApp.Router().AddRoute("stake", NewHandler(keeper)) mApp.SetEndBlocker(getEndBlocker(keeper)) mApp.SetInitChainer(getInitChainer(mApp, keeper)) - require.NoError(t, mApp.CompleteSetup(keyStake, tkeyStake)) + require.NoError(t, mApp.CompleteSetup(keyStake, tkeyStake, keyParams, tkeyParams)) return mApp, keeper } diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index 26b57c2a6..026847206 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + paramutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -464,16 +465,14 @@ func GetCmdQueryParams(storeName string, cdc *codec.Codec) *cobra.Command { Short: "Query the current staking parameters information", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - key := stake.ParamKey cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(key, storeName) + var params types.Params + err := paramutils.QueryParams(cliCtx, storeName, ¶ms) if err != nil { return err } - params := types.MustUnmarshalParams(cdc, res) - switch viper.Get(cli.OutputFlag) { case "text": human := params.HumanReadableString() diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index d7e9c5795..7667589b5 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -335,7 +335,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA //______________________________________________________________________________________________________ // get info for begin functions: MinTime and CreationHeight -func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sdk.ValAddress) ( +func (k Keeper) getBeginInfo(ctx sdk.Context, valSrcAddr sdk.ValAddress) ( minTime time.Time, height int64, completeNow bool) { validator, found := k.GetValidator(ctx, valSrcAddr) @@ -343,7 +343,7 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd case !found || validator.Status == sdk.Bonded: // the longest wait - just unbonding period from now - minTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) + minTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx)) height = ctx.BlockHeader().Height return minTime, height, false @@ -376,9 +376,8 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, } // create the unbonding delegation - params := k.GetParams(ctx) - minTime, height, completeNow := k.getBeginInfo(ctx, params, valAddr) - balance := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt()) + minTime, height, completeNow := k.getBeginInfo(ctx, valAddr) + balance := sdk.Coin{k.BondDenom(ctx), returnAmount.RoundInt()} // no need to create the ubd object just complete now if completeNow { @@ -437,8 +436,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, return err } - params := k.GetParams(ctx) - returnCoin := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt()) + returnCoin := sdk.Coin{k.BondDenom(ctx), returnAmount.RoundInt()} dstValidator, found := k.GetValidator(ctx, valDstAddr) if !found { return types.ErrBadRedelegationDst(k.Codespace()) @@ -449,7 +447,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, } // create the unbonding delegation - minTime, height, completeNow := k.getBeginInfo(ctx, params, valSrcAddr) + minTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr) if completeNow { // no need to create the redelegation object return nil diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 007c2a5de..e3f2ed9c0 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -15,17 +16,19 @@ type Keeper struct { cdc *codec.Codec bankKeeper bank.Keeper hooks sdk.StakingHooks + paramstore params.Space // codespace codespace sdk.CodespaceType } -func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramstore params.Space, codespace sdk.CodespaceType) Keeper { keeper := Keeper{ storeKey: key, storeTKey: tkey, cdc: cdc, bankKeeper: ck, + paramstore: paramstore, hooks: nil, codespace: codespace, } @@ -48,29 +51,6 @@ func (k Keeper) Codespace() sdk.CodespaceType { return k.codespace } -//_________________________________________________________________________ -// some generic reads/writes that don't need their own files - -// load/save the global staking params -func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { - store := ctx.KVStore(k.storeKey) - - b := store.Get(ParamKey) - if b == nil { - panic("Stored params should not have been nil") - } - - k.cdc.MustUnmarshalBinary(b, ¶ms) - return -} - -// set the params -func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { - store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(params) - store.Set(ParamKey, b) -} - //_______________________________________________________________________ // load/save the pool diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 6beb17937..9e0b7bc08 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -12,7 +12,8 @@ import ( //nolint var ( // Keys for store prefixes - ParamKey = []byte{0x00} // key for parameters relating to staking + // TODO DEPRECATED: delete in next release and reorder keys + // ParamKey = []byte{0x00} // key for parameters relating to staking PoolKey = []byte{0x01} // key for the staking pools ValidatorsKey = []byte{0x02} // prefix for each key to a validator ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey diff --git a/x/stake/keeper/params.go b/x/stake/keeper/params.go new file mode 100644 index 000000000..b44201140 --- /dev/null +++ b/x/stake/keeper/params.go @@ -0,0 +1,100 @@ +package keeper + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" +) + +// Default parameter namespace +const ( + DefaultParamSpace = "stake" +) + +// Cached parameter keys +var ( + keyInflationRateChange = types.KeyInflationRateChange() + keyInflationMax = types.KeyInflationMax() + keyInflationMin = types.KeyInflationMin() + keyGoalBonded = types.KeyGoalBonded() + keyUnbondingTime = types.KeyUnbondingTime() + keyMaxValidators = types.KeyMaxValidators() + keyBondDenom = types.KeyBondDenom() +) + +// InflationRateChange - Maximum annual change in inflation rate +func (k Keeper) InflationRateChange(ctx sdk.Context) (res sdk.Dec) { + k.paramstore.Get(ctx, keyInflationRateChange, &res) + return +} + +// InflationMax - Maximum inflation rate +func (k Keeper) InflationMax(ctx sdk.Context) (res sdk.Dec) { + k.paramstore.Get(ctx, keyInflationMax, &res) + return +} + +// InflationMin - Minimum inflation rate +func (k Keeper) InflationMin(ctx sdk.Context) (res sdk.Dec) { + k.paramstore.Get(ctx, keyInflationMin, &res) + return +} + +// GoalBonded - Goal of percent bonded atoms +func (k Keeper) GoalBonded(ctx sdk.Context) (res sdk.Dec) { + k.paramstore.Get(ctx, keyGoalBonded, &res) + return +} + +// UnbondingTime +func (k Keeper) UnbondingTime(ctx sdk.Context) (res time.Duration) { + k.paramstore.Get(ctx, keyUnbondingTime, &res) + return +} + +// MaxValidators - Maximum number of validators +func (k Keeper) MaxValidators(ctx sdk.Context) (res uint16) { + k.paramstore.Get(ctx, keyMaxValidators, &res) + return +} + +// BondDenom - Bondable coin denomination +func (k Keeper) BondDenom(ctx sdk.Context) (res string) { + k.paramstore.Get(ctx, keyBondDenom, &res) + return +} + +// Get all parameteras as types.Params +func (k Keeper) GetParams(ctx sdk.Context) (res types.Params) { + res.InflationRateChange = k.InflationRateChange(ctx) + res.InflationMax = k.InflationMax(ctx) + res.InflationMin = k.InflationMin(ctx) + res.GoalBonded = k.GoalBonded(ctx) + res.UnbondingTime = k.UnbondingTime(ctx) + res.MaxValidators = k.MaxValidators(ctx) + res.BondDenom = k.BondDenom(ctx) + return +} + +// Need a distinct function because setParams depends on an existing previous +// record of params to exist (to check if maxValidators has changed) - and we +// panic on retrieval if it doesn't exist - hence if we use setParams for the very +// first params set it will panic. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + if k.MaxValidators(ctx) != params.MaxValidators { + k.UpdateBondedValidatorsFull(ctx) + } + k.SetNewParams(ctx, params) +} + +// set the params without updating validator set +func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) { + k.paramstore.Set(ctx, keyInflationRateChange, params.InflationRateChange) + k.paramstore.Set(ctx, keyInflationMax, params.InflationMax) + k.paramstore.Set(ctx, keyInflationMin, params.InflationMin) + k.paramstore.Set(ctx, keyGoalBonded, params.GoalBonded) + k.paramstore.Set(ctx, keyUnbondingTime, params.UnbondingTime) + k.paramstore.Set(ctx, keyMaxValidators, params.MaxValidators) + k.paramstore.Set(ctx, keyBondDenom, params.BondDenom) +} diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index 8f5b2fa39..58977cd3c 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -19,6 +19,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -92,12 +93,16 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context keyStake := sdk.NewKVStoreKey("stake") tkeyStake := sdk.NewTransientStoreKey("transient_stake") keyAcc := sdk.NewKVStoreKey("acc") + keyParams := sdk.NewKVStoreKey("params") + tkeyParams := sdk.NewTransientStoreKey("transient_params") db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(tkeyStake, sdk.StoreTypeTransient, nil) ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) err := ms.LoadLatestVersion() require.Nil(t, err) @@ -108,8 +113,11 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context keyAcc, // target store auth.ProtoBaseAccount, // prototype ) + ck := bank.NewBaseKeeper(accountMapper) - keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace) + + pk := params.NewKeeper(cdc, keyParams, tkeyParams) + keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, pk.Subspace("stake"), types.DefaultCodespace) keeper.SetPool(ctx, types.InitialPool()) keeper.SetParams(ctx, types.DefaultParams()) keeper.InitIntraTxCounter(ctx) @@ -118,7 +126,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context for _, addr := range Addrs { pool := keeper.GetPool(ctx) _, _, err := ck.AddCoins(ctx, addr, sdk.Coins{ - {keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)}, + {keeper.BondDenom(ctx), sdk.NewInt(initCoins)}, }) require.Nil(t, err) pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(initCoins)) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index b405312a1..741b787d0 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -238,7 +238,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat store := ctx.KVStore(k.storeKey) // add the actual validator power sorted store - maxValidators := k.GetParams(ctx).MaxValidators + maxValidators := k.MaxValidators(ctx) validators = make([]types.Validator, maxValidators) iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) @@ -263,7 +263,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat // get the group of bonded validators sorted by power-rank func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) - maxValidators := k.GetParams(ctx).MaxValidators + maxValidators := k.MaxValidators(ctx) validators := make([]types.Validator, maxValidators) iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index b81555f03..aba0c9eb7 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -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/mock/simulation" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/stake" ) @@ -23,7 +24,11 @@ func TestStakeWithRandomMessages(t *testing.T) { bankKeeper := bank.NewBaseKeeper(mapper) stakeKey := sdk.NewKVStoreKey("stake") stakeTKey := sdk.NewTransientStoreKey("transient_stake") - stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, stake.DefaultCodespace) + paramsKey := sdk.NewKVStoreKey("params") + paramsTKey := sdk.NewTransientStoreKey("transient_params") + + paramstore := params.NewKeeper(mapp.Cdc, paramsKey, paramsTKey).Subspace(stake.DefaultParamSpace) + stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, paramstore, stake.DefaultCodespace) mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper)) mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { validatorUpdates := stake.EndBlocker(ctx, stakeKeeper) @@ -32,7 +37,7 @@ func TestStakeWithRandomMessages(t *testing.T) { } }) - err := mapp.CompleteSetup(stakeKey, stakeTKey) + err := mapp.CompleteSetup(stakeKey, stakeTKey, paramsKey, paramsTKey) if err != nil { panic(err) } diff --git a/x/stake/stake.go b/x/stake/stake.go index a9a3ca3cd..ffe7a4590 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -40,7 +40,6 @@ var ( GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey GetDelegationKey = keeper.GetDelegationKey GetDelegationsKey = keeper.GetDelegationsKey - ParamKey = keeper.ParamKey PoolKey = keeper.PoolKey ValidatorsKey = keeper.ValidatorsKey ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey @@ -60,6 +59,14 @@ var ( GetREDsToValDstIndexKey = keeper.GetREDsToValDstIndexKey GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey + DefaultParamSpace = keeper.DefaultParamSpace + KeyInflationRateChange = types.KeyInflationRateChange + KeyInflationMax = types.KeyInflationMax + KeyGoalBonded = types.KeyGoalBonded + KeyUnbondingTime = types.KeyUnbondingTime + KeyMaxValidators = types.KeyMaxValidators + KeyBondDenom = types.KeyBondDenom + DefaultParams = types.DefaultParams InitialPool = types.InitialPool NewValidator = types.NewValidator diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 4dcc3782a..de963a56b 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -7,12 +7,22 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" ) // defaultUnbondingTime reflects three weeks in seconds as the default // unbonding time. const defaultUnbondingTime time.Duration = 60 * 60 * 24 * 3 * time.Second +// nolint - Key generators for parameter access +func KeyInflationRateChange() params.Key { return params.NewKey("InflationRateChange") } +func KeyInflationMax() params.Key { return params.NewKey("InflationMax") } +func KeyInflationMin() params.Key { return params.NewKey("InflationMin") } +func KeyGoalBonded() params.Key { return params.NewKey("GoalBonded") } +func KeyUnbondingTime() params.Key { return params.NewKey("UnbondingTime") } +func KeyMaxValidators() params.Key { return params.NewKey("MaxValidators") } +func KeyBondDenom() params.Key { return params.NewKey("BondDenom") } + // Params defines the high level settings for staking type Params struct { InflationRateChange sdk.Dec `json:"inflation_rate_change"` // maximum annual change in inflation rate @@ -26,6 +36,19 @@ type Params struct { BondDenom string `json:"bond_denom"` // bondable coin denomination } +// Implements params.ParamStruct +func (p *Params) KeyFieldPairs() params.KeyFieldPairs { + return params.KeyFieldPairs{ + {KeyInflationRateChange(), &p.InflationRateChange}, + {KeyInflationMax(), &p.InflationMax}, + {KeyInflationMin(), &p.InflationMin}, + {KeyGoalBonded(), &p.GoalBonded}, + {KeyUnbondingTime(), &p.UnbondingTime}, + {KeyMaxValidators(), &p.MaxValidators}, + {KeyBondDenom(), &p.BondDenom}, + } +} + // Equal returns a boolean determining if two Param types are identical. func (p Params) Equal(p2 Params) bool { bz1 := MsgCdc.MustMarshalBinary(&p)