diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 029d3db46e5..2031be3083c 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.32.0 + - uses: bufbuild/buf-setup-action@v1.32.1 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/docs/docs/03-light-clients/01-developer-guide/01-overview.md b/docs/docs/03-light-clients/01-developer-guide/01-overview.md index 89459264f7f..e4abc31574e 100644 --- a/docs/docs/03-light-clients/01-developer-guide/01-overview.md +++ b/docs/docs/03-light-clients/01-developer-guide/01-overview.md @@ -23,7 +23,7 @@ Learn how to build IBC light client modules and fulfill the interfaces required IBC uses light clients in order to provide trust-minimized interoperability between sovereign blockchains. Light clients operate under a strict set of rules which provide security guarantees for state updates and facilitate the ability to verify the state of a remote blockchain using merkle proofs. -The following aims to provide a high level IBC light client module developer guide. Access to IBC light clients are gated by the core IBC `MsgServer` which utilizes the abstractions set by the `02-client` submodule to call into a light client module. A light client module developer is only required to implement a set interfaces as defined in the `modules/core/exported` package of ibc-go. +The following aims to provide a high level IBC light client module developer guide. Access to IBC light clients are gated by the core IBC `MsgServer` which utilizes the abstractions set by the `02-client` submodule to call into a light client module. A light client module developer is only required to implement a set of interfaces as defined in the `modules/core/exported` package of ibc-go. A light client module developer should be concerned with three main interfaces: diff --git a/docs/docs/03-light-clients/01-developer-guide/02-light-client-module.md b/docs/docs/03-light-clients/01-developer-guide/02-light-client-module.md index 783b1c74524..2ec8d18eea2 100644 --- a/docs/docs/03-light-clients/01-developer-guide/02-light-client-module.md +++ b/docs/docs/03-light-clients/01-developer-guide/02-light-client-module.md @@ -8,6 +8,10 @@ slug: /ibc/light-clients/light-client-module # Implementing the `LightClientModule` interface +## `RegisterStoreProvider` method + +`RegisterStoreProvider` is called by core IBC when a `LightClientModule` is added to the router. It allows the `LightClientModule` to set a `ClientStoreProvider` ([interface defined in `modules/core/exported`](https://github.com/cosmos/ibc-go/blob/06fd8eb5ee1697e3b43be7528a6e42f5e4a4613c/modules/core/exported/client.go#L49-L52)) which supplies isolated prefix client stores to IBC light client instances. + ## `Status` method `Status` must return the status of the client. @@ -26,6 +30,10 @@ This field is returned in the response of the gRPC [`ibc.core.client.v1.Query/Cl `TimestampAtHeight` must return the timestamp for the consensus state associated with the provided height. This value is used to facilitate timeouts by checking the packet timeout timestamp against the returned value. +## `LatestHeight` method + +`LatestHeight` should return the latest block height that the client state represents. + ## `Initialize` method Clients must validate the initial consensus state, and set the initial client state and consensus state in the provided client store. @@ -33,6 +41,14 @@ Clients may also store any necessary client-specific metadata. `Initialize` is called when a [client is created](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/02-client/keeper/client.go#L30). +## `UpdateState` method + +`UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. See section [`UpdateState`](05-updates-and-misbehaviour.md#updatestate) for more information. + +## `UpdateStateOnMisbehaviour` method + +`UpdateStateOnMisbehaviour` should perform appropriate state changes on a client state given that misbehaviour has been detected and verified. See section [`UpdateStateOnMisbehaviour`](05-updates-and-misbehaviour.md#updatestateonmisbehaviour) for more information. + ## `VerifyMembership` method `VerifyMembership` must verify the existence of a value at a given commitment path at the specified height. For more information about membership proofs @@ -48,9 +64,17 @@ see the [Existence and non-existence proofs section](07-proofs.md). `VerifyClientMessage` must verify a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. It must handle each type of `ClientMessage` appropriately. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned -if the ClientMessage fails to verify. +if the ClientMessage fails to verify. See section [`VerifyClientMessage`](05-updates-and-misbehaviour.md#verifyclientmessage) for more information. ## `CheckForMisbehaviour` method Checks for evidence of a misbehaviour in `Header` or `Misbehaviour` type. It assumes the `ClientMessage` -has already been verified. +has already been verified. See section [`CheckForMisbehaviour`](05-updates-and-misbehaviour.md#checkformisbehaviour) for more information. + +## `RecoverClient` method + +`RecoverClient` is used to recover an expired or frozen client by updating the client with the state of a substitute client. The method must verify that the provided substitute may be used to update the subject client. See section [Implementing `RecoverClient`](./08-proposals.md#implementing-recoverclient) for more information. + +## `VerifyUpgradeAndUpdateState` method + +`VerifyUpgradeAndUpdateState` provides a path to upgrading clients given an upgraded `ClientState`, upgraded `ConsensusState` and proofs for each. See section [Implementing `VerifyUpgradeAndUpdateState`](./06-upgrades.md#implementing-verifyupgradeandupdatestate) for more information. diff --git a/docs/docs/03-light-clients/01-developer-guide/05-updates-and-misbehaviour.md b/docs/docs/03-light-clients/01-developer-guide/05-updates-and-misbehaviour.md index b32d8b90087..57b92a0fcc1 100644 --- a/docs/docs/03-light-clients/01-developer-guide/05-updates-and-misbehaviour.md +++ b/docs/docs/03-light-clients/01-developer-guide/05-updates-and-misbehaviour.md @@ -10,8 +10,8 @@ slug: /ibc/light-clients/updates-and-misbehaviour As mentioned before in the documentation about [implementing the `ConsensusState` interface](04-consensus-state.md), [`ClientMessage`](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/exported/client.go#L147) is an interface used to update an IBC client. This update may be performed by: -- a single header -- a batch of headers +- a single header, +- a batch of headers, - evidence of misbehaviour, - or any type which when verified produces a change to the consensus state of the IBC client. @@ -19,7 +19,7 @@ This interface has been purposefully kept generic in order to give the maximum a ## Implementing the `ClientMessage` interface -Find the `ClientMessage`interface in `modules/core/exported`: +Find the `ClientMessage` interface in `modules/core/exported`: ```go type ClientMessage interface { @@ -30,20 +30,20 @@ type ClientMessage interface { } ``` -The `ClientMessage` will be passed to the client to be used in [`UpdateClient`](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/02-client/keeper/client.go#L48), which retrieves the `ClientState` by client ID (available in `MsgUpdateClient`). This `ClientState` implements the [`ClientState` interface](03-client-state.md) for its specific consenus type (e.g. Tendermint). +The `ClientMessage` will be passed to the client to be used in [`UpdateClient`](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/02-client/keeper/client.go#L48), which retrieves the `LightClientModule` by client type (parsed from the client ID available in `MsgUpdateClient`). This `LightClientModule` implements the [`LightClientModule` interface](02-light-client-module.md) for its specific consenus type (e.g. Tendermint). -`UpdateClient` will then handle a number of cases including misbehaviour and/or updating the consensus state, utilizing the specific methods defined in the relevant `ClientState`. +`UpdateClient` will then handle a number of cases including misbehaviour and/or updating the consensus state, utilizing the specific methods defined in the relevant `LightClientModule`. ```go -VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error -CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) bool -UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) -UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height +VerifyClientMessage(ctx sdk.Context, clientID string, clientMsg ClientMessage) error +CheckForMisbehaviour(ctx sdk.Context, clientID string, clientMsg ClientMessage) bool +UpdateStateOnMisbehaviour(ctx sdk.Context, clientID string, clientMsg ClientMessage) +UpdateState(ctx sdk.Context, clientID string, clientMsg ClientMessage) []Height ``` ## Handling updates and misbehaviour -The functions for handling updates to a light client and evidence of misbehaviour are all found in the [`ClientState`](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/exported/client.go#L36) interface, and will be discussed below. +The functions for handling updates to a light client and evidence of misbehaviour are all found in the [`LightClientModule`](https://github.com/cosmos/ibc-go/blob/501a8462345da099144efe91d495bfcfa18d760d/modules/core/exported/client.go#L51) interface, and will be discussed below. > It is important to note that `Misbehaviour` in this particular context is referring to misbehaviour on the chain level intended to fool the light client. This will be defined by each light client. @@ -53,13 +53,13 @@ The functions for handling updates to a light client and evidence of misbehaviou It must handle each type of `ClientMessage` appropriately. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. -For an example of a `VerifyClientMessage` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/update.go#L20). +For an example of a `VerifyClientMessage` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/76730ff030b52a351096ee941b7e4da44af9f059/modules/light-clients/07-tendermint/update.go#L23). ## `CheckForMisbehaviour` Checks for evidence of a misbehaviour in `Header` or `Misbehaviour` type. It assumes the `ClientMessage` has already been verified. -For an example of a `CheckForMisbehaviour` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/misbehaviour_handle.go#L19). +For an example of a `CheckForMisbehaviour` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/76730ff030b52a351096ee941b7e4da44af9f059/modules/light-clients/07-tendermint/misbehaviour_handle.go#L22). > The Tendermint light client [defines `Misbehaviour`](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/misbehaviour.go) as two different types of situations: a situation where two conflicting `Header`s with the same height have been submitted to update a client's `ConsensusState` within the same trusting period, or that the two conflicting `Header`s have been submitted at different heights but the consensus states are not in the correct monotonic time ordering (BFT time violation). More explicitly, updating to a new height must have a timestamp greater than the previous consensus state, or, if inserting a consensus at a past height, then time must be less than those heights which come after and greater than heights which come before. @@ -67,7 +67,7 @@ For an example of a `CheckForMisbehaviour` implementation, please check the [Ten `UpdateStateOnMisbehaviour` should perform appropriate state changes on a client state given that misbehaviour has been detected and verified. This method should only be called when misbehaviour is detected, as it does not perform any misbehaviour checks. Notably, it should freeze the client so that calling the `Status` function on the associated client state no longer returns `Active`. -For an example of a `UpdateStateOnMisbehaviour` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/update.go#L199). +For an example of a `UpdateStateOnMisbehaviour` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/76730ff030b52a351096ee941b7e4da44af9f059/modules/light-clients/07-tendermint/update.go#L202). ## `UpdateState` @@ -75,24 +75,30 @@ For an example of a `UpdateStateOnMisbehaviour` implementation, please check the It assumes the `ClientMessage` has already been verified. -For an example of a `UpdateState` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/update.go#L131). +For an example of a `UpdateState` implementation, please check the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/76730ff030b52a351096ee941b7e4da44af9f059/modules/light-clients/07-tendermint/update.go#L134). ## Putting it all together The `02-client` `Keeper` module in ibc-go offers a reference as to how these functions will be used to [update the client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/02-client/keeper/client.go#L48). ```go -if err := clientState.VerifyClientMessage(clientMessage); err != nil { +clientModule, found := k.router.GetRoute(clientID) +if !found { + return errorsmod.Wrap(types.ErrRouteNotFound, clientID) +} + +if err := clientModule.VerifyClientMessage(ctx, clientID, clientMsg); err != nil { return err } -foundMisbehaviour := clientState.CheckForMisbehaviour(clientMessage) +foundMisbehaviour := clientModule.CheckForMisbehaviour(ctx, clientID, clientMsg) if foundMisbehaviour { - clientState.UpdateStateOnMisbehaviour(clientMessage) + clientModule.UpdateStateOnMisbehaviour(ctx, clientID, clientMsg) // emit misbehaviour event return } -clientState.UpdateState(clientMessage) // expects no-op on duplicate header +clientModule.UpdateState(ctx, clientID, clientMsg) // expects no-op on duplicate header // emit update event return +``` diff --git a/docs/docs/03-light-clients/01-developer-guide/06-upgrades.md b/docs/docs/03-light-clients/01-developer-guide/06-upgrades.md index 461e63703d1..b0be176ac5a 100644 --- a/docs/docs/03-light-clients/01-developer-guide/06-upgrades.md +++ b/docs/docs/03-light-clients/01-developer-guide/06-upgrades.md @@ -15,21 +15,23 @@ It is vital that high-value IBC clients can upgrade along with their underlying The IBC protocol allows client implementations to provide a path to upgrading clients given the upgraded `ClientState`, upgraded `ConsensusState` and proofs for each. This path is provided in the `VerifyUpgradeAndUpdateState` method: ```go -// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last height committed by the current revision. Clients are responsible for ensuring that the planned last height of the current revision is somehow encoded in the proof verification process. -// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty may be cancelled or modified before the last planned height. +// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last +// height committed by the current revision. Clients are responsible for ensuring that the planned last +// height of the current revision is somehow encoded in the proof verification process. +// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty +// may be cancelled or modified before the last planned height. // If the upgrade is verified, the upgraded client and consensus states must be set in the client store. -func (cs ClientState) VerifyUpgradeAndUpdateState( +func (l LightClientModule) VerifyUpgradeAndUpdateState( ctx sdk.Context, - cdc codec.BinaryCodec, - store sdk.KVStore, - newClient ClientState, - newConsState ConsensusState, + clientID string, + newClient []byte, + newConsState []byte, upgradeClientProof, upgradeConsensusStateProof []byte, ) error ``` -> Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/upgrade.go#L27) as an example for implementation. +> Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/47162061bcbfe74df791161059715a635e31c604/modules/light-clients/07-tendermint/light_client_module.go#L257) as an example for implementation. It is important to note that light clients **must** handle all management of client and consensus states including the setting of updated `ClientState` and `ConsensusState` in the client store. This can include verifying that the submitted upgraded `ClientState` is of a valid `ClientState` type, that the height of the upgraded client is not greater than the height of the current client (in order to preserve BFT monotonic time), or that certain parameters which should not be changed have not been altered in the upgraded `ClientState`. @@ -38,6 +40,7 @@ Developers must ensure that the `MsgUpgradeClient` does not pass until the last ### Upgrade path Clients should have **prior knowledge of the merkle path** that the upgraded client and upgraded consensus states will use. The height at which the upgrade has occurred should also be encoded in the proof. + > The Tendermint client implementation accomplishes this by including an `UpgradePath` in the `ClientState` itself, which is used along with the upgrade height to construct the merkle path under which the client state and consensus state are committed. ## Chain specific vs client specific client parameters diff --git a/docs/docs/03-light-clients/01-developer-guide/07-proofs.md b/docs/docs/03-light-clients/01-developer-guide/07-proofs.md index b6951842be0..67663baff32 100644 --- a/docs/docs/03-light-clients/01-developer-guide/07-proofs.md +++ b/docs/docs/03-light-clients/01-developer-guide/07-proofs.md @@ -18,7 +18,7 @@ For the purposes of ibc-go, there are two types of proofs which are important: e Existence proofs are used in IBC transactions which involve verification of counterparty state for transactions which will result in the writing of provable state. For example, this includes verification of IBC store state for handshakes and packets. -Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof comprises of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. +Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof is comprised of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. ## Non-existence proofs @@ -32,13 +32,16 @@ In some cases, there is a necessity to "mock" non-existence proofs if the counte The state verification functions for all IBC data types have been consolidated into two generic methods, `VerifyMembership` and `VerifyNonMembership`. -From the [`ClientState` interface definition](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/exported/client.go#L68-L91), we find: +From the [`LightClientModule` interface definition](https://github.com/cosmos/ibc-go/blob/main/modules/core/exported/client.go#L56), we find: ```go +// VerifyMembership is a generic proof verification method which verifies +// a proof of the existence of a value at a given CommitmentPath at the +// specified height. The caller is expected to construct the full CommitmentPath +// from a CommitmentPrefix and a standardized path (as defined in ICS 24). VerifyMembership( ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, + clientID string, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, @@ -47,8 +50,10 @@ VerifyMembership( value []byte, ) error -// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. -// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +// VerifyNonMembership is a generic proof verification method which verifies +// the absence of a given CommitmentPath at a specified height. The caller is +// expected to construct the full CommitmentPath from a CommitmentPrefix and +// a standardized path (as defined in ICS 24). VerifyNonMembership( ctx sdk.Context, clientStore sdk.KVStore, diff --git a/docs/docs/03-light-clients/01-developer-guide/08-proposals.md b/docs/docs/03-light-clients/01-developer-guide/08-proposals.md index 579a9e372e4..c7e4a2da51e 100644 --- a/docs/docs/03-light-clients/01-developer-guide/08-proposals.md +++ b/docs/docs/03-light-clients/01-developer-guide/08-proposals.md @@ -8,29 +8,29 @@ slug: /ibc/light-clients/proposals # Handling proposals -It is possible to update the client with the state of the substitute client through a governance proposal. [This type of governance proposal](https://ibc.cosmos.network/main/ibc/proposals.html) is typically used to recover an expired or frozen client, as it can recover the entire state and therefore all existing channels built on top of the client. `CheckSubstituteAndUpdateState` should be implemented to handle the proposal. +It is possible to update the client with the state of the substitute client through a governance proposal. [This type of governance proposal](https://ibc.cosmos.network/main/ibc/proposals.html) is typically used to recover an expired or frozen client, as it can recover the entire state and therefore all existing channels built on top of the client. `RecoverClient` should be implemented to handle the proposal. -## Implementing `CheckSubstituteAndUpdateState` +## Implementing `RecoverClient` -In the [`ClientState`interface](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/exported/client.go), we find: +In the [`LightClientModule` interface](https://github.com/cosmos/ibc-go/blob/501a8462345da099144efe91d495bfcfa18d760d/modules/core/exported/client.go#L51), we find: ```go -// CheckSubstituteAndUpdateState must verify that the provided substitute may be used to update the subject client. -// The light client must set the updated client and consensus states within the clientStore for the subject client. -CheckSubstituteAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - subjectClientStore, - substituteClientStore sdk.KVStore, - substituteClient ClientState, +// RecoverClient must verify that the provided substitute +// may be used to update the subject client. The light client +// must set the updated client and consensus states within +// the clientStore for the subject client. +RecoverClient( + ctx sdk.Context, + clientID, + substituteClientID string, ) error ``` Prior to updating, this function must verify that: -- the substitute client is the same type as the subject client. For a reference implementation, please see the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/proposal_handle.go#L32). -- the provided substitute may be used to update the subject client. This may mean that certain parameters must remain unaltered. For example, a [valid substitute Tendermint light client](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/proposal_handle.go#L84) must NOT change the chain ID, trust level, max clock drift, unbonding period, proof specs or upgrade path. Please note that `AllowUpdateAfterMisbehaviour` and `AllowUpdateAfterExpiry` have been deprecated (see ADR 026 for more information). +- the substitute client is the same type as the subject client. For a reference implementation, please see the [Tendermint light client](https://github.com/cosmos/ibc-go/blob/47162061bcbfe74df791161059715a635e31c604/modules/light-clients/07-tendermint/proposal_handle.go#L34). +- the provided substitute may be used to update the subject client. This may mean that certain parameters must remain unaltered. For example, a [valid substitute Tendermint light client](https://github.com/cosmos/ibc-go/blob/47162061bcbfe74df791161059715a635e31c604/modules/light-clients/07-tendermint/proposal_handle.go#L86) must NOT change the chain ID, trust level, max clock drift, unbonding period, proof specs or upgrade path. Please note that `AllowUpdateAfterMisbehaviour` and `AllowUpdateAfterExpiry` have been deprecated (see ADR 026 for more information). After these checks are performed, the function must [set the updated client and consensus states](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/proposal_handle.go#L77) within the client store for the subject client. -Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/light-clients/07-tendermint/proposal_handle.go#L27) for reference. +Please refer to the [Tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/47162061bcbfe74df791161059715a635e31c604/modules/light-clients/07-tendermint/proposal_handle.go#L79) for reference. diff --git a/docs/docs/03-light-clients/01-developer-guide/09-setup.md b/docs/docs/03-light-clients/01-developer-guide/09-setup.md index 31481ea7642..23ced3cf80a 100644 --- a/docs/docs/03-light-clients/01-developer-guide/09-setup.md +++ b/docs/docs/03-light-clients/01-developer-guide/09-setup.md @@ -100,9 +100,9 @@ message MsgCreateClient { } ``` -Leveraging protobuf `Any` encoding allows core IBC to [unpack](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/keeper/msg_server.go#L28-L36) both the `ClientState` and `ConsensusState` into their respective interface types registered previously using the light client module's `RegisterInterfaces` method. +Leveraging protobuf `Any` encoding allows core IBC to [unpack](https://github.com/cosmos/ibc-go/blob/47162061bcbfe74df791161059715a635e31c604/modules/core/keeper/msg_server.go#L38) the `ClientState` into its respective interface type registered previously using the light client module's `RegisterInterfaces` method. -Within the `02-client` submodule, the [`ClientState` is then initialized](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/02-client/keeper/client.go#L30-L32) with its own isolated key-value store, namespaced using a unique client identifier. +Within the `02-client` submodule, the [`ClientState` is then initialized](https://github.com/cosmos/ibc-go/blob/47162061bcbfe74df791161059715a635e31c604/modules/core/02-client/keeper/client.go#L40-L42) with its own isolated key-value store, namespaced using a unique client identifier. In order to successfully create an IBC client using a new client type, it [must be supported](https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/02-client/keeper/client.go#L19-L25). Light client support in IBC is gated by on-chain governance. The allow list may be updated by submitting a new governance proposal to update the `02-client` parameter `AllowedClients`. 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 377f06c7358..a242a2b649c 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 @@ -22,8 +22,12 @@ It calls `CreateLocalhostClient`, declaring a new `ClientState` and initializing ```go func (k Keeper) CreateLocalhostClient(ctx sdk.Context) error { - var clientState localhost.ClientState - return clientState.Initialize(ctx, k.cdc, k.ClientStore(ctx, exported.LocalhostClientID), nil) + clientModule, found := k.router.GetRoute(exported.LocalhostClientID) + if !found { + return errorsmod.Wrap(types.ErrRouteNotFound, exported.LocalhostClientID) + } + + return clientModule.Initialize(ctx, exported.LocalhostClientID, nil, nil) } ``` 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 ddf4f03de10..eb92f2899e9 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 @@ -8,7 +8,7 @@ slug: /ibc/light-clients/localhost/state-verification # State verification -The localhost client handles state verification through the `ClientState` interface methods `VerifyMembership` and `VerifyNonMembership` by performing read-only operations directly on the core IBC store. +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. When verifying channel state in handshakes or processing packets the `09-localhost` client can simply compare bytes stored under the standardized key paths defined by [ICS-24](https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements). diff --git a/docs/docs/03-light-clients/04-wasm/02-concepts.md b/docs/docs/03-light-clients/04-wasm/02-concepts.md index ac9fa18f1f6..978b77d3c73 100644 --- a/docs/docs/03-light-clients/04-wasm/02-concepts.md +++ b/docs/docs/03-light-clients/04-wasm/02-concepts.md @@ -11,7 +11,23 @@ Learn about the differences between a proxy light client and a Wasm light client ## Proxy light client -The `08-wasm` module is not a regular light client in the same sense as, for example, the 07-tendermint light client. `08-wasm` is instead a *proxy* light client module, and this means that the module acts a proxy to the actual implementations of light clients. The module will act as a wrapper for the actual light clients uploaded as Wasm byte code and will delegate all operations to them (i.e. `08-wasm` just passes through the requests to the Wasm light clients). Still, the `08-wasm` module implements all the required interfaces necessary to integrate with core IBC, so that 02-client can call into it as it would for any other light client module. These interfaces are `ClientState`, `ConsensusState` and `ClientMessage`, and we will describe them in the context of `08-wasm` in the following sections. For more information about this set of interfaces, please read section [Overview of the light client module developer guide](../01-developer-guide/01-overview.md#overview). +The `08-wasm` module is not a regular light client in the same sense as, for example, the 07-tendermint light client. `08-wasm` is instead a *proxy* light client module, and this means that the module acts a proxy to the actual implementations of light clients. The module will act as a wrapper for the actual light clients uploaded as Wasm byte code and will delegate all operations to them (i.e. `08-wasm` just passes through the requests to the Wasm light clients). Still, the `08-wasm` module implements all the required interfaces necessary to integrate with core IBC, so that 02-client can call into it as it would for any other light client module. These interfaces are `LightClientModule`, `ClientState`, `ConsensusState` and `ClientMessage`, and we will describe them in the context of `08-wasm` in the following sections. For more information about this set of interfaces, please read section [Overview of the light client module developer guide](../01-developer-guide/01-overview.md#overview). + +### `LightClientModule` + +The `08-wasm`'s `LightClientModule` data structure contains two fields: + +- `keeper` is the `08-wasm` module keeper. +- `storeProvider` encapsulates the IBC core store key and provides access to isolated prefix stores for each client so they can read/write in separate namespaces. + +```go +type LightClientModule struct { + keeper wasmkeeper.Keeper + storeProvider exported.ClientStoreProvider +} +``` + +See section [`LightClientModule` of the light client module developer guide](../01-developer-guide/01-overview.md#lightclientmodule) for more information about the `LightClientModule` interface. ### `ClientState` diff --git a/docs/docs/03-light-clients/04-wasm/03-integration.md b/docs/docs/03-light-clients/04-wasm/03-integration.md index 7d6d4541430..69697560761 100644 --- a/docs/docs/03-light-clients/04-wasm/03-integration.md +++ b/docs/docs/03-light-clients/04-wasm/03-integration.md @@ -27,7 +27,7 @@ The following table shows the compatibility matrix between the `08-wasm` module, ## `app.go` setup -The sample code below shows the relevant integration points in `app.go` required to setup the `08-wasm` module in a chain binary. Since `08-wasm` is a light client module itself, please check out as well the section [Integrating light clients](../../01-ibc/02-integration.md#integrating-light-clients) for more information: +The sample code below shows the relevant integration points in `app.go` required to set up the `08-wasm` module in a chain binary. Since `08-wasm` is a light client module itself, please check out as well the section [Integrating light clients](../../01-ibc/02-integration.md#integrating-light-clients) for more information: ```go // app.go @@ -87,6 +87,11 @@ func NewSimApp( wasmVM, app.GRPCQueryRouter(), ) + + clientRouter := app.IBCKeeper.ClientKeeper.GetRouter() + wasmLightClientModule := wasm.NewLightClientModule(app.WasmClientKeeper) + clientRouter.AddRoute(ibcwasmtypes.ModuleName, &wasmLightClientModule) + app.ModuleManager = module.NewManager( // SDK app modules ... @@ -141,13 +146,13 @@ func NewSimApp( ## Keeper instantiation -When it comes to instantiating `08-wasm`'s keeper there are two recommended ways of doing it. Choosing one or the other will depend on whether the chain already integrates [`x/wasm`](https://github.com/CosmWasm/wasmd/tree/main/x/wasm) or not. +When it comes to instantiating `08-wasm`'s keeper, there are two recommended ways of doing it. Choosing one or the other will depend on whether the chain already integrates [`x/wasm`](https://github.com/CosmWasm/wasmd/tree/main/x/wasm) or not. ### If `x/wasm` is present If the chain where the module is integrated uses `x/wasm` then we recommend that both `08-wasm` and `x/wasm` share the same Wasm VM instance. Having two separate Wasm VM instances is still possible, but care should be taken to make sure that both instances do not share the directory when the VM stores blobs and various caches, otherwise unexpected behaviour is likely to happen (from `x/wasm` v0.51 and `08-wasm` v0.2.0+ibc-go-v8.3-wasmvm-v2.0 this will be forbidden anyway, since wasmvm v2.0.0 and above will not allow two different Wasm VM instances to shared the same data folder). -In order to share the Wasm VM instance please follow the guideline below. Please note that this requires `x/wasm` v0.41 or above. +In order to share the Wasm VM instance, please follow the guideline below. Please note that this requires `x/wasm` v0.41 or above. - Instantiate the Wasm VM in `app.go` with the parameters of your choice. - [Create an `Option` with this Wasm VM instance](https://github.com/CosmWasm/wasmd/blob/db93d7b6c7bb6f4a340d74b96a02cec885729b59/x/wasm/keeper/options.go#L21-L25). diff --git a/docs/docs/03-light-clients/04-wasm/04-messages.md b/docs/docs/03-light-clients/04-wasm/04-messages.md index 6b8321583c2..cbb9571616b 100644 --- a/docs/docs/03-light-clients/04-wasm/04-messages.md +++ b/docs/docs/03-light-clients/04-wasm/04-messages.md @@ -27,7 +27,7 @@ This message is expected to fail if: Only light client contracts stored using `MsgStoreCode` are allowed to be instantiated. An attempt to create a light client from contracts uploaded via other means (e.g. through `x/wasm` if the module shares the same Wasm VM instance with 08-wasm) will fail. Due to the idempotent nature of the Wasm VM's `StoreCode` function, it is possible to store the same byte code multiple times. -When execution of `MsgStoreCode` succeeds, the checksum of the contract (i.e. the sha256 hash of the contract's byte code) is stored in an allow list. When a relayer submits [`MsgCreateClient`](https://github.com/cosmos/ibc-go/blob/v8.0.0/proto/ibc/core/client/v1/tx.proto#L25-L37) with 08-wasm's `ClientState`, the client state includes the checksum of the Wasm byte code that should be called. Then 02-client calls [08-wasm's implementation of `Initialize` function](https://github.com/cosmos/ibc-go/blob/v8.0.0/modules/core/02-client/keeper/client.go#L36) (which is an interface function part of `ClientState`), and it will check that the checksum in the client state matches one of the checksums in the allow list. If a match is found, the light client is initialized; otherwise, the transaction is aborted. +When execution of `MsgStoreCode` succeeds, the checksum of the contract (i.e. the sha256 hash of the contract's byte code) is stored in an allow list. When a relayer submits [`MsgCreateClient`](https://github.com/cosmos/ibc-go/blob/v8.0.0/proto/ibc/core/client/v1/tx.proto#L25-L37) with 08-wasm's `ClientState`, the client state includes the checksum of the Wasm byte code that should be called. Then 02-client calls [08-wasm's implementation of `Initialize` function](https://github.com/cosmos/ibc-go/blob/06fd8eb5ee1697e3b43be7528a6e42f5e4a4613c/modules/core/02-client/keeper/client.go#L40) (which is an interface function part of `LightClientModule`), and it will check that the checksum in the client state matches one of the checksums in the allow list. If a match is found, the light client is initialized; otherwise, the transaction is aborted. ## `MsgMigrateContract` diff --git a/docs/versioned_docs/version-v7.5.x/03-light-clients/01-developer-guide/06-proofs.md b/docs/versioned_docs/version-v7.5.x/03-light-clients/01-developer-guide/06-proofs.md index c5a651d0dea..c168865b303 100644 --- a/docs/versioned_docs/version-v7.5.x/03-light-clients/01-developer-guide/06-proofs.md +++ b/docs/versioned_docs/version-v7.5.x/03-light-clients/01-developer-guide/06-proofs.md @@ -18,7 +18,7 @@ For the purposes of ibc-go, there are two types of proofs which are important: e Existence proofs are used in IBC transactions which involve verification of counterparty state for transactions which will result in the writing of provable state. For example, this includes verification of IBC store state for handshakes and packets. -Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof comprises of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. +Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof is comprised of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. ## Non-existence proofs diff --git a/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/01-overview.md b/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/01-overview.md index eeef65a7be7..9626d45ebb1 100644 --- a/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/01-overview.md +++ b/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/01-overview.md @@ -23,7 +23,7 @@ Learn how to build IBC light client modules and fulfill the interfaces required IBC uses light clients in order to provide trust-minimized interoperability between sovereign blockchains. Light clients operate under a strict set of rules which provide security guarantees for state updates and facilitate the ability to verify the state of a remote blockchain using merkle proofs. -The following aims to provide a high level IBC light client module developer guide. Access to IBC light clients are gated by the core IBC `MsgServer` which utilizes the abstractions set by the `02-client` submodule to call into a light client module. A light client module developer is only required to implement a set interfaces as defined in the `modules/core/exported` package of ibc-go. +The following aims to provide a high level IBC light client module developer guide. Access to IBC light clients is gated by the core IBC `MsgServer` which utilizes the abstractions set by the `02-client` submodule to call into a light client module. A light client module developer is only required to implement a set interfaces as defined in the `modules/core/exported` package of ibc-go. A light client module developer should be concerned with three main interfaces: diff --git a/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/06-proofs.md b/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/06-proofs.md index 636e2b70e80..c3a41b7340e 100644 --- a/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/06-proofs.md +++ b/docs/versioned_docs/version-v8.3.x/03-light-clients/01-developer-guide/06-proofs.md @@ -18,7 +18,7 @@ For the purposes of ibc-go, there are two types of proofs which are important: e Existence proofs are used in IBC transactions which involve verification of counterparty state for transactions which will result in the writing of provable state. For example, this includes verification of IBC store state for handshakes and packets. -Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof comprises of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. +Put simply, existence proofs prove that a particular key and value exists in the tree. Under the hood, an IBC existence proof is comprised of two proofs: an IAVL proof that the key exists in IBC store/IBC root hash, and a proof that the IBC root hash exists in the multistore root hash. ## Non-existence proofs diff --git a/modules/apps/callbacks/testing/simapp/app.go b/modules/apps/callbacks/testing/simapp/app.go index db4eee5c2fc..de62728bfa8 100644 --- a/modules/apps/callbacks/testing/simapp/app.go +++ b/modules/apps/callbacks/testing/simapp/app.go @@ -767,7 +767,7 @@ func NewSimApp( // meaning that both `runMsgs` and `postHandler` state will be committed if // both are successful, and both will be reverted if any of the two fails. // - // The SDK exposes a default postHandlers chain, which comprises of only + // The SDK exposes a default postHandlers chain, which is comprised of only // one decorator: the Transaction Tips decorator. However, some chains do // not need it by default, so feel free to comment the next line if you do // not need tips. diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 168a165cb6a..0f7e8a70997 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -952,3 +953,90 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketSetsTotalEscrowAmountForSourceI totalEscrowChainB = suite.chainB.GetSimApp().TransferKeeper.GetTotalEscrowForDenom(suite.chainB.GetContext(), coin.GetDenom()) suite.Require().Equal(sdkmath.ZeroInt(), totalEscrowChainB.Amount) } + +func (suite *KeeperTestSuite) TestPacketForwardsCompatibility() { + // We are testing a scenario where a packet in the future has a new populated + // field called "new_field". And this packet is being sent to this module which + // doesn't have this field in the packet data. The module should be able to handle + // this packet without any issues. + + var packetData []byte + + testCases := []struct { + msg string + malleate func() + expError error + }{ + { + "success: new field", + func() { + jsonString := fmt.Sprintf(`{"denom":"denom","amount":"100","sender":"%s","receiver":"%s","memo":"memo","new_field":"value"}`, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) + packetData = []byte(jsonString) + }, + nil, + }, + { + "success: no new field with memo", + func() { + jsonString := fmt.Sprintf(`{"denom":"denom","amount":"100","sender":"%s","receiver":"%s","memo":"memo"}`, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) + packetData = []byte(jsonString) + }, + nil, + }, + { + "success: no new field without memo", + func() { + jsonString := fmt.Sprintf(`{"denom":"denom","amount":"100","sender":"%s","receiver":"%s"}`, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) + packetData = []byte(jsonString) + }, + nil, + }, + { + "failure: invalid packet data", + func() { + packetData = []byte("invalid packet data") + }, + ibcerrors.ErrInvalidType, + }, + { + "failure: missing field", + func() { + jsonString := fmt.Sprintf(`{"amount":"100","sender":%s","receiver":"%s"}`, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) + packetData = []byte(jsonString) + }, + ibcerrors.ErrInvalidType, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + packetData = nil + + path := ibctesting.NewTransferPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.Version = types.V1 + path.EndpointB.ChannelConfig.Version = types.V1 + path.Setup() + + tc.malleate() + + timeoutHeight := suite.chainB.GetTimeoutHeight() + + seq, err := path.EndpointB.SendPacket(timeoutHeight, 0, packetData) + suite.Require().NoError(err) + + packet := channeltypes.NewPacket(packetData, seq, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, timeoutHeight, 0) + + // receive packet on chainA + err = path.RelayPacket(packet) + + expPass := tc.expError == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorContains(err, tc.expError.Error()) + } + }) + } +} diff --git a/modules/light-clients/08-wasm/testing/simapp/app.go b/modules/light-clients/08-wasm/testing/simapp/app.go index 8537c7f797d..9370edef704 100644 --- a/modules/light-clients/08-wasm/testing/simapp/app.go +++ b/modules/light-clients/08-wasm/testing/simapp/app.go @@ -814,7 +814,7 @@ func NewSimApp( // meaning that both `runMsgs` and `postHandler` state will be committed if // both are successful, and both will be reverted if any of the two fails. // - // The SDK exposes a default postHandlers chain, which comprises of only + // The SDK exposes a default postHandlers chain, which is comprised of only // one decorator: the Transaction Tips decorator. However, some chains do // not need it by default, so feel free to comment the next line if you do // not need tips. diff --git a/testing/README.md b/testing/README.md index dfef3e73604..b06b6c652d6 100644 --- a/testing/README.md +++ b/testing/README.md @@ -2,7 +2,7 @@ ## Components -The testing package comprises of four parts constructed as a stack. +The testing package is comprised of four parts constructed as a stack. - coordinator - chain diff --git a/testing/simapp/app.go b/testing/simapp/app.go index cbb77eac3db..5b52e5f1f52 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -755,7 +755,7 @@ func NewSimApp( // meaning that both `runMsgs` and `postHandler` state will be committed if // both are successful, and both will be reverted if any of the two fails. // - // The SDK exposes a default postHandlers chain, which comprises of only + // The SDK exposes a default postHandlers chain, which is comprised of only // one decorator: the Transaction Tips decorator. However, some chains do // not need it by default, so feel free to comment the next line if you do // not need tips.