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..11ee6732cc --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md @@ -0,0 +1,2 @@ +- `[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 diff --git a/app/consumer/genesis.go b/app/consumer/genesis.go index be56824856..e16c2f4b0e 100644 --- a/app/consumer/genesis.go +++ b/app/consumer/genesis.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "strings" "github.com/spf13/cobra" @@ -16,7 +15,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - consumerTypes "github.com/cosmos/interchain-security/v6/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v6/x/ccv/types" ) @@ -33,152 +31,50 @@ 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 -// 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 - oldConsumerGenesis := consumerTypes.GenesisState{} - err := ctx.Codec.UnmarshalJSON(jsonRaw, &oldConsumerGenesis) - if err != nil { - return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) - } - - initialValSet := oldConsumerGenesis.InitialValSet - // transformation from >= v3.3.x - if len(initialValSet) == 0 { - initialValSet = oldConsumerGenesis.Provider.InitialValSet - } - - clientState := oldConsumerGenesis.ProviderClientState - if clientState == nil { - clientState = oldConsumerGenesis.Provider.ClientState - } - - consensusState := oldConsumerGenesis.ProviderConsensusState - if consensusState == nil { - consensusState = oldConsumerGenesis.Provider.ConsensusState - } - - // Use DefaultRetryDelayPeriod if not set - if oldConsumerGenesis.Params.RetryDelayPeriod == 0 { - oldConsumerGenesis.Params.RetryDelayPeriod = types.DefaultRetryDelayPeriod - } - - // `SoftOptOutThreshold` is deprecated in the current consumer implementation, so set to zero - oldConsumerGenesis.Params.SoftOptOutThreshold = "0" - - // Versions before v3.3.x of provider genesis data fills up deprecated fields - // ProviderClientState, ConsensusState and InitialValSet in type GenesisState - newGenesis := types.ConsumerGenesisState{ - Params: oldConsumerGenesis.Params, - Provider: types.ProviderInfo{ - ClientState: clientState, - ConsensusState: consensusState, - InitialValSet: initialValSet, - }, - NewChain: oldConsumerGenesis.NewChain, - } - - newJson, err := ctx.Codec.MarshalJSON(&newGenesis) - if err != nil { - return nil, fmt.Errorf("failed marshalling data to new type: %s", err) - } - return newJson, 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) { - // v1,2,3 uses deprecated fields of GenesisState type - srcConGen := consumerTypes.GenesisState{} - err := ctx.Codec.UnmarshalJSON(jsonRaw, &srcConGen) - if err != nil { - return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) - } - - // Remove retry_delay_period from 'params' - params, err := ctx.Codec.MarshalJSON(&srcConGen.Params) - if err != nil { - return nil, err - } - tmp := map[string]json.RawMessage{} - if err := json.Unmarshal(params, &tmp); err != 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 := tmp["retry_delay_period"] + _, exists := paramsMap[param] if exists { - delete(tmp, "retry_delay_period") + delete(paramsMap, param) } - params, err = json.Marshal(tmp) - 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 + return json.Marshal(paramsMap) } -// Transformation of consumer genesis content as it is exported from current provider version -// to a format readable by consumer implementation of version v2.x -// Use removePreHashKey to remove prehash_key_before_comparison from result. -func transformToV2(jsonRaw []byte, ctx client.Context, removePreHashKey bool) (json.RawMessage, error) { - // populate deprecated fields of GenesisState used by version v2.x - srcConGen := consumerTypes.GenesisState{} +// Transformation of consumer genesis content as it is exported by provider version >= v6.2.x +// to a format supported by consumer chains version with either SDK v0.47 and ICS < v4.5.0 or SDK v0.50 and ICS < v6.2.0 +// This transformation removes the 'consumer_id' parameter from the 'params' field introduced in ICS v6.2.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 retry_delay_period from 'params' if present (introduced in v4.x) + // Remove 'consumer_id' from 'params' params, err := ctx.Codec.MarshalJSON(&srcConGen.Params) if err != nil { return nil, err } - paramsMap := map[string]json.RawMessage{} - if err := json.Unmarshal(params, ¶msMap); err != nil { - return nil, fmt.Errorf("unmarshalling 'params' failed: %v", err) - } - _, exists := paramsMap["retry_delay_period"] - if exists { - delete(paramsMap, "retry_delay_period") - } - params, err = json.Marshal(paramsMap) + + params, err = removeParameterFromParams(params, "consumer_id") if err != nil { return nil, err } - // marshal GenesisState and patch 'params' value + // Marshal GenesisState and patch 'params' value result, err := ctx.Codec.MarshalJSON(&srcConGen) if err != nil { return nil, err @@ -189,65 +85,10 @@ func transformToV2(jsonRaw []byte, ctx client.Context, removePreHashKey bool) (j } genState["params"] = params - provider, err := ctx.Codec.MarshalJSON(&srcConGen.Provider) - if err != nil { - return nil, fmt.Errorf("marshalling 'Provider' failed: %v", err) - } - providerMap := map[string]json.RawMessage{} - if err := json.Unmarshal(provider, &providerMap); err != nil { - return nil, fmt.Errorf("unmarshalling 'provider' failed: %v", err) - } - - // patch .initial_val_set form .provider.initial_val_set if needed - if len(srcConGen.Provider.InitialValSet) > 0 { - valSet, exists := providerMap["initial_val_set"] - if !exists { - return nil, fmt.Errorf("'initial_val_set' not found in provider data") - } - _, exists = genState["initial_val_set"] - if exists { - genState["initial_val_set"] = valSet - } - } - - // patch .provider_consensus_state from provider.consensus_state if needed - if srcConGen.Provider.ConsensusState != nil { - valSet, exists := providerMap["consensus_state"] - if !exists { - return nil, fmt.Errorf("'consensus_state' not found in provider data") - } - _, exists = genState["provider_consensus_state"] - if exists { - genState["provider_consensus_state"] = valSet - } - } - - // patch .provider_client_state from provider.client_state if needed - if srcConGen.Provider.ClientState != nil { - clientState, exists := providerMap["client_state"] - if !exists { - return nil, fmt.Errorf("'client_state' not found in provider data") - } - _, exists = genState["provider_client_state"] - if exists { - genState["provider_client_state"] = clientState - } - } - - // delete .provider entry (introduced in v3.3.x) - delete(genState, "provider") - - // Marshall final result result, err = json.Marshal(genState) if err != nil { return nil, fmt.Errorf("marshalling transformation result failed: %v", err) } - - if removePreHashKey { - // remove all `prehash_key_before_comparison` entries not supported in v2.x (see ics23) - re := regexp.MustCompile(`,\s*"prehash_key_before_comparison"\s*:\s*(false|true)`) - result = re.ReplaceAll(result, []byte{}) - } return result, nil } @@ -258,16 +99,8 @@ 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 v4_x_x, v5_x_x: + newConsumerGenesis, err = transformToV5(jsonRaw, ctx) default: err = fmt.Errorf("unsupported target version '%s'. Run %s --help", targetVersion, version.AppName) @@ -280,10 +113,9 @@ func transformGenesis(ctx client.Context, targetVersion IcsVersion, jsonRaw []by } // Transform a consumer genesis json file exported from a given ccv provider version -// to a consumer genesis json format supported by current ccv consumer version or v2.x +// to a consumer genesis json format supported by current ccv consumer version // This allows user to patch consumer genesis of -// - current implementation from exports of provider of < v3.3.x -// - v2.x from exports of provider >= v3.2.x +// - v4.x, v5.x, v6.1.x from exports of provider >= v6.2.x // // Result will be written to defined output. func TransformConsumerGenesis(cmd *cobra.Command, args []string) error { @@ -336,7 +168,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 +181,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..8c9266f626 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,84 @@ 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 - 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) - 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 v6.2.x implementation to consumer v5.x +func TestConsumerGenesisTransformationV6ToV5(t *testing.T) { + CheckGenesisTransform(t, V6x, V5x) } -// 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{} - 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 v6.2.x implementation to consumer v4.x +func TestConsumerGenesisTransformationV6ToV4(t *testing.T) { + CheckGenesisTransform(t, V6x, V4x) } -// Check transformation of provider v4.x implementation to consumer V2 -func TestConsumerGenesisTransformationV4ToV2(t *testing.T) { - version := V4x - filePath := createConsumerDataGenesisFile(t, version) +// CheckGenesisTransform checks that the transformation of consumer genesis data +// from a given source version to a target version is successful +func CheckGenesisTransform(t *testing.T, sourceVersion string, targetVersion string) { + filePath := createConsumerDataGenesisFile(t, sourceVersion) defer os.Remove(filePath) - var srcGenesis consumerTypes.GenesisState + var srcGenesis ccvtypes.ConsumerGenesisState ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) + err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[sourceVersion]), &srcGenesis) require.NoError(t, err) - targetVersion := V2x result, err := transformConsumerGenesis(filePath, &targetVersion) require.NoError(t, err) - resultGenesis := consumerTypes.GenesisState{} + resultGenesis := ccvtypes.ConsumerGenesisState{} 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, resultGenesis.Params.RetryDelayPeriod, time.Duration(0)) - - 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) + resultRaw := map[string]json.RawMessage{} + err = json.Unmarshal(result, &resultRaw) 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 that resultRaw has no subelement consumer_id + paramsRaw, found := resultRaw["params"] + require.True(t, found, "params field not found in result genesis") - // 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) + params := map[string]json.RawMessage{} + err = json.Unmarshal(paramsRaw, ¶ms) + require.NoError(t, err) - 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) + } }