From 7df6eb063393a8b9e6ed3ce070efe5997138cbba Mon Sep 17 00:00:00 2001 From: Bernd Mueller Date: Fri, 18 Oct 2024 09:35:03 +0200 Subject: [PATCH 1/2] add genesis transform v5 --- .../2373-consumer-backward-compatibility.md | 2 + app/consumer/genesis.go | 81 +- app/consumer/genesis_test.go | 744 +++++------------- 3 files changed, 238 insertions(+), 589 deletions(-) create mode 100644 .changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md diff --git a/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md new file mode 100644 index 0000000000..553b0593aa --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md @@ -0,0 +1,2 @@ +- `[x/consumer]` Add consumer genesis transformation for v5.x consumers. + ([\#2373](https://github.com/cosmos/interchain-security/pull/2373)) \ No newline at end of file diff --git a/app/consumer/genesis.go b/app/consumer/genesis.go index be56824856..82c9333c80 100644 --- a/app/consumer/genesis.go +++ b/app/consumer/genesis.go @@ -33,24 +33,16 @@ type GenesisState map[string]json.RawMessage type IcsVersion string const ( - v2_x IcsVersion = "v2.x" - v3_0_x IcsVersion = "v3.0.x" - v3_1_x IcsVersion = "v3.1.x" - v3_2_x IcsVersion = "v3.2.x" - v3_3_x IcsVersion = "v3.3.x" v4_x_x IcsVersion = "v4.x" + v5_x_x IcsVersion = "v5.x" ) var TransformationVersions map[string]IcsVersion = map[string]IcsVersion{ - "v2.x": v2_x, - "v3.0.x": v3_0_x, - "v3.1.x": v3_1_x, - "v3.2.x": v3_2_x, - "v3.3.x": v3_3_x, - "v4.x": v4_x_x, + "v4.x": v4_x_x, + "v5.x": v5_x_x, } -// Transformation of consumer genesis content as it is exported from a provider version v1,2,3 +// Transformation of consumer genesis content as it is exported from a provider version v4 // to a format readable by current consumer implementation. func transformToNew(jsonRaw []byte, ctx client.Context) (json.RawMessage, error) { // v1,2,3 uses deprecated fields of GenesisState type @@ -103,6 +95,57 @@ func transformToNew(jsonRaw []byte, ctx client.Context) (json.RawMessage, error) return newJson, nil } +// Remove a parameter from a JSON object +func removeParameterFromParams(params json.RawMessage, param string) (json.RawMessage, error) { + paramsMap := map[string]json.RawMessage{} + if err := json.Unmarshal(params, ¶msMap); err != nil { + return nil, fmt.Errorf("unmarshalling 'params' failed: %v", err) + } + _, exists := paramsMap[param] + if exists { + delete(paramsMap, param) + } + return json.Marshal(paramsMap) +} + +// Transformation of consumer genesis content as it is exported by current provider version +// to a format supported by consumer version v5.x +func transformToV5(jsonRaw []byte, ctx client.Context) (json.RawMessage, error) { + srcConGen := types.ConsumerGenesisState{} + err := ctx.Codec.UnmarshalJSON(jsonRaw, &srcConGen) + if err != nil { + return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) + } + + // Remove 'consumer_id' from 'params' + params, err := ctx.Codec.MarshalJSON(&srcConGen.Params) + if err != nil { + return nil, err + } + + params, err = removeParameterFromParams(params, "consumer_id") + if err != nil { + return nil, err + } + + // Marshal GenesisState and patch 'params' value + result, err := ctx.Codec.MarshalJSON(&srcConGen) + if err != nil { + return nil, err + } + genState := map[string]json.RawMessage{} + if err := json.Unmarshal(result, &genState); err != nil { + return nil, fmt.Errorf("unmarshalling 'GenesisState' failed: %v", err) + } + genState["params"] = params + + result, err = json.Marshal(genState) + if err != nil { + return nil, fmt.Errorf("marshalling transformation result failed: %v", err) + } + return result, nil +} + // Transformation of consumer genesis content as it is exported by current provider version // to a format supported by consumer version v3.3.x func transformToV33(jsonRaw []byte, ctx client.Context) ([]byte, error) { @@ -258,16 +301,10 @@ func transformGenesis(ctx client.Context, targetVersion IcsVersion, jsonRaw []by var err error switch targetVersion { - // v2.x, v3.0-v3.2 share same consumer genesis type - case v2_x: - newConsumerGenesis, err = transformToV2(jsonRaw, ctx, true) - case v3_0_x, v3_1_x, v3_2_x: - // same as v2 replacement without need of `prehash_key_before_comparison` removal - newConsumerGenesis, err = transformToV2(jsonRaw, ctx, false) - case v3_3_x: - newConsumerGenesis, err = transformToV33(jsonRaw, ctx) case v4_x_x: newConsumerGenesis, err = transformToNew(jsonRaw, ctx) + case v5_x_x: + newConsumerGenesis, err = transformToV5(jsonRaw, ctx) default: err = fmt.Errorf("unsupported target version '%s'. Run %s --help", targetVersion, version.AppName) @@ -336,7 +373,7 @@ func GetConsumerGenesisTransformCmd() *cobra.Command { Short: "Transform CCV consumer genesis data exported to a specific target format", Long: strings.TrimSpace( fmt.Sprintf(` -Transform the consumer genesis data exported from a provider version v1,v2, v3, v4 to a specified consumer target version. +Transform the consumer genesis data exported from a provider version v5.x v6.x to a specified consumer target version. The result is printed to STDOUT. Note: Content to be transformed is not the consumer genesis file itself but the exported content from provider chain which is used to patch the consumer genesis file! @@ -349,7 +386,7 @@ $ %s --to v2.x transform /path/to/ccv_consumer_genesis.json Args: cobra.RangeArgs(1, 2), RunE: TransformConsumerGenesis, } - cmd.Flags().String("to", string(v4_x_x), + cmd.Flags().String("to", string(v5_x_x), fmt.Sprintf("target version for consumer genesis. Supported versions %s", maps.Keys(TransformationVersions))) return cmd diff --git a/app/consumer/genesis_test.go b/app/consumer/genesis_test.go index 144dbd74c9..c92d2c6725 100644 --- a/app/consumer/genesis_test.go +++ b/app/consumer/genesis_test.go @@ -3,12 +3,13 @@ package app_test import ( "bytes" "context" + "encoding/json" "fmt" "io/fs" "os" "path/filepath" + "reflect" "testing" - "time" "github.com/spf13/cobra" "github.com/stretchr/testify/require" @@ -17,382 +18,139 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" app "github.com/cosmos/interchain-security/v6/app/consumer" - consumerTypes "github.com/cosmos/interchain-security/v6/x/ccv/consumer/types" ccvtypes "github.com/cosmos/interchain-security/v6/x/ccv/types" ) const ( - V4x = "v4.x" - V33x = "v3.3.x" - V2x = "v2.x" + V6x = "v6.x" + V5x = "v5.x" + V4x = "v4.x" ) // Testdata mapping consumer genesis exports to a provider module version as // used by transformation function for consumer genesis content. var consumerGenesisStates map[string]string = map[string]string{ - "v2.x": ` + "v6.x": ` { - "params": { - "enabled": true, - "blocks_per_distribution_transmission": "1500", - "distribution_transmission_channel": "", - "provider_fee_pool_addr_str": "", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", - "consumer_redistribution_fraction": "0.75", - "historical_entries": "10000", - "unbonding_period": "1728000s", - "soft_opt_out_threshold": "", - "reward_denoms": [], - "provider_reward_denoms": [] - }, - "provider_client_id": "", - "provider_channel_id": "", - "new_chain": true, - "provider_client_state": { - "chain_id": "cosmoshub-4", - "trust_level": { - "numerator": "1", - "denominator": "3" - }, - "trusting_period": "1197504s", - "unbonding_period": "1814400s", - "max_clock_drift": "10s", - "frozen_height": { - "revision_number": "0", - "revision_height": "0" - }, - "latest_height": { - "revision_number": "4", - "revision_height": "15211521" - }, - "proof_specs": [ - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 33, - "min_prefix_length": 4, - "max_prefix_length": 12, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0 - }, - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 32, - "min_prefix_length": 1, - "max_prefix_length": 1, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0 - } - ], - "upgrade_path": [ - "upgrade", - "upgradedIBCState" - ], - "allow_update_after_expiry": true, - "allow_update_after_misbehaviour": true - }, - "provider_consensus_state": { - "timestamp": "2023-05-08T11:00:01.563901871Z", - "root": { - "hash": "qKVnVSXlsjDHC8ekKcy/0zSjzr3YekCurld9R4W07EI=" - }, - "next_validators_hash": "E08978F493101A3C5D459FB3087B8CFBA9E82D7A1FE1441E7D77E11AC0586BAC" - }, - "maturing_packets": [], - "initial_val_set": [ - { - "pub_key": { - "ed25519": "cOQZvh/h9ZioSeUMZB/1Vy1Xo5x2sjrVjlE/qHnYifM=" - }, - "power": "2345194" - }, - { - "pub_key": { - "ed25519": "vGSKfbQyKApvBhinpOOA0XQAdjxceihYNwtGskfZGyQ=" - }, - "power": "463811" - } - ], - "height_to_valset_update_id": [], - "outstanding_downtime_slashing": [], - "pending_consumer_packets": { - "list": [] - }, - "last_transmission_block_height": { - "height": "0" - }, - "preCCV": false - } - - `, - "v3.3.x": ` - { - "params": { - "enabled": true, - "blocks_per_distribution_transmission": "1000", - "distribution_transmission_channel": "", - "provider_fee_pool_addr_str": "", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", - "consumer_redistribution_fraction": "0.75", - "historical_entries": "10000", - "unbonding_period": "1209600s", - "soft_opt_out_threshold": "0.05", - "reward_denoms": [], - "provider_reward_denoms": [] - }, - "provider": { - "client_state": { - "chain_id": "provi", - "trust_level": { - "numerator": "1", - "denominator": "3" - }, - "trusting_period": "1197504s", - "unbonding_period": "1814400s", - "max_clock_drift": "10s", - "frozen_height": { - "revision_number": "0", - "revision_height": "0" - }, - "latest_height": { - "revision_number": "0", - "revision_height": "20" - }, - "proof_specs": [ - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 33, - "min_prefix_length": 4, - "max_prefix_length": 12, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - }, - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 32, - "min_prefix_length": 1, - "max_prefix_length": 1, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - } - ], - "upgrade_path": [ - "upgrade", - "upgradedIBCState" - ], - "allow_update_after_expiry": false, - "allow_update_after_misbehaviour": false - }, - "consensus_state": { - "timestamp": "2023-12-15T09:25:46.098392003Z", - "root": { - "hash": "0aoNOwWy67aQKs2r+FcDf2RxIq2UJtBb3g9ZWn0Gkas=" - }, - "next_validators_hash": "632730A03DEF630F77B61DF4092629007AE020B789713158FABCB104962FA54F" - }, - "initial_val_set": [ - { - "pub_key": { - "ed25519": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI=" - }, - "power": "500" - } - ] - }, - "new_chain": true - } - `, - "v4.x": ` - { - "params": { - "enabled": true, - "blocks_per_distribution_transmission": "1000", - "distribution_transmission_channel": "", - "provider_fee_pool_addr_str": "", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", - "consumer_redistribution_fraction": "0.75", - "historical_entries": "10000", - "unbonding_period": "1209600s", - "soft_opt_out_threshold": "0.05", - "reward_denoms": [], - "provider_reward_denoms": [], - "retry_delay_period": "3600s" - }, - "provider": { - "client_state": { - "chain_id": "provi", - "trust_level": { - "numerator": "1", - "denominator": "3" - }, - "trusting_period": "1197504s", - "unbonding_period": "1814400s", - "max_clock_drift": "10s", - "frozen_height": { - "revision_number": "0", - "revision_height": "0" - }, - "latest_height": { - "revision_number": "0", - "revision_height": "20" - }, - "proof_specs": [ - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 33, - "min_prefix_length": 4, - "max_prefix_length": 12, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - }, - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 32, - "min_prefix_length": 1, - "max_prefix_length": 1, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - } - ], - "upgrade_path": [ - "upgrade", - "upgradedIBCState" - ], - "allow_update_after_expiry": false, - "allow_update_after_misbehaviour": false - }, - "consensus_state": { - "timestamp": "2023-12-15T09:57:02.687079137Z", - "root": { - "hash": "EH9YbrWC3Qojy8ycl5GhOdVEC1ifPIGUUItL70bTkHo=" - }, - "next_validators_hash": "632730A03DEF630F77B61DF4092629007AE020B789713158FABCB104962FA54F" - }, - "initial_val_set": [ - { - "pub_key": { - "ed25519": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI=" - }, - "power": "500" - } - ] - }, - "new_chain": true - } + "params": { + "enabled": true, + "blocks_per_distribution_transmission": "1000", + "distribution_transmission_channel": "", + "provider_fee_pool_addr_str": "", + "ccv_timeout_period": "2419200s", + "transfer_timeout_period": "3600s", + "consumer_redistribution_fraction": "0.75", + "historical_entries": "10000", + "unbonding_period": "1209600s", + "soft_opt_out_threshold": "0", + "reward_denoms": [], + "provider_reward_denoms": [], + "retry_delay_period": "3600s", + "consumer_id": "1" + }, + "provider": { + "client_state": { + "chain_id": "provi", + "trust_level": { + "numerator": "1", + "denominator": "3" + }, + "trusting_period": "1197504s", + "unbonding_period": "1814400s", + "max_clock_drift": "10s", + "frozen_height": { + "revision_number": "0", + "revision_height": "0" + }, + "latest_height": { + "revision_number": "0", + "revision_height": "24" + }, + "proof_specs": [ + { + "leaf_spec": { + "hash": "SHA256", + "prehash_key": "NO_HASH", + "prehash_value": "SHA256", + "length": "VAR_PROTO", + "prefix": "AA==" + }, + "inner_spec": { + "child_order": [ + 0, + 1 + ], + "child_size": 33, + "min_prefix_length": 4, + "max_prefix_length": 12, + "empty_child": null, + "hash": "SHA256" + }, + "max_depth": 0, + "min_depth": 0, + "prehash_key_before_comparison": false + }, + { + "leaf_spec": { + "hash": "SHA256", + "prehash_key": "NO_HASH", + "prehash_value": "SHA256", + "length": "VAR_PROTO", + "prefix": "AA==" + }, + "inner_spec": { + "child_order": [ + 0, + 1 + ], + "child_size": 32, + "min_prefix_length": 1, + "max_prefix_length": 1, + "empty_child": null, + "hash": "SHA256" + }, + "max_depth": 0, + "min_depth": 0, + "prehash_key_before_comparison": false + } + ], + "upgrade_path": [ + "upgrade", + "upgradedIBCState" + ], + "allow_update_after_expiry": false, + "allow_update_after_misbehaviour": false + }, + "consensus_state": { + "timestamp": "2024-10-17T07:47:33.124389629Z", + "root": { + "hash": "cgIJagBEc/5lDkWS12NG5i7SSZ5hNFlDrlparFaWytc=" + }, + "next_validators_hash": "632730A03DEF630F77B61DF4092629007AE020B789713158FABCB104962FA54F" + }, + "initial_val_set": [ + { + "pub_key": { + "ed25519": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" + }, + "power": "500" + }, + { + "pub_key": { + "ed25519": "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" + }, + "power": "500" + }, + { + "pub_key": { + "ed25519": "mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI=" + }, + "power": "500" + } + ] + }, + "new_chain": true + } `, } @@ -458,223 +216,75 @@ func transformConsumerGenesis(filePath string, version *string) ([]byte, error) return result.Bytes(), nil } -// Check transformation of a version 2 ConsumerGenesis export to -// consumer genesis json format used by current consumer implementation. -func TestConsumerGenesisTransformationFromV2ToCurrent(t *testing.T) { - version := V2x - ctx := getClientCtx() - - srcGenesis := consumerTypes.GenesisState{} - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) - require.NoError(t, err, "Error parsing old version of ccv genesis content for consumer") - - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - resultGenesis := consumerTypes.GenesisState{} - result, err := transformConsumerGenesis(filePath, nil) - require.NoError(t, err) - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) - - // Some basic sanity checks on the content. - require.NotNil(t, resultGenesis.Provider.ClientState) - require.Equal(t, "cosmoshub-4", resultGenesis.Provider.ClientState.ChainId) - - require.Empty(t, resultGenesis.InitialValSet) - require.NotEmpty(t, resultGenesis.Provider.InitialValSet) - require.Equal(t, resultGenesis.Params.RetryDelayPeriod, ccvtypes.DefaultRetryDelayPeriod) - - // Check params: retry_delay_period prevents direct comparison - require.EqualValues(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.EqualValues(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.EqualValues(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.EqualValues(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.EqualValues(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.EqualValues(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.EqualValues(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - - // `SoftOptOutThreshold` is deprecated, so it should be set to zero the current version - require.EqualValues(t, "0", resultGenesis.Params.SoftOptOutThreshold) - require.EqualValues(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.EqualValues(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - - require.Equal(t, srcGenesis.ProviderClientState, resultGenesis.Provider.ClientState) - require.Nil(t, resultGenesis.ProviderClientState) - - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.ProviderConsensusState) - require.Nil(t, resultGenesis.ProviderConsensusState) - - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) - require.Equal(t, srcGenesis.InitialValSet, resultGenesis.Provider.InitialValSet) - require.Empty(t, resultGenesis.InitialValSet) -} - -// Check transformation of provider v3.3.x implementation to consumer V2 -func TestConsumerGenesisTransformationV330ToV2(t *testing.T) { - version := V33x +// Check transformation of provider v6.x implementation to consumer v5.x +func TestConsumerGenesisTransformationV6ToV5(t *testing.T) { + version := V6x filePath := createConsumerDataGenesisFile(t, version) defer os.Remove(filePath) - var srcGenesis consumerTypes.GenesisState + var srcGenesis ccvtypes.ConsumerGenesisState ctx := getClientCtx() err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) require.NoError(t, err) - targetVersion := V2x + targetVersion := V5x result, err := transformConsumerGenesis(filePath, &targetVersion) require.NoError(t, err) - resultGenesis := consumerTypes.GenesisState{} - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) - - require.Equal(t, srcGenesis.Params, resultGenesis.Params) - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.ProviderClientState) - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.ProviderConsensusState) - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) -} - -// Check transformation of provider v3.3.x implementation to current consumer version -func TestConsumerGenesisTransformationV330ToCurrent(t *testing.T) { - version := V33x - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - - var srcGenesis consumerTypes.GenesisState - ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) - require.NoError(t, err) - - result, err := transformConsumerGenesis(filePath, nil) - require.NoError(t, err) - - resultGenesis := consumerTypes.GenesisState{} + resultGenesis := ccvtypes.ConsumerGenesisState{} err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) require.NoError(t, err) - require.Equal(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.Equal(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.Equal(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.Equal(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.Equal(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.Equal(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.Equal(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.Equal(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.Equal(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - - // `SoftOptOutThreshold` is deprecated, so it should be set to zero the current version - require.Equal(t, "0", resultGenesis.Params.SoftOptOutThreshold) - - require.Equal(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.Equal(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - - require.Equal(t, resultGenesis.Params.RetryDelayPeriod, ccvtypes.DefaultRetryDelayPeriod) - - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.Provider.ClientState) - require.Nil(t, resultGenesis.ProviderClientState) - require.Nil(t, resultGenesis.ProviderConsensusState) - - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.Provider.ConsensusState) - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) -} - -// Check transformation of provider v4.x implementation to consumer V2 -func TestConsumerGenesisTransformationV4ToV2(t *testing.T) { - version := V4x - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - - var srcGenesis consumerTypes.GenesisState - ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) - require.NoError(t, err) - - targetVersion := V2x - result, err := transformConsumerGenesis(filePath, &targetVersion) - require.NoError(t, err) - - resultGenesis := consumerTypes.GenesisState{} - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) + resultRaw := map[string]json.RawMessage{} + err = json.Unmarshal(result, &resultRaw) require.NoError(t, err) - // Check params: retry_delay_period prevents direct comparison - require.EqualValues(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.EqualValues(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.EqualValues(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.EqualValues(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.EqualValues(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.EqualValues(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.EqualValues(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - require.EqualValues(t, srcGenesis.Params.SoftOptOutThreshold, resultGenesis.Params.SoftOptOutThreshold) - require.EqualValues(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.EqualValues(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - require.Equal(t, resultGenesis.Params.RetryDelayPeriod, time.Duration(0)) + // Check that resultRaw has no subelement consumer_id + paramsRaw, found := resultRaw["params"] + require.True(t, found, "params field not found in result genesis") - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.ProviderClientState) - require.Nil(t, resultGenesis.Provider.ClientState) - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.ProviderConsensusState) - require.Nil(t, resultGenesis.Provider.ConsensusState) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) - - require.Equal(t, 0, len(resultGenesis.Provider.InitialValSet)) - require.Equal(t, srcGenesis.Provider.InitialValSet, resultGenesis.InitialValSet) - require.Empty(t, resultGenesis.Provider.InitialValSet) - - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) -} - -// Check transformation of provider v3.3.x implementation to consumer V2 -func TestConsumerGenesisTransformationV4ToV33(t *testing.T) { - version := V4x - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - - var srcGenesis ccvtypes.ConsumerGenesisState - ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) + params := map[string]json.RawMessage{} + err = json.Unmarshal(paramsRaw, ¶ms) require.NoError(t, err) - targetVersion := V33x - result, err := transformConsumerGenesis(filePath, &targetVersion) - require.NoError(t, err) - resultGenesis := consumerTypes.GenesisState{} // Only difference to v33 is no RetryDelayPeriod - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) - - // Check params: retry_delay_period prevents direct comparison - require.EqualValues(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.EqualValues(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.EqualValues(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.EqualValues(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.EqualValues(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.EqualValues(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.EqualValues(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - require.EqualValues(t, srcGenesis.Params.SoftOptOutThreshold, resultGenesis.Params.SoftOptOutThreshold) - require.EqualValues(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.EqualValues(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.Provider.ClientState) - require.Nil(t, resultGenesis.ProviderClientState) - - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.Provider.ConsensusState) - require.Nil(t, resultGenesis.ProviderConsensusState) + _, consumerIdFound := params["consumer_id"] + require.False(t, consumerIdFound, "consumer_id field should not be present in params") + + // Iterate over all fields of ConsumerParams and check: + // - that they match between source and result genesis + // - that ConsumerId is empty in the result genesis + srcParams := reflect.ValueOf(srcGenesis.Params) + resultParams := reflect.ValueOf(resultGenesis.Params) + srcType := srcParams.Type() + for i := 0; i < srcParams.NumField(); i++ { + fieldName := srcType.Field(i).Name + srcField := srcParams.Field(i).Interface() + // srcType and resultType are the same so we can use index 'i' to get the field from resultParams + resultField := resultParams.Field(i).Interface() + if fieldName == "ConsumerId" { + // ConsumerId is not present in v5.x => expect empty string when unmarshalled to v6 + require.EqualValues(t, "", resultField, "Field %s does not match", fieldName) + } else { + require.EqualValues(t, srcField, resultField, "Field %s does not match", fieldName) + } + } - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) - require.Equal(t, srcGenesis.Provider.InitialValSet, resultGenesis.Provider.InitialValSet) - require.Empty(t, resultGenesis.InitialValSet) + // Iterate over all fields of ConsumerGenesisState and check + // - that they match between source and result genesis + // - that ConsumerId is empty in the result genesis + srcParams = reflect.ValueOf(srcGenesis) + resultParams = reflect.ValueOf(resultGenesis) + srcType = srcParams.Type() + require.Equal(t, srcParams.Type(), resultParams.Type(), "Different types of source and result genesis") + for i := 0; i < srcParams.NumField(); i++ { + fieldName := srcType.Field(i).Name + // Skip Params field as it was checked above + if fieldName == "Params" { + continue + } + srcField := srcParams.Field(i).Interface() + // srcType and resultType are the same so we can use index 'i' to get the field from resultParams + resultField := resultParams.Field(i).Interface() + require.EqualValues(t, srcField, resultField, "Field %s does not match", fieldName) + } } From d24cb497e0d8f6bee304959f6efadf2c8e7048e9 Mon Sep 17 00:00:00 2001 From: Bernd Mueller Date: Fri, 25 Oct 2024 15:54:32 +0200 Subject: [PATCH 2/2] addressed comments --- .../bug-fixes/2373-consumer-backward-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md index 553b0593aa..11ee6732cc 100644 --- a/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md +++ b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md @@ -1,2 +1,2 @@ -- `[x/consumer]` Add consumer genesis transformation for v5.x consumers. +- `[x/consumer]` Updated `genesis transform` CLI to transform `consumer-genesis` content exported by v6.2 providers for consumer chains at version v5. Removed transformation for older consumer versions. ([\#2373](https://github.com/cosmos/interchain-security/pull/2373)) \ No newline at end of file