diff --git a/docs/docs/03-light-clients/02-localhost/01-overview.md b/docs/docs/03-light-clients/02-localhost/01-overview.md index d32ccb01684..c6754004097 100644 --- a/docs/docs/03-light-clients/02-localhost/01-overview.md +++ b/docs/docs/03-light-clients/02-localhost/01-overview.md @@ -14,33 +14,43 @@ slug: /ibc/light-clients/localhost/overview Learn about the 09-localhost light client module. ::: -The 09-localhost light client module implements a localhost loopback client with the ability to send and receive IBC packets to and from the same state machine. +The 09-localhost light client module implements a stateless localhost loopback client with the ability to send and +receive IBC packets to and from the same state machine. ### Context -In a multichain environment, application developers will be used to developing cross-chain applications through IBC. From their point of view, whether or not they are interacting with multiple modules on the same chain or on different chains should not matter. The localhost client module enables a unified interface to interact with different applications on a single chain, using the familiar IBC application layer semantics. +In a multichain environment, application developers will be used to developing cross-chain applications through IBC. +From their point of view, whether or not they are interacting with multiple modules on the same chain or on different +chains should not matter. The localhost client module enables a unified interface to interact with different +applications on a single chain, using the familiar IBC application layer semantics. ### Implementation -There exists a [single sentinel `ClientState`](03-client-state.md) instance with the client identifier `09-localhost`. +There exists a localhost light client module which can be invoked with the client identifier `09-localhost`. The light +client is stateless, so the `ClientState` is constructed on demand when required. -To supplement this, a [sentinel `ConnectionEnd` is stored in core IBC](04-connection.md) state with the connection identifier `connection-localhost`. This enables IBC applications to create channels directly on top of the sentinel connection which leverage the 09-localhost loopback functionality. +To supplement this, a [sentinel `ConnectionEnd` is stored in core IBC](04-connection.md) state with the connection +identifier `connection-localhost`. This enables IBC applications to create channels directly on top of the sentinel +connection which leverage the 09-localhost loopback functionality. -[State verification](05-state-verification.md) for channel state in handshakes or processing packets is reduced in complexity, the `09-localhost` client can simply compare bytes stored under the standardized key paths. +[State verification](05-state-verification.md) for channel state in handshakes or processing packets is reduced in +complexity, the `09-localhost` client can simply compare bytes stored under the standardized key paths. ### Localhost vs *regular* client -The localhost client aims to provide a unified approach to interacting with applications on a single chain, as the IBC application layer provides for cross-chain interactions. To achieve this unified interface though, there are a number of differences under the hood compared to a 'regular' IBC client (excluding `06-solomachine` and `09-localhost` itself). +The localhost client aims to provide a unified approach to interacting with applications on a single chain, as the IBC +application layer provides for cross-chain interactions. To achieve this unified interface though, there are a number of +differences under the hood compared to a 'regular' IBC client (excluding `06-solomachine` and `09-localhost` itself). The table below lists some important differences: -| | Regular client | Localhost | -| -------------------------------------------- | --------------------------------------------------------------------------- | --------- | -| Number of clients | Many instances of a client *type* corresponding to different counterparties | A single sentinel client with the client identifier `09-localhost`| -| Client creation | Relayer (permissionless) | `ClientState` is instantiated in the `InitGenesis` handler of the 02-client submodule in core IBC | -| Client updates | Relayer submits headers using `MsgUpdateClient` | Latest height is updated periodically through the ABCI [`BeginBlock`](https://docs.cosmos.network/v0.47/building-modules/beginblock-endblock) interface of the 02-client submodule in core IBC | -| Number of connections | Many connections, 1 (or more) per client | A single sentinel connection with the connection identifier `connection-localhost` | -| Connection creation | Connection handshake, provided underlying client | Sentinel `ConnectionEnd` is created and set in store in the `InitGenesis` handler of the 03-connection submodule in core IBC | -| Counterparty | Underlying client, representing another chain | Client with identifier `09-localhost` in same chain | -| `VerifyMembership` and `VerifyNonMembership` | Performs proof verification using consensus state roots | Performs state verification using key-value lookups in the core IBC store | -| Integration | Expected to register codec types using the `AppModuleBasic` interface | Registers codec types within the core IBC module | +| | Regular client | Localhost | +|----------------------------------------------|-----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| Number of clients | Many instances of a client *type* corresponding to different counterparties | A single sentinel client with the client identifier `09-localhost` | +| Client creation | Relayer (permissionless) | Implicitly made available by the 02-client submodule in core IBC | +| Client updates | Relayer submits headers using `MsgUpdateClient` | No client updates are required as the localhost implementation is stateless | +| Number of connections | Many connections, 1 (or more) per client | A single sentinel connection with the connection identifier `connection-localhost` | +| Connection creation | Connection handshake, provided underlying client | Sentinel `ConnectionEnd` is created and set in store in the `InitGenesis` handler of the 03-connection submodule in core IBC | +| Counterparty | Underlying client, representing another chain | Client with identifier `09-localhost` in same chain | +| `VerifyMembership` and `VerifyNonMembership` | Performs proof verification using consensus state roots | Performs state verification using key-value lookups in the core IBC store | +| `ClientState` storage | `ClientState` stored and directly provable with `VerifyMembership` | Stateless, so `ClientState` is not provable directly with `VerifyMembership` | diff --git a/docs/docs/03-light-clients/02-localhost/03-client-state.md b/docs/docs/03-light-clients/02-localhost/03-client-state.md index a242a2b649c..3cbdf05442a 100644 --- a/docs/docs/03-light-clients/02-localhost/03-client-state.md +++ b/docs/docs/03-light-clients/02-localhost/03-client-state.md @@ -5,64 +5,6 @@ sidebar_position: 3 slug: /ibc/light-clients/localhost/client-state --- - # `ClientState` -The 09-localhost `ClientState` maintains a single field used to track the latest sequence of the state machine i.e. the height of the blockchain. - -```go -type ClientState struct { - // the latest height of the blockchain - LatestHeight clienttypes.Height -} -``` - -The 09-localhost `ClientState` is instantiated in the `InitGenesis` handler of the 02-client submodule in core IBC. -It calls `CreateLocalhostClient`, declaring a new `ClientState` and initializing it with its own client prefixed store. - -```go -func (k Keeper) CreateLocalhostClient(ctx sdk.Context) error { - clientModule, found := k.router.GetRoute(exported.LocalhostClientID) - if !found { - return errorsmod.Wrap(types.ErrRouteNotFound, exported.LocalhostClientID) - } - - return clientModule.Initialize(ctx, exported.LocalhostClientID, nil, nil) -} -``` - -It is possible to disable the localhost client by removing the `09-localhost` entry from the `allowed_clients` list through governance. - -## Client updates - -The latest height is updated periodically through the ABCI [`BeginBlock`](https://docs.cosmos.network/v0.47/building-modules/beginblock-endblock) interface of the 02-client submodule in core IBC. - -[See `BeginBlocker` in abci.go.](https://github.com/cosmos/ibc-go/blob/v8.1.1/modules/core/02-client/abci.go#L12) - -```go -func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { - // ... - - if clientState, found := k.GetClientState(ctx, exported.Localhost); found { - if k.GetClientStatus(ctx, clientState, exported.Localhost) == exported.Active { - k.UpdateLocalhostClient(ctx, clientState) - } - } -} -``` - -The above calls into the 09-localhost `UpdateState` method of the `ClientState` . -It retrieves the current block height from the application context and sets the `LatestHeight` of the 09-localhost client. - -```go -func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) []exported.Height { - height := clienttypes.GetSelfHeight(ctx) - cs.LatestHeight = height - - clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) - - return []exported.Height{height} -} -``` - -Note that the 09-localhost `ClientState` is not updated through the 02-client interface leveraged by conventional IBC light clients. +The 09-localhost client is stateless and has no types. diff --git a/docs/docs/03-light-clients/02-localhost/05-state-verification.md b/docs/docs/03-light-clients/02-localhost/05-state-verification.md index eb92f2899e9..82d10d6ff40 100644 --- a/docs/docs/03-light-clients/02-localhost/05-state-verification.md +++ b/docs/docs/03-light-clients/02-localhost/05-state-verification.md @@ -5,7 +5,6 @@ sidebar_position: 5 slug: /ibc/light-clients/localhost/state-verification --- - # State verification The localhost client handles state verification through the `LightClientModule` interface methods `VerifyMembership` and `VerifyNonMembership` by performing read-only operations directly on the core IBC store. @@ -20,3 +19,5 @@ The 09-localhost light client module defines a `SentinelProof` as a single byte. ```go var SentinelProof = []byte{0x01} ``` + +The `ClientState` of `09-localhost` is stateless, so it is not directly provable with `VerifyMembership` or `VerifyNonMembership`. diff --git a/docs/docs/05-migrations/13-v8-to-v9.md b/docs/docs/05-migrations/13-v8-to-v9.md index 02c205f74e8..3a5c2242aba 100644 --- a/docs/docs/05-migrations/13-v8-to-v9.md +++ b/docs/docs/05-migrations/13-v8-to-v9.md @@ -102,6 +102,7 @@ func NewMsgModuleQuerySafe( - The utility function `QueryLatestConsensusState` of `04-channel` CLI has been removed. - `UnmarshalPacketData` now takes in the context, portID, and channelID. This allows the packet data to be unmarshaled based on the channel version. - `Router` reference has been removed from IBC core keeper: [#6138](https://github.com/cosmos/ibc-go/pull/6138). Please use `PortKeeper.Router` instead. +- The function `CreateLocalhostClient` has been removed. The localhost client is now stateless. ### 02-client @@ -185,3 +186,10 @@ The `IterateConsensusMetadata` function has been removed. - The `VerifyMembershipMsg` and `VerifyNonMembershipMsg` payloads for `SudoMsg` have been extended to include a new field, `MerklePath`. The existing `Path` field will remain the same. The new `MerklePath` field is used if and only if the provided key path contains non-utf8 encoded symbols, and as a result will encode the JSON field `merkle_path` as a base64 encoded bytestring. See [23-commitment](#23-commitment). - The `ExportMetadataMsg` struct has been removed and is no longer required for contracts to implement. Core IBC will handle exporting all key/value's written to the store by a light client contract. - The `ZeroCustomFields` interface function has been removed from the `ClientState` interface. Core IBC only used this function to set tendermint client states when scheduling an IBC software upgrade. The interface function has been replaced by a type assertion. + +### 09-localhost + +The `09-localhost` light client has been made stateless and will no longer update the client on every block. The `ClientState` is constructed on demand when required. +The `ClientState` itself is therefore no longer provable direcly with `VerifyMembership` or `VerifyNonMembership`. + +Previously stored client state data is pruned automatically on IBC module store migration from `ConsensusVersion` 6 to 7. diff --git a/e2e/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go index cb1da13cc7c..ae041a3b4cb 100644 --- a/e2e/tests/core/02-client/client_test.go +++ b/e2e/tests/core/02-client/client_test.go @@ -487,6 +487,10 @@ func (s *ClientTestSuite) TestAllowedClientsParam() { status, err := query.ClientStatus(ctx, chainA, ibctesting.FirstClientID) s.Require().NoError(err) s.Require().Equal(ibcexported.Unauthorized.String(), status) + + status, err = query.ClientStatus(ctx, chainA, ibcexported.Localhost) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Unauthorized.String(), status) }) } diff --git a/e2e/tests/transfer/localhost_test.go b/e2e/tests/transfer/localhost_test.go index c24a4d19425..982f05a6b68 100644 --- a/e2e/tests/transfer/localhost_test.go +++ b/e2e/tests/transfer/localhost_test.go @@ -53,21 +53,6 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA), "failed to wait for blocks") - t.Run("verify begin blocker was executed", func(t *testing.T) { - cs, err := query.ClientState(ctx, chainA, exported.LocalhostClientID) - s.Require().NoError(err) - - localhostClientState, ok := cs.(*localhost.ClientState) - s.Require().True(ok) - originalHeight := localhostClientState.LatestHeight - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA), "failed to wait for blocks") - - cs, err = query.ClientState(ctx, chainA, exported.LocalhostClientID) - s.Require().NoError(err) - s.Require().True(cs.(*localhost.ClientState).LatestHeight.GT(originalHeight), "client state height was not incremented") - }) - t.Run("channel open init localhost", func(t *testing.T) { msgChanOpenInit := channeltypes.NewMsgChannelOpenInit( transfertypes.PortID, channelA.Version, diff --git a/e2e/testsuite/codec.go b/e2e/testsuite/codec.go index 42f790a78e6..5fabcf48671 100644 --- a/e2e/testsuite/codec.go +++ b/e2e/testsuite/codec.go @@ -34,7 +34,6 @@ import ( channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" ibctesting "github.com/cosmos/ibc-go/v8/testing" simappparams "github.com/cosmos/ibc-go/v8/testing/simapp/params" ) @@ -76,7 +75,6 @@ func codecAndEncodingConfig() (*codec.ProtoCodec, simappparams.EncodingConfig) { channeltypes.RegisterInterfaces(cfg.InterfaceRegistry) connectiontypes.RegisterInterfaces(cfg.InterfaceRegistry) ibctmtypes.RegisterInterfaces(cfg.InterfaceRegistry) - localhost.RegisterInterfaces(cfg.InterfaceRegistry) wasmtypes.RegisterInterfaces(cfg.InterfaceRegistry) // all other types diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index c8d36b414ec..c7ebd8bcfd7 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -5,7 +5,6 @@ import ( "github.com/cosmos/ibc-go/v8/modules/core/02-client/keeper" "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ) @@ -33,11 +32,4 @@ func BeginBlocker(ctx sdk.Context, k *keeper.Keeper) { keeper.EmitUpgradeChainEvent(ctx, plan.Height) } } - - // update the localhost client with the latest block height if it is active. - if clientState, found := k.GetClientState(ctx, exported.Localhost); found { - if k.GetClientStatus(ctx, exported.LocalhostClientID) == exported.Active { - k.UpdateLocalhostClient(ctx, clientState) - } - } } diff --git a/modules/core/02-client/genesis.go b/modules/core/02-client/genesis.go index d89cc5073ed..1e72b363554 100644 --- a/modules/core/02-client/genesis.go +++ b/modules/core/02-client/genesis.go @@ -50,12 +50,6 @@ func InitGenesis(ctx sdk.Context, k *keeper.Keeper, gs types.GenesisState) { } k.SetNextClientSequence(ctx, gs.NextClientSequence) - - // if the localhost already exists in state (included in the genesis file), - // it must be overwritten to ensure its stored height equals the context block height - if err := k.CreateLocalhostClient(ctx); err != nil { - panic(fmt.Errorf("failed to initialise localhost client: %s", err.Error())) - } } // ExportGenesis returns the ibc client submodule's exported genesis. diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 51939164938..275567c195f 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -14,7 +14,6 @@ import ( "github.com/cosmos/ibc-go/v8/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -64,10 +63,7 @@ func (suite *KeeperTestSuite) TestCreateClient() { }, { "failure: 09-localhost client type not supported", - func() { - lhClientState := localhost.NewClientState(clienttypes.GetSelfHeight(suite.chainA.GetContext())) - clientState = suite.chainA.App.AppCodec().MustMarshal(lhClientState) - }, + func() {}, exported.Localhost, false, }, diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index 132332809f9..c231423fcb7 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -119,26 +119,11 @@ func (suite *KeeperTestSuite) TestQueryClientStates() { { "empty pagination", func() { - localhost := types.NewIdentifiedClientState(exported.LocalhostClientID, suite.chainA.GetClientState(exported.LocalhostClientID)) - expClientStates = types.IdentifiedClientStates{localhost} + expClientStates = nil req = &types.QueryClientStatesRequest{} }, true, }, - { - "success, only localhost", - func() { - localhost := types.NewIdentifiedClientState(exported.LocalhostClientID, suite.chainA.GetClientState(exported.LocalhostClientID)) - expClientStates = types.IdentifiedClientStates{localhost} - req = &types.QueryClientStatesRequest{ - Pagination: &query.PageRequest{ - Limit: 3, - CountTotal: true, - }, - } - }, - true, - }, { "success", func() { @@ -151,12 +136,11 @@ func (suite *KeeperTestSuite) TestQueryClientStates() { clientStateA1 := path1.EndpointA.GetClientState() clientStateA2 := path2.EndpointA.GetClientState() - localhost := types.NewIdentifiedClientState(exported.LocalhostClientID, suite.chainA.GetClientState(exported.LocalhostClientID)) idcs := types.NewIdentifiedClientState(path1.EndpointA.ClientID, clientStateA1) idcs2 := types.NewIdentifiedClientState(path2.EndpointA.ClientID, clientStateA2) // order is sorted by client id - expClientStates = types.IdentifiedClientStates{localhost, idcs, idcs2}.Sort() + expClientStates = types.IdentifiedClientStates{idcs, idcs2}.Sort() req = &types.QueryClientStatesRequest{ Pagination: &query.PageRequest{ Limit: 20, diff --git a/modules/core/02-client/keeper/keeper.go b/modules/core/02-client/keeper/keeper.go index d30d50554e4..cb3097dbb89 100644 --- a/modules/core/02-client/keeper/keeper.go +++ b/modules/core/02-client/keeper/keeper.go @@ -68,16 +68,6 @@ func (k *Keeper) Route(clientID string) (exported.LightClientModule, bool) { return k.router.GetRoute(clientID) } -// CreateLocalhostClient initialises the 09-localhost client state and sets it in state. -func (k *Keeper) CreateLocalhostClient(ctx sdk.Context) error { - clientModule, found := k.router.GetRoute(exported.LocalhostClientID) - if !found { - return errorsmod.Wrap(types.ErrRouteNotFound, exported.LocalhostClientID) - } - - return clientModule.Initialize(ctx, exported.LocalhostClientID, nil, nil) -} - // UpdateLocalhostClient updates the 09-localhost client to the latest block height and chain ID. func (k *Keeper) UpdateLocalhostClient(ctx sdk.Context, clientState exported.ClientState) []exported.Height { clientModule, found := k.router.GetRoute(exported.LocalhostClientID) diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 739d735ea54..3d8eb4c3a90 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -26,7 +26,6 @@ import ( host "github.com/cosmos/ibc-go/v8/modules/core/24-host" "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" ibctesting "github.com/cosmos/ibc-go/v8/testing" "github.com/cosmos/ibc-go/v8/testing/simapp" ) @@ -143,10 +142,9 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() { func (suite *KeeperTestSuite) TestGetAllGenesisClients() { clientIDs := []string{ - exported.LocalhostClientID, testClientID2, testClientID3, testClientID, + testClientID2, testClientID3, testClientID, } expClients := []exported.ClientState{ - localhost.NewClientState(types.GetSelfHeight(suite.chainA.GetContext())), ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), @@ -305,10 +303,7 @@ func (suite *KeeperTestSuite) TestIterateClientStates() { "all clientIDs", nil, func() []string { - allClientIDs := []string{exported.LocalhostClientID} - allClientIDs = append(allClientIDs, expSMClientIDs...) - allClientIDs = append(allClientIDs, expTMClientIDs...) - return allClientIDs + return append(expSMClientIDs, expTMClientIDs...) }, }, { diff --git a/modules/core/02-client/keeper/migrations.go b/modules/core/02-client/keeper/migrations.go index cefdd5da0fc..3dd32bbbf46 100644 --- a/modules/core/02-client/keeper/migrations.go +++ b/modules/core/02-client/keeper/migrations.go @@ -5,6 +5,8 @@ import ( "github.com/cosmos/ibc-go/v8/modules/core/02-client/migrations/v7" "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + "github.com/cosmos/ibc-go/v8/modules/core/exported" ) // Migrator is a struct for handling in-place store migrations. @@ -27,12 +29,6 @@ func (m Migrator) Migrate2to3(ctx sdk.Context) error { return v7.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc, m.keeper) } -// Migrate3to4 migrates from consensus version 3 to 4. -// This migration enables the localhost client. -func (m Migrator) Migrate3to4(ctx sdk.Context) error { - return v7.MigrateLocalhostClient(ctx, m.keeper) -} - // MigrateParams migrates from consensus version 4 to 5. // This migration takes the parameters that are currently stored and managed by x/params // and stores them directly in the ibc module's state. @@ -47,3 +43,13 @@ func (m Migrator) MigrateParams(ctx sdk.Context) error { m.keeper.Logger(ctx).Info("successfully migrated client to self-manage params") return nil } + +// MigrateToStatelessLocalhost deletes the localhost client state. The localhost +// implementation is now stateless. +func (m Migrator) MigrateToStatelessLocalhost(ctx sdk.Context) error { + clientStore := m.keeper.ClientStore(ctx, exported.LocalhostClientID) + + // delete the client state + clientStore.Delete(host.ClientStateKey()) + return nil +} diff --git a/modules/core/02-client/keeper/migrations_test.go b/modules/core/02-client/keeper/migrations_test.go index 7bdd26b8a12..7b9370307bf 100644 --- a/modules/core/02-client/keeper/migrations_test.go +++ b/modules/core/02-client/keeper/migrations_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "github.com/cosmos/ibc-go/v8/modules/core/02-client/keeper" "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ) @@ -41,3 +42,19 @@ func (suite *KeeperTestSuite) TestMigrateParams() { }) } } + +func (suite *KeeperTestSuite) TestMigrateToStatelessLocalhost() { + // set localhost in state + clientStore := suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), ibcexported.LocalhostClientID) + clientStore.Set(host.ClientStateKey(), []byte("clientState")) + + m := keeper.NewMigrator(suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + err := m.MigrateToStatelessLocalhost(suite.chainA.GetContext()) + suite.Require().NoError(err) + suite.Require().False(clientStore.Has(host.ClientStateKey())) + + // rerun migration on no localhost set + err = m.MigrateToStatelessLocalhost(suite.chainA.GetContext()) + suite.Require().NoError(err) + suite.Require().False(clientStore.Has(host.ClientStateKey())) +} diff --git a/modules/core/02-client/migrations/v7/expected_keepers.go b/modules/core/02-client/migrations/v7/expected_keepers.go index 4a73d42d20f..0485b120558 100644 --- a/modules/core/02-client/migrations/v7/expected_keepers.go +++ b/modules/core/02-client/migrations/v7/expected_keepers.go @@ -13,5 +13,4 @@ type ClientKeeper interface { GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) SetClientState(ctx sdk.Context, clientID string, clientState exported.ClientState) ClientStore(ctx sdk.Context, clientID string) storetypes.KVStore - CreateLocalhostClient(ctx sdk.Context) error } diff --git a/modules/core/02-client/migrations/v7/localhost.go b/modules/core/02-client/migrations/v7/localhost.go deleted file mode 100644 index 49709b9db9f..00000000000 --- a/modules/core/02-client/migrations/v7/localhost.go +++ /dev/null @@ -1,10 +0,0 @@ -package v7 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// MigrateLocalhostClient initialises the 09-localhost client state and sets it in state. -func MigrateLocalhostClient(ctx sdk.Context, clientKeeper ClientKeeper) error { - return clientKeeper.CreateLocalhostClient(ctx) -} diff --git a/modules/core/02-client/migrations/v7/localhost_test.go b/modules/core/02-client/migrations/v7/localhost_test.go deleted file mode 100644 index 1a6d657477c..00000000000 --- a/modules/core/02-client/migrations/v7/localhost_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package v7_test - -import ( - "github.com/cosmos/ibc-go/v8/modules/core/02-client/migrations/v7" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" - "github.com/cosmos/ibc-go/v8/modules/core/exported" -) - -func (suite *MigrationsV7TestSuite) TestMigrateLocalhostClient() { - suite.SetupTest() - - // note: explicitly remove the localhost client before running migration handler - clientStore := suite.chainA.GetSimApp().GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), exported.LocalhostClientID) - clientStore.Delete(host.ClientStateKey()) - - clientState, found := suite.chainA.GetSimApp().GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), exported.LocalhostClientID) - suite.Require().False(found) - suite.Require().Nil(clientState) - - err := v7.MigrateLocalhostClient(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetIBCKeeper().ClientKeeper) - suite.Require().NoError(err) - - clientState, found = suite.chainA.GetSimApp().GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), exported.LocalhostClientID) - suite.Require().True(found) - suite.Require().NotNil(clientState) -} diff --git a/modules/core/module.go b/modules/core/module.go index e7f6ebd4a94..9a3fc9d9084 100644 --- a/modules/core/module.go +++ b/modules/core/module.go @@ -140,11 +140,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { connectionMigrator := connectionkeeper.NewMigrator(am.keeper.ConnectionKeeper) if err := cfg.RegisterMigration(exported.ModuleName, 3, func(ctx sdk.Context) error { - if err := connectionMigrator.Migrate3to4(ctx); err != nil { - return err - } - - return clientMigrator.Migrate3to4(ctx) + return connectionMigrator.Migrate3to4(ctx) }); err != nil { panic(err) } @@ -160,8 +156,11 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { } channelMigrator := channelkeeper.NewMigrator(am.keeper.ChannelKeeper) - err := cfg.RegisterMigration(exported.ModuleName, 5, channelMigrator.MigrateParams) - if err != nil { + if err := cfg.RegisterMigration(exported.ModuleName, 5, channelMigrator.MigrateParams); err != nil { + panic(err) + } + + if err := cfg.RegisterMigration(exported.ModuleName, 6, clientMigrator.MigrateToStatelessLocalhost); err != nil { panic(err) } } @@ -184,7 +183,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 6 } +func (AppModule) ConsensusVersion() uint64 { return 7 } // BeginBlock returns the begin blocker for the ibc module. func (am AppModule) BeginBlock(ctx context.Context) error { diff --git a/modules/core/types/codec.go b/modules/core/types/codec.go index 57b4018adb8..1216ab22864 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -7,7 +7,6 @@ import ( connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" ) // RegisterInterfaces registers ibc types against interfaces using the global InterfaceRegistry. @@ -17,5 +16,4 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { connectiontypes.RegisterInterfaces(registry) channeltypes.RegisterInterfaces(registry) commitmenttypes.RegisterInterfaces(registry) - localhost.RegisterInterfaces(registry) } diff --git a/modules/light-clients/08-wasm/keeper/contract_keeper_test.go b/modules/light-clients/08-wasm/keeper/contract_keeper_test.go index b7b59bbcdec..35517fa1536 100644 --- a/modules/light-clients/08-wasm/keeper/contract_keeper_test.go +++ b/modules/light-clients/08-wasm/keeper/contract_keeper_test.go @@ -12,7 +12,6 @@ import ( host "github.com/cosmos/ibc-go/v8/modules/core/24-host" "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" ) func (suite *KeeperTestSuite) TestWasmInstantiate() { @@ -105,8 +104,7 @@ func (suite *KeeperTestSuite) TestWasmInstantiate() { "failure: change clientstate type", func() { suite.mockVM.InstantiateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.MessageInfo, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) { - newClientState := localhost.NewClientState(clienttypes.NewHeight(1, 1)) - store.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(suite.chainA.App.AppCodec(), newClientState)) + store.Set(host.ClientStateKey(), []byte("changed client state")) data, err := json.Marshal(types.EmptyResult{}) suite.Require().NoError(err) @@ -269,8 +267,7 @@ func (suite *KeeperTestSuite) TestWasmMigrate() { "failure: change clientstate type", func() { suite.mockVM.MigrateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) { - newClientState := localhost.NewClientState(clienttypes.NewHeight(1, 1)) - store.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(suite.chainA.App.AppCodec(), newClientState)) + store.Set(host.ClientStateKey(), []byte("changed client state")) data, err := json.Marshal(types.EmptyResult{}) suite.Require().NoError(err) @@ -486,21 +483,6 @@ func (suite *KeeperTestSuite) TestWasmSudo() { }, types.ErrWasmAttributesNotAllowed, }, - { - "failure: invalid clientstate type", - func() { - suite.mockVM.RegisterSudoCallback(types.UpdateStateMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) { - newClientState := localhost.NewClientState(clienttypes.NewHeight(1, 1)) - store.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(suite.chainA.App.AppCodec(), newClientState)) - - resp, err := json.Marshal(types.UpdateStateResult{}) - suite.Require().NoError(err) - - return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{Data: resp}}, wasmtesting.DefaultGasUsed, nil - }) - }, - types.ErrWasmInvalidContractModification, - }, { "failure: unmarshallable clientstate bytes", func() { diff --git a/modules/light-clients/08-wasm/keeper/msg_server_test.go b/modules/light-clients/08-wasm/keeper/msg_server_test.go index cb17191c69f..def0351b2a3 100644 --- a/modules/light-clients/08-wasm/keeper/msg_server_test.go +++ b/modules/light-clients/08-wasm/keeper/msg_server_test.go @@ -17,7 +17,6 @@ import ( clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -249,9 +248,7 @@ func (suite *KeeperTestSuite) TestMsgMigrateContract() { suite.mockVM.MigrateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) { // the checksum written in here will be overwritten - newClientState := localhost.NewClientState(clienttypes.NewHeight(1, 1)) - - store.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(suite.chainA.App.AppCodec(), newClientState)) + store.Set(host.ClientStateKey(), []byte("changed client state")) data, err := json.Marshal(types.EmptyResult{}) suite.Require().NoError(err) diff --git a/modules/light-clients/09-localhost/client_state.go b/modules/light-clients/09-localhost/client_state.go deleted file mode 100644 index b5e7cef2d79..00000000000 --- a/modules/light-clients/09-localhost/client_state.go +++ /dev/null @@ -1,163 +0,0 @@ -package localhost - -import ( - "bytes" - - errorsmod "cosmossdk.io/errors" - storetypes "cosmossdk.io/store/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" - commitmenttypesv2 "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types/v2" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" - ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" - "github.com/cosmos/ibc-go/v8/modules/core/exported" -) - -var _ exported.ClientState = (*ClientState)(nil) - -// NewClientState creates a new 09-localhost ClientState instance. -func NewClientState(height clienttypes.Height) *ClientState { - return &ClientState{ - LatestHeight: height, - } -} - -// ClientType returns the 09-localhost client type. -func (ClientState) ClientType() string { - return exported.Localhost -} - -// Validate performs a basic validation of the client state fields. -func (cs ClientState) Validate() error { - if cs.LatestHeight.RevisionHeight == 0 { - return errorsmod.Wrapf(ibcerrors.ErrInvalidHeight, "local revision height cannot be zero") - } - - return nil -} - -// Initialize ensures that initial consensus state for localhost is nil. -func (ClientState) Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore storetypes.KVStore, consState exported.ConsensusState) error { - if consState != nil { - return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "initial consensus state for localhost must be nil.") - } - - clientState := ClientState{ - LatestHeight: clienttypes.GetSelfHeight(ctx), - } - - clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &clientState)) - - return nil -} - -// GetTimestampAtHeight returns the current block time retrieved from the application context. The localhost client does not store consensus states and thus -// cannot provide a timestamp for the provided height. -func (ClientState) GetTimestampAtHeight(ctx sdk.Context, _ storetypes.KVStore, _ codec.BinaryCodec, _ exported.Height) (uint64, error) { - return uint64(ctx.BlockTime().UnixNano()), nil -} - -// VerifyMembership is a generic proof verification method which verifies the existence of a given key and value within the IBC store. -// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). -// The caller must provide the full IBC store. -func (ClientState) VerifyMembership( - ctx sdk.Context, - store storetypes.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - proof []byte, - path exported.Path, - value []byte, -) error { - // ensure the proof provided is the expected sentinel localhost client proof - if !bytes.Equal(proof, SentinelProof) { - return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof)) - } - - merklePath, ok := path.(commitmenttypesv2.MerklePath) - if !ok { - return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypesv2.MerklePath{}, path) - } - - if len(merklePath.GetKeyPath()) != 2 { - return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath()) - } - - // The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store - bz := store.Get(merklePath.KeyPath[1]) - if bz == nil { - return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value not found for path %s", path) - } - - if !bytes.Equal(bz, value) { - return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value provided does not equal value stored at path: %s", path) - } - - return nil -} - -// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath within the IBC store. -// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). -// The caller must provide the full IBC store. -func (ClientState) VerifyNonMembership( - ctx sdk.Context, - store storetypes.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - proof []byte, - path exported.Path, -) error { - // ensure the proof provided is the expected sentinel localhost client proof - if !bytes.Equal(proof, SentinelProof) { - return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof)) - } - - merklePath, ok := path.(commitmenttypesv2.MerklePath) - if !ok { - return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypesv2.MerklePath{}, path) - } - - if len(merklePath.GetKeyPath()) != 2 { - return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath()) - } - - // The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store - if store.Has(merklePath.KeyPath[1]) { - return errorsmod.Wrapf(clienttypes.ErrFailedNonMembershipVerification, "value found for path %s", path) - } - - return nil -} - -// VerifyClientMessage is unsupported by the 09-localhost client type and returns an error. -func (ClientState) VerifyClientMessage(_ sdk.Context, _ codec.BinaryCodec, _ storetypes.KVStore, _ exported.ClientMessage) error { - return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "client message verification is unsupported by the localhost client") -} - -// CheckForMisbehaviour is unsupported by the 09-localhost client type and performs a no-op, returning false. -func (ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ storetypes.KVStore, _ exported.ClientMessage) bool { - return false -} - -// UpdateStateOnMisbehaviour is unsupported by the 09-localhost client type and performs a no-op. -func (ClientState) UpdateStateOnMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ storetypes.KVStore, _ exported.ClientMessage) { -} - -// UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. -// Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. -func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore storetypes.KVStore, _ exported.ClientMessage) []exported.Height { - height := clienttypes.GetSelfHeight(ctx) - cs.LatestHeight = height - - clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) - - return []exported.Height{height} -} diff --git a/modules/light-clients/09-localhost/client_state_test.go b/modules/light-clients/09-localhost/client_state_test.go deleted file mode 100644 index 2eac5411d97..00000000000 --- a/modules/light-clients/09-localhost/client_state_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package localhost_test - -import ( - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v8/modules/core/exported" - ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" -) - -func (suite *LocalhostTestSuite) TestClientType() { - clientState := localhost.NewClientState(clienttypes.NewHeight(3, 10)) - suite.Require().Equal(exported.Localhost, clientState.ClientType()) -} - -func (suite *LocalhostTestSuite) TestGetLatestHeight() { - expectedHeight := clienttypes.NewHeight(3, 10) - clientState := localhost.NewClientState(expectedHeight) - suite.Require().Equal(expectedHeight, clientState.LatestHeight) -} - -func (suite *LocalhostTestSuite) TestGetTimestampAtHeight() { - ctx := suite.chain.GetContext() - clientState := localhost.NewClientState(clienttypes.NewHeight(1, 10)) - - timestamp, err := clientState.GetTimestampAtHeight(ctx, nil, nil, nil) - suite.Require().NoError(err) - suite.Require().Equal(uint64(ctx.BlockTime().UnixNano()), timestamp) -} - -func (suite *LocalhostTestSuite) TestValidate() { - testCases := []struct { - name string - clientState exported.ClientState - expPass bool - }{ - { - name: "valid client", - clientState: localhost.NewClientState(clienttypes.NewHeight(3, 10)), - expPass: true, - }, - { - name: "invalid height", - clientState: localhost.NewClientState(clienttypes.ZeroHeight()), - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - err := tc.clientState.Validate() - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestInitialize() { - testCases := []struct { - name string - consState exported.ConsensusState - expPass bool - }{ - { - "valid initialization", - nil, - true, - }, - { - "invalid consenus state", - &ibctm.ConsensusState{}, - false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - clientState := localhost.NewClientState(clienttypes.NewHeight(3, 10)) - clientStore := suite.chain.GetSimApp().GetIBCKeeper().ClientKeeper.ClientStore(suite.chain.GetContext(), exported.LocalhostClientID) - - err := clientState.Initialize(suite.chain.GetContext(), suite.chain.Codec, clientStore, tc.consState) - - if tc.expPass { - suite.Require().NoError(err, "valid testcase: %s failed", tc.name) - } else { - suite.Require().Error(err, "invalid testcase: %s passed", tc.name) - } - }) - suite.SetupTest() - - } -} - -func (suite *LocalhostTestSuite) TestVerifyClientMessage() { - clientState := localhost.NewClientState(clienttypes.NewHeight(1, 10)) - suite.Require().Error(clientState.VerifyClientMessage(suite.chain.GetContext(), nil, nil, nil)) -} - -func (suite *LocalhostTestSuite) TestVerifyCheckForMisbehaviour() { - clientState := localhost.NewClientState(clienttypes.NewHeight(1, 10)) - suite.Require().False(clientState.CheckForMisbehaviour(suite.chain.GetContext(), nil, nil, nil)) -} - -func (suite *LocalhostTestSuite) TestUpdateState() { - clientState := localhost.NewClientState(clienttypes.NewHeight(1, uint64(suite.chain.GetContext().BlockHeight()))) - store := suite.chain.GetSimApp().GetIBCKeeper().ClientKeeper.ClientStore(suite.chain.GetContext(), exported.LocalhostClientID) - - suite.coordinator.CommitBlock(suite.chain) - - heights := clientState.UpdateState(suite.chain.GetContext(), suite.chain.Codec, store, nil) - - expHeight := clienttypes.NewHeight(1, uint64(suite.chain.GetContext().BlockHeight())) - suite.Require().True(heights[0].EQ(expHeight)) - - var ok bool - clientState, ok = suite.chain.GetClientState(exported.LocalhostClientID).(*localhost.ClientState) - suite.Require().True(ok) - suite.Require().True(heights[0].EQ(clientState.LatestHeight)) -} diff --git a/modules/light-clients/09-localhost/codec.go b/modules/light-clients/09-localhost/codec.go deleted file mode 100644 index 89d4e6980c1..00000000000 --- a/modules/light-clients/09-localhost/codec.go +++ /dev/null @@ -1,16 +0,0 @@ -package localhost - -import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - - "github.com/cosmos/ibc-go/v8/modules/core/exported" -) - -// RegisterInterfaces registers the tendermint concrete client-related -// implementations and interfaces. -func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( - (*exported.ClientState)(nil), - &ClientState{}, - ) -} diff --git a/modules/light-clients/09-localhost/codec_test.go b/modules/light-clients/09-localhost/codec_test.go deleted file mode 100644 index 5b90faf2160..00000000000 --- a/modules/light-clients/09-localhost/codec_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package localhost_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - - ibc "github.com/cosmos/ibc-go/v8/modules/core" - localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" -) - -func TestCodecTypeRegistration(t *testing.T) { - testCases := []struct { - name string - typeURL string - expPass bool - }{ - { - "success: ClientState", - sdk.MsgTypeURL(&localhost.ClientState{}), - true, - }, - { - "type not registered on codec", - "ibc.invalid.MsgTypeURL", - false, - }, - } - - for _, tc := range testCases { - tc := tc - - t.Run(tc.name, func(t *testing.T) { - encodingCfg := moduletestutil.MakeTestEncodingConfig(ibc.AppModuleBasic{}) - msg, err := encodingCfg.Codec.InterfaceRegistry().Resolve(tc.typeURL) - - if tc.expPass { - require.NotNil(t, msg) - require.NoError(t, err) - } else { - require.Nil(t, msg) - require.Error(t, err) - } - }) - } -} diff --git a/modules/light-clients/09-localhost/keys.go b/modules/light-clients/09-localhost/keys.go deleted file mode 100644 index bf10ed0d800..00000000000 --- a/modules/light-clients/09-localhost/keys.go +++ /dev/null @@ -1,12 +0,0 @@ -package localhost - -const ( - // ModuleName defines the 09-localhost light client module name - ModuleName = "09-localhost" -) - -// SentinelProof defines the 09-localhost sentinel proof. -// Submission of nil or empty proofs is disallowed in core IBC messaging. -// This serves as a placeholder value for relayers to leverage as the proof field in various message types. -// Localhost client state verification will fail if the sentintel proof value is not provided. -var SentinelProof = []byte{0x01} diff --git a/modules/light-clients/09-localhost/light_client_module.go b/modules/light-clients/09-localhost/light_client_module.go index 3c4570514b2..a362ab151a6 100644 --- a/modules/light-clients/09-localhost/light_client_module.go +++ b/modules/light-clients/09-localhost/light_client_module.go @@ -1,6 +1,8 @@ package localhost import ( + "bytes" + errorsmod "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" @@ -8,10 +10,24 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" + commitmenttypesv2 "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types/v2" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" "github.com/cosmos/ibc-go/v8/modules/core/exported" ) +const ( + // ModuleName defines the 09-localhost light client module name + ModuleName = "09-localhost" +) + +// SentinelProof defines the 09-localhost sentinel proof. +// Submission of nil or empty proofs is disallowed in core IBC messaging. +// This serves as a placeholder value for relayers to leverage as the proof field in various message types. +// Localhost client state verification will fail if the sentintel proof value is not provided. +var SentinelProof = []byte{0x01} + var _ exported.LightClientModule = (*LightClientModule)(nil) // LightClientModule implements the core IBC api.LightClientModule interface. @@ -36,56 +52,40 @@ func (l *LightClientModule) RegisterStoreProvider(storeProvider exported.ClientS l.storeProvider = storeProvider } -// Initialize ensures that initial consensus state for localhost is nil. +// Initialize returns an error because it is stateless. // // CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to be 09-localhost. -func (l LightClientModule) Initialize(ctx sdk.Context, clientID string, _, consensusStateBz []byte) error { - if len(consensusStateBz) != 0 { - return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, "initial consensus state for localhost must be nil.") - } - - clientState := ClientState{ - LatestHeight: clienttypes.GetSelfHeight(ctx), - } - - clientStore := l.storeProvider.ClientStore(ctx, exported.LocalhostClientID) - clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(l.cdc, &clientState)) - return nil +func (LightClientModule) Initialize(_ sdk.Context, _ string, _, _ []byte) error { + return errorsmod.Wrap(clienttypes.ErrClientExists, "localhost is stateless and cannot be initialized") } // VerifyClientMessage is unsupported by the 09-localhost client type and returns an error. // // CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to be 09-localhost. -func (LightClientModule) VerifyClientMessage(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) error { +func (LightClientModule) VerifyClientMessage(_ sdk.Context, _ string, _ exported.ClientMessage) error { return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "client message verification is unsupported by the localhost client") } // CheckForMisbehaviour is unsupported by the 09-localhost client type and performs a no-op, returning false. -func (LightClientModule) CheckForMisbehaviour(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) bool { +func (LightClientModule) CheckForMisbehaviour(_ sdk.Context, _ string, _ exported.ClientMessage) bool { return false } // UpdateStateOnMisbehaviour is unsupported by the 09-localhost client type and performs a no-op. -func (LightClientModule) UpdateStateOnMisbehaviour(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) { +func (LightClientModule) UpdateStateOnMisbehaviour(_ sdk.Context, _ string, _ exported.ClientMessage) { // no-op } -// UpdateState obtains the localhost client state and calls into the clientState.UpdateState method. +// UpdateState performs a no-op and returns the context height in the updated heights return value. // // CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to be 09-localhost. -func (l LightClientModule) UpdateState(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) []exported.Height { - clientStore := l.storeProvider.ClientStore(ctx, clientID) - cdc := l.cdc - - clientState, found := getClientState(clientStore, cdc) - if !found { - panic(errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID)) - } - - return clientState.UpdateState(ctx, cdc, clientStore, clientMsg) +func (LightClientModule) UpdateState(ctx sdk.Context, _ string, _ exported.ClientMessage) []exported.Height { + return []exported.Height{clienttypes.GetSelfHeight(ctx)} } -// VerifyMembership obtains the localhost client state and calls into the clientState.VerifyMembership method. +// VerifyMembership is a generic proof verification method which verifies the existence of a given key and value within the IBC store. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +// The caller must provide the full IBC store. // // CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to be 09-localhost. func (l LightClientModule) VerifyMembership( @@ -98,19 +98,38 @@ func (l LightClientModule) VerifyMembership( path exported.Path, value []byte, ) error { - clientStore := l.storeProvider.ClientStore(ctx, clientID) ibcStore := ctx.KVStore(l.key) - cdc := l.cdc - clientState, found := getClientState(clientStore, cdc) - if !found { - return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) + // ensure the proof provided is the expected sentinel localhost client proof + if !bytes.Equal(proof, SentinelProof) { + return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof)) + } + + merklePath, ok := path.(commitmenttypesv2.MerklePath) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) + } + + if len(merklePath.GetKeyPath()) != 2 { + return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath()) } - return clientState.VerifyMembership(ctx, ibcStore, cdc, height, delayTimePeriod, delayBlockPeriod, proof, path, value) + // The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store + bz := ibcStore.Get(merklePath.KeyPath[1]) + if bz == nil { + return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value not found for path %s", path) + } + + if !bytes.Equal(bz, value) { + return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value provided does not equal value stored at path: %s", path) + } + + return nil } -// VerifyNonMembership obtains the localhost client state and calls into the clientState.VerifyNonMembership method. +// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath within the IBC store. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +// The caller must provide the full IBC store. // // CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to be 09-localhost. func (l LightClientModule) VerifyNonMembership( @@ -122,41 +141,45 @@ func (l LightClientModule) VerifyNonMembership( proof []byte, path exported.Path, ) error { - clientStore := l.storeProvider.ClientStore(ctx, clientID) ibcStore := ctx.KVStore(l.key) - cdc := l.cdc - clientState, found := getClientState(clientStore, cdc) - if !found { - return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) + // ensure the proof provided is the expected sentinel localhost client proof + if !bytes.Equal(proof, SentinelProof) { + return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof)) + } + + merklePath, ok := path.(commitmenttypesv2.MerklePath) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) } - return clientState.VerifyNonMembership(ctx, ibcStore, cdc, height, delayTimePeriod, delayBlockPeriod, proof, path) + if len(merklePath.GetKeyPath()) != 2 { + return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath()) + } + + // The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store + if ibcStore.Has(merklePath.KeyPath[1]) { + return errorsmod.Wrapf(clienttypes.ErrFailedNonMembershipVerification, "value found for path %s", path) + } + + return nil } // Status always returns Active. The 09-localhost status cannot be changed. -func (LightClientModule) Status(ctx sdk.Context, clientID string) exported.Status { +func (LightClientModule) Status(_ sdk.Context, _ string) exported.Status { return exported.Active } -// LatestHeight returns the latest height for the client state for the given client identifier. -// If no client is present for the provided client identifier a zero value height is returned. +// LatestHeight returns the context height. // // CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to be 09-localhost. -func (l LightClientModule) LatestHeight(ctx sdk.Context, clientID string) exported.Height { - clientStore := l.storeProvider.ClientStore(ctx, clientID) - - clientState, found := getClientState(clientStore, l.cdc) - if !found { - return clienttypes.ZeroHeight() - } - - return clientState.LatestHeight +func (LightClientModule) LatestHeight(ctx sdk.Context, _ string) exported.Height { + return clienttypes.GetSelfHeight(ctx) } // TimestampAtHeight returns the current block time retrieved from the application context. The localhost client does not store consensus states and thus // cannot provide a timestamp for the provided height. -func (LightClientModule) TimestampAtHeight(ctx sdk.Context, clientID string, height exported.Height) (uint64, error) { +func (LightClientModule) TimestampAtHeight(ctx sdk.Context, _ string, _ exported.Height) (uint64, error) { return uint64(ctx.BlockTime().UnixNano()), nil } @@ -166,6 +189,6 @@ func (LightClientModule) RecoverClient(_ sdk.Context, _, _ string) error { } // VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded. -func (LightClientModule) VerifyUpgradeAndUpdateState(ctx sdk.Context, clientID string, newClient, newConsState, upgradeClientProof, upgradeConsensusStateProof []byte) error { +func (LightClientModule) VerifyUpgradeAndUpdateState(_ sdk.Context, _ string, _, _, _, _ []byte) error { return errorsmod.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") } diff --git a/modules/light-clients/09-localhost/light_client_module_test.go b/modules/light-clients/09-localhost/light_client_module_test.go index e0cf02291d3..cccb09de28f 100644 --- a/modules/light-clients/09-localhost/light_client_module_test.go +++ b/modules/light-clients/09-localhost/light_client_module_test.go @@ -1,6 +1,10 @@ package localhost_test import ( + "testing" + + testifysuite "github.com/stretchr/testify/suite" + sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -14,10 +18,53 @@ import ( "github.com/cosmos/ibc-go/v8/testing/mock" ) -func (suite *LocalhostTestSuite) TestStatus() { +type LocalhostTestSuite struct { + testifysuite.Suite + + coordinator ibctesting.Coordinator + chain *ibctesting.TestChain +} + +func (suite *LocalhostTestSuite) SetupTest() { + suite.coordinator = *ibctesting.NewCoordinator(suite.T(), 1) + suite.chain = suite.coordinator.GetChain(ibctesting.GetChainID(1)) +} + +func TestLocalhostTestSuite(t *testing.T) { + testifysuite.Run(t, new(LocalhostTestSuite)) +} + +func (suite *LocalhostTestSuite) TestInitialize() { lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) suite.Require().True(found) - suite.Require().Equal(exported.Active, lightClientModule.Status(suite.chain.GetContext(), exported.LocalhostClientID)) + + err := lightClientModule.Initialize(suite.chain.GetContext(), exported.LocalhostClientID, nil, nil) + suite.Require().Error(err) +} + +func (suite *LocalhostTestSuite) TestVerifyClientMessage() { + lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) + suite.Require().True(found) + + err := lightClientModule.Initialize(suite.chain.GetContext(), exported.LocalhostClientID, nil, nil) + suite.Require().Error(err) +} + +func (suite *LocalhostTestSuite) TestVerifyCheckForMisbehaviour() { + lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) + suite.Require().True(found) + + suite.Require().False(lightClientModule.CheckForMisbehaviour(suite.chain.GetContext(), exported.LocalhostClientID, nil)) +} + +func (suite *LocalhostTestSuite) TestUpdateState() { + lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) + suite.Require().True(found) + + heights := lightClientModule.UpdateState(suite.chain.GetContext(), exported.LocalhostClientID, nil) + + expHeight := clienttypes.NewHeight(1, uint64(suite.chain.GetContext().BlockHeight())) + suite.Require().True(heights[0].EQ(expHeight)) } func (suite *LocalhostTestSuite) TestVerifyMembership() { @@ -31,20 +78,6 @@ func (suite *LocalhostTestSuite) TestVerifyMembership() { malleate func() expPass bool }{ - { - "success: client state verification", - func() { - clientState := suite.chain.GetClientState(exported.LocalhostClientID) - - merklePath := commitmenttypes.NewMerklePath(host.FullClientStateKey(exported.LocalhostClientID)) - merklePath, err := commitmenttypes.ApplyPrefix(suite.chain.GetPrefix(), merklePath) - suite.Require().NoError(err) - - path = merklePath - value = clienttypes.MustMarshalClientState(suite.chain.Codec, clientState) - }, - true, - }, { "success: connection state verification", func() { @@ -144,21 +177,21 @@ func (suite *LocalhostTestSuite) TestVerifyMembership() { true, }, { - "invalid type for key path", + "failure: invalid type for key path", func() { path = mock.KeyPath{} }, false, }, { - "key path has too many elements", + "failure: key path has too many elements", func() { path = commitmenttypes.NewMerklePath([]byte("ibc"), []byte("test"), []byte("key")) }, false, }, { - "no value found at provided key path", + "failure: no value found at provided key path", func() { merklePath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementKey(mock.PortID, ibctesting.FirstChannelID, 100)) merklePath, err := commitmenttypes.ApplyPrefix(suite.chain.GetPrefix(), merklePath) @@ -170,7 +203,7 @@ func (suite *LocalhostTestSuite) TestVerifyMembership() { false, }, { - "invalid value, bytes are not equal", + "failure: invalid value, bytes are not equal", func() { channel := channeltypes.NewChannel( channeltypes.OPEN, @@ -303,6 +336,22 @@ func (suite *LocalhostTestSuite) TestVerifyNonMembership() { } } +func (suite *LocalhostTestSuite) TestStatus() { + lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) + suite.Require().True(found) + suite.Require().Equal(exported.Active, lightClientModule.Status(suite.chain.GetContext(), exported.LocalhostClientID)) +} + +func (suite *LocalhostTestSuite) TestGetTimestampAtHeight() { + lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) + suite.Require().True(found) + + ctx := suite.chain.GetContext() + timestamp, err := lightClientModule.TimestampAtHeight(ctx, exported.LocalhostClientID, nil) + suite.Require().NoError(err) + suite.Require().Equal(uint64(ctx.BlockTime().UnixNano()), timestamp) +} + func (suite *LocalhostTestSuite) TestRecoverClient() { lightClientModule, found := suite.chain.GetSimApp().IBCKeeper.ClientKeeper.Route(exported.LocalhostClientID) suite.Require().True(found) diff --git a/modules/light-clients/09-localhost/localhost.pb.go b/modules/light-clients/09-localhost/localhost.pb.go deleted file mode 100644 index 61c3999c56d..00000000000 --- a/modules/light-clients/09-localhost/localhost.pb.go +++ /dev/null @@ -1,322 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/lightclients/localhost/v2/localhost.proto - -package localhost - -import ( - fmt "fmt" - _ "github.com/cosmos/gogoproto/gogoproto" - proto "github.com/cosmos/gogoproto/proto" - types "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// ClientState defines the 09-localhost client state -type ClientState struct { - // the latest block height - LatestHeight types.Height `protobuf:"bytes,1,opt,name=latest_height,json=latestHeight,proto3" json:"latest_height"` -} - -func (m *ClientState) Reset() { *m = ClientState{} } -func (m *ClientState) String() string { return proto.CompactTextString(m) } -func (*ClientState) ProtoMessage() {} -func (*ClientState) Descriptor() ([]byte, []int) { - return fileDescriptor_60e51cfed1fd7859, []int{0} -} -func (m *ClientState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientState.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *ClientState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientState.Merge(m, src) -} -func (m *ClientState) XXX_Size() int { - return m.Size() -} -func (m *ClientState) XXX_DiscardUnknown() { - xxx_messageInfo_ClientState.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientState proto.InternalMessageInfo - -func init() { - proto.RegisterType((*ClientState)(nil), "ibc.lightclients.localhost.v2.ClientState") -} - -func init() { - proto.RegisterFile("ibc/lightclients/localhost/v2/localhost.proto", fileDescriptor_60e51cfed1fd7859) -} - -var fileDescriptor_60e51cfed1fd7859 = []byte{ - // 257 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xcd, 0x4c, 0x4a, 0xd6, - 0xcf, 0xc9, 0x4c, 0xcf, 0x28, 0x49, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x29, 0xd6, 0xcf, 0xc9, 0x4f, - 0x4e, 0xcc, 0xc9, 0xc8, 0x2f, 0x2e, 0xd1, 0x2f, 0x33, 0x42, 0x70, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, - 0xf2, 0x85, 0x64, 0x33, 0x93, 0x92, 0xf5, 0x90, 0x95, 0xeb, 0x21, 0x54, 0x94, 0x19, 0x49, 0xc9, - 0x83, 0x4c, 0x4b, 0xce, 0x2f, 0x4a, 0xd5, 0x87, 0x48, 0xeb, 0x97, 0x19, 0x42, 0x59, 0x10, 0xfd, - 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, 0x55, 0x8a, 0xe2, 0xe2, - 0x76, 0x06, 0xab, 0x0a, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0x72, 0xe5, 0xe2, 0xcd, 0x49, 0x2c, 0x49, - 0x2d, 0x2e, 0x89, 0xcf, 0x48, 0x05, 0x59, 0x25, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0x24, 0xa5, - 0x07, 0xb2, 0x1c, 0x64, 0xba, 0x1e, 0xd4, 0xcc, 0x32, 0x43, 0x3d, 0x0f, 0xb0, 0x0a, 0x27, 0x96, - 0x13, 0xf7, 0xe4, 0x19, 0x82, 0x78, 0x20, 0xda, 0x20, 0x62, 0x56, 0x2c, 0x1d, 0x0b, 0xe4, 0x19, - 0x9c, 0x92, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, - 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x23, 0x3d, 0xb3, - 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x3f, 0x39, 0xbf, 0x38, 0x37, 0xbf, 0x58, 0x3f, - 0x33, 0x29, 0x59, 0x37, 0x3d, 0x5f, 0xbf, 0xcc, 0x42, 0x3f, 0x37, 0x3f, 0xa5, 0x34, 0x27, 0xb5, - 0x18, 0x12, 0x34, 0xba, 0xb0, 0xb0, 0x31, 0xb0, 0xd4, 0x85, 0xfb, 0xd7, 0x1a, 0xce, 0x4a, 0x62, - 0x03, 0x7b, 0xc3, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xac, 0xfc, 0x52, 0xb3, 0x4d, 0x01, 0x00, - 0x00, -} - -func (m *ClientState) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ClientState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - { - size, err := m.LatestHeight.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintLocalhost(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - -func encodeVarintLocalhost(dAtA []byte, offset int, v uint64) int { - offset -= sovLocalhost(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ClientState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.LatestHeight.Size() - n += 1 + l + sovLocalhost(uint64(l)) - return n -} - -func sovLocalhost(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozLocalhost(x uint64) (n int) { - return sovLocalhost(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ClientState) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ClientState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LatestHeight", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthLocalhost - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthLocalhost - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.LatestHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipLocalhost(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthLocalhost - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipLocalhost(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthLocalhost - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupLocalhost - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthLocalhost - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthLocalhost = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowLocalhost = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupLocalhost = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/light-clients/09-localhost/localhost_test.go b/modules/light-clients/09-localhost/localhost_test.go deleted file mode 100644 index c12c4546cd8..00000000000 --- a/modules/light-clients/09-localhost/localhost_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package localhost_test - -import ( - "testing" - - testifysuite "github.com/stretchr/testify/suite" - - ibctesting "github.com/cosmos/ibc-go/v8/testing" -) - -type LocalhostTestSuite struct { - testifysuite.Suite - - coordinator ibctesting.Coordinator - chain *ibctesting.TestChain -} - -func (suite *LocalhostTestSuite) SetupTest() { - suite.coordinator = *ibctesting.NewCoordinator(suite.T(), 1) - suite.chain = suite.coordinator.GetChain(ibctesting.GetChainID(1)) -} - -func TestLocalhostTestSuite(t *testing.T) { - testifysuite.Run(t, new(LocalhostTestSuite)) -} diff --git a/modules/light-clients/09-localhost/store.go b/modules/light-clients/09-localhost/store.go deleted file mode 100644 index c80b371bc22..00000000000 --- a/modules/light-clients/09-localhost/store.go +++ /dev/null @@ -1,30 +0,0 @@ -package localhost - -import ( - "fmt" - - storetypes "cosmossdk.io/store/types" - - "github.com/cosmos/cosmos-sdk/codec" - - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" -) - -// getClientState retrieves the client state from the store using the provided KVStore and codec. -// It returns the unmarshaled ClientState and a boolean indicating if the state was found. -func getClientState(store storetypes.KVStore, cdc codec.BinaryCodec) (*ClientState, bool) { - bz := store.Get(host.ClientStateKey()) - if len(bz) == 0 { - return nil, false - } - - clientStateI := clienttypes.MustUnmarshalClientState(cdc, bz) - var clientState *ClientState - clientState, ok := clientStateI.(*ClientState) - if !ok { - panic(fmt.Errorf("cannot convert %T into %T", clientStateI, clientState)) - } - - return clientState, true -} diff --git a/proto/ibc/lightclients/localhost/v2/localhost.proto b/proto/ibc/lightclients/localhost/v2/localhost.proto deleted file mode 100644 index 635db85214e..00000000000 --- a/proto/ibc/lightclients/localhost/v2/localhost.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -package ibc.lightclients.localhost.v2; - -option go_package = "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost;localhost"; - -import "ibc/core/client/v1/client.proto"; -import "gogoproto/gogo.proto"; - -// ClientState defines the 09-localhost client state -message ClientState { - option (gogoproto.goproto_getters) = false; - - // the latest block height - ibc.core.client.v1.Height latest_height = 1 [(gogoproto.nullable) = false]; -}