diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 40e868669404..629b15cfe695 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -17,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/ibc" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" ) @@ -44,6 +45,7 @@ type GaiaApp struct { keySlashing *sdk.KVStoreKey keyGov *sdk.KVStoreKey keyFeeCollection *sdk.KVStoreKey + keyParams *sdk.KVStoreKey // Manage getting and setting accounts accountMapper auth.AccountMapper @@ -53,6 +55,7 @@ type GaiaApp struct { stakeKeeper stake.Keeper slashingKeeper slashing.Keeper govKeeper gov.Keeper + paramsKeeper params.Keeper } func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { @@ -69,6 +72,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { keySlashing: sdk.NewKVStoreKey("slashing"), keyGov: sdk.NewKVStoreKey("gov"), keyFeeCollection: sdk.NewKVStoreKey("fee"), + keyParams: sdk.NewKVStoreKey("params"), } // define the accountMapper @@ -81,10 +85,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // add handlers app.coinKeeper = bank.NewKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) + app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace)) app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) + app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) // register message routes app.Router(). @@ -99,7 +104,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection) + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index 121f437a3c63..a40dc0681cff 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -21,6 +21,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/ibc" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" @@ -130,6 +131,7 @@ type GaiaApp struct { keyIBC *sdk.KVStoreKey keyStake *sdk.KVStoreKey keySlashing *sdk.KVStoreKey + keyParams *sdk.KVStoreKey // Manage getting and setting accounts accountMapper auth.AccountMapper @@ -138,6 +140,7 @@ type GaiaApp struct { ibcMapper ibc.Mapper stakeKeeper stake.Keeper slashingKeeper slashing.Keeper + paramsKeeper params.Keeper } func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { @@ -152,6 +155,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { keyIBC: sdk.NewKVStoreKey("ibc"), keyStake: sdk.NewKVStoreKey("stake"), keySlashing: sdk.NewKVStoreKey("slashing"), + keyParams: sdk.NewKVStoreKey("params"), } // define the accountMapper @@ -164,8 +168,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // add handlers app.coinKeeper = bank.NewKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) + app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace)) + app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) // register message routes app.Router(). diff --git a/x/params/keeper.go b/x/params/keeper.go new file mode 100644 index 000000000000..8817b7c6c8de --- /dev/null +++ b/x/params/keeper.go @@ -0,0 +1,405 @@ +package params + +import ( + "fmt" + "reflect" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" +) + +// Keeper manages global parameter store +type Keeper struct { + cdc *wire.Codec + key sdk.StoreKey +} + +// NewKeeper constructs a new Keeper +func NewKeeper(cdc *wire.Codec, key sdk.StoreKey) Keeper { + return Keeper{ + cdc: cdc, + key: key, + } +} + +// InitKeeper constructs a new Keeper with initial parameters +func InitKeeper(ctx sdk.Context, cdc *wire.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]) + } + + 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") + } + } + + bz, err := k.cdc.MarshalBinary(param) + if err != nil { + return err + } + store.Set([]byte(key), bz) + + return nil +} + +// 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) +} + +// 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 +} + +// GetRat is helper function for rat params +func (k Getter) GetRat(ctx sdk.Context, key string) (res sdk.Rat, 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 +} + +// GetRatWithDefault is helper function for sdk.Rat params with default value +func (k Getter) GetRatWithDefault(ctx sdk.Context, key string, def sdk.Rat) (res sdk.Rat) { + 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) + } +} + +// SetRat is helper function for rat params +func (k Setter) SetRat(ctx sdk.Context, key string, param sdk.Rat) { + if err := k.k.set(ctx, key, param); err != nil { + panic(err) + } +} diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go new file mode 100644 index 000000000000..4bb5744ea458 --- /dev/null +++ b/x/params/keeper_test.go @@ -0,0 +1,280 @@ +package params + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + 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/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" +) + +func defaultContext(key sdk.StoreKey) sdk.Context { + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + cms.LoadLatestVersion() + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + return ctx +} + +func TestKeeper(t *testing.T) { + kvs := []struct { + key string + param int64 + }{ + {"key1", 10}, + {"key2", 55}, + {"key3", 182}, + {"key4", 17582}, + {"key5", 2768554}, + } + + skey := sdk.NewKVStoreKey("test") + ctx := defaultContext(skey) + setter := NewKeeper(wire.NewCodec(), skey).Setter() + + for _, kv := range kvs { + err := setter.Set(ctx, kv.key, kv.param) + assert.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) + } + + cdc := wire.NewCodec() + 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) + } + + for _, kv := range kvs { + var param bool + err := setter.Get(ctx, kv.key, ¶m) + assert.NotNil(t, err) + } + + for _, kv := range kvs { + err := setter.Set(ctx, kv.key, true) + assert.NotNil(t, err) + } +} + +func TestGetter(t *testing.T) { + key := sdk.NewKVStoreKey("test") + ctx := defaultContext(key) + keeper := NewKeeper(wire.NewCodec(), key) + + g := keeper.Getter() + s := keeper.Setter() + + kvs := []struct { + key string + param 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.NewRat(1)}, + } + + 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.SetRat(ctx, kvs[10].key, sdk.NewRat(1)) }) + + var res interface{} + var err error + + // String + def0 := "default" + res, err = g.GetString(ctx, kvs[0].key) + assert.Nil(t, err) + assert.Equal(t, kvs[0].param, res) + + _, 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.NewRat(0) + res, err = g.GetRat(ctx, kvs[10].key) + assert.Nil(t, err) + assert.Equal(t, kvs[10].param, res) + + _, err = g.GetRat(ctx, "invalid") + assert.NotNil(t, err) + + res = g.GetRatWithDefault(ctx, kvs[10].key, def10) + assert.Equal(t, kvs[10].param, res) + + res = g.GetRatWithDefault(ctx, "invalid", def10) + assert.Equal(t, def10, res) + +} diff --git a/x/params/msg_status.go b/x/params/msg_status.go new file mode 100644 index 000000000000..72704e4dc7aa --- /dev/null +++ b/x/params/msg_status.go @@ -0,0 +1,36 @@ +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 +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) (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/slashing/app_test.go b/x/slashing/app_test.go index 333b4dc836ee..a7e689419911 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/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/cosmos/cosmos-sdk/x/stake" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -26,15 +27,18 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) { RegisterWire(mapp.Cdc) keyStake := sdk.NewKVStoreKey("stake") keySlashing := sdk.NewKVStoreKey("slashing") + keyParams := sdk.NewKVStoreKey("params") coinKeeper := bank.NewKeeper(mapp.AccountMapper) + paramsKeeper := params.NewKeeper(mapp.Cdc, keyParams) stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, coinKeeper, mapp.RegisterCodespace(stake.DefaultCodespace)) - keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, 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([]*sdk.KVStoreKey{keyStake, keySlashing})) + require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keySlashing, keyParams})) return mapp, stakeKeeper, keeper } diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index d5a6b15dbb77..89c1e11a604b 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -11,7 +11,7 @@ import ( func TestCannotUnrevokeUnlessRevoked(t *testing.T) { // initial setup - ctx, ck, sk, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t) slh := NewHandler(keeper) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 9f1ff205b5a1..3d721f4a45fd 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/tendermint/tendermint/crypto" ) @@ -13,17 +14,19 @@ type Keeper struct { storeKey sdk.StoreKey cdc *wire.Codec validatorSet sdk.ValidatorSet + params params.Getter // codespace codespace sdk.CodespaceType } // NewKeeper creates a slashing keeper -func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, params params.Getter, codespace sdk.CodespaceType) Keeper { keeper := Keeper{ storeKey: key, cdc: cdc, validatorSet: vs, + params: params, codespace: codespace, } return keeper @@ -37,16 +40,17 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infracti address := sdk.ValAddress(pubkey.Address()) // Double sign too old - if age > MaxEvidenceAge { - logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), infractionHeight, age, MaxEvidenceAge)) + maxEvidenceAge := k.MaxEvidenceAge(ctx) + if age > maxEvidenceAge { + logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), infractionHeight, age, maxEvidenceAge)) return } // Double sign confirmed - logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), infractionHeight, age, MaxEvidenceAge)) + logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), infractionHeight, age, maxEvidenceAge)) // Slash validator - k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, SlashFractionDoubleSign) + k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, k.SlashFractionDoubleSign(ctx)) // Revoke validator k.validatorSet.Revoke(ctx, pubkey) @@ -56,7 +60,7 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infracti if !found { panic(fmt.Sprintf("Expected signing info for validator %s but not found", address)) } - signInfo.JailedUntil = time + DoubleSignUnbondDuration + signInfo.JailedUntil = time + k.DoubleSignUnbondDuration(ctx) k.setValidatorSigningInfo(ctx, address, signInfo) } @@ -73,7 +77,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, // If this validator has never been seen before, construct a new SigningInfo with the correct start height signInfo = NewValidatorSigningInfo(height, 0, 0, 0) } - index := signInfo.IndexOffset % SignedBlocksWindow + index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx) signInfo.IndexOffset++ // Update signed block bit array & counter @@ -93,15 +97,15 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, } if !signed { - logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", pubkey.Address(), height, signInfo.SignedBlocksCounter, MinSignedPerWindow)) + logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", pubkey.Address(), height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx))) } - minHeight := signInfo.StartHeight + SignedBlocksWindow - if height > minHeight && signInfo.SignedBlocksCounter < MinSignedPerWindow { + minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx) + if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) { // Downtime confirmed, slash, revoke, and jail the validator - logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, MinSignedPerWindow)) - k.validatorSet.Slash(ctx, pubkey, height, power, SlashFractionDowntime) + logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx))) + k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx)) k.validatorSet.Revoke(ctx, pubkey) - signInfo.JailedUntil = ctx.BlockHeader().Time + DowntimeUnbondDuration + signInfo.JailedUntil = ctx.BlockHeader().Time + k.DowntimeUnbondDuration(ctx) } // Set the updated signing info diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 6d0bf868c7c5..6a965b4e5c5b 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -14,10 +14,9 @@ import ( // Have to change these parameters for tests // lest the tests take forever func init() { - SignedBlocksWindow = 1000 - MinSignedPerWindow = SignedBlocksWindow / 2 - DowntimeUnbondDuration = 60 * 60 - DoubleSignUnbondDuration = 60 * 60 + defaultSignedBlocksWindow = 1000 + defaultDowntimeUnbondDuration = 60 * 60 + defaultDoubleSignUnbondDuration = 60 * 60 } // Test that a validator is slashed correctly @@ -25,7 +24,7 @@ func init() { func TestHandleDoubleSign(t *testing.T) { // initial setup - ctx, ck, sk, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt)) @@ -46,7 +45,7 @@ func TestHandleDoubleSign(t *testing.T) { sk.Unrevoke(ctx, val) // power should be reduced require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower()) - ctx = ctx.WithBlockHeader(abci.Header{Time: 1 + MaxEvidenceAge}) + ctx = ctx.WithBlockHeader(abci.Header{Time: 1 + keeper.MaxEvidenceAge(ctx)}) // double sign past max age keeper.handleDoubleSign(ctx, val, 0, 0, amtInt) @@ -58,7 +57,7 @@ func TestHandleDoubleSign(t *testing.T) { func TestHandleAbsentValidator(t *testing.T) { // initial setup - ctx, ck, sk, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) sh := stake.NewHandler(sk) @@ -77,24 +76,24 @@ func TestHandleAbsentValidator(t *testing.T) { height := int64(0) // 1000 first blocks OK - for ; height < SignedBlocksWindow; height++ { + for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val, amtInt, true) } info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, SignedBlocksWindow, info.SignedBlocksCounter) + require.Equal(t, keeper.SignedBlocksWindow(ctx), info.SignedBlocksCounter) // 500 blocks missed - for ; height < SignedBlocksWindow+(SignedBlocksWindow-MinSignedPerWindow); height++ { + for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val, amtInt, false) } info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, SignedBlocksWindow-MinSignedPerWindow, info.SignedBlocksCounter) + require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.SignedBlocksCounter) // validator should be bonded still validator, _ := sk.GetValidatorByPubKey(ctx, val) @@ -108,7 +107,7 @@ func TestHandleAbsentValidator(t *testing.T) { info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, SignedBlocksWindow-MinSignedPerWindow-1, info.SignedBlocksCounter) + require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) // validator should have been revoked validator, _ = sk.GetValidatorByPubKey(ctx, val) @@ -119,7 +118,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.False(t, got.IsOK()) // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(abci.Header{Time: DowntimeUnbondDuration + 1}) + ctx = ctx.WithBlockHeader(abci.Header{Time: keeper.DowntimeUnbondDuration(ctx) + 1}) got = slh(ctx, NewMsgUnrevoke(addr)) require.True(t, got.IsOK()) @@ -135,7 +134,7 @@ func TestHandleAbsentValidator(t *testing.T) { info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) require.Equal(t, height, info.StartHeight) - require.Equal(t, SignedBlocksWindow-MinSignedPerWindow-1, info.SignedBlocksCounter) + require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) // validator should not be immediately revoked again height++ @@ -145,14 +144,14 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, sdk.Bonded, validator.GetStatus()) // 500 signed blocks - nextHeight := height + MinSignedPerWindow + 1 + nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 for ; height < nextHeight; height++ { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val, amtInt, false) } // validator should be revoked again after 500 unsigned blocks - nextHeight = height + MinSignedPerWindow + 1 + nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val, amtInt, false) @@ -166,7 +165,7 @@ func TestHandleAbsentValidator(t *testing.T) { // and that they are not immediately revoked func TestHandleNewValidator(t *testing.T) { // initial setup - ctx, ck, sk, keeper := createTestInput(t) + ctx, ck, sk, _, keeper := createTestInput(t) addr, val, amt := addrs[0], pks[0], int64(100) sh := stake.NewHandler(sk) got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) @@ -176,16 +175,16 @@ func TestHandleNewValidator(t *testing.T) { require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower()) // 1000 first blocks not a validator - ctx = ctx.WithBlockHeight(SignedBlocksWindow + 1) + ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) // Now a validator, for two blocks keeper.handleValidatorSignature(ctx, val, 100, true) - ctx = ctx.WithBlockHeight(SignedBlocksWindow + 2) + ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) keeper.handleValidatorSignature(ctx, val, 100, false) info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) - require.Equal(t, int64(SignedBlocksWindow+1), info.StartHeight) + require.Equal(t, int64(keeper.SignedBlocksWindow(ctx)+1), info.StartHeight) require.Equal(t, int64(2), info.IndexOffset) require.Equal(t, int64(1), info.SignedBlocksCounter) require.Equal(t, int64(0), info.JailedUntil) diff --git a/x/slashing/params.go b/x/slashing/params.go index ebf14f283d4f..b0c85698ca92 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -4,39 +4,75 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// nolint +const ( + MaxEvidenceAgeKey = "slashing/MaxEvidenceAge" + SignedBlocksWindowKey = "slashing/SignedBlocksWindow" + MinSignedPerWindowKey = "slashing/MinSignedPerWindow" + DoubleSignUnbondDurationKey = "slashing/DoubleSignUnbondDuration" + DowntimeUnbondDurationKey = "slashing/DowntimeUnbondDuration" + SlashFractionDoubleSignKey = "slashing/SlashFractionDoubleSign" + SlashFractionDowntimeKey = "slashing/SlashFractionDowntime" +) + +// MaxEvidenceAge - Max age for evidence - 21 days (3 weeks) +// MaxEvidenceAge = 60 * 60 * 24 * 7 * 3 +func (k Keeper) MaxEvidenceAge(ctx sdk.Context) int64 { + return k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge) +} + +// SignedBlocksWindow - sliding window for downtime slashing +func (k Keeper) SignedBlocksWindow(ctx sdk.Context) int64 { + return k.params.GetInt64WithDefault(ctx, SignedBlocksWindowKey, defaultSignedBlocksWindow) +} + +// Downtime slashing thershold - default 50% +func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { + minSignedPerWindow := k.params.GetRatWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow) + signedBlocksWindow := k.SignedBlocksWindow(ctx) + return sdk.NewRat(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() +} + +// Double-sign unbond duration +func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) int64 { + return k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration) +} + +// Downtime unbond duration +func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) int64 { + return k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration) +} + +// SlashFractionDoubleSign - currently default 5% +func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Rat { + return k.params.GetRatWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign) +} + +// SlashFractionDowntime - currently default 1% +func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Rat { + return k.params.GetRatWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime) +} + +// declared as var because of keeper_test.go +// TODO: make it const or parameter of NewKeeper + var ( - // MaxEvidenceAge - Max age for evidence - 21 days (3 weeks) - // TODO Should this be a governance parameter or just modifiable with SoftwareUpgradeProposals? - // MaxEvidenceAge = 60 * 60 * 24 * 7 * 3 + // defaultMaxEvidenceAge = 60 * 60 * 24 * 7 * 3 // TODO Temporarily set to 2 minutes for testnets. - MaxEvidenceAge int64 = 60 * 2 + defaultMaxEvidenceAge int64 = 60 * 2 - // SignedBlocksWindow - sliding window for downtime slashing - // TODO Governance parameter? - // TODO Temporarily set to 40000 blocks for testnets - SignedBlocksWindow int64 = 40000 + // TODO Temporarily set to five minutes for testnets + defaultDoubleSignUnbondDuration int64 = 60 * 5 - // Downtime slashing threshold - 50% - // TODO Governance parameter? - MinSignedPerWindow = SignedBlocksWindow / 2 + // TODO Temporarily set to 100 blocks for testnets + defaultSignedBlocksWindow int64 = 100 - // Downtime unbond duration - // TODO Governance parameter? - // TODO Temporarily set to five minutes for testnets - DowntimeUnbondDuration int64 = 60 * 5 + // TODO Temporarily set to 10 minutes for testnets + defaultDowntimeUnbondDuration int64 = 60 * 10 - // Double-sign unbond duration - // TODO Governance parameter? - // TODO Temporarily set to five minutes for testnets - DoubleSignUnbondDuration int64 = 60 * 5 -) + defaultMinSignedPerWindow sdk.Rat = sdk.NewRat(1, 2) -var ( - // SlashFractionDoubleSign - currently 5% - // TODO Governance parameter? - SlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20)) + defaultSlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20)) - // SlashFractionDowntime - currently 1% - // TODO Governance parameter? - SlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100)) + defaultSlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100)) ) diff --git a/x/slashing/signing_info_test.go b/x/slashing/signing_info_test.go index 742769013a20..b2da974e79a0 100644 --- a/x/slashing/signing_info_test.go +++ b/x/slashing/signing_info_test.go @@ -9,7 +9,7 @@ import ( ) func TestGetSetValidatorSigningInfo(t *testing.T) { - ctx, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t) info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0])) require.False(t, found) newInfo := ValidatorSigningInfo{ @@ -28,7 +28,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { } func TestGetSetValidatorSigningBitArray(t *testing.T) { - ctx, _, _, keeper := createTestInput(t) + ctx, _, _, _, keeper := createTestInput(t) signed := keeper.getValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0) require.False(t, signed) // treat empty key as unsigned keeper.setValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0, true) diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 823a6b96b6b2..c630fec6b86c 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -17,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/wire" "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" ) @@ -46,21 +47,24 @@ func createTestCodec() *wire.Codec { return cdc } -func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keeper) { +func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, params.Setter, Keeper) { keyAcc := sdk.NewKVStoreKey("acc") keyStake := sdk.NewKVStoreKey("stake") keySlashing := sdk.NewKVStoreKey("slashing") + keyParams := sdk.NewKVStoreKey("params") db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) err := ms.LoadLatestVersion() require.Nil(t, err) ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout)) cdc := createTestCodec() accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount) ck := bank.NewKeeper(accountMapper) + params := params.NewKeeper(cdc, keyParams) sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace) genesis := stake.DefaultGenesisState() genesis.Pool.LooseTokens = initCoins.MulRaw(int64(len(addrs))).Int64() @@ -73,8 +77,8 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keep }) } require.Nil(t, err) - keeper := NewKeeper(cdc, keySlashing, sk, DefaultCodespace) - return ctx, ck, sk, keeper + keeper := NewKeeper(cdc, keySlashing, sk, params.Getter(), DefaultCodespace) + return ctx, ck, sk, params.Setter(), keeper } func newPubKey(pk string) (res crypto.PubKey) { diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 247fe0972a3c..38e339e590b1 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) addr, pk, amt := addrs[2], pks[2], sdk.NewInt(100) // bond the validator @@ -47,7 +47,7 @@ func TestBeginBlocker(t *testing.T) { height := int64(0) // for 1000 blocks, mark the validator as having signed - for ; height < SignedBlocksWindow; height++ { + for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ Validators: []abci.SigningValidator{{ @@ -59,7 +59,7 @@ func TestBeginBlocker(t *testing.T) { } // for 500 blocks, mark the validator as having not signed - for ; height < ((SignedBlocksWindow * 2) - MinSignedPerWindow + 1); height++ { + for ; height < ((keeper.SignedBlocksWindow(ctx) * 2) - keeper.MinSignedPerWindow(ctx) + 1); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ Validators: []abci.SigningValidator{{