From 1890b8bd12463b3b81a235ddeef895cb2bafc10a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:26:49 +0200 Subject: [PATCH] feat: Add GetParamSetIfExists to prevent panic on breaking param changes (backport #12615) (#12792) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add GetParamSetIfExists to prevent panic on breaking param changes (#12615) * imp(params): Add GetParamSetIfExists to prevent panic on breaking param changes * changelog * test Co-authored-by: Marko (cherry picked from commit 2932e11c1cd68dd9ab7e46daac98ba5762408076) # Conflicts: # CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Marko --- CHANGELOG.md | 1 + x/params/types/common_test.go | 32 +++++++++++++++++++++++++++++--- x/params/types/subspace.go | 9 +++++++++ x/params/types/subspace_test.go | 23 +++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d96c47901a..5b00aa55157c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (x/params) [#12615](https://github.com/cosmos/cosmos-sdk/pull/12615) Add `GetParamSetIfExists` function to params `Subspace` to prevent panics on breaking changes. * (x/bank) [#12674](https://github.com/cosmos/cosmos-sdk/pull/12674) Add convenience function `CreatePrefixedAccountStoreKey()` to construct key to access account's balance for a given denom. ## [v0.46.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.0) - 2022-07-26 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 e528a185eb9e..f90914f3a62a 100644 --- a/x/params/types/subspace.go +++ b/x/params/types/subspace.go @@ -240,6 +240,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 fa1d1a36ecb4..2f57119d01f3 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -199,6 +199,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