diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d4002d44ec..a502dd7f70f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,6 +117,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (core/02-client) [\#2824](https://github.com/cosmos/ibc-go/pull/2824) Add genesis migrations for v6 to v7. The migration migrates the solo machine client state definition, removes all solo machine consensus states and removes the localhost client. * (core/24-host) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Add `PrefixedClientStorePath` and `PrefixedClientStoreKey` functions to 24-host * (core/02-client) [\#2819](https://github.com/cosmos/ibc-go/pull/2819) Add automatic in-place store migrations to remove the localhost client and migrate existing solo machine definitions. * (light-clients/06-solomachine) [\#2826](https://github.com/cosmos/ibc-go/pull/2826) Add `AppModuleBasic` for the 06-solomachine client and remove solo machine type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. diff --git a/modules/core/02-client/migrations/v7/genesis.go b/modules/core/02-client/migrations/v7/genesis.go new file mode 100644 index 00000000000..b1071f1a4f4 --- /dev/null +++ b/modules/core/02-client/migrations/v7/genesis.go @@ -0,0 +1,77 @@ +package v7 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v6/modules/core/exported" +) + +// MigrateGenesis accepts an exported IBC client genesis file and migrates it to: +// +// - Update solo machine client state protobuf definition (v2 to v3) +// - Remove all solo machine consensus states +// - Remove localhost client +func MigrateGenesis(clientGenState *clienttypes.GenesisState, cdc codec.ProtoCodecMarshaler) (*clienttypes.GenesisState, error) { + // To prune the client and consensus states, we will create new slices to fill up + // with information we want to keep. + var ( + clientsConsensus []clienttypes.ClientConsensusStates + clients []clienttypes.IdentifiedClientState + ) + + for _, client := range clientGenState.Clients { + clientType, _, err := clienttypes.ParseClientIdentifier(client.ClientId) + if err != nil { + return nil, err + } + + switch clientType { + case exported.Solomachine: + var clientState ClientState + if err := cdc.Unmarshal(client.ClientState.Value, &clientState); err != nil { + return nil, sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") + } + + updatedClientState := migrateSolomachine(clientState) + + protoAny, err := clienttypes.PackClientState(&updatedClientState) + if err != nil { + return nil, err + } + + clients = append(clients, clienttypes.IdentifiedClientState{ + ClientId: client.ClientId, + ClientState: protoAny, + }) + + case Localhost: + // remove localhost client state by not adding client state + + default: + // add all other client states + clients = append(clients, client) + } + + // iterate consensus states by client + for _, clientConsensusStates := range clientGenState.ClientsConsensus { + // look for consensus states for the current client + if clientConsensusStates.ClientId == client.ClientId { + switch clientType { + case exported.Solomachine, Localhost: + // remove all consensus states for the solo machine and localhost + // do not add to new clientsConsensus + + default: + // ensure all consensus states added for other client types + clientsConsensus = append(clientsConsensus, clientConsensusStates) + } + } + } + } + + clientGenState.Clients = clients + clientGenState.ClientsConsensus = clientsConsensus + return clientGenState, nil +} diff --git a/modules/core/02-client/migrations/v7/genesis_test.go b/modules/core/02-client/migrations/v7/genesis_test.go new file mode 100644 index 00000000000..990fc7d367e --- /dev/null +++ b/modules/core/02-client/migrations/v7/genesis_test.go @@ -0,0 +1,134 @@ +package v7_test + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + + ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" + "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v6/testing" +) + +func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { + // create tendermint clients + for i := 0; i < 3; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + suite.coordinator.SetupClients(path) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // update a second time to add more state + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + } + + // create multiple legacy solo machine clients + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) + + clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + // manually generate old proto buf definitions and set in genesis + // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + var clients []types.IdentifiedClientState + for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &v7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &v7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + // set client state + protoAny, err := codectypes.NewAnyWithValue(legacyClientState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + clients = append(clients, types.IdentifiedClientState{ + ClientId: sm.ClientID, + ClientState: protoAny, + }) + + // set in store for ease of determining expected genesis + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + bz, err := suite.chainA.App.AppCodec().MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + // obtain marshalled bytes to set in client store + bz, err = suite.chainA.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + var consensusStates []types.ConsensusStateWithHeight + + // set consensus states in store and genesis + for i := uint64(0); i < numCreations; i++ { + height := types.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + consensusStates = append(consensusStates, types.ConsensusStateWithHeight{ + Height: height, + ConsensusState: protoAny, + }) + } + + clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, types.ClientConsensusStates{ + ClientId: sm.ClientID, + ConsensusStates: consensusStates, + }) + } + + // solo machine clients must come before tendermint in expected + clientGenState.Clients = append(clients, clientGenState.Clients...) + + // migrate store get expected genesis + // store migration and genesis migration should produce identical results + // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + suite.Require().NoError(err) + expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + cdc, ok := suite.chainA.App.AppCodec().(codec.ProtoCodecMarshaler) + suite.Require().True(ok) + + migrated, err := v7.MigrateGenesis(&clientGenState, cdc) + suite.Require().NoError(err) + + bz, err := cdc.MarshalJSON(&expectedClientGenState) + suite.Require().NoError(err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + suite.Require().NoError(err) + expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + suite.Require().NoError(err) + + bz, err = cdc.MarshalJSON(migrated) + suite.Require().NoError(err) + + // Indent the JSON bz correctly. + err = json.Unmarshal(bz, &jsonObj) + suite.Require().NoError(err) + indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + suite.Require().NoError(err) + + suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) +} diff --git a/modules/core/legacy/v100/genesis.go b/modules/core/legacy/v100/genesis.go deleted file mode 100644 index 668d8a1f78a..00000000000 --- a/modules/core/legacy/v100/genesis.go +++ /dev/null @@ -1,54 +0,0 @@ -package v100 - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - tmtypes "github.com/tendermint/tendermint/types" - - clientv100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/types" -) - -// MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: -// -// - Update solo machine client state protobuf definition (v1 to v2) -// - Remove all solo machine consensus states -// - Remove all expired tendermint consensus states -func MigrateGenesis(appState genutiltypes.AppMap, clientCtx client.Context, genDoc tmtypes.GenesisDoc, maxExpectedTimePerBlock uint64) (genutiltypes.AppMap, error) { - if appState[host.ModuleName] != nil { - // ensure legacy solo machines are registered - clientv100.RegisterInterfaces(clientCtx.InterfaceRegistry) - - // unmarshal relative source genesis application state - ibcGenState := &types.GenesisState{} - clientCtx.Codec.MustUnmarshalJSON(appState[host.ModuleName], ibcGenState) - - clientGenState, err := clientv100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &ibcGenState.ClientGenesis, genDoc.GenesisTime, clienttypes.NewHeight(clienttypes.ParseChainID(genDoc.ChainID), uint64(genDoc.InitialHeight))) - if err != nil { - return nil, err - } - - ibcGenState.ClientGenesis = *clientGenState - - // set max expected time per block - connectionGenesis := connectiontypes.GenesisState{ - Connections: ibcGenState.ConnectionGenesis.Connections, - ClientConnectionPaths: ibcGenState.ConnectionGenesis.ClientConnectionPaths, - NextConnectionSequence: ibcGenState.ConnectionGenesis.NextConnectionSequence, - Params: connectiontypes.NewParams(maxExpectedTimePerBlock), - } - - ibcGenState.ConnectionGenesis = connectionGenesis - - // delete old genesis state - delete(appState, host.ModuleName) - - // set new ibc genesis state - appState[host.ModuleName] = clientCtx.Codec.MustMarshalJSON(ibcGenState) - } - return appState, nil -} diff --git a/modules/core/legacy/v100/genesis_test.go b/modules/core/legacy/v100/genesis_test.go deleted file mode 100644 index c9f1e800ce0..00000000000 --- a/modules/core/legacy/v100/genesis_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package v100_test - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/stretchr/testify/suite" - tmtypes "github.com/tendermint/tendermint/types" - - ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" - clientv100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - v100 "github.com/cosmos/ibc-go/v6/modules/core/legacy/v100" - "github.com/cosmos/ibc-go/v6/modules/core/types" - ibctesting "github.com/cosmos/ibc-go/v6/testing" - "github.com/cosmos/ibc-go/v6/testing/simapp" -) - -type LegacyTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -// TestLegacyTestSuite runs all the tests within this package. -func TestLegacyTestSuite(t *testing.T) { - suite.Run(t, new(LegacyTestSuite)) -} - -// SetupTest creates a coordinator with 2 test chains. -func (suite *LegacyTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) - suite.coordinator.CommitNBlocks(suite.chainA, 2) - suite.coordinator.CommitNBlocks(suite.chainB, 2) -} - -// NOTE: this test is mainly copied from 02-client/legacy/v100 -func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithCodec(encodingConfig.Marshaler) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // create tendermint clients - // NOTE: only 1 set of metadata is created, we aren't testing ordering - // The purpose of this test is to ensure the genesis states can be marshalled/unmarshalled - suite.coordinator.SetupClients(path) - clientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // manually generate old proto buf definitions and set in genesis - // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - var clients []clienttypes.IdentifiedClientState - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto definition - legacyClientState := &clientv100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &clientv100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - } - - // set client state - any, err := codectypes.NewAnyWithValue(legacyClientState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - client := clienttypes.IdentifiedClientState{ - ClientId: sm.ClientID, - ClientState: any, - } - clients = append(clients, client) - - // set in store for ease of determining expected genesis - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := clienttypes.NewHeight(0, 1) - height2 := clienttypes.NewHeight(1, 2) - height3 := clienttypes.NewHeight(0, 123) - - any, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - consensusState1 := clienttypes.ConsensusStateWithHeight{ - Height: height1, - ConsensusState: any, - } - consensusState2 := clienttypes.ConsensusStateWithHeight{ - Height: height2, - ConsensusState: any, - } - consensusState3 := clienttypes.ConsensusStateWithHeight{ - Height: height3, - ConsensusState: any, - } - - clientConsensusState := clienttypes.ClientConsensusStates{ - ClientId: sm.ClientID, - ConsensusStates: []clienttypes.ConsensusStateWithHeight{consensusState1, consensusState2, consensusState3}, - } - - clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clientConsensusState) - - // set in store for ease of determining expected genesis - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - // solo machine clients must come before tendermint in expected - clientGenState.Clients = append(clients, clientGenState.Clients...) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err := clientv100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // NOTE: these lines are added in comparison to 02-client/legacy/v100 - // generate appState with old ibc genesis state - appState := genutiltypes.AppMap{} - ibcGenState := types.DefaultGenesisState() - ibcGenState.ClientGenesis = clientGenState - clientv100.RegisterInterfaces(clientCtx.InterfaceRegistry) - appState[host.ModuleName] = clientCtx.Codec.MustMarshalJSON(ibcGenState) - genDoc := tmtypes.GenesisDoc{ - ChainID: suite.chainA.ChainID, - GenesisTime: suite.coordinator.CurrentTime, - InitialHeight: suite.chainA.GetContext().BlockHeight(), - } - - // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning - migrated, err := v100.MigrateGenesis(appState, clientCtx, genDoc, uint64(connectiontypes.DefaultTimePerBlock)) - suite.Require().NoError(err) - - expectedAppState := genutiltypes.AppMap{} - expectedIBCGenState := types.DefaultGenesisState() - expectedIBCGenState.ClientGenesis = expectedClientGenState - - bz, err := clientCtx.Codec.MarshalJSON(expectedIBCGenState) - suite.Require().NoError(err) - expectedAppState[host.ModuleName] = bz - - suite.Require().Equal(expectedAppState, migrated) -} diff --git a/modules/core/migrations/v7/genesis.go b/modules/core/migrations/v7/genesis.go new file mode 100644 index 00000000000..356589fd3d3 --- /dev/null +++ b/modules/core/migrations/v7/genesis.go @@ -0,0 +1,42 @@ +package v7 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + + clientv7 "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + "github.com/cosmos/ibc-go/v6/modules/core/types" +) + +// MigrateGenesis accepts an exported IBC client genesis file and migrates it to: +// +// - Update solo machine client state protobuf definition (v2 to v3) +// - Remove all solo machine consensus states +// - Remove any localhost clients +func MigrateGenesis(appState genutiltypes.AppMap, cdc codec.ProtoCodecMarshaler) (genutiltypes.AppMap, error) { + if appState[host.ModuleName] == nil { + return appState, nil + } + + // ensure legacy solo machines types are registered + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + + // unmarshal old ibc genesis state + ibcGenState := &types.GenesisState{} + cdc.MustUnmarshalJSON(appState[host.ModuleName], ibcGenState) + + clientGenState, err := clientv7.MigrateGenesis(&ibcGenState.ClientGenesis, cdc) + if err != nil { + return nil, err + } + + ibcGenState.ClientGenesis = *clientGenState + + // delete old genesis state + delete(appState, host.ModuleName) + + // set new ibc genesis state + appState[host.ModuleName] = cdc.MustMarshalJSON(ibcGenState) + return appState, nil +} diff --git a/modules/core/migrations/v7/genesis_test.go b/modules/core/migrations/v7/genesis_test.go new file mode 100644 index 00000000000..1ba9fcdf48d --- /dev/null +++ b/modules/core/migrations/v7/genesis_test.go @@ -0,0 +1,160 @@ +package v7_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/stretchr/testify/suite" + + ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" + clientv7 "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + v7 "github.com/cosmos/ibc-go/v6/modules/core/migrations/v7" + "github.com/cosmos/ibc-go/v6/modules/core/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" +) + +type MigrationsV7TestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +// TestMigrationsV7TestSuite runs all the tests within this package. +func TestMigrationsV7TestSuite(t *testing.T) { + suite.Run(t, new(MigrationsV7TestSuite)) +} + +// SetupTest creates a coordinator with 2 test chains. +func (suite *MigrationsV7TestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +// NOTE: this test is mainly copied from 02-client/migrations/v7/genesis_test.go +func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { + // create tendermint clients + for i := 0; i < 3; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + suite.coordinator.SetupClients(path) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // update a second time to add more state + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + } + + // create multiple legacy solo machine clients + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) + + clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + // manually generate old proto buf definitions and set in genesis + // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + var clients []clienttypes.IdentifiedClientState + for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &clientv7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &clientv7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + // set client state + protoAny, err := codectypes.NewAnyWithValue(legacyClientState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + clients = append(clients, clienttypes.IdentifiedClientState{ + ClientId: sm.ClientID, + ClientState: protoAny, + }) + + // set in store for ease of determining expected genesis + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + bz, err := suite.chainA.App.AppCodec().MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + // obtain marshalled bytes to set in client store + bz, err = suite.chainA.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + var consensusStates []clienttypes.ConsensusStateWithHeight + + // set consensus states in store and genesis + for i := uint64(0); i < 10; i++ { + height := clienttypes.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + consensusStates = append(consensusStates, clienttypes.ConsensusStateWithHeight{ + Height: height, + ConsensusState: protoAny, + }) + } + + clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clienttypes.ClientConsensusStates{ + ClientId: sm.ClientID, + ConsensusStates: consensusStates, + }) + } + + // solo machine clients must come before tendermint in expected + clientGenState.Clients = append(clients, clientGenState.Clients...) + + // migrate store get expected genesis + // store migration and genesis migration should produce identical results + // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients + err := clientv7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + suite.Require().NoError(err) + expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + cdc := suite.chainA.App.AppCodec().(*codec.ProtoCodec) + + // NOTE: these lines are added in comparison to 02-client/migrations/v7/genesis_test.go + // generate appState with old ibc genesis state + appState := genutiltypes.AppMap{} + ibcGenState := types.DefaultGenesisState() + ibcGenState.ClientGenesis = clientGenState + + // ensure tests pass even if the legacy solo machine is already registered + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + appState[host.ModuleName] = cdc.MustMarshalJSON(ibcGenState) + + // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning + migrated, err := v7.MigrateGenesis(appState, cdc) + suite.Require().NoError(err) + + expectedAppState := genutiltypes.AppMap{} + expectedIBCGenState := types.DefaultGenesisState() + expectedIBCGenState.ClientGenesis = expectedClientGenState + + bz, err := cdc.MarshalJSON(expectedIBCGenState) + suite.Require().NoError(err) + expectedAppState[host.ModuleName] = bz + + suite.Require().Equal(expectedAppState, migrated) +} diff --git a/modules/core/migrations/v7/migrations.go b/modules/core/migrations/v7/migrations.go deleted file mode 100644 index 8eeda416eb4..00000000000 --- a/modules/core/migrations/v7/migrations.go +++ /dev/null @@ -1,44 +0,0 @@ -package v7 - -import ( - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" - - clientkeeper "github.com/cosmos/ibc-go/v6/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" -) - -// Localhost is the client type for a localhost client. It is also used as the clientID -// for the localhost client. -const Localhost string = "09-localhost" - -// MigrateToV7 prunes the 09-Localhost client and associated consensus states from the ibc store -func MigrateToV7(ctx sdk.Context, clientKeeper clientkeeper.Keeper) { - clientStore := clientKeeper.ClientStore(ctx, Localhost) - - iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) - var heights []exported.Height - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - // key is in the format "consensusStates/" - if len(keySplit) != 2 || keySplit[0] != string(host.KeyConsensusStatePrefix) { - continue - } - - // collect consensus states to be pruned - heights = append(heights, clienttypes.MustParseHeight(keySplit[1])) - } - - // delete all consensus states - for _, height := range heights { - clientStore.Delete(host.ConsensusStateKey(height)) - } - - // delete the client state - clientStore.Delete(host.ClientStateKey()) -} diff --git a/modules/core/migrations/v7/migrations_test.go b/modules/core/migrations/v7/migrations_test.go deleted file mode 100644 index 53c18ae20d8..00000000000 --- a/modules/core/migrations/v7/migrations_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package v7_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" - - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" - v7 "github.com/cosmos/ibc-go/v6/modules/core/migrations/v7" - ibctesting "github.com/cosmos/ibc-go/v6/testing" -) - -type MigrationsV7TestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -func (suite *MigrationsV7TestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) -} - -func TestIBCTestSuite(t *testing.T) { - suite.Run(t, new(MigrationsV7TestSuite)) -} - -func (suite *MigrationsV7TestSuite) TestMigrateToV7() { - var clientStore sdk.KVStore - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success: prune localhost client state", - func() { - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - }, - true, - }, - { - "success: prune localhost client state and consensus states", - func() { - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - - for i := 0; i < 10; i++ { - clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) - } - }, - true, - }, - { - "07-tendermint client state and consensus states remain in client store", - func() { - clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clienttypes.FormatClientIdentifier(exported.Tendermint, 0)) - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - - for i := 0; i < 10; i++ { - clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) - } - }, - false, - }, - { - "06-solomachine client state and consensus states remain in client store", - func() { - clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clienttypes.FormatClientIdentifier(exported.Solomachine, 0)) - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - - for i := 0; i < 10; i++ { - clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) - } - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - ctx := suite.chainA.GetContext() - clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), v7.Localhost) - - tc.malleate() - - v7.MigrateToV7(ctx, suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) - - if tc.expPass { - suite.Require().False(clientStore.Has(host.ClientStateKey())) - - for i := 0; i < 10; i++ { - suite.Require().False(clientStore.Has(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))))) - } - } else { - suite.Require().True(clientStore.Has(host.ClientStateKey())) - - for i := 0; i < 10; i++ { - suite.Require().True(clientStore.Has(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))))) - } - } - }) - } -}