diff --git a/CHANGELOG.md b/CHANGELOG.md index d34e76d6320e..5ebdf1237341 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (x/params) [#12724](https://github.com/cosmos/cosmos-sdk/pull/12724) Add `GetParamSetIfExists` function to params `Subspace` to prevent panics on breaking changes. * [#12668](https://github.com/cosmos/cosmos-sdk/pull/12668) Add `authz_msg_index` event attribute to message events emitted when executing via `MsgExec` through `x/authz`. * [#12697](https://github.com/cosmos/cosmos-sdk/pull/12697) Upgrade IAVL to v0.19.0 with fast index and error propagation. NOTE: first start will take a while to propagate into new model. diff --git a/x/params/types/common_test.go b/x/params/types/common_test.go index c7cb067c62c4..1228aeb8c015 100644 --- a/x/params/types/common_test.go +++ b/x/params/types/common_test.go @@ -10,9 +10,10 @@ import ( ) var ( - keyUnbondingTime = []byte("UnbondingTime") - keyMaxValidators = []byte("MaxValidators") - keyBondDenom = []byte("BondDenom") + keyUnbondingTime = []byte("UnbondingTime") + keyMaxValidators = []byte("MaxValidators") + keyBondDenom = []byte("BondDenom") + keyMaxRedelegationEntries = []byte("MaxRedelegationEntries") key = sdk.NewKVStoreKey("storekey") tkey = sdk.NewTransientStoreKey("transientstorekey") @@ -24,6 +25,13 @@ type params struct { BondDenom string `json:"bond_denom" yaml:"bond_denom"` } +type paramsV2 struct { + UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` + MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` + BondDenom string `json:"bond_denom" yaml:"bond_denom"` + MaxRedelegationEntries uint32 `json:"max_redelegation_entries" yaml:"max_redelegation_entries"` +} + func validateUnbondingTime(i interface{}) error { v, ok := i.(time.Duration) if !ok { @@ -59,6 +67,15 @@ func validateBondDenom(i interface{}) error { return nil } +func validateMaxRedelegationEntries(i interface{}) error { + _, ok := i.(uint32) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} + func (p *params) ParamSetPairs() types.ParamSetPairs { return types.ParamSetPairs{ {keyUnbondingTime, &p.UnbondingTime, validateUnbondingTime}, @@ -67,6 +84,15 @@ func (p *params) ParamSetPairs() types.ParamSetPairs { } } +func (p *paramsV2) ParamSetPairs() types.ParamSetPairs { + return types.ParamSetPairs{ + {keyUnbondingTime, &p.UnbondingTime, validateUnbondingTime}, + {keyMaxValidators, &p.MaxValidators, validateMaxValidators}, + {keyBondDenom, &p.BondDenom, validateBondDenom}, + {keyMaxRedelegationEntries, &p.MaxRedelegationEntries, validateMaxRedelegationEntries}, + } +} + func paramKeyTable() types.KeyTable { return types.NewKeyTable().RegisterParamSet(¶ms{}) } diff --git a/x/params/types/subspace.go b/x/params/types/subspace.go index 42afe6cc9b21..948d5f1c3227 100644 --- a/x/params/types/subspace.go +++ b/x/params/types/subspace.go @@ -223,6 +223,15 @@ func (s Subspace) GetParamSet(ctx sdk.Context, ps ParamSet) { } } +// GetParamSetIfExists iterates through each ParamSetPair where for each pair, it will +// retrieve the value and set it to the corresponding value pointer provided +// in the ParamSetPair by calling Subspace#GetIfExists. +func (s Subspace) GetParamSetIfExists(ctx sdk.Context, ps ParamSet) { + for _, pair := range ps.ParamSetPairs() { + s.GetIfExists(ctx, pair.Key, pair.Value) + } +} + // SetParamSet iterates through each ParamSetPair and sets the value with the // corresponding parameter key in the Subspace's KVStore. func (s Subspace) SetParamSet(ctx sdk.Context, ps ParamSet) { diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index a347a5f5427e..ddee81bc5bee 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -162,6 +162,29 @@ func (suite *SubspaceTestSuite) TestGetParamSet() { suite.Require().Equal(a.BondDenom, b.BondDenom) } +func (suite *SubspaceTestSuite) TestGetParamSetIfExists() { + a := params{ + UnbondingTime: time.Hour * 48, + MaxValidators: 100, + BondDenom: "stake", + } + suite.Require().NotPanics(func() { + suite.ss.Set(suite.ctx, keyUnbondingTime, a.UnbondingTime) + suite.ss.Set(suite.ctx, keyMaxValidators, a.MaxValidators) + suite.ss.Set(suite.ctx, keyBondDenom, a.BondDenom) + }) + + b := paramsV2{} + suite.Require().NotPanics(func() { + suite.ss.GetParamSetIfExists(suite.ctx, &b) + }) + suite.Require().Equal(a.UnbondingTime, b.UnbondingTime) + suite.Require().Equal(a.MaxValidators, b.MaxValidators) + suite.Require().Equal(a.BondDenom, b.BondDenom) + suite.Require().Zero(b.MaxRedelegationEntries) + suite.Require().False(suite.ss.Has(suite.ctx, keyMaxRedelegationEntries), "key from the new param version should not yet exist") +} + func (suite *SubspaceTestSuite) TestSetParamSet() { testCases := []struct { name string