From dbf9dede2a8935098cddae7db111b86659f92a4a Mon Sep 17 00:00:00 2001 From: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Mon, 22 May 2023 16:10:05 -0700 Subject: [PATCH] provider migration --- x/ccv/consumer/keeper/migration.go | 6 +- x/ccv/provider/keeper/keeper.go | 4 + x/ccv/provider/keeper/migration.go | 75 ++++++++++++++ x/ccv/provider/keeper/migration_test.go | 130 ++++++++++++++++++++++++ x/ccv/provider/types/params.go | 12 +-- 5 files changed, 218 insertions(+), 9 deletions(-) create mode 100644 x/ccv/provider/keeper/migration.go create mode 100644 x/ccv/provider/keeper/migration_test.go diff --git a/x/ccv/consumer/keeper/migration.go b/x/ccv/consumer/keeper/migration.go index 60c1f9dcfb..8e7ad1e8d5 100644 --- a/x/ccv/consumer/keeper/migration.go +++ b/x/ccv/consumer/keeper/migration.go @@ -11,13 +11,13 @@ import ( // Migrator is a struct for handling in-place store migrations. type Migrator struct { - ccvConsumerParamSpace paramtypes.Subspace ccvConsumerKeeper Keeper + ccvConsumerParamSpace paramtypes.Subspace } // NewMigrator returns a new Migrator. -func NewMigrator(ccvConsumerKeeper Keeper) Migrator { - return Migrator{ccvConsumerKeeper: ccvConsumerKeeper} +func NewMigrator(ccvConsumerKeeper Keeper, ccvConsumerParamSpace paramtypes.Subspace) Migrator { + return Migrator{ccvConsumerKeeper: ccvConsumerKeeper, ccvConsumerParamSpace: ccvConsumerParamSpace} } func (m Migrator) Migratev1p0To1p3(ctx sdk.Context) error { diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 2da6a17359..71d1fe022c 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -82,6 +82,10 @@ func NewKeeper( return k } +func (k *Keeper) SetParamSpace(ctx sdk.Context, ps paramtypes.Subspace) { + k.paramSpace = ps +} + // Validates that the provider keeper is initialized with non-zero and // non-nil values for all its fields. Otherwise this method will panic. func (k Keeper) mustValidateFields() { diff --git a/x/ccv/provider/keeper/migration.go b/x/ccv/provider/keeper/migration.go new file mode 100644 index 0000000000..3cd45600b9 --- /dev/null +++ b/x/ccv/provider/keeper/migration.go @@ -0,0 +1,75 @@ +package keeper + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + providertypes "github.com/cosmos/interchain-security/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/x/ccv/types" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + ccvProviderKeeper Keeper + stakingKeeper ccvtypes.StakingKeeper + ccvProviderParamSpace paramtypes.Subspace +} + +// NewMigrator returns a new Migrator. +func NewMigrator(ccvProviderKeeper Keeper, stakingKeeper ccvtypes.StakingKeeper, + ccvProviderParamSpace paramtypes.Subspace) Migrator { + return Migrator{ccvProviderKeeper: ccvProviderKeeper, ccvProviderParamSpace: ccvProviderParamSpace} +} + +func (m Migrator) Migratev1p0To1p3(ctx sdk.Context) error { + // Migrate params + MigrateParamsv1p0To1p3(ctx, + m.ccvProviderParamSpace, + // See https://github.com/cosmos/interchain-security/blob/7861804cb311507ec6aebebbfad60ea42eb8ed4b/x/ccv/provider/keeper/params.go#L84 + // The v1.1.0-multiden version of ICS hardcodes this param as 10 of bond type: k.stakingKeeper.BondDenom(ctx). + // Here we use the same starting value, but the param can now be changed through governance. + sdk.NewCoin(m.stakingKeeper.BondDenom(ctx), sdk.NewInt(10000000)), + ) + + return nil +} + +// MigrateParamsv1p0To1p3 migrates the provider CCV module params from v1.0.0 to v1.3.0, +// setting default values for new params. +func MigrateParamsv1p0To1p3(ctx sdk.Context, paramsSubspace paramtypes.Subspace, consumerRewardDenomRegistrationFee sdk.Coin) { + // Get old params + var templateClient ibctmtypes.ClientState + paramsSubspace.Get(ctx, providertypes.KeyTemplateClient, &templateClient) + var trustingPeriodFraction string + paramsSubspace.Get(ctx, providertypes.KeyTrustingPeriodFraction, &trustingPeriodFraction) + var ccvTimeoutPeriod time.Duration + paramsSubspace.Get(ctx, ccvtypes.KeyCCVTimeoutPeriod, &ccvTimeoutPeriod) + var initTimeoutPeriod time.Duration + paramsSubspace.Get(ctx, providertypes.KeyInitTimeoutPeriod, &initTimeoutPeriod) + var vscTimeoutPeriod time.Duration + paramsSubspace.Get(ctx, providertypes.KeyVscTimeoutPeriod, &vscTimeoutPeriod) + var slashMeterReplenishPeriod time.Duration + paramsSubspace.Get(ctx, providertypes.KeySlashMeterReplenishPeriod, &slashMeterReplenishPeriod) + var slashMeterReplenishFraction string + paramsSubspace.Get(ctx, providertypes.KeySlashMeterReplenishFraction, &slashMeterReplenishFraction) + var maxThrottledPackets int64 + paramsSubspace.Get(ctx, providertypes.KeyMaxThrottledPackets, &maxThrottledPackets) + + // Recycle old params, set new param to input value + newParams := providertypes.NewParams( + &templateClient, + trustingPeriodFraction, + ccvTimeoutPeriod, + initTimeoutPeriod, + vscTimeoutPeriod, + slashMeterReplenishPeriod, + slashMeterReplenishFraction, + maxThrottledPackets, + consumerRewardDenomRegistrationFee, + ) + + // Persist new params + paramsSubspace.SetParamSet(ctx, &newParams) +} diff --git a/x/ccv/provider/keeper/migration_test.go b/x/ccv/provider/keeper/migration_test.go new file mode 100644 index 0000000000..1180e36052 --- /dev/null +++ b/x/ccv/provider/keeper/migration_test.go @@ -0,0 +1,130 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + types2 "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types" + providerkeeper "github.com/cosmos/interchain-security/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/x/ccv/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmdb "github.com/tendermint/tm-db" +) + +func TestMigrateParamsv1p0To1p3(t *testing.T) { + // Setup raw store + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + storeKey := sdk.NewKVStoreKey(paramtypes.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey("mem_key") + stateStore.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(memStoreKey, sdk.StoreTypeMemory, nil) + require.NoError(t, stateStore.LoadLatestVersion()) + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + require.NoError(t, stateStore.LoadLatestVersion()) + + // Create new empty subspace + subspace := paramtypes.NewSubspace(cdc, + codec.NewLegacyAmino(), + storeKey, + memStoreKey, + paramtypes.ModuleName, + ) + + // Note that new param key table is set in keeper constructor + subspace = subspace.WithKeyTable(v1p0p0KeyTable()) + + // Set 8 params from v1.0.0 + subspace.Set(ctx, providertypes.KeyTemplateClient, providertypes.DefaultParams().TemplateClient) + subspace.Set(ctx, providertypes.KeyTrustingPeriodFraction, "0.75") + subspace.Set(ctx, ccvtypes.KeyCCVTimeoutPeriod, time.Hour) + subspace.Set(ctx, providertypes.KeyInitTimeoutPeriod, time.Hour) + subspace.Set(ctx, providertypes.KeyVscTimeoutPeriod, time.Hour) + subspace.Set(ctx, providertypes.KeySlashMeterReplenishPeriod, time.Hour) + subspace.Set(ctx, providertypes.KeySlashMeterReplenishFraction, "0.5") + subspace.Set(ctx, providertypes.KeyMaxThrottledPackets, int64(10)) + + // Confirm new param cannot be set with old key table + require.Panics(t, func() { + subspace.Set(ctx, providertypes.KeyConsumerRewardDenomRegistrationFee, sdk.NewInt64Coin("uatom", 100)) + }) + + // Now create new subspace, mocking an upgrade where app initialization happens again + subspace = paramtypes.NewSubspace(cdc, + codec.NewLegacyAmino(), + storeKey, + memStoreKey, + paramtypes.ModuleName, + ).WithKeyTable(providertypes.ParamKeyTable()) // Use new key table, this would be set in keeper constructor + + // Run migration + providerkeeper.MigrateParamsv1p0To1p3(ctx, subspace, sdk.NewCoin("uatom", sdk.NewInt(100000))) + + // Use keeper to confirm params are set correctly + keeper := providerkeeper.Keeper{} + keeper.SetParamSpace(ctx, subspace) + + params := keeper.GetParams(ctx) + require.Equal(t, providertypes.DefaultParams().TemplateClient, params.TemplateClient) + require.Equal(t, "0.75", params.TrustingPeriodFraction) + require.Equal(t, time.Hour, params.CcvTimeoutPeriod) + require.Equal(t, time.Hour, params.InitTimeoutPeriod) + require.Equal(t, time.Hour, params.VscTimeoutPeriod) + require.Equal(t, time.Hour, params.SlashMeterReplenishPeriod) + require.Equal(t, "0.5", params.SlashMeterReplenishFraction) + require.Equal(t, int64(10), params.MaxThrottledPackets) + // New param should be set + require.Equal(t, sdk.NewCoin("uatom", sdk.NewInt(100000)), params.ConsumerRewardDenomRegistrationFee) + + // Set new params to other values + params.ConsumerRewardDenomRegistrationFee = sdk.NewCoin("uatom", sdk.NewInt(1000000000)) + keeper.SetParams(ctx, params) + require.Equal(t, sdk.NewCoin("uatom", sdk.NewInt(1000000000)), keeper.GetParams(ctx).ConsumerRewardDenomRegistrationFee) +} + +// +// Note: the following methods and struct could be removed if v1.3.0 is actually defined as v2.0.0 +// and we bump the go.mod package name accordingly +// + +// v1p0p0Params is a copy of the ParamKeyTable method from v1.0.0 +func v1p0p0KeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&v1p0p0Params{}) +} + +// ParamSetPairs implements params.ParamSet for v1p0p0Params +func (p *v1p0p0Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(providertypes.KeyTemplateClient, p.TemplateClient, providertypes.ValidateTemplateClient), + paramtypes.NewParamSetPair(providertypes.KeyTrustingPeriodFraction, p.TrustingPeriodFraction, ccvtypes.ValidateStringFraction), + paramtypes.NewParamSetPair(ccvtypes.KeyCCVTimeoutPeriod, p.CcvTimeoutPeriod, ccvtypes.ValidateDuration), + paramtypes.NewParamSetPair(providertypes.KeyInitTimeoutPeriod, p.InitTimeoutPeriod, ccvtypes.ValidateDuration), + paramtypes.NewParamSetPair(providertypes.KeyVscTimeoutPeriod, p.VscTimeoutPeriod, ccvtypes.ValidateDuration), + paramtypes.NewParamSetPair(providertypes.KeySlashMeterReplenishPeriod, p.SlashMeterReplenishPeriod, ccvtypes.ValidateDuration), + paramtypes.NewParamSetPair(providertypes.KeySlashMeterReplenishFraction, p.SlashMeterReplenishFraction, ccvtypes.ValidateStringFraction), + paramtypes.NewParamSetPair(providertypes.KeyMaxThrottledPackets, p.MaxThrottledPackets, ccvtypes.ValidatePositiveInt64), + } +} + +// v1p0p0Params is a copy of the Params struct from v1.0.0 +type v1p0p0Params struct { + TemplateClient *types2.ClientState `protobuf:"bytes,1,opt,name=template_client,json=templateClient,proto3" json:"template_client,omitempty"` + TrustingPeriodFraction string `protobuf:"bytes,2,opt,name=trusting_period_fraction,json=trustingPeriodFraction,proto3" json:"trusting_period_fraction,omitempty"` + CcvTimeoutPeriod time.Duration `protobuf:"bytes,3,opt,name=ccv_timeout_period,json=ccvTimeoutPeriod,proto3,stdduration" json:"ccv_timeout_period"` + InitTimeoutPeriod time.Duration `protobuf:"bytes,4,opt,name=init_timeout_period,json=initTimeoutPeriod,proto3,stdduration" json:"init_timeout_period"` + VscTimeoutPeriod time.Duration `protobuf:"bytes,5,opt,name=vsc_timeout_period,json=vscTimeoutPeriod,proto3,stdduration" json:"vsc_timeout_period"` + SlashMeterReplenishPeriod time.Duration `protobuf:"bytes,6,opt,name=slash_meter_replenish_period,json=slashMeterReplenishPeriod,proto3,stdduration" json:"slash_meter_replenish_period"` + SlashMeterReplenishFraction string `protobuf:"bytes,7,opt,name=slash_meter_replenish_fraction,json=slashMeterReplenishFraction,proto3" json:"slash_meter_replenish_fraction,omitempty"` + MaxThrottledPackets int64 `protobuf:"varint,8,opt,name=max_throttled_packets,json=maxThrottledPackets,proto3" json:"max_throttled_packets,omitempty"` +} diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index 4bfb906f4b..7e420826d7 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -121,7 +121,7 @@ func (p Params) Validate() error { if p.TemplateClient == nil { return fmt.Errorf("template client is nil") } - if err := validateTemplateClient(*p.TemplateClient); err != nil { + if err := ValidateTemplateClient(*p.TemplateClient); err != nil { return err } if err := ccvtypes.ValidateStringFraction(p.TrustingPeriodFraction); err != nil { @@ -145,7 +145,7 @@ func (p Params) Validate() error { if err := ccvtypes.ValidatePositiveInt64(p.MaxThrottledPackets); err != nil { return fmt.Errorf("max throttled packets is invalid: %s", err) } - if err := validateCoin(p.ConsumerRewardDenomRegistrationFee); err != nil { + if err := ValidateCoin(p.ConsumerRewardDenomRegistrationFee); err != nil { return fmt.Errorf("consumer reward denom registration fee is invalid: %s", err) } return nil @@ -154,7 +154,7 @@ func (p Params) Validate() error { // ParamSetPairs implements params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ - paramtypes.NewParamSetPair(KeyTemplateClient, p.TemplateClient, validateTemplateClient), + paramtypes.NewParamSetPair(KeyTemplateClient, p.TemplateClient, ValidateTemplateClient), paramtypes.NewParamSetPair(KeyTrustingPeriodFraction, p.TrustingPeriodFraction, ccvtypes.ValidateStringFraction), paramtypes.NewParamSetPair(ccvtypes.KeyCCVTimeoutPeriod, p.CcvTimeoutPeriod, ccvtypes.ValidateDuration), paramtypes.NewParamSetPair(KeyInitTimeoutPeriod, p.InitTimeoutPeriod, ccvtypes.ValidateDuration), @@ -162,11 +162,11 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeySlashMeterReplenishPeriod, p.SlashMeterReplenishPeriod, ccvtypes.ValidateDuration), paramtypes.NewParamSetPair(KeySlashMeterReplenishFraction, p.SlashMeterReplenishFraction, ccvtypes.ValidateStringFraction), paramtypes.NewParamSetPair(KeyMaxThrottledPackets, p.MaxThrottledPackets, ccvtypes.ValidatePositiveInt64), - paramtypes.NewParamSetPair(KeyConsumerRewardDenomRegistrationFee, p.ConsumerRewardDenomRegistrationFee, validateCoin), + paramtypes.NewParamSetPair(KeyConsumerRewardDenomRegistrationFee, p.ConsumerRewardDenomRegistrationFee, ValidateCoin), } } -func validateTemplateClient(i interface{}) error { +func ValidateTemplateClient(i interface{}) error { cs, ok := i.(ibctmtypes.ClientState) if !ok { return fmt.Errorf("invalid parameter type: %T, expected: %T", i, ibctmtypes.ClientState{}) @@ -193,7 +193,7 @@ func validateTemplateClient(i interface{}) error { return nil } -func validateCoin(i interface{}) error { +func ValidateCoin(i interface{}) error { v, ok := i.(sdk.Coin) if !ok { return fmt.Errorf("invalid parameter type: %T", i)